Securing FreeBSD step by step (for Dummies and even Geeks) cns at minithins.net Last Update > 23/12/2003 > fix login.conf, huge grammatical clean up "A physician, a civil engineer, and a computer scientist were arguing about what was the oldest profession in the world. The physician remarked, "Well, in the Bible, it says that God created Eve from a rib taken out of Adam. This clearly required surgery, and so I can rightly claim that mine is the oldest profession in the world." The civil engineer interrupted, and said, "But even earlier in the book of Genesis, it states that God created the order of the heavens and the earth from out of the chaos. This was the first and certainly the most spectacular application of civil engineering. Therefore, fair doctor, you are wrong : mine is the oldest profession in the world." The computer scientist leaned back in her chair, smiled, and then said confidently, "Ah, but who do you think created the chaos ?" Résumé Ce paper est issu comme beaucoup d'initiatives libres d'un besoin de la part des auteurs. Dans cet article nous souhaitons faire partager ce que nous avons traversé afin d'obtenir une machine FreeBSD configurée au mieux pour résister a toutes sortes de menaces. C'est une sorte de compilation des connaissances disparates dont nous avons nous même eu besoin. Comme le faisait remarquer Bruce Scheiner, la sécurité est une processus, pas un produit ; c'est pourquoi nous tentons dans ce document d'aborder un large panel de sujets et d'utilisations en nous basant sur la branche FreeBSD 4.x-STABLE. 1. Burn out ! 1.1. services 1.2. CVSup 1.3. (re)compilation et update 2. Tuning système 2.1. sysctl 2.1.1. securelevel et chflags 2.1.2. performances 2.2. gestion utilisateurs 2.2.1. adduser / rmuser / chpass / watch 2.2.2. quotas et login.conf 2.2.3. jail 2.3. intégrité 2.4. secure shell 2.5. syslog 2.6. cron 2.7. ipfw et natd 3. Outils 3.1. TCPdump 3.2. Nessus 3.3. lsof 3.4. stack smashing 3.5. tunneling 4. Conclusion 1. burn out ! Nous allons partir du fait que vous avez réussi à installer correctement FreeBSD, et que vous êtes parvenu à une connexion Internet stable. Nous ne donnerons donc aucune indication quant à ces phases. Dans ce chapitre, nous nous concentrerons sur la configuration de base de FreeBSD, c'est-à-dire les premières mesures que vous appliquerez dans une optique de sécurité, peu après votre installation réussie. 1.1. services Inetd est un super daemon qui permet de lancer plusieurs services reseau ainsi qu'une partie de leur configuration comme ftpd, smptd ou telnetd. Le fichier de configuration pour inetd est conservé dans /etc/inetd.conf. En voici un extrait : ftp stream tcp nowait root /usr/libexec/ftpd ftpd -l #shell stream tcp nowait root /usr/libexec/rshd rshd En règle générale, on place le caractere '#' devant une ligne que nous ne voulons pas afin de la mettre en commentaire. Si nous ne desirons offrir aucun de ces services - il est preferable de supprimer cette configuration de base laxiste afin de repartir de zero - nous pouvons retirer inetd de notre fichier de demarrage pour augmenter encore un peu la sécurité et la convivialité. Si par ailleurs vous desirez tout de meme offrir un shell distant à quelques utilisateurs, un chapitre entier couvre la configuration du sshd d'OpenSSH. Enfin, en éclipsant inetd, nous décidons d'abandonner également TCP Wrappers utilisé par défaut sous FreeBSD. Tout d'abord il est utile de vérifier quel service tourne en écoute sur un port actuellement. Pour cela nous allons utiliser l'utilitaire netstat qui affiche une liste des ports et connexions actives. Nous l'allions à grep pour préciser notre recherche. # netstat -a | grep 'listen' Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State tcp4 0 0 *:ssh *:* LISTEN tcp4 0 0 *:ftp *:* LISTEN tcp4 0 0 *:smtp *:* LISTEN udp4 0 0 *:portmap *:* LISTEN Comme vous le voyez nous ne croulons pas sous les services. Mais si votre machine est destinée à devenir un serveur avec de multiples services, je vous recommande de suivre notre solution. Bref, sur le même schéma que précédemment, tous les services réseau que vous voudrez proposer à l'avenir tourneront sous la forme de stand alone daemon, c'est-à-dire des daemons autonomes augmentant la sécurité mais aussi simplifiant la configuration et améliorant la rapidité de réponse des services en éliminant l'intermédiaire inetd. Pour éviter qu'inetd ne se lance au démarrage, nous éditons le fichier /etc/rc.conf après avoir effectué un rapide grep permettant de savoir si nous avons effectivement besoin d'éditer : # grep inetd /etc/rc.conf inetd_enable="YES" inetd_flags="-wW" inetd est donc lancé au démarrage avec par ailleurs les options -wW signifiant la capacité de filtrage des services internes et externes TCP via TCP Wrappers que nous n'utiliseront pas non plus. Donc, toujours dans rc.conf, inetd_enable="YES" devient inetd_enable="NO" et on met inetd_flags="-wW" en commentaire. Nous désactivons également portmapper, un outil extrêmement pratique dans le cadre de services RPC tels que NFS mais qui présentent un nombre incalculable de vulnérabilités. Nous transformons donc la ligne portmap_enable="YES" en NO. Ainsi inetd et portmapper ne seront pas exécutés la prochaine fois que vous redémarrerez. Si vous voulez killer inetd de suite, vous pouvez faire : # killall inetd Notez que c'est également dans rc.conf que vous pourrez configurer les programmes ou scripts à lancer dès le démarrage. Selon le destin de votre machine, ça peut être une bonne idée de manipuler les champs portmap_enable, named_enable ou sendmail_enable. 1.2 CVSup Le meilleur moyen d'obtenir un système sécurisé - en plus du fait d'avoir une configuration bétonnée et de connaître FreeBSD sur le bout des doigts bien sûr - reste de conserver les sources système et l'arborescence des ports à jour. Ainsi dès qu'une vulnérabilité ou un quelconque problème apparaît au sein du système ou encore qu'une nouvelle release ou plutôt qu'une nouvelle version stable est sortie, vous maintenez votre système à jour et donc protégé au mieux. Une bonne idée consécutive à l'update de votre système consiste à s'abonner a la liste freebsd-security afin d'être tenu au courant des derniers patchs. Notez également la liste freebsd-questions en français utile à tous les utilisateurs francophones. Consultez les pages suivantes : http://lists.freebsd.org/mailman/listinfo/freebsd-security-notifications http://www.freebsd-fr.org/local-fr/www/spec/support/liste_diffusion.html Maintenant nous allons voir un aspect extrêmement séduisant de la famille *BSD : la mise à jour complète du système par le net. Pour cela nous utiliserons tout d'abord un utilitaire nomme CVSup. Il permet l'update simple de collections de fichiers à travers un réseau. Il peut efficacement et précisément mirrorer tous types de fichiers incluant sources, binaires, hard links, symlinks, et même des noeuds de périphériques. Le protocole de communication en streaming et l'architecture multithreads font très probablement de lui l'outil de mirroring le plus performant existant à ce jour. En plus de toutes ces qualités, CVSup inclut des usages et des optimisations spécifiquement conçues en fonction des repositories CVS. En d'autres termes, CVSup se relie à la base de données principale de code source FreeBSD et met à jour les fichiers source qui ont été modifié. Nous effectuons donc un simple : # cd /usr/ports/net/cvsup-bin && make install clean en tant que root afin d'installer cet utilitaire. Nous devons maintenant editer le cvs-upfile duquel cvsup recevra ses directives pour l'update. # mkdir /usr/share/cvsup/ # cp /usr/share/examples/cvsup/ports-supfile /usr/share/cvsup/ # cp /usr/share/examples/cvsup/doc-supfile /usr/share/cvsup/ # cp /usr/share/examples/cvsup/cvs-supfile /usr/share/cvsup/ # ee /usr/share/cvsup/cvs-supfile ------------------------------------ SNiP ------------------------------------- # nous allons rentrer une série de paramètres par défaut que nous utiliserons # a chaque invocation de cvsup. # nous rentrons ici le serveurs cvsup que nous voulons utiliser. Vous pouvez # en choisir sur la liste presente sur # http://www.freebsd.org/handbook/mirrors.html *default host=cvsup.fr.FreeBSD.org # ici nous informons cvsup du repertoire ou stocker les fichiers transferes # plus quelques autres informations notamment celle de faire le menage apres # download ou encore quel version nous desirons obtenir grace au champ Tag qui # peut correspondre aussi bien a la version stable que current ou une version # precedente. Enfin nous activons la compression de part notre faible bande # passante. *default base=/usr *default prefix=/dist *default release=cvs tag=RELENG_4 *default delete use-rel-suffix *default compress # ici nous decidons de mettre a jour l'ensemble de l'arborescence. # Notez que les sources des programmes soumis a des limitations d'exportation # (crypto) ne seront pas mis a jour. src-all ------------------------------------ SNiP ------------------------------------- Pareil pour les ports ... ------------------------------------ SNiP ------------------------------------- # nous devons maintenant selectionner les ports que nous desirons mettre a jour. # La collection de ports FreeBSD vous offre une multitude de programmes simples # a installer et optimiser pour FreeBSD, cependant certains vous paraitront # d'une utilite douteuse d'ou notre choix au cas par cas et la mise en # commentaire de : ports-all #ports-archivers #ports-astro ports-audio ports-base #ports-benchmarks #ports-biology #ports-cad #ports-chinese #ports-comms #ports-converters ports-databases ports-deskutils ports-devel ports-editors ports-emulators ports-ftp ports-french #ports-games #ports-german ports-graphics ports-irc #ports-japanese ports-java #ports-korean ports-lang ports-mail #ports-math #ports-mbone ports-misc ports-net ports-news ports-palm ports-picobsd ports-print #ports-russian ports-security ports-shells ports-sysutils ports-textproc #ports-vietnamese ports-www # et on ne sait jamais, des fois que l'appel du graphisme soit trop fort... ports-x11 ports-x11-clocks ports-x11-fm ports-x11-fonts ports-x11-servers ports-x11-toolkits ports-x11-wm ------------------------------------ SNiP ------------------------------------- ... puis la doc. ------------------------------------ SNiP ------------------------------------- # pour finir nous decidons de profiter de l'excellent travail de documentation # issu du FreeBSD Documentation Project qui comporte notamment le FreeBSD # handbook, veritable bible de cet OS et même traduit en francais :) doc-all ------------------------------------ SNiP ------------------------------------- Nous en profitons également pour éditer le fichier /etc/make.conf afin de nous assurer de la presence de certaines variables. # cp /etc/default/make.conf /etc/ # ee /etc/make.conf ------------------------------------ SNiP ------------------------------------- INSTALL=install -C -S -s PPP_NOSUID= true ENABLE_SUID_SSH= true ENABLE_SUID_NEWGRP= true NO_FORTRAN= true # do not build g77 and related libraries NO_I4B= true # do not build isdn4bsd package NO_IPFILTER= true # do not build IP Filter package NO_KERBEROS= true # do not build and install Kerberos 5 (KTH Heimdal) NO_OBJC= true # do not build Objective C support NO_SENDMAIL= true # do not build sendmail and related programs NOGAMES= true # do not build games (games/ subdir) COMPAT1X= no COMPAT20= yes COMPAT21= yes COMPAT22= yes COMPAT3X= yes MAKE_RSAINTL= yes # RSA (public key exchange) USA_RESIDENT= no SUP_UPDATE= yes SUP= /usr/local/bin/cvsup SUPFLAGS= -g -L 2 SUPFILE= /usr/share/cvsup/cvs-supfile PORTSSUPFILE= /usr/share/cvsup/ports-supfile DOCSUPFILE= /usr/share/cvsup/doc-supfile ------------------------------------ SNiP ------------------------------------- Vous pouvez vous amuser avec les nombreux exemples de supfile et de refuse disponibles dans le répertoire examples, puis, une fois votre paramétrage effectué, il ne vous reste plus qu'à lancer une update : # cd /usr/src && make update Notez bien que la totalité du système est mis à jour (ou tout du moins les sources si NO_DOCUPDATE et NO_PORTSUPDATE sont mis à "yes"). SUPFILE, DOCSUPFILE et PORTSSUPFILE permet simplement de filtrer les modules qui seront mis à jour pour les sources, la documentation et le ports tree, respectivement. 1.3. (re)compilation et update Maintenant que nous avons une arborescence des sources et de la port collection correctement mis à jour, il ne nous reste plus (sic) qu'à 'construire' cette arborescence. Tout d'abord nous allons réellement construire l'ensemble des sources de notre systeme de base : # cd /usr/src && make buildworld Cette opération assez importante peut durer plusieurs heures. Sur un Celeron 400, il aura fallu un peu plus d'une heure et demie. Bref le nombre de verres que vous prendrez dépendra de la puissance de votre machine. Nous nous attelons maintenant à la configuration et la compilation du kernel. Pour les configurations ultérieures et des performances optimales, nous vous recommandons de sélectionner les options suivantes dans votre fichier de configuration kernel situé dans le répertoire /sys/votre_architecture/conf. Afin de pouvoir toujours revenir en arrière avec une configuration fonctionnelle, nous éditerons une copie de GENERIC, à partir de laquelle nous compilerons. # cd /sys/i386/conf && cp GENERIC LSD # ee LSD ------------------------------------ SNiP ------------------------------------- options INET options INET6 options IPSEC options IPSEC_ESP options IPSEC_FILTERGIF options IPFIREWALL options IPFIREWALL_VERBOSE options IPFIREWALL_VERBOSE_LIMIT=30 options IPFIREWALL_FORWARD options IPSTEALTH options BRIDGE options IPDIVERT options DUMMYNET #options IPFIREWALL_DEFAULT_TO_ACCEPT options ACCEPT_FILTER_DATA options ACCEPT_FILTER_HTTP options NETGRAPH options NETGRAPH_ONE2MANY options NETGRAPH_PPPOE options NETGRAPH_HOLE options NETGRAPH_ECHO options NETGRAPH_TEE options NETGRAPH_TTY options NETGRAPH_ASYNC options NETGRAPH_INTERFACE options TCP_DROP_SYNFIN options ICMP_BANDLIM options RANDOM_IP_ID options SC_DISABLE_DDBKEY options SC_DISABLE_REBOOT options SC_NO_HISTORY options NO_LKM options NO_KLD options QUOTA options SOFTUPDATES options UFS_DIRHASH options COMPAT_LINUX options DDB options DEVICE_POLLING maxusers 0 options HZ=1000 options NMBCLUSTERS=32768 pseudo-device snp 4 # high because of all the security tools pseudo-device bpf 10 # high because of IPSec pseudo-device gif 10 pseudo-device faith 1 pseudo-device stf 1 ------------------------------------ SNiP ------------------------------------- Dans l'ordre, nous sélectionnons le support IPv4, IPv6, IPSEC et ESP. Puis nous activons le support IPFW (que vous pouvez décider de remplacer par IPFilter non traité dans cet article) avec l'envoi des messages à syslog limité à 30 fois la même occurrence. Nous aurons eu soin avant cela de permettre à IPFW de filtrer les paquets encapsulés avec IPSEC et transitant via des interfaces gif, tout ceci depuis FreeBSD 4.9. Le forwarding est aussi activé ainsi que le forwarding caché (passant un paquet sans décroître son TTL), tout comme les divert sockets permettant de modifier le transit d'un paquet dans le kernel ; et enfin le support de dummynet, le traffic shaper basique du système. Nous activons ensuite les accept filters qui accélère le processus d'admission de certains types de connexions (comme HTTP) en les plaçant directement dans le kernel. Notez que mi-2002, Luigi Rizzo a totalement réécrit les mécanismes internes d'ipfw afin d'en doubler la vitesse et de le rendre facilement extensible via un jeu de microinstructions similaires à BPF. Ce code a été backporté sur stable et se trouve totalement compatible avec vos rulesets. Bien qu'encore non documenté, vous pouvez l'activer en plaçant l'option IPFW2 dans votre configuration. Après cela, nous activons la cohorte de noeuds du sous-système NetGraph qui permet des manipulations complexes au niveau réseau à l'aide de nodes héritant des particularités de leur type (hooks possibles, traitement du trafic à chaque hook, interprétation des messages de contrôle...) qui peuvent être chaînés à travers des hooks pour constituer une suite d'edges : un graphe. Plus d'informations et la descriptions des nodes dans la manpage netgraph(4). Nous enchaînons avec quelques sécurités réseau, d'abord, comme le rejet de certains paquets forgés (SYN/FIN) permettant la reconnaissance d'OS, la limitation, via sysctl, d'émission de paquets ICMP afin de ne pas servir de réflecteur lors d'un DoS, et la génération aléatoire des IP ID pour réduire les opportunités de scanning. Puis sécurité physique avec la désactivation des séquences clavier de debugging et de redémarrage, ainsi que la désactivation du backscrolling pour les terminaux virtuels. Enfin, sécurité système avec la désactivation des LKM ; et même des KLD si vous appliquez le patch suivant http://people.freebsd.org/~cjc/kld_stable.patch. Suivent l'activation des quotas disque, du code de compatibilité Linux via l'émulation de certains appels système et du debugger kernel DDB. Nous vérifions aussi l'activation des SoftUpdates, méthode d'écriture et de lecture asynchrone résolvant les problèmes liés aux metadata et à leur perte. L'approche de Linux est la journalisation (concept hérité des base de données) qui consiste à écrire les mises à jour de metadata avec leurs dépendances dans une partie distincte du système de fichier : le journal. Quand les metadata sont prêtes, elles sont effectivement écrites. Le système de fichiers de FreeBSD nommé UFS utilise une approche différente (inspirée de CVS) dans laquelle les opérations d'écriture ou de lecture sont placées dans une file d'attente divisée en un buffer d'attente et un buffer de vérification des dépendances. Si un bloc appartient à une boucle de dépendances, il est rejeté dans le buffer d'attente. Cette approche permettra de plus à l'avenir des redémarrage suite à un crash avec un fsck fonctionnant en tâche de fond. Pour de plus amples explications, voir http://www.di.ens.fr/~pornin/jfs.html. Toujours au niveau système, vous apprécierez le device polling permettant d'améliorer les performances kernel en limitant les interruptions et donc le changement de contexte et l'appel à un gestionnaire d'interruption. A la place, les périphériques sont sondés aux moments opportuns comme les interruptions d'horloge, les appels système ou pendant les périodes de non-activité. Notez enfin que le nombres maximal d'utilisateurs est placé à 0 pour qu'il soit calculé au moment du boot en fonction de la mémoire physique disponible. Cette variable ainsi que le nombre de clusters du système de fichiers sont utilisées pour calculer l'allocation de certaines ressources mémoire. La fréquence d'horloge est ensuite placée à 1000 Hz pour augmenter l'acuité du device polling (et aussi limiter les burst si vous utilisez ALTQ). Viennent enfin les pseudo-devices snoop et bpf pour la surveillance respective des tty et des trames ethernet, et gif, faith et stf pour le tunneling v6/v4. Certaines options seront déjà présentes, nous ne faisons que les vérifier. Vous pouvez également réfléchir à mettre en commentaire le support procfs et NFS qui peuvent créer d'éventuelles vulnérabilités dans le système, à moins bien sûr que vous n'en ayez besoin. Pour examiner l'ensemble des options kernel disponibles, référez vous au fichier LINT dans le même répertoire que GENERIC. Par la séquence de commandes suivantes, nous construisons successivement le kernel LSD puis nous l'installons et enfin nous le protégeons à l'aide des flags immutables tout en nettoyant le système des fichiers générés par l'install. # cd /usr/src && make buildkernel KERNCONF=LSD && make installkernel \ KERNCONF=LSD && make clean Nous en avons fini avec la première partie de la recompilation complète du système. Le système mis à jour est désormais fin prêt à être installé. Cependant pour plus de sûreté il est recommandé d'effectuer la suite des opérations en single-user mode. Pour ce faire au moment du prompt annonçant le boot, pressez la barre d'espace pour entrer dans le menu de boot puis tapez 'boot -s'. Cette manoeuvre est recommandée pour chaque mise à jour ou manipulation entraînant une reconstruction générale du système. # reboot La commande suivante installe donc l'ensemble du système de base mis à jour. Le buildworld précédent correspondait à la compilation des sources (d'où sa durée) tandis qu'ici nous nous contentons d'installer les binaires générés (d'où une moindre attente). # cd /usr/src && make installworld Nous appliquons ensuite le script mergemaster, très pratique puisqu'il effectue une comparaison entre les anciens fichiers de configuration et ceux par défaut de l'installation. Tout cela afin de mettre à jour la configuration du nouveau système et nous éviter de recommencer un travail fastidieux. # cd /usr/src/usr.sbin/mergemaster # ./mergemaster -sv -D /etc/ Voilà, le système est fin prêt pour attaquer sa réelle configuration. Nous redémarrons une nouvelle fois afin de repasser en multiuser mode. # reboot Reportez-vous au handbook en cas de problèmes : http://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/makeworld.html. 2. Tuning Système Maintenant que notre système est comme neuf et on ne peut plus à jour, il ne nous reste plus qu'à entamer sa réelle sécurisation. Pour cela nous allons d'abord voir ce que nous pouvons faire par défaut avec le système de base afin d'augmenter la sécurité de notre système et de limiter se fenêtre d'exposition aux attaques. 2.1. sysctl Sysctl est un outil extrêmement pratique au sein de FreeBSD puisqu'il va nous permettre de vérifier ou de manipuler l'état du kernel à chaud. Les informations sont stockées et affichées à travers une Management Information Base ou MIB selon le même modèle que SNMP. Par exemple pour afficher une liste des différentes variables d'état kernel, il vous suffit d'effectuer un simple # sysctl -a de la même manière que vous utiliseriez un ls. Notez que vous pouvez préciser l'entrée de la MIB si vous la connaissez juste après l'option afin de n'afficher qu'elle. Suivant le principe des MIB, vous pouvez réduire l'affichage en précisant un champ de la MIB. Par exemple pour afficher toutes les entrées en rapport avec IP # sysctl net.inet.ip.* Notez aussi que certaines variables ne sont pas modifiables et que certaines entrées de la MIB sont sous forme de tableaux utilisés à l'occasion par ps, netsat ou systat. Enfin, pour obtenir une liste des variables sysctl que vous pouvez modifier, consultez la page de manuel de sysctl. 2.1.1. securelevel et chflags L'une des fonctionnalités intéressantes de FreeBSD consiste en l'établissement de secure level au sein du système. Il existe ainsi 5 niveaux de sécurité au sein de FreeBSD qui ne peuvent pas être diminués sans relancer init. Ils peuvent cependant être augmentés par un processus root via la MIB sysctl, et ce même en cours d'exécution. Pour définir le securelevel mis en place par init, modifiez les lignes suivantes dans rc.conf : kern_securelevel_enable="YES" kern_securelevel="N" N représente ici l'entier correspondant à l'un des 5 entiers possibles. Remplacez-le par le niveau qui vous convient. Puis si vous souhaitez l'élever une fois votre système initialisé, modifiez l'entrée sysctl suivante : # sysctl -w kern.securelevel=N Outre des limitations inscrites dans le code kernel, donc inchangeable, propres à chacun d'eux, chaque level définit également les opérations possibles sur des file flags, attributs de fichiers permettant d'améliorer la sécurité fournie par les classiques permissions Unix. Ci-dessous, les 5 niveaux et leurs limitations : - -1 qui instaure le mode insecure (0) de manière permanente. C'est la valeur par défaut. - 0 indique le mode insecure dans lequel les files flags 'immutable' et 'system append only' peuvent être retirés et où l'on peut effectuer des opérations de lecture/écriture sur tous les périphériques avec pour seule restriction les droits déjà instaurés, par exemple, dans la fstab. - 1 indique nous sommes en secure mode dans lequel les flags 'system immutable' et 'system append only' ne peuvent être désactivés. L'accès à /dev/mem et /dev/kmem est interdit en écriture et les KLD ne peuvent plus être chargés ou déchargés. - 2 instaure le highly secure mode qui est strictement le même que le secure mode à la différence près que les seules opérations désormais effectuées sur les disques sont le montage et démontage. - 3 signifie l'instauration du network secure mode qui est similaire au level précédent avec en plus l'impossibilité de modifier les règles ipfw ou la configuration du traffic shaper basé sur ipfw dummynet. Pour une station de travail, l'utilisation devient problématique dès le securelevel 1. Par exemple, XFree86 peut nécessiter l'accès à /dev/mem alors que cela lui est interdit. Les niveaux suivants sont adaptés à des serveurs, et le dernier est plus particulièrement destiné à une passerelle. Notez le niveau 2, très restrictif, qui force l'accès des disques en read-only. A ce niveau, même un serveur risque de se voir perturbé par les securelevel, encore plus s’il nécessite des compilations ou modifications de configuration régulières. Notez que le niveau protège très bien de la plupart des backdoors basées sur des modules kernel. Il pourrait cependant être judicieusement de remplacer ou d'ajouter l'option kernel NO_LKM. Pour paramétrer les file flags sur vos fichiers en fonction de ces levels, il vous faudra utiliser la commande chflags. Les flags disponibles sont listés ci-après. - arch, uniquement utilisable par root, transforme le fichier en archive. - opaque, rend le fichier "opaque" à certaine lecture (utile contre la lecture par d'autres applications) et modifiable uniquement par l'owner ou le root. - nodump, permet d'interdire un backup du fichier via l'utilitaire dump. Modifiable uniquement par le owner ou le root. - sappnd, instaure le flag system append-only uniquement modifiable par le root en securelevel supérieur à 0. Append signifie qu'on ne peut que rajouter des données et non en retirer. - schg place le flag system immutable qui, comme son nom l'indique, est censé empêcher toute évènement. Modifiable par le root uniquement en niveau 0. - sunlnk permet d'interdire la suppression d'un fichier, sachant que cette option n'est modifiable que par le root - uappnd, uchg et uunlnk sont les pendant respectifs de sappnd, schg et sunlnk. La différence réside dans le fait que, outre le root, le owner du fichier a également le droit de modifier les file flags. La syntaxe pourra par exemple être la suivante : # chflags -RH schg /bin # chflags -RH schg /sbin # chflags -RH schg /usr/libexec # chflags -RH schg /usr/lib # chflags -RH schg /usr/share/lib # chflags -RH schg /boot # chflags -RH sappnd /var/log/* # chflags -RH sappnd /home/*/.*history # chflags schg /kernel Notez qu'en effectuant un man chflags, vous découvrez les quelques options - notamment la récursivité - que propose cette commande. Pour retirer un file flag ce sont les mêmes options avec le prefix no tel que nosappnd. Les file flags sont intéressants à placer sur certains fichiers sensibles précis ou alors carrément sur un répertoire dont vous souhaitez vous assurer de l'intégrité et qui sera rarement modifié. Dans l'exemple, R et H permettent de suivre les liens symboliques et ce récursivement, la commande étant appliquée à des répertoires entiers de binaires. Dernier détail qui n'a pas grand chose à voir avec sysctl mais qui importe beaucoup dans la gestion des droits et des fichiers. Vous avez le loisir de modifier la fstab afin de monter vos diverses partitions avec certaines options. Sous FreeBSD, au lieu de ne créer qu'une unique partition root /, le système crée simultanément une partition /usr et /var. Vous pouvez définir dans votre fstab pour l'ensemble de vos systèmes de fichiers : - les droits d'écriture avec l'option rw pour read-write, ou ro pour read-only. - les droits d'exécution avec l'option noexec. - les privilèges accordés avec l'option nosuid. # ee /etc/fstab #device mountpoint fs options dump pass /dev/ad0s2b none swap sw 0 0 /dev/ad0s2a / ufs rw 2 2 /dev/ad0s2f /usr ufs rw,nodev 2 2 /dev/ad0s2c /home ufs rw,nosuid,nodev,userquota 2 1 /dev/ad0s2e /var ufs rw,noexec,nosuid,nodev 2 2 /dev/acd0c /cdrom cd9660 ro,noauto 0 Nous limitons ici l'exécution dans /var afin d'éviter qu'un intrus tente d'y placer des binaires et nous limitons la présence de binaires suid ou de devices dans les autres répertoires majeurs. Notez au passage que les soft updates que nous avons mis en place dans notre configuration kernel ne s'activent pas via la fstab mais grâce à l'utilitaire tunefs qui modifie directement, et de manière persistante, l'entête du système de fichier. Pour effectivement activer ces soft updates, suivez la ligne suivante : # tunefs -n enable /usr # tunefs -n enable /home Remplacez alors enable par disable pour désactiver cette option. 2.1.3. Performances Sysctl peut également nous apporter une aide précieuse dans la configuration système et réseau à des fins de performances élevées. Il va ainsi nous permettre d'activer certaines capacités réseau que supporte parfaitement la pile TCP/IP BSD - qui est cela dit en passant certainement la plus stable, la plus performante et la plus standard des piles TCP/IP - mais qui ne sont pas activées par défaut. La première entrée intéressante est l'option log_in_vain qui va loguer à travers une simple entrée dans le fichier de log correspondant de syslogd, toute tentative d'accès à un service même si aucun service n'est à l'écoute sur le port de la tentative d'accès. Ceci peut nous permettre dans un environnement sécurisé, allié de préférence avec un outil d'analyse de log tel que logcheck ou ASAX, de repérer des tentatives de network mapping même si nous avons un minimum de services disponibles et qui plus est un firewall. Cependant je ferai remarquer 2 difficultés : tout d'abord log_in_vain ne logue en réalité que les paquets avec un flag SYN, ce qui n'est pas assez pour réellement détecter un scan. Et d'autre part, dans un environnement "chaud" comme une machine servant de passerelle ou un serveur au sein d'une DMZ, l'utilisation de log_in_vain peut entraîner une quantité de log assez impressionnante et capable d'occuper un analyste ou un administrateur sans outils d'analyse pendant des semaines. Mais au sein d'un réseau d'hors et déjà sécurisé ou sur une machine critique, cette capacité de log peut être intéressante. Pour l'activer, il vous suffit de saisir : # sysctl -w net.inet.tcp.log_in_vain=1 # sysctl -w net.inet.udp.log_in_vain=1 Les astuces suivantes sont intéressantes si votre machine doit servir de passerelle, de filtre ou de load balancer pour d'autres machines. La commande suivante vous offre la possibilité d'activer le forwarding IP et donc de transformer notre machine en gateway ce qui s'avérera utile pour le partage de connexion ou le déploiement d'une jail. Vous pouvez lui adjoindre l'entrée fastforwarding qui active un système de cache des routes menant à des adresses externes au réseau local. L'intérêt est de contourner de cette manière l'inspection par la couche IP pour obtenir un passage direct des trames entre les niveaux 2 des interfaces de la passerelle. # sysctl -w net.inet.ip.forwarding=1 # sysctl -w net.inet.fastforwarding=1 Les entrées suivantes sont particulièrement utiles dans le cadre d'un serveur qui nécessité une haute disponibilité ou une grande fiabilité. En effet, nous allons apporter à notre machine le support des extensions TCP pour hautes performances. Ces extensions sont décrites dans la RFC 1323 que je vous recommande d'étudier. Elles sont constituées de 3 extensions. La première est le champ Window Scale qui est un facteur appliqué au champ Window Size et utile sur les Large Fat Network (LFN) afin d'optimiser le flux de donnees sur ces grands reseaux et d'apporter un meilleur contrôle en multipliant la Window Size. Nous trouvons ensuite 2 options extrêmement utiles dans le cas d'utilisation de load balancing en fournissant des informations aux algorithmes de répartition de charge. Le Round-Trip Time Mesurement (RTTM) et le Protection Against Wrapper Sequence Numbers (PAWS) se basent tous les 2 sur l'adjonction d'une option de timestamp aux segments TCP. Avec de simples vérifications de timestamp on peut ainsi calculer le temps d'aller retour entre 2 ACK et ainsi optimiser le flux ou modifier la route. On peut également se prévenir du hijacking, de l'overlapping et surtout du rejeu en effectuant une double vérification sur le numéro de séquence et le timestamp. Ces options viennent s'ajouter à la suite d'algorithmes NewReno, évolution de la pile Reno, permettant de détecter et corriger une congestion dans le réseau en jouant sur la perte de paquets, les délais et les tailles de fenêtre TCP. L'unique bémol est que ces options ne fonctionnent bien sûr qu'entre des machines les supportant toutes les 2 de manière similaire, or elles ne semblent pas être très utilisées - notez que la branche 4.x supporte la RFC 1323 par défaut depuis la release 4.4 - et enfin vous pourriez risquer des désagréments face à certains firewalls ou plugins de normalisation de trafic s'ils ne reconnaissent pas ces options. Cependant nous décidons de l'aborder dans cet article afin de faciliter sa diffusion et nous l'appliquons à notre système par acquis de conscience ! Vous trouverez ensuite une entrée récente qui force FreeBSD à calculer, pour chaque connexion TCP, la quantité de données en cours de transmission dans le réseau (à partir du produit delay*bw), afin de limiter l'envoi de paquets à même d'entraîner une surcharge non pertinente des routeurs et/ou switchs présent sur la route. Cette configuration rapproche alors le comportement de FreeBSD de la suite d'algorithmes TCP Vegas. Nous n'avons plus ensuite qu'à augmenter les tailles par défaut de nos buffers d'envoi et de réception (aussi bien pour TCP que pour UDP), qui ont une influence directe sur le champ TCP window size. Du fait de l'influence possible de ces valeurs sur le délai, il est recommandé d'expérimenter plusieurs valeurs, en suivant par exemple la règle wnd = bw / 8 * RTT. Par exemple, nous avons volontairement limité la taille des buffers pour UDP qui ne possède pas de mécanisme de détection et évitement de congestion et s'y trouve donc plus sensible. Nous avons également attribué des valeurs différentes aux buffers d'envoi et de réception pour TCP, la vitesse de download se trouvant souvent plus élevée que celle d'upload (pensez aux lignes xDSL). Pour finir, les deux dernières entrées sysctl indiquent de ne pas générer de paquets avec l'option source route, ni de les accepter, cette option IP facilitant le spoofing déjà évoqué en faisant remonter les paquets IP strictement par le chemin utilisé à l'aller. Pour tout cela, il vous suffit d'effectuer les commandes suivantes : # sysctl -w net.inet.tcp.rfc1323=1 # sysctl -w net.inet.tcp.newreno=1 # sysctl -w net.inet.tcp.inflight_enable=1 # sysctl -w net.inet.tcp.inflight_min=6144 # sysctl -w net.inet.tcp.sendspace=32768 # sysctl -w net.inet.tcp.recvspace=65535 # sysctl -w net.inet.udp.sendspace=32768 # sysctl -w net.inet.udp.recvspace=32768 # sysctl -w net.inet.udp.maxdgram=28672 # sysctl -w net.inet.ip.sourceroute=0 # sysctl -w net.inet.ip.accept_sourceroute=0 Ensuite, nous activons l'implémentation de la RFC 1948 appliquant la recommandation de Steve Bellovin sur la génération aléatoire d'ISN selon l'équation ISN = M + F(localhost,localport,remotehost,remoteport) où M est un timestamp. Cependant, la sécurité de cette recommandation repose essentiellement sur le caractère aléatoire de la clé secrète et la puissance de l'algorithme de hashage. En effet, l'étude de Michal Zalewski (http://razor.bindview.com/publish/papers/tcpseq.html), concernant l'utilisation des attracteurs étranges à des fins de construction de spoofing sets répondant au problème de prédiction d'ISN TCP de manière aveugle, a démontré qu'avec la liberté laissée par la recommandation de ne générer une clé secrète qu'au démarrage seulement et avec la réutilisation des adresses IPv4, il est possible pour un serveur au long uptime qu'un attaquant puisse créer un attracteur étrange assez large pour s'assurer d'un bon taux de réussite en utilisant la même adresse IP source. Malgré l'absence de démonstrations mathématiques pour cette étude, nous activons la régénération de secret à un intervalle de 3600 secondes. Sachez que la phase de régénération brise le mécanismes de recyclage TIME_WAIT, permettant de purger avant les 240 secondes réglementaires les connexions TCP en cours de fermeture (état TIME_WAIT), allouant donc des ressources un peu plus longtemps. A titre personnel je me demande aussi de quelle manière le projet CBOSS a concilié l'intégration des syncookies dans FreeBSD à partir de la release 4.5 avec le respect de la RFC 1948. Nous rappelons enfin les différentes entrées relatives aux mécanismes de syncookies et de syncache limitant fortement les risques liés au SYN flood tout en améliorant le fonctionnement normal. # sysctl -w net.inet.tcp.strict_rfc1948=1 # sysctl -w net.inet.tcp.isn_reseed_interval=1800 # sysctl -w net.inet.tcp.syncookies=1 # sysctl -w net.inet.tcp.syncache.hashsize=512 # sysctl -w net.inet.tcp.syncache.cachelimit=15359 # sysctl -w net.inet.tcp.syncache.bucketlimit=30 # sysctl -w net.inet.tcp.syncache.rexmtlimit=3 Nous décidons maintenant de nous protéger face à certaines tentatives de DoS ainsi que de diverses techniques de network mapping. A l'aide des lignes suivantes, vous pourrez donc notamment modifier la valeur de TTL par défaut, qui peut être utilisé pour identifier l'OS, ou interdire les réponses aux ICMP mask reply menant à une éventuelle cartographie réseau, mais aussi aux ICMP broadcast, très souvent source de smurf ou DoS par amplification, et de mettre la limite maximale de paquets ICMP en réponse à 200 par seconde. Pour TCP, nous activons les delayed acknowledgments, restreignant l'inclination du système à envoyer des ACK pour chaque segment reçu. Ceci permet d'éviter le Silly Window Syndrome (SWS) qui tend à réduire la window size et donc d'assurer une meilleure efficacité (voir RFC 813, 896 et 2581). Nous activons également les keepalive, segments TCP permettant de vérifier si une transmission TCP est toujours réellement active et non pas conservée dans un état artificiel (suite à un SYN flood ou Naptha, par exemple). Vient ensuite l'activation des blackholes consistant à empêcher votre système d'être scanné en ne répondant ni par un RST pour TCP ni par un ICMP port unreachable pour UDP aux paquets envoyés sur un port fermé et donc transformer votre système en "trou noir". La dernière ligne n'est applicable que sur les hôtes finaux puisqu'elle induit la vérification à chaque arrivée de paquets que son adresse de destination corresponde à une adresse de l'interface de réception. # sysctl -w net.inet.ip.ttl=128 # sysctl -w net.inet.icmp.maskrepl=0 # sysctl -w net.inet.icmp.bmcastecho=0 # sysctl -w net.inet.icmp.icmplim=200 # sysctl -w net.inet.tcp.delayed_ack=1 # sysctl -w net.inet.tcp.always_keepalive=1 # sysctl -w net.inet.tcp.blackhole=2 # sysctl -w net.inet.udp.blackhole=1 # sysctl -w net.inet.ip.check_interface=1 Par ailleurs nous possédons également quelques astuces afin d'éviter les tentatives de cache poisoning en accélérant le temps de rafraîchissement de la table de routage et de la table ARP. # sysctl -w net.inet.ip.rtexpire=60 # sysctl -w net.inet.ip.rtminexpire=10 # sysctl -w net.link.ether.inet.max_age=1200 Finissons avec quelques modifications liées au système à modifier : # sysctl -w vfs.vmiodirenable=1 # sysctl -w kern.coredump=1 # sysctl -w kern.corefile=%N.sexfault # sysctl -w kern.ps_showallprocs=0 La première entrée permet d'améliorer le traitement notamment sur des larges volumes de fichiers. Il concerne les fichiers Unix qui seront cachés dans le buffer cache plutôt que directement sur le disque exploitant ainsi pleinement la mémoire virtuelle FreeBSD par ailleurs déjà très performante. Ensuite nous décidons d'activer l'application savecore qui permet de conserver une trace du core dump associé à un kernel dans un but d'étude après un crash par exemple afin d'en déterminer les causes. En dernier lieu, nous faisons en sorte que les utilisateurs ne voient que leurs propres processus et que seul le root puisse voir l'ensemble. Notez que nous disposons également de quelques fonctionnalités configurables par l'intermédiaire du loader. Par exemple, si vous disposez d'un disque IDE, la commande suivante permet d'activer le cache en écriture : # loader set hw.ata.wc=1 Toujours dans les protections contre les DoS mais cette fois-ci côté ressources plutôt que réseau. Les entrées suivantes de la MIB vont nous permettrent d'abord de limiter le nombre de processus par utilisateur et le nombre fichiers (incluant file descriptor et IPC) qu'il peut ouvrir. Nous augmentons aussi la taille de la queue de connexions de pair avec le nombre maximal de sockets (2 fois le maximum de connexion approximativement). de même que la taille maximal des buffers pour sockets (empiriquement 8 fois la taille de {recv,send}space). Enfin, nous augmentons également le nombre maximum de fichiers. Toutes ces valeurs paraissent élevées, mais elles ne servent en réalité qu'à l'allocation de ressources. # sysctl -w kern.maxprocperuid=512 # sysctl -w kern.maxfilesperproc=1024 # sysctl -w kern.ipc.somaxconn=4096 # sysctl -w kern.ipc.maxsockbuf=262144 # sysctl -w kern.maxfiles=16384 Si vous disposez de disques IBM DPTA ou DTLA, vous pouvez utiliser à la place l'entrée hw.ata.tags mais à vos risques et périls puisqu'elle est encore expérimentale. Ces modifications doivent être répercutées sur /boot/loader.conf. De la même manière, les options sysctl que voudrez retrouver à chaque demarrage doivent se trouver dans le fichier /etc/sysctl.conf sous la forme 'entrée=paramètre'. Ci-dessous notre sysctl.conf final. ------------------------------------- SNiP ------------------------------------ net.inet.tcp.rfc1323=1 net.inet.tcp.newreno=1 net.inet.tcp.inflight_enable=1 net.inet.tcp.inflight_min=6144 net.inet.tcp.sendspace=32768 net.inet.tcp.recvspace=65535 net.inet.tcp.log_in_vain=1 net.inet.tcp.always_keepalive=1 net.inet.tcp.blackhole=2 net.inet.tcp.delayed_ack=1 net.inet.tcp.strict_rfc1948=1 net.inet.tcp.isn_reseed_interval=1800 net.inet.tcp.syncookies=1 net.inet.tcp.syncache.hashsize=512 net.inet.tcp.syncache.cachelimit=15359 net.inet.tcp.syncache.bucketlimit=30 net.inet.tcp.syncache.rexmtlimit=3 net.inet.icmp.maskrepl=0 net.inet.icmp.bmcastecho=0 net.inet.icmp.icmplim=300 net.inet.udp.sendspace=32768 net.inet.udp.recvspace=32768 net.inet.udp.maxdgram=28672 net.inet.udp.blackhole=1 net.inet.udp.log_in_vain=1 net.inet.ip.ttl=128 net.inet.ip.forwarding=1 # ou check_interface=1 net.inet.ip.sourceroute=0 net.inet.ip.accept_sourceroute=0 net.inet.ip.rtexpire=60 net.inet.ip.rtminexpire=10 net.link.ether.inet.max_age=1200 vfs.vmiodirenable=1 kern.coredump=1 kern.corefile=%N.sexfault kern.ps_showallprocs=0 ------------------------------------- SNiP ------------------------------------ Et la même chose pour /boot/loader.conf ------------------------------------- SNiP ------------------------------------ kern.maxprocperuid=512 kern.maxfilesperproc=1024 kern.maxfiles=16384 kern.ipc.somaxconn=4096 kern.ipc.maxsockbuf=262144 ------------------------------------- SNiP ------------------------------------ 2.2. Gestion utilisateurs Dans un système multi-utilisateurs, chaque utilisateur local ou distant se doit d'avoir un compte propre permettant de converser l'environnement de chacun dans l'état désiré ainsi qu'une gestion plus claire de l'activité du système. En marge des comptes utilisateur classiques nous vous recommandons fortement de créer plusieurs comptes dit 'système' destinés à exécuter avec un minimum de risques de compromission ou d'exploitation de compte compromis, les services réseau sensibles que vous désirez mettre en place. Parmi les plus intéressant sur lesquels appliquer cette pratique, on trouve les serveurs DNS, les serveurs web ou encore les serveurs smtp et pop3/imap4. Notez aussi la présence du compte nobody correspondant a l'utilisateur système non privilégié générique, mais plus il y a de services qui utilisent nobody, plus ce compte acquiert de privilèges. Cette méthode est par ailleurs utilisée par beaucoup de procédures d'installation dans les ports, réduisant votre charge de travail. Notez que les multiples chapitres sur sysctl, la configuration kernel, la gestion des utilisateurs ou la mise en place d'une jail, nous pensons pouvoir nous dispenser d'un chapitre supplémentaire sur les commandes basiques que sont chroot, chmod et chown accompagné d'une explication rapide sur les privilèges. Donc ne cherchez pas d'information sur ces commandes dans nos colonnes. Notez par ailleurs que l'utilisation du compte root or des opérations limitées de maintenance est fortement déconseillée pour des raisons de sécurité. Chaque manipulation du root peut entraîner des conséquences importantes pour l'intégrité du système ou bien si le compte est compromis, alors toute la machine passe sous contrôle de l'intrus. Bref, il est préférable de se constituer un compte utilisateur appartenant lui aussi au group wheel et ayant la capacité d'exécuter des commandes en root par sudo qui nous permet de rester sous un compte utilisateur et effectuer des opérations ponctuelles nécessitent des droits root ou encore d'intervenir dans sur d'autres comptes si besoin est. Par exemple nous pouvons éditer l'index de notre serveur web sur le compte système www en utilisant l'option -u qui permet de préciser l'utilisateur dont on souhaite endosser les droits # sudo -u eberkut vi ~eberkut/CNS/FreeBSD.txt Bien sûr cette liberté de manipulation peut être dangereuse c'est pourquoi nous avons besoin d'éditer et de configurer /etc/sudoers afin de limiter les accès. Le schéma de /etc/sudoers est le suivant : vous pouvez définir des alias pour un ou plusieurs utilisateurs, ou encore une ou plusieurs commandes puis vous définissez les autorisations selon le schema WHO WHERE=WHAT. ------------------------------------ SNiP ------------------------------------- # options Defaults syslog=auth, mail_no_user, lecture, insults,\ syslog_badpri=alert, rootpw, passwd_timeout=3, authenticate Defaults:FULLTIMERS !lecture # alias utilisateurs > root User_Alias FULLTIMERS = eberkut User_Alias PARTTIMERS = bindmaster,webmaster Run_alias OP = root,named,www # alias commandes Cmnd_Alias DEBUG = /usr/bin/mt,/usr/sbin/dump,/usr/sbin/restore, \ /usr/sbin/dd,/usr/bin/gdb,/usr/bin/ktrace, \ /usr/bin/kdump,/usr/bin/file,/usr/bin/truss, \ /usr/bin/ldd,/usr/bin/objdump,/usr/bin/strings, \ /usr/bin/nm,/usr/bin/size,/usr/bin/kill Cmnd_Alias KILL = /usr/sbin/shutdown,/usr/sbin/halt,/usr/sbin/reboot Cmnd_Alias SHELLS = /usr/bin/sh,/usr/bin/csh,/usr/local/bin/zsh, \ /usr/bin/ssh,/usr/X11R6/bin/startx Cmnd_Alias USER = /usr/bin/su,/usr/sbin/adduser, /usr/sbin/rmuser, \ /usr/bin/chsh Cmnd_Alias NET = /usr/sbin/ppp,/usr/sbin/ifconfig,/usr/sbin/ipfw Cmnd_Alias DAEMON = /usr/sbin/named,/usr/local/apache,/usr/bin/sshd Cmnd_Alias RIGHTS = /usr/sbin/chroot,/usr/sbin/jail,/usr/sbin/chown, \ /usr/bin/chmod Cmnd_Alias CDROM = /sbin/umount /cdrom, /sbin/mount_cd9660 /dev/acd0c /cdrom #directives root ALL = (ALL) ALL FULLTIMERS ALL = NOPASSWD: DEBUG, KILL, SHELLS, RIGHTS, USER, NET, DAEMON PARTTIMERS ALL = DEBUG, NET, (OP) NOPASSWD: DAEMON ALL ALL = NOPASSWD: CDROM ------------------------------------ SNiP ------------------------------------- Sudo se base sur des timestamp entre les différences commandes entrées pour assurer un minimum de sécurité en plaçant un timeout. Pour updater votre timestamp sans exécuter de commandes, vous pouvez taper sudo -v et pour le tuer définitivement, sudo -K. 2.2.1. adduser / rmuser / chpass / watch adduser est un outil extrêmement utile qui nous permet d'ajouter de nouveaux utilisateurs de manière très simple. Il permet en une opération de gérer l'ensemble des actions nécessaire à la création d'un nouveau compte. Une simple commande effectue une configuration pas à pas du compte ceci incluant la création des entrées nécessaires dans /etc/passwd et /etc/group, la création du répertoire de l'utilisateur et la copie des fichiers requis par défaut jusqu'à une notification de bienvenue. Nous devons tout d'abord créer le fichier de configuration adduser par : # adduser -s -config_create Puis nous lançons la création de l'utilisateur. # adduser -v Use option ``-silent'' if you don't want to see all warnings and questions. Check /etc/shells Check /etc/master.passwd Check /etc/group Enter your default shell: csh date no sh tcsh zsh [sh]: sh Your default shell is: sh -> /usr/local/bin/sh Enter your default HOME partition: [/home]: Copy dotfiles from: /usr/share/skel no [/usr/share/skel]: Send message from file: /etc/adduser.message no [/etc/adduser.message]: no Do not send message Use passwords (y/n) [y]: y Write your changes to /etc/adduser.conf? (y/n) [n]: y Ok, let's go. Don't worry about mistakes. I will give you the chance later to correct any input. Enter username [a-z0-9_-]: eberkut Enter full name []: eberkut Enter shell csh date no sh tcsh zsh [zsh]: Enter home directory (full path) [/home/eberkut]: Uid [1000]: Enter login class []: root Login group wheel [wheel]: Login group is ``eberkut''. Invite eberkut into other groups: guest no [no]: Enter password []: Enter password again []: Name: eberkut Password: ******** Fullname: eberkut Uid: 1000 Gid: 1000 Class: root Groups: wheel HOME: /home/eberkut Shell: /usr/local/bin/zsh OK? (y/n) [y]: y Added user ``eberkut'' Copy files from /usr/share/skel to /home/eberkut Add another user? (y/n) [y]: n Goodbye! Notez que vous pouvez facilement remarquer que nous utilisons ici adduser pour la première fois puisqu'il nous a fallu créer le fichier de configuration puis adduser nous a demande ses valeurs par défaut. De plus pour plus de simplicité nous étions en mode verbose (-v). A l'avenir vous n'aurez que les informations de l'utilisateur à entrer et vous pourrez effectuer cette opération en mode silent (-s). Adduser possède un programme frère, rmuser, qui va nous permettre de manière symétrique à adduser, de supprimer en une seule opération un utilisateur et toutes les dépendances que cela suppose. Rmuser effectue ainsi la suppression de l'entrée utilisateur dans le fichier de mots de passe, de son repertoire (dans /home), de son courrier en attente (dans /var/mail), de ses fichiers temporaires (dans /tmp), et son entrée dans /etc/group voire la suppression du groupe s’il devient vide. Mais rmuser efface également les entrées de l'utilisateur dans la crontab ou encore tue tous les processus en cours appartenant à l'utilisateur en question. # rmuser eberkut Matching password entry: eberkut:*:1000:1000::0:0:eberkut:/home/eberkut:/usr/local/bin/zsh Is this the entry you wish to remove? y Remove user's home directory (/home/eberkut)? y Updating password file, updating databases, done. Updating group file: trusted done. Removing user's incoming mail file /var/mail/jru: done. Removing files belonging to eberkut from /tmp: done. Removing files belonging to eberkut from /var/tmp: done. Removing files belonging to eberkut from /var/tmp/vi.recover: done. Enfin, chpass est un autre outil merveilleux de plus nous permettant de faciliter grandement la gestion utilisateur. Il permet de modifier les informations de base d'un utilisateur telles que son password, son shell ou des informations plus personnelles. Seul le root et l'utilisateur lui-même peuvent modifier les informations avec chpass. Chpass fonctionne de la même manière que edquota, c'est-à-dire qu'il va ouvrir un éditeur permettant de modifier notre configuration. # chpass eberkut #Changing user database information for eberkut. Login: eberkut Password: ******** Uid [#]: 1000 Gid [# or name]: 1000 Change [month day year]: Expire [month day year]: Class: Home directory: /home/eberkut Shell: /usr/local/bin/zsh Full Name: eberkut Office Location: Office Phone: Home Phone: Other information: Lorsque vous désirez mettre en place un service, en plus de le faire fonctionner en stand alone comme expliquer précédemment, et s’il nécessite certains droits, alors lui assigner un utilisateur spécifique peut permettre de limiter partiellement avec des mécanismes supplémentaires comme jail ou chroot les dégâts provoqués par une intrusion par l'intermédiaire de ce service. Cette remarque est vraie aussi pour BIND qui avec les bonnes options ne reste root que quelques instants ou pour Apache qui tourne en nobody - et ses scripts aussi ce qui peut donner des vulnérabilités en cas de mauvaise configuration. Enfin, lorsqu’un utilisateur vient à se loguer et que vous avez remarqué de sa part un comportement illégitime ou intrusif, les options pour les snoop device que nous avons placé au moment de la configuration kernel vont nous permettre de prendre possession afin d'observer et même d'écrire sur le tty d'un utilisateur. Pour cela nous allons utiliser l'outil watch. Nous créons d'abord les périphériques suivants : # cd /dev # ./MAKEDEV snp0 # ./MAKEDEV snp1 # ./MAKEDEV snp2 # ./MAKEDEV snp3 Puis nous pouvons lancer watch. Avant cela nous vérifions les utilisateurs actifs sur le système afin de spécifier le tty device à surveiller. Nous plaçons l'option t pour obtenir un timestamp au début de l'observation, n pour empêcher l'utilisateur de changer de tty attaché et l'option W pour permettre d'écrire au sein du tty surveillé. # who # watch -tnW ttyp1 2.2.2. quotas et limites Les quotas sont une option de FreeBSD qui vous permettant de limiter la quantité d'espace disque ou encore le nombre de fichiers auxquels a le droit un utilisateur ou tous les utilisateurs du même groupe, sur un système de fichiers donné. Dans un système multi-utilisateurs avec des accès distant, cette méthode évite qu'un seul utilisateur ne consomme tout l'espace disque. Les quotas devant être activés manuellement au sein du fichier de configuration du kernel et nécessitant donc une recompilation, nous vous avons recommande de vérifier la présence de l'option quotas ainsi que plusieurs autres au début de ce texte. Une fois cette étape passée, vous devez activer une fois de plus les quotas manuellement cette fois-ci au sein du fichier /etc/rc.conf. Pour cela nous l'éditons et y ajoutons la ligne suivante : enable_quotas="YES" Pour un contrôle accru, vous disposez également d'une seconde option mais qui va lancer un programme - quotacheck - qui peut considérablement ralentir le démarrage de votre machine. Cependant la sécurité primant, nous vous recommandons d'éditer la ligne ci-dessous : check_quotas="YES" Vous devez enfin éditer le fichier /etc/fstab pour mettre en service les quotas système de fichiers par système de fichiers. C'est là que vous dites si vous voulez des quotas d'utilisation des disques par utilisateur, par groupe ou les deux, pour chaque système de fichiers. Pour mettre en service des quotas par utilisateur, ajoutez l'option userquota à la zone d'options de l'entrée de /etc/fstab pour le système de fichiers sur lequel vous voulez des quotas. Par exemple : /dev/ad0s1c /home ufs rw,nosuid,userquota 2 2 Pour des quotas par utilisateur on utilise userquota et pour des quotas par groups, on utilisera groupquota. Pour appliquer des quotas à la fois à l'utilisateur et à son groupe, on combinera les 2 commandes précédentes comme nous l'avons fait pour notre fichier. Il ne vous reste plus qu'à redémarrer afin d'activer la prise en compte de quota et la génération des fichiers /etc/quota.user et /etc/quota/group. Les quotas peuvent etre instaurés sur un système selon plusieurs critères. Tout d'abord en plus de pouvoir appliquer des quotas par utilisateur et/ou par groupes, vous avez la possibilité d'appliquer ces limitations selon differents formats soit en termes d'espace disque, les blocs, soit en terme de fichiers, les inodes. Par ailleurs, il faut ajouter des degrés dans les limitations : strictes ou souples. Les premières suivent les règles de quotas à la lettre, mais lorsqu'un utilisateur atteint son quota, il ne peut plus rien ajouter. Le second degré de limitation offre à l'utilisateur un délai - valable durant une semaine par défaut - lui permettant de ne pas être subitement limité dans son travail par un quota trop strict et d'ajouter ou (surtout) retirer des fichiers pendant ce délai. Cependant s’il n'est pas revenu à un niveau normal une fois le délai écoulé, la limitation devient stricte et il ne peut plus rien ajouter jusqu'à ce qu'il redescende en dessous de sa limitation. Pour éditer nos quotas, nous utiliserons la commande edquota qui va ouvrir un éditeur (vi par défaut) : # edquota Quotas for user eberkut: /usr/home/eberkut: blocks in use: 0, limits (soft = 80, hard = 100) inodes in use: 0, limits (soft = 40, hard = 60) /usr/var: blocks in use: 0, limits (soft = 80, hard = 90) inodes in use: 0, limits (soft = 60, hard = 80) Pour se simplifier l'attribution de quotas, on peut appliquer un quota prototype à une plage d'uid par l'intermédiaire de l'option -p de edquota une fois les quotas définis une première fois : # edquota -p eberkut 1000-10000 La commande quota peut être utilisée afin de connaître les quotas et l'utilisation de l'espace disque d'un utilisateur et/ou d'un groupe. Comme pour la majorité des autres commandes relatives aux informations utilisateurs, seul le root peut aller consulter les quotas de tous comptes et tous groupes. # quota -u eberkut Un autre avantage majeur de FreeBSD, est de disposer, avec /etc/login.conf, d'un fichier centralisé permettant de définir des classes renvoyant à un utilisateur ou un groupe (au sens Unix) afin de spécifier le plus simplement possible plusieurs restrictions touchant à l'authentification, aux permissions, ou aux limitations de ressources des utilisateurs. C'est également un moyen très pratique de limiter les ressources des comptes utilisés par vos services. Les différents attributs sont groupés par classes. Celles-ci représentent donc une politique cohérente à appliquer à un utilisateur ou un groupe, comme déjà suggéré. Mais ces différentes classes ont la particularité de pouvoir être héritées. Ainsi, les valeurs spécifiées dans une classe données peuvent être reprises dans une autre classe grâce à l'attribut 'tc'. Il existe ainsi une classe 'default' qui contient une configuration basique dont tous les utilisateurs pourront hériter (et dont ils héritent effectivement si aucune politique spécifique n'est indiquée). Il peut être alors intéressant d'y ajouter une classe 'root' qui augmentera la rigueur de l'authentification et limitera au plus juste les ressources allouées. La classe 'daemon' quant à elle s'applique à tous les services lancés par /etc/rc au démarrage. De plus, nous pouvons même remplacer certains attributs, ou en ajouter de nouveaux, en les écrivant dans la nouvelle classe, après avoir spécifié un héritage (toujours avec 'tc'). Le format suivi dans login.conf est celui de termcap(5), avec ses bien-connues colonnes séparées par des : ou ses valeurs séparées par des virgules. Un aperçu en est donné dans la manpage getcap(3) : example|an example of binding multiple values to names:\ :foo%bar:foo^blah:foo@:\ :abc%xyz:abc^frap:abc$@:\ :tc=more: Comme vous le voyez, l'attribution se fait un peu de la même manière que pour /etc/sysctl.conf, c'est-à-dire 'nom=valeur'. En plus de pouvoir être modifiés dans login.conf entre deux classes parentes, les utilisateurs peuvent changer certains attributs en plaçant un fichier .login_conf dans leur home directory avec, pour seule classe, 'me'. Les variables que l'utilisateur peut lui-même spécifier ne sont pas nombreuses, puisque authentification, limitation des ressources et comptabilité en sont soustrait. Cela permet tout de même de spécifier dans un fichier unique lu par login(1) des variables d'environnement, par exemple. Cette fonctionnalité est spécifique à FreeBSD. Il existe de nombreux attributs paramétrables, tombant dans plusieurs catégories allant de simples booléens, jusqu'aux chemins d'accès, en passant par des tailles (en b, kb, mb, gb ou tb) et des horaires (en s, m, h, d, w ou y, indiquant ainsi les secondes, minutes, heures, jours, semaines ou mois). Nous continuerons maintenant de suivre la manpage en reprenant la description des attributs selon les catégories de limites de ressources, variables d'environnement, authentification, et comptabilité. Vous trouverez ci-dessous des explications des attributs qui nous ont semblé les plus pertinents. o les limites de ressources - cputime, permet de limiter le temps CPU que peut exploiter temps absolu, pas en pourcentage. Correspond à l'option -f de limits(1). - datasize, indique la taille maximale du segment 'data' d'un processus lancé sous cette classe. Un peu désuet puisque cette limite s'applique aux appels à brk(2) et sbrk(2). Identique à l'option -d de limits(1). - maxproc, subordonné à l'entrée sysctl kern.maxproc, indique le nombre maximal qu'un utilisateur de cette classe peut posséder. Comme le fait judicieusement remarquer la section 8.7 du FreeBSD Handbook, prenez garde à ne pas trop restreindre les utilisateurs compilant beaucoup, utilisant plusieurs sessions, ou simplement prévu pour des serveurs fortement basés sur fork() comme Apache 1.x. Option -u de limits(1). - memoryuse, spécifie la quantité maximale de mémoire utilisée par un processus, auss bien RAM que swap. Voir également 'memorylock' qui a le même sens vis-à-vis des appels à mlock(2). Options -m et -l de limits(1). - openfiles, subordonné à kern.maxfiles, définit le nombre maximal de file descriptors (fichiers, IPC, sockets) qu'un processus peut avoir. Voir l'option -n de limits(1). - sbsize, pour socket buffer size, permet de contrôler la quantité de mémoire allouée pour tous les sockets buffers d'un utilisateur, c'est-à-dire la mémoire utilisée pour les transmissions sur le réseau. Il est certainement soumis aux entrées sysctl net.inet.{tcp,udp}.{send,recv}space, et son intérêt a diminué avec l'augmentation des protections disponibles contre les SYN floods, à cause desquels cet attribut fut créé. Option -b de limits(1). - vmemoryuse, fixe la quantité maximale de tous types de mémoire virtuelle (y compris via mmap()), qu'un processus peut exploiter. - stacksize, que vous retrouverez avec l'option -s de limits(1), indique la taille maximale jusqu'à laquelle le système peut étendre la stack d'un processus. - filesize, précise la taille maximale d'un fichier qu'un utilisateur de cette classe peut détenir. Correspond à l'option -f de limits(1). Notez que les quotas sont prioritaires sur cet attribut. - coredumpsize, subordonné à filesize, permet de définir la taille maximale acceptable pour un coredump. De très gros programmes peuvent en effet générer des coredumps importants, qui finissent éventuellement par remplir un disque ou une partition. Identique à l'option -c de limits(1). Chacun des attributs est valable pour tous processus lancés par un utilisateur concerné par la classe définissant les premiers. Notez que pour des définitions précises de chacun d'entre eux, il faudra vous référer aux manpages de {set,get}rlimit(2) qui sont les fonctions contrôlant ces limites. D'autre part, chacun d'entre eux peut être précisé comme une limite soft ou hard en ajoutant respectivement le suffixe -cur et -max. Sans suffixe, la valeur de l'attribut vaut pour les deux types de limites. Enfin, ces attributs décrivent rapidement toute l'API *rlimit, ce qui explique en partie le temps passé dessus. Nous tâcherons d'être plus succints par la suite. o Les variables d'environnement - lang, permet de spécifier $LANG, la variable d'environnement indiquant votre langue. - manpath, pour le chemin d'accès par défaut au manpages - nologin, le chemin d'accès à un éventuel fichier 'nologin' à afficher juste avant de terminer une tentative d'ouverture de session ayant abouti sur /sbin/nologin. - requirehome, attribut booléen, permet de requerir un répertoire /home valide afin d'autoriser l'ouverture de la session. Ceci permet d'ajouter une couche de sécurité en obligeant chaque utilisateur à passer par un processus d'authentification complet. - priority, définit la priorité initiale des processus lancés par les utilisateurs de la classe, selon la syntaxe de nice(1). Ceci peut faciliter l'imposition de précédence entre utilisateurs et services, par exemple. - setenv, permet de spécifier une liste de variables d'environnement séparées par des virgules. - shell, indique le shell à exécuter après l'authentification d'un utilisateur. Cet attribut est prioritaire sur celui indiqué dans /etc/passwd. - term, détermine le type de terminal - umask spécifie la valeur du masque de création de fichier (ex : 022 = 755), en octal. - timezone, permet d'indiquer la valeur de la variable $TZ, afin de paramétrer votre horaire local. - welcome défini le fichier contenant le message de bienvenue o les variables d'authentification - minpasswordlen permet d'imposer une longueur minimale pour un mot de passe. La longueur Unix typique de 8 caractères est un bon choix pour commencer. Vous pouvez aller jusqu'à 128 caractères. - mixpasswordcase entraînera la génération d'alertes de la part de passwd(1), courant aussi bien parmis les utilisateurs que dans les scripts, si le mot de passe ne comporte que des minuscules, le rendant faible et vulnérable aux attaques par dictionnaire. - passwd_format permet de spécifier l'algorithme utilisé pour le stockage des mots de passe. Par défaut, cet attribut vaut 'des', pour DES, qui est relativement faible, mais assure une grande compatibilité avec les nombreux services et systèmes d'authentification. Vous pouvez le mettre à 'md5' pour disposer de mots de passe MD5 et, plus récemment, à 'blf' pour utiliser Blowfish. - login-backoff indique le nombre de tentatives de login infructueuses au cours d'une unique connexion avant que login(1) ne commence à introduire des délais entre chaque tentatives afin de réduire l'efficacité d'une attaque par brute-force. Dans la même veine, 'login-tries' indique le nombre de tentatives avant que login(1) ne ferme la connexion. - ttys.allow et ttys.deny permettent de spécifier une liste de tty utilisables par les membres d'une classe - host.allow et host.deny offre la possibilité d'indiquer une liste d'hôtes (adresses IP ou nom d'hôte) depuis lesquels on autorise ou pas les logins. Pratique au sein d'un réseau local où les utilisateurs ne risquent pas de changer de poste ou d'adresse. - times.allow et times.deny servent à définir des horaires pendant lesquels les logins sont autorisés ou interdits. Il est de cette manière très simple d'interdire l'accès à certaines machines en dehors des heures habituelles de travail, etc. Attention cependant à la syntaxe ; elle se décompose en [dd[dd]...]start-end où 'dd' indique des jours de la semaine (deux premières lettres des jours en anglais, comme Mo pour Monday), et 'start' et 'end' sont le début et de fin de la limite horaire en format 24h, représentés par quatre chiffres attachés. Exemple : MoTuWeThFr0080-0020 autorise ou interdit l'accès de lundi à vendredi entre 8h et 20h. Remarquez que times.deny est prioritaire sur times.allow en cas de chevauchement des horaires. o comptabilité - accounted est un attribut booléen activant les options de comptabilité pour cette classe, avec le même effet que accton(8). - passwordtime permet de définir la durée de validité des mots de passe - warnpassword indiquera alors quand il sera nécessaire d'avertir l'utilisateur avant d'atteindre passwordtime. - expireperiod indique la durée au bout de laquelle un compte lié à une classe expire. - avec warnexpire, vous pouvez décider combien de temps avec l'expiration de son compte un utilisateur sera prévenu. - avec graceexpire vous indiquez pendant combien de temps un utilisateur peut encore se connecter avant l'expiration définitive. - autodelete permet de définir la durée après expiration au bout de laquelle un compte expiré est effectivement supprimé. L'expiration du compte n'implique en effet aucunement sa suppression automatique. - daytime, weektime et monthtime permettent quant à eux de spécifier la durée maximale de connexion d'un membre d'une classe, à l'échelle du jour, de la semaine et du moins respectivement. - warntime définit le moment auquel il un utilisateur est averti, avant que le temps de session n'ait expiré. Lorsque cette limite est atteinte, 'gracetime' permet de laisser un sursis à ce même utilisateur avant de couper la connexion. Ces valeurs sont exprimées en temps avant que la limite ne soit atteinte. - sessiontime spécifie la durée maximale que peut atteindre une session. - sessionlimit spécifie le nombre maximal de sessions concurrentes, en limitant le nombre de tty simultanément ouverts possible. - ttys.accounted, ttys.exempt, host.accounted, host.exempt ont les mêmes sens et syntaxes que les variables d'authentification ttys.* et host.*, mais pour la comptabilité bien sûr. Je vais maintenant être dans l'obligation de vous décevoir. Votre serviteur ainsi que bien d'autres utilisateurs de FreeBSD ont pu tristement remarquer que plusieurs, si ce n'est toutes, les options de comptabilité proposées à travers login.conf sont sans effet aucun. Les tests limités et la spécificité de plusieurs d'entre elles interdisent de dresser la moindre liste. Il est cependant aisé de voir que plusieurs d'entre elles ne correspondent à aucun symbole dans les sources. J'ai tout de même maintenu leurs descriptions dans l'espoir qu'une âme charitable soit tentée d'implémenter ces fonctionnalités (tâche sans doute rendue plus ardue par la PAM'ification massive de la série 5.x). Vous trouverez ci-dessous un login.conf d'exemple comportant une classe default, une classe root, une classe users pour les utilisateurs, ainsi qu'une classe wheel pour les administrateurs. Enfin, nous configurons une classe daemon qui concernera donc la plupart des services lancés au démarrage. # vi /etc/login.conf ------------------------------------ SNiP ------------------------------------- default:\ :copyright=/etc/COPYRIGHT:\ :welcome=/etc/motd:\ :setenv=MAIL=/var/mail/$,BLOCKSIZE=K,FTP_PASSIVE_MODE=YES:\ :path=/sbin /bin /usr/sbin /usr/bin /usr/local/sbin /usr/local/bin /usr/X11R6/bin ~/bin:\ :nologin=/var/nologin:\ :cputime=unlimited:\ :datasize=unlimited:\ :stacksize=unlimited:\ :memorylocked=unlimited:\ :memoryuse=unlimited:\ :vmemoryuse=unlimited:\ :filesize=unlimited:\ :coredumpsize=unlimited:\ :openfiles=unlimited:\ :maxproc=unlimited:\ :sbsize=unlimited:\ :priority=10:\ :umask=022:\ :minpasswordlen=8:\ :mixpasswordcase=true:\ :passwd_format=blf:\ :login-backoff=3:\ :login-retries=6:\ root:\ :tc=default: :priority=5:\ # une valeur élevée pour memoryuse-max sera nécessaire en cas d'utilisation # d'un server graphique (XFree) users:\ :tc=default: :cputime=2h:\ :filesize=100m:\ :coredumpsize=1m:\ :memoryuse-cur=5m:\ :memoryuse-max=15m:\ :vmemoryuse-cur=5m:\ :vmemoryuse-max=15m:\ :maxproc=32:\ :openfiles=128:\ :priority=20:\ :passwordtime=90d:\ :warnpasswordtime=7d:\ :sessionlimit=3:\ :times.allow=MoTuWeThFr0080-0020:\ :requirehome:\ wheel:\ :tc=users :requirehome:\ :sessionlimit=unlimited:\ :times.allow=unlimited:\ # de même, ici nous donnons pour exemple des utilisations mémoire élevées, # dans l'hypothèse de serveurs chargés daemon:\ :tc=default: :memoryuse-cur=20m:\ :memoryuse-max=60m:\ :vmemoryuse-cur=20m:\ :vmemoryuse-max=60m:\ :filesize=500m:\ :coredumpsize=5m:\ :openfiles=512:\ :maxproc-cur=128:\ :maxproc-max=512:\ :sbsize-cur=4k:\ :sbsize-max=16k:\ :priority=10:\ ------------------------------------ SNiP ------------------------------------- Après chaque modification de login.conf, il est nécessaire de mettre à jour la base de données des utilisateurs afin de mettre en place la nouvelle politique, grâce à cap_mkdb, comme ci-dessous : # cap_mkdb /etc/login.conf Afin de tester des limites de ressources, ou bien de modifier en urgence celles-ci avant une modification de politique, vous disposez de la commande limits(1). Celle-ci vous permettra de modifier les restrictions imposées à un daemon par exemple, au cours de son exécution. Avec les options -S ou -H vous pouvez même spécifier des limites hard ou soft (ou bien les deux confondues, via -B). # limits -E -C www -B -n 1024 La ligne précédente permet par exemple de permettre à l'utilisateur www (qui lance apache httpd sous FreeBSD) d'utiliser jusqu'à 1024 file descriptors (tout étant fichier sous Unix, ceci concerne aussi bien les fichiers au sens classiques que les sockets). Les diverses options sont détaillées dans limits(1). 2.2.3. jail Jail est un mécanisme très puissant qui, comme son nom l'indique, offre la la possibilité d'affiner les privilèges et la gestion des utilisateurs selon un principe, similaire à chroot, d'emprisonnement des processus. Jail va cependant beaucoup plus loin puisqu'il propose la reproduction d'un environnement complet à l'intérieur d'un environnement parent. La fonction jail() se base sur une version renforcée de chroot(). Construire une jail peut s'avérer très utile pour confiner un utilisateur ou service auquel on ne fait aucune confiance ou bien qui risque de menacer l'intégrité et la stabilité du système. Pour cela jail offre un environnement FreeBSD virtuel totalement opérationnel avec ses propres fichiers de configuration ou gestion des utilisateurs, le tout rattaché à un alias IP. De plus, un processus une fois dans la jail est comme derrière une glace sans teint, il évolue librement mais ne peut pas voir au-delà de son environnement tandis que l'hôte de la jail peut tout observer et surveiller. Ainsi, un processus dans la jail ne peut pas en sortir, et vous pouvez déléguer des comptes root au sein de chaque jail. Dans l'hypothèse d'une attaque, l'intrus obtiendrai par exemple d'abord un accès local par un exploit distant, puis un accès root par un exploit local mais se verrait confiner à sa jail (théoriquement, jail étant basé sur chroot(), rien n'est sûr). Jail se pose ici en précurseur du concept plus récent de honeypot, et peut très bien être utilisé comme tel pour inciter et surveiller des intrusions avec un risque limité de compromission totale de la machine, tout en conservant logs et informations à l'abri. Pour conserver son intégrité, une jail restreint les actions possibles par rapport à un environnement complet. Il est ainsi interdit de modifier le kernel (qui reste unique pour l'ensemble du système) et charger des modules kernel, modifier la configuration réseau, monter des systèmes de fichiers, créer des devices, utiliser des raw sockets, modifier la MIB sysctl ou les les securelevels. Tout ce qui n'est pas directement lié au fonctionnement de la jail est interdit. Nous pouvons cependant utiliser comme d'habitude des ports privilégiés, modifiers les permissions des fichiers et utilisateurs à l'intérieur de la jail, etc. afin de réellement cloner un environnement classique. La mise en place effective d'une jail s'effectue à l'aide de la séquence de commandes suivante : # ifconfig fxp0 alias $jail_IP_alias netmask 255.255.255.0 # mkdir -p /jail/jailone # cd /usr/src # export JONE=/jail/jailone # make world DESTDIR=$JONE # cd /etc # make distribution DESTDIR=$JONE NO_MAKEDEV=yes # cd $JONE/dev # ./MAKEDEV jail # ln -sf null /boot/kernel/ # touch ../etc/fstab Nous attribuons une adresse alias IP à notre jail puis nous créons le répertoire /jail/jailone dans lequel sera confiner notre environnement virtuel. La commande make permet alors de construire un système complet avec pour destination la jailone, incluant binaires, fichiers de configuration et arborescence des devices. Notez à la fin le lien symbolique vers le kernel, ainsi que la création d'une fstab vide pour s'assurer du bon fonctionnement de certains programmes. Les jails ne sont pas encore persistantes, il est donc nécessaire de configurer votre rc.conf principal pour conserver la configuration ifconfig à chaque reboot : ifconfig_fxp0="alias $jail_IP_alias netmask 255.255.255.0" Puis il faudra copier la ligne suivante au sein de rc.local pour rendre la jail persistante entre deux redémarrages : # jail /jail/jailone/ $hostname $jail_IP_alias /bin/sh Si vous prévoyez d'utiliser cron ou syslog, nous vous recommandons de recréer entièrement votre fichier de configuration en adaptant les entrées à la jail. Pour l'utilisation de cron, nous vous recommandons de recréer entièrement votre fichier de configuration avec vos propres entrées. Par ailleurs tous les fichiers copiés le sont après configuration complète de l'environnement hôte, ce qui signifie que ces fichiers ont été chmodé, pensez donc à faire de même avec crontab, httpd.conf et named.conf. En utilisant adduser et chpass comme vue précédemment, nous n'avons plus qu'à changer le password du root de la jail, puis à créer un utilisateur par service que nous souhaitons confiner. Nous prendrons comme exemple named et httpd. Si vous désirez autoriser l'accès à votre jail à des utilisateurs préexistants, il va vous falloir copier spwd.db. Pour cela nous n'allons recopier que les informations qui nous intéressent : # fgrep $username /etc/passwd > $JONE/etc/passwd # fgrep $username /etc/master.passwd > $JONE/etc/master.passwd # cd $JONE/etc/ && pwd_mkdb -d master.passwd Pour une sécurisation du système, en marge de fichier de configuration utile que nous avons déjà copié, vous pouvez utiliser les fonctionnalités de chmod, ou même les chflags, pour modifier les permissions sur les correspondants confinés des répertoires /bin, /sbin, /usr/lib, /usr/share/lib afin d'éviter toute modification ou compromission. Nous pouvons également rajouter au sysctl.conf de l'environnement hôte certaines variables concernant les jails. Notez que maintenant que nous avons mis en place une jail, nous nous referons à la jail comme l'environnement jail et à la machine normale comme à l'environnement hôte. Les entrées sysctl suivantes permettent de décider si la jail a le droit de changer son propre nom de domaine, d'utiliser des IPC standards System V pour la communication inter processus lui permettant de communiquer et donc d'altérer des processus en dehors de la jail, et enfin de limiter ou pas l'accès à des types socket plus nombreux et non soumis aux restrictions jail : # sysctl -w jail.set_hostname_allowed=0 # sysctl -w jail.socket_unixiproute_only=1 # sysctl -w jail.sysvipc_allowed=0 Si vous désirez configurer un nombre important de jail, comme ce sera le cas si vous confiner chaque service ou site hébergé indépendamment, pour à la fois plus de sécurité et aussi limiter la taille occupée par la jail, vous pouvez utilisez des pseudo-devices vn exploitant le driver vnode qui permet de traiter un fichier comme un device. Lorsque vous avez décidé de la taille maximale par jail, utilisez 'truncate' pour créer un fichier de cette taille (ici 1 Go) : # truncate 1G jailfile Puis, pour mounter ces fichiers comme des partitions sur lesquelles installer vos jail, utilisez 'vnconfig' pour transformer le fichier précédent en un device (ici vn0c). Si aucun fichier n'est spécifié, vnconfig utilisera la swap pour créer le device. Vous pourrez alors en spécifier la taille avec l'option -S suivi de la taille (en ko, mo, go et to), et la remplir de zéros avec -T, supplantant ainsi l'utilisation de truncate : # vnconfig -e vn0c jailfile ou, pour supprimer le recours à truncate : # vnconfig -S 1g -T vn0c Vous pouvez ensuite le monter et démonter comme d'habitude. Vous devrez insérer ces nouveaux périphériques dans vote fstab afin de les monter au démarrage en tant que partition jail (un périphérique vn par jail). L'intérêt en est la grande souplesse puisque vous pouvez aisément augmenter ou réduire la taille du fichier utilisé pour émuler des quotas. Pour retransformer votre device vn en fichier, exécutez : # vnconfig -u /dev/vn0c Si vous faîtes tourner plusieurs services dans vos jails, il peut arriver qu'une faille de sécurité soit découverte, nécessitant le patching de ce service. Grâce à la virtualisation de la jail, vous pourriez utiliser NFS entre celle-ci et l'environnement hôte. Il existe cependant une solution plus simple consistant à utiliser mount_null qui permet de dupliquer une partie d'un système de fichier afin qu'elle soit vue par un chemin différent. Comme patcher et recompiler nécessite l'accès à /usr/src, vous préservez votre sécurité en montant ce chemin en read-only du point de vue de la jail. # mount_null -o ro /usr/src $JONE/usr/src Il ne vous reste plus alors qu'à appliquer le patch et à recompiler normalement vos binaires userland. A noter tout de même qu'il est recommandé d'utiliser l'utilisateur named depuis lequel bind - qui se placera quelques instants avec les droits root pour récupérer les file descriptor avant de revenir à des privilèges plus raisonnables - sera utilisé. Avec BIND il est également fortement recommandé d'utiliser une clé d'authentification TSIG entre vos serveurs DNS, de limiter les AXFR ou transferts de zone à des serveurs DNS secondaires sûrs ou encore de modifier la réponse à la requête host -l chaos version.bind qui peut donner à l'attaquant des informations sur la version de BIND et donc d'éventuels remote buffer overflow possibles. Si vous utilisez Apache en tant que httpd, n'oubliez pas d'éditer httpd.conf afin de spécifier comme owner l'utilisateur www afin de ne pas avoir recourt à nobody qui plus il sera utilisé, plus il gagnera de drois. Veillez aussi à ne pas surcharger le système et, plus particulièrement, à ne pas atteindre trop facilemenent le nombre de processus maximal par utilisateur (dans un tel cas si le trafic est légitime passez en root jail pour adapter le paramètre par sysctl). Si vous êtes inquiet d'une dépendance quelconque à chroot(), et recherchez des capacités de configuration plus précises, vous pouvez vous tourner vers la réimplémentation partielle de Robert Watson : jailNG. Celle-ci fournit plusieurs améliorations pour ce qui est de la configuration et de la gestion de la jail. Il vous suffit de récupérer le patch sur http://www.watson.org/~robert/jailng/ et de l'appliquer de la façon suivante : # cd /usr/src && patch -p1 < jailng.1.diff Il ne reste plus qu'à recompiler votre kernel. L'utilisation s'effectue ensuite par l'intermédiaire de 'jailctl' disposant à l'heure actuelle de 3 commandes : - 'jailctl create' ou 'destroy' qui prennent en argument le nom de la jail et permettent de créer ou supprimer une jail - 'jailctl join prenant en argument le nom de la jail à rejoindre, le chemin à confiner avec l'option -c, et enfin une commande, telle que le lancement d'un shell. Les droits de chaque jail pourra être gérer plus finement grâce à des entrées sysctl spécifique, de la forme jail.jail_name.entry_name, pour chacun d'entre elles. Ces nouvelles entrées sont les suivantes : # sysctl jail.jail_name.set_hostname_permitted=0 # sysctl jail.jail_name.sysvipc_permitted=1 # sysctl jail.jail_name.socket_ipv4_permitted=1 # sysctl jail.jail_name.socket_unix_permitted=1 # sysctl jail.jail_name.socket_route_permitted=1 # sysctl jail.jail_name.socket_other_permitted=0 # sysctl jail.jail_name.ipv4addr=192.168.1.1 Les jails peuvent - ou non - ainsi modifier leur hostname, les processus en leur sein peuvent utiliser les IPC SysV, les sockets INET, les sockets UNIX, les sockets route, tout autre type de socket, et enfin vous pouvez paramétrer l'adresse IP de la jail. Une petite remarque mais de poids après cet encensement : le projet jailNG est déjà vieux et ne montre plus aucun signe d'activité nulle part depuis longtemps. Le code risque donc de causer de nombreux problèmes, et les nouvelles fonctionnalités tardent à venir, sans doute depuis que Robert Watson, désormais membre de la core team, est à la tête du massif projet TrustedBSD, visant l'implémentation du standard POSIX.1e à partir de FreeBSD 5.x, et l'un des instigateurs du projet CBOSS qui chapeaute plusieurs développements open source dans le domaine de la sécurité. FreeBSD 5.0 a tout de même quelques maigres améliorations comme la configuration de securelevel indépendant pour chaque jail. 2.3. intégrité Le chapitre précédent abordant une sorte de système dans le système, il sort un peu du chemin logique et pas à pas de cet article. N'oubliez donc pas de tenir compte des astuces suivantes dans la configuration de vos environnements jail en plus de vote environnement host. Nous nous intéressons ici à l'intégrité des fichiers et notamment par les permissions et privilèges accordés dans le cadre de l'utilisation ou de la gestion de certains fichiers. Un danger majeur de la sécurité des fichiers est représenté par le bits SUID. Ce bit placé sur un fichier permet de l'utiliser avec les mêmes droits que le propriétaire du fichier ce qui peut se révéler dangereux si le propriétaire est le root. Mais tout d'abord, nous recherchons les fichiers marqués SUID et SGID (similaire à SUID mais à l'échelle d'un groupe) avec find # find / -perm 02000 -o -perm 04000 print Nous obtenons une liste de tous les fichiers ou repertoires avec le bit SUID ou SGID. Parmi tout cela, il y a des exécutables que nous ne voulons pas forcément laisser à la portée de n'importe qui ou bien que nous n'avons pas l'intention d'utiliser. La sélection à effectuer parmi les fichiers SUID est laissée à votre discrétion selon votre utilisation. Cependant, il est fortement recommandé de ne pas laisser des procédures shell en SUID surtout appartenant au root, ceci pouvant éventuellement entraîner l'exécution illégitime de commandes. Pour effectuer nos modifications, nous utilisons l'utilitaire chmod qui nous permet de modifier les permissions allouées à un fichier. Pour calculer les modes que nous désirons appliquer à tel fichier, nous nous basons sur le schéma de permissions suivant : 4000 bit SUID (SetUserID on execution) dont nous avons déjà parlé 2000 bit SGID (Set-Group-ID-on-execution) que nous avons également abordé 1000 sticky bit, sous freebsd ceci ne fonctionne que sur les répertoires et permet aux utilisateurs de supprimer ou renommer uniquement les fichiers dont ils sont propriétaires 0400 permet la lecture par le propriétaire 0200 permet l'écriture par le propriétaire 0100 permet l'exécution d'un fichier par le propriétaire et la recherche au sein d'un répertoire 0040 permet la lecture par les membres du group 0020 permet l'écriture par les membres du group 0010 permet l'exécution d'un fichier par les membres du group et la recherche au sein d'un répertoire 0004 permet la lecture par tous les autres utilisateurs 0002 permet l'écriture par tous les autres utilisateurs 0001 permet l'exécution d'un fichier par tous les autres utilisateurs et la recherche au sein d'un répertoire Lorsque vous faites un ls, ce sont les omniprésents srwxrwxrwx. Vous trouverez plus de précisions sur les permissions et l'utilisation de chmod par un man 1 chmod. Nous pouvons tout d'abord décider de limiter les modifications apportées à certains de nos fichiers de configuration. Nous permettons donc la lecture et l'écriture par l'owner, la lecture par le groupe et aucun droit au reste des utilisateurs # chmod 600 /etc/fstab # chmod 600 /etc/rc.* # chmod 600 /etc/syslog.conf /etc/newsyslog.conf # chmod 600 /etc/ppp/ppp.conf # chmod 600 /etc/ssh/sshd_config # chmod 600 /etc/racoon.conf /etc/ipsec.conf # chmod 600 /etc/passwd # chmod -RH 640 /etc/nessus/ Et vous pouvez en faire de même avec certains répertoires que vous ne souhaitez absolument pas voir compromis. En l'occurrence nous n'autorisons la lecture, écriture et exécution par le root et uniquement l'exécution pour tous les autres. Ceci pourra entraîner à l'avenir des problèmes d'accès, dans ce cas un rapide chmod 755 sur le fichier ou exécutable incriminé résoudra l'affaire. # chmod -RH 755 /bin /sbin /boot # chmod -RH 711 /usr/libexec /usr/lib /usr/bin /usr/sbin # chmod -RH 711 /usr/local/libexec /usr/local/lib /usr/local/bin \ /usr/local/sbin # chmod -RH 644 /var/log Notez que nous pouvons combiner ces opérations avec quelque chose de plus drastique dans le cas où nous disposerions d'un securelevel assez élevé. Ainsi, si vous recompilez ou mettez à jour rarement vous pouvez également appliquer la commande chflags étudiée plus haut sur les répertoires et fichiers de votre choix en prenant garde aux problèmes ultérieurs en cas de compilation ou de configuration. Profitez également es messages d'erreurs de chmod pour vérifier vos bits SUID/SGID et le désélectionner sur les binaires sensibles. Nous allons ensuite nous inquiéter de l'intégrité de nos fichiers et de notre système. Pour cela nous allons tout d'abord nous servir de mtree. Mtree peut être considéré comme un vérificateur d'intégrité système intégré dans le même style que AIDE ou TripWire. Il va nous permettre de générer une spécification de notre file system contre laquelle comparer régulièrement notre système afin de détecter d'éventuelles modifications illégitimes. Pour générer notre spécification nous entrons la commande suivante # mtree -s 31337 -K cksum -K sha1digest -K uname -x -c -p /your/path > \ /etc/mtree/file.spec L'option s permet d'obtenir un unique checksum pour tous les fichiers auxquels on a appliqué le keyword 'cksum' et nécessite un chiffre en keyword (à conserver à l'abri). L'option K précise un keyword à appliquer qui sera stocké dans la spécification et comparé plus tard sachant que par défaut on a les keywords flags (pour les file flags), gid (pour le group), mode (pour les permissions en octal), nlink (pour les hard link), size (pour la taille), link (pour les liens symboliques), time (pour le dernier timestamp de modification), et uid (pour l'owner) auxquels nous ajoutons cksum pour obtenir un checksum selon le schéma de la commande cksum(1), sha1digest pour obtenir une signature à l'aide de l'algorithme à sens unique SHA-1 et uname afin d'obtenir l'owner sous forme de nom. Ensuite l'option x permet de ne pas sauter les points de mountage, c permet de sortir les informations sur l'output standard et p précise le chemin. Ce qui nous donnera en pratique # mtree -s 31337 -K cksum -K sha1digest -K uname -x -c -p /bin > \ /etc/mtree/bin.spec # mtree -s 31337 -K cksum -K sha1digest -K uname -x -c -p /sbin > \ /etc/mtree/sbin.spec # mtree -s 31337 -K cksum -K sha1digest -K uname -x -c -p /usr/libexec \ > /etc/mtree/libexec.spec # mtree -s 31337 -K cksum -K sha1digest -K uname -x -c -p /usr/lib > \ /etc/mtree/lib.spec # mtree -s 31337 -K cksum -K sha1digest -K uname -x -c -p \ /usr/share/lib > /etc/mtree/sharelib.spec # mtree -s 31337 -K cksum -K sha1digest -K uname -x -c -p /boot > \ /etc/mtree/boot.spec Puis pour comparer nos spécifications et communiquer les résultats au root avec un minimum d'indentation, nous n'avons plus qu'à effectuer # mtree -x -i -f file.spec | mail -s 'mtree results' root Une bonne idée est de lancer les premières commandes une fois après chaque recompilation du système et la vérification une fois par semaine ou bien dès que vous avez un doute sur l'intégrité de votre système. Tout cela bien sûr par l'intermédiaire de cron. Vous pourrez trouver plusieurs améliorations dédiées à l'utilisation de mtree en tant qu'outil de vérification d'intégrité - notamment son utilisation via /etc/periodic/security - à http://people.freebsd.org/~fanf/FreeBSD/. Un dernier outil très utile que nous allons utiliser pour nous assurer de l'intégrité de notre système sera KSEC pour Kernel Security Checker. KSEC ne fait pas partie de la distribution officielle FreeBSD et n'est pas non plus dans les ports. KSEC est l'adaptation à FreeBSD de l'outil de sécurité Linux KSTAT (Kernel Security Therapy Anti Trolls) par s0ftpr0ject. Pour vous procurer l'un ou l'autre, rendez vous à http://www.s0ftpj.org/en/site.html. Ces outils fonctionnent selon une conception hautement paranoïaque du système et décide donc de ne prendre leurs informations que du kernel en utilisant la librairie KVM et en limitant les opérations sur le syscall ioctl(). Il va donc nous servir à détecter la présence de backdoors dans le système sous leur forme la plus redoutable, les modules kernel. KSEC est si poussé qu'il peut nous servir de remplacement à ifconfig pour la lecture. En effet, en observant directement les structures ifnet ou les files descriptor bpf, il peut détecter si une interface se trouve ou pas en mode promiscuous révélateur d'un sniffer et même fournir des informations et statistiques sur le trafic ou l'état des interfaces. Ces vérifications peuvent être effectuées à travers les opérations suivantes pour l'interface fxp0 -- préciser 'all' pour toutes les interfaces disponibles : # ksec -i fxp0 Mais KSEC va également nous servir à détecter une altération de notre intégrité système en vérifiant si les adresses indiquées dans notre syscall table sont effectivement les mêmes qu'annoncées par /dev/kmem. La modification de la syscall table est une méthode très répandue dans la conception de backdoors LKM mais qui devient un peu ancienne et peut se voir remplacer par des méthodes d'hijacking des syscalls ou des pointeurs vers la syscall table, ou encore d'un patching de /dev/kmem pour éviter ce type de détection. C'est sur ce dernier point que l'utilisation des securelevels, empêchant toute modification de /dev/kmem, est judicieuse. Il peut également effectuer différentes opérations de vérification d'intégrité des protocoles ou d'intégrité des fichiers linkés entre eux. Il ne nous reste plus qu'à effectuer une compilation de ces options afin d'obtenir un rapport complet d'intégrité système : # ksec -i interface -b -k -p Par ailleurs nous vous recommandons d'essayer en cas de doutes à la suite de rapports mtree et ksec, d'effectuer une ultime vérification à l'aide de chkrootkit qui lui se base essentiellement sur des signatures connues signalant des fichiers trojanisés mais fournit également une petite batterie de tests dans un style similaire à KSEC mais moins kernel-dependant. Voir http://www.chkrootkit.org et les ports security pour plus d'informations. 2.4. secure shell SSH signifie Secure Shell. A l'origine SSH est un remplacement des Berkeley r* commandes tels que rsh, rlogin ou rcp considérées comme très peu sûres. SSH utilise pour sécuriser la transmission un tunnel crypté entre les 2 machines. SSH a rapidement dépassé toutes les attentes et il est devenu un remplacement intéressant pour telnet ou ftp lorsqu'ils sont nécessaires pour des utilisateurs réguliers possédant un compte sur la machine en leur offrant une méthode d'accès à distance hautement sécurisée et très bien supportée. En particulier OpenSSH est une version libre et recodée du protocole SSH et fournit un client et un serveur pour des nombreuses plate-formes unix. C'est lui que nous allons maintenant utiliser pour la configuration de SSH. Tout d'abord nous allons éditer le fichier de configuration du daemon sshd. # ee /etc/ssh/sshd_config ------------------------------------ SNiP ------------------------------------- # This is ssh server systemwide configuration file. Port 22 # évitez SSHv1 soumis à plusieurs vulnérabilités Protocol 2,1 # lorsque vous copierez ce fichier pour votre jail user pensez à mettre ici # l'alias de votre jail. ListenAddress 127.0.0.1, public_IP, jail_IP HostKey /etc/ssh/ssh_host_key HostKey /etc/ssh/ssh_host_rsa_key HostKey /etc/ssh/ssh_host_dsa_key ServerKeyBits 768 LoginGraceTime 60 KeyRegenerationInterval 3600 RhostsAuthentication no RSAAuthentication yes PubkeyAuthentication yes # ordre de préférences des algorithmes de chiffrement et d'authentification Ciphers blowfish-cbc,aes256-cbc,aes192-cbc,aes128-cbc,3des-cbc,cast128-cbc,arcfour MACs hmac-sha1,hmac-md5,hmac-ripemd160,hmac-sha1-96,hmac-md5-96 # envoi d'un message après un intervalle donné # et deconnexion après plusieurs envois KeepAlive yes ClientAliveInterval 30 ClientAliveCountMax 5 # Pour eviter les similis flooding du a des tentatives de connexion repetees, # nous installons une sorte de quotas au niveau de la gestion des connexions. # 10 pour le nombre de connexions non authentifiees, 40 pour le pourcentage de # refus apres le premier nombre atteint, et 50 signifiant qu'au bout de 50 # tentative toute connexion non authentifiee est refusee. MaxStartups 10:40:50 # ici nous eliminons les vulnerabilites lies aux fichiers ~/.rhosts et # ~/.shosts et a leurs relations de confiance. IgnoreRhosts yes # verifier permissions et ownership des fichiers et du /home avant d'accepter # un login StrictModes yes X11Forwarding no X11DisplayOffset 10 PrintMotd yes # Syslog SyslogFacility AUTH LogLevel DEBUG # Ci-dessous nous privilegions l'utilisation de cles RSA et DSA pour # l'authentification au lieu du password PasswordAuthentication no # si vous choississez de mettre l'option precedente a yes, ajoutez celle # ci-dessous pour interdire les passwords vides # PermitEmptyPasswords no # désactive l'authentification s/key SkeyAuthentication no KbdInteractiveAuthentication yes ChallengeResponseAuthentication no # ces blocs sont relatifs a l'authentification kerberos #KerberosAuthentication no #KerberosOrLocalPasswd yes #AFSTokenPassing no #KerberosTicketCleanup no #Kerberos TGT Passing does only work with the AFS kaserver #KerberosTgtPassing yes PermitRootLogin no CheckMail yes UseLogin yes # nous ne le recommandons pas du fait de sa relative experimentalite mais cette # ligne vous permet le cas echeant d'utiliser sftp. #Subsystem sftp /usr/libexec/sftp-server ------------------------------------ SNiP ------------------------------------- Il ne nous reste plus maintenant qu'à éditer encore une fois le fichier rc.conf afin de s'assurer que sshd se lancera bien au démarrage. Nous transformons donc la ligne sshd_enable="NO" en sshd_enable="YES" et nous lui adjoinions également la ligne sshd_flags="-4" afin de limiter l'utilisation à des connexions IPv4. Pour générer vos clés il vous suffira alors d'exécuter ssh-keygen ; bien que celui-ci soit d'hors et déjà utilisé par rc.network avec sshd_enable. Vous pourrez enfin décider de ne pas offrir de shell à vos utilisateurs distants. Ceci peut être effectuer à l'aide de chpass ou de chsh en précisant comme shell /sbin/nologin. # chsh -s /sbin/nologin user 2.5. logging Nous allons maintenant nous pencher sur les facilites que nous offre FreeBSD dans le logging des diverses activités système et utilisateur. Nous allons plus particulièrement étudier le system accounting, le system logging et l'analyse de ces logs par des moyens automatisés. Sous FreeBSD, nous avons la possibilité d'activer le system accounting qui nous offre la possibilité d'enregistrer et de récapituler les commandes exécutées et nous permet de conserver des informations détaillées sur les ressources système utilisées, leur répartition entre les utilisateurs, et de surveiller le système. Pour ce faire, nous disposons de accton et de sa. Accton permet d'activer ou de désactiver le system accounting comme dans l’exemple ci-dessous : # accton /var/account/acct Nous spécifions ici un fichier vers lequel sera redirige les donnes de l'accounting, pour désactiver il suffit d'exécuter la même commande sans le fichier en argument. Pour consulter les donnes de l'accounting il suffit d'exécuter sa avec un classement par utilisateur # sa -u Nous obtenons ainsi des statistiques détaillées de l'activité système par utilisateurs. Vous pouvez également vous servir de l'option rc.conf accounting_enable="YES" au même effet. Nous avons également a notre disposition la famille syslog, au premier rang de laquelle nous trouvons syslogd qui nous permet d'enregistrer les messages d'erreurs et autres messages systèmes dans le répertoire /var/log. Pour l'activer, nous éditons encore une fois rc.conf pour y ajouter les entrées suivantes : syslogd_enable="YES" syslogd_flags="-ss -m 0" Nous avons de plus ajoute des flags faisant en sorte que le daemon syslog fonctionne en secure mode sans possibilité de log ou de transmission depuis l'extérieur. Puis pour affiner l'enregistrement des messages, nous allons editer /etc/syslog.conf. Syslog.conf possède toute une syntaxe particulière : o Les blocs de directives sont classes par programme o les directives sont de la forme facility.level suivi de la destination des messages qui peut aussi bien etre un fichier qu'un utilisateur ou un périphérique. Les différentes facilities sont : - AUTH, rapporte les messages du système d'autorisation comportant des programmes tel que login, su ou getty - AUTHPRIV, est similaire a AUTH mais permet de limiter la lecture du fichier de log - CRON, est relatif au script cron aborde plus bas - DAEMON, est relatif aux daemons système qui ne beneficient pas d'un champ facility dédié - FTP, se reporte aux daemons ftpd et tftpd - KERN, rapporte les messages générés par le kernel - LPR, est relatif à tous les périphériques et outils d'impression tels que lpr, lpc, lpd - MAIL, permet de loguer les messages relatifs au courrier - NEWS, similaire a MAIL mais pour usenet - SECURITY, concerne les sous systèmes de sécurité tels que IPF ou ipfw - SYSLOG, est relatif aux messages générés par syslogd lui-même - USER, indique les messages issus de processus utilisateur ne relevant d'ancune des catégories précédentes, c'est donc la valeur par défaut si rien n'est spécifié. - UUCP, désigne la facility se rapportant au protocole Unix-to-Unix Copy Program - local0 à local7, désignent des facilities variables qui peuvent être utilisés ponctuellement par certains softs - * l'asterisque représente ici l'ensemble des entrées précédentes Suivis du champ level : - EMERG, alerte générale, habituellement diffusée à tous les utilisateurs - ALERT, désigne une alerte nécessitent une attention/correction immédiate - CRIT, conditions critiques telles que des problèmes de périphériques - ERR, relatif aux messages d'erreur - WARNING, relatif aux messages d'alertes basiques - NOTICE, désigne un évènement pas forcement en rapport avec un erreur mais qui peut demander une gestion particulière - INFO, génère de simples messages à titre d'information - DEBUG, se rapporte à des messages permettant d'approfondir le compréhension du fonctionnement d'un logiciel et donc éventuellement de le debugger en cas de problème - NONE, permet de désactiver ce champ Nous pouvons également utiliser des opérateurs de comparaison comme < (plus petit que), = (égal), et > (plus grand que) afin de préciser plusieurs niveaux de logging pour un même facility. ------------------------------------ SNiP ------------------------------------- # facility destination *.= action from to - Les commandes peuvent être 'add' pour ajouter une règle, 'delete' pour supprimer une règle individuellement et 'flush' pour la totalité des règles, ainsi que 'show' ou 'list' pour avoir les règles actuelles. Chaque règle doit avoir un numéro unique pour éviter la confusion et les règles sont classées selon l'ordre du matching. - Les actions peuvent être 'allow' pour laisser passer un paquet, deny pour le refuser silencieusement et 'reject' pour envoyer un icmp host unreachable (erreur spécifiable avec les actions 'unreach' et 'reset'), 'check-state' afin de vérifier une correspondance avec les règles dynamiques, ou encore 'fwd' suivi de l'IP et éventuellement du port si vous possédez des IP routables. Nous avons également la possibilité de logger - comme action unique ou supplémentaire aux précédentes - avec 'log' auquel vous pouvez ajouter 'logamount' pour overwriter l'option IPFIREWALL_VERBOSE_LIMIT. - Les protocoles peuvent être 'all' ou 'ip' pour faire correspondre tous les protocoles, ou bien le nom ou numéro du protocole désiré en accord avec /etc/protocols. - Nous pouvons ensuite préciser la source après le champ 'from', la destination après le champ 'to' ainsi que les ports juste après l'adresse source ou de destination. Notez également la présence de 'me' permettant de récupérer l'IP de notre firewall depuis ifconfig ce qui s'avère très utile en cas de configuration dynamique. Enfin, pour les ports ou les adresses, IPFW supporte désormais les opérateurs logiques. - Les mots clés ifspec nous permettant de travailler sur les interfaces. Nous pouvons par exemple spécifier le matching des paquets uniquement en ingress avec 'in' et en egress avec 'out', ou les interfaces vérifiées avec 'via' suivi de l'interface, recv pour ne vérifier que l'interface de réception et xmit pour ne vérifier que l'interface d'envoi. - Pour la stateful inspection, 'keep-state' annonce un suivi dynamique pour ce filtre, 'setup' matche les paquets avec le flag SYN uniquement, 'etablished' concerne les paquets avec les flags ACK ou RST. Mais vous pouvez également préciser des 'ipoptions' (lssr, ssrr, rr, ts) ou 'tcpoptions' (mss, window, cc, sack, ts) ainsi que des 'tcpflags' (fin, syn, rst, psh, ack et urg) ou des 'icmptypes' (0, 3, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18). D'autre part, le mot clé 'limit' a été introduit récemment (4.5-RELEASE) pour permettre de limiter le nombre de connexions simultanées par association pour un filtre donné, ceci en précisant l'adresse et/ou le port source, l'adresse et/ou le port de destination puis la limite de connexions. Sachez enfin que vous pouvez au niveau du firewall lui-même filtrer les utilisateurs via les options 'uid' et 'gid' suivis de leur valeur. Enfin, face à la menace d'abus de trust relationship par les tunnels IPSEC ESP, il est désormais possible, depuis FreeBSD 4.9, de filtrer les connexions initiées via de tels tunnels, ce grâce au mot-clé 'ipsec'. Attention, cette option n'est valable que si vous utilisez des tunnels gif pour monter votre tunnel. Pour obtenir la totalité des champs et notamment une description de l'utilisation de dummynet comme traffic shaper basique, reportez vous à la page de man d'ipfw. Mais dans ce domaine nous vous recommandons vraiment ALTQ développé dans le cadre du projet KAME (et donc régulièrement mergé) implémentant notamment les disciplines CBQ, WFQ/SFQ ou encore HFSC ainsi que les algorithmes RED et ses variantes RIO ou Blue et enfin les extensions ECN, RSVP (avec CBQ et HFSC) ainsi qu'un support pour le modèle DiffServ. Plus d'informations sur le site d'ALTQ, http://www.csl.sony.co.jp/person/kjc/software.html#ALTQ. L'intégration définitive de ALTQ au main tree FreeBSD est en cours pour la release 5.0 Notez pour en finir avec les commandes ipfw qu'il existe un intéressant patch depuis FreeBSD 4.3 dit lifetime qui permet d'imposer une timeout à une transmission via une règle du firewall qui la bloquera une fois ce timeout atteint. Ce patch n'étant pas intégré au système de base, nous ne nous attarderons pas sur son utilisation. Vous pourrez trouver toutes les informations nécessaires à l'installation du patch et à l'utilisation du nouveau keyword à http://www.aarongifford.com/computers/ipfwpatch.html. Lorsque vous lancez votre machine avec les options activant le firewall pour la première fois, environ 200 règles basiques sont automatiquement générées. Nous commençons donc par purger nos règles : # ipfw flush Au début du ruleset, nous définissons quelques variables comme la commande ipfw avec l'option -q pour un output discret, le masque de notre réseau interne, l'adresse de notre jail, et nos interfaces d'entrée et de sortie. Nous rejetons ensuite sur l'interface d'entrée tous les paquets avec pour source une adresse réservée non routable ou bien l'adresse de notre réseau interne, et nous journalisons ces cas manifestes de spoofing. Ces adresses sont répertoriées sur le site de l'IANA et dans la RFC 1918. Puis nous faisons diverger le trafic pour passer par natd pour la translation d'adresses et de ports vers nos adresses privées. Cette règle est suivie de la vérification de chaque paquet contre la state table pour savoir si il appartient ou non à une connexion déjà acceptée. Nous pouvons ensuite rejeter tous les paquets en état TCP établi ou les fragments IP puisqu'ils seront assurés de ne pas faire partie d'une connexion en cours. Puis nous autorisons certaines communications comme DNS dont nous aurons repris les adresses dans /etc/revolv.conf, suivi de l'administration de la machine via SSH, et enfin nous autorisons Racoon, Argus et Nessus à communiquer. Pour les clients derrière le firewall, nous autorisons aussi en sortie les connexions ftp, smtp, ssh, http, pop3, ntp, imap ainsi que https, ircs, pop3s. Viennent ensuite les restrictions ICMP utilisé aussi bien en scanning qu'en DoS. Nous laissons passer depuis l'extérieur les les echo reply, destination unreachable, time exceeded, parameter problem, et timestamp reply. Vers l'extérieur, nous autorisons les echo request, time exceeded (fragment reassembly) et les timestamp request. Avec ceci, nous avons le minimum pour assurer le path discovery, la vérification de connectivité et la détection. Pour appliquer maintenant un filtrage de paquets aux services fournit par une jail, nous faisons comme si c'était un filtrage sur l'environnement hôte, seulement nous y substituons l'alias privée de la jail que nous filtrons en passant par l'interface de loopback lo0. Lors de la création de vos règles, faites attention à l'ordre dans lequel vous les placez dans votre ruleset, aux interfaces sur lesquelles il faut les appliquer et à leur destination. Comme spécifié par les implementations notes de ipfw(8), le nombre de fois où un paquet est inspecté varie : de une fois pour les paquets ayant une extrémité en local et pour les paquets bridgés, à 2 fois pour les paquets dont les 2 extrémités sont locales ou pour les paquets forwardés. La seule manière de changer ce comportement est à travers sysctl : # sysctl -w net.inet.ip.fw.one_pass=1 Mais soyez extrêmement prudent dans ce cas lors de l'écriture de ces règles pour ne pas laisser entrer n'importe quel paquet construit spécialement après analyse des règles. Prendre en compte toutes ces informations, c'est vérifier qu'un paquet sera bien inspecté et au bon endroit. Pour finir, nous refusons tout autre trafic que celui expressément autoriser et pour plus de sûreté. Voir ci-dessous le fichier /etc/rc.firewall final. ------------------------------------ SNiP ------------------------------------- # variables ... fwcmd="ipfw -q" net="192.168.0.0" mask="255.255.255.0" jail="192.168.0.2" intif="fxp0" extif="fxp1" forbidden="192.168.0.0/16,172.16.0.0/12,10.0.0.0/8,127.0.0.0/8,224.0.0.0/3,\ 169.254.0.0/16,0.0.0.0/8,192.0.2.0/24,204.152.64.0/24" ${fwcmd} -f flush # adresses réservées ${fwcmd} add 201 deny log all from ${forbidden},${net}:${mask} to any in via \ ${extif} # divert vers natd ${fwcmd} add 300 divert 8668 all from any to any in via ${extinf} # vérification par rapport à la state table ${fwcmd} add 400 check-state ${fwcmd} add 401 deny tcp from any to any in established ${fwcmd} add 402 deny ip from any to any in frag # communication DNS, SSH, Racoon, Argus et Nessus ${fwcmd} add 403 allow udp from ${net}:${mask} to primary_DNS 53 in keep-state ${fwcmd} add 404 allow tcp from any to me 22 keep-state setup limit src-addr 5 ${fwcmd} add 405 allow udp from any 500 to any keep-state ${fwcmd} add 406 allow udp from any to any 500 keep-state ${fwcmd} add 407 allow tcp from any to any 561,3001 keep-state limit dst-addr 2 # communications vers des serveurs classiques ${fwcmd} add 408 allow tcp from ${net}:${mask} to any \ 20,21,22,25,80,110,123,143,443,994,995,6667 keep-state setup # limitations ICMP (ping, Van Jacobson's traceroute...) ${fwcmd} add 500 allow icmp from any to ${net}:${mask} in icmptypes \ 0,3,11,12,13,14 ${fwcmd} add 501 allow icmp from ${net}:${mask} to any out icmptypes 1,8,11 ${fwcmd} add 502 allow udp from ${net}:${mask} to any in 33400-33500 ${fwcmd} add 503 deny log icmp from any to any # redirection services jail ${fwcmd} add 602 allow udp from any to ${jail} 53 in keep-state via lo0 ${fwcmd} add 603 allow tcp from any to ${jail} 80,443 in keep-state setup via \ lo0 # Restrictive stance : everything not explicitely allowed is forbidden. ${fwcmd} add 900 deny log all from any to any ${fwcmd} add 901 deny log all from any to ${jail} via lo0 ------------------------------------ SNiP ------------------------------------- Pour que ces règles soient appliquées, vous devez soit redemarrer, soit relancer init qui initialisera ces règles. # kill -HUP init Le NAT pour Network Translation Adress est un mécanisme à l'origine créé pour palier à la pénurie d'adresses IP disponibles. Il permet d'utiliser une gateway qui pour chaque communication va rediriger les transmissions entre adresses externes routables et adresses internes non routables en réécrivant les entêtes correspondantes et en stockant les informations de correspondances dans une hash table. Ainsi nous n'utilisons qu'une seule IP routable qui est celle de la gateway NAT. Le NAT est un mécanisme de partage de connexion intéressant mais il ne fera pas l'affaire en cas de load balancing puisque sa méthode de queueing est basé sur un Weighted Round Robin qui sert à tour de rôle chaque queue en attente, or le NAT par WRR traite la priorité selon l'implémentation DiffServ Assured Forwarding rejetant ainsi les paquets basse priorité sous fortes contraintes. De plus le débit de congestion du NAT est assez bas, encore plus si vous utilisez natd qui est userland entraînant une copie depuis le kernel vers le userland. Préférez ipnat/ipf en module kernel pour de meilleures performances. L'autre couple étant ipfw/natd, nous allons maintenant voir une configuration minimale de natd afin de relayer les requêtes de notre hôte vers notre jail ou toute autre machine se trouvant derrière notre FreeBSD. Pour commencer, nous éditons le fichier rc.conf pour y ajouter les lignes suivantes gateway_enable="YES" natd_enable="YES" natd_interface="fxp0" natd_flags="-f /etc/natd.rules" Ainsi natd sera lancé à chaque démarrage avec comme fichier de configuration natd.rules, que nous allons maintenant éditer. Pour plus d'informations sur les règles utilisées, reportez vous à la man page. Nous ne faisons qu'une rapide introduction en rapport avec notre configuration sécurisée. Cependant, notez que la redirection via natd peut se faire à partir de l'adresse avec redirect_adress, par port avec redirect_port et par protocoles avec redirect_proto. Une dernière règle peut s'avérer très utile pour les utilisateurs d'IRC et FTP : la règle punch_fw suivi de basenumber:count respectivement le numéro de la règle de départ suivi du nombre de règles dynamiques pouvant être créées. ------------------------------------ SNiP ------------------------------------- log yes deny_incoming no use_sockets yes # alloue une socket limitant les conflits de ports # dynamiques same_ports yes # tente d'utiliser le même port pour la translation verbose no port natd unregistered_only yes # NAT uniquement pour les adresses type RFC 1918 log_ipfw_denied yes # log les paquets non ré-injecté pour cause de # blocage par ipfw (utile pour debugger) # DNS redirect_port udp jail_IP_alias:53 public_IP_adress:53 # HTTP et HTTPS # LSNAT > RFC 2391 redirect_port tcp jail_IP_alias:80,443 80,443 redirect_adress tcp www1_IP:80, www2_IP:80 jail_IP_adress:80 # SSH sur la seconde jail redirect_port tcp jail_user_IP_alias:22 # static NAT pour d'autres machines redirect_address internal_IP1 public_IP redirect_address internal_IP2 public_IP redirect_address internal_IP3 public_IP ------------------------------------ SNiP ------------------------------------- Notez que natd intègre naturellement des fonctionnalités de suivi de connexions. Donc, lorsque vous configurez une passerelle pour utiliser natd, il n'est plus vraiment utile d'utiliser la stateful inspection au niveau d'ipfw. Vous pouvez simplement configurer un firewall statique et natd s'occupe de l'inspection d'états pour les connexions relayées. Si vous sentez encore le besoin de conserver des règles keep-state, alors le mot clé skipto renvoyant à une autre règles peut s'avérer utile. 3. Outils Nous allons nous pencher sur certains outils plus ou moins en rapport direct avec le système mais qui ne sont pas forcément disponible par défaut ou alors par les ports mais pas à jour, ou encore qui sont une caractéristique du système bien spécifique. Ainsi nous allons ici découvrir quelques outils permettant de renforcer notre sécurité aussi bien de manière proactive que réactive. 3.1. TCPdump TCPdump est l'outil ultime pour sniffer le trafic d'un réseau afin d'effectuer son debugging. Il nous permettra de capturer tout ou partie du trafic réseau local afin de nous permettre de l'analyser afin de vérifier le bon fonctionnement de nos configurations réseau. Pour ce faire TCPdump se base sur la couche système BPF pour Berkeley Packet Filter afin d'intercepter les trames Ethernet et les paquets IP transitant par la machine en mode promiscuous (mode où le Network Interface Card ou NIC peut voir l'ensemble du trafic réseau) selon des expressions bpf similaires aux concepts d'expressions régulières. Cette méthode de capture par BPF est fournit par la librairie libpcap facilitant énormément le développement de sniffers évolués. TCPdump fournit un nombre d'options impressionnant dont nous aborderons les plus utiles ici. Tout d'abord à chaque capture nous vous recommandons la syntaxe suivante : # tcpdump -X -s 1500 -e -n -i fxp0 Cette ligne de commande permet d'obtenir un dump à la fois en hexa et ascii, d'une longueur de 1500 octets, affichant les informations d'en-