Impossible de communiquer via Linux Bridge

introduction

Lorsque j'ai enquêté lors du dépannage du titre, j'ai décidé de l'écrire car il y avait peu d'articles qui mentionnaient de manière inattendue cette question comme critique.

un événement

Lorsque j'ai créé un pont Linux sur CentOS7, attribué un conteneur Docker sous celui-ci et confirmé la communication, la communication n'a pas pu être effectuée. qiita-bridge-1.png

Conclusion

Linux Bridge fonctionne en utilisant un module de noyau appelé bridge, mais sa sécurité est gérée par un module de noyau appelé br_netfilter (dépendance avec bridge), et br_netfilter contrôle la communication en regardant les paramètres iptales. .. Par conséquent, la communication peut être effectuée en effectuant l'une des opérations suivantes.

① Désactiver Bridge Netfilter ② Définir l'autorisation pour iptables

Environnement de vérification

OS:CentOS 7.5 Kernel Ver:3.10.0-862.14.4.el7.x86_64 docker Ver:18.06.1-ce

Vérification avec docker normal 0

Tout d'abord, assurez-vous que les conteneurs peuvent communiquer entre eux via docker0, qui est généralement attribué au conteneur docker lorsqu'il est déployé.

Déployer le conteneur Docker

Exécutez docker et déployez deux conteneurs.

 docker run -d --name cent1 centos/tools:latest /sbin/init
 docker run -d --name cent2 centos/tools:latest /sbin/init

Confirmez que le conteneur a démarré normalement avec la commande docker ps.

 docker ps
CONTAINER ID        IMAGE                 COMMAND             CREATED              STATUS              PORTS               NAMES
8126f9f72ee2        centos/tools:latest   "/sbin/init"        6 seconds ago        Up 3 seconds                            cent2
a957a097b6a5        centos/tools:latest   "/sbin/init"        About a minute ago   Up About a minute                       cent1

Vérifiez l'état de l'affectation à docker0

Commencez par vérifier l'association entre la carte réseau du conteneur déployé et la carte réseau de l'hôte docker. La vérification de chaque carte réseau du conteneur Docker est la suivante. eth0 de cent1 est l'index9 de l'hôte docker eth0 de cent2 est l'index11 de l'hôte docker Vous pouvez voir qu'il est lié à.

 docker exec cent1 ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
8: eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

 docker exec cent2 ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
10: eth0@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

La vérification de la carte réseau de l'hôte docker est la suivante.

 ip l
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens160: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 00:0c:29:b3:b5:18 brd ff:ff:ff:ff:ff:ff
3: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 00:0c:29:b3:b5:22 brd ff:ff:ff:ff:ff:ff
4: vlan10@ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether 00:0c:29:b3:b5:22 brd ff:ff:ff:ff:ff:ff
5: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default 
    link/ether 02:42:1c:c2:6d:d0 brd ff:ff:ff:ff:ff:ff
9: vethc59a2d1@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default 
    link/ether f6:1a:1b:00:b9:b5 brd ff:ff:ff:ff:ff:ff link-netnsid 0
11: vethfee6857@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default 
    link/ether 86:45:ea:11:db:35 brd ff:ff:ff:ff:ff:ff link-netnsid 1

De plus, si vous vérifiez les informations de Linux Bridge, vous pouvez voir que veth du côté hôte de cent1 et 2 est affecté à docker0 comme indiqué ci-dessous.

 brctl show
bridge name	bridge id		STP enabled	interfaces
docker0		8000.02421cc26dd0	no		vethc59a2d1
							vethfee6857

Ce qui précède peut être résumé comme indiqué dans l'image ci-dessous.

qiita-bridge-2.png

Confirmation de la communication via docker0

Si vous ignorez le ping de cent1 à cent2 via docker0 et vérifiez la communication, vous pouvez communiquer normalement comme suit.

 docker exec cent2 ping -c 3 172.17.0.3
PING 172.17.0.3 (172.17.0.3) 56(84) bytes of data.
64 bytes from 172.17.0.3: icmp_seq=1 ttl=64 time=10.2 ms
64 bytes from 172.17.0.3: icmp_seq=2 ttl=64 time=0.048 ms
64 bytes from 172.17.0.3: icmp_seq=3 ttl=64 time=0.045 ms

--- 172.17.0.3 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2001ms
rtt min/avg/max/mdev = 0.045/3.448/10.252/4.811 ms

Vérification avec Bridge nouvellement créé

Voici le problème principal. Ensuite, lorsque vous créez un nouveau pont Linux et attribuez un conteneur docker, vérifiez si vous pouvez communiquer comme docker0.

Créer un nouveau pont

Créez un pont nommé new-bridge1 comme nouveau pont.

 brctl addbr new-bridge1
 brctl show
bridge name	bridge id		STP enabled	interfaces
docker0		8000.02421cc26dd0	no		vethc59a2d1
							vethfee6857
new-bridge1		8000.000000000000	no		

Après l'avoir créé, démarrez Bridge comme suit.

 ip l set dev new-bridge1 up
 ip l
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens160: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 00:0c:29:b3:b5:18 brd ff:ff:ff:ff:ff:ff
3: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 00:0c:29:b3:b5:22 brd ff:ff:ff:ff:ff:ff
4: vlan10@ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether 00:0c:29:b3:b5:22 brd ff:ff:ff:ff:ff:ff
5: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default 
    link/ether 02:42:1c:c2:6d:d0 brd ff:ff:ff:ff:ff:ff
9: vethc59a2d1@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master new-bridge1 state UP mode DEFAULT group default 
    link/ether f6:1a:1b:00:b9:b5 brd ff:ff:ff:ff:ff:ff link-netnsid 0
11: vethfee6857@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master new-bridge1 state UP mode DEFAULT group default 
    link/ether 86:45:ea:11:db:35 brd ff:ff:ff:ff:ff:ff link-netnsid 1
12: new-bridge1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether 86:45:ea:11:db:35 brd ff:ff:ff:ff:ff:ff

Exclure la carte réseau du conteneur de docker0

Le NIC du conteneur déployé par docker (pour être exact, le veth correspondant au NIC du conteneur du côté hôte du docker) est dans l'état affecté à docker0. Excluez ces cartes réseau de conteneur du docker 0 pour vérification.

 brctl delif docker0 vethc59a2d1
 brctl delif docker0 vethfee6857
 brctl show
bridge name	bridge id		STP enabled	interfaces
docker0		8000.02421cc26dd0	no		
new-bridge1		8000.000000000000	no		

Attribuer une carte réseau de conteneur au pont créé

Affectez la carte réseau du conteneur au nouveau-pont1 nouvellement créé.

 brctl addif new-bridge1 vethc59a2d1
 brctl addif new-bridge1 vethfee6857

 brctl show
bridge name	bridge id		STP enabled	interfaces
docker0		8000.02421cc26dd0	no		
new-bridge1		8000.8645ea11db35	no		vethc59a2d1
							vethfee6857

En effectuant les opérations jusqu'à ce point, l'état sera comme indiqué dans l'image ci-dessous.

qiita-bridge-3.png

Confirmation de la communication via le pont nouvellement créé

Via le new-bridge1 nouvellement créé, essayez d'envoyer un ping de cent1 à cent2 de la même manière que docker0 plus tôt pour vérifier la communication.

 docker exec cent1 ping -c 3 172.17.0.3
PING 172.17.0.3 (172.17.0.3) 56(84) bytes of data.

--- 172.17.0.3 ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 1999ms

Ensuite, contrairement au cas via docker0 précédemment, vous pouvez voir qu'il n'y a pas de communication entre cent1 et cent2.

Enquête d'événement

Capture de paquets

Tout d'abord, essayez d'obtenir tcpdump sur chaque carte réseau. Deux choses peuvent être vues à partir des résultats ci-dessous. (1) La demande ARP est arrivée normalement de cent1 à cent2, et cent1 a reçu la réponse. ② Le ping a atteint le pont Linux (new-bridge1) mais pas le cent2

qiita-bridge-4.png

NIC de cent1

 tcpdump -i vethc59a2d1 
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on vethc59a2d1, link-type EN10MB (Ethernet), capture size 262144 bytes
23:20:39.379638 IP 172.17.0.2 > 172.17.0.3: ICMP echo request, id 45, seq 1, length 64
23:20:40.378780 IP 172.17.0.2 > 172.17.0.3: ICMP echo request, id 45, seq 2, length 64
23:20:41.378785 IP 172.17.0.2 > 172.17.0.3: ICMP echo request, id 45, seq 3, length 64
23:20:44.383711 ARP, Request who-has 172.17.0.3 tell 172.17.0.2, length 28
23:20:44.383744 ARP, Reply 172.17.0.3 is-at 02:42:ac:11:00:03 (oui Unknown), length 28

NIC de cent2

 tcpdump -i vethfee6857
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on vethfee6857, link-type EN10MB (Ethernet), capture size 262144 bytes
23:20:44.383726 ARP, Request who-has 172.17.0.3 tell 172.17.0.2, length 28
23:20:44.383741 ARP, Reply 172.17.0.3 is-at 02:42:ac:11:00:03 (oui Unknown), length 28

new-bridge1

 tcpdump -i new-bridge1
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on new-bridge1, link-type EN10MB (Ethernet), capture size 262144 bytes
23:20:39.379638 IP 172.17.0.2 > 172.17.0.3: ICMP echo request, id 45, seq 1, length 64
23:20:40.378780 IP 172.17.0.2 > 172.17.0.3: ICMP echo request, id 45, seq 2, length 64
23:20:41.378785 IP 172.17.0.2 > 172.17.0.3: ICMP echo request, id 45, seq 3, length 64
23:20:44.383711 ARP, Request who-has 172.17.0.3 tell 172.17.0.2, length 28
23:20:44.383741 ARP, Reply 172.17.0.3 is-at 02:42:ac:11:00:03 (oui Unknown), length 28

En ce qui concerne ①, je vérifierai le cache ARP dans chaque conteneur au cas où. Vous pouvez voir que l'adresse MAC du partenaire de communication est écrite normalement.

 docker exec cent1 arp -e
Address                  HWtype  HWaddress           Flags Mask            Iface
172.17.0.3               ether   02:42:ac:11:00:03   C                     eth0
gateway                          (incomplete)                              eth0

 docker exec cent2 arp -e
Address                  HWtype  HWaddress           Flags Mask            Iface
172.17.0.2               ether   02:42:ac:11:00:02   C                     eth0
gateway                          (incomplete)                              eth0

Pourquoi cela arrive

Linux Bridge fonctionne en utilisant un module de noyau appelé bridge, mais sa sécurité est gérée par un module de noyau appelé br_netfilter (dépendance avec bridge), et br_netfilter contrôle la communication en regardant les paramètres iptables. Il semble. Par conséquent, la communication via Bridge n'est pas autorisée par défaut, et cela se produit.

$ lsmod | grep br_netfilter
br_netfilter           24576  0
bridge                155648  1 br_netfilter

Solution

La communication entre les conteneurs sera possible en prenant l'une des mesures suivantes.

Partie 1 Désactiver Bridge Netfilter

Netfilter, qui contrôle normalement la communication Linux Bridge, est activé, mais La communication peut être réalisée en désactivant intentionnellement cela. De plus, l'activation / la désactivation de Bridge Netfilter est le paramètre de noyau net.bridge.bridge-nf-call-iptables. Peut être défini par.

Vérifiez l'état du filtre Bridge Net Actuellement, 1 est défini et il est dans un état valide.

 sysctl net.bridge.bridge-nf-call-iptables
net.bridge.bridge-nf-call-iptables = 1

changement de réglage Définissez net.bridge.bridge-nf-call-iptables = 0 dans /etc/sysctl.conf et Reflétez les paramètres.

 cat /etc/sysctl.conf
 sysctl settings are defined through files in
 /usr/lib/sysctl.d/, /run/sysctl.d/, and /etc/sysctl.d/.

 Vendors settings live in /usr/lib/sysctl.d/.
 To override a whole file, create a new file with the same in
 /etc/sysctl.d/ and put new settings there. To override
 only specific settings, add a file with a lexically later
 name in /etc/sysctl.d/ and put new settings there.

 For more information, see sysctl.conf(5) and sysctl.d(5).
net.bridge.bridge-nf-call-iptables = 0

 sysctl -p 
net.bridge.bridge-nf-call-iptables = 0

 sysctl net.bridge.bridge-nf-call-iptables
net.bridge.bridge-nf-call-iptables = 0

Partie 2 Définir les paramètres d'autorisation pour iptables

Bridge Netfilter fait référence à iptables pour contrôler la communication. Par conséquent, en ajoutant la règle d'autorisation à iptables comme suit Vous pourrez communiquer via le pont Linux. Ici, lors de l'ajout d'une règle avec la commande iptables, en spécifiant un module d'appariement de paquets appelé physdev qui gère l'entrée et la sortie du pont avec -m, il est configuré pour autoriser toutes les communications via Brige.

iptables --Description des commandes de gestion système --Liste des commandes Linux https://kazmax.zpp.jp/cmd/i/iptables.8.html

 iptables -I FORWARD -m physdev --physdev-is-bridged -j ACCEPT

 iptables -nvL --line-number
Chain INPUT (policy ACCEPT 52 packets, 3250 bytes)
num   pkts bytes target     prot opt in     out     source               destination         

Chain FORWARD (policy DROP 0 packets, 0 bytes)
num   pkts bytes target     prot opt in     out     source               destination         
1        0     0 ACCEPT     all  --  *      *       0.0.0.0/0            0.0.0.0/0            PHYSDEV match --physdev-is-bridged
2     2006 2508K DOCKER-USER  all  --  *      *       0.0.0.0/0            0.0.0.0/0           
3     2006 2508K DOCKER-ISOLATION-STAGE-1  all  --  *      *       0.0.0.0/0            0.0.0.0/0           
4     1126 2451K ACCEPT     all  --  *      docker0  0.0.0.0/0            0.0.0.0/0            ctstate RELATED,ESTABLISHED
5       46  5840 DOCKER     all  --  *      docker0  0.0.0.0/0            0.0.0.0/0           
6      834 51247 ACCEPT     all  --  docker0 !docker0  0.0.0.0/0            0.0.0.0/0           
7       46  5840 ACCEPT     all  --  docker0 docker0  0.0.0.0/0            0.0.0.0/0           

 (Omis)

Dans kubernetes, il est spécifié que net.bridge.bridge-nf-call-iptables = 1. Parce que j'ai rencontré ce problème lors de la validation suivante avec kubernetes Ajout de la prise en charge des règles iptables.

Jouez avec Multus https://rheb.hatenablog.com/entry/multus_introduction

Pourquoi la communication via docker0 est-elle possible?

À ce stade, une question se pose: "Pourquoi docker0 peut communiquer même s'il s'agit en fait du même pont Linux?" La réponse réside dans les paramètres iptables. docker semble décrire les règles requises pour l'installation et la création de réseau docker dans iptables. Si vous vérifiez les informations de réglage iptables, vous pouvez voir que la communication de docker0 vers l'extérieur et la communication via docker0 sont ACCEPTÉES dans les chaînes FRWARD n ° 5 et 6. De plus, docker définit également NAT pour iptables.

 iptables -nvL --line-number
Chain INPUT (policy ACCEPT 228K packets, 579M bytes)
num   pkts bytes target     prot opt in     out     source               destination         

Chain FORWARD (policy DROP 12 packets, 1008 bytes)
num   pkts bytes target     prot opt in     out     source               destination         
1     9003   12M DOCKER-USER  all  --  *      *       0.0.0.0/0            0.0.0.0/0           
2     9003   12M DOCKER-ISOLATION-STAGE-1  all  --  *      *       0.0.0.0/0            0.0.0.0/0           
3     5650   12M ACCEPT     all  --  *      docker0  0.0.0.0/0            0.0.0.0/0            ctstate RELATED,ESTABLISHED
4        0     0 DOCKER     all  --  *      docker0  0.0.0.0/0            0.0.0.0/0           
5     3341  191K ACCEPT     all  --  docker0 !docker0  0.0.0.0/0            0.0.0.0/0           
6        0     0 ACCEPT     all  --  docker0 docker0  0.0.0.0/0            0.0.0.0/0           

Chain OUTPUT (policy ACCEPT 130K packets, 7700K bytes)
num   pkts bytes target     prot opt in     out     source               destination         

Chain DOCKER (1 references)
num   pkts bytes target     prot opt in     out     source               destination         

Chain DOCKER-ISOLATION-STAGE-1 (1 references)
num   pkts bytes target     prot opt in     out     source               destination         
1     3341  191K DOCKER-ISOLATION-STAGE-2  all  --  docker0 !docker0  0.0.0.0/0            0.0.0.0/0           
2     9003   12M RETURN     all  --  *      *       0.0.0.0/0            0.0.0.0/0           

Chain DOCKER-ISOLATION-STAGE-2 (1 references)
num   pkts bytes target     prot opt in     out     source               destination         
1        0     0 DROP       all  --  *      docker0  0.0.0.0/0            0.0.0.0/0           
2     3341  191K RETURN     all  --  *      *       0.0.0.0/0            0.0.0.0/0           

Chain DOCKER-USER (1 references)
num   pkts bytes target     prot opt in     out     source               destination         
1     9003   12M RETURN     all  --  *      *       0.0.0.0/0            0.0.0.0/0  

Résumé

Lors de l'utilisation de Linux Bridge, cela ne devrait fonctionner que sur la couche L2, alors j'ai pensé que je devrais pouvoir communiquer sans me soucier de quoi que ce soit, mais c'était une erreur. Linux Bridge fonctionne également comme L3, y compris la possibilité d'attribuer une adresse IP. Cette fois, j'ai expérimenté un conteneur, mais je suppose que cet exemple peut résoudre le même problème lors de l'attribution d'une machine virtuelle KVM. (Non vérifié)

Je vous remercie

Dans le cadre de cette enquête, nous avons reçu diverses coopérations d'enquête et consultations de la part des personnes qui nous entourent. Je voudrais prendre ce cas et merci.

référence

Paramètres réseau du pont KVM https://qiita.com/TsutomuNakamura/items/e15d2c8c02586a7ae572#bridge-%E3%83%88%E3%83%A9%E3%83%95%E3%82%A3%E3%83%83%E3%82%AF%E3%81%AEnetfilter-%E3%82%92%E7%84%A1%E5%8A%B9%E5%8C%96%E3%81%99%E3%82%8B

11.2. Réseau de pont utilisant libvirt https://docs.fedoraproject.org/ja-JP/Fedora/13/html/Virtualization_Guide/sect-Virtualization-Network_Configuration-Bridged_networking_with_libvirt.html

Recommended Posts

Impossible de communiquer via Linux Bridge
[Windows] RDP vers Windows via Linux