리눅스 시스템 기초 · 입문편 — 본문의 "N편"은 입문 과정 번호다. 번호별 글과 전체 250편 구성은 통합 로드맵에서 확인할 수 있다.

리눅스 시스템 기초 46 / 50 · Part 5. 네트워크·보안·SOC
실습 환경: Rocky Linux 9 (10.0.0.200) / Ubuntu 22.04 · zone 설정 출력은 firewall-offline-cmd(firewalld 2.x)의 실제 출력, 런타임 명령 결과는 형식 예시
이전 글: 45. SSH 구조 이해

1. 들어가며

44편에서 ss -tulnp로 열린 포트를 봤고, "0.0.0.0에 열려 있으면 방화벽이 없는 한 외부에 노출된다"고 했다. 이번 글은 그 방화벽이다.

41편 그림에서 방화벽은 커널의 netfilter 계층에 있었다. 하지만 netfilter 규칙(nftables)을 직접 작성하는 일은 복잡하다. RHEL 계열은 이를 관리하는 도구로 firewalld 를 기본 제공하고, Ubuntu는 ufw 를 제공한다.

관제에서 firewalld를 알아야 하는 이유는 세 가지다.

  1. 대응 — 공격 IP를 차단하거나 SSH 접근을 관리망으로 제한하는 것은 관제 요원이 가장 자주 요청하는 조치다.
  2. 분석 — "이 포트가 열려 있지만 실제로 외부에서 접근 가능한가?"를 판단해야 한다.
  3. 탐지 — 공격자는 방화벽을 끄거나 규칙을 추가해 자신의 통로를 연다.

2. 핵심 개념

2-1. firewalld와 netfilter의 관계

firewall-cmd (사용자 명령)
     ↓ D-Bus
firewalld (데몬, 규칙 관리)          ← systemd 서비스 (36·37편)
     ↓
nftables 규칙 (커널에 적재)
     ↓
netfilter (커널, 실제 패킷 검사)

firewalld가 멈춰도 이미 커널에 적재된 규칙은 남을 수 있다. 반대로 systemctl stop firewalld로 끄면 규칙이 내려가 모든 포트가 열린 상태 가 된다.

2-2. Zone

firewalld는 규칙을 zone(구역) 단위로 묶는다. zone은 "이 출처의 트래픽을 얼마나 신뢰할 것인가"를 나타낸다.

zone성격
drop들어오는 모든 연결을 응답 없이 버림
block들어오는 연결을 거부 응답과 함께 차단
public기본 zone. 허용한 서비스만 받음
external / dmz / work / home / internal용도별 신뢰 수준
trusted모든 연결 허용

패킷이 어느 zone으로 판정되는지는 다음 순서로 정해진다.

  1. 출발 IP가 어떤 zone의 source 에 등록되어 있으면 그 zone
  2. 아니면 패킷이 들어온 인터페이스 의 zone
  3. 둘 다 아니면 기본 zone(보통 public)

2-3. service, port, rich rule

요소예설명
servicessh, http미리 정의된 포트 묶음 (ssh = 22/tcp)
port8080/tcp직접 지정
source10.0.0.0/24이 출처를 특정 zone으로
rich rulerule family=ipv4 source address=... service name=ssh accept출처·서비스·로그·제한을 조합한 세밀한 규칙

2-4. runtime과 permanent

구분적용유지
runtime (기본)즉시--reload나 재부팅 시 사라짐
--permanent--reload 후/etc/firewalld/zones/*.xml에 저장

실무에서는 runtime으로 먼저 적용해 확인 → 문제없으면 permanent로 저장 하는 방식을 쓴다. 원격 SSH로 작업하다 규칙을 잘못 넣으면 자기 접속이 끊길 수 있기 때문이다. --runtime-to-permanent로 현재 상태를 한 번에 저장할 수도 있다.


3. 동작 원리

firewalld — zone으로 들어오는 트래픽을 판정한다

3-1. 기본 상태

firewall-offline-cmd(데몬 없이 설정 파일을 다루는 도구)로 확인한 기본 public zone의 실제 출력이다.

public (default)
  target: default
  icmp-block-inversion: no
  interfaces:
  sources:
  services: dhcpv6-client ssh
  ports:
  forward: yes
  masquerade: no
  rich rules:

기본적으로 ssh가 모든 출처에 허용 되어 있다. 인터넷에 연결된 서버라면 45편의 무차별 대입에 그대로 노출된다.

3-2. SSH를 관리망으로 제한하고 공격 IP 차단

다음 순서로 설정을 바꾼 뒤의 실제 출력이다.

firewall-offline-cmd --zone=public --remove-service-from-zone=ssh
firewall-offline-cmd --zone=public --add-service=http
firewall-offline-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="10.0.0.0/24" service name="ssh" accept'
firewall-offline-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="10.0.0.128" service name="ssh" log prefix="SSH-BLOCK " level="info" limit value="1/m" drop'
public (default)
  services: dhcpv6-client http
  rich rules:
	rule family="ipv4" source address="10.0.0.0/24" service name="ssh" accept
	rule family="ipv4" source address="10.0.0.128" service name="ssh" log prefix="SSH-BLOCK " level="info" limit value="1/m" drop

저장된 설정 파일(/etc/firewalld/zones/public.xml)의 실제 내용이다(설명 문구 생략).

<zone>
  <short>Public</short>
  <service name="dhcpv6-client"/>
  <service name="http"/>
  <rule family="ipv4">
    <source address="10.0.0.0/24"/>
    <service name="ssh"/>
    <accept/>
  </rule>
  <rule family="ipv4">
    <source address="10.0.0.128"/>
    <service name="ssh"/>
    <log prefix="SSH-BLOCK " level="info">
      <limit value="1/m"/>
    </log>
    <drop/>
  </rule>
  <forward/>
</zone>

rich rule의 평가 순서. 10.0.0.128은 10.0.0.0/24 안에 있으므로 두 규칙 모두 해당된다. firewalld는 rich rule을 적힌 순서가 아니라 동작 종류별로 정렬 해서 평가한다: log → drop/reject(거부) → accept(허용). 그래서 10.0.0.128의 SSH는 accept보다 먼저 drop되고, 차단 시 SSH-BLOCK 접두어로 커널 로그가 남는다(limit으로 분당 1건만 기록해 로그 폭주를 막음).

drop과 reject의 차이. reject는 상대에게 거부 응답을 보내 즉시 "막혔다"는 것을 알린다. drop은 아무 응답도 하지 않아 상대는 타임아웃까지 기다린다. 42편에서 본 것처럼 스캐너는 응답이 없으면 포트를 filtered로 판정한다.


4. 실습

# 1) 상태
systemctl status firewalld
sudo firewall-cmd --state
sudo firewall-cmd --get-default-zone
sudo firewall-cmd --get-active-zones
sudo firewall-cmd --list-all                  # 기본 zone
sudo firewall-cmd --list-all-zones | less

# 2) 서비스·포트 (runtime → 확인 → permanent)
sudo firewall-cmd --add-service=http
sudo firewall-cmd --add-port=8080/tcp
sudo firewall-cmd --runtime-to-permanent

# 3) SSH 를 관리망으로 제한
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.0.0/24" service name="ssh" accept'
sudo firewall-cmd --permanent --remove-service=ssh
sudo firewall-cmd --reload

# 4) 공격 IP 차단 (drop zone 에 source 등록 — 모든 포트 차단)
sudo firewall-cmd --zone=drop --add-source=203.0.113.7
sudo firewall-cmd --zone=drop --list-sources

# 5) 긴급 차단 모드 (모든 네트워크 차단 — 원격 접속도 끊김, 주의)
# sudo firewall-cmd --panic-on

# 6) 거부 패킷 로그 켜기와 확인
sudo firewall-cmd --set-log-denied=all
sudo journalctl -k | grep -E 'SSH-BLOCK|filter_IN_.*_REJECT|_DROP' | tail

# 7) 커널에 실제 적재된 규칙
sudo nft list ruleset | less

# Ubuntu ufw
sudo ufw status verbose
sudo ufw allow from 10.0.0.0/24 to any port 22 proto tcp
sudo ufw deny from 203.0.113.7

주의: 원격에서 SSH 규칙을 바꿀 때는 허용 규칙을 먼저 추가한 뒤 기존 규칙을 제거한다. 순서를 반대로 하면 자기 세션이 끊길 수 있다. 콘솔 접근 수단을 확보해 두는 것이 좋다.


5. 결과 분석

아래 출력은 형식 설명용 예시다.

① 차단 로그 (journalctl -k)

kernel: SSH-BLOCK IN=ens33 OUT= MAC=... SRC=10.0.0.128 DST=10.0.0.200 LEN=60 ... PROTO=TCP SPT=41862 DPT=22 WINDOW=64240 RES=0x00 SYN URGP=0
필드해석
SSH-BLOCKrich rule의 log prefix — 어떤 규칙에 걸렸는지
SRC / DST출발·목적지 IP
DPT=22, SYNSSH 연결 시도 (42편: SYN = 시도)

규칙 적용 후에도 이 로그가 계속 쌓인다면 공격이 계속 시도 중이지만 차단되고 있다 는 뜻이다. 반대로 sshd 로그(secure)에는 더 이상 이 IP가 나타나지 않아야 한다.

② 방화벽 비활성화 흔적

$ systemctl status firewalld
   Active: inactive (dead) since Tue 02:24:10 KST
$ sudo grep -E 'firewall-cmd|systemctl (stop|disable) firewalld' /var/log/secure
... sudo: devuser : ... COMMAND=/usr/bin/systemctl stop firewalld

37편의 판단 방식 그대로다. enabled 서비스가 inactive이고, 그 시각에 누군가 sudo로 중지했다.


6. 보안 관점

공격흔적점검
방화벽 중지 (T1562.004)firewalld inactive, systemctl stop 기록서비스 상태 감시 (37편)
백도어 포트 허용새 --add-port, trusted zone에 공격자 IP--list-all-zones 기준값 비교
permanent만 변경재부팅 후에 열리는 규칙/etc/firewalld/zones/*.xml 무결성·변경 시각
runtime만 변경파일에는 흔적이 없음firewall-cmd --list-all vs 파일 비교
과도한 허용전체 대역에 SSH·DB 허용출처 제한 rich rule

runtime과 permanent가 다르다 는 것 자체가 점검 포인트다. 공격자가 runtime에만 규칙을 넣으면 설정 파일에는 흔적이 없고, 반대로 permanent에만 넣으면 지금은 안 보이다가 재부팅 후 열린다.


7. SOC / 보안관제 활용

7-1. 방화벽 점검 세트

{
  systemctl is-active firewalld; systemctl is-enabled firewalld
  sudo firewall-cmd --get-active-zones
  sudo firewall-cmd --list-all-zones
  sudo firewall-cmd --permanent --list-all-zones > /tmp/perm.txt
  ls -l --time-style=full-iso /etc/firewalld/zones/
} > /root/evidence/fw_$(date +%Y%m%d_%H%M).txt 2>&1

# runtime 과 permanent 차이
diff <(sudo firewall-cmd --list-all-zones) <(sudo firewall-cmd --permanent --list-all-zones)

7-2. 대응 흐름

[Alert]   secure: 10.0.0.128 Failed password 95회 (02:10~02:19)
   ↓
[즉시]    firewall-cmd --add-rich-rule='... source address="10.0.0.128" service name="ssh" log prefix="SSH-BLOCK " drop'
   ↓
[확인]    secure 에서 10.0.0.128 신규 시도 중단 · journalctl -k 에 SSH-BLOCK 기록
   ↓
[근본]    SSH 를 관리망만 허용, PasswordAuthentication no (45편)
   ↓
[저장]    --runtime-to-permanent · 변경 이력 기록
   ↓
[조사]    10.0.0.128 은 내부 IP → 그 호스트 자체의 침해 조사 (49·50편)

8. 핵심 정리

  • firewalld는 규칙 관리 데몬, 실제 검사는 커널 netfilter(nftables) 다. Ubuntu는 ufw.
  • 패킷의 zone 판정: source → 인터페이스 → 기본 zone(public).
  • 허용은 service·port·rich rule로 한다. rich rule은 log → drop/reject → accept 순서로 평가된다.
  • runtime(즉시, 휘발) vs permanent(파일, reload 후). 원격 작업은 허용 규칙부터 추가.
  • drop은 응답 없음, reject는 거부 응답.
  • 점검: 서비스 활성 상태, zone별 규칙, runtime과 permanent의 차이, 차단 로그.

9. 다음 글

다음 글 「47. SELinux 이해」 에서는 방화벽과는 다른 층의 방어인 SELinux 를 다룬다. 파일 권한(DAC, 15편)만으로는 막지 못하는 상황에서 강제 접근 제어(MAC) 가 어떻게 동작하는지, 컨텍스트(ls -Z)와 enforcing/permissive 모드, AVC 거부 로그 읽기, 그리고 Ubuntu의 AppArmor 와의 차이를 정리한다.


참고 자료

profile
코드에 숨겨진 위협을 읽고 AI로 보안의 미래를 설계합니다. 프론트엔드 개발 경험을 자산 삼아 더 견고하고 지능적인 보안 운영 시스템을 구축해 나가는 과정을 기록합니다

0개의 댓글