포트를 열었는데 외부에서 "Connection timed out"이 납니다. ss -tlnp에는 포트가 LISTEN 상태인데도 마찬가지입니다. 방화벽 규칙을 확인해야 하는데 이 서버에서 iptables, firewalld, ufw 중 어느 도구가 실제로 작동하는지도 모르는 상태입니다. 더 위험한 상황은 반대쪽에서 옵니다 — SSH 포트를 바꾸다 방화벽 오픈 없이 재시작해서 서버에서 완전히 잠기는 Lock-out 입니다.
방화벽 (firewalld & iptables)
- 1netfilter → iptables → Tables·Chains·Rules 3계층 구조를 설명할 수 있다
- 2iptables 규칙을 조회·추가·삽입·삭제하고 iptables-save로 영구 저장할 수 있다
- 3conntrack 모듈로 ESTABLISHED/RELATED 상태 기반 방화벽을 구성할 수 있다
- 4firewalld zone과 firewall-cmd, ufw로 서버 방화벽을 관리할 수 있다
- 5클라우드 보안그룹과 OS 방화벽 두 레이어를 함께 진단할 수 있다
iptables --version && iptables -L -n -vsystemctl status firewalld && firewall-cmd --stateufw status verboseiptables-save | grep -v '^#'Linux 방화벽 계층 구조: iptables → netfilter → 커널

iptables -L로 확인하면 아무 규칙이 없는데, firewall-cmd --list-all로 보면 규칙이 있습니다. 어떤 도구로 설정해야 효과가 있는지, 두 도구가 서로 충돌하는 건지 파악하기 어렵습니다. 이 혼란의 근원은 Linux 방화벽이 계층으로 나뉘어 있기 때문입니다. 커널 안에 netfilter라는 패킷 필터링 엔진이 있고, 그 위에 iptables/nftables/firewalld/ufw가 각각 다른 방식으로 같은 엔진을 제어합니다. 어떤 도구가 어느 레이어에 있는지를 이해하면, 내가 설정한 규칙이 왜 작동하는지(또는 왜 안 먹히는지)가 명확해집니다.
Linux 방화벽은 여러 계층으로 구성되어 있습니다. 각 도구가 어느 레벨에서 동작하는지 이해하면 문제 해결이 훨씬 쉬워집니다.
사용자 공간 (User Space)
┌─────────────────────────────────────────────────┐
│ firewall-cmd │ ufw │ iptables │
│ (firewalld) │ (Ubuntu) │ (직접 제어) │
└────────┬────────┴──────┬──────┴────────┬────────┘
│ │ │
└───────────────┴───────────────┘
│
netfilter API (iptables/nftables)
│
커널 공간 (Kernel Space)
┌─────────────────────────────────────────────────┐
│ netfilter 프레임워크 │
│ (패킷 필터링, NAT, 패킷 변조 핵심 엔진) │
└─────────────────────────────────────────────────┘
│
NIC (네트워크 인터페이스 카드)
계층별 역할
- netfilter: Linux 커널에 내장된 패킷 필터링 프레임워크. 실제 패킷을 허용/차단하는 주체입니다.
- iptables: netfilter를 제어하는 전통적인 사용자 공간 도구. 현재도 RHEL 7 이하, 많은 레거시 시스템에서 표준으로 쓰입니다.
- nftables: iptables의 후속 도구. RHEL 8+, Ubuntu 20.04+에서는 내부적으로 nftables를 사용하지만 iptables 명령 호환성을 유지합니다.
- firewalld: Red Hat이 만든 동적 방화벽 관리 데몬. zone 기반으로 정책을 관리하며, 내부적으로 iptables 또는 nftables를 사용합니다.
- ufw (Uncomplicated Firewall): Ubuntu가 채택한 iptables 프론트엔드. 명령어가 단순하여 초보자도 쉽게 사용할 수 있습니다.
패킷이 통과하는 경로 (Netfilter Hook Points)
[외부 네트워크]
│
▼
PREROUTING ← DNAT, 포트 포워딩이 여기서 발생
│
├─→ [로컬 프로세스로] → INPUT → 로컬 프로세스
│ │
│ OUTPUT ← 로컬 프로세스 응답
│ │
└─→ FORWARD ─────────────┘
│
POSTROUTING ← SNAT, 마스커레이드가 여기서 발생
│
[외부 네트워크]
이 흐름을 이해하면 "왜 INPUT 체인에 룰을 추가했는데 효과가 없지?"와 같은 의문이 해소됩니다.
iptables 핵심 구조: Tables, Chains, Rules

iptables -A INPUT -p tcp --dport 80 -j ACCEPT를 실행했는데 왜 이 규칙이 앞에 있는 DROP 규칙 뒤에 붙어서 적용이 안 되는지, 왜 -I와 -A의 결과가 다른지, FORWARD 체인은 언제 쓰는지 — 이 질문들이 모두 iptables의 3단 구조에서 답이 나옵니다. 패킷이 커널에 들어오면 어떤 테이블의 어떤 체인을 거쳐서 어떤 룰과 매칭되는지, 그 흐름을 한 번 이해하면 이후 모든 iptables 명령이 논리적으로 연결됩니다.
iptables는 테이블(Table) → 체인(Chain) → 룰(Rule) 의 3단 계층 구조로 동작합니다.
Tables (테이블)
각 테이블은 특정 목적을 위해 존재합니다.
| 테이블 | 용도 | 주요 체인 |
|---|---|---|
| filter | 기본 패킷 필터링 (허용/차단) | INPUT, OUTPUT, FORWARD |
| nat | 주소 변환 (포트 포워딩, 마스커레이드) | PREROUTING, POSTROUTING, OUTPUT |
| mangle | 패킷 헤더 수정 (TTL, TOS, MARK) | 모든 체인 |
| raw | connection tracking 예외 처리 | PREROUTING, OUTPUT |
일반적인 서버 방화벽 작업은 거의 모두 filter 테이블에서 이루어집니다.
Chains (체인)
filter 테이블의 3가지 기본 체인:
INPUT : 외부 → 로컬 서버로 들어오는 패킷
OUTPUT : 로컬 서버 → 외부로 나가는 패킷
FORWARD : 서버를 통과하는 패킷 (라우터 역할 시 사용)
실무 관점에서 가장 중요한 것은 INPUT 체인입니다. 서버에 접속하는 모든 외부 연결이 이 체인을 통과합니다.
Rules (룰)
각 룰은 다음 요소로 구성됩니다:
매칭 조건 (Match Criteria)
- 프로토콜: -p tcp / -p udp / -p icmp
- 출발지 IP: -s 192.168.1.0/24
- 목적지 IP: -d 10.0.0.1
- 포트: --dport 80 / --sport 1024:65535
- 인터페이스: -i eth0 / -o eth0
처리 액션 (Target)
- ACCEPT : 패킷을 허용
- DROP : 패킷을 조용히 버림 (응답 없음)
- REJECT : 패킷을 버리고 오류 응답 반환
- LOG : 커널 로그에 기록 (이후 룰 계속 적용)
- DNAT : 목적지 주소 변환
- SNAT : 출발지 주소 변환
- MASQUERADE : 동적 SNAT
룰 적용 순서가 중요합니다. iptables는 위에서 아래로 순서대로 룰을 검사하고, 처음 매칭되는 룰이 적용됩니다. 이후 룰은 검사하지 않습니다.
룰 1: -s 10.0.0.5 -j DROP ← 이 룰이 먼저 매칭되면
룰 2: -s 10.0.0.5 -j ACCEPT ← 이 룰은 절대 실행되지 않음
기본 정책 (Default Policy)
체인의 기본 정책은 어떤 룰도 매칭되지 않을 때 적용됩니다.
# 현재 기본 정책 확인
iptables -L -n | grep "policy"
# 결과 예시
Chain INPUT (policy ACCEPT) ← 아무 룰도 없으면 허용
Chain FORWARD (policy DROP) ← 아무 룰도 없으면 차단
Chain OUTPUT (policy ACCEPT) ← 아무 룰도 없으면 허용
보안 강화 서버의 일반적인 정책:
- INPUT: DROP (화이트리스트 방식 — 명시적으로 허용된 것만 허용)
- OUTPUT: ACCEPT (나가는 트래픽은 대부분 허용)
- FORWARD: DROP (라우터가 아니므로 통과 패킷 차단)
주의: INPUT 정책을 DROP으로 변경하기 전에 반드시 SSH 허용 룰을 먼저 추가하세요. 순서를 바꾸면 서버에서 잠길 수 있습니다.
iptables 기본 명령어
실습 전 디렉토리와 예제 파일을 먼저 준비합니다.
# 실습 디렉토리 준비
mkdir -p /tmp/linux/part4/exam_20 && cd /tmp/linux/part4/exam_20
# 실습 디렉토리 준비 (방화벽 규칙 백업 디렉토리)
mkdir -p /tmp/linux/part4/exam_4 && cd /tmp/linux/part4/exam_4
이제 실습을 진행합니다.
방화벽 현황을 확인하는 것이 모든 작업의 시작입니다.
# 기본 룰 조회 (filter 테이블)
sudo iptables -L
# 상세 조회: -n (숫자로 표시), -v (패킷/바이트 카운터), --line-numbers (줄 번호)
sudo iptables -L -n -v --line-numbers
# 출력 예시:
# Chain INPUT (policy ACCEPT)
# num pkts bytes target prot opt in out source destination
# 1 12543 2.1M ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22
# 2 8901 1.8M ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:80
# 3 0 0 DROP all -- * * 192.168.100.50 0.0.0.0/0
# 특정 체인만 조회
sudo iptables -L INPUT -n -v
# nat 테이블 조회 (포트 포워딩 확인)
sudo iptables -t nat -L -n -v
# mangle 테이블 조회
sudo iptables -t mangle -L -n -v
컬럼 설명:
pkts: 해당 룰에 매칭된 패킷 수 (트래픽 분석에 유용)bytes: 매칭된 바이트 합계target: 적용될 액션 (ACCEPT, DROP 등)prot: 프로토콜 (tcp, udp, all 등)source: 출발지 IP/CIDRdestination: 목적지 IP/CIDR
- sudo iptables -L -n -v 출력에서 INPUT, FORWARD, OUTPUT 체인과 각 체인의 기본 정책이 표시된다
- -n 옵션으로 IP가 숫자 그대로 출력되어 DNS 역질의 없이 결과가 빠르게 나온다
- pkts/bytes 컬럼으로 트래픽이 실제로 매칭된 룰을 식별할 수 있다
- sudo iptables -t nat -L 실행 시 NAT 테이블의 PREROUTING, POSTROUTING 체인이 별도로 표시된다
룰 추가 (-A: Append)
# SSH 허용 (INPUT 체인 끝에 추가)
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# HTTP 허용
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
# HTTPS 허용
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# 특정 IP에서 오는 모든 트래픽 허용
sudo iptables -A INPUT -s 10.10.0.0/16 -j ACCEPT
# 특정 IP 차단
sudo iptables -A INPUT -s 203.0.113.50 -j DROP
# ICMP (ping) 허용
sudo iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
# 이미 확립된 연결과 관련 패킷 허용 (중요! 나가는 요청의 응답이 들어올 수 있도록)
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# 루프백 인터페이스 허용 (로컬 서비스 간 통신)
sudo iptables -A INPUT -i lo -j ACCEPT
룰 삽입 (-I: Insert)
# INPUT 체인의 1번 위치에 삽입 (최우선 적용)
sudo iptables -I INPUT 1 -s 10.10.0.100 -j ACCEPT
# 3번 위치에 삽입
sudo iptables -I INPUT 3 -p tcp --dport 8080 -j ACCEPT
-A는 체인의 맨 끝에 추가하고, -I는 지정한 위치에 삽입합니다. 기본 정책이 DROP인 환경에서는 삽입 위치가 매우 중요합니다.
룰 삭제 (-D: Delete)
# 룰 번호로 삭제 (먼저 --line-numbers로 번호 확인)
sudo iptables -L INPUT --line-numbers
sudo iptables -D INPUT 3 # INPUT 체인의 3번 룰 삭제
# 룰 내용으로 삭제 (추가할 때 사용한 명령의 -A를 -D로 변경)
sudo iptables -D INPUT -p tcp --dport 8080 -j ACCEPT
# 체인의 모든 룰 초기화
sudo iptables -F INPUT # INPUT 체인만 초기화
sudo iptables -F # 모든 체인 초기화 (주의!)
기본 정책 변경
# INPUT 기본 정책을 DROP으로 변경 (반드시 SSH 허용 룰 먼저 추가 후 실행!)
sudo iptables -P INPUT DROP
# FORWARD 기본 정책을 DROP으로 변경
sudo iptables -P FORWARD DROP
# 다시 ACCEPT로 변경
sudo iptables -P INPUT ACCEPT
기본 보안 정책 구성 (실무 예시)
#!/bin/bash
# 실무 서버 방화벽 기본 설정 스크립트
# 1. 기존 룰 초기화
iptables -F
iptables -X
iptables -t nat -F
iptables -t nat -X
# 2. 기본 정책 설정 (일단 ACCEPT로 시작, 마지막에 DROP으로 변경)
iptables -P INPUT ACCEPT
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
# 3. 루프백 허용 (항상 첫 번째로)
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT
# 4. 이미 확립된 연결 허용 (응답 패킷이 들어올 수 있도록)
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# 5. SSH 허용 (관리 접속 — 특정 관리자 IP에서만 허용)
iptables -A INPUT -p tcp --dport 22 -s 203.0.113.0/24 -j ACCEPT
# 6. 웹 서비스 허용
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# 7. 특정 IP 차단 (차단 목록)
iptables -A INPUT -s 198.51.100.0/24 -j DROP
# 8. 포트 스캔 방어: 비정상 TCP 플래그 패킷 차단
iptables -A INPUT -p tcp --tcp-flags ALL NONE -j DROP # NULL 스캔
iptables -A INPUT -p tcp --tcp-flags ALL ALL -j DROP # XMAS 스캔
iptables -A INPUT -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP
# 9. ICMP 제한적 허용 (ping은 허용하되 rate limit)
iptables -A INPUT -p icmp --icmp-type echo-request -m limit --limit 1/s --limit-burst 3 -j ACCEPT
# 10. 차단된 패킷 로그 기록 (마지막 룰 전에 위치)
iptables -A INPUT -j LOG --log-prefix "IPTABLES-DROPPED: " --log-level 4
# 11. 나머지 모든 INPUT 차단
iptables -P INPUT DROP
echo "방화벽 룰 적용 완료"
iptables -L -n -v
특정 포트 허용/차단
# 포트 범위 허용 (10000~20000)
sudo iptables -A INPUT -p tcp --dport 10000:20000 -j ACCEPT
# UDP 포트 허용 (DNS)
sudo iptables -A INPUT -p udp --dport 53 -j ACCEPT
# 특정 포트를 특정 IP에서만 허용 (DB 포트)
sudo iptables -A INPUT -p tcp --dport 3306 -s 10.0.1.0/24 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 3306 -j DROP # 나머지 IP에서는 차단
# 특정 인터페이스에서 오는 트래픽 허용
sudo iptables -A INPUT -i eth1 -p tcp --dport 8080 -j ACCEPT
로그 기록
# 차단 전 로그 기록 (LOG는 패킷을 계속 통과시킴)
sudo iptables -A INPUT -p tcp --dport 22 -s 0/0 -j LOG --log-prefix "SSH-ATTEMPT: "
sudo iptables -A INPUT -p tcp --dport 22 -s 0/0 -j DROP
# 로그 확인
sudo dmesg | grep "SSH-ATTEMPT"
sudo journalctl -k | grep "SSH-ATTEMPT"
# 또는
sudo tail -f /var/log/kern.log | grep IPTABLES
iptables 룰은 기본적으로 재부팅 시 사라집니다. 영구 저장을 위한 방법을 알아봅니다.
iptables-save / iptables-restore
# 현재 룰을 파일로 저장
sudo iptables-save > /etc/iptables/rules.v4
sudo ip6tables-save > /etc/iptables/rules.v6
# 파일 내용 예시:
# *filter
# :INPUT DROP [0:0]
# :FORWARD DROP [0:0]
# :OUTPUT ACCEPT [0:0]
# -A INPUT -i lo -j ACCEPT
# -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
# -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
# -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
# -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT
# COMMIT
# 파일에서 룰 복원
sudo iptables-restore < /etc/iptables/rules.v4
RHEL/CentOS에서 영구 저장
# iptables-services 패키지 설치
sudo dnf install iptables-services -y
# 서비스 활성화
sudo systemctl enable iptables
sudo systemctl start iptables
# 현재 룰 저장 (재부팅 후에도 유지)
sudo service iptables save
# 또는
sudo iptables-save > /etc/sysconfig/iptables
# 저장된 룰 확인
cat /etc/sysconfig/iptables
Ubuntu/Debian에서 영구 저장
# iptables-persistent 패키지 설치 (설치 시 현재 룰을 저장할지 묻는 대화상자 표시)
sudo apt install iptables-persistent -y
# 룰은 /etc/iptables/rules.v4에 저장됨
# 나중에 룰 변경 후 다시 저장:
sudo netfilter-persistent save
# 서비스가 부팅 시 자동으로 룰 복원
sudo systemctl status netfilter-persistent
systemd를 이용한 방법 (배포판 무관)
# /etc/systemd/system/iptables-restore.service 생성
sudo tee /etc/systemd/system/iptables-restore.service << 'EOF'
[Unit]
Description=Restore iptables rules
Before=network-pre.target
Wants=network-pre.target
[Service]
Type=oneshot
ExecStart=/sbin/iptables-restore /etc/iptables/rules.v4
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable iptables-restore
sudo systemctl start iptables-restore
firewalld (RHEL/CentOS 기본 방화벽)
firewalld는 zone(영역) 기반으로 네트워크 트래픽을 관리합니다. 각 zone은 신뢰 수준을 나타내며, 네트워크 인터페이스나 IP 주소를 특정 zone에 할당합니다.
Zone 종류와 기본 정책
zone 이름 신뢰 수준 기본 동작
─────────────────────────────────────────────────────────
drop 최저 모든 인바운드 패킷 드롭, 아웃바운드만 허용
block 낮음 인바운드 거부 (ICMP 오류 반환), 아웃바운드 허용
public 보통 선택한 서비스만 허용 (서버의 기본 zone)
external 보통 NAT 마스커레이드 포함, 외부 연결용
internal 높음 내부 네트워크용, 대부분 허용
dmz 보통 DMZ 서버용, 제한적 허용
work 높음 업무 네트워크, 대부분 허용
home 높음 홈 네트워크, 대부분 허용
trusted 최고 모든 연결 허용
firewalld 서비스 관리
# firewalld 상태 확인
sudo systemctl status firewalld
# 시작/중지/재시작
sudo systemctl start firewalld
sudo systemctl stop firewalld
sudo systemctl restart firewalld
# 부팅 시 자동 시작
sudo systemctl enable firewalld
현재 상태 조회
# 전체 현황 조회
sudo firewall-cmd --list-all
# 출력 예시:
# public (active)
# target: default
# icmp-block-inversion: no
# interfaces: eth0
# sources:
# services: dhcpv6-client ssh
# ports: 8080/tcp
# protocols:
# masquerade: no
# forward-ports:
# source-ports:
# icmp-blocks:
# rich rules:
# 모든 zone 조회
sudo firewall-cmd --list-all-zones
# 활성 zone 확인
sudo firewall-cmd --get-active-zones
# 기본 zone 확인
sudo firewall-cmd --get-default-zone
# 사용 가능한 서비스 목록
sudo firewall-cmd --get-services
# 특정 zone 조회
sudo firewall-cmd --zone=public --list-all
포트 및 서비스 추가/제거
# 포트 열기 (임시 — 재부팅 시 사라짐)
sudo firewall-cmd --add-port=8080/tcp
# 포트 열기 (영구 저장)
sudo firewall-cmd --permanent --add-port=8080/tcp
# 영구 설정 후 반드시 reload 실행
sudo firewall-cmd --reload
# 포트 범위 열기
sudo firewall-cmd --permanent --add-port=10000-20000/tcp
# UDP 포트 열기
sudo firewall-cmd --permanent --add-port=53/udp
# 포트 닫기
sudo firewall-cmd --permanent --remove-port=8080/tcp
sudo firewall-cmd --reload
# 서비스 이름으로 추가 (서비스 정의 파일 사용)
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --permanent --add-service=mysql
sudo firewall-cmd --reload
# 서비스 제거
sudo firewall-cmd --permanent --remove-service=mysql
sudo firewall-cmd --reload
--permanent vs 임시 적용
# 임시 적용: 즉시 효과, 재부팅 또는 reload 시 사라짐
sudo firewall-cmd --add-port=9090/tcp
# 영구 적용: 설정 파일에 저장, 즉시 효과 없음
sudo firewall-cmd --permanent --add-port=9090/tcp
# 영구 설정을 즉시 반영
sudo firewall-cmd --reload
# 실무 패턴: 테스트 후 영구 적용
# 1. 임시로 포트 열기
sudo firewall-cmd --add-port=9090/tcp
# 2. 접속 테스트
curl http://localhost:9090
# 3. 문제 없으면 영구 저장
sudo firewall-cmd --permanent --add-port=9090/tcp
sudo firewall-cmd --reload
# 현재 임시 설정과 영구 설정 비교
sudo firewall-cmd --list-ports # 현재 활성 설정
sudo firewall-cmd --permanent --list-ports # 영구 저장된 설정
Zone 변경 및 특정 IP 허용
# 특정 소스 IP를 trusted zone으로 이동 (모든 포트 허용)
sudo firewall-cmd --permanent --zone=trusted --add-source=10.0.1.0/24
sudo firewall-cmd --reload
# 특정 IP에서 특정 포트만 허용 (rich rule)
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.1.100" port port="3306" protocol="tcp" accept'
# 특정 IP 차단
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.50" reject'
# rich rule 목록 확인
sudo firewall-cmd --list-rich-rules
# 인터페이스를 다른 zone으로 변경
sudo firewall-cmd --permanent --zone=internal --change-interface=eth1
sudo firewall-cmd --reload
ufw (Ubuntu 기본 방화벽)
ufw(Uncomplicated Firewall)는 Ubuntu/Debian 계열에서 기본 제공하는 방화벽으로, iptables보다 훨씬 간단한 명령어를 제공합니다.
ufw 기본 조작
# ufw 상태 확인
sudo ufw status
# 상세 상태 확인 (포트별 규칙 표시)
sudo ufw status verbose
# ufw 활성화 (처음 활성화 시 SSH가 차단될 수 있으니 주의)
sudo ufw enable
# ufw 비활성화
sudo ufw disable
# 룰 초기화 (모든 룰 삭제 및 비활성화)
sudo ufw reset
포트 허용/차단
# 포트 허용 (프로토콜 지정)
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 53/udp
# 프로토콜 생략 시 tcp+udp 모두 허용
sudo ufw allow 8080
# 서비스 이름으로 허용
sudo ufw allow ssh
sudo ufw allow http
sudo ufw allow https
# 포트 범위 허용
sudo ufw allow 10000:20000/tcp
# 포트 차단
sudo ufw deny 3306/tcp
# 특정 IP에서만 허용
sudo ufw allow from 10.0.1.0/24 to any port 22
# 특정 IP를 특정 포트로 허용
sudo ufw allow from 203.0.113.10 to any port 80 proto tcp
# 특정 IP 전체 차단
sudo ufw deny from 198.51.100.50
룰 삭제
# 룰 번호로 삭제 (먼저 번호 확인)
sudo ufw status numbered
# 출력 예시:
# Status: active
# To Action From
# -- ------ ----
# [ 1] 22/tcp ALLOW IN Anywhere
# [ 2] 80/tcp ALLOW IN Anywhere
# [ 3] 443/tcp ALLOW IN Anywhere
# 번호로 삭제
sudo ufw delete 3
# 룰 내용으로 삭제
sudo ufw delete allow 8080/tcp
sudo ufw delete allow http
기본 정책 설정
# 기본 정책 설정 (보안 강화)
sudo ufw default deny incoming # 인바운드 기본 차단
sudo ufw default allow outgoing # 아웃바운드 기본 허용
# 허용할 서비스 추가 후 활성화
sudo ufw allow ssh
sudo ufw allow http
sudo ufw allow https
sudo ufw enable
ufw 로그
# 로그 활성화
sudo ufw logging on
# 로그 레벨 설정
sudo ufw logging low # 차단된 패킷만
sudo ufw logging medium # 차단 + 허용된 새 연결
sudo ufw logging high # 모든 패킷
# 로그 확인
sudo tail -f /var/log/ufw.log
# 로그 비활성화
sudo ufw logging off
클라우드 보안그룹과 OS 방화벽
클라우드 환경(AWS, GCP, Azure 등)에서는 방화벽이 두 겹으로 존재합니다. 이를 이해하지 못하면 한쪽만 열고 "안 된다"는 결론을 내리게 됩니다.
트래픽 흐름
인터넷
│
▼
[클라우드 보안그룹 / Network ACL] ← 클라우드 레벨 (VPC, 하이퍼바이저)
│ 차단되면 패킷이 VM 자체에 도달하지 않음
▼
[OS 방화벽 (iptables / firewalld / ufw)] ← OS 레벨 (VM 내부)
│ 차단되면 패킷이 프로세스에 도달하지 않음
▼
[애플리케이션 프로세스] ← nginx, node, java 등
두 레이어를 모두 통과해야 서비스에 접속 가능합니다.
AWS 보안그룹 vs OS 방화벽 차이
| 항목 | AWS 보안그룹 | OS 방화벽 (iptables 등) |
|---|---|---|
| 위치 | 하이퍼바이저 레벨 | VM 내부 커널 |
| 상태 저장 | 상태 저장형 (Stateful) | 기본 상태 없음 (Stateless), conntrack 모듈로 상태 저장 가능 |
| 기본 정책 | 인바운드 전체 차단, 아웃바운드 허용 | 배포판마다 다름 |
| 적용 시점 | 즉시 (서비스 재시작 불필요) | 즉시 |
| 우선순위 | 먼저 통과해야 OS까지 도달 | 클라우드 보안그룹 통과 후 적용 |
실무 체크리스트
외부 접속이 안 될 때:
□ 1. 클라우드 보안그룹에서 해당 포트가 열려 있는가?
□ 2. OS 방화벽(iptables/firewalld/ufw)에서 해당 포트가 허용되어 있는가?
□ 3. 애플리케이션이 실제로 해당 포트에서 리슨하고 있는가?
□ 4. 애플리케이션이 0.0.0.0(모든 인터페이스)에서 리슨하는가, 127.0.0.1에서만 리슨하는가?
실무 예시: 웹 서버 포트 80 열기
# Step 1: AWS 콘솔 또는 CLI에서 보안그룹 설정
aws ec2 authorize-security-group-ingress \
--group-id sg-xxxxxxxxx \
--protocol tcp \
--port 80 \
--cidr 0.0.0.0/0
# Step 2: OS 방화벽 설정 (RHEL/CentOS)
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --reload
# Step 3: 애플리케이션이 모든 인터페이스에서 리슨하는지 확인
sudo ss -tlnp | grep :80
# tcp LISTEN 0 128 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=1234,fd=6))
# ↑ 0.0.0.0이어야 외부에서 접속 가능
# 127.0.0.1이면 로컬에서만 접속 가능
NAT & 포트 포워딩
포트 포워딩은 외부에서 들어오는 특정 포트 트래픽을 내부 서버의 다른 포트로 전달하는 기술입니다. 로드밸런서 없이 간단한 트래픽 리다이렉션에 유용합니다.
DNAT (Destination NAT) 기본 개념
외부 클라이언트
│ → 서버 IP:80으로 접속 시도
▼
[PREROUTING 체인] ← nat 테이블
│ DNAT 룰: 목적지를 192.168.1.10:8080으로 변경
▼
[내부 애플리케이션 서버 192.168.1.10:8080]
포트 포워딩 설정
# 외부 포트 80을 로컬 8080으로 포워딩
sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
# 외부 포트 80을 다른 서버(192.168.1.10)의 8080으로 포워딩
sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 192.168.1.10:8080
# FORWARD 체인에서 전달 허용
sudo iptables -A FORWARD -p tcp -d 192.168.1.10 --dport 8080 -j ACCEPT
# 응답 패킷을 위한 MASQUERADE (POSTROUTING)
sudo iptables -t nat -A POSTROUTING -j MASQUERADE
# 커널 IP 포워딩 활성화 (필수)
echo 1 > /proc/sys/net/ipv4/ip_forward
# 영구 활성화 (/etc/sysctl.conf)
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
sysctl -p
NAT 테이블 확인
# nat 테이블 룰 조회
sudo iptables -t nat -L -n -v
# 출력 예시:
# Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
# pkts bytes target prot opt in out source destination
# 5 300 DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 to:192.168.1.10:8080
# firewalld로 포트 포워딩 설정
sudo firewall-cmd --permanent --add-forward-port=port=80:proto=tcp:toport=8080
sudo firewall-cmd --reload
# 다른 IP로 포워딩
sudo firewall-cmd --permanent --add-forward-port=port=80:proto=tcp:toport=8080:toaddr=192.168.1.10
sudo firewall-cmd --reload
마스커레이드 (MASQUERADE) — NAT 게이트웨이
# 내부 네트워크(192.168.1.0/24)가 외부 인터넷에 접속할 수 있도록 SNAT 설정
sudo iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j MASQUERADE
# firewalld로 마스커레이드 설정
sudo firewall-cmd --permanent --zone=external --add-masquerade
sudo firewall-cmd --reload
트러블슈팅
상황: 프로세스가 포트에 LISTEN하고 있는데 외부에서 접속이 안 됩니다. 서버 내부에서 curl localhost:80은 정상인데 외부에서는 timed out입니다.
원인: 클라우드 보안그룹 차단, OS 방화벽(iptables/firewalld/ufw) 차단, 또는 127.0.0.1에만 바인딩된 경우 중 하나입니다.
진단
Step 1: 프로세스가 실제로 리슨 중인지 확인
# 포트 80에서 리슨 중인 프로세스 확인
sudo ss -tlnp | grep :80
sudo netstat -tlnp | grep :80
# 결과 해석:
# tcp LISTEN 0.0.0.0:80 → 모든 인터페이스에서 리슨 (외부 접속 가능)
# tcp LISTEN 127.0.0.1:80 → 루프백에서만 리슨 (외부 접속 불가!)
# (아무것도 없음) → 프로세스가 해당 포트에서 리슨하지 않음
127.0.0.1에서만 리슨 중인 경우: 애플리케이션 설정에서 bind address를 0.0.0.0으로 변경하세요.
Step 2: OS 방화벽 확인
# iptables 확인
sudo iptables -L INPUT -n -v --line-numbers
# firewalld 확인
sudo firewall-cmd --list-all
sudo systemctl status firewalld
# ufw 확인
sudo ufw status verbose
Step 3: 클라우드 보안그룹 확인
# AWS: 현재 인스턴스의 보안그룹 확인
aws ec2 describe-instance-attribute --instance-id i-xxxxxxxx --attribute groupSet
# 보안그룹 인바운드 룰 확인
aws ec2 describe-security-groups --group-ids sg-xxxxxxxx --query 'SecurityGroups[*].IpPermissions'
Step 4: 서버 내부에서 직접 테스트
# 서버 자신에서 접속 테스트 (방화벽 우회)
curl http://localhost:80
curl http://127.0.0.1:80
# 서버 자신의 외부 IP로 테스트
curl http://$(curl -s ifconfig.me):80
Step 5: 외부에서 포트 연결 테스트
# 다른 서버에서 nc(netcat)로 포트 연결 테스트
nc -zv 서버IP 80
# 성공: Connection to 서버IP 80 port [tcp/http] succeeded!
# 실패: nc: Connection refused (포트는 열림, 프로세스 없음)
# 실패: nc: Operation timed out (방화벽이 DROP으로 차단)
# telnet으로 테스트
telnet 서버IP 80
Step 6: tcpdump로 패킷 도달 여부 확인
# 서버에서 실행 (외부에서 접속 시도 중에)
sudo tcpdump -i eth0 port 80 -n
# 패킷이 보이면 → OS까지 도달함 → OS 방화벽이나 프로세스 문제
# 패킷이 안 보이면 → 클라우드 보안그룹이 차단 중
해결 — 원인별 조치
| 증상 | 원인 | 해결 방법 |
|---|---|---|
| tcpdump에 패킷 없음 | 클라우드 보안그룹 차단 | 보안그룹 인바운드 룰 추가 |
| tcpdump에 패킷 있음, 응답 없음 | OS 방화벽 차단 | iptables/firewalld 룰 추가 |
| 방화벽 통과, 연결 거부 | 프로세스 미실행 또는 127.0.0.1 바인딩 | 프로세스 시작 또는 바인드 주소 변경 |
상황: SSH 포트를 22→2222로 변경하고 sshd를 재시작했는데 방화벽에 2222를 오픈하지 않아 서버 접속이 완전히 불가능합니다.
원인: 방화벽 오픈 → sshd 설정 변경 → 재시작 순서를 지키지 않아 sshd가 2222에 bind됐지만 방화벽 DROP 상태입니다.
진단 — 올바른 순서 (사전 예방)
잘못된 순서 (절대 금지):
# 위험한 순서 (절대 하지 마세요!)
# 1. /etc/ssh/sshd_config에서 Port를 2222로 변경
# 2. sshd 재시작 → Port 22에서 리슨 중단
# 3. 방화벽에서 2222 미오픈
# 4. 기존 SSH 세션 종료
# 5. 서버에 접속 불가 😱
올바른 순서 (반드시 이 순서로):
# 1. 먼저 새 포트를 방화벽에 추가
sudo firewall-cmd --permanent --add-port=2222/tcp
sudo firewall-cmd --reload
# 또는
sudo iptables -A INPUT -p tcp --dport 2222 -j ACCEPT
sudo iptables-save > /etc/iptables/rules.v4
# 2. 새 포트로 접속 테스트 (sshd 변경 전에)
# 다른 터미널에서: ssh -p 2222 user@서버IP
# (sshd가 아직 2222에서 안 리슨하므로 실패하지만, 방화벽은 열린 것 확인)
# 3. sshd 설정 변경
sudo vi /etc/ssh/sshd_config
# Port 2222 (또는 두 포트 모두: Port 22, Port 2222)
# SELinux가 활성화된 경우 SSH 포트 레이블 추가
sudo semanage port -a -t ssh_port_t -p tcp 2222
# 4. sshd 재시작 (기존 세션은 유지됨)
sudo systemctl restart sshd
# 5. 기존 세션 유지한 채로 새 포트로 접속 테스트
# ssh -p 2222 user@서버IP
# 6. 접속 성공 확인 후 기존 포트 22 차단 (선택사항)
sudo firewall-cmd --permanent --remove-service=ssh
sudo firewall-cmd --permanent --add-port=2222/tcp
sudo firewall-cmd --reload
해결 — Lock-out 발생 시 복구
이미 잠긴 경우 클라우드 플랫폼의 콘솔 접근을 이용합니다:
# AWS: EC2 Instance Connect 또는 Systems Manager Session Manager
aws ssm start-session --target i-xxxxxxxxxxxxxxxxx
# AWS: EC2 Serial Console (인스턴스 타입에 따라 지원)
# GCP: Cloud Shell에서 gcloud compute ssh
gcloud compute ssh 인스턴스명 --zone=asia-northeast3-a
# Azure: Serial Console 또는 Bastion
# 콘솔 접속 후 방화벽 룰 수정
sudo firewall-cmd --permanent --add-port=22/tcp
sudo firewall-cmd --reload
# 또는
sudo iptables -I INPUT 1 -p tcp --dport 22 -j ACCEPT
예방 — at 명령으로 자동 복구 예약:
# at 명령으로 10분 후 방화벽 규칙 자동 복원 (안전망)
sudo at now + 10 minutes << 'EOF'
/sbin/iptables -F
/sbin/iptables -P INPUT ACCEPT
EOF
# 변경 작업 성공 시 at job 취소
sudo atq # 예약된 job 확인
sudo atrm 1 # job 번호 1 취소
상황: 특정 IP를 차단하는 DROP 룰을 -A(append)로 추가했는데 해당 IP에서 여전히 접속됩니다.
원인: iptables는 위에서 아래로 순서대로 매칭하며 처음 매칭되는 룰에서 처리를 종료합니다. ACCEPT ALL이 DROP보다 앞에 있으면 DROP은 절대 도달하지 않습니다.
진단
문제 사례 1: 특정 IP 차단이 효과 없는 경우
# 현재 룰 상태
sudo iptables -L INPUT -n --line-numbers
# 결과:
# Chain INPUT (policy ACCEPT)
# num target prot opt source destination
# 1 ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
# 2 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 ← 모든 IP에서 80 허용
# 3 DROP all -- 203.0.113.50 0.0.0.0/0 ← 특정 IP 차단 (효과 없음!)
# 203.0.113.50이 80 포트로 접속하면:
# 룰 1: 매칭 안됨 (기존 연결 아님)
# 룰 2: 매칭됨 → ACCEPT 적용, 여기서 처리 종료
# 룰 3: 절대 도달하지 않음
해결: 차단 룰을 허용 룰보다 앞에 배치
# 차단 룰을 1번 위치에 삽입
sudo iptables -I INPUT 1 -s 203.0.113.50 -j DROP
# 결과:
# 1 DROP all -- 203.0.113.50 ← 이제 먼저 검사됨
# 2 ACCEPT all -- 0.0.0.0/0 ctstate RELATED,ESTABLISHED
# 3 ACCEPT tcp -- 0.0.0.0/0 tcp dpt:80
문제 사례 2: ACCEPT ALL이 맨 앞에 있는 경우
# 잘못된 구성
# 1 ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 ← 모든 트래픽 허용
# 2 DROP tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:3306 ← 절대 실행 안됨
# 올바른 구성
# 1 DROP tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:3306 ← 먼저 차단
# 2 ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 ← 나머지 허용
해결 — 룰 순서 점검 및 수정
# 현재 룰을 순서대로 확인
sudo iptables -L INPUT -n -v --line-numbers
# 특정 IP/포트가 어느 룰에 매칭되는지 추적
# (iptables에는 trace 기능이 있음)
sudo iptables -t raw -A PREROUTING -p tcp --dport 80 -s 203.0.113.50 -j TRACE
sudo modprobe nf_log_ipv4
sudo sysctl net.netfilter.nf_log.2=nf_log_ipv4
# 로그 확인
sudo dmesg | grep "TRACE:"
# 추적 완료 후 TRACE 룰 제거
sudo iptables -t raw -D PREROUTING -p tcp --dport 80 -s 203.0.113.50 -j TRACE
권장 룰 순서 설계 원칙:
좋은 룰 순서 (권장):
1. 루프백 허용
2. ESTABLISHED/RELATED 허용
3. 명시적 차단 룰 (블랙리스트)
4. 서비스별 허용 룰
5. 관리 접속 허용 (SSH 등)
6. LOG 룰 (선택)
7. 기본 정책 (DROP)
실무 참고
방화벽 도구 선택 가이드

새 서버에서 방화벽 설정을 하려는데 검색하면 ufw, firewalld, iptables, nftables가 동시에 나옵니다. 우분투 공식 문서는 ufw를 쓰고, RHEL 튜토리얼은 firewalld를 쓰고, Docker 관련 글은 iptables를 직접 건드립니다. 어떤 걸 써야 서로 충돌 없이 안정적으로 운영할 수 있는지 기준이 없으면, 나중에 규칙이 적용됐는지 안 됐는지 모르는 상황이 됩니다. 배포판과 용도 기준으로 어떤 도구를 선택해야 하는지 정리해두면 첫 설정 때 방향을 잡는 데 도움이 됩니다.
어떤 도구를 언제 사용해야 할지 혼란스러울 수 있습니다. 실무 기준으로 정리합니다.
배포판별 기본 도구
| 배포판 | 기본 방화벽 도구 | 내부 엔진 |
|---|---|---|
| RHEL/CentOS 7 | firewalld | iptables |
| RHEL/CentOS 8+ | firewalld | nftables |
| Ubuntu 18.04+ | ufw | iptables/nftables |
| Debian | iptables (수동) | iptables |
| Amazon Linux 2 | firewalld 또는 iptables | iptables |
| Amazon Linux 2023 | firewalld | nftables |
도구별 장단점
iptables
- 장점: 세밀한 제어, 모든 리눅스에서 사용 가능, 스크립트화 용이
- 단점: 명령어 복잡, 영구 저장을 별도로 처리해야 함, 동적 변경 어려움
- 적합한 상황: 복잡한 NAT 설정, 세밀한 패킷 필터링, 레거시 시스템
firewalld
- 장점: 동적 변경 (서비스 재시작 불필요), zone 기반 직관적 관리, D-Bus API
- 단점: 추상화로 인한 복잡성, RHEL 계열에 최적화
- 적합한 상황: RHEL/CentOS 서버, 동적으로 변경되는 포트 관리
ufw
- 장점: 단순한 명령어, 빠른 설정
- 단점: 세밀한 제어 어려움, Ubuntu에 최적화
- 적합한 상황: Ubuntu 서버, 간단한 포트 허용/차단
공존 문제 주의
# firewalld와 iptables를 동시에 사용하면 충돌 발생 가능
# RHEL에서 iptables를 직접 사용하려면 firewalld를 비활성화
sudo systemctl stop firewalld
sudo systemctl disable firewalld
sudo systemctl mask firewalld # 실수로 시작되지 않도록
# 그 후 iptables-services 사용
sudo systemctl enable iptables
sudo systemctl start iptables
nftables로의 전환
현대 Linux 배포판에서는 내부적으로 nftables를 사용하지만, iptables 명령은 호환성 레이어(iptables-nft)를 통해 계속 동작합니다.
# iptables가 nft 백엔드를 사용하는지 확인
iptables --version
# iptables v1.8.7 (nf_tables) ← nftables 백엔드 사용 중
# 직접 nftables 사용 (미래 방향)
sudo nft list ruleset
sudo nft add rule inet filter input tcp dport 80 accept
방화벽 관리자 혼용 금지 — 1개 도구만 사용하는 이유

한 서버에서 ufw, firewalld, 직접 iptables, nftables를 동시에 사용하면 "룰이 있는데도 안 먹히는" 현상이 빈번하게 발생합니다.
혼용이 위험한 이유:
- ufw와 firewalld가 같은 netfilter 체인에 동시에 룰을 추가 → 충돌/누락
- firewalld direct(passthrough)는 deprecated 상태 — policies로 대체 예정
- nftables 환경에서는 ACCEPT가 즉시 허용을 의미하지 않을 수 있음 (chain/priority 의존)
현재 시스템의 방화벽 구조 확인:
# iptables 백엔드 확인 (nft vs legacy)
iptables --version
# iptables v1.8.10 (nf_tables) ← Ubuntu 22.04+, RHEL 9+ 기본
# iptables v1.8.4 (legacy) ← 구형 또는 명시적 전환 시
# 어떤 방화벽 관리자가 활성 상태인지 확인
systemctl is-active ufw firewalld 2>/dev/null
# 직접 nftables 룰 확인
sudo nft list ruleset | head -30
배포판별 권장 도구 (1개만 선택):
| 배포판 | 권장 도구 | 백엔드 |
|---|---|---|
| Ubuntu 20.04+ | ufw | iptables-nft |
| RHEL/Rocky/Alma 8+ | firewalld | nftables |
| Debian 12+ | ufw 또는 nftables 직접 | nftables |
| 컨테이너 호스트 (Docker) | iptables 직접 관리 | nft 또는 legacy |
운영 원칙: 신규 서버 구성 시 1개 도구를 선택하고 나머지는 비활성화하세요. ufw를 선택했다면
systemctl mask firewalld, firewalld를 선택했다면ufw disable.
IPv6 방화벽 기초 — ICMPv6 필수 허용과 ip6tables

IPv4 방화벽만 신경 쓰고 IPv6를 방치하면 IPv6 경로로 방화벽을 우회하는 장애가 발생합니다. 또한 ICMPv6를 차단하면 IPv6 통신 자체가 완전히 불가능해집니다.
ip6tables 기본 사용:
# IPv6 규칙 조회
sudo ip6tables -L -n -v
# IPv6 SSH 허용 (IPv6 주소로 접속하는 경우)
sudo ip6tables -A INPUT -p tcp --dport 22 -j ACCEPT
# IPv6 규칙 영구 저장
sudo ip6tables-save > /etc/iptables/rules.v6
ICMPv6 필수 허용 규칙 (차단하면 IPv6 완전 불능):
# ICMPv6 필수 허용 (Neighbor Discovery, Router Advertisement 등)
# 이 규칙들을 차단하면 IPv6 주소 할당, 라우팅이 모두 실패함
sudo ip6tables -A INPUT -p ipv6-icmp -j ACCEPT
sudo ip6tables -A OUTPUT -p ipv6-icmp -j ACCEPT
# 특정 ICMPv6 타입만 허용하는 더 엄격한 버전
sudo ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 135 -j ACCEPT # Neighbor Solicitation
sudo ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 136 -j ACCEPT # Neighbor Advertisement
sudo ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 133 -j ACCEPT # Router Solicitation
sudo ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 134 -j ACCEPT # Router Advertisement
nftables로 IPv4/IPv6 통합 관리 (inet 패밀리):
# inet 패밀리는 IPv4와 IPv6를 동시에 처리 (중복 설정 불필요)
sudo nft add table inet filter
sudo nft add chain inet filter input '{ type filter hook input priority 0; policy drop; }'
# SSH 허용 (IPv4 + IPv6 동시)
sudo nft add rule inet filter input tcp dport 22 accept
# ICMPv6 허용 (IPv4의 ICMP와 IPv6의 ICMPv6 각각 허용)
sudo nft add rule inet filter input icmp type echo-request accept
sudo nft add rule inet filter input icmpv6 type { echo-request, nd-neighbor-solicit, nd-neighbor-advert, nd-router-advert, nd-router-solicit } accept
# 규칙 확인
sudo nft list table inet filter
클라우드 환경: AWS/GCP/Azure의 Security Group은 IPv4와 IPv6를 별도 규칙으로 관리합니다. OS 방화벽뿐 아니라 Security Group에서도 IPv6 규칙을 추가했는지 확인하세요.
다음 모듈에서는 Linux 서버 프로비저닝 & 부팅 프로세스 — BIOS/UEFI, GRUB, systemd target까지 서버가 켜지는 순서와 부팅 장애 복구를 다룹니다.