[LINUX] Verfolgen Sie den Kommunikationsfluss der Docker-Bridge-Verbindung mit nftables

Einführung

Ich habe gerade angefangen, Docker unter CentOS 8 zu studieren. Also wollte ich den Paketfluss im externen Host <---> Container verfolgen, also habe ich viel recherchiert.

Ich konnte jedoch dem Ablauf der Anfrage folgen, aber ich konnte der Antwort nicht folgen. .. .. Vorerst werde ich nur die Anforderungsroute zusammenfassen.

Verschiedene Voraussetzungen

Überprüfungsumgebung

Ich verfolge in der folgenden Umgebung.

Docker-Betriebsumgebung

userland proxy(docker-proxy) Ich habe Docker-Pxory nicht verwendet, weil ich die grundlegende Funktionsweise des Docker-Netzwerks mit iptables / nftables sehen wollte. (Haarnadel NAT) Die folgenden Dateien werden platziert und überprüft.

/etc/docker/daemon.json


{
    "userland-proxy": false
}

Referenz: https://github.com/nigelpoulton/docker/blob/master/docs/userguide/networking/default_network/binding.md

docker network and containers Verwenden Sie als Überprüfungsumgebung die durch den folgenden Eintrag generierte. Der Docker-Host 10.254.10.252 ist mit CentOS8 konfiguriert. Die Erklärung bezieht sich auf den Radius.

** Docker Compose kann Netzwerkdienste in 5 Minuten erstellen (dhcp / radius / proxy / tftp / syslog) **

Screenshot from Gyazo

Die folgenden Container werden erstellt.

server app address listen
proxy squid 172.20.0.2 8080/tcp
syslog rsyslog 172.20.0.3 514/udp
radius freeRADIUS 172.20.0.4 1812/udp
dhcp ISC-Kea 172.20.0.5 67/udp
tftp tftp-server - 69/udp
# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                    NAMES
b11308767849        infraserv:proxy     "/usr/sbin/init"    3 minutes ago       Up 3 minutes        0.0.0.0:8080->8080/tcp   proxy
33054f8b7d58        infraserv:tftp      "/usr/sbin/init"    35 hours ago        Up 2 hours                                   tftp
851ea861d04e        infraserv:syslog    "/usr/sbin/init"    35 hours ago        Up 2 hours          0.0.0.0:514->514/udp     syslog
dd3a657cfda2        infraserv:dhcp      "/usr/sbin/init"    35 hours ago        Up 2 hours          0.0.0.0:67->67/udp       dhcp
7249b9c4f11d        infraserv:radius    "/usr/sbin/init"    35 hours ago        Up 2 hours          0.0.0.0:1812->1812/udp   radius

Ein Netzwerk mit den folgenden Parametern wird generiert.

key value
name infraserv_infranet
subnet 172.20.0.0/24
interface docker1

Da tftp in der Umgebung von "--net = host" arbeitet, befindet sich "Docker-Netzwerk" im folgenden Zustand.

# docker network inspect infraserv_infranet
[
    {
        "Name": "infraserv_infranet",
        "Id": "7ed8face2e4fec3110384fa3366512f8c78db6e10be6e7271b3d92452aefd254",
        "Created": "2020-02-15T05:37:59.248249755-05:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.20.0.0/24",
                    "Gateway": "172.20.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": true,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "7249b9c4f11de1f986892965671086d20957a6021269a5f5bc6dd85263bc0d70": {
                "Name": "radius",
                "EndpointID": "03ae6a9b9ff7817eea101955d2d6ff016982beb65c7dd6631c75c7299682c2dd",
                "MacAddress": "02:42:ac:14:00:04",
                "IPv4Address": "172.20.0.4/24",
                "IPv6Address": ""
            },
            "851ea861d04edeb5f5c2498cc60f58532c87a44592db1f6c51280a8ce27940bd": {
                "Name": "syslog",
                "EndpointID": "d18e466d27def913ac74b7555acc9ef79c88c62e62085b50172636546d2e72bb",
                "MacAddress": "02:42:ac:14:00:03",
                "IPv4Address": "172.20.0.3/24",
                "IPv6Address": ""
            },
            "b11308767849c7227fbde53234c1b1816859c8e871fcc98c4fcaacdf7818e89e": {
                "Name": "proxy",
                "EndpointID": "ffa6479b4f28c9c1d106970ffa43bd149461b4728b64290541643eb895a02892",
                "MacAddress": "02:42:ac:14:00:02",
                "IPv4Address": "172.20.0.2/24",
                "IPv6Address": ""
            },
            "dd3a657cfda211c08b7c5c2166f10d189986e4779f1dfea227b3afe284cbafec": {
                "Name": "dhcp",
                "EndpointID": "7371f4cf652d8b1bdbf2dc1e5e8ae97013a9a70b890c2caa36c2a7cc93b165df",
                "MacAddress": "02:42:ac:14:00:05",
                "IPv4Address": "172.20.0.5/24",
                "IPv6Address": ""
            }
        },
        "Options": {
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
            "com.docker.network.bridge.name": "docker1"
        },
        "Labels": {
            "com.docker.compose.network": "infranet",
            "com.docker.compose.project": "infraserv",
            "com.docker.compose.version": "1.25.3"
        }
    }
]

Adresse Familie

Der Kürze halber haben wir uns auf IPv4 konzentriert.

Informationen zum Paketfluss in Docker

Folgen Sie der Mitteilung (bei Radius)

Dieses Mal nehmen wir ein Beispiel für das Senden einer Radiusanforderung von einem externen Terminal (10.254.10.105) an den Docker-Host (10.254.10.252). Der Haken der interessierenden Kette ist Prerouting-> Forward-> Postrouting, da er weitergeleitet wird, nachdem er beim lokalen Host angekommen ist. Daher wird der Kettentyp erläutert, wobei nur Filter und Nat im Mittelpunkt stehen.

Die Regeln schließen unnötige Regeln aus dem "nft list ruleset" aus, sind jedoch keine sehr nützlichen Informationen. Deshalb habe ich sie in [Supplement] zusammengefasst (#Überprüfen Sie die erforderlichen Tabellen).

Anfrage von einem externen Terminal (Prerouting)

Wenn der Hook aus dem Regelsatz "nft list" vorläuft, ist dies wie folgt.

         table ip nat {
           chain PREROUTING {
(1)          type nat hook prerouting priority -100; policy accept;
(2)->        fib daddr type local COUNTER jump DOCKER
           }
   ->(2)   chain DOCKER {
      ↓      meta l4proto udp udp dport 514 COUNTER dnat to 172.20.0.3:514
      ↓      meta l4proto udp udp dport 67 COUNTER dnat to 172.20.0.5:67
      ↓      meta l4proto tcp tcp dport 8080 COUNTER dnat to 172.20.0.2:8080
     (3)     meta l4proto udp udp dport 1812 COUNTER dnat to 172.20.0.4:1812
           }
         }

Die aktuelle Mitteilung lautet "10.254.10.105:random-> 10.254.10.252: 1812". (1) Es wird eine Kette namens PREROUTING ausgewählt, die das Prerouting einbindet und nat ausführt. (2) Da DstAddr lokal ist, springen Sie zu der Kette namens DOCKER Der Adr-Typ local ist die Adresse des lokalen Hosts (in diesem Fall Docker-Host). In diesem Fall ist es lo: 127.0.0.1`` ens192: 10.254.10.252`` docker1: 172.20.0.1. (3) Da DstPort 1812 ist, ** DNAT DstAddr bis 172.20.0.4:1812 ** Richtlinie anwenden-> ** akzeptieren **, da keine weitere Verarbeitung erfolgt

Die Mitteilung an dieser Stelle lautet "10.254.10.105:random-> 172.20.0.4: 1812". Da das Ziel in 172.20.0.4 geändert wurde, führt Sie die Routing-Entscheidung zum Forward-Hook.

Anfrage von einem externen Terminal (weiterleiten)

Wenn der Hook vom nft list ruleset weitergeleitet wird, ist dies wie folgt.

                                table ip filter {
                                  chain FORWARD {
(1)                                 type filter hook forward priority 0; policy drop;
(2)->                               COUNTER jump DOCKER-USER
        ->(3)(4)->                  COUNTER jump DOCKER-ISOLATION-STAGE-1
                    ->(5)           oifname "docker1" ct state related,established COUNTER accept
                      (6)->         oifname "docker1" COUNTER jump DOCKER
                                    iifname "docker1" oifname != "docker1" COUNTER accept
                                    iifname "docker1" oifname "docker1" COUNTER accept
                                  }
               ->(4)              chain DOCKER-ISOLATION-STAGE-1 {
                 (5)->              COUNTER return
                                  }
   ->(2)                          chain DOCKER-USER {
     (3)->                          COUNTER return
                                  }
                         ->(6)    chain DOCKER {
                            ↓       iifname != "docker1" oifname "docker1" meta l4proto udp ip daddr 172.20.0.3 udp dport 514 COUNTER accept
                            ↓       iifname != "docker1" oifname "docker1" meta l4proto udp ip daddr 172.20.0.5 udp dport 67 COUNTER accept
                            ↓       iifname != "docker1" oifname "docker1" meta l4proto tcp ip daddr 172.20.0.2 tcp dport 8080 COUNTER accept
                           (7)      iifname != "docker1" oifname "docker1" meta l4proto udp ip daddr 172.20.0.4 udp dport 1812 COUNTER accept
                                  }                          
                                }
                                table inet firewalld {
                                  chain filter_FORWARD {
                           (8)      type filter hook forward priority 10; policy accept;
                            ↓       ct state established,related accept
                           (9)      ct status dnat accept
                                    iifname "lo" accept
                                    jump filter_FORWARD_IN_ZONES
                                    jump filter_FORWARD_OUT_ZONES
                                    ct state invalid drop
                                    reject with icmpx type admin-prohibited
                                  }
                                  chain filter_FORWARD_IN_ZONES {
                                    iifname "ens192" goto filter_FWDI_public
                                    goto filter_FWDI_public
                                  }
                                  chain filter_FORWARD_OUT_ZONES {
                                    oifname "ens192" goto filter_FWDO_public
                                    goto filter_FWDO_public
                                  }
                                  chain filter_FWDI_public { meta l4proto { icmp, ipv6-icmp } accept }
                                  chain filter_FWDO_public { jump filter_FWDO_public_allow }
                                  chain filter_FWDO_public_allow { ct state new,untracked accept }
                                }

Die aktuelle Mitteilung lautet "10.254.10.105:random-> 172.20.0.4: 1812". (1) Da es die höchste Priorität unter den Vorwärts-Hooks hat, wird eine Kette namens FORWARD ausgewählt, die eine Filterung durchführt (pri: 0). (2) Fliegen Sie unbedingt zu DOCKER-USER (3) Kehre zurück, ohne etwas zu tun (4) Fliegen Sie bedingungslos zur DOCKER-ISOLATION-STAGE-1 (5) Kehre zurück, ohne etwas zu tun (6) Da die Ausgangs-IF Docker1 ist, springen Sie zu DOCKER (7) Die Eingangs-IF ist ens192, die Ausgangs-IF ist Docker1 und DstAddr ist 172.20.0.4:1812, also ** akzeptieren ** Die reguläre Kette DOCKER wird von der Basiskette FORWARD aufgerufen. Bei Annahme durch DOCKER wird der FORWARD des Anrufers ausgewertet und diese Kette endet. (8) Da es die zweithöchste Priorität unter den Vorwärts-Hooks hat, wird eine Kette namens filter_FORWARD ausgewählt, die eine Filterung durchführt (pri: 10). (9) Da das Paket DNAT ist, ** akzeptiere ** Die Kommunikation an dieser Stelle ist dieselbe wie die erste, "10.254.10.105: random-> 172.20.0.4: 1812".

Anfrage von einem externen Terminal (Postrouting)

Wenn der Hook vom nft list ruleset nachroutet, ist dies wie folgt.

                   table ip nat {
                     chain POSTROUTING {
(1)                    type nat hook postrouting priority 100; policy accept;
 ↓                     oifname "docker1" fib saddr type local COUNTER masquerade
 ↓                     oifname != "docker1" ip saddr 172.20.0.0/24 COUNTER masquerade
 ↓                     meta l4proto udp ip saddr 172.20.0.3 ip daddr 172.20.0.3 udp dport 514 COUNTER masquerade
 ↓                     meta l4proto udp ip saddr 172.20.0.5 ip daddr 172.20.0.5 udp dport 67 COUNTER masquerade
 ↓                     meta l4proto tcp ip saddr 172.20.0.2 ip daddr 172.20.0.2 tcp dport 8080 COUNTER masquerade
 ↓                     meta l4proto udp ip saddr 172.20.0.4 ip daddr 172.20.0.4 udp dport 1812 COUNTER masquerade
                     }
                     table ip firewalld {
                       chain nat_POSTROUTING {
(2)                    type nat hook postrouting priority 110; policy accept;
(3)->                    jump nat_POSTROUTING_ZONES
                       }
   ->(3)               chain nat_POSTROUTING_ZONES {
      ↓                  oifname "ens192" goto nat_POST_public
     (4)->               goto nat_POST_public
                       }
        ->(4)          chain nat_POST_public {
          (5)->          jump nat_POST_public_allow
                       }
             ->(5)     chain nat_POST_public_allow {
               (6)       oifname != "lo" masquerade
                       }
                     }
                   }

Die aktuelle Mitteilung lautet "10.254.10.105:random-> 172.20.0.4: 1812". (1) Da es die höchste Priorität unter den Postrouting-Hooks hat, wird eine Kette namens POSTROUTING ausgewählt, die nat ausführt (pri: 100). Richtlinie anwenden-> ** akzeptieren **, da keine weitere Verarbeitung erfolgt (2) Da es die zweithöchste Priorität unter den Postrouting-Hooks hat, wird eine Kette mit dem Namen nat_POSTROUTING ausgewählt, die nat ausführt (pri: 110). (3) Fliegen Sie unbedingt zu nat_POSTROUTING_ZONES (4) Fliegen Sie unbedingt nach nat_POST_public (5) Fliegen Sie unbedingt nach nat_POST_public_allow (6) Da die Ausgangs-IF Docker1 ist, ** Maskerade ** Da die Kette an dem von goto aufgerufenen Ziel endet, wird die Richtlinie angewendet-> ** accept ** Die reguläre Kette nat_POST_public_allow wird von der regulären Kette nat_POST_public aufgerufen. Die reguläre Kette nat_POST_public wird durch die Anweisung goto aus der regulären Kette nat_POSTROUTING_ZONES aufgerufen. Wenn die vom Befehl goto aufgerufene Verarbeitung von nat_POST_public abgeschlossen ist, endet der Aufruf von nat_POSTROUTING_ZONES. Das aufgerufene nat_POSTROUTING wird ebenfalls beendet und die Richtlinienakzeptanz wird angewendet.

Nach der Verarbeitung durch Maskerade lautet das Endergebnis "172.20.0.1: random-> 172.20.0.4: 1812". (Da es von Docker1 gesendet wird, lautet die Quelladresse Docker1, wenn es von Maskerade verarbeitet wird.)

Ob eine Authentifizierung nach Radius möglich ist

Vom Radiuscontainer empfangene Anfragen 172.20.0.1:random --> 172.20.0.4:1812

Der Radius-Server prüft die Verfügbarkeit und gibt eine Antwort an den Radius-Client zurück.

Antwort Der Radiuscontainer antwortet 172.20.0.4:1812 --> 172.20.0.1:random

Reaktion auf externe Terminals

Ich bin erschöpft. .. .. Wenn ich einen Zähler mit nftables einrichtete, konnte ich die Adresse sehen, wenn ich die folgende Kette durchlief. Da es sich um einen einmaligen Authentifizierungsaustausch handelte, war in jeder Kette ein Paket sichtbar.

type filter hook prerouting  : 172.20.0.4:1812 --> 172.20.0.1:random
type filter hook input       : 172.20.0.4:1812 --> 10.254.10.105:random
type filter hook forward     : 172.20.0.4:1812 --> 10.254.10.105:random
type filter hook postrouting : 172.20.0.4:1812 --> 10.254.10.105:random

Die Antwort vom Radiuscontainer lautet "172.20.0.4:1812-> 172.20.0.1: zufällig". Wenn Sie einen eingehenden Anruf erhalten, sieht es aus wie eine an Sie gerichtete Kommunikation, sodass Sie wissen, dass Sie "hook: input" durchlaufen. Fahren Sie danach mit LocalProcess fort? Ich bin mir darüber nicht sicher. .. ..

Es ist auf halbem Weg geworden. .. ..

Ich kenne die Route des Antwortpakets vom Radius nicht. Warum geht keine Kette type: nat vorbei? .. .. Warum durchlaufen Sie gleichzeitig "hook: input" und "hook: forward"? .. .. Obwohl es sich im Typ: Filter Hook: Input Pri: -200 des Tabellenbrückenfilters befindet Ich bin nicht auf den Typ: Filter Hook: Input Pri: 0 des Tabellen-IP-Filters eingegangen. Führen Sie eine unterschiedliche Verarbeitung zwischen der L2-Bridge und der L3-IP durch?

Quelle

https://knowledge.sakura.ad.jp/22636/ https://ja.wikipedia.org/wiki/Iptables https://ja.wikipedia.org/wiki/Nftables https://wiki.archlinux.jp/index.php/Nftables https://wiki.archlinux.jp/index.php/Iptables https://wiki.nftables.org/wiki-nftables/index.php/Netfilter_hooks https://www.frozentux.net/iptables-tutorial/iptables-tutorial.html#TRAVERSINGOFTABLES https://wiki.archlinux.jp/index.php/Nftables https://knowledge.sakura.ad.jp/22636/ https://www.codeflow.site/ja/article/a-deep-dive-into-iptables-and-netfilter-architecture

Recommended Posts

Verfolgen Sie den Kommunikationsfluss der Docker-Bridge-Verbindung mit nftables
Zusammenfassung des grundlegenden Ablaufs des maschinellen Lernens mit Python
Visualisieren Sie den Tweet-Fluss mit Diamond + Graphite + Grafana
Folgen Sie dem QAOA-Fluss (VQE) auf der Quellcode-Ebene von Blueqat
Folgen Sie der Dateihierarchie mit fts
Bearbeiten Sie die Datei des SSH-Verbindungszielservers auf dem Server mit VS-Code
Richten Sie die Größe der Farbleiste an der Matplotlib aus
Überprüfen Sie die Existenz der Datei mit Python
Die dritte Nacht der Runde mit für
Die zweite Nacht der Runde mit für
Zählen Sie die Anzahl der Zeichen mit Echo