Dans mon script iptables, j'ai expérimenté l'écriture de règles aussi fines que possible. Je limite les utilisateurs autorisés à utiliser quels services, en partie pour des raisons de sécurité et en partie comme exercice d'apprentissage.
Utiliser iptables v1.4.16.2 sur Debian 6.0.6 exécutant le noyau 3.6.2.
Cependant, j'ai rencontré un problème que je ne comprends pas encore ...
ports sortants pour tous les utilisateurs
Cela fonctionne parfaitement bien. Je n'ai pas de règles de suivi d'état génériques.
## Port sortant 81 $ IPTABLES -A OUTPUT -p tcp --dport 81 -m conntrack --ctstate NOUVEAU, ÉTABLI -j ACCEPTER $ IPTABLES -A INPUT -p tcp --sport 81 -s $ MYIP -m conntrack --ctstate ESTABLISHED -j ACCEPT
ports sortants avec correspondance utilisateur
## port sortant 80 pour compte d'utilisateur $ IPTABLES -A OUTPUT --match owner --uid-owner useraccount -p tcp --dport 80 -m conntrack --ctstate NOUVEAU, ÉTABLI --sport 1024: 65535 -j ACCEPT $ IPTABLES -A INPUT -p tcp --sport 80 --dport 1024: 65535 -d $ MYIP -m conntrack --ctstate ESTABLISHED -j ACCEPT
Cela autorise le port 80 à sortir uniquement pour le compte "compte d'utilisateur", mais des règles comme celle-ci pour le trafic TCP ont des problèmes.
## Journal sortant par défaut + règles de blocage $ IPTABLES -A OUTPUT -j LOG --log-prefix "BAD OUTGOING" --log-ip-options --log-tcp-options --log-uid $ IPTABLES -A SORTIE -j DROP
Le problème
Ce qui précède fonctionne, l'utilisateur "useraccount" peut obtenir des fichiers parfaitement bien. Aucun autre utilisateur du système ne peut établir de connexions sortantes avec le port 80.
useraccount @ host: $ wget http://cachefly.cachefly.net/10mb.test
Mais le wget ci-dessus laisse x7 entrées perdues dans mon syslog:
18 octobre 02:00:35 noyau xxxx: MAUVAIS SORTIE EN = SORTIE = eth0 SRC = xx.xx.xx.xx DST = 205.234.175.175 LEN = 40 TOS = 0x00 PREC = 0x00 TTL = 64 ID = 12170 DF PROTO = TCP SPT = 37792 DPT = 80 SEQ = 164520678 ACK = 3997126942 WINDOW = 979 RES = 0x00 ACK URGP = 0
Je ne reçois pas ces baisses pour des règles similaires avec le trafic UDP. J'ai déjà des règles en place qui limitent les utilisateurs qui peuvent effectuer des requêtes DNS.
Les paquets ACK sortants abandonnés semblent provenir du compte root (URGP = 0) que je ne comprends pas. Même lorsque j'échange le compte utilisateur pour root.
Je crois que les paquets ACK sont classés comme nouveaux parce que conntrack commence à suivre les connexions après la 3e étape de la prise de contact à 3 voies, mais pourquoi sont-ils abandonnés?
Ces gouttes peuvent-elles être ignorées en toute sécurité?
Éditer
Je vois donc souvent des règles comme celles-ci, qui fonctionnent bien pour moi:
$ IPTABLES -A OUTPUT -s $ MYIP -p tcp -m tcp --dport 80 -m state --state NOUVEAU, ÉTABLI -j ACCEPTER $ IPTABLES -A INPUT -p tcp -m tcp --sport 80 -d $ MYIP -m state --state ESTABLISHED -j ACCEPT
J'ai remplacé "-m state --state" par "-m conntrack --ctstate" car la correspondance d'état est apparemment obsolète.
Est-il préférable d'avoir des règles de suivi d'état génériques? Les règles ci-dessus ne sont-elles pas considérées comme correctes?
Pour un contrôle strict des connexions des utilisateurs sortants, quelque chose comme ça serait-il mieux?
$ IPTABLES -A INPUT -m conntrack --ctstate ESTABLISHED -j ACCEPT $ IPTABLES -A OUTPUT -m conntrack --ctstate ESTABLISHED -j ACCEPT $ IPTABLES -A OUTPUT -p tcp --dport 80 -s $ SERVER_IP_TUNNEL -m conntrack --ctstate NEW -m owner --uid-owner useraccount -j ACCEPT $ IPTABLES -A OUTPUT -p tcp --dport 80 -s $ SERVER_IP_TUNNEL -m conntrack --ctstate NEW -m owner --uid-owner otheraccount -j ACCEPT
Réponses:
Pour faire court, cet ACK a été envoyé lorsque le socket n'appartenait à personne. Au lieu d'autoriser les paquets appartenant à un socket appartenant à l'utilisateur
x
, autorisez les paquets appartenant à une connexion initiée par un socket de l'utilisateurx
.L'histoire la plus longue.
Pour comprendre le problème, il permet de comprendre comment
wget
et les requêtes HTTP fonctionnent en général.Dans
wget
établit une connexion TCP àcachefly.cachefly.net
, et une fois établie envoie une demande dans le protocole HTTP qui dit: "Veuillez m'envoyer le contenu de/10mb.test
(GET /10mb.test HTTP/1.1
) et en passant, pourriez-vous s'il vous plaît ne pas fermer la connexion après avoir terminé (Connection: Keep-alive
). La raison c'est parce que si le serveur répond avec une redirection pour une URL sur la même adresse IP, il peut réutiliser la connexion.Maintenant, le serveur peut répondre par l'un ou l'autre: "voici les données que vous avez demandées, méfiez-vous qu'il fait 10 Mo de large (
Content-Length: 10485760
), et oui, je laisse la connexion ouverte". Ou s'il ne connaît pas la taille des données, "Voici les données, désolé je ne peux pas laisser la connexion ouverte mais je dirai quand vous pouvez arrêter de télécharger les données en fermant ma fin de connexion".Dans l'URL ci-dessus, nous sommes dans le premier cas.
Ainsi, dès qu'il
wget
a obtenu les en-têtes de la réponse, il sait que son travail est terminé une fois qu'il a téléchargé 10 Mo de données.Fondamentalement, ce
wget
qui est lu les données jusqu'à ce que 10 Mo aient été reçus et quittent. Mais à ce stade, il reste encore beaucoup à faire. Et le serveur? On lui a dit de laisser la connexion ouverte.Avant de quitter,
wget
ferme (close
appel système) le descripteur de fichier du socket. Leclose
, le système termine d'acquitter les données envoyées par le serveur et envoie unFIN
pour dire: "Je n'enverrai plus de données". À ce pointclose
revient etwget
sort. Il n'y a plus de socket associé à la connexion TCP (du moins pas un appartenant à un utilisateur). Mais ce n'est pas encore fini. A réception de celaFIN
, le serveur HTTP voit la fin du fichier lors de la lecture de la prochaine requête du client. En HTTP, cela signifie "plus de demande, je vais fermer ma fin". Donc, il envoie également son FIN, pour dire: "Je n'enverrai rien non plus, cette connexion est en train de disparaître".A réception de cette FIN, le client envoie un "ACK". Mais, à ce stade, a
wget
disparu depuis longtemps, de sorte que ACK ne provient d'aucun utilisateur. C'est pourquoi il est bloqué par votre pare-feu. Parce que le serveur ne reçoit pas le ACK, il va envoyer le FIN encore et encore jusqu'à ce qu'il abandonne et vous verrez plus de ACK abandonnés. Cela signifie également qu'en supprimant ces ACK, vous utilisez inutilement les ressources du serveur (qui doit maintenir un socket dans l'état LAST-ACK) pendant un certain temps.Le comportement aurait été différent si le client n'avait pas demandé "Keep-alive" ou si le serveur n'avait pas répondu "Keep-alive".
Comme déjà mentionné, si vous utilisez le traqueur de connexion, ce que vous voulez faire, c'est laisser passer chaque paquet dans les états ESTABLISHED et RELATED et ne vous souciez que des
NEW
paquets.Si vous autorisez les
NEW
paquets de l'utilisateurx
mais pas les paquets de l'utilisateury
, alors d'autres paquets pour les connexions établies par l'utilisateurx
passeront, et parce qu'il ne peut pas y avoir de connexions établies par l'utilisateury
(puisque nous bloquons lesNEW
paquets qui établiraient la connexion), il n'y aura aucun paquet pour lesy
connexions des utilisateurs .la source
- au moins, les règles que vous avez montrées n'impliquent pas cela, en fait.
Il y a aussi une place pour des conseils - ne faites pas de vérification des utilisateurs sur les flux ESTABLISHED, faites simplement cette vérification sur NEW. Je ne vois pas non plus d'intérêt à vérifier le port source lorsque je vérifie Incoming ESTABLISHED, quelle est la différence de quel port il s'agissait, il est déjà à l'état ESTABLISHED du PoV de conntrack. Le pare-feu doit être aussi simple que possible mais efficace pour le moment, donc l' approche du rasoir d'Occam est la meilleure solution.
la source