보안 점검에서 "일단 포트를 다 열어두자"는 임시 조치가 그대로 운영에 남아 있었습니다. 장애 때는 빠른 해결처럼 보였지만, 나중에는 접근 경로를 설명할 수 없는 위험이 됩니다.
방화벽 정책은 명령어보다 기준이 먼저입니다. 무엇을 누구에게 왜 열었는지 ACL로 표현할 수 있어야 합니다.
방화벽 정책 수립과 ACL
네트워크 보안의 첫 번째 방어선인 방화벽은 단순한 포트 차단을 넘어, 세션 상태 추적과 세밀한 접근 제어 목록(ACL)을 통해 서버를 보호한다. 이 챕터에서는 Stateful Inspection 원리부터 firewalld를 이용한 실전 정책 수립까지 단계적으로 학습한다.
신규 서비스 배포 전 보안팀에서 "방화벽 정책 신청서"를 요구하는 경우가 많다. 개발자는 어떤 포트를, 어떤 출발지 IP 범위에서, 어떤 프로토콜로 허용해야 하는지 명확히 알아야 한다. 잘못된 정책은 서비스 장애 또는 보안 사고로 이어진다. 클라우드 환경에서는 Security Group이 동일한 역할을 하므로, 방화벽 개념을 이해하면 AWS/GCP/Azure 보안 설정도 자연스럽게 이해된다.
방화벽의 두 가지 방향: Inbound와 Outbound
모든 방화벽 규칙은 트래픽의 방향을 기준으로 분류된다.
- 1Inbound/Outbound 트래픽 방향과 기본 차단 정책
- 2Stateful Inspection과 conntrack 세션 상태 추적
- 3firewalld Zone 기반 신뢰 수준 분리
- 4Rich Rule을 이용한 출발지 IP·포트·프로토콜 ACL
- 5최소 권한 원칙(Default Deny) 방화벽 정책 설계
- 6방화벽 규칙 변경 시 기존 세션 끊김 부작용 관리
sudo dnf install -y firewalld && sudo systemctl enable --now firewalldsudo firewall-cmd --get-active-zones && sudo firewall-cmd --list-allsudo dnf install -y conntrack-toolssudo firewall-cmd --runtime-to-permanent 으로 변경 전 현재 상태를 저장해 두면 롤백이 용이합니다
Inbound와 Outbound 방향 이해
보안 감사 결과 서버 방화벽이 "모두 허용" 상태였습니다. 포트를 하나씩 막으며 최소 허용 정책으로 바꾸려는데 서비스가 하나씩 죽기 시작합니다. 어떤 방향의 어떤 포트가 왜 필요한지 정리가 안 된 채로 규칙을 수정하면 이런 혼란이 반복됩니다. 인바운드와 아웃바운드를 구분하고 방향별 필요 포트를 이해하는 것이 방화벽 정책 설계의 출발점입니다.

방화벽 규칙을 설정할 때 가장 먼저 정해야 하는 것은 "어느 방향의 트래픽을 제어하는가"입니다. 서버는 외부 요청을 받기도 하고(Inbound), 외부로 직접 통신을 시작하기도 합니다(Outbound). 이 두 방향에 다른 기본 정책이 적용됩니다.
Inbound(인바운드): 외부에서 서버로 들어오는 트래픽
- 예시: 클라이언트가 웹 서버 80포트에 접속하는 요청
- 기본 정책: 대부분의 서버에서 기본 차단(Default Deny) 후 필요한 포트만 허용
Outbound(아웃바운드): 서버에서 외부로 나가는 트래픽
- 예시: 서버가 패키지 업데이트를 위해 외부 리포지토리에 접속
- 기본 정책: 보안 수준에 따라 허용하거나, 폐쇄망에서는 차단
방향성이 중요한 이유:
- 웹 서버는 인바운드 80/443만 허용해도 동작하지만, DB 서버는 인바운드를 애플리케이션 서버 IP로만 제한해야 한다
- 아웃바운드 제한은 악성코드의 C2(Command & Control) 서버 통신을 차단하는 데 효과적이다
트래픽 방향을 도식으로 보면 규칙이 어느 지점에서 검사되는지 명확해집니다.
인터넷 ──→ [Inbound 규칙 검사] ──→ 서버
인터넷 ←── [Outbound 규칙 검사] ←── 서버
Stateful Inspection과 conntrack
현대 방화벽의 핵심은 단순한 패킷 필터링이 아닌 세션 상태 추적이다.
Stateful Inspection과 conntrack 테이블
방화벽 규칙에 인바운드 TCP 80을 허용했는데 HTTP 응답이 안 돌아옵니다. 요청은 들어오는데 응답 패킷이 차단된 겁니다. Stateless 방화벽은 패킷 하나하나를 독립적으로 판단하기 때문에, 내가 먼저 요청한 응답이라도 별도 허용 규칙이 없으면 막습니다. Stateful Inspection이 어떻게 세션 상태를 추적해서 이 문제를 해결하는지 알아야, 왜 현대 방화벽이 응답 트래픽을 따로 허용하지 않아도 동작하는지 납득이 됩니다.
Stateless 패킷 필터링의 문제점:
- 각 패킷을 독립적으로 검사하므로 응답 패킷도 별도로 허용해야 한다
- HTTP 요청에 대한 응답(TCP ACK, 데이터 패킷)을 모두 허용 규칙으로 나열해야 한다

Stateful Inspection:
Linux 커널의 conntrack(Connection Tracking) 모듈이 세션 테이블을 관리한다.
클라이언트 → 서버:80 (SYN) → conntrack: NEW 상태로 기록
서버 → 클라이언트 (SYN-ACK) → conntrack: ESTABLISHED 상태
클라이언트 → 서버 (ACK, 데이터) → conntrack: ESTABLISHED (자동 허용)
conntrack 상태값 — conntrack이 추적하는 연결 상태 유형과 의미입니다:
| 상태 | 설명 |
|---|---|
| NEW | 새 연결 시도 (첫 번째 패킷) |
| ESTABLISHED | 양방향 통신이 확인된 연결 |
| RELATED | FTP 데이터 채널처럼 기존 연결에서 파생된 연결 |
| INVALID | 어떤 상태에도 속하지 않는 비정상 패킷 |
실습: conntrack 테이블 확인 — 현재 커널이 추적 중인 연결 목록을 조회합니다:
# 실습 디렉토리 준비
mkdir -p /tmp/networking/part4/exam_21 && cd /tmp/networking/part4/exam_21
# conntrack 테이블 조회
conntrack -L
# 특정 포트 필터링
conntrack -L | grep dport=80
# 실시간 이벤트 모니터링
conntrack -E
- 핵심 출력—명령 결과에서 성공/실패를 가르는 값을 먼저 확인합니다
- 대상 식별—IP, 포트, 인터페이스, 프로세스명처럼 다음 조치를 결정하는 필드를 봅니다
- 다음 분기—결과가 기대와 다르면 어느 계층을 이어서 점검할지 정합니다
장점: 인바운드 허용 규칙만 작성해도, 해당 세션의 응답 패킷은 자동으로 허용된다.
ACL: 출발지/목적지/포트/프로토콜 기반 제어
ACL(Access Control List)은 네트워크 접근을 세밀하게 제어하는 규칙 목록이다.
mkdir -p /tmp/networking/part4/firewall && cd /tmp/networking/part4/firewall
cat > policy.txt << 'EOF'
# 방화벽 정책 정의서
# 형식: 방향 소스 목적지 포트 프로토콜 액션
# 인바운드 허용
INBOUND ANY THIS_HOST 22 TCP ALLOW # SSH
INBOUND ANY THIS_HOST 80 TCP ALLOW # HTTP
INBOUND ANY THIS_HOST 443 TCP ALLOW # HTTPS
INBOUND 10.0.0.0/8 THIS_HOST ANY ANY ALLOW # 내부망
# 아웃바운드 허용
OUTBOUND THIS_HOST ANY 80 TCP ALLOW # HTTP
OUTBOUND THIS_HOST ANY 443 TCP ALLOW # HTTPS
OUTBOUND THIS_HOST ANY 53 UDP ALLOW # DNS
# 기본 거부
DEFAULT DROP
EOF
이 명령은 방화벽 정책을 변경해 현재 접속 중인 세션이나 운영 트래픽에 즉시 영향을 줄 수 있습니다. 적용 전 허용 대상 IP·포트와 롤백 명령을 확인하세요.
# firewalld 설치 (CentOS/RHEL/Rocky)
sudo dnf install -y firewalld
# firewalld 시작 및 자동 시작 설정
sudo systemctl start firewalld
sudo systemctl enable firewalld
# 현재 방화벽 상태 전체 확인
sudo firewall-cmd --state
sudo firewall-cmd --list-all
# 활성화된 Zone 확인
sudo firewall-cmd --get-active-zones
# 모든 Zone 목록 확인
sudo firewall-cmd --get-zones
예상 출력:
running
public (active)
target: default
icmp-block-inversion: no
interfaces: eth0
sources:
services: cockpit dhcpv6-client ssh
ports:
protocols:
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:
firewalld는 사전 정의된 서비스 이름으로 포트를 쉽게 관리할 수 있다.
이 명령은 방화벽 정책을 변경해 현재 접속 중인 세션이나 운영 트래픽에 즉시 영향을 줄 수 있습니다. 적용 전 허용 대상 IP·포트와 롤백 명령을 확인하세요.
# HTTP(80) 허용
sudo firewall-cmd --zone=public --add-service=http --permanent
# HTTPS(443) 허용
sudo firewall-cmd --zone=public --add-service=https --permanent
# SSH(22) 허용 확인 (기본 포함됨)
sudo firewall-cmd --zone=public --list-services
# 사용자 정의 포트 허용 (예: 8080/TCP)
sudo firewall-cmd --zone=public --add-port=8080/tcp --permanent
# 포트 범위 허용 (예: 8000-8100/TCP)
sudo firewall-cmd --zone=public --add-port=8000-8100/tcp --permanent
# 설정 적용 (--permanent 옵션 후 반드시 필요)
sudo firewall-cmd --reload
# 적용 확인
sudo firewall-cmd --zone=public --list-all
주의: --permanent 없이 추가하면 재부팅 시 사라진다. --permanent로 추가하면 --reload가 필요하다.
Rich Rule을 사용하면 출발지 IP, 포트, 프로토콜을 조합한 정밀한 규칙을 만들 수 있다.
이 명령은 방화벽 정책을 변경해 현재 접속 중인 세션이나 운영 트래픽에 즉시 영향을 줄 수 있습니다. 적용 전 허용 대상 IP·포트와 롤백 명령을 확인하세요.
# 특정 IP에서 SSH만 허용 (관리자 IP만 SSH 접속 허용)
sudo firewall-cmd --zone=public --add-rich-rule='
rule family="ipv4"
source address="203.0.113.10/32"
service name="ssh"
accept' --permanent
# 내부 네트워크(192.168.1.0/24)에서 MySQL(3306) 허용
sudo firewall-cmd --zone=public --add-rich-rule='
rule family="ipv4"
source address="192.168.1.0/24"
port port="3306" protocol="tcp"
accept' --permanent
# 특정 IP 차단 (블랙리스트)
sudo firewall-cmd --zone=public --add-rich-rule='
rule family="ipv4"
source address="10.0.0.5/32"
reject' --permanent
# 연결 시도를 로그로 기록하면서 차단
sudo firewall-cmd --zone=public --add-rich-rule='
rule family="ipv4"
source address="10.0.0.0/8"
port port="3306" protocol="tcp"
log prefix="DB_ACCESS_ATTEMPT " level="warning"
reject' --permanent
sudo firewall-cmd --reload
# Rich Rule 목록 확인
sudo firewall-cmd --zone=public --list-rich-rules
firewalld Zone 관리
firewalld Zone 개념과 활용
서버에 인터페이스가 두 개 있습니다. 하나는 내부 관리망, 다른 하나는 외부 공개망에 연결됩니다. 두 인터페이스에 같은 방화벽 규칙을 적용하면 관리망에서나 외부에서나 동일하게 취급되어 보안 구분이 사라집니다. Zone은 인터페이스나 출발지 IP에 따라 신뢰 수준을 다르게 적용하는 개념으로, 이것을 이해해야 "내부망은 관대하게, 외부망은 엄격하게"라는 정책을 firewalld에서 제대로 구현할 수 있습니다.
Zone은 네트워크 인터페이스나 출발지 IP에 따라 다른 신뢰 수준을 부여하는 개념이다.

주요 Zone 비교 — Zone마다 신뢰 수준이 다르며, 인터페이스에 Zone을 할당해 정책을 적용합니다:
| Zone | 신뢰 수준 | 설명 |
|---|---|---|
| trusted | 최고 | 모든 트래픽 허용 (사내 관리망) |
| internal | 높음 | 내부 네트워크용 |
| home | 중간 | 홈 네트워크 |
| public | 낮음 | 외부 인터페이스 기본값 |
| dmz | 낮음 | DMZ 구간 서버 |
| block | 없음 | 모든 인바운드 차단 (ICMP 거부) |
| drop | 없음 | 모든 인바운드 무시 (응답 없음) |
Zone 활용 예시 - 3-tier 아키텍처 — 각 계층에 서로 다른 Zone을 적용해 트래픽 흐름을 제어합니다:
인터넷 → [Web Server: public Zone] → [App Server: internal Zone] → [DB: trusted Zone]
# 인터페이스를 특정 Zone에 할당
sudo firewall-cmd --zone=internal --change-interface=eth1 --permanent
# 출발지 IP 대역을 Zone에 바인딩
sudo firewall-cmd --zone=trusted --add-source=10.0.0.0/8 --permanent
# Zone별 규칙 확인
sudo firewall-cmd --zone=internal --list-all
sudo firewall-cmd --zone=trusted --list-all
프로덕션 서버에 최소 권한 원칙을 적용한다.
이 명령은 방화벽 정책을 변경해 현재 접속 중인 세션이나 운영 트래픽에 즉시 영향을 줄 수 있습니다. 적용 전 허용 대상 IP·포트와 롤백 명령을 확인하세요.
# 현재 public Zone의 불필요한 서비스 제거
# cockpit(웹 콘솔)과 dhcpv6-client 제거
sudo firewall-cmd --zone=public --remove-service=cockpit --permanent
sudo firewall-cmd --zone=public --remove-service=dhcpv6-client --permanent
# 필요한 서비스만 명시적 허용
# 1. 관리자 IP에서만 SSH 허용 (기본 SSH 서비스 제거 후 Rich Rule로 교체)
sudo firewall-cmd --zone=public --remove-service=ssh --permanent
sudo firewall-cmd --zone=public --add-rich-rule='
rule family="ipv4"
source address="YOUR_ADMIN_IP/32"
service name="ssh"
accept' --permanent
# 2. 웹 서비스 허용
sudo firewall-cmd --zone=public --add-service=http --permanent
sudo firewall-cmd --zone=public --add-service=https --permanent
# 3. 기본 정책 확인 (default는 REJECT)
sudo firewall-cmd --zone=public --get-target
# 적용
sudo firewall-cmd --reload
# 최종 확인
sudo firewall-cmd --zone=public --list-all
결과: SSH는 관리자 IP에서만, HTTP/HTTPS는 전체에서, 나머지는 모두 차단된 최소 권한 정책이 완성된다.
기존 세션 끊김 부작용
증상: firewall-cmd --reload를 실행하자마자 SSH 터미널이 응답 없음 상태가 되었다.
원인 분석 — reload 전후 conntrack 상태를 비교해 세션 초기화 여부를 확인합니다:
# 변경 전 conntrack 상태 확인
conntrack -L | grep dport=22
# 출력: tcp 6 86398 ESTABLISHED src=203.0.113.10 dst=10.0.1.5 sport=52431 dport=22 ...
# --reload는 규칙을 완전히 재로드하며, 일부 배포판에서 conntrack 테이블이 초기화됨
# 또는 새 규칙이 기존 ESTABLISHED 세션을 다시 검사
해결 방법:
방법 1: --reload 대신 --runtime-to-permanent 사용
# 임시 규칙을 먼저 적용 (reload 없이 즉시 반영)
sudo firewall-cmd --zone=public --add-service=ssh # --permanent 없이
# 동작 확인 후 영구 저장
sudo firewall-cmd --runtime-to-permanent
방법 2: 새 SSH 세션 열어둔 채로 작업
# 작업 전 다른 터미널에서 SSH 접속을 열어둔다
# 규칙 변경 후 새 세션으로 접속 테스트 → 성공 확인 후 기존 세션 종료
방법 3: at 명령으로 자동 롤백 예약
이 명령은 방화벽 정책을 변경해 현재 접속 중인 세션이나 운영 트래픽에 즉시 영향을 줄 수 있습니다. 적용 전 허용 대상 IP·포트와 롤백 명령을 확인하세요.
# 5분 후 방화벽 규칙 초기화 예약 (실수 대비 안전망)
echo "firewall-cmd --reload" | sudo at now + 5 minutes
# 방화벽 규칙 적용
sudo firewall-cmd --zone=public --remove-service=ssh --permanent
sudo firewall-cmd --reload
# 접속 테스트 성공 시 at 작업 취소
sudo atq # 작업 번호 확인
sudo atrm [작업번호]
예방: 프로덕션 방화벽 변경 시 항상 Maintenance Window를 잡고, 콘솔 접근(IPMI, iDRAC) 또는 Out-of-Band 채널을 확보한다.
증상: 웹 서버 80포트는 접속되는데, 애플리케이션에서 외부 API를 호출하면 타임아웃이 발생한다.
원인 분석 — 아웃바운드 규칙과 실시간 conntrack 세션을 순서대로 점검합니다:
# 아웃바운드 규칙 확인
sudo firewall-cmd --zone=public --list-all
# → outbound 제한이 보임
# 서버에서 외부 API 직접 테스트
curl -v https://api.example.com/v1/test
# → 연결 타임아웃
# 방화벽 로그 확인
sudo journalctl -u firewalld -f
# conntrack으로 아웃바운드 세션 추적
conntrack -L | grep dport=443
해결 — 아웃바운드 HTTPS를 rich-rule로 명시적으로 허용합니다:
이 명령은 방화벽 정책을 변경해 현재 접속 중인 세션이나 운영 트래픽에 즉시 영향을 줄 수 있습니다. 적용 전 허용 대상 IP·포트와 롤백 명령을 확인하세요.
# 아웃바운드 HTTPS 허용 (기본적으로 아웃바운드는 허용이지만,
# 일부 강화된 정책에서는 차단될 수 있음)
sudo firewall-cmd --zone=public --add-rich-rule='
rule family="ipv4"
destination address="0.0.0.0/0"
port port="443" protocol="tcp"
accept' --permanent
# 또는 nftables 직접 확인
sudo nft list ruleset | grep -A5 "output"
sudo firewall-cmd --reload
교훈: 방화벽 정책은 인바운드뿐 아니라 아웃바운드도 고려해야 하며, 특히 서버가 외부 의존성(API, DNS, NTP)을 갖는 경우 아웃바운드 규칙을 명시적으로 관리해야 한다.
실전 방화벽 정책 설계
3-tier 웹 서비스의 애플리케이션 서버에 적합한 방화벽 정책을 완성한다.
이 명령은 방화벽 정책을 변경해 현재 접속 중인 세션이나 운영 트래픽에 즉시 영향을 줄 수 있습니다. 적용 전 허용 대상 IP·포트와 롤백 명령을 확인하세요.
#!/bin/bash
# app-server-firewall.sh
# 애플리케이션 서버 방화벽 정책 설정 스크립트
# 변수 정의
ADMIN_IP="203.0.113.10" # 관리자 IP
WEB_SERVER_IP="10.0.1.10" # 웹 서버 (프론트엔드)
DB_SERVER_IP="10.0.1.20" # DB 서버
# 1. 기본 불필요 서비스 제거
firewall-cmd --zone=public --remove-service=cockpit --permanent
firewall-cmd --zone=public --remove-service=dhcpv6-client --permanent
firewall-cmd --zone=public --remove-service=ssh --permanent
# 2. 관리자만 SSH 허용
firewall-cmd --zone=public --add-rich-rule="
rule family=ipv4
source address=${ADMIN_IP}/32
service name=ssh
accept" --permanent
# 3. 웹 서버에서 앱 포트(8080) 허용
firewall-cmd --zone=public --add-rich-rule="
rule family=ipv4
source address=${WEB_SERVER_IP}/32
port port=8080 protocol=tcp
accept" --permanent
# 4. 앱 서버 → DB 서버 아웃바운드 허용 (나머지 아웃바운드는 제한)
firewall-cmd --zone=public --add-rich-rule="
rule family=ipv4
destination address=${DB_SERVER_IP}/32
port port=5432 protocol=tcp
accept" --permanent
# 5. ICMP (ping) - 네트워크 진단용으로 내부에서만 허용
firewall-cmd --zone=public --add-rich-rule="
rule family=ipv4
source address=10.0.0.0/8
icmp-type name=echo-request
accept" --permanent
# 6. 그 외 모든 트래픽 차단 (기본 target: default = REJECT)
firewall-cmd --zone=public --set-target=default --permanent
firewall-cmd --reload
# 최종 정책 확인
echo "=== 최종 방화벽 정책 ==="
firewall-cmd --zone=public --list-all
# 스크립트 실행
chmod +x app-server-firewall.sh
sudo ./app-server-firewall.sh
# 정책 검증
# 1. 외부에서 SSH 시도 → 거부 확인
# 2. 관리자 IP에서 SSH 접속 → 성공 확인
# 3. 웹 서버에서 8080 포트 접속 → 성공 확인
# 4. 다른 IP에서 8080 포트 접속 → 거부 확인
방화벽 정책 감사와 모니터링
# 방화벽 드롭 패킷 로그 활성화
sudo firewall-cmd --zone=public --add-rich-rule='
rule family="ipv4"
source address="0.0.0.0/0"
log prefix="FIREWALL_DROP " level="info" limit value="5/m"
drop' --permanent
# 로그 실시간 모니터링
sudo journalctl -f | grep FIREWALL_DROP
# 또는 커널 로그에서 확인
sudo dmesg | grep FIREWALL_DROP
# nftables 규칙 전체 확인 (firewalld 백엔드)
sudo nft list ruleset
# 규칙 통계 (패킷/바이트 카운트)
sudo nft list ruleset | grep -E "packets|bytes"
# iptables 호환 모드로 현재 규칙 확인 (구버전 호환)
sudo iptables -L -n -v --line-numbers
정리
이 챕터에서 학습한 핵심 내용:
- 방향성: Inbound는 기본 차단, Outbound는 정책에 따라 관리
- Stateful Inspection: conntrack이 세션 상태를 추적해 응답 패킷을 자동 허용
- ACL 구성 요소: 출발지 IP + 목적지 포트 + 프로토콜 조합으로 세밀한 제어
- firewalld Zone: trusted/public 등 신뢰 수준별 Zone으로 인터페이스 관리
- 최소 권한: 기본 차단 후 필요한 것만 허용 (Default Deny)
- 부작용 관리: 규칙 변경 시 기존 세션 끊김 가능성을 고려한 안전한 작업 절차
다음 챕터에서는 이 방화벽 앞단에서 트래픽을 분산하는 로드밸런서를 학습한다.