LabHub

네트워크 기초 — 리눅스 VM 에서 손으로 · 방화벽과 NAT · 이론

막고 바꾸기 — netfilter 훅·상태 추적·NAT

LabHub 에서 이어서 보기

한 줄 요약

nftables 규칙은 패킷이 지나는 다섯 훅(prerouting·input·forward·output·postrouting) 중 하나에 걸린다. 상태 추적(conntrack)이 "이미 열린 연결의 패킷" 을 알아보므로 응답은 한 줄로 허용하고, NAT 는 같은 conntrack 위에서 주소를 바꾸고 되돌린다.

왜 이게 필요했나

방화벽은 규칙 목록이 아니라 기본값과 순서다. 정책이 drop 이면 빠뜨린 것이 막히고 accept 면 빠뜨린 것이 열린다. 그리고 규칙은 방향이 있다 — 서버로 들어오는 SYN 을 열었다고 서버가 내보내는 응답이 자동으로 열리지 않는다. 옛날엔 그래서 규칙을 두 배로 썼고, 그것이 상태 추적이 생긴 이유다. ct state established,related accept 한 줄이 "이미 열린 연결은 통과" 를 뜻하므로, 처음 여는 SYN 만 고르면 된다.

사설 주소는 인터넷에서 라우팅되지 않는다. 그런데 집·회사·클라우드·쿠버네티스 파드가 전부 사설 주소다. 이들이 바깥과 이야기하려면 경계에서 주소를 바꾸고 응답이 오면 되돌려야 한다. 되돌리려면 "이 응답이 원래 누구 것인지" 를 기억해야 하고, 그 기억이 conntrack 이다. NAT 장비가 재시작되면 모든 연결이 끊기는 이유다.

어떻게 동작하나

훅은 위치다. 자기에게 온 패킷은 prerouting → input, 자기가 보내는 패킷은 output → postrouting, 남의 것을 넘길 때는 prerouting → forward → postrouting 을 지난다. 서버를 잠그는 규칙은 input, 라우터의 규칙은 forward 에 둔다. 라우터 자신의 input 을 잠그면 관리 접속까지 막힌다.

체인은 type filter hook input priority 0; policy drop 처럼 만든다. 규칙은 위에서부터 보고 첫 매치의 판정(accept/drop)으로 끝난다. counter 를 붙이면 몇 개가 걸렸는지 세고, log 를 붙이면 커널 로그에 남긴다(네임스페이스 안이면 nf_log_all_netns=1 이 필요하다).

마스커레이드는 postrouting 에 둔다 — 라우팅이 끝나 어느 인터페이스로 나가는지 아는 자리라서다. ip saddr 10.30.0.0/24 oifname "enp1s0" masquerade 는 그 대역이 uplink 로 나갈 때 출발지를 uplink 주소로 바꾼다. DNAT 는 prerouting 에 둔다 — 라우팅 에 목적지를 바꿔야 바뀐 목적지로 경로를 찾는다. 도커의 -p 8080:80 이 만드는 것이 이 규칙이다. NAT 체인은 연결의 첫 패킷만 타고, 나머지는 conntrack 이 처리한다.

현장에서 만나는 모습

자기 잠금. SSH 로 들어가 policy drop 을 켰다. 세션이 얼어붙는다. established 허용을 먼저 넣지 않아 자기 세션의 응답이 죽은 것이다. 순서가 곧 안전이다 — 허용을 먼저, 정책을 나중에.

NAT 는 되는데 안 나간다. 마스커레이드 규칙을 넣었는데 사설망이 인터넷에 못 나간다. forward 체인의 정책이 drop 이다. NAT 는 주소를 바꿀 뿐 통과를 허락하지 않는다 — 두 체인은 다른 일이다. ip_forward 가 0 인 경우도 같은 증상이다.

다음 실습에서 할 것

네임스페이스 안의 서버에 input 체인을 만들어 기본 drop 에서 시작해 lo·established·icmp·80·출처 제한 2222 를 하나씩 열고, counter 로 버려지는 것을 센다. 그다음 VM 을 라우터로 삼아 사설 네임스페이스를 마스커레이드로 인터넷에 내보내고, 양쪽 인터페이스에서 출발지가 바뀌는 것을 잡고, DNAT 로 포트를 안쪽 서버에 넘긴다.