UDP-Verkehr wird nicht von Docker-Containern weitergeleitet -> Docker-Host

9

Ich habe einen Docker-Container und kann keine DNS-Lookups in Containern ausführen, obwohl dies vom Docker-Host aus einwandfrei funktioniert.

Es ist bekannt, dass der Konfigurationsverwaltungscode, der den Docker-Host erstellt, auf einem Standard-RHEL 7-Image vom Markt funktioniert. Daher ist bekannt, dass das Problem im SOE RHEL 7-Image liegt.

RHEL 7.2 / Docker Version 1.12.6, Build 88a4867 / 1.12.6. Behälter ist RHEL 7.3. SELinux im aktivierten / zulässigen Modus. Der Docker-Host ist eine Amazon EC2-Instanz.

Einige Konfigurationen:

# /etc/sysconfig/docker
OPTIONS='--dns=10.0.0.10 --dns=10.0.0.11 --dns-search=example.com'
DOCKER_CERT_PATH=/etc/docker
ADD_REGISTRY='--add-registry registry.example.com'
no_proxy=169.254.169.254,localhost,127.0.0.1,registory.example.com
http_proxy=http://proxy.example.com:8080
https_proxy=http://proxy.example.com:8080
ftp_proxy=http://proxy.example.com:8080

Die Resolver-Konfiguration in Container und Host ist identisch:

# /etc/resolv.conf
search example.com
nameserver 10.0.0.10
nameserver 10.0.0.11

Wenn ich den Docker-Daemon mit neu starte, wird --debugFolgendes angezeigt journalctl -u docker.service:

Aug 08 11:44:23 myhost.example.com dockerd-current[17341]: time="2017-08-08T11:44:23.430769581+10:00" level=debug msg="Name To resolve: http://proxy.example.com."
Aug 08 11:44:23 myhost.example.com dockerd-current[17341]: time="2017-08-08T11:44:23.431488213+10:00" level=debug msg="Query http://proxy.example.com.[1] from 172.18.0.6:38189, forwarding to udp:10.162.182.101"
Aug 08 11:44:27 myhost.example.com dockerd-current[17341]: time="2017-08-08T11:44:27.431772666+10:00" level=debug msg="Read from DNS server failed, read udp 172.18.0.6:38189->10.162.182.101:53: i/o timeout"

Wenn ich dieser Beobachtung weiter folge, stellt sich heraus, dass ich ein Netzwerk zum Laufen bringen kann, wenn ich eine IP-Adresse anstelle des DNS-Namens des Proxys spezifiziere. Dies ist jedoch nur eine Möglichkeit, die Verwendung von DNS zu vermeiden, und keine echte Lösung.

In der Tat ( Update Nr. 3 ) stellt sich heraus, dass ich das Problem vollständig vermeiden kann, indem ich DNS einfach so konfiguriere, dass TCP anstelle von UDP verwendet wird, d. H.

# head -1 /etc/sysconfig/docker
OPTIONS="--dns=10.0.0.10 --dns=10.0.0.11 --dns-search=example.com --dns-opt=use-vc"

(Durch Hinzufügen einer Zeile use-vcwird der Resolver angewiesen, TCP anstelle von UDP zu verwenden.)

Ich habe einige verdächtig aussehende Regeln in iptables notiert, aber diese erwiesen sich als normal:

# iptables -n -L DOCKER-ISOLATION -v --line-numbers
Chain DOCKER-ISOLATION (1 references)
num   pkts bytes target     prot opt in     out     source               destination         
1        0     0 DROP       all  --  br-1d6a05c10468 docker0  0.0.0.0/0            0.0.0.0/0           
2        0     0 DROP       all  --  docker0 br-1d6a05c10468  0.0.0.0/0            0.0.0.0/0           
3    34903   11M RETURN     all  --  *      *       0.0.0.0/0            0.0.0.0/0           

Nachdem ich diese beiden DROP-Regeln gelöscht hatte, sah ich das Problem weiterhin.

Vollständige iptables:

# iptables -nL -v
Chain INPUT (policy ACCEPT 2518 packets, 1158K bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
23348 9674K DOCKER-ISOLATION  all  --  *      *       0.0.0.0/0            0.0.0.0/0           
    0     0 DOCKER     all  --  *      docker0  0.0.0.0/0            0.0.0.0/0           
    0     0 ACCEPT     all  --  *      docker0  0.0.0.0/0            0.0.0.0/0            ctstate RELATED,ESTABLISHED
    0     0 ACCEPT     all  --  docker0 !docker0  0.0.0.0/0            0.0.0.0/0           
    0     0 ACCEPT     all  --  docker0 docker0  0.0.0.0/0            0.0.0.0/0           
23244 9667K DOCKER     all  --  *      br-1d6a05c10468  0.0.0.0/0            0.0.0.0/0           
23232 9667K ACCEPT     all  --  *      br-1d6a05c10468  0.0.0.0/0            0.0.0.0/0            ctstate RELATED,ESTABLISHED
  104  6230 ACCEPT     all  --  br-1d6a05c10468 !br-1d6a05c10468  0.0.0.0/0            0.0.0.0/0           
   12   700 ACCEPT     all  --  br-1d6a05c10468 br-1d6a05c10468  0.0.0.0/0            0.0.0.0/0           

Chain OUTPUT (policy ACCEPT 2531 packets, 414K bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain DOCKER (2 references)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 ACCEPT     tcp  --  !br-1d6a05c10468 br-1d6a05c10468  0.0.0.0/0            172.18.0.2           tcp dpt:443
    0     0 ACCEPT     tcp  --  !br-1d6a05c10468 br-1d6a05c10468  0.0.0.0/0            172.18.0.2           tcp dpt:80
    0     0 ACCEPT     tcp  --  !br-1d6a05c10468 br-1d6a05c10468  0.0.0.0/0            172.18.0.3           tcp dpt:389

Chain DOCKER-ISOLATION (1 references)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 DROP       all  --  br-1d6a05c10468 docker0  0.0.0.0/0            0.0.0.0/0           
    0     0 DROP       all  --  docker0 br-1d6a05c10468  0.0.0.0/0            0.0.0.0/0           
23348 9674K RETURN     all  --  *      *       0.0.0.0/0            0.0.0.0/0           

Bridge-Konfiguration

# ip addr show docker0
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN 
    link/ether 02:42:a8:73:db:bb brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 scope global docker0
       valid_lft forever preferred_lft forever
# ip addr show br-1d6a05c10468
3: br-1d6a05c10468: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:d5:b6:2d:f5 brd ff:ff:ff:ff:ff:ff
    inet 172.18.0.1/16 scope global br-1d6a05c10468
       valid_lft forever preferred_lft forever

und

# docker network inspect bridge 
[
    {
        "Name": "bridge",
        "Id": "e159ddd37386cac91e0d011ade99a51f9fe887b8d32d212884beace67483af44",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.17.0.0/16",
                    "Gateway": "172.17.0.1"
                }
            ]
        },
        "Internal": false,
        "Containers": {},
        "Options": {
            "com.docker.network.bridge.default_bridge": "true",
            "com.docker.network.bridge.enable_icc": "true",
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
            "com.docker.network.bridge.name": "docker0",
            "com.docker.network.driver.mtu": "1500"
        },
        "Labels": {}
    }
]

In den Protokollen:

Aug 04 17:33:32 myhost.example.com systemd[1]: Starting Docker Application Container Engine...
Aug 04 17:33:33 myhost.example.com dockerd-current[2131]: time="2017-08-04T17:33:33.056770003+10:00" level=info msg="libcontainerd: new containerd process, pid: 2140"
Aug 04 17:33:34 myhost.example.com dockerd-current[2131]: time="2017-08-04T17:33:34.740346421+10:00" level=info msg="Graph migration to content-addressability took 0.00 seconds"
Aug 04 17:33:34 myhost.example.com dockerd-current[2131]: time="2017-08-04T17:33:34.741164354+10:00" level=info msg="Loading containers: start."
Aug 04 17:33:34 myhost.example.com dockerd-current[2131]: .........................time="2017-08-04T17:33:34.903371015+10:00" level=info msg="Firewalld running: true"
Aug 04 17:33:35 myhost.example.com dockerd-current[2131]: time="2017-08-04T17:33:35.325581993+10:00" level=info msg="Default bridge (docker0) is assigned with an IP address 172.17.0.0/16. Daemon option --bip can be used to set a preferred IP address" 
Aug 04 17:33:36 myhost.example.com dockerd-current[2131]: time="2017-08-04T17:33:36+10:00" level=info msg="Firewalld running: true"
Aug 04 17:33:37 myhost.example.com dockerd-current[2131]: time="2017-08-04T17:33:37+10:00" level=info msg="Firewalld running: true"
Aug 04 17:33:37 myhost.example.com dockerd-current[2131]: time="2017-08-04T17:33:37+10:00" level=info msg="Firewalld running: true"
Aug 04 17:33:38 myhost.example.com dockerd-current[2131]: time="2017-08-04T17:33:38+10:00" level=info msg="Firewalld running: true"
Aug 04 17:33:39 myhost.example.com dockerd-current[2131]: time="2017-08-04T17:33:39+10:00" level=info msg="Firewalld running: true"
Aug 04 17:33:40 myhost.example.com dockerd-current[2131]: time="2017-08-04T17:33:40+10:00" level=info msg="Firewalld running: true"
Aug 04 17:33:40 myhost.example.com dockerd-current[2131]: time="2017-08-04T17:33:40+10:00" level=info msg="Firewalld running: true"
Aug 04 17:33:42 myhost.example.com dockerd-current[2131]: time="2017-08-04T17:33:42+10:00" level=info msg="Firewalld running: true"
Aug 04 17:33:42 myhost.example.com dockerd-current[2131]: time="2017-08-04T17:33:42+10:00" level=info msg="Firewalld running: true"
Aug 04 17:33:43 myhost.example.com dockerd-current[2131]: time="2017-08-04T17:33:43.541905145+10:00" level=info msg="Loading containers: done."
Aug 04 17:33:43 myhost.example.com dockerd-current[2131]: time="2017-08-04T17:33:43.541975618+10:00" level=info msg="Daemon has completed initialization"
Aug 04 17:33:43 myhost.example.com dockerd-current[2131]: time="2017-08-04T17:33:43.541998095+10:00" level=info msg="Docker daemon" commit="88a4867/1.12.6" graphdriver=devicemapper version=1.12.6
Aug 04 17:33:43 myhost.example.com dockerd-current[2131]: time="2017-08-04T17:33:43.548508756+10:00" level=info msg="API listen on /var/run/docker.sock"
Aug 04 17:33:43 myhost.example.com systemd[1]: Started Docker Application Container Engine.

Über den Container kann ich das Standard-Gateway anpingen, aber die Namensauflösung schlägt fehl.

Ich habe eine seltsame Sache im Protokoll bemerkt ( Update Nr. 2 Ich weiß jetzt, dass dies ein roter Hering ist - siehe Diskussion unten):

# journalctl -u docker.service |grep insmod > /tmp/log # \n's replaced below
Jul 26 23:59:02 myhost.example.com dockerd-current[3185]: time="2017-07-26T23:59:02.056295890+10:00" level=warning msg="Running modprobe bridge br_netfilter failed with message: insmod /lib/modules/3.10.0-514.26.2.el7.x86_64/kernel/net/bridge/bridge.ko 
sysctl: cannot stat /proc/sys/net/bridge/bridge-nf-call-arptables: No such file or directory
sysctl: cannot stat /proc/sys/net/bridge/bridge-nf-call-iptables: No such file or directory
sysctl: cannot stat /proc/sys/net/bridge/bridge-nf-call-ip6tables: No such file or directory
modprobe: ERROR: Error running install command for bridge
modprobe: ERROR: could not insert 'bridge': Unknown error 253
insmod /lib/modules/3.10.0-514.26.2.el7.x86_64/kernel/net/llc/llc.ko 
insmod /lib/modules/3.10.0-514.26.2.el7.x86_64/kernel/net/802/stp.ko 
install /sbin/modprobe --ignore-install bridge && /sbin/sysctl -q -w net.bridge.bridge-nf-call-arptables=0 net.bridge.bridge-nf-call-iptables=0 net.bridge.bridge-nf-call-ip6tables=0 
insmod /lib/modules/3.10.0-514.26.2.el7.x86_64/kernel/net/bridge/br_netfilter.ko 
, error: exit status 1"

Update Nr. 1 : und das kommt von:

# tail -2 /etc/modprobe.d/dist.conf
# Disable netfilter on bridges when the bridge module is loaded
install bridge /sbin/modprobe --ignore-install bridge && /sbin/sysctl -q -w net.bridge.bridge-nf-call-arptables=0 net.bridge.bridge-nf-call-iptables=0 net.bridge.bridge-nf-call-ip6tables=0

Ebenfalls:

# cat /proc/sys/net/bridge/bridge-nf-call-{arp,ip,ip6}tables
1
1
1

Aber auch nachdem ich das getan habe:

# for i in /proc/sys/net/bridge/bridge-nf-call-{arp,ip,ip6}tables ; do echo 0 > $i ; done 

Immer noch kein Glück.

Ich habe einen ganzen Tag damit verbracht, mir inzwischen die Haare auszureißen. Alle Gedanken darüber, was ich sonst noch versuchen könnte oder was das Problem sonst noch sehr geschätzt werden könnte.

Update Nr. 4

Ich habe einige Experimente mit Netcat durchgeführt und festgestellt, dass nicht alle UDP-Pakete weitergeleitet werden, wenn sie von einem Container -> Host gesendet werden. Ich habe versucht, mehrere Ports zu verwenden, darunter 53, 2115 und 50000. TCP-Pakete sind jedoch in Ordnung. Dies gilt immer noch, wenn ich die iptables-Regeln mit spüle iptables -F.

Außerdem kann ich UDP-Pakete von einem Container zu einem anderen senden - nur UDP-Verkehr vom Container -> Host wird nicht weitergeleitet.

So richten Sie den Test ein:

Auf dem Host mit IP 10.1.1.10:

# nc -u -l 50000

Auf dem Container:

# echo "foo" | nc -w1 -u 10.1.1.10 50000

Während einer TCP-Dump-Erfassung sehe ich:

17:20:36.761214 IP (tos 0x0, ttl 64, id 48146, offset 0, flags [DF], proto UDP (17), length 32)
    172.17.0.2.41727 > 10.1.1.10.50000: [bad udp cksum 0x2afa -> 0x992f!] UDP, length 4
        0x0000:  4500 0020 bc12 4000 4011 53de ac11 0002  E.....@[email protected].....
        0x0010:  0aa5 7424 a2ff c350 000c 2afa 666f 6f0a  ..t$...P..*.foo.
        0x0020:  0000 0000 0000 0000 0000 0000 0000 0000  ................
17:20:36.761214 IP (tos 0x0, ttl 64, id 48146, offset 0, flags [DF], proto UDP (17), length 32)
    172.17.0.2.41727 > 10.1.1.10.50000: [bad udp cksum 0x2afa -> 0x992f!] UDP, length 4
        0x0000:  4500 0020 bc12 4000 4011 53de ac11 0002  E.....@[email protected].....
        0x0010:  0aa5 7424 a2ff c350 000c 2afa 666f 6f0a  ..t$...P..*.foo.
        0x0020:  0000 0000 0000 0000 0000 0000 0000 0000  ................

Ich versuchte erfolglos , die schlechten UDP - Prüfsummen über zu beheben diese .

Ich habe jedoch festgestellt, dass die fehlerhaften UDP-Prüfsummen auch während der erfolgreichen Übertragung von UDP-Paketen (Host -> Host) und Container -> Container angezeigt werden.

Zusammenfassend weiß ich jetzt:

  • Routing ist in Ordnung

  • iptables wird gespült

  • SELinux ist zulässig

  • Alle TCP arbeiten in alle Richtungen

  • Alle UDP von Container -> Container ist in Ordnung

  • Alle UDP von Host -> Host ist in Ordnung

  • Alle UDP vom Host -> Container ist in Ordnung

  • ABER es werden keine UDP-Pakete vom Container -> Host weitergeleitet

Alex Harvey
quelle
Können Ihre Container auf Port 53 kommunizieren? Telnet 8.8.8.8 53
Waisenkinder
Ich kann den 8.8.8.8 (TCP) -Port 53 nicht erreichen, da 8.8.8.8 von einer Unternehmensfirewall blockiert wird. Ich kann jedoch über TCP-Port 53 eine Verbindung zu meinem lokalen DNS-Server herstellen - siehe mein Update. Am Morgen möchte ich einen Weg finden, mit Netcat zu beweisen (was ich derzeit glaube), dass das Problem wirklich darin besteht, dass diese Container einfach keinen ausgehenden UDP-Verkehr weiterleiten.
Alex Harvey
@Orphans, aktualisiert mit Ergebnissen meiner Netcat-Experimente. Grundsätzlich geht alles UDP von Container -> Host verloren, aber TCP ist in Ordnung, und UDP ist auch in Ordnung von Container -> Container und von Host -> Container. Und alles wahr nach iptables -F.
Alex Harvey
Vielen Dank, aber nein, hier geht es um die Portweiterleitung - Weiterleiten eines Ports in einem Container, damit ein Client unter einer Adresse auf dem Docker-Host eine Verbindung zu ihm herstellen kann. In meinem Fall versucht der Container, ausgehende UDP-Pakete zu senden.
Alex Harvey

Antworten:

3

Es scheint, dass Sie eine Modprobe- installDirektive haben, die nicht funktionieren kann. Möglicherweise ist dies auf ein unvollständiges Update auf RHEL 7.2 oder einige manuelle Korrekturen zurückzuführen.

Versuchen Sie es grep -r bridge /etc/modprobe.d /lib/modprobe.dzunächst oder graben Sie auf andere Weise herum /etc/modprobe.doder /lib/modprobe.dversuchen Sie herauszufinden, wo die aufrufende installRegel definiert istsysctl -q -w net.bridge.bridge-nf-call-arptables=0 net.bridge.bridge-nf-call-iptables=0 net.bridge.bridge-nf-call-ip6tables=0

Dies sysctlist eindeutig am falschen Ort. Es ist entweder überflüssig oder sollte danach erscheinen br_netfilter, nicht vorher. Warum? Vor kurzem wurde die /proc/sys/net/bridgeHandhabung vom bridgeModul zum br_netfilterModul verschoben . Dies geschieht bei einigen Versionen von kernel*.rpm, während der Inhalt von modprobe.dVerzeichnissen mit anderen einzelnen Paketen verteilt wird. Ich habe auf meinem RHEL 7.2 überprüft:

# modprobe bridge
# sysctl -q -w net.bridge.bridge-nf-call-iptables=0
sysctl: cannot stat /proc/sys/net/bridge/bridge-nf-call-iptables: No such file or directory
# modprobe br_netfilter
# sysctl -q -w net.bridge.bridge-nf-call-iptables=0    # ok now

Ich sehe diese "gebrochenen" Regeln auf meiner Vanille RHEL 7.1 nicht und ihre Herkunft ist mir rätselhaft . Ich habe es versucht:

# modprobe -n -vvv bridge
modprobe: INFO: custom logging function 0x40a130 registered
insmod /lib/modules/3.10.0-229.11.1.el7.x86_64/kernel/net/llc/llc.ko
insmod /lib/modules/3.10.0-229.11.1.el7.x86_64/kernel/net/802/stp.ko
insmod /lib/modules/3.10.0-229.11.1.el7.x86_64/kernel/net/bridge/bridge.ko
modprobe: INFO: context 0xf1c270 released
# echo "install bridge echo example_of_a_modprobe_rule" > /etc/modprobe.d/zzz5.conf
# modprobe -n -vvv bridge
modprobe: INFO: custom logging function 0x40a130 registered
insmod /lib/modules/3.10.0-229.11.1.el7.x86_64/kernel/net/llc/llc.ko
insmod /lib/modules/3.10.0-229.11.1.el7.x86_64/kernel/net/802/stp.ko
install echo example_of_a_modprobe_rule
modprobe: INFO: context 0xeaa270 released
# rm /etc/modprobe.d/zzz5.conf

Update: Sieht aus wie xenserver verwendet ein ähnliches modprobe Hack . Es ist ein böser Fehler, das Verhalten von Kernelmodulen für alle global zu ändern, unabhängig davon, ob Sie tatsächlich xenserver ausführen oder nicht. und der Käfer hat auf uns zurückgeschossen.

Update 2: Jetzt haben Sie festgestellt, /etc/modprobe.d/dist.confdass dies dieses Problem verursacht und nicht Docker. Unabhängig davon, ob Sie Docker haben oder nicht, modprobe bridgewird immer 1 zurückgegeben und ein Fehler ausgegeben. Normalerweise ist dist.conf ein Teil des module-init-toolsPakets auf RHEL6. Diese Datei darf nicht auf RHEL7 verwendet werden. Es ist auf keinem meiner RHEL7-Systeme und sie laufen einwandfrei. In RHEL7 ist das Paket dist.conf kmodund enthält es nicht. Ich würde:

rpm -qf /etc/modprobe.d/dist.conf  # what package owns this file?

Wenn dist.conf nicht im Besitz von package ist, sichern Sie es und löschen Sie alle Zeilen, die Ihnen keinen offensichtlichen Vorteil bringen (möglicherweise löschen Sie die Datei sogar ganz).

Wenn dist.conf einem Paket gehört, sollten Sie dieses Paket entfernen / aktualisieren, da es hinsichtlich der RHEL 7.2-Kompatibilität eindeutig fehlerhaft wurde.

kubanczyk
quelle
Die Datei gehört in der Tat keinem Paket. Ich habe die Datei jedoch vollständig entfernt und den Docker-Host neu erstellt, und ich befürchte, ich habe bewiesen, dass dies nicht das Problem der DNS-Konnektivität verursacht. :(
Alex Harvey
Schade :(
Seufz
3

Ich habe es herausgefunden.

Wir hatten einen Trend Micro (Antiviren) -Agenten im SOE, von dem ich nichts wusste.

Das Problem zu beheben war so einfach wie:

# systemctl stop ds_agent.service
# pkill ds_agent

Ich bin mir zu diesem Zeitpunkt nicht ganz sicher, warum UDP von Containern blockiert wird oder wie es gestoppt werden kann.

Alex Harvey
quelle
Stellen Sie sicher, dass für Ihre Unternehmensrichtlinie ds_agent nicht ausgeführt werden muss. Möglicherweise möchten Sie einen Fehler bei Trend Micro melden, damit dieser an der Quelle behoben werden kann.
Ed Neville
Gute Idee, aber es war nicht mein Team, das Trend Micro geleitet hat, und ich habe diesen Job jetzt verlassen!
Alex Harvey
Ich hatte ein ähnliches Problem mit dem TCP-Verkehr, der nicht einmal in der externen tcpdump-Ausgabe angezeigt wurde. Ich hatte vergessen, dass ds_agent auf dieser Box ausgeführt wurde, und das scheint die Hauptursache zu sein. Gut, denn ich hatte keine anderen Ideen mehr! :-)
AnotherHowie