🐳

[4/5] macvlan을 버리고 L3 Docker 브리지를 택한 이유 — foxirain4·gdbserver

Description
Windows 분석 VM과 Docker 디버깅 환경을 직접 L2로 붙이지 않고 Linux FORWARD를 통과하는 L3 bridge로 재설계한 과정
URL
기간
Oct 2, 2025 → Oct 16, 2025
분야
보안
상태
초안
시리즈
Security Foundations
요약
macvlan을 철회하고 foxirain4 10.20.0.0/24, Windows 정적 route, TCP 31234 gdbserver 경로를 구성한 이유와 실제 컨테이너 설정을 설명한다.
원작성일
Aug 26, 2026
태그
Docker
Linux
Pentesting
🐳
Windows: 10.10.0.20 GDB client
Docker: 10.20.0.10 gdbserver TCP 31234
경로: enx00e04c637a20 → Linux FORWARD → foxirain4

Windows–Docker 디버깅 요구사항

Windows 분석 VM에서는 Windows 도구를 실행하고, Docker에서는 CTF·커널·Linux 바이너리를 실행한다. Windows의 GDB client가 Docker의 gdbserver TCP 31234로 접속해야 한다.
GDB 연결 경로는 다음과 같다.
Windows VM 10.10.0.20 └─ GDB client └─ Linux nftables router └─ foxirain4 10.20.0.1/24 └─ my_container 10.20.0.10 └─ gdbserver TCP 31234
gdbserver는 Docker 안에서 Linux 실행 파일을 시작하고 원격 GDB 프로토콜을 제공한다. 네트워크 격리는 Linux의 route와 nftables가 담당한다.

macvlan 패킷 경로

Docker 컨테이너를 Windows와 연결된 물리 NIC에 macvlan으로 붙이면 같은 L2 segment에서 직접 통신할 수 있다. 컨테이너가 물리 네트워크의 독립 장비처럼 보이기 때문에 route 설정도 단순해진다.
이 방식에는 다음 문제가 있었다.
  • 컨테이너와 Windows가 L2에서 직접 통신하면 패킷이 Linux의 L3 FORWARD 정책을 통과하지 않는다.
  • macvlan용 별도 L2 필터 정책을 추가하면 정책 위치가 분산된다.
  • Windows–Docker 트래픽이 Linux FORWARD hook을 통과해야 했다.
Docker 공식 문서도 bridge network에는 방화벽 규칙을 만들지만 macvlan·ipvlan·host networking에는 같은 방식의 규칙을 만들지 않는다고 설명한다. Docker packet filtering and firewalls
당시 테스트 기록에서도 macvlan 트래픽이 Linux의 L3 FORWARD 정책을 통과하지 않는 문제를 확인했다.

L3 bridge 패킷 경로

Docker bridge는 호스트 내부에 private network를 만들고, 컨테이너의 veth를 Linux bridge에 연결한다. 외부 네트워크와의 통신은 호스트의 routing과 firewall을 거친다. Docker networking drivers
Windows 10.10.0.20에서 Docker 10.20.0.0/24로 가는 패킷은 Linux의 FORWARD hook을 지난다. nftables 규칙에서 ingress, egress, source IP, destination IP, port, connection state를 검사한다.

foxirain4 network 설정

docker network inspect foxirain4_net의 설정값은 다음과 같다.
{ "Name": "foxirain4_net", "Created": "2025-10-02T21:20:24+09:00", "Driver": "bridge", "EnableIPv4": true, "EnableIPv6": false, "IPAM": { "Config": [ { "Subnet": "10.20.0.0/24", "Gateway": "10.20.0.1" } ] }, "Internal": false, "Options": { "com.docker.network.bridge.name": "foxirain4" } }
같은 네트워크를 만드는 명령은 다음과 같다.
docker network create --driver bridge --subnet 10.20.0.0/24 --gateway 10.20.0.1 --opt com.docker.network.bridge.name=foxirain4 foxirain4_net
기본 br-<ID> 이름 대신 Linux interface 이름을 foxirain4로 고정했다. nftables에서 동적으로 바뀌는 bridge ID를 추적하지 않고 iifname "foxirain4"oifname "foxirain4"를 안정적으로 사용할 수 있기 때문이다.
Internal: false이므로 이 네트워크는 Docker 자체의 internal-only network가 아니다. nftables에도 Docker bridge → WAN mark가 있기 때문에 컨테이너의 인터넷 egress가 허용된 별도 디버깅망이다.

Docker interface 목록

에서 Linux에 생긴 인터페이스를 세 종류로 정리했다.
인터페이스
역할
네트워크 설정
docker0
Docker 기본 bridge
기본 컨테이너 네트워크
br-042225748a2d
다른 사용자 정의 bridge
ID 기반 이름이라 재생성 시 변경 가능
foxirain4
이름을 고정한 사용자 정의 bridge
10.20.0.0/24와 nftables 정책 연결
veth*
컨테이너 eth0와 bridge를 잇는 가상 Ethernet pair
각 컨테이너의 L2 연결
[container eth0] │ [veth pair] │ [host veth] │ [foxirain4 bridge 10.20.0.1] │ [Linux routing + nftables] │ [10.10.0.0/24 or WAN]

Windows static route

Windows는 10.20.0.0/24가 자신의 직접 연결 subnet이 아니라는 것을 알아야 한다. 다음 hop은 Linux의 분석망 주소 10.10.0.10이다.
route -p add 10.20.0.0 mask 255.255.255.0 10.10.0.10
Microsoft 문서에서 route /p add는 route를 registry에 저장해 TCP/IP가 다시 시작된 뒤에도 유지하는 옵션이다. Microsoft route command
확인은 다음과 같이 한다.
route print 10.* tracert 10.20.0.10 Test-NetConnection 10.20.0.10 -Port 31234
당시 Windows 명령 기록은 Linux에 남아 있지 않지만, Linux의 FORWARD 규칙과 실제 SSH 시험 기록은 이 L3 경로를 사용하도록 구성되어 있다.

Windows → Docker gdbserver 허용 규칙

iifname $IF_INTERNAL oifname $IF_foxirain4 ip saddr 10.10.0.20 tcp dport 31234 ct state new counter accept comment "foxirain5 -> foxirain4 gdbserver"
규칙의 조건은 다음과 같다.
  • ingress는 Windows와 연결된 전용 NIC다.
  • source는 Windows VM 10.10.0.20이다.
  • egress는 foxirain4다.
  • 신규 TCP 연결의 destination port는 31234다.
현재 규칙에는 ip daddr 10.20.0.10이 없다. foxirain4에 연결된 컨테이너가 TCP 31234를 listen하면 접속 대상이 될 수 있다. 목적지 IP를 10.20.0.10으로 제한하는 규칙은 다음과 같다.
ip saddr 10.10.0.20 ip daddr 10.20.0.10 tcp dport 31234 ct state new accept

my_container 실행 설정

항목
Image
amemoyoi-ctf
User
pwn
Network
foxirain4_net
IP
10.20.0.10
Published ports
127.0.0.1:18080→18080, 127.0.0.1:31234→31234
Security
privileged, AppArmor unconfined, label disable
자동 명령
socat TCP 18080 → ./prob
inspect 결과를 실행 명령으로 표현하면 다음과 같다.
docker run -it --name my_container --privileged --security-opt label=disable --network foxirain4_net --ip 10.20.0.10 -p 127.0.0.1:18080:18080 -p 127.0.0.1:31234:31234 amemoyoi-ctf /bin/sh -c 'socat TCP-LISTEN:18080,reuseaddr,fork EXEC:./prob,stderr'
컨테이너의 자동 시작 명령은 18080의 ./prob 서비스다. 31234는 publish와 nftables에 gdbserver용으로 준비되어 있지만 Config에 gdbserver 자동 실행 명령은 없다. 컨테이너에 들어가 분석할 바이너리를 지정해 gdbserver를 별도로 실행하는 형태였다.
예시는 다음과 같은 형태다.
gdbserver 0.0.0.0:31234 ./target_binary
Windows의 GDB client는 10.20.0.10:31234에 연결한다.
target remote 10.20.0.10:31234
호스트 publish 주소가 127.0.0.1인 것은 호스트의 31234를 외부 인터페이스에 공개하지 않기 위한 설정이다. Windows는 host port binding이 아니라 10.20.0.10으로 직접 routing하는 별도 경로를 사용한다.

my_kernel_container 실행 설정

docker run -it --name my_kernel_container --privileged --security-opt label=disable --network foxirain4_net --ip 10.20.0.30 -p 127.0.0.1:8080:8080 -p 127.0.0.1:41234:41234 -v /home/amemoyoi/kernel/kernel-connection/deploy:/home/pwn/ amemoyoi-kernel-ctf /bin/sh -c 'socat TCP-LISTEN:8080,reuseaddr,fork EXEC:./run.sh,stderr'
이 컨테이너는 커널 실습 디렉터리를 bind mount하고, 8080을 문제 실행 포트, 41234를 별도 디버깅 포트로 준비했다. 둘 다 호스트에서는 loopback에만 publish했다.

Docker → Windows 허용 규칙

DOCKER-USER에는 다음 mark 규칙도 있다.
iifname $IF_foxirain4 oifname $IF_INTERNAL meta mark set (meta mark & $MARK_KEEP_MASK) | $MARK_ALLOW
FWD-DOCKER는 이 mark가 설정된 패킷을 accept한다. foxirain4에서 Windows 분석망으로 시작하는 신규 연결에는 목적지 IP와 포트 조건이 없다. Windows → Docker는 TCP 31234로 제한되지만 Docker → Windows는 제한 범위가 더 넓다.
Docker → Windows 규칙에 source subnet, destination 10.10.0.20, destination port 조건을 추가하면 허용 범위를 줄일 수 있다.

패킷 경로 검증

docker network inspect foxirain4_net docker inspect my_container docker inspect my_kernel_container ip -br link show foxirain4 ip route show 10.20.0.0/24 sudo nft list chain inet filter FWD-DOCKER sudo nft list chain ip filter DOCKER-USER sudo tcpdump -ni enx00e04c637a20 tcp port 31234 sudo tcpdump -ni foxirain4 tcp port 31234 ss -lntp | grep 31234
정상 연결이면 전용 NIC와 foxirain4 양쪽에서 같은 TCP 세션이 보이고, FWD-DOCKER의 31234 counter가 증가해야 한다.

정리

Docker 컨테이너는 foxirain4 bridge의 10.20.0.0/24를 사용한다. Windows에는 10.20.0.0/24 via 10.10.0.10 persistent route를 추가한다. Windows → Docker 트래픽은 Linux FORWARD hook에서 처리된다.
Windows 10.10.0.20에서 foxirain4의 TCP 31234로 시작하는 신규 연결을 허용한다. 현재 규칙은 목적지 IP가 없고 Docker → Windows 방향에도 목적지·포트 제한이 없다.

Sources

작업 기록

공식 문서


시리즈 이동

전체 시리즈