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

introduction

La mise en place d'un pare-feu est une étape importante dans la sécurisation de votre serveur. Une grande partie de cette tâche consiste à décider des règles et des stratégies individuelles qui imposeront des restrictions de trafic à votre réseau. Les pare-feu commeiptables 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 pouvant servir de base à des ensembles de règles plus complexes. Ce pare-feu s'attachera principalement à fournir des paramètres par défaut raisonnables et à établir un cadre favorisant une extensibilité aisée. Nous allons en faire la démonstration sur un serveur Ubuntu 14.04.

Conditions préalables

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

Pour pouvoir suivre, vous devez avoir accès à un serveur Ubuntu 14.04. Nous utiliserons un utilisateur non root configuré avec les privilègessudo tout au long de ce guide. Vous pouvez apprendre comment configurer ce type d'utilisateur dans nosUbuntu 14.04 initial server setup guide.

Lorsque vous avez terminé, continuez ci-dessous.

Installation du service de pare-feu persistant

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

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

Au cours de l’installation, il vous sera demandé si vous souhaitez enregistrer vos règles actuelles. Dites «oui» ici. Nous allons éditer les fichiers de règles générés dans quelques instants.

Une note sur IPv6 dans ce guide

Avant de commencer, nous devrions parler brièvement de IPv4 vs IPv6. La commandeiptables 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 séparées. Pouriptables-persistent, les règles IPv4 sont écrites et lues depuis/etc/iptables/rules.v4 et les règles IPv6 sont conservées dans/etc/iptables/rules.v6.

Ce guide suppose que vous utiliseznotactivement IPv6 sur votre serveur. Si vos services n'utilisent pas 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 (solution rapide)

Pour être opérationnel le plus rapidement possible, nous allons vous montrer comment modifier le fichier de règles directement 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 commandeiptables au lieu de modifier le fichier.

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

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 les erreurs de syntaxe dans le fichier 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

Ensuite, ouvrez 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 commandeip6tables-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 les appliquer en tapant:

sudo service iptables-persistent reload

Cela implémentera immédiatement la politique décrite dans vos fichiers. Vous pouvez le vérifier en listant les règlesiptables 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 sommes principalement concernés par la chaîneINPUT dans la tablefilter. 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 car nous cherchons uniquement à filtrer les paquets dans ce guide.

En général, nos règles établissent un pare-feu qui refusera le trafic entrant par défaut. Nous allons ensuite créer des exceptions pour les services et les types de trafic que nous souhaitons exclure de cette politique.

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

Ensuite, nous adaptons le trafic en fonction du protocole utilisé et le brassons 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îneTCP. 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 dans le protocole spécifique est traité par les dernières règles de la chaîneINPUT. Nous avons défini la stratégie par défaut surDROP pour notre pare-feu, ce qui refusera les paquets qui tombent sous nos règles. Cependant, les règles à la fin de la chaîneINPUT 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 s'engager dans 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écuterapt-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'

. . .

Suivez cette section pour que APT fonctionne à nouveau.

Commencez par définir 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 lignedns-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 les paramètres de votre réseau:

sudo ifdown eth0 && sudo ifup eth0

Le résultat attendu est:

Sortie

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

Créez ensuite une nouvelle règle de pare-feu pour forcer IPv4 quand 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 construite, nous allons vous expliquer comment créer ces règles à l'aide des commandesiptables. Nous nous retrouverons avec les mêmes règles que celles spécifiées ci-dessus, mais nous créerons nos règles en ajoutant des règles de manière itérative. Parce queiptables 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éinitialiser votre pare-feu

Nous allons commencer par réinitialiser nos règles de pare-feu afin de voir comment les stratégies 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 tablefilter ont disparu et que la stratégie par défaut est définie surACCEPT 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 traficUDP, un pourTCP et un pourICMP:

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 sur 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 en remplaçant le numéro de port.

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

Dans la chaîneINPUT, où tout le trafic entrant commence à filtrer, nous devons ajouter nos règles générales. Ce sont des règles de bon sens qui définissent la base de notre pare-feu en acceptant le trafic à faible risque (trafic local et le trafic associé à des connexions que nous avons déjà vérifiées) et en supprimant le trafic qui n’est manifestement pas utile (paquets non valides).

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'extensionconntrack, qui fournit un suivi interne afin queiptables dispose du contexte nécessaire pour évaluer les paquets dans le cadre de connexions plus importantes plutôt que comme 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 les protocoles UDP et autres protocoles sans connexion, les connexions établies font référence au trafic ayant reçu une réponse (la source du paquet d'origine sera la destination du paquet de réponse, et inversement). 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 déjà établie.

Nous voulons également autoriser tout le trafic provenant de l'interface de bouclage local. C'est le trafic généré par le serveur et destiné au serveur. Les services de l'hôte l'utilisent pour communiquer entre eux:

sudo iptables -A INPUT -i lo -j ACCEPT

Enfin, nous voulons refuser tous les paquets non valides. Les paquets peuvent être invalides pour plusieurs raisons. Ils peuvent faire référence à des connexions inexistantes, être destinés à des interfaces, des adresses ou des ports inexistants, ou simplement être mal formés. Dans tous les cas, nous allons supprimer tous les paquets non valides car il n’existe pas de moyen approprié pour les traiter et parce qu’ils peuvent représenter une activité malveillante:

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

Création des règles de saut dans les chaînes spécifiques au protocole

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

Nous devons diriger le trafic de la chaîneINPUT 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 veillerons également à ce que le paquet représente une nouvelle connexion (toutes les connexions établies ou associées doivent déjà être gérées plus tôt). Pour les paquets TCP, nous ajouterons l'exigence supplémentaire selon laquelle le paquet est 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é passé à une chaîne spécifique au protocole ne correspond à aucune des règles contenues, le contrôle sera renvoyé à la chaîneINPUT. Tout ce qui atteint ce point ne devrait pas être autorisé par notre pare-feu.

Nous refuserons le trafic en utilisant la cibleREJECT, qui envoie un message de réponse au client. Cela nous permet de spécifier la messagerie sortante afin de pouvoir 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.

Si vous tentez d’atteindre un port UDP fermé, un message ICMP «Port inaccessible» apparaît. Nous pouvons l'imiter en tapant:

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

La tentative d'établissement d'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 «protocole inaccessible» 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

Ajuster les règles par défaut

Les trois dernières règles que nous avons ajoutées devraient gérer tout le trafic restant dans la chaîneINPUT. Cependant, nous devons définir la politique par défaut surDROP par précaution. Nous devons également définir cette politique dans la chaîneFORWARD si ce serveur n’est pas configuré comme routeur vers d’autres machines:

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

Attention

[.warning] # Avec votre politique définie surDROP, si vous effacez vosiptables avecsudo iptables -F, votre connexion SSH actuelle sera abandonnée! Le vidage avecsudo iptables-persistent flush est un meilleur moyen d'effacer les règles car il réinitialisera également la stratégie par défaut.
#

Pour correspondre à notre politique IPv6 d'abandonner tout le trafic, nous pouvons utiliser les commandesip6tables suivantes:

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

Cela devrait reproduire assez étroitement 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 interdire tout en ne nuisant pas à votre accès normal. Une fois que vous êtes certain 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 tapant:

sudo service iptables-persistent save

Cela écrasera vos fichiers/etc/iptables/rules.v4 et/etc/iptables/rules.v6 avec les stratégies 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 démarrage du pare-feu. Vous devrez ajouter les règles individuelles pour autoriser l'accès aux services que vous souhaitez rendre disponibles.

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

Related