Comment configurer un pare-feu Iptables pour protéger le trafic entre vos serveurs

introduction

Le déploiement de composants discrets dans la configuration de votre application sur différents nœuds est un moyen courant de réduire la charge et de commencer la mise à l'échelle horizontalement. Un exemple typique est la configuration d'une base de données sur un serveur distinct de votre application. Bien que cette configuration présente de nombreux avantages, la connexion via un réseau pose de nouveaux problèmes de sécurité.

Dans ce guide, nous expliquerons comment configurer un pare-feu simple sur chacun de vos serveurs dans une configuration distribuée. Nous allons configurer notre politique pour autoriser le trafic légitime entre nos composants tout en refusant tout autre trafic.

Pour la démonstration de ce guide, nous utiliserons deux serveurs Ubuntu 14.04. L'un aura une instance WordPress avec Nginx et l'autre hébergera la base de données MySQL pour l'application. Bien que nous utilisions cette configuration à titre d'exemple, vous devriez être capable d'extrapoler les techniques impliquées pour répondre aux besoins de votre propre serveur.

Conditions préalables

Pour commencer, vous devrez disposer de deux nouveaux serveurs Ubuntu 14.04. Ajoutez un compte utilisateur standard avec les privilègessudo sur chacun. Pour savoir comment faire cela correctement, suivez nosUbuntu 14.04 initial server setup guide.

La configuration de l'application que nous allons sécuriser est basée surthis guide. Si vous souhaitez suivre, configurez vos serveurs d’application et de base de données comme indiqué dans ce tutoriel.

Configuration d'un pare-feu de base

Nous allons commencer par mettre en place une configuration de pare-feu de base pour chacun de nos serveurs. La politique que nous allons mettre en œuvre adopte une approche de sécurité d'abord. Nous allons verrouiller presque tout ce qui est autre que le trafic SSH, puis percer des trous dans le pare-feu pour notre application spécifique.

Le pare-feu dethis guide fournit la configuration de base dont nous avons besoin. Installez le packageiptables-persistent et collez les règles de base dans le fichier/etc/iptables/rules.v4:

sudo apt-get update
sudo apt-get install iptables-persistent
sudo nano /etc/iptables/rules.v4

/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

Si vous implémentez cela dans un environnement en directdo not reload your firewall rules yet. Le chargement du jeu de règles de base décrit ici entraînera immédiatement l'interruption de la connexion entre votre application et le serveur de base de données. Nous devrons ajuster les règles pour refléter nos besoins opérationnels avant de recharger.

Découvrez les ports utilisés par vos services

Afin d'ajouter des exceptions pour permettre la communication entre nos composants, nous devons connaître les ports réseau utilisés. Nous pourrions trouver les bons ports réseau en examinant nos fichiers de configuration, mais une méthode indépendante de toute application pour trouver les bons ports consiste à vérifier quels services écoutent les connexions sur chacune de nos machines.

Nous pouvons utiliser l'outilnetstat pour le découvrir. Étant donné que notre application ne communique que sur IPv4, nous ajouterons l'argument-4 mais vous pouvez le supprimer si vous utilisez également IPv6. Les autres arguments dont nous avons besoin pour trouver nos services en cours d'exécution sont-plunt.

Sur votre serveur Web, nous verrions quelque chose comme ceci:

sudo netstat -4plunt
OutputActive Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      1058/sshd
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      4187/nginx

La première colonne en surbrillance indique l'adresse IP et le port sur lequel le service en surbrillance en fin de ligne écoute. L'adresse spéciale0.0.0.0 signifie que le service en question écoute sur toutes les adresses disponibles.

Sur notre serveur de base de données, nous verrions quelque chose comme ceci:

sudo netstat -4plunt
OutputActive Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      1097/sshd
tcp        0      0 192.0.2.30:3306     0.0.0.0:*               LISTEN      3112/mysqld

Vous pouvez lire ces colonnes exactement les mêmes. Dans l'exemple ci-dessus, l'adresse192.0.2.30 représente l'adresse IP privée du serveur de base de données. Lors de la configuration de l'application, nous avons verrouillé MySQL sur l'interface privée pour des raisons de sécurité.

Prenez note des valeurs que vous trouvez dans cette étape. Ce sont les détails de réseau dont nous avons besoin pour ajuster la configuration de notre pare-feu.

Dans notre exemple de scénario, nous pouvons noter que sur notre serveur Web, nous devons nous assurer que les ports suivants sont accessibles:

  • Port 80 sur toutes les adresses

  • Port 22 sur toutes les adresses (déjà pris en compte dans les règles de pare-feu)

Notre serveur de base de données devrait s'assurer que les ports suivants sont accessibles:

  • Port 3306 sur l'adresse192.0.2.30 (ou l'interface qui lui est associée)

  • Port 22 sur toutes les adresses (déjà pris en compte dans les règles de pare-feu)

Ajuster les règles du pare-feu du serveur Web

Maintenant que nous avons les informations de port dont nous avons besoin, nous allons ajuster le jeu de règles de pare-feu de notre serveur Web. Ouvrez le fichier de règles dans votre éditeur avec les privilègessudo:

sudo nano /etc/iptables/rules.v4

Sur le serveur Web, nous devons ajouter le port 80 à notre liste de trafic acceptable. Étant donné que le serveur écoute toutes les adresses disponibles, nous ne limiterons pas la règle par interface ou adresse de destination.

Nos visiteurs Web utiliseront le protocole TCP pour se connecter. Notre framework de base a déjà une chaîne personnalisée appeléeTCP pour les exceptions d'application TCP. Nous pouvons ajouter le port 80 à cette chaîne, juste en dessous de l'exception pour notre port SSH:

/etc/iptables/rules.v4

*filter
. . .

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

. . .

Notre serveur Web établira la connexion avec notre serveur de base de données. Notre pare-feu ne limite pas le trafic sortant et le trafic entrant associé aux connexions établies est autorisé. Il n'est donc pas nécessaire d'ouvrir de ports supplémentaires sur ce serveur pour autoriser cette connexion.

Enregistrez et fermez le fichier lorsque vous avez terminé. Notre serveur Web a maintenant une politique de pare-feu qui autorise tout le trafic légitime tout en bloquant tout le reste.

Testez votre fichier de règles pour les erreurs de syntaxe:

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

Si aucune erreur de syntaxe n'est affichée, rechargez le pare-feu pour implémenter le nouvel ensemble de règles:

sudo service iptables-persistent reload

Ajuster les règles du pare-feu du serveur de base de données

Sur notre serveur de base de données, nous devons autoriser l’accès au port3306 sur l’adresse IP privée de notre serveur. Dans notre cas, cette adresse était192.0.2.30. Nous pouvons limiter l'accès à cette adresse en particulier, ou nous pouvons limiter l'accès en effectuant une correspondance avec l'interface affectée à cette adresse.

Pour trouver l'interface réseau associée à cette adresse, tapez:

ip -4 addr show scope global
Output2: eth0:  mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    inet 203.0.113.5/24 brd 104.236.113.255 scope global eth0
       valid_lft forever preferred_lft forever
3: eth1:  mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    inet 192.0.2.30/24 brd 192.0.2.255 scope global eth1
       valid_lft forever preferred_lft forever

Les zones en surbrillance montrent que l'interfaceeth1 est associée à cette adresse.

Ensuite, nous allons ajuster les règles de pare-feu sur le serveur de base de données. Ouvrez le fichier de règles avec les privilègessudo sur votre serveur de base de données:

sudo nano /etc/iptables/rules.v4

Encore une fois, nous ajouterons une règle à notre chaîneTCP pour former une exception pour la connexion entre nos serveurs Web et de base de données.

Si vous souhaitez restreindre l'accès en fonction de l'adresse réelle en question, vous devez ajouter la règle suivante:

/etc/iptables/rules.v4

*filter
. . .

# Acceptable TCP traffic
-A TCP -p tcp --dport 22 -j ACCEPT
-A TCP -p tcp --dport 3306 -d 192.0.2.30 -j ACCEPT

. . .

Si vous préférez autoriser l'exception en fonction de l'interface qui héberge cette adresse, vous pouvez ajouter une règle similaire à celle-ci:

/etc/iptables/rules.v4

*filter
. . .

# Acceptable TCP traffic
-A TCP -p tcp --dport 22 -j ACCEPT
-A TCP -p tcp --dport 3306 -i eth1 -j ACCEPT

. . .

Enregistrez et fermez le fichier lorsque vous avez terminé.

Recherchez les erreurs de syntaxe avec cette commande:

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

Lorsque vous êtes prêt, rechargez les règles du pare-feu:

sudo service iptables-persistent reload

Vos deux serveurs devraient maintenant être protégés sans restreindre le flux de données nécessaire entre eux.

Conclusion

L'implémentation d'un pare-feu approprié doit toujours faire partie de votre plan de déploiement lors de la configuration d'une application. Bien que nous ayons démontré cette configuration en utilisant les deux serveurs exécutant Nginx et MySQL pour fournir une instance WordPress, les techniques présentées ci-dessus sont applicables quels que soient vos choix technologiques.

Pour en savoir plus sur les pare-feu et lesiptables en particulier, consultez les guides suivants: