가이드

ufw 설정: 리눅스 서버 방화벽 구성과 nftables 기초

업데이트: · Skillaria.Top

ufw 설정은 세 단계로 끝납니다. 기본 정책을 정하고(sudo ufw default deny incoming, sudo ufw default allow outgoing), SSH와 필요한 포트를 허용한 뒤(sudo ufw allow 22/tcp, sudo ufw allow 443/tcp), sudo ufw enable로 방화벽을 켭니다. 규칙은 sudo ufw status numbered로 확인하며, ufw는 내부적으로 netfilter를 iptables나 nftables를 통해 제어합니다.

리눅스 방화벽은 서버로 들어오는 네트워크 패킷 중 어떤 것을 통과시키고 어떤 것을 버릴지 결정합니다. 실제 필터링은 커널의 netfilter 서브시스템이 담당하고, 이를 제어하는 도구는 저수준의 iptables와 nftables, 그리고 이를 감싼 ufw(Uncomplicated Firewall)입니다. 우분투와 데비안 서버라면 ufw 설정은 5분이면 끝납니다. 들어오는 연결을 모두 막고, SSH와 서비스 포트만 열고, 방화벽을 켜면 됩니다. 아래에서는 단계별 절차, 흔한 실수, 그리고 내부 동작을 이해하기 위한 nftables 기초를 설명합니다.

리눅스 방화벽의 구조

패킷 필터링은 커널 안에 있으며, 이것이 netfilter입니다. netfilter는 후킹 지점(hook)을 제공합니다. 호스트로 들어오는 트래픽, 호스트에서 나가는 트래픽, 그리고 통과만 하는 트래픽(forward)입니다. 사용자 도구는 이 지점들에 대한 규칙을 커널에 기록할 뿐입니다.

도구수준언제 사용하나
ufw간단한 문법을 가진 래퍼우분투/데비안의 단일 서버, 일반적인 규칙
firewalld영역(zone)을 쓰는 래퍼RHEL, Rocky, Fedora
nftables (nft)저수준, 최신복잡한 규칙, NAT, 주소 집합
iptables저수준, 구식오래된 시스템과 스크립트

중요: 관리 도구는 하나만 쓰세요. ufw와 iptables를 동시에 손으로 고치면 규칙이 서로 덮어쓰게 되어 결과를 예측할 수 없습니다.

ufw 설치와 확인

우분투에는 보통 ufw가 이미 설치되어 있지만 꺼져 있습니다. 데비안에서는 따로 설치해야 합니다.

$ sudo apt update
$ sudo apt install ufw
$ sudo ufw status
Status: inactive

inactive는 ufw 규칙이 적용되지 않는다는 뜻입니다. 아직 켜지 마세요. 먼저 SSH를 허용해야 합니다. 그렇지 않으면 원격 서버에서 자기 자신의 접속을 끊게 됩니다.

단계별 ufw 설정

1단계. 기본 정책

기본 정책은 어떤 규칙에도 해당하지 않는 패킷을 어떻게 처리할지를 정합니다. 서버에 안전한 방식은 들어오는 것은 거부하고, 나가는 것은 허용하는 것입니다.

$ sudo ufw default deny incoming
Default incoming policy changed to 'deny'
$ sudo ufw default allow outgoing
Default outgoing policy changed to 'allow'

직접 시작한 외부 연결의 응답은 자동으로 통과합니다. ufw가 연결 상태를 추적(connection tracking)하기 때문에, 응답을 받기 위해 포트를 따로 열 필요는 없습니다.

2단계. SSH 허용

$ sudo ufw allow OpenSSH
Rules updated
Rules updated (v6)

OpenSSH는 애플리케이션 프로필입니다. SSH가 비표준 포트에서 동작한다면 명시적으로 허용하세요. 예: sudo ufw allow 2222/tcp. sshd가 실제로 어떤 포트를 쓰는지는 sudo ss -tlnp | grep sshd로 확인할 수 있습니다.

3단계. 서비스 포트 열기

$ sudo ufw allow 80/tcp
$ sudo ufw allow 443/tcp

프로토콜을 항상 지정하세요. /tcp를 생략하면 ufw가 TCP와 UDP를 모두 열므로, 필요 없는 문이 함께 열리게 됩니다.

4단계. 방화벽 켜기

$ sudo ufw enable
Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
Firewall is active and enabled on system startup

ufw는 즉시 실행되고 부팅할 때마다 자동으로 켜집니다. 현재 SSH 세션은 닫지 마세요. 새 터미널을 하나 더 열어 접속이 되는지 확인하세요.

5단계. 규칙 확인

$ sudo ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip

To                         Action      From
--                         ------      ----
22/tcp (OpenSSH)           ALLOW IN    Anywhere
80/tcp                     ALLOW IN    Anywhere
443/tcp                    ALLOW IN    Anywhere
22/tcp (OpenSSH (v6))      ALLOW IN    Anywhere (v6)
80/tcp (v6)                ALLOW IN    Anywhere (v6)
443/tcp (v6)               ALLOW IN    Anywhere (v6)

IPv6 규칙은 /etc/default/ufw에 IPV6=yes(기본값)가 설정되어 있으면 ufw가 자동으로 만듭니다.

ufw에서 포트 여는 모든 문법

ufw allow 명령은 짧은 형식과 전체 형식을 모두 지원합니다. 짧은 형식은 흔한 경우에 편리하고, 전체 형식은 출발지, 인터페이스, 목적지 주소를 지정해야 할 때 사용합니다.

작업명령어
TCP 포트sudo ufw allow 443/tcp
UDP 포트sudo ufw allow 51820/udp
포트 범위sudo ufw allow 6000:6007/tcp
서비스 이름으로sudo ufw allow https
한 IP에서만sudo ufw allow from 203.0.113.10
서브넷에서 포트로sudo ufw allow from 10.0.0.0/24 to any port 5432 proto tcp
특정 인터페이스에서sudo ufw allow in on eth1 to any port 3306 proto tcp
차단sudo ufw deny 23/tcp
응답과 함께 거절sudo ufw reject 25/tcp

deny와 reject의 차이: deny는 패킷을 조용히 버리므로 클라이언트는 시간 초과까지 기다립니다. reject는 포트가 닫혀 있다는 응답을 보내므로 클라이언트가 즉시 오류를 봅니다. 외부 인터넷에는 보통 deny를, 내부 네트워크에는 reject가 편리합니다. 클라이언트가 바로 원인을 알 수 있기 때문입니다.

데이터베이스를 한 서버에서만 접근하도록 제한

흔한 작업입니다. PostgreSQL이나 MySQL은 인터넷에서 보이면 안 되지만, 옆 서버의 애플리케이션은 접속할 수 있어야 합니다.

$ sudo ufw allow from 10.0.0.5 to any port 5432 proto tcp
Rule added

다른 모든 주소에서는 deny incoming 정책 덕분에 5432 포트가 닫혀 있습니다. 여기에 더해 DBMS 자체가 내부 주소에서만 수신하도록 설정하는 것이 좋습니다. 방화벽과 서비스 설정은 서로를 보완합니다.

애플리케이션 프로필

패키지는 자신의 포트 정보를 /etc/ufw/applications.d/에 둘 수 있습니다. 프로필 목록과 내용은 다음과 같이 확인합니다.

$ sudo ufw app list
Available applications:
  Nginx Full
  Nginx HTTP
  Nginx HTTPS
  OpenSSH
$ sudo ufw app info "Nginx Full"
Profile: Nginx Full
Title: Web Server (Nginx, HTTP + HTTPS)
Description: Small, but very powerful and efficient web server

Ports:
  80,443/tcp

프로필 허용: sudo ufw allow "Nginx Full". 공백이 있는 이름은 따옴표로 감싸세요. 직접 프로필을 만들 때는 같은 디렉터리에 INI 형식으로 적고 sudo ufw app update 이름으로 적용합니다.

ufw 규칙 삭제

가장 확실한 방법은 번호로 지우는 것입니다.

$ sudo ufw status numbered
Status: active

     To                         Action      From
     --                         ------      ----
[ 1] OpenSSH                    ALLOW IN    Anywhere
[ 2] 80/tcp                     ALLOW IN    Anywhere
[ 3] 443/tcp                    ALLOW IN    Anywhere
[ 4] OpenSSH (v6)               ALLOW IN    Anywhere (v6)
[ 5] 80/tcp (v6)                ALLOW IN    Anywhere (v6)
[ 6] 443/tcp (v6)               ALLOW IN    Anywhere (v6)

$ sudo ufw delete 2
Deleting:
 allow 80/tcp
Proceed with operation (y|n)? y
Rule deleted

규칙을 지우면 번호가 앞으로 당겨지므로, 다음 삭제 전에 다시 status numbered를 실행하세요. 두 번째 방법은 규칙을 delete와 함께 그대로 입력하는 것입니다. 예: sudo ufw delete allow 80/tcp. 이 방법은 IPv4와 IPv6 규칙을 한꺼번에 지웁니다.

규칙 순서와 맨 위에 끼워 넣기

ufw는 규칙을 위에서 아래로 검사하며, 처음 일치하는 규칙이 적용됩니다. 따라서 특정 주소에 대한 차단은 전체 허용보다 위에 있어야 합니다.

$ sudo ufw insert 1 deny from 198.51.100.23
Rule inserted

차단 규칙을 일반 명령으로 추가하면 목록 끝, 즉 allow 443/tcp 뒤에 놓이고, 그 포트에서는 동작하지 않습니다.

SSH 무차별 대입 방어: ufw limit

limit 규칙은 연결을 허용하되, 30초 안에 6회 이상 연결을 시도한 주소를 차단합니다.

$ sudo ufw limit OpenSSH
Rule updated

이것은 대량 비밀번호 대입 공격을 막는 간단한 방어입니다. 더 강력한 보호를 원한다면 키로만 로그인하도록 설정하고, 필요하면 fail2ban을 함께 쓰세요. fail2ban은 ufw를 통해 주소를 차단할 수 있습니다.

ufw 로그

로깅은 sudo ufw logging on으로 켭니다. 단계는 low, medium, high, full입니다. 기록은 /var/log/ufw.log(rsyslog를 통해)와 커널 로그에 남습니다.

$ sudo journalctl -k | grep 'UFW BLOCK' | tail -2
Oct 14 10:12:03 web kernel: [UFW BLOCK] IN=eth0 OUT= SRC=45.155.205.1 DST=192.0.2.10 PROTO=TCP SPT=51544 DPT=3389
Oct 14 10:12:09 web kernel: [UFW BLOCK] IN=eth0 OUT= SRC=89.248.165.7 DST=192.0.2.10 PROTO=TCP SPT=40312 DPT=23

SRC와 DPT 필드는 누가 어느 포트로 접속을 시도했는지 보여 줍니다. 서비스가 응답하지 않는데 로그에 그 포트의 UFW BLOCK이 있다면, 규칙을 추가하지 않은 것입니다.

ufw와 Docker: 규칙을 무시하고 열리는 포트

가장 잘 알려진 함정입니다. Docker는 포트를 게시할 때(-p 8080:80) NAT 규칙과 DOCKER 체인을 직접 추가합니다. 컨테이너로 가는 트래픽은 forward 경로를 타므로 ufw의 입력 규칙을 거치지 않습니다. 그 결과 ufw status에는 8080 포트가 보이지 않는데도 외부에서 접속할 수 있습니다.

해결 방법:

  • 포트를 localhost에만 게시하세요: -p 127.0.0.1:8080:80. 외부에는 리버스 프록시(nginx)를 통해 노출합니다.
  • Docker가 덮어쓰지 않는 DOCKER-USER 체인에 직접 규칙을 추가하세요.
  • 보조 컨테이너의 포트는 아예 게시하지 말고, Docker 네트워크로 연결하세요.

초기화와 비활성화

$ sudo ufw disable     # 끄기, 규칙은 유지
$ sudo ufw reset       # 모든 규칙 삭제 후 끄기
$ sudo ufw reload      # 설정 다시 읽기

reset 전에 ufw는 규칙 파일의 사본을 날짜 접미사를 붙여 /etc/ufw/에 보관합니다. 실제 규칙은 /etc/ufw/user.rules와 user6.rules에 저장되며, 이 파일은 손으로 고치지 말고 명령으로만 관리하세요.

nftables 기초

nftables는 리눅스 커널에서 iptables를 대체하는 최신 도구입니다. Debian 10 이상과 Ubuntu 20.10 이상에서는 iptables 명령이 기본적으로 호환 계층 iptables-nft를 통해 동작하므로, ufw의 규칙도 결국 nftables에 들어갑니다. 커널에 적재된 모든 규칙은 다음과 같이 확인합니다.

$ sudo nft list ruleset

nftables의 구조는 테이블(패밀리 ip, ip6, 또는 둘 다를 위한 inet)이 체인을 가지고, 체인이 규칙을 가지는 형태입니다. 체인은 netfilter의 후킹 지점에 연결됩니다. 서버용 최소 방화벽을 /etc/nftables.conf에 작성하면 다음과 같습니다.

#!/usr/sbin/nft -f
flush ruleset

table inet filter {
    chain input {
        type filter hook input priority 0; policy drop;
        ct state established,related accept
        ct state invalid drop
        iif "lo" accept
        ip protocol icmp accept
        meta l4proto ipv6-icmp accept
        tcp dport { 22, 80, 443 } accept
    }
    chain forward {
        type filter hook forward priority 0; policy drop;
    }
    chain output {
        type filter hook output priority 0; policy accept;
    }
}

적용하고 부팅 시 자동으로 켜기:

$ sudo nft -c -f /etc/nftables.conf   # 문법만 검사
$ sudo systemctl enable --now nftables

규칙은 실행 중에도 추가할 수 있지만, 파일에 기록하지 않으면 재부팅 후 사라집니다.

$ sudo nft add rule inet filter input tcp dport 5432 ip saddr 10.0.0.5 accept
$ sudo nft -a list chain inet filter input   # -a는 규칙의 handle을 표시
$ sudo nft delete rule inet filter input handle 9

nftables의 가장 큰 장점은 집합({ 22, 80, 443 })과 IPv4, IPv6를 함께 다루는 inet 테이블입니다. nftables 서비스를 ufw와 동시에 실행하지 마세요. flush ruleset이 ufw 규칙을 모두 지워 버립니다.

자주 하는 실수

  • SSH 규칙 없이 ufw를 켬 — 접속이 끊어집니다. 이때는 호스팅 업체의 콘솔(VNC/시리얼)로 들어가 sudo ufw allow OpenSSH를 실행하거나 sudo ufw disable을 하세요.
  • 포트는 열었는데 서비스에 접속되지 않음 — 서비스가 외부 주소가 아니라 127.0.0.1에서 수신 중입니다. ss -tlnp로 확인하세요.
  • 클라우드 방화벽이 따로 있음 — 많은 클라우드 업체에는 보안 그룹이 있습니다. 거기에서도 포트를 열어야 합니다.
  • deny가 동작하지 않음 — 차단 규칙이 허용 규칙 아래에 있습니다. insert 1을 사용하세요.

정리

기본적인 ufw 설정은 default deny incoming, SSH와 서비스 포트 허용, ufw enable, 그리고 status numbered로 확인하는 것입니다. 특정 대상만 허용하려면 from과 port를 쓰고, SSH에는 limit을 쓰세요. Docker를 쓴다면 Docker가 ufw를 우회한다는 점을 잊지 마세요. NAT나 복잡한 집합 규칙이 필요해지면 nftables로 넘어가세요. 서버 방화벽 운영은 리눅스 과정에서 실습으로 다룹니다.

자주 묻는 질문

ufw와 iptables는 어떻게 다른가요?

ufw는 간단한 문법을 제공하는 래퍼로, iptables나 nftables 규칙을 자동으로 생성합니다. iptables와 nftables는 저수준 도구로 완전한 제어가 가능하지만 더 많은 지식이 필요합니다.

재부팅 후에도 ufw 규칙이 유지되나요?

네. sudo ufw enable을 실행하면 방화벽이 부팅 시 자동으로 시작되고, 규칙은 /etc/ufw/user.rules와 user6.rules에 저장됩니다.

나가는 연결에도 포트를 열어야 하나요?

나가는 정책이 allow outgoing이면 필요 없습니다. ufw는 연결 상태 추적 덕분에 나가는 연결의 응답을 자동으로 통과시킵니다.

ufw를 켠 상태에서 ping을 허용하려면 어떻게 하나요?

기본적으로 ufw는 들어오는 ICMP echo 요청을 이미 허용합니다. 이 규칙은 /etc/ufw/before.rules에 있습니다. ping을 막으려면 그 파일에서 ACCEPT를 DROP으로 바꾸고 sudo ufw reload를 실행하세요.

CentOS나 Rocky Linux에서도 ufw를 쓸 수 있나요?

해당 배포판의 기본 도구는 firewalld와 firewall-cmd입니다. ufw는 EPEL에서 설치할 수 있지만 firewalld와 함께 섞어 쓰지 않는 것이 좋습니다.

영상으로 이 주제 배우기

이 주제는 “Linux: с нуля до уверенного администрирования” 강의에서 실습과 함께 자세히 다룹니다.

강의로 이동