De bricolaje: cortafuegos con OpenBSD y ALIX

Introducción

En la entrada de hoy les proponemos “cacharrear” un poco con OpenBSD, un sabor de UN*X descendiente de 4.4BSD, y ALIX, una placa de bajo consumo fabricada por PC Engines. El objetivo es montar un cortafuegos barato y de bajo consumo.

Ingredientes

La ISO de instalación de OpenBSD 4.9:

$ wget http://mirror.cdmon.com/pub/OpenBSD/4.9/i386/install49.iso

$ sha256sum install49.iso 
5e3f9e961c0f37fd12f2d0719df50cfa9a0fdcac93e337f7bb5a52ea1de0f485  install49.iso

Un placa ALIX 2C3, aunque las instrucciones que siguen deberían funcionar con los demás modelos sin demasiados cambios. El sabor 2C3 lleva una CPU AMD Geode LX800 a 500 MHz, 256 MB de RAM y 3 interfaces Ethernet a 10/100. Además, le podemos pinchar…

… una tarjeta Compact Flash de al menos 512 MB de capacidad. En nuestro caso, hemos utilizado una Kingston de 4 GB.
… un lector/grabador de tarjetas Compact Flash. Hemos utilizado un modelo “todo-en-uno” USB estándar.
… un PC con Linux y qemu, para instalar OpenBSD en la tarjeta. Hemos utilizado un i386 con Debian Squeeze y el qemu de paquete.
… un cable serie (RS-232) “null modem” DB9-DB9 (hembra-hembra), para acceder a la consola de la ALIX desde el PC.

Instalación de OpenBSD en la tarjeta

En primer lugar, pinchamos el lector/grabador Compact Flash en el PC. Con un dmesg, podemos obtener el dispositivo que el núcleo le ha asignado. En nuestro caso, ha sido /dev/sdb:

[ 5887.266604] scsi 3:0:0:0: Direct-Access Generic- Compact Flash 1.00 PQ: 0 ANSI: 0 CCS
[ 5887.272963] scsi 3:0:0:1: Direct-Access Generic- SM/xD-Picture 1.00 PQ: 0 ANSI: 0 CCS
[ 5887.279209] scsi 3:0:0:2: Direct-Access Generic- SD/MMC        1.00 PQ: 0 ANSI: 0 CCS
[ 5887.285457] scsi 3:0:0:3: Direct-Access Generic- MS/MS-Pro     1.00 PQ: 0 ANSI: 0 CCS
[ 5887.286191] sd 3:0:0:0: Attached scsi generic sg2 type 0
[ 5887.286483] sd 3:0:0:1: Attached scsi generic sg3 type 0
[ 5887.286765] sd 3:0:0:2: Attached scsi generic sg4 type 0
[ 5887.287063] sd 3:0:0:3: Attached scsi generic sg5 type 0
[ 5888.309508] sd 3:0:0:2: [sdd] Attached SCSI removable disk
[ 5888.310136] sd 3:0:0:3: [sde] Attached SCSI removable disk
[ 5888.310742] sd 3:0:0:1: [sdc] Attached SCSI removable disk
[ 5888.311131] sd 3:0:0:0: [sdb] 7806960 512-byte logical blocks: (3.99 GB/3.72 GiB)
[ 5888.312523] sd 3:0:0:0: [sdb] Write Protect is off
[ 5888.312533] sd 3:0:0:0: [sdb] Mode Sense: 03 00 00 00
[ 5888.312539] sd 3:0:0:0: [sdb] Assuming drive cache: write through
[ 5888.315375] sd 3:0:0:0: [sdb] Assuming drive cache: write through
[ 5888.315388]  sdb: sdb1
[ 5888.323744] sd 3:0:0:0: [sdb] Assuming drive cache: write through
[ 5888.323754] sd 3:0:0:0: [sdb] Attached SCSI removable disk

A continuación, la idea es lanzar un qemu que use como disco duro el anterior dispositivo, y arranque desde el CD-ROM, que será la ISO de instalación:

$ qemu -boot d -cdrom install49.iso -hda /dev/sdb


El instalador de OpenBSD arranca. Le indicamos que queremos llevar a cabo una nueva instalación:

Utilizamos un teclado español estándar:

Indicamos diversas opciones; a saber:

  • No queremos configurar ningún interfaz de red de momento
  • Utilizamos el nombre de dominio por defecto
  • No configuramos servidores DNS por el momento
  • No configuramos de forma manual la red
  • La clave para root: por ejemplo, una segura como alix ;-)
  • No queremos que se levante el demonio SSH de momento
  • No queremos que se levante el demonio NTP de momento
  • No queremos correr X Window
  • Importante: queremos la consola en COM0, i.e. el puerto serie
  • Importante: 38400 bps es la velocidad por defecto en las ALIX
  • No queremos añadir un usuario sin privilegios al sistema

Utilizamos toda la tarjeta para OpenBSD:

Creamos una única partición con todo el espacio para el sistema de ficheros raíz (el esquema de particionado final se explica más adelante):

Instalaremos únicamente los sets bsd, bsd.rd, base49.tgz y etc49.tgz. Esto nos proporciona un sistema base binario mínimo: sin páginas del manual, sin compiladores, sin juegos y sin X Window.

Una vez los paquetes especificados se han instalado, el sistema nos da un prompt:

Por supuesto, lo vamos a aprovechar ;-) Buen momento para tomar un café antes de seguir :-)

Unos pasos más atrás, creamos una única partición con todo el espacio en la tarjeta para el sistema de ficheros raíz. La idea es montar ese sistema de ficheros en modo sólo lectura, de forma que podamos apagar en frío la ALIX sin que se nos corrompa el filesystem. Además, no es conveniente escribir demasiado en memorias Compact Flash, sobretodo si son baratas: se acaban estropeando.

Pero entonces ¿dónde metemos los datos que el sistema escribe a disco? Montaremos /var en un sistema de ficheros en memoria (de tipo mfs, “memory filesystem”) de 65 MB. El truco es copiar el /var “prototipo” en otro directorio (que hemos llamado /protovar) y que el sistema monte su contenido en memoria en /var durante el inicio. Lo hacemos con los siguientes comandos:

Como se observa, también hemos movido los ficheros de dispositivo de pseudo-terminal en los que el sistema escribe (/dev/?typ?) a /protovar/dev y hemos creado los correspondientes enlaces simbólicos para que el sistema los encuentre utilizando la ruta habitual.

La base de datos de paquetes (/var/db/pkg) la pasamos a /etc para que persista entre reinicios. También pasamos /tmp a /var/tmp, para tener un único punto de montaje donde el sistema escribe.

Al final, /etc/fstab queda como sigue:

/dev/wd0a / ffs rw 1 1
swap /var mfs rw,-P=/protovar,-s=65535 0 0

Es decir, un sistema de ficheros raíz (de momento en modo lectura y escritura) y un sistema de ficheros /var que se monta en un espacio de memoria de 65 MB a partir del contenido de /protovar. Al estar en memoria, los cambios hechos a /var no sobreviven a reinicios. Como se observa, no hemos definido memoria swap, ya que el código de firewalling se ejecuta en el núcleo y no produce swapping. Con los más o menos 200 MB de memoria libres que nos quedan, los procesos en modo usuario no deberían producir intercambio.

Por último, paramos el sistema para sincronizar el filesystem con la tarjeta:

Tareas post instalación

Una vez instalado el operativo en la tarjeta, ya podemos extraerla del lector/grabador y pincharla en la ALIX. Antes de encenderla, conectamos el puerto serie del PC con el de la ALIX con el cable “null modem” y lanzamos un programa de terminal en el PC. Así podremos ver el arranque del sistema. Lanzamos por ejemplo un screen (hay gente que prefiere el minicom) contra /dev/ttyS0 (aka COM0, que es el puerto del PC donde hemos conectado el cable) a 38400 bps:

$ screen /dev/ttyS0 38400

/dev/rwd0a: file system is clean; not checking
mount_mfs: reduced number of fragments per cylinder group from 8184 to 8112 to enlarge 
last cylinder group
setting tty flags
pf enabled
starting network
starting system logger
starting initial daemons:.
savecore: /dev/wd0b: Device not configured
checking quotas: done.
building ps databases: kvm dev.
clearing /tmp
starting pre-securelevel daemons:.
setting kernel security level: kern.securelevel: 0 -> 1
creating runtime link editor directory cache.
preserving editor files.
starting network daemons: sendmail inetd.
starting local daemons:.
standard daemons: cron.
Fri Sep 23 12:01:19 CEST 2011

OpenBSD/i386 (alix.my.domain) (tty00)

login: 

Una vez el sistema ha arrancado, iniciamos sesión como root y llevamos a cabo varias tareas post instalación. En primer lugar, deshabilitamos el demonio inetd; ya que no lo vamos a utilizar, que no chupe recursos:

# pkill inetd
# echo inetd=NO >> /etc/rc.conf.local

Truncamos el fichero /etc/fbtab:

# mv /etc/fbtab /etc/fbtab.orig
# : > /etc/fbtab

Reescribimos /etc/fstab para que el sistema de ficheros raíz se monte en modo sólo lectura al inicio, tal como se ha explicado antes:

# tmp=`tail -1 /etc/fstab`
# echo /dev/wd0a / ffs ro 1 1 > /etc/fstab
# echo $tmp >> /etc/fstab
# tmp=

# cat /etc/fstab
/dev/wd0a / ffs ro 1 1
swap /var mfs rw,-P=/protovar,-s=65535 0 0

Y lo montamos en modo sólo lectura para esta sesión:

# mount -ur /

Importante: a partir de ahora, para llevar a cabo cambios de configuración sobre /etc, habrá que remontar el sistema de ficheros raíz en modo lectura y escritura, de la siguiente forma:

# mount -uw /

Al escribir nuestros cambios, debemos volver a montarlo en modo sólo lectura:

# mount -ur /

Por último, hemos de tener en cuenta que el reloj de la ALIX es impreciso y no dispone de batería. Para solucionar este problema, podemos planificar una tarea en cron que sincronice el reloj cada hora:

58  *  *  *  *  rdate -ncav 1.europe.pool.ntp.org | logger -t rdate

Notar que para que esto funcione, debemos configurar servidores DNS en el sistema. También habría que añadir el anterior comando a /etc/rc.local para que se ejecute durante el arranque.

Configuración de la red

Lo primero es activar el IP forwarding para poder pasar paquetes de un interfaz de red a otro:

# echo net.inet.ip.forwarding=1 >> /etc/sysctl.conf
# sysctl net.inet.ip.forwarding=1
net.inet.ip.forwarding: 0 -> 1

Los interfaces de red son vr0, vr1 y vr2. En nuestro sistema, vr0 se ha asignado al interfaz más cercano al conector de la fuente de alimentación, vr1 al central, y vr2 al que resta. La configuración de los interfaces habrá que realizarla por lo tanto, tal como se explica en la documentación, mediante los ficheros /etc/hostname.vr0, /etc/hostname.vr1, y /etc/hostname.vr2.

Por ejemplo, le ponemos la IP 192.168.1.1/24 a vr0 (interfaz que toca con Internet), y la IP 192.168.2.1/24 a vr1 (interfaz que toca con nuestra red interna. La puerta de enlace (192.168.1.254) se especifica en /etc/mygate:

# mount -uw /
# echo inet 192.168.1.1 255.255.255.0 NONE > /etc/hostname.vr0
# echo inet 192.168.2.1 255.255.255.0 NONE > /etc/hostname.vr1
# echo 192.168.1.254 > /etc/mygate
# sh /etc/netstart
# mount -ur /

# netstat -rn | head
Routing tables

Internet:
Destination        Gateway            Flags   Refs      Use   Mtu  Prio Iface
default            192.168.1.254      GS         0        0     -     8 vr0  
127/8              127.0.0.1          UGRS       0        0 33200     8 lo0  
127.0.0.1          127.0.0.1          UH         0        0 33200     4 lo0  
192.168.1/24       link#1             C          1        0     -     4 vr0  
192.168.1.254      link#1             HLc        1        0     -     4 vr0  
192.168.2/24       link#2             C          0        0     -     4 vr1  

# ifconfig vr0 | grep 'inet '
        inet 192.168.1.1 netmask 0xffffff00 broadcast 192.168.1.255

# ifconfig vr1 | grep 'inet ' 
        inet 192.168.2.1 netmask 0xffffff00 broadcast 192.168.2.255

Firewalling

La configuración de Packet Filter (PF), el motor de firewalling de OpenBSD, está excelentemente documentada en el FAQ de PF y en diversas páginas del manual. Básicamente, las reglas se guardan en /etc/pf.conf. Para cargarlas (tanto si PF está activo como si no), se haría un

# pfctl -f /etc/pf.conf

Para activar el firewall (por defecto ya se activa durante el arranque), un:

# pfctl -e

y para desactivarlo (no descarga las reglas), un:

# pfctl -d

El siguiente conjunto de reglas básico, basado en el del FAQ, bloquea(y registra) todo el tráfico excepto el proveniente de la red interna, el SSH a la propia ALIX y los ICMP echo request.

# interfaz que conecta con Internet
ext_if="vr0"
# interfaz que conecta con la red interna
int_if="vr1"

# no aplicar filtros sobre loopback
set skip on lo

# por defecto, bloquear y registrar todo lo entrante
block in log
# si ya ha entrado, permitir que salga
pass out quick

# antispoofing para la interna y la loopback
antispoof quick for { lo $int_if }

# permitir la conexion por SSH a la propia ALIX
pass in on egress inet proto tcp from any to (egress) \
    port 22

# permitir ICMP echo request
pass in inet proto icmp all icmp-type echoreq

# permitir todo lo que llegue desde la interna
pass in on $int_if

Otras opciones

Distribuciones como m0n0wall o pfSense (ambas basadas en FreeBSD) soportan placas ALIX y permiten montar un cortafuegos con interfaz web (entre otras cosas) de forma cómoda y rápida. También existen “embebedores” de OpenBSD en tarjetas flash como flashdist o flashrd.

Comments

  1. Excelente post, muy chulo! Un motivo más para ponerme un día a trastear con *bsd.

  2. Mola¡