wazuh-docker
wazuh-docker copied to clipboard
Rootless podman with SELinux
OS Rocky Linux 9.5 (Blue Onyx) podman-compose version 1.3.0 podman version 5.2.2
I managed to deploy Wazuh on a rootless podman setup with just little modifications. I would like to share it, also in hope that these modifications will be incorporated, as they shouldn't introduce a breaking change. With further testing, this could lead to official rootless podman support.
System setup
# as root
vi /etc/sysctl.conf
# add the follwing line
# vm.max_map_count=262144
reboot
# check if updated
sysctl -a |grep vm.max_map_count
# install new packages
dnf upgrade
dnf install podman python3-pip netavark aardvark-dns
vi /usr/share/containers/containers.conf # set network_backend = "netavark"
# The systemd user instance is killed after the last session for the user is closed.
# The systemd user instance can be started at boot and kept running even after the
# user logs out by enabling lingering using
loginctl enable-linger myuser
# as regular user
pip3 install --user podman-compose
pip3 install --upgrade podman-compose pip
sudop mkdir -p /opt/wazuh
sudo chown -R myuser:mygroup /opt/wazuh
cd /opt/wazuh
# as root
restorecon -vv -F -r /
Unprivileged Ports
sudo su -
# if needed to bind to ports < 1024, will be reset after reboot!
echo 443 > /proc/sys/net/ipv4/ip_unprivileged_port_start
# if needed permanently
echo "net.ipv4.ip_unprivileged_port_start=443" >> /etc/sysctl.conf
ulimit -aH
ulimit -aS
vi /etc/security/limits.conf
# Exit and re-login from the terminal for the change to take effect.
#<domain> <type> <item> <value>
...
myuser hard memlock -1
myuser hard nofile 655360
Firewall on VM
sudo su -
firewall-cmd --list-all
firewall-cmd --permanent --zone=public --add-service=http
firewall-cmd --permanent --zone=public --add-service=https
firewall-cmd --reload
firewall-cmd --list-all
Initial Deployment
podman-compose --in-pod=true up -d without file changes:
cp: cannot create regular file '/certificates/admin-key.pem': Permission denied
cp: cannot create regular file '/certificates/admin.pem': Permission denied
cp: cannot create regular file '/certificates/root-ca.key': Permission denied
cp: cannot create regular file '/certificates/root-ca.pem': Permission denied
...
Modifications
add :z to generate-indexer-certs.yml bind mounts:
...
volumes:
- ./config/wazuh_indexer_ssl_certs/:/certificates/:z
- ./config/certs.yml:/config/certs.yml:z
also add :z to all bind mounts inside docker-compose.yml (for nginx bind mounts :ro,z)
Final Deployment
podman-compose --in-pod=true up -d
Everything should come up as usual.
Hello, thank you for your contribution. We will analyze what was proposed and try to incorporate the changes for our version 5.0.0.
@c-bordon no thank you! I submitted a PR: https://github.com/wazuh/wazuh-docker/pull/1834 Wazuh manager in rootless podman seems to work so far.
FYI: I'm looking into the agent right now. The plan is to create a daemonSet for Openshifts restricted env. The idea is to have a rootless image based on rockylinux:9-ubi-micro. Will see how that works out, so far SETGID cap is causing some issues.
Hello, I have quadlet setup, it runs, but need some tunning with the host where is installed, because not finish run because /proc and /sys, right now I don't have time to continue until next week, but if you found the solution here is the script [rootless] (My system use selinux enforce):
@leonidas-o @c-bordon
#!/usr/bin/env bash
set -euo pipefail
## ========== ##
## Init ##
## ========== ##
# Start podman socker and auto-update timer
if [ $(systemctl --user is-enabled podman.socket) == "disabled" ]; then
systemctl --user enable --now podman.socket podman-auto-update.timer
fi
## ================== ##
## Environments ##
## ================== ##
APP_NAME="wazuh"
## Rutas
ROOTLESS_PATH="${HOME}/.config/containers"
QUADLET_PATH="${ROOTLESS_PATH}"/systemd/${APP_NAME}
VOLUMES_PATH="${HOME}/.volumes"
# container setup for rootless
if [ ! -d "${ROOTLESS_PATH}" ]; then
rsync -avx --exclude="*storage.conf*" --ignore-existing /usr/share/containers "${ROOTLESS_PATH}"
#### CHANGE IF THE USER IS NOT INSTALLED IN A BTRFS PARTTION TO overlay"
printf '[storage]\ndriver = "btrfs"\n' >"${ROOTLESS_PATH}"/storage.conf
fi
# Create the systemd-quadlet spaces
[ ! -d "${QUADLET_PATH}" ] && mkdir -vp "${QUADLET_PATH}"
## ============= ##
## Volumes ##
## ============= ##
VOLUMES=(
"wazuh_api_configuration"
"wazuh_etc"
"wazuh_logs"
"wazuh_queue"
"wazuh_var_multigroups"
"wazuh_active_response"
"wazuh_wodles"
"filebeat_etc"
"filebeat_var"
"wazuh-indexer-data"
"wazuh-dashboard-config"
"wazuh-dashboard-custom"
)
for volume in "${VOLUMES[@]}"; do
# Creación del directorio
mkdir -p "${VOLUMES_PATH}/${volume}"
# Limpieza del nombre para el archivo .volume
volume_file_name=$(echo "${volume}" | tr '/' '-')
cat >${QUADLET_PATH}/${volume_file_name}.volume <<EOF
[Unit]
Description=Volume for ${APP_NAME} ${volume^}
[Quadlet]
# Disable dependency "podman-user-wait-network-online.service"
DefaultDependencies=false
[Volume]
Type=none
Device=${VOLUMES_PATH}/${volume}
Options=rbind,rw
EOF
done
## ================== ##
## Devs configs ##
## ================== ##
# 1) Light Clone
tmpdir="$(mktemp -d)"
command -v git >/dev/null 2>&1 || zypper in -y git-core
git clone --depth 1 --filter=blob:none --sparse --single-branch --branch v4.13.1 \
https://github.com/wazuh/wazuh-docker.git "$tmpdir"
cd "$tmpdir"
# 2) Ask only one folder
git sparse-checkout set single-node/config
# 3) Copy the contain to the correct volume
mkdir -p "${VOLUMES_PATH}/config/wazuh_indexer_ssl_certs"
cp -a single-node/config/* "${VOLUMES_PATH}/config/"
# 4) Limpiar
cd
rm -rf "$tmpdir"
## ============= ##
## Network ##
## ============= ##
cat >${QUADLET_PATH}/${APP_NAME,,}.network <<EOF
[Unit]
Description=${APP_NAME} Network
After=network.target
[Install]
WantedBy=default.target
EOF
## ========= ##
## Pod ##
## ========= ##
cat >${QUADLET_PATH}/${APP_NAME,,}.pod <<EOF
[Unit]
Description=${APP_NAME} POD
[Pod]
PodName=${APP_NAME,,}
Network=${APP_NAME,,}.network
# Wazuh Manager - Ports
PublishPort=1514:1514
PublishPort=1515:1515
PublishPort=514:514/udp
PublishPort=55000:55000
# Wazuh Indexer - Ports
PublishPort=9200:9200
# Wazuh Dasshboard - Ports
PublishPort=443:5601
[Service]
# Restart service when sleep finishes
Restart=always
# Extend Timeout to allow time to pull the image
TimeoutStartSec=900
[Install]
WantedBy=default.target
EOF
## =============== ##
## Container ##
## =============== ##
##
## Wazuh Manager
##
cat >${QUADLET_PATH}/${APP_NAME,,}-manager.container <<EOF
[Unit]
Description="${APP_NAME} Manager"
[Service]
# Restart service when sleep finishes
Restart=always
# Extend Timeout to allow time to pull the image
TimeoutStartSec=900
[Container]
Pod=${APP_NAME,,}.pod
# Imagen y actualización
Image=docker.io/wazuh/wazuh-manager:4.13.1
AutoUpdate=registry
Pull=newer
# Ulimit
Ulimit=memlock=-1:-1
Ulimit=nofile=65536:65536
# Variables de Entornos
Environment="INDEXER_URL=https://localhost:9200"
Environment="INDEXER_USERNAME=admin"
Environment="INDEXER_PASSWORD=SecretPassword"
Environment="FILEBEAT_SSL_VERIFICATION_MODE=full"
Environment="SSL_CERTIFICATE_AUTHORITIES=/etc/ssl/root-ca.pem"
Environment="SSL_CERTIFICATE=/etc/ssl/filebeat.pem"
Environment="SSL_KEY=/etc/ssl/filebeat.key"
Environment="API_USERNAME=wazuh-wui"
Environment="API_PASSWORD=MyS3cr37P450r.*--"
# Volúmenes
Volume=wazuh_api_configuration.volume:/var/ossec/api/configuration:Z
Volume=wazuh_etc.volume:/var/ossec/etc:Z
Volume=wazuh_logs.volume:/var/ossec/logs:Z
Volume=wazuh_queue.volume:/var/ossec/queue:Z
Volume=wazuh_var_multigroups.volume:/var/ossec/var/multigroups:Z
Volume=wazuh_active_response.volume:/var/ossec/active-response/bin:Z
Volume=wazuh_wodles.volume:/var/ossec/wodles:Z
Volume=filebeat_etc.volume:/etc/filebeat:Z
Volume=filebeat_var.volume:/var/lib/filebeat:Z
Volume=${VOLUMES_PATH}/./config/wazuh_indexer_ssl_certs/root-ca-manager.pem:/etc/ssl/root-ca.pem:Z
Volume=${VOLUMES_PATH}/./config/wazuh_indexer_ssl_certs/wazuh.manager.pem:/etc/ssl/filebeat.pem:Z
Volume=${VOLUMES_PATH}/./config/wazuh_indexer_ssl_certs/wazuh.manager-key.pem:/etc/ssl/filebeat.key:Z
Volume=${VOLUMES_PATH}/./config/wazuh_cluster/wazuh_manager.conf:/wazuh-config-mount/etc/ossec.conf:Z
EOF
##
## Wazuh Indexer
##
cat >${QUADLET_PATH}/${APP_NAME,,}-indexer.container <<EOF
[Unit]
Description="${APP_NAME} indexer"
[Service]
# Restart service when sleep finishes
Restart=always
# Extend Timeout to allow time to pull the image
TimeoutStartSec=900
[Container]
Pod=${APP_NAME,,}.pod
# Imagen y actualización
Image=docker.io/wazuh/wazuh-indexer:4.13.1
AutoUpdate=registry
Pull=newer
# Ulimit
Ulimit=memlock=-1:-1
Ulimit=nofile=65536:65536
# Variables de entornor del contenedor
Environment="OPENSEARCH_JAVA_OPTS=-Xms1g -Xmx1g"
# Volúmenes
Volume=wazuh-indexer-data.volume:/var/lib/wazuh-indexer:Z
Volume=${VOLUMES_PATH}/config/wazuh_indexer_ssl_certs/root-ca.pem:/usr/share/wazuh-indexer/config/certs/root-ca.pem:Z
Volume=${VOLUMES_PATH}/config/wazuh_indexer_ssl_certs/wazuh.indexer-key.pem:/usr/share/wazuh-indexer/config/certs/wazuh.indexer.key:Z
Volume=${VOLUMES_PATH}/config/wazuh_indexer_ssl_certs/wazuh.indexer.pem:/usr/share/wazuh-indexer/config/certs/wazuh.indexer.pem:Z
Volume=${VOLUMES_PATH}/config/wazuh_indexer_ssl_certs/admin.pem:/usr/share/wazuh-indexer/config/certs/admin.pem:Z
Volume=${VOLUMES_PATH}/config/wazuh_indexer_ssl_certs/admin-key.pem:/usr/share/wazuh-indexer/config/certs/admin-key.pem:Z
Volume=${VOLUMES_PATH}/config/wazuh_indexer/wazuh.indexer.yml:/usr/share/wazuh-indexer/config/opensearch.yml:Z
Volume=${VOLUMES_PATH}/config/wazuh_indexer/internal_users.yml:/usr/share/wazuh-indexer/config/opensearch-security/internal_users.yml:Z
EOF
##
## Wazuh Dashboard
##
cat >${QUADLET_PATH}/${APP_NAME,,}-dashboard.container <<EOF
[Unit]
Description="${APP_NAME} dashboard"
[Service]
# Restart service when sleep finishes
Restart=always
# Extend Timeout to allow time to pull the image
TimeoutStartSec=900
[Container]
Pod=${APP_NAME,,}.pod
# Imagen y actualización
Image=docker.io/wazuh/wazuh-dashboard:4.13.1
AutoUpdate=registry
Pull=newer
# Variables de entornor del contenedor
Environment="INDEXER_USERNAME=admin"
Environment="INDEXER_PASSWORD=SecretPassword"
Environment="WAZUH_API_URL=https://localhost"
Environment="DASHBOARD_USERNAME=kibanaserver"
Environment="DASHBOARD_PASSWORD=kibanaserver"
Environment="API_USERNAME=wazuh-wui"
Environment="API_PASSWORD=MyS3cr37P450r.*-"
# Volúmenes
Volume=${VOLUMES_PATH}/config/wazuh_indexer_ssl_certs/wazuh.dashboard.pem:/usr/share/wazuh-dashboard/certs/wazuh-dashboard.pem:Z
Volume=${VOLUMES_PATH}/config/wazuh_indexer_ssl_certs/wazuh.dashboard-key.pem:/usr/share/wazuh-dashboard/certs/wazuh-dashboard-key.pem:Z
Volume=${VOLUMES_PATH}/config/wazuh_indexer_ssl_certs/root-ca.pem:/usr/share/wazuh-dashboard/certs/root-ca.pem:Z
Volume=${VOLUMES_PATH}/config/wazuh_dashboard/opensearch_dashboards.yml:/usr/share/wazuh-dashboard/config/opensearch_dashboards.yml:Z
Volume=${VOLUMES_PATH}/config/wazuh_dashboard/wazuh.yml:/usr/share/wazuh-dashboard/data/wazuh/config/wazuh.yml:Z
Volume=wazuh-dashboard-config.volume:/usr/share/wazuh-dashboard/data/wazuh/config:Z
Volume=wazuh-dashboard-custom.volume:/usr/share/wazuh-dashboard/plugins/wazuh/public/assets/custom:Z
EOF
## =========== ##
## Tools ##
## =========== ##
podman run --rm -it --name wazuh-certs-generator \
-v ${VOLUMES_PATH}/config/wazuh_indexer_ssl_certs/:/certificates/:Z \
-v ${VOLUMES_PATH}/config/certs.yml:/config/certs.yml:Z \
docker.io/wazuh/wazuh-certs-generator:0.0.2
## ================== ##
## Verify Units ##
## ================== ##
systemctl --user daemon-reload
# Verify if the quadlet it is generated correctly
/usr/libexec/podman/quadlet -dryrun -user
don't know why you are building that bash script, I simply use a systemd service file and podman-compose to utilize the already existing docker-compose.yml files (either in multi-node or single-node dir):
[Unit]
Description=Podman-compose wazuh.service
Wants=network.target
After=network-online.target
[Service]
Type=oneshot
RemainAfterExit=true
WorkingDirectory=/opt/wazuh/wazuh-docker/multi-node
ExecStart=/home/nuvariant/.local/bin/podman-compose --in-pod=true up -d
ExecStop=/home/nuvariant/.local/bin/podman-compose down
[Install]
WantedBy=default.target