Fecha: 2015-10-15 Tiempo de lectura: 6 minutos Categoría: Virtualización Tags: linux / debian / jessie / lxc / bridge / firehol
En este tutorial se propone montar un servidor de contenedores LXC, de forma que todos los contenedores queden expuestos a la misma red que el servidor que los aloja. Para protegerlos de posibles ataques de esta red, pondremos un firewall basado en iptables mediante una capa de abstracción llamada firehol.
Para conseguir este objetivo, se van a usar las siguientes tecnologías:
En cuanto a las capacidades hardware, vamos a hacer el tutorial con un equipo de capacidades modestas, virtualizado en una máquina virtual VirtualBox.
Partimos de una distribución Debian jessie instalada con un CD netinstall y con el único paquete instalado openssh-server, para mi comodidad.
El primer paso consiste en instalar las tecnologías usadas:
root@lxc:~# apt-get install bridge-utils firehol lxc
Leyendo lista de paquetes... Hecho
Creando árbol de dependencias
Leyendo la información de estado... Hecho
...
Configurando lxc (1:1.0.6-6+deb8u1) ...
Configurando dh-python (1.20141111-2) ...
Procesando disparadores para libc-bin (2.19-18+deb8u1) ...
Procesando disparadores para systemd (215-17+deb8u2) ...
root@lxc:~#
Acto seguido debemos modificar la configuración de red, para que la interfaz de red de la máquina represente la salida de todas las IPs que maneja el bridge y para que el host obtenga una dirección de red en el bridge.
ANTES:
root@lxc:~# cat /etc/network/interfaces
source /etc/network/interfaces.d/*
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
address 192.168.56.4
netmask 255.255.255.0
gateway 192.168.56.1
root@lxc:~#
DESPUES:
root@lxc:~# cat /etc/network/interfaces
source /etc/network/interfaces.d/*
auto lo
iface lo inet loopback
auto lxc0
iface lxc0 inet static
bridge_ports eth0
address 192.168.56.4
netmask 255.255.255.0
gateway 192.168.56.1
root@lxc:~#
En este punto es necesario reconfigurar la red, siendo especialmente importante que eth0 quede sin dirección IP asignada (en mi caso tuve que reiniciar la máquina).
root@lxc:~# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master lxc0 state UP group default qlen 1000
link/ether 08:00:27:e4:0a:60 brd ff:ff:ff:ff:ff:ff
3: lxc0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 08:00:27:e4:0a:60 brd ff:ff:ff:ff:ff:ff
inet 192.168.56.4/24 brd 192.168.56.255 scope global lxc0
valid_lft forever preferred_lft forever
inet6 fe80::a00:27ff:fee4:a60/64 scope link
valid_lft forever preferred_lft forever
root@lxc:~#
El último paso consiste en activar el firewall con unas reglas básicas, para proteger el equipo anfitrión de posibles ataques o intrusiones, dejando solamente el acceso a SSH. Con firehol es posible combinar el demonio knockd para ocultar el puerto tras una secuencia de port knocking; en principio sería suficiente con forzar la entrada SSH por claves RSA.
root@lxc:~# cat /etc/firehol/firehol.conf
interface lxc0 world
policy drop
protection strong
server ssh accept
client all accept
root@lxc:~#
Hay que modificar otro fichero para permitir el inicio del firewall:
ANTES:
root@lxc:~# grep START /etc/default/firehol
#To enable firehol at startup set START_FIREHOL=YES
START_FIREHOL=NO
root@lxc:~#
DESPUES:
root@lxc:~# grep START /etc/default/firehol
#To enable firehol at startup set START_FIREHOL=YES
START_FIREHOL=YES
root@lxc:~#
Y para acabar, (re)iniciamos el servicio firehol.
root@lxc:~# service firehol restart
Broadcast message from systemd-journald@lxc (Wed 2015-10-14 16:59:30 CEST):
FireHOL[620]: Firewall has been stopped. Policy is ACCEPT EVERYTHING!
Message from syslogd@lxc at Oct 14 16:59:30 ...
FireHOL[493]: Firewall has been stopped. Policy is ACCEPT EVERYTHING!
root@lxc:~#
La creación de contenedores pasa por usar las herramientas estándar de la distribución, a lo solo tendremos que modificar algunas configuraciones propias de nuestra red.
Creamos un contenedor webserver como demostración. La primera que se crea es un poco lenta porque hace un debootstrap de una distribución debian estable para crear una cache en /var/cache/lxc; las siguientes se benefician de esta caché y solo la actualizan, acelerando el proceso.
root@lxc:~# lxc-create -n webserver -t debian
debootstrap is /usr/sbin/debootstrap
Checking cache download in /var/cache/lxc/debian/rootfs-jessie-i386 ...
Downloading debian minimal ...
...
I: Base system installed successfully.
Download complete.
Copying rootfs to /var/lib/lxc/webserver/rootfs...
...
Current default time zone: 'Europe/Madrid'
Local time is now: Wed Oct 14 17:26:37 CEST 2015.
Universal Time is now: Wed Oct 14 15:26:37 UTC 2015.
Root password is 'sFj7Jm9N', please change !
root@lxc:~#
Acabada la generación del contenedor, vamos a configurarle algunos parámetros; que tenga una interfaz eth0 activa y enchufada al bridge lxc0, y que el contenedor se autoinicie en cada reinicio del anfitrión.
root@lxc:~# cat /var/lib/lxc/webserver/config
...
lxc.start.auto = 1
lxc.network.type = veth
lxc.network.flags = up
lxc.network.link = lxc0
lxc.network.name = eth0
root@lxc:~#
Y para que su interfaz de red sea funcional, vamos a configurarle una dirección IP. Todo esto se hace en los ficheros habituales, teniendo en cuenta que un contenedor es una jaula, y que esta se encuentra en /var/lib/lxc/webserver/rootfs/
root@lxc:~# cat /var/lib/lxc/webserver/rootfs/etc/network/interfaces
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
address 192.168.56.10
netmask 255.255.255.0
gateway 192.168.56.1
root@lxc:~#
El contenedor ya está funcional, y se puede levantar:
root@lxc:~# lxc-start -n webserver -d
root@lxc:~#
Sin embargo, el firewall impide que se llegue al mismo; tendremos que poner reglas para permitir el flujo de red hacia la nueva dirección IP configurada para el contenedor. Esto se consigue con reglas de forward que entren por el bridge y salgan por el mismo hacia nuestro contenedor.
Ya de paso habilitamos reglas para que todo lo que pase por el bridge hacia internet se permita. Como particularidad de nuestra red, el servidor anfitrión tiene un servidor DNS dnsmasq; así que añadimos también esa ruta.
Por ejemplo, suponiendo que queremos habilitar el servicio SSH (tcp 22) y el puerto del servicio HTTP (tcp 80), pondremos lo siguiente en la configuración del firewall (tras lo cual lo reiniciaremos):
root@lxc:~# cat /etc/firehol/firehol.conf
interface lxc0 world
policy drop
protection strong
server ssh accept
client all accept
router internal inface lxc0 outface lxc0
policy drop
client all accept
group with dst not "${UNROUTABLE_IPS}"
route all accept
group end
group with dst 192.168.56.1
route dns accept
group end
group with dst 192.168.56.10
route ssh accept
route http accept
group end
root@lxc:~# service firehol restart
Broadcast message from systemd-journald@lxc (Wed 2015-10-14 17:43:38 CEST):
FireHOL[8690]: Firewall has been stopped. Policy is ACCEPT EVERYTHING!
Message from syslogd@lxc at Oct 14 17:43:38 ...
FireHOL[8565]: Firewall has been stopped. Policy is ACCEPT EVERYTHING!
root@lxc:~#
Y solamente queda entrar al contenedor, por ejemplo por SSH para instalar lo que se necesite; en este caso con un nginx sería suficiente como demostración.
gerard@workstation:~$ ssh root@192.168.56.10
root@192.168.56.10's password:
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
root@webserver:~# apt-get install nginx-light
Leyendo lista de paquetes... Hecho
Creando árbol de dependencias... Hecho
Se instalarán los siguientes paquetes extras:
nginx-common
Paquetes sugeridos:
fcgiwrap nginx-doc ssl-cert
Se instalarán los siguientes paquetes NUEVOS:
nginx-common nginx-light
0 actualizados, 2 nuevos se instalarán, 0 para eliminar y 0 no actualizados.
Se necesita descargar 439 kB de archivos.
Se utilizarán 1.040 kB de espacio de disco adicional después de esta operación.
¿Desea continuar? [S/n] s
...
Configurando nginx-common (1.6.2-5) ...
Configurando nginx-light (1.6.2-5) ...
Procesando disparadores para systemd (215-17+deb8u2) ...
root@webserver:~#
Y con esto ya tenemos nuestro contenedor en marcha y ofreciendo servicios en nuestra red local de forma segura.