Recherche de site Web

Comment mettre en œuvre un modèle de pare-feu de base avec Iptables sur Ubuntu 14.04


Introduction

Mettre en œuvre un pare-feu est une étape importante pour sécuriser votre serveur. Une grande partie de cela consiste à décider des règles et politiques individuelles qui appliqueront des restrictions de trafic à votre réseau. Les pare-feu comme iptables vous permettent également de participer à la structure dans laquelle vos règles sont appliquées.

Dans ce guide, nous allons construire un pare-feu qui pourra servir de base à des ensembles de règles plus complexes. Ce pare-feu se concentrera principalement sur la fourniture de valeurs par défaut raisonnables et l'établissement d'un cadre qui encourage une extensibilité facile. Nous allons démontrer cela sur un serveur Ubuntu 14.04.

Conditions préalables

Avant de commencer, vous devriez avoir une idée de base des politiques de pare-feu que vous souhaitez mettre en œuvre. Vous pouvez suivre ce guide pour mieux comprendre certaines des choses auxquelles vous devriez penser.

Pour suivre ce guide, vous aurez besoin d'accéder à un serveur Ubuntu 14.04. Nous utiliserons un utilisateur non-root configuré avec des privilèges sudo tout au long de ce guide. Vous pouvez apprendre comment configurer ce type d'utilisateur dans notre guide de configuration initiale du serveur Ubuntu 14.04.

Lorsque vous avez terminé, continuez ci-dessous.

Installer le service de pare-feu persistant

Pour commencer, vous devrez installer le package iptables-persistent si ce n'est pas déjà fait. Cela nous permettra de sauvegarder nos ensembles de règles et de les appliquer automatiquement au démarrage :

  1. sudo apt-get update
  2. sudo apt-get install iptables-persistent

Lors de l'installation, on vous demandera si vous souhaitez sauvegarder vos règles actuelles. Dites « oui » ici. Nous allons modifier les fichiers de règles générés sous peu.

Une note sur l'IPv6 dans ce guide

Avant de commencer, nous devrions parler brièvement de l'IPv4 par rapport à l'IPv6. La commande iptables ne gère que le trafic IPv4. Pour le trafic IPv6, un outil compagnon distinct appelé ip6tables est utilisé. Les règles sont stockées dans des tables et des chaînes distinctes. Pour iptables-persistent, les règles IPv4 sont écrites et lues à partir de /etc/iptables/rules.v4 et les règles IPv6 sont conservées dans /etc/iptables/rules.v6.

Ce guide suppose que vous n’utilisez pas activement IPv6 sur votre serveur. Si vos services n'exploitent pas IPv6, il est plus sûr de bloquer complètement l'accès, comme nous le ferons dans cet article.

Mise en œuvre de la politique de pare-feu de base (La méthode rapide)

Dans un souci de vous mettre en route le plus rapidement possible, nous allons vous montrer comment modifier directement le fichier des règles pour copier et coller la politique de pare-feu terminée. Par la suite, nous expliquerons la stratégie générale et vous montrerons comment ces règles pourraient être mises en œuvre en utilisant la commande iptables au lieu de modifier le fichier.

Pour mettre en œuvre notre politique et notre cadre de pare-feu, nous allons modifier les fichiers /etc/iptables/rules.v4 et /etc/iptables/rules.v6. Ouvrez le fichier rules.v4 dans votre éditeur de texte avec des privilèges sudo :

  1. sudo nano /etc/iptables/rules.v4

À l'intérieur, vous verrez un fichier qui ressemble à ceci :

Generated by iptables-save v1.4.21 on Tue Jul 28 13:29:56 2015
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
COMMIT
Completed on Tue Jul 28 13:29:56 2015

Remplacez le contenu par :

*filter
Allow all outgoing, but drop incoming and forwarding packets by default
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]

Custom per-protocol chains
:UDP - [0:0]
:TCP - [0:0]
:ICMP - [0:0]

Acceptable UDP traffic

Acceptable TCP traffic
-A TCP -p tcp --dport 22 -j ACCEPT

Acceptable ICMP traffic

Boilerplate acceptance policy
-A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-A INPUT -i lo -j ACCEPT

Drop invalid packets
-A INPUT -m conntrack --ctstate INVALID -j DROP

Pass traffic to protocol-specific chains
## Only allow new connections (established and related should already be handled)
## For TCP, additionally only allow new SYN packets since that is the only valid
## method for establishing a new TCP connection
-A INPUT -p udp -m conntrack --ctstate NEW -j UDP
-A INPUT -p tcp --syn -m conntrack --ctstate NEW -j TCP
-A INPUT -p icmp -m conntrack --ctstate NEW -j ICMP

Reject anything that's fallen through to this point
## Try to be protocol-specific w/ rejection message
-A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable
-A INPUT -p tcp -j REJECT --reject-with tcp-reset
-A INPUT -j REJECT --reject-with icmp-proto-unreachable

Commit the changes
COMMIT

*raw
:PREROUTING ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
COMMIT

*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
COMMIT

*security
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
COMMIT

*mangle
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
COMMIT

Enregistrez et fermez le fichier.

Vous pouvez tester le fichier pour des erreurs de syntaxe en tapant cette commande. Corrigez toutes les erreurs de syntaxe que cela révèle avant de continuer :

  1. sudo iptables-restore -t /etc/iptables/rules.v4

Ensuite, ouvrez le fichier /etc/iptables/rules.v6 pour modifier les règles IPv6 :

  1. sudo nano /etc/iptables/rules.v6

Nous pouvons bloquer tout le trafic IPv6 en remplaçant le contenu du fichier par la configuration ci-dessous :

*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT DROP [0:0]
COMMIT

*raw
:PREROUTING DROP [0:0]
:OUTPUT DROP [0:0]
COMMIT

*nat
:PREROUTING DROP [0:0]
:INPUT DROP [0:0]
:OUTPUT DROP [0:0]
:POSTROUTING DROP [0:0]
COMMIT

*security
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT DROP [0:0]
COMMIT

*mangle
:PREROUTING DROP [0:0]
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT DROP [0:0]
:POSTROUTING DROP [0:0]
COMMIT

Enregistrez et fermez le fichier.

Pour tester ce fichier à la recherche d'erreurs de syntaxe, nous pouvons utiliser la commande ip6tables-restore avec l'option -t :

  1. sudo ip6tables-restore -t /etc/iptables/rules.v6

Lorsque les deux fichiers de règles ne signalent aucune erreur de syntaxe, vous pouvez appliquer les règles à l'intérieur en tapant :

  1. sudo service iptables-persistent reload

Cela mettra immédiatement en œuvre la politique décrite dans vos fichiers. Vous pouvez vérifier cela en répertoriant les règles iptables actuellement en usage :

  1. sudo iptables -S
  2. sudo ip6tables -S

Ces règles de pare-feu seront réappliquées à chaque démarrage. Testez pour vous assurer que vous pouvez toujours vous connecter et que tout autre accès est bloqué.

Une explication de notre stratégie générale de pare-feu

Dans le pare-feu de base que nous avons construit avec les règles ci-dessus, nous avons créé un cadre extensible qui peut être facilement ajusté pour ajouter ou supprimer des règles. Pour le trafic IPv4, nous sommes principalement préoccupés par la chaîne INPUT dans la table filter. Cette chaîne traitera tous les paquets destinés à notre serveur. Nous avons également autorisé tout le trafic sortant et refusé tout transfert de paquets, ce qui ne serait approprié que si ce serveur agissait en tant que routeur pour d'autres hôtes. Nous acceptons les paquets dans toutes les autres tables puisque nous cherchons uniquement à filtrer les paquets dans ce guide.

En général, nos règles établissent un pare-feu qui refuse le trafic entrant par défaut. Nous procédons ensuite à la création d'exceptions pour les services et les types de trafic que nous souhaitons exclure de cette politique.

Dans la chaîne principale INPUT, nous avons ajouté des règles génériques pour le trafic que nous sommes convaincus seront toujours traitées de la même manière. Par exemple, nous voulons toujours refuser les paquets jugés « invalides » et nous voulons toujours autoriser le trafic sur l'interface de bouclage local et les données associées à une connexion établie.

Par la suite, nous faisons correspondre le trafic en fonction du protocole qu'il utilise et le réorientons vers une chaîne spécifique au protocole. Ces chaînes spécifiques au protocole sont destinées à contenir des règles qui correspondent et autorisent le trafic pour des services spécifiques. Dans cet exemple, le seul service que nous autorisons est SSH dans notre chaîne TCP. Si nous proposions un autre service, comme un serveur HTTP(S), nous pourrions également ajouter des exceptions ici. Ces chaînes seront l'objectif de la plupart de vos personnalisations.

Tout trafic qui ne correspond pas aux règles génériques ou aux règles de service dans les règles spécifiques au protocole est géré par les dernières règles de la chaîne INPUT. Nous avons défini la politique par défaut à DROP pour notre pare-feu, ce qui refusera les paquets qui ne correspondent à aucune de nos règles. Cependant, les règles à la fin de la chaîne INPUT rejettent les paquets et envoient un message au client qui imite la manière dont le serveur réagirait s'il n'y avait pas de service en cours d'exécution sur ce port.

Pour le trafic IPv6, nous abandonnons simplement tout le trafic. Notre serveur n'utilise pas ce protocole, il est donc plus sûr de ne pas interagir du tout avec ce trafic.

(Optionnel) Mettre à jour les serveurs de noms

Bloquer tout le trafic IPv6 peut interférer avec la façon dont votre serveur résout les choses sur Internet. Par exemple, cela peut affecter la manière dont vous utilisez APT.

Si vous obtenez des erreurs comme celle-ci lorsque vous essayez d'exécuter apt-get update :

Err http://security.ubuntu.com trusty-security InRelease
  
Err http://security.ubuntu.com trusty-security Release.gpg
  Could not resolve 'security.ubuntu.com'
  
. . .

Vous devriez suivre cette section pour faire fonctionner APT à nouveau.

Tout d'abord, définissez vos serveurs de noms sur des serveurs de noms externes. Cet exemple utilise les serveurs de noms de Google. Ouvrez /etc/network/interfaces pour l'éditer :

  1. sudo nano /etc/network/interfaces

Mettez à jour la ligne dns-nameservers comme indiqué :

. . .
iface eth0 inet6 static
        address 2604:A880:0800:0010:0000:0000:00B2:0001
        netmask 64
        gateway 2604:A880:0800:0010:0000:0000:0000:0001
        autoconf 0
        dns-nameservers 8.8.8.8 8.8.4.4

Actualisez vos paramètres réseau :

  1. sudo ifdown eth0 && sudo ifup eth0

La sortie attendue est :

RTNETLINK answers: No such process
Waiting for DAD... Done

Ensuite, créez une nouvelle règle de pare-feu pour forcer l'IPv4 lorsqu'il est disponible. Créez ce nouveau fichier :

  1. sudo nano /etc/apt/apt.conf.d/99force-ipv4

Ajoutez cette ligne unique au fichier :

Acquire::ForceIPv4 "true";

Enregistrez et fermez le fichier. Vous devriez maintenant pouvoir utiliser APT.

Mise en œuvre de nos pare-feu en utilisant la commande IPTables

Maintenant que vous comprenez l'idée générale derrière la politique que nous avons construite, nous allons passer en revue comment vous pourriez créer ces règles en utilisant des commandes iptables. Nous finirons avec les mêmes règles que celles que nous avons spécifiées ci-dessus, mais nous créerons nos politiques en ajoutant des règles de manière itérative. Parce que iptables applique immédiatement chacune des règles, l'ordre des règles est très important (nous laissons les règles qui refusent les paquets à la fin).

Réinitialisez votre pare-feu

Nous allons commencer par réinitialiser nos règles de pare-feu afin de voir comment des politiques peuvent être créées à partir de la ligne de commande. Vous pouvez vider toutes vos règles en tapant :

  1. sudo service iptables-persistent flush

Vous pouvez vérifier que vos règles sont réinitialisées en tapant :

  1. sudo iptables -S

Vous devriez voir que les règles dans la table filter ont disparu et que la politique par défaut est définie sur ACCEPT sur toutes les chaînes :

output
-P INPUT ACCEPT -P FORWARD ACCEPT -P OUTPUT ACCEPT

Créer des chaînes spécifiques au protocole

Nous commencerons par créer toutes nos chaînes spécifiques au protocole. Celles-ci seront utilisées pour contenir les règles qui créent des exceptions à notre politique de refus pour les services que nous souhaitons exposer. Nous en créerons une pour le trafic UDP, une pour le TCP, et une pour le ICMP :

  1. sudo iptables -N UDP
  2. sudo iptables -N TCP
  3. sudo iptables -N ICMP

Nous pouvons tout de suite ajouter l'exception pour le trafic SSH. SSH utilise TCP, donc nous allons ajouter une règle pour accepter le trafic TCP destiné au port 22 dans la chaîne TCP :

  1. sudo iptables -A TCP -p tcp --dport 22 -j ACCEPT

Si nous voulions ajouter des services TCP supplémentaires, nous pourrions le faire maintenant en répétant la commande avec le numéro de port remplacé.

Créer des règles d'acceptation et de refus à usage général

Dans la chaîne INPUT, où tout le trafic entrant commence à être filtré, nous devons ajouter nos règles générales. Ce sont des règles de bon sens qui établissent la ligne de base de notre pare-feu en acceptant le trafic à faible risque (trafic local et trafic associé à des connexions que nous avons déjà vérifiées) et en rejetant le trafic qui n’est clairement pas utile (paquets invalides).

Tout d'abord, nous allons créer une exception pour accepter tout le trafic qui fait partie d'une connexion établie ou qui est lié à une connexion établie :

  1. sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

Cette règle utilise l'extension conntrack, qui fournit un suivi interne afin que iptables dispose du contexte nécessaire pour évaluer les paquets dans le cadre de connexions plus larges au lieu de les considérer comme un flux de paquets discrets et non liés. TCP est un protocole basé sur des connexions, donc une connexion établie est assez bien définie. Pour UDP et d'autres protocoles sans connexion, les connexions établies se réfèrent au trafic ayant reçu une réponse (la source du paquet original sera la destination du paquet de réponse, et vice versa). Une connexion associée fait référence à une nouvelle connexion qui a été initiée en association avec une connexion existante. L'exemple classique ici est une connexion de transfert de données FTP, qui serait liée à la connexion de contrôle FTP qui a déjà été établie.

Nous souhaitons également autoriser tout le trafic provenant de l'interface de boucle locale. Il s'agit d'un trafic généré par le serveur et destiné au serveur. Il est utilisé par les services sur l'hôte pour communiquer entre eux :

  1. sudo iptables -A INPUT -i lo -j ACCEPT

Enfin, nous voulons rejeter tous les paquets invalides. Les paquets peuvent être invalides pour un certain nombre de raisons. Ils peuvent faire référence à des connexions qui n'existent pas, ils peuvent être destinés à des interfaces, des adresses ou des ports qui n'existent pas, ou ils peuvent simplement être mal formés. Dans tous les cas, nous allons rejeter tous les paquets invalides car il n'y a pas de manière appropriée de les traiter et parce qu'ils pourraient représenter une activité malveillante.

  1. sudo iptables -A INPUT -m conntrack --ctstate INVALID -j DROP

Créer les règles de saut vers les chaînes spécifiques au protocole

Jusqu'à présent, nous avons créé des règles générales dans la chaîne INPUT et certaines règles pour des services spécifiques acceptables au sein de nos chaînes spécifiques au protocole. Cependant, en ce moment, le trafic entre dans la chaîne INPUT et n'a aucun moyen d'atteindre nos chaînes spécifiques au protocole.

Nous devons diriger le trafic dans la chaîne INPUT vers les chaînes spécifiques au protocole approprié. Nous pouvons faire correspondre le type de protocole pour l'envoyer à la bonne chaîne. Nous veillerons également à ce que le paquet représente une nouvelle connexion (toutes les connexions établies ou liées devraient déjà avoir été traitées plus tôt). Pour les paquets TCP, nous ajouterons l'exigence supplémentaire que le paquet soit un paquet SYN, qui est le seul type valide pour établir une connexion TCP:

  1. sudo iptables -A INPUT -p udp -m conntrack --ctstate NEW -j UDP
  2. sudo iptables -A INPUT -p tcp --syn -m conntrack --ctstate NEW -j TCP
  3. sudo iptables -A INPUT -p icmp -m conntrack --ctstate NEW -j ICMP

Rejeter tout le trafic restant

Si un paquet transmis à une chaîne spécifique au protocole ne correspondait à aucune des règles à l'intérieur, le contrôle sera renvoyé à la chaîne INPUT. Tout ce qui atteint ce point ne devrait pas être autorisé par notre pare-feu.

Nous allons rejeter le trafic en utilisant la cible REJECT, qui envoie un message de réponse au client. Cela nous permet de spécifier la messagerie sortante afin que nous puissions imiter la réponse qui serait donnée si le client essayait d'envoyer des paquets à un port fermé classique. La réponse dépend du protocole utilisé par le client.

Essayer d'atteindre un port UDP fermé se traduira par un message ICMP "port inaccessible". Nous pouvons imiter cela en tapant :

  1. sudo iptables -A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable

Tenter d'établir une connexion TCP sur un port fermé entraîne une réponse TCP RST :

  1. sudo iptables -A INPUT -p tcp -j REJECT --reject-with tcp-reset

Pour tous les autres paquets, nous pouvons envoyer un message ICMP "protocole inaccessible" pour indiquer que le serveur ne répond pas aux paquets de ce type :

  1. sudo iptables -A INPUT -j REJECT --reject-with icmp-proto-unreachable

Ajustement des politiques par défaut

Les trois dernières règles que nous avons ajoutées devraient gérer tout le trafic restant dans la chaîne INPUT. Cependant, nous devrions définir la politique par défaut sur DROPPER par précaution. Nous devrions également définir cette politique dans la chaîne FORWARD si ce serveur n'est pas configuré en tant que routeur pour d'autres machines :

  1. sudo iptables -P INPUT DROP
  2. sudo iptables -P FORWARD DROP

Avertissement

Avec votre politique définie sur DROP, si vous videz vos iptables avec sudo iptables -F, votre connexion SSH actuelle sera interrompue ! Vider avec sudo iptables-persistent flush est une meilleure façon de vider les règles car cela réinitialisera également la politique par défaut.

Pour respecter notre politique IPv6 de rejet de tout le trafic, nous pouvons utiliser les commandes ip6tables suivantes:

  1. sudo ip6tables -P INPUT DROP
  2. sudo ip6tables -P FORWARD DROP
  3. sudo ip6tables -P OUTPUT DROP

Ceci devrait reproduire nos règles établies assez fidèlement.

Sauvegarde des règles IPTables

À ce stade, vous devez tester vos règles de pare-feu et vous assurer qu'elles bloquent le trafic que vous souhaitez maintenir à l'écart tout en ne gênant pas votre accès normal. Une fois que vous êtes satisfait du comportement de vos règles, vous pouvez les enregistrer afin qu'elles soient automatiquement appliquées à votre système au démarrage.

Enregistrez vos règles actuelles (à la fois IPv4 et IPv6) en tapant :

  1. sudo service iptables-persistent save

Cela écrasera vos fichiers /etc/iptables/rules.v4 et /etc/iptables/rules.v6 avec les politiques que vous avez créées dans la ligne de commande.

Conclusion

En suivant ce guide, que ce soit en collant vos règles de pare-feu directement dans les fichiers de configuration ou en les appliquant et en les enregistrant manuellement via la ligne de commande, vous avez créé une bonne configuration de pare-feu de départ. Vous devrez ajouter les règles individuelles pour permettre l'accès aux services que vous souhaitez rendre disponibles.

Le cadre établi dans ce guide devrait vous permettre de faire facilement des ajustements et peut aider à clarifier vos politiques existantes. Consultez certains de nos autres guides pour voir comment élaborer votre politique de pare-feu avec des services populaires :

  • Les essentiels d'Iptables : Règles et commandes courantes du pare-feu
  • Comment configurer un pare-feu Iptables pour protéger le trafic entre vos serveurs
  • Comment rediriger des ports à travers une passerelle Linux avec Iptables
  • Comment tester la configuration de votre pare-feu avec Nmap et Tcpdump