상당히 일반적인 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" accept
INPUT 체인에 추가하지 않으면 작동하지 않습니다.
이유를 이해하고 싶습니다.
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 체인에 들어가고 위의 규칙에 따라 차단됩니다.