J'ai un client Centos 8 qui court sur un hôte Fedora 31. L'invité est attaché à un réseau de pont, virbr0
, et a l'adresse 192.168.122.217
. Je peux me connecter à l'invité via SSH à cette adresse.
Si je démarre un service sur la liste des invités sur le port 80, toutes les connexions de l'hôte à l'invité manquent comme suit:
$ curl 192.168.122.217
curl: (7) Failed to connect to 192.168.122.217 port 80: No route to Host
Le service est tenu de 0.0.0.0
:
guest# ss -tln
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
LISTEN 0 5 0.0.0.0:80 0.0.0.0:*
LISTEN 0 128 [::]:22 [::]:*
En utilisant tcpdump
(soit sur virbr0
sur l'hôte, ou sur eth0
Sur l'invité), je vois que l'invité semble répondre avec un message ICMP "Admin Interdit".
19:09:25.698175 IP 192.168.122.1.33472 > 192.168.122.217.http: Flags [S], seq 959177236, win 64240, options [mss 1460,sackOK,TS val 3103862500 ecr 0,nop,wscale 7], length 0
19:09:25.698586 IP 192.168.122.217 > 192.168.122.1: ICMP Host 192.168.122.217 unreachable - admin prohibited filter, length 68
Il n'y a pas de règles de pare-feu sur la chaîne INPUT
à l'invité:
guest# iptables -S INPUT
-P INPUT ACCEPT
La table de routage dans l'invité a l'air parfaitement normale:
guest# ip route
default via 192.168.122.1 dev eth0 proto dhcp metric 100
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown
192.168.122.0/24 dev eth0 proto kernel scope link src 192.168.122.217 metric 100
SELINUX est en mode permissif:
guest# getenforce
Permissive
Si j'arrête sshd
et démarrez mon service sur le port 22, tout fonctionne comme prévu.
Qu'est-ce qui cause ces connexions échoue?
Au cas où quelqu'un le demande, la sortie complète de iptables-save
sur l'invité est:
*filter
:INPUT ACCEPT [327:69520]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [285:37235]
:DOCKER - [0:0]
:DOCKER-ISOLATION-STAGE-1 - [0:0]
:DOCKER-ISOLATION-STAGE-2 - [0:0]
:DOCKER-USER - [0:0]
-A FORWARD -j DOCKER-USER
-A FORWARD -j DOCKER-ISOLATION-STAGE-1
-A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -o docker0 -j DOCKER
-A FORWARD -i docker0 ! -o docker0 -j ACCEPT
-A FORWARD -i docker0 -o docker0 -j ACCEPT
-A DOCKER-ISOLATION-STAGE-1 -i docker0 ! -o docker0 -j DOCKER-ISOLATION-STAGE-2
-A DOCKER-ISOLATION-STAGE-1 -j RETURN
-A DOCKER-ISOLATION-STAGE-2 -o docker0 -j DROP
-A DOCKER-ISOLATION-STAGE-2 -j RETURN
-A DOCKER-USER -j RETURN
COMMIT
*security
:INPUT ACCEPT [280:55468]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [285:37235]
COMMIT
*raw
:PREROUTING ACCEPT [348:73125]
:OUTPUT ACCEPT [285:37235]
COMMIT
*mangle
:PREROUTING ACCEPT [348:73125]
:INPUT ACCEPT [327:69520]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [285:37235]
:POSTROUTING ACCEPT [285:37235]
COMMIT
*nat
:PREROUTING ACCEPT [78:18257]
:INPUT ACCEPT [10:600]
:POSTROUTING ACCEPT [111:8182]
:OUTPUT ACCEPT [111:8182]
:DOCKER - [0:0]
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
-A DOCKER -i docker0 -j RETURN
COMMIT
Eh bien, je l'ai compris. Et c'est un doozy.
Centos 8 utilise nftables , ce qui n'est en soi pas surprenant. Il est livré avec la version nft
des commandes iptables
, ce qui signifie que lorsque vous utilisez la commande iptables
, il maintient réellement un ensemble de tables de compatibilité dans NFTABBLES.
Toutefois...
FirewallD - qui est installé par défaut - a Native prend en charge les NFTABLES, de sorte qu'il n'utilise pas la couche de compatibilité IPTABLES.
Donc pendant iptables -S INPUT
vous montre:
# iptables -S INPUT
-P INPUT ACCEPT
Ce que vous en fait ont:
chain filter_INPUT {
type filter hook input priority 10; policy accept;
ct state established,related accept
iifname "lo" accept
jump filter_INPUT_ZONES_SOURCE
jump filter_INPUT_ZONES
ct state invalid drop
reject with icmpx type admin-prohibited <-- HEY LOOK AT THAT!
}
La solution ici (et honnêtement probablement bon conseil en général) est:
systemctl disable --now firewalld
Avec le pare-feu hors du passage, les règles IPTABLES visibles avec iptables -S
se comportera comme prévu.
Si vous devez quitter le pare-feu, vous devez ajouter NFTABLES RESTION sur l'hôte Hypervisor.
Ajoutez d'abord Docker0 à la zone interne Si vous n'en utilisez pas un autre spécifique:
firewall-cmd --zone=internal --change-interface=docker0 --permanent
firewall-cmd --reload
Exécutez NFTABLES pour appliquer la règle pour accepter le trafic TCP sur 80 ports à l'interface VIRBR0 avec la destination 192.168.122.217:
/sbin/nft add rule inet firewalld filter_FWDI_internal_allow oifname "virbr0" ip daddr 192.168.122.217 tcp dport 80 counter accept comment "comment something"
En outre, il est nécessaire d'ajouter une règle d'Iptables:
/usr/sbin/iptables -t filter -I FORWARD -p tcp -o virbr0 -d 192.168.122.217 --dport 80 -j ACCEPT -m comment --comment "comment something"
Ces deux règles que vous pouvez envelopper avec un script shell et ajouter une unité de service SystemD avec délai défini sur 5 secondes, pour l'avoir exécuté automatiquement pendant la démarrage. Ou convertissez la règle IPTABLES en NFTABLES et sauvegardez-les à la fois à NFTABLES. Mais une fois que vous exécuterez une règle d'iptables convertie, les iptables seront verrouillés et toute nouvelle modification des IPTABLES ne sera possible qu'avec un pare-feu et NFTABLES.
J'AIME J'ai également personnalisé Kernel Tuntables que les paquets traversant le pont n'allèderont pas à IPTABLES.
sysctl net.bridge.bridge-nf-call-iptables=0
sysctl net.bridge.bridge-nf-call-arptables=0
sysctl net.bridge.bridge-nf-call-ip6tables=0
Remarque Si vous allez l'ajouter à SYSCTL.CONF, il peut ne pas être automatiquement appliqué lors du redémarrage comme un bogue connu en fonction de votre distribution Linux.