Un firewall est un logiciel permettant de filtrer les paquets réseau. Le logiciel le plus utiliser sur les systèmes GNU/Linux est iptables. Il permet de configurer des filtres en espace kernel grâce aux modules Netfilter.
La chose la plus importante avec un firewall est de filtrer les paquets en entrée. Pour attaquer un serveur, un pirate doit établir une connexion avec celui-ci. Filtrer les paquets venant de l'extérieur est une bonne manière d'éviter bon nombre de problèmes.
iptables utilise 2 chaînes nommées INPUT and OUTPUT. INPUT contient les règles pour filtrer les paquets venant de l'extérieur et OUTPUT contient les règles permettant de filtrer les paquets voulant se diriger vers l'extérieur de la machine.
Note : Toutes les fonctions ici sont utilisées dans une script shell et dépendent d'autres éléments. Regardez le script final pour voir comment les utiliser.
Une bonne pratique pour un firewall est de jeter tous les paquets par défaut. Avec iptables ceci est relativement simple à faire :
deny_everything() { print_debug "Denying all connections" iptables -t filter -P INPUT DROP iptables -t filter -P FORWARD DROP iptables -t filter -P OUTPUT DROP end_debug $? }
iptables est peut-être déjà configuré lorsque l'on souhaite le reconfigurer. Il peut donc être intéressant de vider les tables afin de redémarrer une configuration de 0 :
cleanup_tables() { print_debug "Cleaning up tables" iptables -t filter -F iptables -t filter -X end_debug $? }
Si vous utiliser un serveur déjà en production il se peut qu'il est déjà accepté des connexions, les conserver comme actives peut être pratique. Ainsi, vous ne serez pas déconnecté si vous êtes connecté via SSH.
dont_break_connections() { print_debug "Keeping active connections" iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT end_debug $? }
La chose suivante à prendre en compte est d'autoriser la boucle locale sinon cela risque de poser des problèmes avec certains logiciels.
allow_loopback() { print_debug "Allowing loopback" iptables -t filter -A INPUT -i lo -j ACCEPT iptables -t filter -A OUTPUT -o lo -j ACCEPT end_debug $? }
Accepter des connexions sur un port avec iptables est simple. La syntaxe est plutôt claire. Il faut tout d'abord spécifier la chaîne à utiliser, INPUT ou OUTPUT, avec l'option -A, ensuite on donne le protocole de la couche transport (voir modèle OSI) à utiliser, en général soit TCP soit UDP, avec l'option -p et enfin on donne le numéro du port à ouvrir avec –dport. Cela nous donne donc une ligne de la forme :
iptables -t filter -A INPUT -p tcp --dport 80 -j ACCEPT iptables -t filter -A OUTPUT -p tcp --dport 80 -j ACCEPT
Maintenant vous devriez pouvoir comprendre ce script. Il utilise des fonctions pour configurer iptables.
### BEGIN INIT INFO # Provides: firewall # Required-Start: $local_fs $network # Required-Stop: $local_fs $network # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Configure iptables (IPv4) # Description: Setup basic rules for iptables (IPv4) ### END INIT INFO #!/bin/bash . /lib/lsb/init-functions # In debug mode there will be more outputs # 0 to disable # 1 to enable DEBUG=0 # Print message only in debug mode # Usage: print_debug ${message} ${return_code} # ${return_code} is optional print_debug() { [ ${DEBUG} -eq 0 ] && return 0 [ $# -ne 1 ] && return 1 log_action_begin_msg ${1} } end_debug() { [ ${DEBUG} -eq 0 ] && return 0 [ $# -ne 1 ] && return 1 log_action_end_msg ${1} } deny_everything() { print_debug "Denying connections" iptables -t filter -P INPUT DROP iptables -t filter -P FORWARD DROP iptables -t filter -P OUTPUT DROP end_debug $? } accept_everything() { print_debug "Accepting all connections" iptables -t filter -P INPUT ACCEPT iptables -t filter -P FORWARD ACCEPT iptables -t filter -P OUTPUT ACCEPT end_debug $? } cleanup_tables() { print_debug "Cleaning up tables" iptables -t filter -F iptables -t filter -X end_debug $? } dont_break_connections() { print_debug "Keeping active connections" iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT end_debug $? } allow_loopback() { print_debug "Allowing loopback" iptables -t filter -A INPUT -i lo -j ACCEPT iptables -t filter -A OUTPUT -o lo -j ACCEPT end_debug $? } deny_spoofing() { print_debug "Denying spoofing" iptables -N SPOOFED iptables -A SPOOFED -s 127.0.0.0/8 -j DROP iptables -A SPOOFED -s 169.254.0.0/12 -j DROP iptables -A SPOOFED -s 172.16.0.0/12 -j DROP iptables -A SPOOFED -s 192.168.0.0/16 -j DROP iptables -A SPOOFED -s 10.0.0.0/8 -j DROP end_debug $? } misc_config() { print_debug "Misc configurations" echo 1 > /proc/sys/net/ipv4/tcp_syncookies echo 0 > /proc/sys/net/ipv4/ip_forward echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts echo 1 >/proc/sys/net/ipv4/conf/all/log_martians echo 1 > /proc/sys/net/ipv4/ip_always_defrag echo 1 > /proc/sys/net/ipv4/icmp_ignore_bogus_error_responses echo 1 > /proc/sys/net/ipv4/conf/all/rp_filter echo 0 > /proc/sys/net/ipv4/conf/all/send_redirects echo 0 > /proc/sys/net/ipv4/conf/all/accept_source_route end_debug $? } open_input_port() { [ $# -ne 2 ] && exit 1 print_debug "Opening input port ${2} in ${1}" iptables -t filter -A INPUT -p "${1}" --dport "${2}" -j ACCEPT end_debug $? } open_output_port() { [ $# -ne 2 ] && exit 1 print_debug "Opening output port ${2} in ${1}" iptables -t filter -A OUTPUT -p "${1}" --dport "${2}" -j ACCEPT end_debug $? } allow_input_protocol() { [ $# -ne 1 ] && exit 1 print_debug "Allowing input protocol ${1}" iptables -t filter -A INPUT -p "${1}" -j ACCEPT end_debug $? } allow_output_protocol() { [ $# -ne 1 ] && exit 1 print_debug "Allowing output protocol ${1}" iptables -t filter -A OUTPUT -p "${1}" -j ACCEPT end_debug $? } allow_6in4_protocol() { [ $# -ne 1 ] && exit 1 print_debug "Allowing IPv6 in IPv4 protocol" iptables -t filter -A INPUT -s ${1} -p 41 -j ACCEPT iptables -t filter -A OUTPUT -p 41 -j ACCEPT end_debug $? } case ${1} in start) # Stop fail to ban before configuring firewall /etc/init.d/fail2ban stop log_action_begin_msg "Firewall (IPv4) configuration" deny_everything cleanup_tables dont_break_connections # Loopback allow_loopback # IPv6 in IPv4 allow_6in4_protocol "216.66.84.42" # SSH open_input_port tcp 22 open_output_port tcp 22 # DNS, FTP, HTTP, NTP open_output_port tcp 21 open_output_port tcp 80 open_output_port tcp 53 open_output_port tcp 443 open_output_port udp 53 open_output_port udp 123 # ICMP allow_input_protocol icmp allow_output_protocol icmp # HTTP open_input_port tcp 80 open_input_port tcp 443 # MySQL open_input_port tcp 3306 open_output_port tcp 3306 # POP, IMAP open_input_port tcp 25 open_input_port tcp 110 open_input_port tcp 995 open_input_port tcp 143 open_input_port tcp 993 open_output_port tcp 25 open_output_port tcp 110 open_output_port tcp 995 open_output_port tcp 143 open_output_port tcp 993 # Mumble open_input_port tcp 64738 open_input_port udp 64738 open_output_port tcp 64738 open_output_port udp 64738 # Bazaar open_input_port tcp 4155 open_output_port tcp 4155 # Keys server open_input_port tcp 11371 open_output_port tcp 11371 # OpenVPN open_input_port tcp 1194 open_output_port tcp 1194 # Transmission open_input_port tcp 9091 open_input_port tcp 51413 open_output_port tcp 9091 open_output_port tcp 51413 # Irssi open_input_port tcp 6667 open_input_port udp 6667 open_output_port tcp 6667 open_output_port udp 6667 # Minecraft for i in 25565 25566; do open_input_port tcp ${i} open_input_port udp ${i} open_output_port tcp ${i} open_output_port udp ${i} done # Steam for i in 7707 7708 7717 20560 28852; do open_input_port udp ${i} open_output_port udp ${i} done for i in 8075 20560 28852; do open_input_port tcp ${i} open_output_port tcp ${i} done for i in 27011 27900; do open_input_port tcp ${i} open_input_port udp ${i} open_output_port tcp ${i} open_output_port udp ${i} done deny_spoofing misc_config # Starting fail2ban again /etc/init.d/fail2ban start log_action_end_msg 0 ;; stop) log_action_begin_msg "Remove firewall (IPv4) configuration" accept_everything cleanup_tables log_action_end_msg 0 ;; *) echo "Usage: /etc/init.d/firewall {start|stop}" exit 1 ;; esac exit 0
Ce script peut être placé dans /etc/init.d/ et doit avoir les droits d'exécution pour être exécuté par root. Il est aussi recommandé de d'utiliser ce script lors du démarrage du système.
update-rc.d firewall defaults
L'utilisation manuel s'effectue par “start” pour démarrer le firewall, et par “stop” pour accepter toutes les connexions et vider toutes les règles.