docker 포트 전달 및 iptables/nftables에 대해 알아보기

docker 포트 전달 및 iptables/nftables에 대해 알아보기

상당히 일반적인 nftables/iptables 방화벽 설정을 고려하면(OUTPUT 승인, INPUT/FORWARD 승인 설정 + 종속, 기본 삭제):

table ip nat {
    chain DOCKER {
        iifname "docker0" return
        iifname != "docker0" meta l4proto tcp ip daddr 172.17.0.1 tcp dport 5000 dnat to 172.17.0.2:5000
        iifname != "docker0" meta l4proto tcp ip daddr 127.0.0.1 tcp dport 5000 dnat to 172.17.0.2:5000
    }

    chain POSTROUTING {
        type nat hook postrouting priority srcnat; policy accept;
        oifname != "docker0" ip saddr 172.17.0.0/16 masquerade
        meta l4proto tcp ip saddr 172.17.0.2 ip daddr 172.17.0.2 tcp dport 5000 masquerade
    }

    chain PREROUTING {
        type nat hook prerouting priority dstnat; policy accept;
        fib daddr type local jump DOCKER
    }

    chain OUTPUT {
        type nat hook output priority -100; policy accept;
        ip daddr != 127.0.0.0/8 fib daddr type local jump DOCKER
    }
}
table ip filter {
    chain INPUT {
        type filter hook input priority filter; policy drop;
        iif "lo" accept comment "Allow loopback"
        ct state invalid drop
        ct state established,related accept
        iifname "bond0-data" tcp dport 22 accept
        udp dport 3052 accept
        iifname "bond0-data" tcp dport 9090 accept
        log drop
    }

    chain FORWARD {
        type filter hook forward priority filter; policy drop;
        jump DOCKER-USER
        jump DOCKER-ISOLATION-STAGE-1
        oifname "docker0" ct state related,established accept
        oifname "docker0" jump DOCKER
        iifname "docker0" oifname != "docker0" accept
        iifname "docker0" oifname "docker0" accept
        log drop
    }

    chain OUTPUT {
        type filter hook output priority filter; policy accept;
    }

    chain FORWARD-OVERRIDE {
        type filter hook forward priority filter + 10; policy accept;
        ct state invalid drop
        ct state established,related accept comment "Accept established, related"
        iifname "bond1-control" drop comment "Drop new connections from bond1-control"
    }

    chain DOCKER {
        iifname != "docker0" oifname "docker0" meta l4proto tcp ip daddr 172.17.0.2 tcp dport 5000 accept
    }

    chain DOCKER-ISOLATION-STAGE-1 {
        iifname "docker0" oifname != "docker0" jump DOCKER-ISOLATION-STAGE-2
        return
    }

    chain DOCKER-ISOLATION-STAGE-2 {
        oifname "docker0" drop
        return
    }

    chain DOCKER-USER {
        return
    }
}

컨테이너 172.17.0.2:5000은 docker의 포트 전달을 사용하여 포트 5000에 매핑됩니다.

컨테이너 172.17.0.3에서 172.17.0.2:5000으로 직접 연결하려고 하면 작동합니다. 호스트 IP(172.17.0.1:5000)를 통해 컨테이너 172.17.0.3에서 연결을 시도하는 경우 iffname "docker0" acceptINPUT 체인에 추가하지 않으면 작동하지 않습니다.

이유를 이해하고 싶습니다.

kern.log는 연결을 시도할 때 다음을 표시합니다.

IN=docker0 OUT= PHYSIN=vethbdc197b MAC=xxxxxxx SRC=172.17.0.3 DST=172.17.0.1 LEN=44 TOS=0x00 PREC=0x00 TTL=58 ID=37344 PROTO=TCP SPT=52535 DPT=5000 WINDOW=1024 RES=0x00 SYN URGP=0

직접 연결은 허용되는데 왜 이 연결은 차단되나요? 현재로서는 출력이 어떻게든 직접 연결을 허용하고 입력이 이를 확립되고 관련 있는 것으로 취급한다는 것이 최선의 추측입니다. 그러나 Docker 포트 매핑을 수행할 때 상황이 어떻게 다른지는 명확하지 않습니다. DNAT로 인해 별도의 INPUT 연결이 발생하여 차단될 수 있습니까?

iptables/nftables 관점에서 이것이 어떻게 작동하는지 알려줄 수 있는 사람이 있나요?

답변1

이 질문에 대한 답은 매우 간단하다는 것이 밝혀졌습니다. 내 생각에 컨테이너는 호스트 네트워크 스택에 의해 "추가" IP로 표시되는 것 같습니다. 이는 사실이 아닙니다. 컨테이너에는 호스트와 별도로 자체 네트워크 스택이 있습니다(기술적으로 컨테이너에는 자체 iptables/nftables 규칙도 있을 수 있음).

이는 관련된 IP가 호스트 네트워크 스택의 로컬 IP가 아니기 때문에 컨테이너 간 직접 연결이 호스트의 OUTPUT 또는 INPUT 체인을 통과하지 않음을 의미합니다. 방향 연결은 다음과 같습니다.

  • 컨테이너 A 출력(비어 있을 수 있음)
  • 호스트 전달
  • 컨테이너 B 입력(비어 있을 수 있음)

이것이 호스트에서 FORWARD를 통해 작동한다는 사실은 약간 놀랍습니다. 제가 테스트해 본 결과 실제로 그렇습니다. 이는 브리징이 작동하는 방식 때문인 것 같습니다.

이 경우 FORWARD 체인에 가 있으므로 호스트는 연결을 허용합니다 iifname "docker0" oifname "docker0" accept.

반면, 컨테이너 A가 호스트의 전달된 포트에 연결을 시도하면 호스트의 INPUT 체인에 들어가고 위의 규칙에 따라 차단됩니다.

관련 정보