Lo configuré así. La parte más complicada fue que, incluso si eres root, seguirás obteniendo errores de “permiso denegado” al intentar ver o editar las reglas de iptables dentro del contenedor Docker predeterminado de Discourse.
root@24a1f9f4c038:/# iptables -L
# Advertencia: existen tablas iptables-legacy, usa iptables-legacy para verlas
iptables: Permiso denegado (debes ser root).
root@24a1f9f4c038:/#
Esto se debe a que, por defecto, el script launcher de Discourse, que envuelve los comandos de Docker, ejecuta el contenedor Docker de Discourse sin la capacidad “NET_ADMIN”.
La forma más robusta de agregar la capacidad NET_ADMIN al contenedor Docker de Discourse es actualizar el archivo yaml de tu contenedor para incluir el argumento necesario en el comando docker run ... /sbin/boot mediante la cadena yaml docker_args:
docker_args: "--cap-add NET_ADMIN"
Por ejemplo:
[root@osestaging1 discourse]# head -n15 containers/app.yml
## esta es la plantilla del contenedor Docker de Discourse todo-en-uno, independiente
##
## Después de realizar cambios en este archivo, DEBES reconstruir
## /var/discourse/launcher rebuild app
##
## TEN *MUCHO* CUIDADO AL EDITAR!
## ¡LOS ARCHIVOS YAML SON SUPER SENSIBLES A ERRORES EN LOS ESPACIOS EN BLANCO O EN LA ALINEACIÓN!
## visita http://www.yamllint.com/ para validar este archivo según sea necesario
docker_args: "--cap-add NET_ADMIN"
templates:
- "templates/postgres.template.yml"
- "templates/redis.template.yml"
- "templates/web.template.yml"
[root@osestaging1 discourse]#
Estos se agregarán al comando docker run ... /sbin/boot ejecutado por launcher a través de la variable $user_args:
[root@osestaging1 discourse]# grep -A2 -E '^\s*\$docker_path run' launcher
$docker_path run --shm-size=512m $links $attach_on_run $restart_policy "${env[@]}" "${labels[@]}" -h "$hostname" \
-e DOCKER_HOST_IP="$docker_ip" --name $config -t "${ports[@]}" $volumes $mac_address $user_args \
$run_image $boot_command
[root@osestaging1 discourse]#
Dado que es muy complejo realizar cambios integrados en la imagen Docker de Discourse después de un docker pull y antes del docker run ... /sbin/boot, decidí instalar y configurar iptables mediante un script simple ejecutado por runit cuando el contenedor se inicia.
El siguiente comando creará el archivo yaml de plantilla necesario, que generará el script runit en el próximo ./launcher rebuild app:
cat << EOF > /var/discourse/templates/iptables.template.yml
run:
- file:
path: /etc/runit/1.d/01-iptables
chmod: "+x"
contents: |
#!/bin/bash
################################################################################
# Archivo: /etc/runit/1.d/01-iptables
# Versión: 0.2
# Propósito: instala y bloquea iptables
# Autor: Michael Altfield <michael@opensourceecology.org>
# Creado: 2019-11-26
# Actualizado: 2019-12-17
################################################################################
sudo apt-get update
sudo apt-get install -y iptables
sudo iptables -A INPUT -i lo -j ACCEPT
sudo iptables -A INPUT -s 127.0.0.1/32 -j DROP
sudo iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
sudo iptables -A INPUT -j DROP
sudo iptables -A OUTPUT -s 127.0.0.1/32 -d 127.0.0.1/32 -j ACCEPT
sudo iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
sudo iptables -A OUTPUT -m owner --uid-owner 0 -j ACCEPT
sudo iptables -A OUTPUT -m owner --uid-owner 100 -j ACCEPT
sudo iptables -A OUTPUT -j DROP
sudo ip6tables -A INPUT -i lo -j ACCEPT
sudo ip6tables -A INPUT -s ::1/128 -j DROP
sudo ip6tables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
sudo ip6tables -A INPUT -j DROP
sudo ip6tables -A OUTPUT -s ::1/128 -d ::1/128 -j ACCEPT
sudo ip6tables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
sudo ip6tables -A OUTPUT -m owner --uid-owner 0 -j ACCEPT
sudo ip6tables -A OUTPUT -m owner --uid-owner 100 -j ACCEPT
sudo ip6tables -A OUTPUT -j DROP
EOF
Ten en cuenta que esta es una configuración de iptables muy básica. Con el archivo web.socketed.template.yml (que uso), el contenedor Docker técnicamente no necesita acceso a Internet, ya que el proxy inverso nginx en el host de Docker se comunica con el nginx de Discourse en el contenedor Docker a través de un socket de dominio Unix. La razón principal por la que permito el acceso a Internet es para que el sistema operativo base del contenedor Docker pueda actualizarse con parches de seguridad críticos mediante unattended-upgrades. De ahí que abra el acceso para el usuario 100, que es el usuario apt.
Finalmente, agrega el archivo templates/iptables.template.yml anterior al archivo app.yaml de tu contenedor.
[root@osestaging1 discourse]# head -n15 /var/discourse/containers/discourse_ose.yml
## esta es la plantilla del contenedor Docker de Discourse todo-en-uno, independiente
##
## Después de realizar cambios en este archivo, DEBES reconstruir
## /var/discourse/launcher rebuild app
##
## TEN *MUCHO* CUIDADO AL EDITAR!
## ¡LOS ARCHIVOS YAML SON SUPER SENSIBLES A ERRORES EN LOS ESPACIOS EN BLANCO O EN LA ALINEACIÓN!
## visita http://www.yamllint.com/ para validar este archivo según sea necesario
docker_args: "--cap-add NET_ADMIN"
templates:
- "templates/iptables.template.yml"
- "templates/postgres.template.yml"
- "templates/redis.template.yml"
[root@osestaging1 discourse]#
Ahora deberías poder ejecutar:
./launcher rebuild app
Y después de esperar unos 10 minutos, tu contenedor Docker de Discourse tendrá iptables completamente funcional 