기본 거부 방화벽, 관리 통로는 열어 두고
이 실습은 VM 에서 돕니다
우분투 24.04 VM 한 대입니다. root 이고 systemd 와 커널 기능이 그대로 있습니다. 실습 파드에는 capability 가 없어 이 실습이 다루는 설정을 바꿀 수 없기 때문에 VM 으로 돕니다. 처음 뜨는 데 몇 분이 걸립니다. 세션이 끝나면 VM 은 통째로 사라지므로 남기고 싶은 것은 화면에서 복사해 두세요.
목표
방화벽이 없는 서버에 nftables 로 기본 거부(default-deny) 입력 방화벽을 세웁니다. nginx(80)는 모두에게, 관리용 API(9000)는 사내 대역(10.50.0.0/24)에만 열고, 정상 서비스가 그대로 도는지와 막아야 할 접근이 막히는지 양쪽을 확인한 뒤 재부팅을 넘기게 저장합니다.
왜 중요한가
포트가 전부 열린 서버는 관리용 API, 디버그 포트, 임시로 띄운 서비스가 모두 인터넷에 노출됩니다. 기본 거부 방화벽은 "명시적으로 연 것만 닿는다" 로 이 그림을 뒤집습니다.
기본 거부에는 치명적인 함정이 하나 있습니다. 지금 당신이 그 서버에 연결된 통로도 들어오는 연결이라는 점입니다. 아무 생각 없이 policy drop 을 켜면 자기 연결이 끊겨 서버를 되살릴 수 없습니다. 그래서 순서가 중요합니다 — 루프백, 이미 맺은 연결(established), 관리 통로를 먼저 허용하고 나서 기본 거부를 켭니다. 이 실습에서 관리 통로는 채점 에이전트의 8899 포트입니다(실제 서버라면 SSH).
이 실습은 들어오는(input) 방향만 다룹니다. 나가는 방향까지 기본 거부로 잠그면 채점 연결의 응답이 막혀 VM 이 먹통이 됩니다.
단계
- 지금 듣는 포트를
/root/fw/01-open.txt에 저장합니다. - 무엇을 남길지
/root/fw/02-plan.txt에 적습니다(8899 포함). - 기본 거부 골격과 루프백·established·8899 를 올립니다.
- 80 과 9000(10.50.0.0/24)을 엽니다.
- 정상 접근이 되는지
/root/fw/05-ok.txt에 확인합니다. - 사내 밖에서 9000 이 막히는지
/root/fw/06-blocked.txt에 확인합니다. /etc/nftables.conf로 저장하고 서비스를 활성화합니다.
참고
nft list ruleset으로 전체 규칙을,ss -tlnp로 듣는 포트를 봅니다.- 사내 대역 흉내:
ip netns exec corp ...(출발지 10.50.0.2), 바깥 흉내:ip netns exec ext ...(출발지 198.51.100.9). VM 쪽 주소는 각각 10.50.0.1·198.51.100.1 입니다. - 흔한 실수: 루프백·established·8899 를 넣기 전에 policy drop 을 켜는 것. 연결이 끊깁니다.
- 흔한 실수: 규칙을 올리기만 하고 저장하지 않는 것. 재부팅에 사라집니다.
지금 무엇이 열려 있나
지금 듣고 있는 TCP 포트와 각 포트의 프로세스를 /root/fw/01-open.txt 에 저장하세요. 80·9000·8899(채점 에이전트)가 보여야 합니다.
ss -tlnp 는 듣는 소켓과 프로세스를 보여 줍니다. 방화벽을 세우기 전에 무엇이 열려 있는지 알아야 무엇을 남기고 무엇을 막을지 정할 수 있습니다.
무엇을 남길지 정한다
/root/fw/02-plan.txt 에 방화벽 계획을 적으세요. 네 줄로, 각 줄은 포트 또는 통로: 허용 범위 꼴입니다 — 루프백 전체, established 연결, 80 은 모두, 9000 은 10.50.0.0/24 와 8899 는 모두(채점 통로). 8899 를 빠뜨리면 안 되는 이유도 한 줄 적습니다.
방화벽을 세우기 전에 '무엇이 반드시 살아야 하는가' 를 먼저 적습니다. 관리 통로(여기서는 채점 에이전트 8899, 실제 서버라면 SSH)를 규칙에 넣지 않고 기본 거부를 켜면 자기가 앉은 가지를 자르는 셈입니다.
기본 거부 골격을 올린다
nftables 테이블 inet filter 와 input 체인(hook input, policy drop)을 만들고, 루프백 허용 · established/related 허용 · 8899 허용 세 규칙을 먼저 넣으세요. 아직 80·9000 은 열지 않습니다. 규칙을 올린 뒤에도 채점이 계속 되어야 합니다.
nft add table inet filter, nft add chain inet filter input '{ type filter hook input priority 0; policy drop; }' 로 시작합니다. established 는 ct state established,related accept 입니다. 이 세 줄을 먼저 넣어야 policy drop 을 켜도 지금 연결이 끊기지 않습니다.
서비스 포트를 연다
input 체인에 규칙을 더해 80 은 모든 주소에서, 9000 은 10.50.0.0/24 에서만 허용하세요.
nft add rule inet filter input tcp dport 80 accept 와, 출발지를 제한하려면 ip saddr {MGMT} tcp dport 9000 accept 입니다. 순서는 상관없습니다 — accept 규칙끼리는 겹쳐도 됩니다.
정상 서비스는 되는가
방화벽을 켠 상태에서 정상 접근이 여전히 되는지 확인해 /root/fw/05-ok.txt 에 적으세요. 루프백으로 80 과 9000 이 되고, 사내 네임스페이스(ip netns exec corp)에서 9000 이 되어야 합니다. 각 줄에 200 을 적습니다.
curl -s -o /dev/null -w '%{http_code}' 로 상태 코드만 받습니다. 사내 대역에서 오는 요청은 ip netns exec corp curl ... http://10.50.0.1:9000 입니다(VM 쪽 veth 주소 10.50.0.1).
막아야 할 것은 막히는가
사내 대역 밖(ip netns exec ext)에서 9000 이 막히는지 확인해 /root/fw/06-blocked.txt 에 적으세요. 막히면 blocked 를 적습니다. 바깥에서 80 은 여전히 되어야 하므로 그 줄도 함께 적습니다.
막힌 연결은 curl 이 시간 초과나 거부로 끝납니다. --max-time 4 를 주고 종료 코드로 판정하세요. 바깥 네임스페이스에서 VM 으로 오는 주소는 VM 쪽 veth(198.51.100.1)입니다.
재부팅을 넘긴다
지금 규칙을 /etc/nftables.conf 에 저장하고, nftables 서비스가 부팅 때 그것을 올리도록 활성화하세요. 저장된 파일만으로 규칙이 되살아나야 합니다.
nft list ruleset 이 지금 규칙 전체를 출력합니다. 그것을 파일로 저장하고 맨 위에 #!/usr/sbin/nft -f 와 flush ruleset 을 둡니다. systemctl enable --now nftables 가 부팅 때 그 파일을 읽습니다. 확인은 flush 한 뒤 서비스를 재시작해 규칙이 돌아오는지 보면 됩니다.