Comment implémenter un modèle de pare-feu de base avec Iptables sur Ubuntu 14.04

De Get Docs
Aller à :navigation, rechercher

Introduction

La mise en place d'un pare-feu est une étape importante dans la sécurisation de votre serveur. Une grande partie de cela consiste à décider des règles et politiques individuelles qui appliqueront les restrictions de trafic sur votre réseau. Les pare-feu comme iptables vous permettent également d'avoir votre mot à dire sur le cadre structurel dans lequel vos règles sont appliquées.

Dans ce guide, nous allons construire un pare-feu qui peut 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 sur l'établissement d'un cadre qui encourage une extensibilité facile. Nous en ferons la démonstration sur un serveur Ubuntu 14.04.

Conditions préalables

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

Pour suivre, vous devrez avoir accès à un serveur Ubuntu 14.04. Nous utiliserons un utilisateur non root configuré avec les privilèges sudo tout au long de ce guide. Vous pouvez apprendre à configurer ce type d'utilisateur dans notre Guide de configuration initiale du serveur Ubuntu 14.04.

Lorsque vous avez terminé, continuez ci-dessous.

Installation du service de pare-feu persistant

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

sudo apt-get update
sudo apt-get install iptables-persistent

Lors de l'installation, il vous sera demandé si vous souhaitez enregistrer vos règles actuelles. Dites "oui" ici. Nous modifierons momentanément les fichiers de règles générés.

Remarque sur IPv6 dans ce guide

Avant de commencer, nous devrions parler brièvement d'IPv4 vs IPv6. La commande iptables gère uniquement le trafic IPv4. Pour le trafic IPv6, un outil complémentaire 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 pas utilisez activement IPv6 sur votre serveur. Si vos services ne tirent pas parti d'IPv6, il est plus sûr de bloquer complètement l'accès, comme nous le ferons dans cet article.

Implémentation de la stratégie de pare-feu de base (méthode rapide)

Afin d'être opérationnel le plus rapidement possible, nous vous montrerons comment modifier directement le fichier de règles pour copier et coller la stratégie de pare-feu terminée. Ensuite, nous expliquerons la stratégie générale et vous montrerons comment ces règles pourraient être implémentées en utilisant la commande iptables au lieu de modifier le fichier.

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

sudo nano /etc/iptables/rules.v4

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

/etc/iptables/rules.v4

# 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 :

/etc/iptables/rules.v4

*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 les erreurs de syntaxe en tapant cette commande. Corrigez les erreurs de syntaxe que cela révèle avant de continuer :

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

Ouvrez ensuite le fichier /etc/iptables/rules.v6 pour modifier les règles IPv6 :

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 :

/etc/iptables/rules.v6

*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 pour les erreurs de syntaxe, nous pouvons utiliser la commande ip6tables-restore avec l'option -t :

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 qu'ils contiennent en saisissant :

sudo service iptables-persistent reload

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

sudo iptables -S
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 tous les autres accès sont bloqués.

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 nous intéressons principalement à 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 comme un 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 configurent un pare-feu qui refusera 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 dont nous sommes convaincus qu'elles 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 locale et les données associées à une connexion établie.

Ensuite, nous faisons correspondre le trafic en fonction du protocole qu'il utilise et le mélangeons dans 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 offrions un autre service, comme un serveur HTTP(S), nous pourrions également ajouter des exceptions ici. Ces chaînes seront au centre de la plupart de vos personnalisations.

Tout trafic qui ne correspond pas aux règles génériques ou aux règles de service 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 sur DROP pour notre pare-feu, qui refusera les paquets qui ne respectent pas 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 façon dont le serveur répondrait s'il n'y avait pas de service en cours d'exécution sur ce port.

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

(Facultatif) Mettre à jour les serveurs de noms

Le blocage de tout le trafic IPv6 peut interférer avec la façon dont votre serveur résout les problèmes sur Internet. Par exemple, cela peut affecter la façon dont vous utilisez APT.

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

Erreur

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 que APT fonctionne à nouveau.

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

sudo nano /etc/network/interfaces

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

/etc/network/interfaces

. . .
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 :

sudo ifdown eth0 && sudo ifup eth0

La sortie attendue est :

Production

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

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

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

Ajoutez cette seule ligne au fichier :

/etc/apt/apt.conf.d/99force-ipv4

Acquire::ForceIPv4 "true";

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

Implémentation de nos pare-feu à l'aide de la commande IPTables

Maintenant que vous comprenez l'idée générale derrière la stratégie que nous avons créée, nous allons vous expliquer comment créer ces règles à l'aide des commandes iptables. Nous nous retrouverons avec les mêmes règles 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 chacune des règles immédiatement, l'ordre des règles est très important (nous laissons les règles qui refusent les paquets jusqu'à la fin).

Réinitialisez votre pare-feu

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

sudo service iptables-persistent flush

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

sudo iptables -S

Vous devriez voir que les règles de la table filter ont disparu et que la stratégie 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 allons commencer par créer toutes nos chaînes spécifiques au protocole. Ceux-ci seront utilisés pour contenir les règles qui créent des exceptions à notre politique de refus pour les services que nous voulons exposer. Nous allons en créer un pour le trafic UDP, un pour TCP et un pour ICMP :

sudo iptables -N UDP
sudo iptables -N TCP
sudo iptables -N ICMP

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

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 à filtrer, nous devons ajouter nos règles à usage général. Ce sont quelques règles de bon sens qui définissent la base de notre pare-feu en acceptant le trafic à faible risque (trafic local et trafic associé aux connexions que nous avons déjà vérifiées) et en supprimant le trafic qui n'est clairement pas utile (paquets invalides).

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

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 ait le contexte dont il a besoin pour évaluer les paquets dans le cadre de connexions plus importantes au lieu d'un flux de paquets discrets et non liés. TCP est un protocole basé sur la connexion, donc une connexion établie est assez bien définie. Pour UDP et d'autres protocoles sans connexion, les connexions établies font référence au trafic qui a vu une réponse (la source du paquet d'origine 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 bouclage local. Il s'agit du 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 :

sudo iptables -A INPUT -i lo -j ACCEPT

Enfin, nous voulons refuser 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 malformés. Dans tous les cas, nous supprimerons tous les paquets non valides car il n'existe aucun moyen approprié de les gérer et car ils pourraient représenter une activité malveillante :

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

Création des 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 des règles pour des services acceptables spécifiques au sein de nos chaînes spécifiques au protocole. Cependant, à l'heure actuelle, 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 de la chaîne INPUT vers les chaînes spécifiques au protocole appropriées. Nous pouvons faire correspondre le type de protocole pour l'envoyer à la bonne chaîne. Nous nous assurerons également que le paquet représente une nouvelle connexion (toutes les connexions établies ou associées doivent déjà être 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 démarrer une connexion TCP :

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

Rejeter tout le trafic restant

Si un paquet qui a été transmis à une chaîne spécifique au protocole ne correspondait à aucune des règles qu'il contient, 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 refuserons 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é normal. La réponse dépend du protocole utilisé par le client.

Tenter d'atteindre un port UDP fermé entraînera un message ICMP "port inaccessible". Nous pouvons imiter cela en tapant:

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 :

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

Pour tous les autres paquets, nous pouvons envoyer un message ICMP « protocol unreachable » pour indiquer que le serveur ne répond pas aux paquets de ce type :

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

Ajustement des stratégies par défaut

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

sudo iptables -P INPUT DROP
sudo iptables -P FORWARD DROP

Avertissement

Avec votre politique définie sur DROP, si vous effacez votre iptables avec sudo iptables -F, votre connexion SSH actuelle sera abandonnée ! Rincer avec sudo iptables-persistent flush est un meilleur moyen d'effacer les règles car il réinitialisera également la politique par défaut.


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

sudo ip6tables -P INPUT DROP
sudo ip6tables -P FORWARD DROP
sudo ip6tables -P OUTPUT DROP

Cela devrait reproduire assez fidèlement nos règles.

Enregistrement des règles IPTables

À ce stade, vous devez tester vos règles de pare-feu et vous assurer qu'elles couvrent le blocage du trafic que vous souhaitez empêcher tout en n'entravant pas votre accès normal. Une fois que vous êtes convaincu que vos règles se comportent correctement, vous pouvez les enregistrer afin qu'elles soient automatiquement appliquées à votre système au démarrage.

Enregistrez vos règles actuelles (IPv4 et IPv6) en saisissant :

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 sur la ligne de commande.

Conclusion

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

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 savoir comment créer votre politique de pare-feu avec certains services populaires :