[LINUX] Suivez le flux de communication de la connexion de pont de Docker avec nftables

introduction

Je viens de commencer à étudier Docker sur CentOS 8. Donc, je voulais suivre le flux des paquets dans le conteneur de l'hôte externe <--->, j'ai donc fait beaucoup de recherches.

Cependant, j'ai pu suivre le déroulement de la demande, mais je n'ai pas pu suivre la réponse. .. .. Pour le moment, je ne résumerai que l'itinéraire de la demande.

Divers prérequis

Environnement de vérification

Je trace dans l'environnement suivant.

Environnement d'exploitation Docker

userland proxy(docker-proxy) Je n'ai pas utilisé docker-pxory parce que je voulais voir le fonctionnement de base du réseau docker avec iptables / nftables. (Épingle à cheveux NAT) Les fichiers suivants sont placés et vérifiés.

/etc/docker/daemon.json


{
    "userland-proxy": false
}

Référence: https://github.com/nigelpoulton/docker/blob/master/docs/userguide/networking/default_network/binding.md

docker network and containers Comme environnement de vérification, utilisez celui généré par l'entrée suivante. L'hôte docker 10.254.10.252 configuré avec CentOS8, l'explication sera pour le rayon.

** Docker Compose peut créer des services réseau en 5 minutes (dhcp / radius / proxy / tftp / syslog) **

Screenshot from Gyazo

Les conteneurs suivants seront créés.

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

Un réseau avec les paramètres suivants est généré.

key value
name infraserv_infranet
subnet 172.20.0.0/24
interface docker1

Puisque tftp fonctionne dans l'environnement de --net = host, docker network est dans l'état suivant.

# 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"
        }
    }
]

Famille d'adresses

Par souci de concision, nous nous sommes concentrés sur IPv4.

À propos du flux de paquets dans Docker

Suivez la communication (dans le cas du rayon)

Cette fois, nous prendrons un exemple d'envoi d'une requête de rayon depuis un terminal externe (10.254.10.105) vers l'hôte Docker (10.254.10.252). Le hook de la chaîne d'intérêt est prerouting-> forward-> postrouting car il est retransmis après son arrivée à l'hôte local. Par conséquent, le type de chaîne sera expliqué en se concentrant uniquement sur le filtre et le nat.

Les règles excluent les règles inutiles de nft list ruleset, mais ce ne sont pas des informations très utiles, donc je les ai résumées dans [Supplement](#Check required tables).

Demande depuis un terminal externe (pré-routage)

Si le hook est pré-acheminé depuis nft list ruleset, ce sera comme suit.

         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
           }
         }

La communication actuelle est «10.254.10.105:random-> 10.254.10.252: 1812». (1) Une chaîne appelée PREROUTING qui accroche le pré-routage et exécute nat est sélectionnée. (2) Puisque DstAddr est local, passez à la chaîne appelée DOCKER addr type local est l'adresse de l'hôte local (hôte Docker dans ce cas). Dans ce cas, il s'agit de lo: 127.0.0.1 ʻens192: 10.254.10.252`` docker1: 172.20.0.1`. (3) Puisque DstPort est 1812, ** DNAT DstAddr à 172.20.0.4:1812 ** Appliquer la politique-> ** accepter ** car il n'y a pas de traitement supplémentaire

La communication à ce stade est «10.254.10.105: aléatoire-> 172.20.0.4: 1812». Étant donné que la destination a été modifiée en 172.20.0.4, la décision d'acheminement vous amènera au hook avant.

Demande d'un terminal externe (en avant)

Si le hook est en avant du nft list ruleset, ce sera comme suit.

                                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 }
                                }

La communication actuelle est «10.254.10.105:random-> 172.20.0.4: 1812». (1) Comme il a la priorité la plus élevée parmi les hooks avant, une chaîne appelée FORWARD qui effectue le filtrage est sélectionnée (pri: 0). (2) Vol sans condition à DOCKER-USER (3) Revenir sans rien faire (4) Voler sans condition à DOCKER-ISOLATION-STAGE-1 (5) Revenir sans rien faire (6) Puisque la sortie IF est docker1, passez à DOCKER (7) L'entrée IF est ens192, la sortie IF est docker1 et DstAddr est 172.20.0.4:1812, donc ** accepter ** La chaîne régulière DOCKER est appelée à partir de la chaîne de base FORWARD. Lorsqu'il est accepté par DOCKER, le FORWARD de l'appelant est évalué et cette chaîne se termine. (8) Puisqu'il a la deuxième priorité la plus élevée parmi les hooks avant, une chaîne appelée filter_FORWARD qui effectue le filtrage est sélectionnée (pri: 10). (9) Puisque le paquet est DNAT, ** accepter ** La communication à ce stade est la même que la première, «10.254.10.105: random-> 172.20.0.4: 1812».

Demande depuis un terminal externe (post-routage)

Si le hook est post-acheminé depuis nft list ruleset, ce sera comme suit.

                   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
                       }
                     }
                   }

La communication actuelle est «10.254.10.105:random-> 172.20.0.4: 1812». (1) Puisqu'il a la priorité la plus élevée parmi les hooks de post-routage, une chaîne appelée POSTROUTING qui exécute nat est sélectionnée (pri: 100). Appliquer la politique-> ** accepter ** car il n'y a pas de traitement supplémentaire (2) Puisqu'il a la deuxième priorité la plus élevée parmi les hooks de post-routage, une chaîne appelée nat_POSTROUTING qui exécute nat est sélectionnée (pri: 110). (3) Vol sans condition vers nat_POSTROUTING_ZONES (4) Voler sans condition vers nat_POST_public (5) Voler sans condition vers nat_POST_public_allow (6) Puisque le IF de sortie est docker1, ** masquerade ** Puisque la chaîne se termine à la destination appelée par goto, la politique est appliquée -> ** accept ** La chaîne régulière nat_POST_public_allow est appelée à partir de la chaîne régulière nat_POST_public. La chaîne régulière nat_POST_public est appelée par l'instruction goto de la chaîne régulière nat_POSTROUTING_ZONES. Lorsque le traitement de nat_POST_public appelé par la commande goto est terminé, l'appel nat_POSTROUTING_ZONES se termine. Le nat_POSTROUTING qui l'a appelé se termine également et la politique d'acceptation est appliquée.

Après avoir été traité par mascarade, le résultat final est 172.20.0.1: random-> 172.20.0.4: 1812. (Puisqu'elle est envoyée depuis docker1, l'adresse source sera docker1 lorsqu'elle sera traitée par masquerade)

Si l'authentification par rayon est possible

Demandes reçues par le conteneur de rayon 172.20.0.1:random --> 172.20.0.4:1812

Le serveur radius vérifie la disponibilité et renvoie une réponse au client radius.

réponse le conteneur de rayon répond 172.20.0.4:1812 --> 172.20.0.1:random

Réponse aux bornes externes

Je suis épuisé. .. .. Lorsque j'ai mis en place un compteur avec nftables, je pouvais voir l'adresse en passant par la chaîne suivante. Puisqu'il s'agissait d'un échange d'authentification unique, un paquet était visible dans chaque chaîne.

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

La réponse du conteneur de rayon est 172.20.0.4:1812-> 172.20.0.1: random, Lorsque vous recevez un appel entrant, cela ressemble à une communication qui vous est adressée, vous savez donc que vous passez par hook: input. Après cela, passez-vous à LocalProcess? Je ne suis pas sûr de cela. .. ..

Il est devenu à mi-chemin. .. ..

Je ne connais pas l'itinéraire du paquet de réponse depuis radius. Pourquoi aucune chaîne type: nat ne passe? .. .. Pourquoi passez-vous par «hook: input» et «hook: forward» en même temps? .. .. Même s'il est dans le type: filter hook: input pri: -200 du filtre de pont de table Je ne suis pas entré dans le type: filter hook: input pri: 0 de la table ip filter. Effectuez-vous un traitement différent entre le pont L2 et l'IP L3?

La source

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

Suivez le flux de communication de la connexion de pont de Docker avec nftables
Résumé du flux de base de l'apprentissage automatique avec Python
Visualisez le flux des tweets avec Diamond + Graphite + Grafana
Suivez le flux de QAOA (VQE) au niveau du code source de Blueqat
Suivez la hiérarchie des fichiers avec fts
Modifiez le fichier du serveur de destination de la connexion SSH sur le serveur avec VS Code
Alignez la taille de la barre de couleurs avec matplotlib
Vérifier l'existence du fichier avec python
La troisième nuit de la boucle avec pour
La deuxième nuit de la boucle avec pour
Compter le nombre de caractères avec écho