라즈베리파이 ufw 방화벽 설정하며 배운 것들 (연재 2/3)
블로그 서버에 ufw를 켜는 단순한 작업이었는데, 알고 보니 SSH 접속이 끊길 수도 있고 쿠버네티스 파드 통신이 깨질 수도 있는 꽤 위험한 작업이었습니다. 왜 그런지, 어떻게 안전하게 처리했는지 정리해봤습니다.
학습 주제
라즈베리파이에서 운영 중인 블로그 서버에 UFW(Uncomplicated Firewall)를 활성화하는 작업을 진행하면서, 방화벽 규칙 설계와 리눅스 네트워크 체인(INPUT/FORWARD)에 대해 공부했습니다. 날짜는 2026년 8월 4일.
탐구 과정
처음엔 그냥 "ufw enable 한 줄이면 끝나겠지" 싶었습니다. 그런데 이 라즈베리파이 한 대에서 블로그 말고도 k3s(쿠버네티스), Docker 기반 모니터링 스택(Prometheus, Loki, Grafana 등), Samba 파일공유, PostgreSQL까지 같이 돌고 있다는 걸 다시 확인하게 됐습니다. 그냥 기본값으로 ufw를 켜버리면 지금 SSH로 접속하고 있는 경로 자체가 막힐 수도 있고, 완전히 무관해 보이는 다른 서비스들까지 한꺼번에 끊길 수 있다는 걸 알게 됐습니다.
먼저 지금 내가 어디서 접속 중인지부터 확인했습니다. VPN(WireGuard, 10.0.0.0/24)이나 LAN(172.30.1.0/24)이 아니라 그냥 집 밖 공인 IP에서 SSH로 붙어있는 상태였습니다. 이 상태에서 ufw 기본 정책(inbound deny)을 켜면 SSH 포트를 명시적으로 열어두지 않는 이상 접속이 그 순간 끊깁니다. 그래서 규칙을 설계할 때 "SSH/HTTP/HTTPS는 전체 허용, 나머지(DB, 파일공유, 모니터링)는 LAN·VPN에서만 허용"이라는 원칙을 먼저 세우고 시작했습니다.
여기서 두 번째 함정을 만났습니다. FORWARD 체인이라는 개념이었는데, ufw를 기본 설정 그대로 켜면 FORWARD 체인 정책이 DROP으로 잡혀 있어서 k3s의 flannel 오버레이 네트워크나 Docker 컨테이너 간 라우팅이 막힐 수 있다는 걸 알게 됐습니다. INPUT 체인(호스트로 직접 들어오는 트래픽)과 FORWARD 체인(컨테이너/파드 간에 라우팅되는 트래픽)이 다른 개념이라는 걸 이때 제대로 구분하게 됐습니다.
작업 중간에 k3s는 아예 제거하기로 방침을 바꿨는데, Docker 모니터링 스택은 계속 쓸 예정이라 FORWARD 정책 조정은 여전히 필요했습니다. /etc/default/ufw의 DEFAULT_FORWARD_POLICY를 DROP에서 ACCEPT로 바꿔서, ufw는 호스트로 들어오는 트래픽만 통제하고 컨테이너 라우팅은 건드리지 않게 설정했습니다.
마지막으로 ufw show added로 기존에 추가만 해두고 활성화는 안 했던 규칙들을 살펴봤는데, 예전에 쓰다 잊어버린 것 같은 흔적이 두 개 나왔습니다. 하나는 25565/tcp(마인크래프트 서버 포트)를 전체 공개로 열어둔 규칙, 다른 하나는 Samba를 소스 제한 없이 전체 공개로 허용한 규칙이었습니다. 25565는 실제로 그 포트에서 아무것도 듣고 있지 않았고 마인크래프트 프로세스도 없어서 죽은 규칙으로 판단해 삭제했고, Samba도 방금 정한 "LAN/VPN만 허용" 원칙에 맞지 않아서 함께 제거했습니다.
핵심 학습 내용
이번에 정리하면서 확실히 이해하게 된 개념들입니다.
INPUT vs FORWARD 체인
- INPUT: 호스트 자체로 들어오는 트래픽 (SSH, 웹서버, DB 접속 등)
- FORWARD: 호스트를 거쳐서 다른 곳(컨테이너, 파드)으로 라우팅되는 트래픽
ufw는 기본적으로 두 체인 모두 deny로 잡혀 있어서, 컨테이너 네트워킹을 쓰는 서버에서는 FORWARD 정책을 반드시 확인해야 한다는 걸 배웠습니다.
규칙 설계 원칙
이번에 정리한 최종 규칙 구조입니다.
# 전체 공개
22/tcp (SSH)
80/tcp (HTTP)
443/tcp (HTTPS)
# LAN + VPN에서만 전체 포트 허용
172.30.1.0/24 (LAN)
10.0.0.0/24 (WireGuard VPN)
이렇게 하면 PostgreSQL(5432), Samba(139/445), 모니터링 포트(9090/9100/3100/3001) 같은 것들은 인터넷에서 직접 접근이 안 되고, 신뢰된 네트워크 안에서만 열립니다.
안전한 활성화 순서
되돌리기 어려운 명령(ufw enable)은 신중하게 접근해야 했습니다. 실제로 sudo ufw enable을 치면 "Command may disrupt existing ssh connections. Proceed?"라는 확인 프롬프트가 뜨는데, 자동화된 환경에서는 이 인터랙티브 프롬프트에 응답을 못 해서 그냥 취소돼 버렸습니다. --force 옵션을 붙여야 확인 절차를 건너뛰고 바로 적용됩니다.
sudo ufw --force enable
활성화 직후에는 반드시 같은 SSH 세션에서 아무 명령이나 실행해서 접속이 살아있는지 바로 확인하는 게 중요하다는 것도 체감했습니다. 만약 끊기면 물리적으로 기기에 접근하거나 다른 경로로 들어와서 ufw disable로 되돌려야 하니까요.
이해한 내용
가장 크게 깨달은 건, 방화벽 작업은 "무엇을 막을까"보다 "지금 내가 무엇에 의존하고 있는가"를 먼저 파악하는 게 우선이라는 점입니다. SSH 접속 경로, 컨테이너 네트워킹, 이미 돌고 있는 서비스 목록을 먼저 확인하지 않고 규칙부터 짰다면 스스로 접속을 끊거나 운영 중인 서비스를 멈출 뻔했습니다.
또 하나는 오래된 설정이 생각보다 쉽게 방치된다는 점입니다. 마인크래프트 포트나 전체공개 Samba 규칙처럼, 예전에 어떤 이유로 추가해두고 활성화는 안 한 채 잊어버린 흔적이 남아있었는데, 이런 건 실제로 방화벽을 켜는 시점에 한 번씩 전수 점검하지 않으면 계속 묻혀 있게 됩니다.
실전 적용
이번 작업 이후로 문서 관리 방식도 바꿨습니다. 프로젝트 가이드 문서(CLAUDE.MD)에는 협업 원칙만 남기고, 실제로 겪은 트러블슈팅이나 보안 대응 기록은 별도의 LEARNINGS.md 파일로 분리했습니다. 발견 배경 → 원인 분석 → 해결 → 검증 → 교훈 순서로 포스트모템 형식을 통일해서 쌓아두니, 나중에 비슷한 문제가 생겼을 때 다시 찾아보기 좋겠다는 생각이 들었습니다.
앞으로 서버에 새 서비스를 추가할 때마다 "이 포트가 어느 네트워크 대역에서 접근 가능해야 하는가"를 먼저 정하고 ufw 규칙에 반영하는 걸 기본 절차로 삼으려 합니다.
추가 학습 계획
- iptables/nftables 레벨에서 ufw가 실제로 어떤 규칙을 생성하는지 더 자세히 뜯어보고 싶습니다.
- fail2ban과 ufw를 같이 쓸 때 상호작용 방식(브루트포스 차단이 ufw 규칙에 어떻게 반영되는지)을 더 공부할 계획입니다.
- k3s를 걷어내기로 한 만큼, Docker 단독 환경에서의 네트워크 격리(예: 컨테이너별 네트워크 분리)도 다음 주제로 살펴보려 합니다.