Envoyé par unreal
Introduction et objectifs
Les Jails FreeBSD permettent de réaliser une virtualisation légère afin d'héberger plusieurs environnements indépendants sur une seule machine physique. On parle de virtualisation "légère" parce qu'un seul kernel est démarré ; la "virtualisation" ne concerne donc qu'un espace userland dédié à chaque environnement qui partage ainsi certaines ressources avec le système hôte (espace disque chrooté, dev, accès réseau, etc). Cela permet en principe d'héberger un grand nombre d'environnements, car il n'y a pas l'overhead lié au démarrage d'un système complet, mais impose certaines limitations : le système virtualisé sera FreeBSD (il est toutefois possible d'héberger d'autres système), et de préférence de même version que la machine hôte.
Cet article a comme but de configurer un système virtualisé prêt pour la production avec les caractéristiques suivantes :
Avant de commencer
Avant d'attaquer le vif du sujet, je vous propose un peu de lecture :
Environnement
Nous allons utiliser l'environnement suivant, représenté par le schéma ci-dessous :
Création de Jails
La documentation officielle propose un tutorial intéressant, que j'ai suivi avec les changements suivants :
Ce qui donne l'arborescence suivante pour mes trois jails "adm", "mail" et "www" :
Niveau /etc/fstab cela donne :
Nous utilisons nullfs pour monter les espaces RO et RW vers les espaces des jails, ainsi il ne sera pas possible de modifier les options de montage depuis l'intérieur des Jails. Les montages /usr/ports permettent aux jails d'accéder à l'arborescence des ports sans consommer d'espace disque supplémentaire.
Les adresses IP des Jails se configurent depuis /etc/rc.conf. Nous avons choisi de configurer des IPv4 et IPv6 privées pour chaque jail :
Pour finir, il est nécessaire de modifier les /etc/make.conf de chaque jail pour réaliser les builds sur l'espace RW :
Pour plus de simplicité, je vous conseille à se stade de redémarrer la machine pour ne pas créer l'interface lo1 et ses alias à la main. Après le reboot, vous pouvez vérifier que les jails sont bien démarrés :
Par ailleurs, ifconfig permet de vérifier que les différentes IP sont configurées :
Les jails sont donc bien démarrés, mais sans accès réseau pour l'instant.
Contrôle des accès réseau avec Packet Filter
Je vous propose quelques rappels sur le fonctionnement de PF :
Il en découle les conséquences suivantes :
Ci-dessous un exemple de configuration PF avec des commentaires :
Astuces et mot de fin
En suivant ces conseils, vous avez une plateforme d'hébergement très sécurisée :
Historique
20111228 - version initiale
Les Jails FreeBSD permettent de réaliser une virtualisation légère afin d'héberger plusieurs environnements indépendants sur une seule machine physique. On parle de virtualisation "légère" parce qu'un seul kernel est démarré ; la "virtualisation" ne concerne donc qu'un espace userland dédié à chaque environnement qui partage ainsi certaines ressources avec le système hôte (espace disque chrooté, dev, accès réseau, etc). Cela permet en principe d'héberger un grand nombre d'environnements, car il n'y a pas l'overhead lié au démarrage d'un système complet, mais impose certaines limitations : le système virtualisé sera FreeBSD (il est toutefois possible d'héberger d'autres système), et de préférence de même version que la machine hôte.
Cet article a comme but de configurer un système virtualisé prêt pour la production avec les caractéristiques suivantes :
- Séparation des rôles ; chaque jail réalisera un nombre réduit de tâches
- Les services hébergés devront être accessibles en IPv4 et IPv6 (dual stack)
- Les accès réseau des jails seront fortement restreints
- Le système hôte disposera d'une IPv4 publique et d'une IPv6 publique ; les jails auront des IP privées.
- L'essentiel du système de fichiers sera en lecture seule depuis l'intérieur des jails
- La création de nouveaux jails doit être simple, à base de modèles
Avant de commencer
Avant d'attaquer le vif du sujet, je vous propose un peu de lecture :
- La documentation officielle Packet Filter
- La documentation officielle de FreeBSD sur les jails
- Gérer un serveur distant sous FreeBSD
Environnement
Nous allons utiliser l'environnement suivant, représenté par le schéma ci-dessous :
- FreeBSD 9.0
- Interface réseau publique : ue0
- Interface réseau jails : lo1
- 3 jails : adm (DNS, Proxy), mail, www
Création de Jails
La documentation officielle propose un tutorial intéressant, que j'ai suivi avec les changements suivants :
- Les jails sont dans /home/jails
- Le template se trouve dans /home/jails/skel
- L'install lecture seule se trouve dans /home/jails/mroot
- Chaque jail a son point de montage RO dans /home/jails/nomdejail et son espace RW dans /home/jails/nomdejail_rw
- Le montage de l'espace RW se fait dans /home/jails/nomdejail/data
Ce qui donne l'arborescence suivante pour mes trois jails "adm", "mail" et "www" :
# ls -l /home/jails/
total 32
drwxr-xr-x 14 root wheel 512 Dec 14 17:46 adm
drwxr-xr-x 11 root wheel 512 Dec 14 22:47 adm_rw
drwxr-xr-x 14 root wheel 512 Dec 14 17:46 mail
drwxr-xr-x 11 root wheel 512 Dec 14 22:47 mail_rw
drwxr-xr-x 14 root wheel 512 Dec 14 17:46 mroot
drwxr-xr-x 11 root wheel 512 Dec 14 22:47 skel
drwxr-xr-x 14 root wheel 512 Dec 14 17:46 www
drwxr-xr-x 11 root wheel 512 Dec 14 22:47 www_rw
total 32
drwxr-xr-x 14 root wheel 512 Dec 14 17:46 adm
drwxr-xr-x 11 root wheel 512 Dec 14 22:47 adm_rw
drwxr-xr-x 14 root wheel 512 Dec 14 17:46 mail
drwxr-xr-x 11 root wheel 512 Dec 14 22:47 mail_rw
drwxr-xr-x 14 root wheel 512 Dec 14 17:46 mroot
drwxr-xr-x 11 root wheel 512 Dec 14 22:47 skel
drwxr-xr-x 14 root wheel 512 Dec 14 17:46 www
drwxr-xr-x 11 root wheel 512 Dec 14 22:47 www_rw
Niveau /etc/fstab cela donne :
/home/jails/mroot /home/jails/adm nullfs ro 0 0
/home/jails/adm_rw /home/jails/adm/data nullfs rw 0 0
/usr/ports /home/jails/adm/usr/ports nullfs ro 0 0
/home/jails/mroot /home/jails/mail nullfs ro 0 0
/home/jails/mail_rw /home/jails/mail/data nullfs rw 0 0
/usr/ports /home/jails/mail/usr/ports nullfs ro 0 0
/home/jails/mroot /home/jails/www nullfs ro 0 0
/home/jails/www_rw /home/jails/www/data nullfs rw 0 0
/usr/ports /home/jails/www/usr/ports nullfs ro 0 0
/home/jails/adm_rw /home/jails/adm/data nullfs rw 0 0
/usr/ports /home/jails/adm/usr/ports nullfs ro 0 0
/home/jails/mroot /home/jails/mail nullfs ro 0 0
/home/jails/mail_rw /home/jails/mail/data nullfs rw 0 0
/usr/ports /home/jails/mail/usr/ports nullfs ro 0 0
/home/jails/mroot /home/jails/www nullfs ro 0 0
/home/jails/www_rw /home/jails/www/data nullfs rw 0 0
/usr/ports /home/jails/www/usr/ports nullfs ro 0 0
Nous utilisons nullfs pour monter les espaces RO et RW vers les espaces des jails, ainsi il ne sera pas possible de modifier les options de montage depuis l'intérieur des Jails. Les montages /usr/ports permettent aux jails d'accéder à l'arborescence des ports sans consommer d'espace disque supplémentaire.
Les adresses IP des Jails se configurent depuis /etc/rc.conf. Nous avons choisi de configurer des IPv4 et IPv6 privées pour chaque jail :
ifconfig_ue0="DHCP"
# IPv6 stuff
ifconfig_ue0_ipv6="inet6 accept_rtadv"
ipv6_defaultrouter="<ipv6 du routeur>"
# Jails stuff
cloned_interfaces="lo1"
ifconfig_lo1="inet 192.168.204.254 netmask 255.255.255.0"
ifconfig_lo1_ipv6="inet6 fec0::204:254 prefixlen 64"
ifconfig_lo1_alias0="inet 192.168.204.1 netmask 255.255.255.0"
ifconfig_lo1_alias1="inet 192.168.204.2 netmask 255.255.255.0"
ifconfig_lo1_alias2="inet 192.168.204.3 netmask 255.255.255.0"
ifconfig_lo1_alias3="inet6 fec0::204:1 prefixlen 64"
ifconfig_lo1_alias4="inet6 fec0::204:2 prefixlen 64"
ifconfig_lo1_alias5="inet6 fec0::204:3 prefixlen 64"
jail_enable="YES" # Set to NO to disable starting of any jails
jail_list="adm mail www"
jail_sysvipc_allow="YES"
# Adm
jail_adm_rootdir="/usr/home/jails/adm" # jail's root directory
jail_adm_hostname="adm" # jail's hostname
jail_adm_ip="192.168.204.1,fec0::204:1" # jail's IP address
jail_adm_devfs_enable="YES" # mount devfs in the jail
jail_adm_devfs_ruleset="devfsrules_jail" # devfs ruleset to apply to jail
# Mail
jail_mail_rootdir="/usr/home/jails/mail"
jail_mail_hostname="mail"
jail_mail_ip="192.168.204.2,fec0::204:2"
jail_mail_devfs_enable="YES"
jail_mail_devfs_ruleset="devfsrules_jail"
# Web
jail_www_rootdir="/usr/home/jails/www"
jail_www_hostname="www"
jail_www_ip="192.168.204.3,fec0::204:3"
jail_www_devfs_enable="YES"
jail_www_devfs_ruleset="devfsrules_jail"
# IPv6 stuff
ifconfig_ue0_ipv6="inet6 accept_rtadv"
ipv6_defaultrouter="<ipv6 du routeur>"
# Jails stuff
cloned_interfaces="lo1"
ifconfig_lo1="inet 192.168.204.254 netmask 255.255.255.0"
ifconfig_lo1_ipv6="inet6 fec0::204:254 prefixlen 64"
ifconfig_lo1_alias0="inet 192.168.204.1 netmask 255.255.255.0"
ifconfig_lo1_alias1="inet 192.168.204.2 netmask 255.255.255.0"
ifconfig_lo1_alias2="inet 192.168.204.3 netmask 255.255.255.0"
ifconfig_lo1_alias3="inet6 fec0::204:1 prefixlen 64"
ifconfig_lo1_alias4="inet6 fec0::204:2 prefixlen 64"
ifconfig_lo1_alias5="inet6 fec0::204:3 prefixlen 64"
jail_enable="YES" # Set to NO to disable starting of any jails
jail_list="adm mail www"
jail_sysvipc_allow="YES"
# Adm
jail_adm_rootdir="/usr/home/jails/adm" # jail's root directory
jail_adm_hostname="adm" # jail's hostname
jail_adm_ip="192.168.204.1,fec0::204:1" # jail's IP address
jail_adm_devfs_enable="YES" # mount devfs in the jail
jail_adm_devfs_ruleset="devfsrules_jail" # devfs ruleset to apply to jail
jail_mail_rootdir="/usr/home/jails/mail"
jail_mail_hostname="mail"
jail_mail_ip="192.168.204.2,fec0::204:2"
jail_mail_devfs_enable="YES"
jail_mail_devfs_ruleset="devfsrules_jail"
# Web
jail_www_rootdir="/usr/home/jails/www"
jail_www_hostname="www"
jail_www_ip="192.168.204.3,fec0::204:3"
jail_www_devfs_enable="YES"
jail_www_devfs_ruleset="devfsrules_jail"
Pour finir, il est nécessaire de modifier les /etc/make.conf de chaque jail pour réaliser les builds sur l'espace RW :
WRKDIRPREFIX=/data/portbuild
DISTDIR=/data/distfiles
DISTDIR=/data/distfiles
Pour plus de simplicité, je vous conseille à se stade de redémarrer la machine pour ne pas créer l'interface lo1 et ses alias à la main. Après le reboot, vous pouvez vérifier que les jails sont bien démarrés :
# jls
JID IP Address Hostname Path
1 192.168.204.1 adm /usr/home/jails/adm
2 192.168.204.2 mail /usr/home/jails/mail
3 192.168.204.3 www /usr/home/jails/www
JID IP Address Hostname Path
1 192.168.204.1 adm /usr/home/jails/adm
2 192.168.204.2 mail /usr/home/jails/mail
3 192.168.204.3 www /usr/home/jails/www
Par ailleurs, ifconfig permet de vérifier que les différentes IP sont configurées :
# ifconfig lo1
lo1: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384
options=3<RXCSUM,TXCSUM>
inet 192.168.204.254 netmask 0xffffff00
inet6 fec0::204:254 prefixlen 64
inet 192.168.204.1 netmask 0xffffff00
inet 192.168.204.2 netmask 0xffffff00
inet 192.168.204.3 netmask 0xffffff00
inet6 fec0::204:1 prefixlen 64
inet6 fec0::204:2 prefixlen 64
inet6 fec0::204:3 prefixlen 64
nd6 options=21<PERFORMNUD,AUTO_LINKLOCAL>
lo1: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384
options=3<RXCSUM,TXCSUM>
inet 192.168.204.254 netmask 0xffffff00
inet6 fec0::204:254 prefixlen 64
inet 192.168.204.1 netmask 0xffffff00
inet 192.168.204.2 netmask 0xffffff00
inet 192.168.204.3 netmask 0xffffff00
inet6 fec0::204:1 prefixlen 64
inet6 fec0::204:2 prefixlen 64
inet6 fec0::204:3 prefixlen 64
nd6 options=21<PERFORMNUD,AUTO_LINKLOCAL>
Les jails sont donc bien démarrés, mais sans accès réseau pour l'instant.
Contrôle des accès réseau avec Packet Filter
Je vous propose quelques rappels sur le fonctionnement de PF :
- Les opérations de redirection (divert-to, route-to...) se font à l'entrée de l'interface, pas en sortie
- Les opérations de translation se font en sortie d'interface
- Les opérations de translation se font avant les opérations de filtrage en sortie d'interface (ce qui signifie que le filtre ne peut voir que les IP translatées)
Il en découle les conséquences suivantes :
- Il ne sera pas possible proxifier de façon transparente le trafic sortant des jails
- Il faut utiliser les règles NAT pour filtrer le trafic sortant
Ci-dessous un exemple de configuration PF avec des commentaires :
############################################################
# PF config for jailed FreeBSD #
# #
# Last update: 20111026 #
############################################################
############################################################
# Définitions d'interface
ext_if = "ue0"
int_if = "lo1"
# Il est nécessaire de spécifier l'IPv6 de translation, pour ne pas que PF prenne le Local Link par erreur.
ext_ipv6 = "<IPv6 extérieure>"
jail_network = $int_if:network
jail_host_adm = "192.168.204.1"
jail_host_adm_v6 = "fec0::204:1"
jail_host_mail = "192.168.204.2"
jail_host_mail_v6 = "fec0::204:2"
jail_host_www = "192.168.204.3"
jail_host_www_v6 = "fec0::204:3"
# Configuration
set limit states 100000
set timeout tcp.established 86400
set timeout tcp.finwait 30
set skip on lo0
# Par défaut envoyer un TCP reset ou message ICMP en cas de drop
set block-policy return
############################################################
### Tables ###
# Comme on ne peut pas proxifier le FTP, il faut faire une liste blanche
# 87.51.34.132, 149.20.64.73, 204.152.184.73 = ftp.freebsd.org
# 131.111.8.80 = ftp.csx.cam.ac.uk
# 140.186.70.20 = ftp.gnu.org
# 130.133.3.130 = ftp.fu-berlin.de
table <ports_ftp_ips> {
87.51.34.132, \
149.20.64.73, \
204.152.184.73, \
\
131.111.8.80, \
\
140.186.70.20, \
\
130.133.3.130
}
### Normaliser les paquets ###
scrub in on $ext_if all
# La translation se fait avant le filtrage, donc nous utilisons la translation pour contrôler les accès vers l'extérieur
# Tous les jails ont un accès FTP vers la liste blanche
nat on $ext_if inet proto tcp from $jail_network to <ports_ftp_ips> port { ftp, >1023 } -> ($ext_if)
### 'Adm' jail ###
# 'Adm' (proxy) est seul à avoir un accès HTTP
nat on $ext_if inet proto tcp from $jail_host_adm to any port { 80, 443 } -> ($ext_if)
nat on $ext_if inet6 proto tcp from $jail_host_adm_v6 to any port { 80, 443 } -> $ext_ipv6
# 'Adm' est seul avoir accès DNS
nat on $ext_if inet proto { tcp, udp } from $jail_host_adm to any port 53 -> ($ext_if)
nat on $ext_if inet6 proto { tcp, udp } from $jail_host_adm_v6 to any port 53 -> $ext_ipv6
### 'Mail' jail ###
# 'Mail' est seul à pouvoir envoyer des mails
nat on $ext_if inet proto tcp from $jail_host_mail to any port 25 -> ($ext_if)
nat on $ext_if inet6 proto tcp from $jail_host_mail_v6 to any port 25 -> $ext_ipv6
### "Www" jail ###
# N'a pas d'accès
# Translation de ports
# Web vers www
rdr on $ext_if proto tcp from any to any port { 80, 443 } -> $jail_host_www
rdr on $ext_if inet6 proto tcp from any to any port { 80, 443 } -> $jail_host_www_v6
# Tout ce qui veut sortir mais qui n'a pas été tranlaté est dropé
block out quick log on $ext_if inet proto tcp from $jail_network
block out quick log on $ext_if inet6 proto tcp from $jail_network
block out quick log on $ext_if from $jail_network
# PF config for jailed FreeBSD #
# #
# Last update: 20111026 #
############################################################
############################################################
# Définitions d'interface
ext_if = "ue0"
int_if = "lo1"
# Il est nécessaire de spécifier l'IPv6 de translation, pour ne pas que PF prenne le Local Link par erreur.
ext_ipv6 = "<IPv6 extérieure>"
jail_network = $int_if:network
jail_host_adm = "192.168.204.1"
jail_host_adm_v6 = "fec0::204:1"
jail_host_mail = "192.168.204.2"
jail_host_mail_v6 = "fec0::204:2"
jail_host_www = "192.168.204.3"
jail_host_www_v6 = "fec0::204:3"
# Configuration
set limit states 100000
set timeout tcp.established 86400
set timeout tcp.finwait 30
set skip on lo0
# Par défaut envoyer un TCP reset ou message ICMP en cas de drop
set block-policy return
############################################################
### Tables ###
# Comme on ne peut pas proxifier le FTP, il faut faire une liste blanche
# 87.51.34.132, 149.20.64.73, 204.152.184.73 = ftp.freebsd.org
# 131.111.8.80 = ftp.csx.cam.ac.uk
# 140.186.70.20 = ftp.gnu.org
# 130.133.3.130 = ftp.fu-berlin.de
table <ports_ftp_ips> {
87.51.34.132, \
149.20.64.73, \
204.152.184.73, \
\
131.111.8.80, \
\
140.186.70.20, \
\
130.133.3.130
}
### Normaliser les paquets ###
scrub in on $ext_if all
# La translation se fait avant le filtrage, donc nous utilisons la translation pour contrôler les accès vers l'extérieur
# Tous les jails ont un accès FTP vers la liste blanche
nat on $ext_if inet proto tcp from $jail_network to <ports_ftp_ips> port { ftp, >1023 } -> ($ext_if)
### 'Adm' jail ###
# 'Adm' (proxy) est seul à avoir un accès HTTP
nat on $ext_if inet proto tcp from $jail_host_adm to any port { 80, 443 } -> ($ext_if)
nat on $ext_if inet6 proto tcp from $jail_host_adm_v6 to any port { 80, 443 } -> $ext_ipv6
# 'Adm' est seul avoir accès DNS
nat on $ext_if inet proto { tcp, udp } from $jail_host_adm to any port 53 -> ($ext_if)
nat on $ext_if inet6 proto { tcp, udp } from $jail_host_adm_v6 to any port 53 -> $ext_ipv6
### 'Mail' jail ###
# 'Mail' est seul à pouvoir envoyer des mails
nat on $ext_if inet proto tcp from $jail_host_mail to any port 25 -> ($ext_if)
nat on $ext_if inet6 proto tcp from $jail_host_mail_v6 to any port 25 -> $ext_ipv6
### "Www" jail ###
# N'a pas d'accès
# Translation de ports
# Web vers www
rdr on $ext_if proto tcp from any to any port { 80, 443 } -> $jail_host_www
rdr on $ext_if inet6 proto tcp from any to any port { 80, 443 } -> $jail_host_www_v6
# Tout ce qui veut sortir mais qui n'a pas été tranlaté est dropé
block out quick log on $ext_if inet proto tcp from $jail_network
block out quick log on $ext_if inet6 proto tcp from $jail_network
block out quick log on $ext_if from $jail_network
Astuces et mot de fin
- Comme le jail 'Mail' est seul autorisé à relayer vers l'extérieur, il faut configurer les serveurs d'email locaux à utiliser Mail comme smarthost.
- Exportez la variable d'environnement http_proxy pour utiliser 'Adm' comme proxy Web
- Editez /etc/resolv.conf pour utiliser 'Adm' comme serveur DNS
En suivant ces conseils, vous avez une plateforme d'hébergement très sécurisée :
- Un jail compromis n'a pas la possibilité de contaminer d'autres jails ou de modifier la partie lecture seule de son espace disque
- Un jail compromis ne peut difficilement émettre du trafic non désirable sur le réseau (spam, flood) car ce trafic est bloqué et loggé par Packet Filter
- Un utilisateur indélicat ne pourra lancer des services en écoute sur le réseau car les jails sont en IP privée
- Un jail ne peut charger de module kernel ou passer les interfaces en mode 'promiscuous'
Historique
20111228 - version initiale
Posté le 28/12/11 à 11:51