13년차의 서버실

서버, 인프라, 홈랩 기술 블로그

[태그:] iptables

  • [Linux] 리눅스 서버 네트워크 연결 장애: IP 설정부터 방화벽까지 디버깅 5단계

    [Linux] 리눅스 서버 네트워크 연결 장애: IP 설정부터 방화벽까지 디버깅 5단계

    [리눅스] 리눅스 서버 네트워크 연결 장애: IP 설정부터 방화벽까지 디버깅 5단계

    리눅스 네트워크 장애는 평소엔 조용하다가 꼭 바쁠 때 터지더라고요. SSH는 안 붙고, 서비스 Health Check(헬스 체크, 상태 확인)는 빨갛게 뜨고, 팀 메신저에는 왜 서버가 안 되냐는 메시지가 쌓입니다. 저도 홈랩이랑 운영 서버를 만지면서 비슷한 상황을 정말 많이 겪었는데, 처음엔 케이블 문제인가 싶었는데, 실제로는 리눅스 IP 설정 하나가 꼬였던 적도 있었고, 반대로 IP는 멀쩡한데 iptables(아이피테이블스, 리눅스 패킷 필터) 규칙 때문에 통신이 막힌 적도 많았어요.

    그래서 오늘은 제가 실제로 쓰는 기준으로 리눅스 네트워크 장애를 좁혀 가는 5단계 디버깅 흐름을 정리해보겠습니다. 무작정 재부팅부터 하는 게 아니라, 아래에서 위로 차근차근 확인하는 방식이죠. 특히 Ubuntu 계열에서 자주 쓰는 netplan(넷플랜, 네트워크 설정 추상화 도구), systemd-networkd(시스템디 네트워크 관리 데몬), 그리고 방화벽 쪽의 nftables(엔에프테이블스, 최신 패킷 필터 프레임워크)까지 같이 보겠습니다.

    리눅스 네트워크 장애 점검 순서를 보여주는 전체 흐름도

    리눅스 서버에서 링크 상태, IP 설정, 라우팅, DNS, 방화벽 순서로 점검하는 전체 디버깅 흐름을 보여주는 개요 이미지입니다.

    리눅스 네트워크를 층으로 나눠 봐야 하는 이유

    쉽게 말해 네트워크 장애는 한 덩어리가 아니라 여러 층으로 나뉘어 있거든요. 물리 링크가 살아 있는지, 인터페이스가 Up(업, 활성화) 상태인지, IP가 붙었는지, 기본 게이트웨이(Default Gateway, 기본 경로)가 맞는지, 이름 해석 DNS가 되는지, 마지막으로 방화벽이 막고 있지는 않은지요. 여기서 중요한 포인트는 위 증상만 보고 아래 원인을 단정하면 삽질이 길어진다는 점입니다.

    제가 직접 해보니 제일 효율적인 방법은 이렇더라고요. 1단계에서 링크와 인터페이스를 확인하고, 2단계에서 리눅스 IP 설정을 보고, 3단계에서 라우팅과 DNS를 확인한 뒤, 4단계에서 netplan과 systemd-networkd를 보고, 마지막 5단계에서 iptables 또는 nftables를 점검하는 겁니다. 이 순서대로 보면 원인 범위가 빠르게 줄어들어요.

    리눅스 네트워크 장애 디버깅 5단계

    1단계. 링크 상태와 인터페이스 확인 (제일 많이 놓치는 부분)

    제일 먼저 보는 건 케이블, 가상 NIC, 스위치 포트, 인터페이스 상태입니다. 이 단계에서 해결되는 경우가 생각보다 많더라고요. 특히 VM(가상머신)에서는 가상 스위치 설정이, 베어메탈에서는 포트 비활성화가 원인일 때가 꽤 있거든요.

    ip link show
    ip -br link
    ethtool eth0
    

    여기서 보는 포인트는 간단해요.

    1. 인터페이스 상태가 UP인지 확인하세요.
    2. LOWER_UP 표시가 있는지 봐야 합니다. 이게 없으면 링크 자체가 안 잡힌 경우가 많거든요.
    3. ethtool로 Speed(속도), Duplex(이중화 모드), Link detected 여부를 봅니다.

    예를 들어 인터페이스가 내려가 있으면 이렇게 올릴 수 있어요.

    sudo ip link set eth0 up
    

    별거 아닌 것 같지만, 저도 예전에 테스트하다가 인터페이스를 내려놓고 그대로 잊어버려서 한참 헤맨 적이 있습니다. 진짜 허무했어요 ㅎㅎ

    2단계. 리눅스 IP 설정 확인

    다음은 IP 주소, 서브넷 마스크(Subnet Mask, 네트워크 범위 정보), DHCP(디에이치씨피, 자동 IP 할당) 여부를 확인하는 거예요. 여기서 리눅스 IP 설정이 의도와 다르게 잡혀 있으면 외부 통신이 바로 꼬입니다.

    ip addr show
    ip -br addr
    hostname -I
    

    출력에서 확인할 것은 아래입니다.

    • 예상한 인터페이스에 IP가 붙었는지
    • 대역이 맞는지 예: 192.168.10.0/24
    • 중복 IP 가능성은 없는지
    • DHCP로 받아야 하는 서버인데 주소가 비어 있지는 않은지

    Ubuntu 서버에서 netplan을 쓴다면 설정 파일은 보통 이렇게 생겨요.

    network:
      version: 2
      renderer: networkd
      ethernets:
        eth0:
          dhcp4: false
          addresses:
            - 192.168.10.20/24
          routes:
            - to: default
              via: 192.168.10.1
          nameservers:
            addresses:
              - 1.1.1.1
              - 8.8.8.8
    

    설정 반영은 아래처럼 하면 됩니다.

    sudo netplan generate
    sudo netplan apply
    

    처음엔 이게 뭔가 싶었는데, YAML(야믈, 들여쓰기 기반 설정 형식) 들여쓰기 하나만 틀려도 적용이 안 되더라고요. 그래서 저는 반영 전에 꼭 눈으로 한 번 더 봅니다.

    리눅스 IP 설정과 netplan 점검 장면

    netplan YAML 설정과 ip 명령 결과를 나란히 확인하는 실전 점검 장면을 담은 이미지입니다.

    3단계. 라우팅과 DNS 확인

    IP가 붙어 있어도 목적지로 가는 길이 없으면 통신이 안 되거든요. 그래서 라우팅 테이블(Route Table, 경로 정보)과 DNS를 따로 봐야 합니다. 이 구간에서 많이들 헷갈리세요. 핑은 IP로 되는데 도메인은 안 된다면 DNS 쪽일 가능성이 높고, 같은 대역은 되는데 외부가 안 된다면 기본 게이트웨이 문제일 가능성이 커요.

    ip route show
    ip route get 8.8.8.8
    ping -c 4 192.168.10.1
    ping -c 4 8.8.8.8
    getent hosts example.com
    resolvectl status
    

    제가 주로 이렇게 해석하더라고요.

    증상 가능성 높은 원인 우선 확인할 것
    게이트웨이 핑 실패 L2 링크, VLAN, 잘못된 IP 케이블, 스위치, 인터페이스 상태
    외부 IP 핑 실패 기본 라우트 누락, 업스트림 차단 ip route, 게이트웨이
    도메인만 실패 DNS 설정 오류 nameserver, resolvectl
    특정 포트만 실패 방화벽 또는 서비스 미기동 ss, iptables, nftables

    여기서 중요한 포인트! DNS 문제를 네트워크 전체 장애로 오해하는 경우가 정말 많아요. 실제로 써보니까 IP 통신과 이름 해석을 분리해서 보는 습관이 장애 시간을 많이 줄여줍니다.

    4단계. netplan, systemd-networkd 로그 확인

    설정 파일이 맞아 보여도 실제 적용 과정에서 실패할 수 있거든요. 특히 cloud-init(클라우드이닛, 초기 인스턴스 설정 도구)와 netplan이 같이 엮인 환경에서는 예상과 다르게 덮어써지는 경우도 있어요.

    networkctl status eth0
    systemctl status systemd-networkd
    journalctl -u systemd-networkd --no-pager
    sudo netplan try
    

    netplan try는 꽤 유용하더라고요. 원격 서버에서 네트워크 설정 바꿀 때 잘못 적용하면 SSH가 끊길 수 있거든요. 이 명령은 일정 시간 안에 확인하지 않으면 롤백되는 방식이라 조금 더 안전합니다.

    저도 홈랩에서 라우트를 바꾸다가 접속이 끊겨서 콘솔로 다시 들어간 적이 몇 번 있어요. 그 뒤로는 원격 작업에서는 무조건 보수적으로 갑니다. 가능하면 콘솔 접근 경로를 하나 확보하고 작업하시길 권장합니다.

    리눅스 네트워크 장애 원인을 systemd-networkd 로그로 분석하는 모습

    systemd-networkd 상태 출력과 journal 로그를 보며 적용 실패 원인을 찾는 디버깅 장면입니다.

    5단계. iptables, nftables, 서비스 포트 확인

    마지막 단계는 방화벽과 리스닝 포트(Listening Port, 대기 중인 서비스 포트)예요. 여기까지 왔는데도 통신이 안 되면, 사실 방화벽일 때가 꽤 많습니다. 특히 오래된 서버는 iptables를, 최근 배포판은 nftables를 쓰는 경우가 많아서 둘 다 확인해야 헷갈리지 않아요.

    sudo iptables -L -n -v
    sudo iptables -S
    sudo nft list ruleset
    ss -tulpn
    

    체크 포인트는 이렇습니다.

    • INPUT 체인 기본 정책이 DROP인지
    • SSH, HTTP, HTTPS 등 필요한 포트 허용 규칙이 있는지
    • 서비스가 실제로 해당 포트에서 listen 중인지
    • iptables와 nftables가 혼재되어 정책을 헷갈리게 만들고 있지는 않은지

    예를 들어 SSH 22/tcp가 막혀 있으면 서버는 살아 있어도 접속이 안 돼요. 반대로 방화벽은 열려 있는데 서비스가 안 떠 있으면 역시 안 되죠. 그래서 저는 항상 패킷 필터와 서비스 포트를 같이 봅니다.

    ⚠️ 실제로 자주 겪는 트러블슈팅 포인트

    여기서는 제가 삽질 좀 했던 사례를 중심으로 적어보겠습니다. 혹시 이런 경험 있으신가요? 겉으로는 리눅스 네트워크 장애처럼 보이는데, 실제 원인은 아주 사소한 설정 하나인 경우 말입니다.

    1. YAML 들여쓰기 오류
      netplan은 형식이 엄격해요. 공백 수가 틀리면 적용이 실패하거나 의도와 다르게 해석됩니다.
    2. 인터페이스 이름 혼동
      예전엔 eth0로 익숙했는데, 환경에 따라 ens18, enp1s0처럼 다르게 보여요. 설정 파일과 실제 NIC 이름이 다르면 당연히 안 붙습니다.
    3. 기본 라우트 누락
      같은 대역 통신만 되고 외부가 안 되는 전형적인 증상이죠.
    4. DNS 서버 미설정
      핑은 되는데 apt 업데이트나 도메인 접근이 안 됩니다.
    5. 방화벽 정책 잔존
      이전 작업에서 넣어둔 DROP 규칙이 그대로 남아 있는 경우가 있더라고요.

    이런 문제를 줄이려면 변경 전후 비교가 중요합니다. 저는 보통 현재 상태를 먼저 저장해 둬요.

    ip addr show
    ip route show
    resolvectl status
    sudo iptables -S
    sudo nft list ruleset
    

    그리고 변경 후에는 꼭 다시 비교합니다. 이 습관이 쌓이면 리눅스 네트워크 디버깅 속도가 확실히 빨라집니다.

    검증 방법: 어디까지 확인해야 정말 해결된 걸까요?

    설정만 반영됐다고 끝이 아닙니다. 리눅스 네트워크 장애는 재현이 사라진 것처럼 보여도 실제 서비스 경로가 여전히 막혀 있을 수 있거든요. 그래서 저는 최소한 아래 검증은 꼭 합니다.

    1. 게이트웨이 핑 확인
    2. 외부 IP 핑 확인
    3. 도메인 이름 해석 확인
    4. 필요 포트 접속 확인
    5. 서비스 로그와 클라이언트 관점 확인
    ping -c 2 192.168.10.1
    ping -c 2 8.8.8.8
    getent hosts example.com
    curl -I http://example.com
    nc -zv 127.0.0.1 22
    

    여기까지 다 통과하면 거의 끝입니다. 드디어 됐다! 싶은 순간이 오죠. 다만 운영 환경이라면 여기서 한 번 더, 다른 서버나 사용자 위치에서 역방향 확인도 해보시는 걸 권장합니다. 서버 안에서만 되는 경우가 있거든요.

    리눅스 네트워크 장애 복구 후 검증 화면

    ping, curl, 포트 체크 결과가 정상으로 돌아온 모습을 한눈에 보여주는 검증 이미지입니다.

    정리 표: 단계별 점검 포인트 한 번에 보기

    단계 확인 명령 핵심 질문
    1. 링크 ip link, ethtool 인터페이스와 물리 링크가 살아 있나?
    2. IP ip addr 리눅스 IP 설정이 맞게 붙었나?
    3. 라우팅/DNS ip route, getent, resolvectl 길이 있나? 이름 해석이 되나?
    4. 설정 적용 netplan, networkctl, journalctl 설정이 실제로 반영됐나?
    5. 방화벽/포트 iptables, nft, ss 패킷과 서비스 포트가 열려 있나?

    이 표만 머릿속에 넣어두셔도 현장에서 꽤 도움이 됩니다. 특히 초반에 당황해서 이것저것 동시에 건드리기 시작하면 원인 추적이 더 어려워져요. 순서대로, 하나씩, 확인한 사실만 쌓아가는 게 제일 빠릅니다.

    리눅스 네트워크 장애 5단계 디버깅 요약 인포그래픽

    링크, IP, 라우팅, 설정 적용, 방화벽 점검 순서를 한 장으로 요약한 체크리스트 이미지입니다.

    마무리: 리눅스 네트워크 장애는 감으로 풀기보다 순서로 푸는 게 낫습니다

    오늘 정리한 흐름의 핵심은 단순합니다. 리눅스 네트워크 장애가 생기면 링크, IP, 라우팅, DNS, 설정 적용, 방화벽 순서로 보자는 거예요. 저도 처음엔 여기저기 막 건드렸는데, 실제로 써보니까 장애 대응에서 제일 중요한 건 화려한 명령어보다 점검 순서였습니다.

    특히 netplan과 systemd-networkd를 쓰는 환경에서는 설정 파일과 적용 로그를 같이 봐야 하고, 구형 환경이나 혼재된 환경에서는 iptables와 nftables를 둘 다 체크해야 합니다. 이 부분만 익숙해져도 리눅스 네트워크 디버깅이 훨씬 덜 막막해집니다.

    다음 글에서는 tcpdump(티씨피덤프, 패킷 캡처 도구)로 패킷 흐름을 직접 보면서 원인을 좁히는 방법도 다뤄보겠습니다. 이전 글에서 다뤘던 홈랩 VLAN 구성 글이 있다면 같이 보셔도 흐름 이해에 도움이 됩니다. 현장에서 바로 써먹을 수 있는 기준으로 계속 정리해보겠습니다.

    자주 묻는 질문

    Q1. ping은 되는데 웹 접속만 안 되면 어디부터 봐야 하나요?

    A. 보통은 서비스 포트와 방화벽을 먼저 봅니다. ss -tulpn으로 리스닝 여부를 확인하고, 그다음 iptables 또는 nftables 규칙을 점검해보세요.

    Q2. netplan apply 전에 더 안전한 방법이 있나요?

    A. 원격 서버라면 netplan try를 먼저 권장합니다. 잘못 적용돼도 자동 롤백되는 흐름이라 실수 비용이 줄어듭니다.

    Q3. DNS 문제와 라우팅 문제는 어떻게 구분하나요?

    A. IP로는 되는데 도메인만 안 되면 DNS 문제일 가능성이 커요. 반대로 외부 IP 자체가 안 되면 라우팅이나 게이트웨이를 먼저 보시면 됩니다.

  • [Linux] iptables에서 nftables로 전환: 레거시 방화벽 마이그레이션 가이드

    [Linux] iptables에서 nftables로 전환: 레거시 방화벽 마이그레이션 가이드

    [Linux] iptables에서 nftables로 전환: 레거시 방화벽 마이그레이션 가이드

    리눅스 서버를 오래 운영하다 보면 방화벽 규칙이 점점 덕지덕지 붙는 순간이 오더라고요. 저도 홈랩과 실서버를 같이 굴리면서 iptables nftables 전환 작업을 몇 번 했었는데, 처음엔 “굳이 잘 돌아가는 걸 왜 바꿔?” 싶었어요. 근데 규칙이 늘어나고, IPv4와 IPv6를 따로 관리하고, 서비스별 예외가 계속 생기기 시작하면 이야기가 확 달라져요. 그때부터는 레거시 방화벽 구조가 발목을 잡기 시작하거든요. 이번 글에서는 iptables에서 nftables로 전환할 때 어떤 흐름으로 접근하면 덜 고생하는지, 제가 직접 해보며 삽질했던 포인트까지 묶어서 정리해보겠습니다.

    특히 이 글은 새로운 방화벽을 처음 설계하는 분보다는, 이미 운영 중인 규칙을 가진 상태에서 nftables 마이그레이션을 고민하는 분들께 맞춰 썼습니다. “규칙은 많은데 서비스 중단은 싫다”, “iptables-save 출력은 복잡한데 어떻게 옮겨야 할지 모르겠다” 같은 상황이 딱 여기에 해당합니다.

    iptables nftables 전환 아키텍처를 보여주는 리눅스 방화벽 마이그레이션 이미지

    레거시 체인과 테이블이 nftables의 통합 규칙셋으로 재구성되는 흐름을 보여주는 개요 이미지입니다.

    왜 지금 iptables에서 nftables로 전환해야 할까요

    쉽게 말해, nftables는 Linux 커널의 Netfilter(넷필터, 리눅스 패킷 필터링 프레임워크) 위에서 동작하는 더 현대적인 규칙 관리 방식이에요. 예전에는 iptables, ip6tables, arptables, ebtables처럼 도구가 나뉘어 있었는데, nftables 쪽은 이걸 훨씬 일관되게 다룰 수 있게 정리해둔 느낌이 강합니다.

    제가 직접 운영하면서 체감한 장점은 크게 세 가지였어요. 첫째, 규칙 표현이 훨씬 읽기 좋습니다. 둘째, IPv4와 IPv6를 한 구조 안에서 관리하기가 정말 편해요. 셋째, 집합(Set, 셋)과 맵(Map, 매핑) 같은 기능을 활용하면 반복 규칙이 크게 줄어듭니다. 예전엔 포트 하나 열 때마다 규칙을 늘어놓았는데, nftables에서는 묶어서 다루니까 관리가 훨씬 편하더라고요.

    항목 iptables nftables
    규칙 구조 테이블/체인/룰이 분산되어 장황해지기 쉬움 문법이 비교적 일관적이고 집합 활용이 쉬움
    IPv4/IPv6 관리 보통 분리 관리 inet 패밀리로 통합 관리 가능
    변환 도구 기존 자산 많음 iptables-translate 같은 변환 도구 활용 가능
    장기 운영성 레거시 환경과의 호환성은 좋음 새 규칙 설계와 정리에 유리

    여기서 중요한 포인트가 하나 있어요. iptables를 무조건 즉시 버리라는 뜻은 아닙니다. 실제 운영에서는 배포판 기본값, 커널 버전, 시스템 서비스와의 연동 방식까지 같이 봐야 하거든요. 다만 새로 정리할 기회가 있다면 nftables 쪽이 구조적으로 훨씬 낫다는 건 분명해요.

    iptables에서 nftables로 전환 전에 꼭 확인할 체크리스트

    저도 처음엔 규칙부터 바꾸려고 달려들었는데, 나중에 보니 그게 제일 위험한 접근이었어요. 리눅스 방화벽 교체 전에 아래 항목부터 확인하는 게 훨씬 안전합니다.

    1. 현재 규칙 백업
      현재 적용된 IPv4/IPv6 규칙을 반드시 저장합니다.
    2. 원격 접속 경로 확인
      SSH 관리 포트가 어디서 열려 있는지 먼저 확인합니다. 이거 놓치면 진짜 난감해집니다.
    3. 배포판의 기본 방화벽 스택 확인
      일부 환경은 iptables 명령이 내부적으로 nft 백엔드를 쓰기도 해요. 헷갈리기 쉬운 부분이죠.
    4. 자동 시작 서비스 확인
      재부팅 후 `nftables.service` 또는 배포판별 방화벽 서비스가 어떤 규칙을 로드하는지 봐야 합니다.
    5. 롤백 경로 준비
      문제가 생기면 바로 이전 규칙으로 되돌릴 방법을 준비해둡니다.

    예를 들어 현재 규칙 백업은 이렇게 해두면 돼요.

    sudo iptables-save > /root/iptables-backup.rules
    sudo ip6tables-save > /root/ip6tables-backup.rules

    그리고 nftables 쪽 현재 상태도 함께 확인해보세요.

    sudo nft list ruleset

    출력이 비어 있더라도 괜찮습니다. 중요한 건 지금 시스템이 무엇을 기준으로 패킷을 처리하는지 감을 잡는 거예요.

    nftables 개념, 쉽게 말해 이렇게 이해하면 됩니다

    저도 처음엔 `table`, `chain`, `hook` 같은 용어가 한꺼번에 나와서 좀 헷갈렸는데요. 쉽게 말해 이렇습니다.

    • Table(테이블): 규칙을 묶는 큰 서랍이에요.
    • Chain(체인): 실제 패킷이 지나가며 검사되는 규칙 목록입니다.
    • Hook(훅): 커널 네트워크 경로의 어느 지점에서 체인을 실행할지 정하는 연결점입니다.
    • Set(셋): IP, 포트 같은 값을 묶어 재사용하는 구조예요.

    iptables에서는 INPUT, OUTPUT, FORWARD 체인 중심으로 익숙해져 있다 보니 nftables 문법이 낯설게 느껴지는데, 구조를 이해하고 나면 오히려 더 명확합니다. 특히 `inet` 패밀리를 쓰면 IPv4와 IPv6 규칙을 함께 다룰 수 있어서, 실무에서 규칙 중복이 많이 줄어들어요.

    레거시 규칙을 그대로 옮기기보다 재설계가 필요한 이유

    이 부분이 꽤 중요합니다. iptables-translate는 분명 유용한 도구입니다. 하지만 “기계적으로 변환된 규칙 = 가장 좋은 nftables 규칙”은 아니더라고요. 번역은 되는데, 구조가 지저분하게 남는 경우가 많아요. 그래서 저는 보통 이렇게 접근합니다. 먼저 변환 도구로 초안을 만들고, 그다음 사람이 읽기 좋은 구조로 다시 정리합니다. 처음엔 귀찮아 보여도 장기적으로 훨씬 이득이에요.

    실전: iptables 규칙을 nftables로 마이그레이션하는 순서

    이제 본격적으로 옮겨보겠습니다. 여기서는 가장 흔한 서버 기준으로 설명할게요. SSH는 열어두고, 이미 확립된 연결은 유지하고, 기본 정책은 보수적으로 가져가는 흐름입니다.

    1. 현재 iptables 규칙 확인

    sudo iptables -S
    sudo ip6tables -S

    규칙을 읽어보면서 실제로 필요한 것과 과거 흔적을 구분하는 게 먼저예요. 저 같은 경우 예전에 테스트하다 남긴 포트 허용 규칙이 생각보다 많았습니다. 이런 건 옮기기 전에 정리하는 게 맞아요.

    2. 변환 초안 생성

    단일 규칙 한 줄은 `iptables-translate`로, 전체 저장본은 `iptables-restore-translate`로 보는 식으로 접근하면 편합니다.

    sudo iptables-translate -A INPUT -p tcp --dport 22 -j ACCEPT
    sudo iptables-save | sudo iptables-restore-translate

    출력 결과를 바로 적용하기보다, 먼저 파일로 저장해서 검토하는 쪽을 권장해요.

    sudo sh -c 'iptables-save | iptables-restore-translate > /root/translated.nft'

    여기서 중요한 포인트! 변환 결과를 그대로 믿지 마시고, 불필요하게 중복된 규칙이나 체인 구조를 꼭 점검하세요.

    iptables nftables 전환 과정에서 iptables-translate를 사용하는 터미널 이미지

    기존 iptables 규칙을 읽고 nftables 초안으로 변환하는 과정의 핵심 명령 흐름을 보여주는 이미지입니다.

    3. 사람이 읽기 좋은 nftables 규칙으로 정리

    제가 실제로는 아래처럼 새 파일을 다시 구성하는 편입니다. 예시는 가장 기본적인 서버용 필터 규칙셋입니다.

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

    이 규칙은 설명하기도 좋고, 나중에 봐도 의도가 비교적 분명합니다. `ct state established,related accept` 같은 부분은 기존 연결을 유지하는 데 아주 중요해서 빠뜨리면 안 돼요. 저도 초반에 이걸 빼먹고 “왜 응답 패킷이 이상하지?” 하며 한참 들여다봤었습니다.

    4. Set으로 반복 규칙 줄이기

    nftables의 진짜 장점은 이런 반복 제거에서 드러나요.

    table inet filter {
        set allowed_tcp_ports {
            type inet_service
            elements = { 22, 80, 443 }
        }
    
        chain input {
            type filter hook input priority 0;
            policy drop;
    
            iif lo accept
            ct state established,related accept
            tcp dport @allowed_tcp_ports accept
        }
    }

    포트가 많아질수록 이 방식이 진짜 편하더라고요. 나중에 하나 추가하거나 제거할 때도 눈에 잘 들어옵니다.

    5. 문법 검증 후 적용

    실제 적용 전에는 반드시 문법 검사를 먼저 해요.

    sudo nft -c -f /etc/nftables.conf

    이상 없으면 적용합니다.

    sudo nft -f /etc/nftables.conf

    그리고 자동 시작도 확인해둬요.

    sudo systemctl enable nftables
    sudo systemctl restart nftables

    배포판에 따라 서비스 이름이나 기본 설정 경로가 조금 다를 수 있으니, 이 부분은 현재 환경 기준으로 꼭 다시 확인하셔야 해요.

    ⚠️ 트러블슈팅: 제가 실제로 겪었던 문제들

    여기서부터가 실전입니다. 문서만 보면 금방 끝날 것 같지만, 실제로는 작은 차이 때문에 시간이 꽤 들어가요. 저도 삽질 좀 했습니다 ㅎㅎ

    SSH 접속이 갑자기 끊길 뻔한 경우

    가장 흔한 문제입니다. 기본 정책을 `drop`으로 바꿨는데 SSH 허용 규칙 순서가 뒤에 있거나, 아예 빠져 있으면 바로 위험해져요. 그래서 저는 항상 아래 순서를 지킵니다.

    1. 현재 접속 세션을 유지하는 `established,related` 규칙 먼저 추가
    2. SSH 허용 규칙 추가
    3. 그다음 기본 정책 적용

    이 순서를 지키면 사고 확률이 많이 줄어들어요.

    iptables와 nftables가 동시에 있는 것처럼 보이는 경우

    이건 처음 보면 꽤 혼란스러워요. 배포판에 따라 `iptables` 명령이 내부적으로 nft 기반 호환 레이어를 쓰는 경우가 있거든요. 그래서 “분명 nft로 바꿨는데 iptables 명령도 뭔가 보인다?” 같은 상황이 생깁니다. 이럴 땐 감으로 보지 말고, 실제 활성 규칙셋이 무엇인지 `nft list ruleset` 기준으로 확인하는 습관이 필요해요.

    변환은 됐는데 규칙이 너무 지저분한 경우

    이건 거의 반드시 겪어요. `iptables-translate`는 시작점으로는 훌륭하지만, 최종 결과물로 보기엔 장황한 경우가 많습니다. 여기서 시간을 조금 더 써서 `set`, `inet`, 상태 기반 매칭 중심으로 재구성하면 나중에 유지보수가 훨씬 쉬워져요. 제가 직접 써보니 이 단계가 가장 큰 차이를 만들었습니다.

    nftables 마이그레이션 시 set 기반 규칙 구성을 설명하는 리눅스 방화벽 이미지

    반복되는 포트 허용 규칙을 set으로 정리해 유지보수성을 높이는 구성을 시각화한 이미지입니다.

    검증: nftables 마이그레이션 후 무엇을 확인해야 할까요

    규칙 적용이 끝났다고 바로 마무리하면 안 돼요. 실제로 원하는 동작이 나오는지 검증이 꼭 필요합니다.

    기본 검증 명령

    sudo nft list ruleset
    sudo ss -tulpn
    sudo journalctl -u nftables --no-pager

    첫 번째는 현재 규칙셋 확인, 두 번째는 실제 리슨(Listen, 수신 대기) 포트 확인, 세 번째는 서비스 적용 로그 확인입니다. 저는 여기에 외부 호스트에서 실제 접속 테스트도 꼭 붙입니다. 서버 안에서만 보면 놓치는 게 있거든요.

    체크해야 할 항목

    • SSH, 웹, 모니터링 포트가 의도대로 열려 있는지
    • 허용하지 않은 포트가 막혀 있는지
    • 재부팅 후에도 동일한 규칙이 유지되는지
    • IPv6 트래픽도 의도대로 동작하는지

    특히 재부팅 검증은 꼭 해보세요. 적용은 됐는데 부팅 후 원래 규칙이 다시 올라오거나, 반대로 아무 규칙도 안 올라오는 경우가 있거든요. 저도 예전에 이걸 놓쳐서 “어제 분명 됐는데 왜 오늘 다르지?” 하며 로그를 뒤졌던 적이 있습니다.

    iptables nftables 전환 후 규칙 검증과 포트 확인을 보여주는 운영 점검 이미지

    적용된 규칙셋과 실제 서비스 포트 상태를 함께 확인하는 검증 단계의 결과 이미지입니다.

    iptables와 nftables, 어떤 방식으로 운영 정리하는 게 좋을까요

    운영 기준으로 보면 저는 이렇게 정리해요. 기존 시스템이 매우 안정적으로 돌고 있고 외부 의존성이 강하면, 한 번에 크게 바꾸기보다 단계적으로 옮기는 게 맞습니다. 반대로 규칙이 이미 복잡하고, 중복이 많고, 앞으로도 계속 확장될 예정이라면 nftables로 정리할 가치가 충분해요.

    운영 상황 권장 접근
    단순한 단일 서비스 서버 기본 규칙을 nftables로 재작성 후 빠르게 전환
    규칙이 많은 오래된 서버 iptables 백업 후 변환 초안 생성, 단계적 검증
    IPv4/IPv6 동시 운영 inet 테이블 중심으로 통합 설계
    반복 포트/주소 규칙이 많음 set과 map 활용으로 구조 단순화

    혹시 이런 경험 있으신가요? 규칙은 분명 맞는 것 같은데, 몇 달 뒤 다시 보면 왜 이렇게 짰는지 본인도 기억이 안 나는 경우요. 사실 리눅스 방화벽은 “작동만 하면 된다”가 아니라, 나중에 다시 읽어도 이해되는 구조가 정말 중요합니다. nftables는 바로 그 지점에서 큰 장점이 있어요.

    정리: 리눅스 방화벽 교체는 번역보다 재설계가 핵심입니다

    이번 iptables nftables 전환의 핵심은 단순 치환이 아닙니다. 리눅스 방화벽 교체를 한다고 생각하면, 기존 규칙을 점검하고, 필요한 것만 남기고, nftables 문법과 구조에 맞게 다시 정리하는 과정이 훨씬 중요합니다. `iptables-translate`는 좋은 출발점이지만, 최종 목적지는 결국 사람이 관리하기 좋은 규칙셋이어야 해요.

    제가 직접 해보니 가장 중요한 건 세 가지였습니다. 백업, 순서, 검증입니다. 백업 없이 시작하지 말 것. SSH 같은 필수 허용 규칙을 먼저 둘 것. 적용 후에는 반드시 외부에서 검증할 것. 이 세 가지만 지켜도 큰 사고를 많이 줄일 수 있어요.

    다음 글에서는 `nftables`에서 NAT(Network Address Translation, 네트워크 주소 변환) 규칙과 포트 포워딩을 정리하는 방법도 다뤄볼까 합니다. 홈랩 돌리시는 분들은 그쪽도 꽤 자주 쓰시거든요. 이전 글에서 다룬 리눅스 네트워크 기본 흐름과 함께 보시면 훨씬 이해가 잘 되실 겁니다.

    iptables nftables 전환 핵심 체크리스트를 요약한 인프라 운영 인포그래픽

    전환 이유, 절차, 검증 포인트를 한 번에 복습할 수 있도록 요약한 마무리 인포그래픽입니다.

    자주 묻는 질문

    iptables 규칙을 그대로 자동 변환해서 써도 될까요?

    가능은 하지만 권장하지는 않아요. 초안 생성에는 좋지만, 장기 운영을 생각하면 사람이 읽기 좋은 형태로 정리하는 게 훨씬 낫습니다.

    nftables 마이그레이션 시 가장 위험한 실수는 뭔가요?

    원격 접속 허용 규칙보다 먼저 기본 차단 정책을 적용하는 거예요. SSH 세션이 끊기면 복구가 번거로워집니다.

    IPv6를 안 쓰는 것 같으면 무시해도 될까요?

    환경에 따라 다르지만, 실제로는 IPv6가 살아 있는 경우가 적지 않아요. 인터페이스와 서비스 노출 상태를 먼저 확인해보는 게 안전합니다.

    netfilter를 꼭 깊게 알아야 하나요?

    커널 내부까지 깊게 파고들 필요는 없지만, 패킷이 어느 훅(hook)을 지나고 어느 체인에서 처리되는지 정도는 이해해두면 문제 해결이 훨씬 빨라져요.

  • [보안] 방화벽 설정, 이것만은 꼭! 10가지 필수 점검 항목

    [보안] 방화벽 설정, 이것만은 꼭! 10가지 필수 점검 항목

    [보안] 방화벽 설정, 이것만은 꼭! 10가지 필수 점검 항목

    서버를 올리고 포트만 열어둔 뒤 불안했던 경험, 아마 한 번쯤 있으실 겁니다. 저도 홈랩에서 테스트 서버를 굴리다가 방화벽 설정을 대충 해두고 넘어갔다가 나중에 로그를 보고 식은땀을 흘린 적이 있거든요. 방화벽 설정은 단순히 포트를 열고 닫는 작업이 아니라, 네트워크 보안의 기본선(baseline, 최소 보안 기준)을 만드는 일입니다. 이번 글에서는 실무와 홈랩에서 반복해서 확인하는 방화벽 보안 체크리스트 중심으로, 꼭 봐야 할 필수 점검 항목 10가지를 정리해보겠습니다.

    특히 이 글은 “방화벽 설정 후 뭘 먼저 확인해야 하지?” 하고 막막하신 분들을 위해 썼습니다. Linux 서버 기준 예시를 넣겠지만, 원칙 자체는 온프레미스(on-premise, 사내 구축 환경), 클라우드, 가상화 환경 모두에 그대로 적용됩니다.

    방화벽 설정이 적용된 홈랩 네트워크 아키텍처 이미지

    방화벽 설정 점검 항목이 DMZ, 내부망, 관리망으로 나뉘어 보이는 전체 개요 이미지입니다.

    1. 왜 방화벽 설정 점검은 항상 사고 전에 해야 할까요

    방화벽(Firewall, 트래픽을 제어하는 보안 장치)은 평소엔 조용합니다. 그래서 더 무섭습니다. 문제가 생기기 전까지는 존재감이 없거든요. 그런데 실제 장애나 침해사고 대응에서는 거의 항상 “이 포트 왜 열려 있었지?”, “관리 포트가 왜 외부에 노출됐지?” 같은 질문이 나옵니다.

    제가 직접 경험해보니 방화벽 설정 검토는 잘한 티는 안 나는데, 한 번 놓치면 문제가 됩니다. 특히 다음 상황에서 실수가 많이 나옵니다.

    • 테스트 서버를 운영 서버처럼 오래 끌고 갔을 때
    • 임시 오픈한 포트를 닫지 않았을 때
    • 소스 IP 제한 없이 관리 포트를 공개했을 때
    • 클라우드 보안 그룹(Security Group, 가상 방화벽)과 OS 방화벽 정책이 따로 놀 때

    여기서 중요한 포인트는 이것입니다: 방화벽은 “설정해뒀다”보다 “지금도 맞는 상태인지 확인하는 것”이 더 중요합니다.

    2. 좋은 방화벽 설정의 기본 원칙

    좋은 방화벽 설정은 결국 한 문장으로 정리됩니다: 필요한 통신만 허용하고, 나머지는 기본 차단(Default Deny, 기본 거부)하는 상태입니다. 이 원칙 하나만 제대로 잡아도 절반은 갑니다.

    방화벽 설정 점검 기준은 세 가지로 보면 편합니다.

    1. 누가 들어오나: 소스 IP, 네트워크 대역
    2. 어디로 들어오나: 목적지 포트, 서비스
    3. 왜 열어두나: 실제 업무 필요성, 운영 근거

    결국 방화벽 설정은 ACL(Access Control List, 접근 제어 목록)을 얼마나 명확하게 관리하느냐의 문제더라고요. “웹 서비스라서 80/443 허용”, “운영자만 SSH 22 접근”, “DB 3306은 앱 서버만 허용” 이런 식으로요.

    3. 방화벽 설정 점검 체크리스트: 필수 10가지 항목

    아래 10가지는 제가 서버 오픈 전에 거의 습관처럼 확인하는 필수 점검 항목입니다. 이 순서로 보면 빠르고, 빠뜨릴 것도 줄어듭니다.

    항목 무엇을 확인하나 핵심 이유
    1 기본 정책(Default Policy) 미정의 트래픽 차단
    2 허용 포트 최소화 공격 표면 축소
    3 관리 포트 접근 제한 SSH, RDP 등 보호
    4 소스 IP 대역 제한 불필요한 외부 접근 차단
    5 인바운드/아웃바운드 구분 양방향 정책 명확화
    6 불필요한 Any 허용 제거 과도한 허용 방지
    7 로그 활성화 추적과 감사 가능
    8 규칙 우선순위 확인 예상과 다른 매칭 방지
    9 예외 정책 문서화 운영 지속성 확보
    10 검증 명령과 정기 점검 설정 드리프트 방지

    보안 체크리스트는 화려할 필요 없습니다. 대신 반복 가능해야 합니다. 그리고 누가 봐도 같은 결론이 나와야 합니다.

    10가지 필수 항목을 짧게 풀어보면

    • 기본 정책은 deny: 허용보다 차단을 기본값으로 둡니다.
    • 포트는 최소한만: 서비스와 무관한 포트는 닫습니다.
    • 관리 포트는 외부 전체 공개 금지: Bastion(배스천, 중계 관리 서버)이나 VPN 뒤로 넣는 게 좋습니다.
    • 소스 제한: 가능하면 특정 사무실 IP나 관리망만 허용합니다.
    • 아웃바운드도 본다: 내부 서버가 외부로 아무 데나 나가는 것도 위험할 수 있습니다.
    • Any-Any 규칙 제거: 편하지만 위험합니다. 저도 급할 때 열었다가 나중에 후회했었습니다.
    • 로그는 꼭 남긴다: 침해 대응의 시작점입니다.
    • 우선순위 확인: 먼저 걸리는 규칙 때문에 뒤 규칙이 무의미해질 수 있습니다.
    • 예외는 기록: 왜 열었는지 안 적어두면 몇 달 뒤 아무도 모릅니다.
    • 정기 검증: 월 1회만 해도 효과가 큽니다.

    4. 실전 구현: Linux 서버의 방화벽 설정 기본

    여기서는 Ubuntu 계열에서 많이 쓰는 UFW(Uncomplicated Firewall, 간단 방화벽 관리 도구) 기준으로 보여드리겠습니다. 환경에 따라 nftables(리눅스 패킷 필터 프레임워크)나 iptables(전통적 패킷 필터)를 쓰실 수도 있는데, 원칙은 같습니다.

    1. 현재 열려 있는 서비스와 포트를 먼저 확인합니다.
    2. 기본 정책을 설정합니다.
    3. 서비스별 허용 규칙을 최소 권한으로 추가합니다.
    4. 로그와 검증을 수행합니다.
    ss -tulpn
    sudo ufw status verbose
    sudo ufw default deny incoming
    sudo ufw default allow outgoing
    sudo ufw allow 80/tcp
    sudo ufw allow 443/tcp
    sudo ufw allow from 203.0.113.10 to any port 22 proto tcp
    sudo ufw logging on
    sudo ufw enable
    sudo ufw status numbered

    위 예시는 정말 기본형입니다. 웹 서버라면 80/443만, SSH는 관리 IP에서만 허용하는 식이죠. 실제로 써보니까 처음부터 이렇게 보수적으로 잡는 게 나중에 훨씬 편하더라고요.

    서비스별 방화벽 설정 기준

    서비스 권장 접근 방식 비고
    SSH 특정 관리 IP만 허용 가능하면 VPN 뒤에서 접근
    HTTP/HTTPS 외부 공개 허용 리버스 프록시 뒤 구성 가능
    DB 포트 앱 서버 IP만 허용 인터넷 직접 공개 지양
    모니터링 포트 관리망만 허용 Prometheus 등 내부 수집 권장
    방화벽 설정에서 허용 포트를 구분한 서버 구성 이미지

    웹 포트와 관리 포트가 구분되어 보이는 실전 방화벽 설정 예시 이미지입니다.

    5. 더 실무적으로: 인바운드와 아웃바운드를 함께 점검하세요

    많이 놓치는 부분이 아웃바운드(Outbound, 서버에서 외부로 나가는 트래픽)입니다. 인바운드만 막아두면 끝이라고 생각하기 쉬운데, 실제 운영에서는 그렇지 않거든요. 만약 서버가 침해당했다면 외부 C2(Command and Control, 원격 제어 서버)와 통신을 시도할 수도 있습니다.

    그래서 저는 최소한 아래는 꼭 봅니다.

    • 패키지 저장소, 시간 동기화, 외부 API 등이 꼭 필요한 목적지인지
    • 내부 서버가 임의 포트로 외부에 나가고 있지 않은지
    • 백업, 모니터링, 알림 전송 경로가 정책과 충돌하지 않는지

    예를 들어 DB 서버는 인터넷으로 직접 나갈 이유가 거의 없습니다. 그런 서버는 아웃바운드 정책도 보수적으로 잡는 편이 낫습니다.

    sudo ufw default deny outgoing
    sudo ufw allow out 53
    sudo ufw allow out 123/udp
    sudo ufw allow out 443/tcp
    sudo ufw status verbose

    물론 이렇게 하면 처음엔 업데이트나 에이전트 통신이 막혀서 좀 삽질했습니다. ㅎㅎ 그래서 운영 서버에 바로 적용하기보다는, 먼저 필요한 통신 목록부터 뽑아보시는 걸 추천드립니다.

    6. 규칙 우선순위, 로그, 문서화: 운영 품질을 갈라놓는 세 가지

    방화벽 설정이 당장은 맞아 보여도, 시간이 지나면 예외 규칙이 늘어나면서 복잡해집니다. 그때 진짜 차이가 나는 게 규칙 우선순위(rule order), 로그(logging), 문서화(documentation)입니다.

    규칙 우선순위 확인하기

    특히 iptables나 클라우드 ACL에서는 먼저 매칭된 규칙이 적용되는 경우가 많습니다. 그래서 아래처럼 번호나 순서를 보고 정리해야 합니다.

    sudo ufw status numbered

    분명 차단 규칙을 넣었는데 접속이 되는 경험 있으신가요? 나중에 보면 위쪽에 더 넓은 허용 규칙이 먼저 있더라고요. 저도 처음엔 꽤 헷갈렸습니다.

    로그는 최소한 이 정도는 남기세요

    • 거부된 접속 시도
    • 관리 포트 접근 시도
    • 비정상적으로 반복되는 스캔 패턴

    로그가 있어야 Fail2ban 같은 자동 방어 도구를 붙이기도 쉽고, 나중에 보안 체크리스트 점검 결과를 설명하기도 편합니다.

    예외 정책은 꼭 기록하세요

    예를 들어 “협력사 고정 IP에서만 임시 허용, 만료일은 2026-08-03” 같은 걸 남겨야 합니다. 안 그러면 임시가 영구가 됩니다. 이거 진짜 자주 봅니다.

    방화벽 설정 결과를 모니터링하는 네트워크 보안 대시보드 이미지

    차단 로그와 관리 포트 접근 시도가 보이는 결과 검증용 모니터링 이미지입니다.

    7. ⚠️ 실제로 겪었던 문제들: 방화벽 트러블슈팅 포인트

    실무든 홈랩이든 방화벽 설정에서 가장 무서운 건 “서비스를 보호하려다가 내가 못 들어가는 상황”입니다. 저도 원격지 장비에서 SSH를 제 손으로 막아본 적 있습니다. “드디어 됐다!” 하고 나갔는데 다시 접속이 안 되더라고요.

    자주 겪는 문제 1: SSH를 너무 빨리 막음

    해결 방법은 간단합니다: 현재 접속 중인 세션을 유지한 상태에서 새 세션으로 재접속 테스트를 먼저 하세요.

    sudo ufw allow from 203.0.113.10 to any port 22 proto tcp
    ssh user@server-ip

    새 세션 접속이 확인되기 전에는 기존 세션을 끊지 않는 게 안전합니다.

    자주 겪는 문제 2: 클라우드 방화벽과 OS 방화벽이 충돌

    AWS Security Group, GCP VPC Firewall, Azure NSG 같은 상위 정책과 OS 내부 정책이 둘 다 있으면, 어디서 막히는지 헷갈립니다. 이런 경우는 아래 순서로 확인하면 됩니다.

    1. 클라우드 보안 그룹에서 허용 여부 확인
    2. OS 방화벽에서 동일 포트 허용 여부 확인
    3. 서비스 데몬이 실제로 Listen(포트 대기) 중인지 확인
    4. 라우팅과 NAT(Network Address Translation, 주소 변환) 경로 확인
    ss -tulpn
    sudo ufw status verbose
    ip addr
    ip route

    자주 겪는 문제 3: IPv4만 보고 끝냄

    이 부분도 은근히 놓칩니다. IPv6이 활성화된 환경이라면 IPv4 규칙만 보고 안심하면 안 됩니다. 방화벽 설정 점검 시 IPv6 정책도 같이 봐야 합니다.

    8. 검증 방법: 설정했으면 반드시 확인하세요

    보안은 추측으로 끝내면 안 됩니다. 설정 후에는 꼭 검증해야 합니다. 저는 보통 서버 내부 확인과 외부 확인을 나눠서 봅니다.

    서버 내부에서 확인

    sudo ufw status verbose
    sudo ufw status numbered
    ss -tulpn
    • 열려 있어야 하는 포트만 열려 있는지
    • 허용 대상 IP가 의도와 맞는지
    • 기본 정책이 incoming deny인지

    외부에서 확인

    nc -vz server-ip 22
    nc -vz server-ip 80
    nc -vz server-ip 443

    관리 PC에서는 열려야 하고, 허용되지 않은 위치에서는 막혀야 정상입니다. 이 차이를 직접 확인해보면 훨씬 마음이 놓입니다.

    방화벽 설정 검증 체크리스트

    1. 기본 정책이 차단 중심인지 확인
    2. 불필요한 포트가 남아 있지 않은지 확인
    3. 관리 포트가 특정 IP로 제한됐는지 확인
    4. 로그가 남고 있는지 확인
    5. 예외 정책이 문서화됐는지 확인

    이 정도만 해도 네트워크 보안 수준이 꽤 안정됩니다. 화려한 장비보다 이런 기본기가 훨씬 오래 갑니다.

    방화벽 설정 필수 점검 항목을 요약한 네트워크 보안 이미지

    점검 전후 차이와 필수 점검 항목 10가지를 한눈에 보여주는 요약 이미지입니다.

    9. 정리: 방화벽 설정은 결국 운영 습관입니다

    정리하면 방화벽 설정의 핵심은 세 가지입니다: 기본 차단, 최소 허용, 지속 검증. 사실 특별한 비법은 없습니다. 대신 귀찮아도 반복해야 합니다. 저도 처음엔 규칙 몇 개 넣고 끝내려 했었는데, 시간이 지나 보니 결국 방화벽 보안 체크리스트를 얼마나 성실하게 돌리느냐가 차이를 만들더라고요.

    다음 글에서는 홈랩 기준으로 리버스 프록시(역방향 프록시)와 방화벽을 함께 구성하는 방법도 다뤄볼 예정입니다. 이전 글에서 다뤘던 로그 모니터링 내용과 연결해서 보시면 더 이해가 쉬우실 겁니다.

    자주 묻는 질문

    Q. 방화벽 설정은 서버마다 다르게 해야 하나요?
    네, 서비스 역할이 다르면 달라져야 합니다. 웹 서버, DB 서버, 모니터링 서버는 필요한 포트와 접근 주체가 다르거든요.

    Q. 클라우드 보안 그룹만 있으면 OS 방화벽은 안 써도 되나요?
    환경에 따라 다르지만, 저는 이중으로 두는 편입니다. 방어 계층(다층 방어)이 생기기 때문입니다.

    Q. 제일 먼저 바꿔야 할 한 가지는 뭔가요?
    기본 정책을 정리하고, SSH 같은 관리 포트의 소스 IP 제한부터 거세요. 체감 효과가 가장 큽니다.

    마지막으로 한 줄만 드리면, 방화벽 설정은 한 번 잘해두는 작업이 아니라 계속 점검하는 운영 루�ine입니다. 오늘 서버 한 대라도 체크리스트대로 다시 보시면 분명 놓친 게 하나쯤은 보일 겁니다.

  • [k8s] GKE WireGuard 네트워크 장애: 디버깅 및 해결 사례 연구

    [k8s] GKE WireGuard 네트워크 장애: 디버깅 및 해결 사례 연구

    [k8s] GKE WireGuard 네트워크 장애: 디버깅 및 해결 사례 연구

    안녕하세요, 13년차 서버실 지킴이입니다. 오늘은 제가 최근 GKE (Google Kubernetes Engine) 환경에서 WireGuard를 쓰다가 겪었던 네트워크 장애 경험과, 그걸 해결하기 위해 삽질했던 과정을 솔직하게 공유해볼게요. 😅 아마 인프라 엔지니어 분들이라면 누구나 한 번쯤은 복잡한 네트워크 문제로 밤샘 디버깅을 해본 경험이 있으실 거더라고요. 특히 쿠버네티스(Kubernetes) 같은 동적인 환경에서는 예측 불가능한 변수가 많아 정말 골치 아플 때가 많습니다. 저도 이번에 제대로 당했는데, 결국 해결하고 나니 또 하나 배우는 게 있네요. 💡

    이번 글에서는 GKE에서 WireGuard를 운영하면서 발생했던 특정 네트워크 통신 문제를 어떻게 발견하고, 어떤 도구들을 사용해서 원인을 분석했으며, 최종적으로 어떤 방법으로 해결했는지 자세히 이야기해 드릴게요. 혹시 비슷한 문제로 고민하고 계신 분들에게 작은 도움이 되었으면 좋겠습니다.

    GKE 클러스터와 WireGuard VPN 터널이 연결된 전체 아키텍처의 개념도입니다.

    GKE와 WireGuard, 왜 함께 쓰려 했을까요?

    먼저, 왜 제가 GKE 클러스터에 WireGuard를 도입하려고 했는지부터 말씀드려야겠네요. 사실 GKE 자체적으로도 훌륭한 네트워크 기능들을 제공합니다. VPC Native 클러스터는 Private IP를 사용하고, Cloud VPN이나 Interconnect를 통해 온프레미스(On-Premise) 환경과도 쉽게 연결할 수 있거든요. 그럼에도 불구하고 제가 WireGuard를 선택한 이유는 크게 두 가지였습니다.

    • 성능과 간결함 (Performance & Simplicity): WireGuard는 다른 VPN 솔루션들에 비해 오버헤드(Overhead)가 정말 적고, 설정도 간결하더라고요. 홈랩에서 다양한 테스트를 해봤는데 성능이 정말 만족스러웠거든요. 복잡한 IPsec 설정에 비하면 정말 ‘혁명’ 같았습니다.
    • 특정 서비스의 보안 강화 및 멀티 클러스터 네트워킹: 특정 마이크로서비스(Microservice) 간의 통신을 더욱 안전하게 암호화하고, 나아가 다른 클라우드 환경이나 온프레미스에 있는 또 다른 쿠버네티스 클러스터와 안전하게 연결하고 싶었습니다. GKE의 기본 네트워킹을 보완하는 개념으로 접근한 거죠.

    쉽게 말해, GKE의 기본 네트워크 인프라는 유지하되, 그 위에 특정 트래픽에 대해서만 WireGuard 터널(Tunnel)을 만들어서 보안과 유연성을 동시에 잡으려는 시도였습니다. 🚀

    GKE에 WireGuard 실전 구현 (그리고 문제의 시작)

    GKE에 WireGuard를 배포하는 방법은 여러 가지가 있겠지만, 저는 각 노드(Node)에 WireGuard 인터페이스를 생성하고 설정을 유지하기 위해 DaemonSet을 활용했습니다. DaemonSet은 모든 노드에 파드(Pod)를 하나씩 배포해주니까, WireGuard를 인프라 레벨에서 관리하기에 딱 맞겠다 싶었거든요.

    간단한 흐름은 이렇습니다.

    1. WireGuard 커널 모듈이 설치된 베이스 이미지 준비 (또는 initContainer 활용).
    2. 각 노드의 파드에서 WireGuard 인터페이스(wg0) 생성 및 설정.
    3. 다른 피어(Peer)들과의 연결 설정 (PublicKey, Endpoint, AllowedIPs).
    4. 필요한 라우팅 테이블(Routing Table) 및 iptables 규칙 추가.

    초기 배포는 나름 순조로웠습니다. DaemonSet으로 WireGuard 파드를 띄우고, 각 노드에 wg0 인터페이스가 잘 생성되는 것을 확인했거든요. kubectl exec -it [wireguard-pod] -- wg show 명령어로 상태를 확인해보니 피어들과의 핸드셰이크(Handshake)도 정상적으로 이루어지는 것처럼 보였어요. 🎉

    apiVersion: apps/v1
    kind: DaemonSet
    metadata:
      name: wireguard-node
      namespace: kube-system
    spec:
      selector:
        matchLabels:
          app: wireguard-node
      template:
        metadata:
          labels:
            app: wireguard-node
        spec:
          hostNetwork: true # 노드 네트워크 직접 사용
          hostPID: true # 프로세스 네임스페이스 공유
          containers:
          - name: wireguard
            image: <your-wireguard-image>
            securityContext:
              privileged: true # 특권 모드 활성화
              capabilities:
                add:
                - NET_ADMIN
                - SYS_MODULE
            volumeMounts:
            - name: lib-modules
              mountPath: /lib/modules
              readOnly: true
            - name: wg-config
              mountPath: /etc/wireguard
            command: ["sh", "-c"]
            args: 
              - |-
                # WireGuard 커널 모듈 로드
                modprobe wireguard
                # WireGuard 인터페이스 및 라우팅 설정 스크립트 실행
                /usr/local/bin/setup_wireguard.sh
          volumes:
          - name: lib-modules
            hostPath:
              path: /lib/modules
          - name: wg-config
            configMap:
              name: wireguard-config
              items:
              - key: wg0.conf
                path: wg0.conf
    

    이렇게 설정하고, ConfigMap에 각 노드의 WireGuard 설정 파일(wg0.conf)을 넣어서 배포했습니다. 초기에는 내부 서비스 간 통신이나 외부 WireGuard 피어와의 통신도 잘 되는 듯했어요. 그런데….

    GKE DaemonSet을 이용한 WireGuard 설정 YAML과 WireGuard `wg0.conf` 파일 예시

    WireGuard 설정을 위한 DaemonSet YAML과 wg0.conf 파일의 구성 예시입니다.

    ⚠️ 삽질의 시작: 네트워크 장애 발생 및 쿠버네티스 트러블슈팅

    문제는 GKE 노드가 재시작되거나, 특정 네트워크 이벤트를 겪은 후에 발생했습니다. 갑자기 WireGuard 터널을 통해 통신해야 하는 파드들이 서로 연결되지 않는 현상이 나타나기 시작한 겁니다. 처음엔 ‘이게 뭔가?’ 싶었죠. 🤯

    증상

    • WireGuard 터널을 사용하는 파드(Pod) 간의 통신 불능.
    • wg show 명령으로는 터널이 up 상태이고 피어들도 연결된 것처럼 보임.
    • 하지만 ping이나 curl 같은 기본적인 네트워크 명령어조차 실패.

    디버깅 과정

    저는 다음과 같은 단계로 디버깅을 시작했습니다.

    1. wg show 확인: 가장 먼저 WireGuard 자체의 상태를 확인했습니다. 아까 말씀드렸듯이, 터널은 정상적으로 보였거든요. latest handshake 시간도 최근으로 업데이트되고 있었고요.
    2. ip a, ip route 확인: WireGuard 인터페이스(wg0)가 정상적으로 IP를 가지고 있는지, 그리고 WireGuard 서브넷(Subnet)으로 가는 라우팅 테이블이 제대로 설정되어 있는지 확인했습니다. 여기서 이상한 점을 발견했습니다. 특정 노드에서는 WireGuard 서브넷으로 가는 라우팅 규칙이 사라져 있거나, GKE의 자체 네트워킹과 충돌하는 듯한 규칙이 혼재되어 있었습니다.
    3. iptables -L -n -v 확인: 라우팅 문제가 아니라 iptables 규칙 문제일 수도 있겠다 싶어서 확인해봤거든요. GKE는 자체적으로 복잡한 iptables 규칙들을 관리하는데, 제가 추가한 WireGuard 관련 규칙들이 제대로 적용되지 않거나, GKE의 규칙에 의해 덮어씌워지는 경우가 있었습니다. 특히 FORWARD 체인에서 문제가 발생할 가능성이 높다고 판단했어요.
    4. tcpdump 활용: 실제 패킷(Packet)이 어디까지 도달하는지 확인하기 위해 tcpdump를 사용했습니다. WireGuard 인터페이스(wg0)와 물리 인터페이스(eth0 등)에서 동시에 패킷을 캡처해보니, 파드에서 WireGuard 서브넷으로 나가는 패킷은 wg0으로 들어오지만, 실제 암호화되어 외부로 나가는 트래픽이 없거나, 혹은 돌아오는 응답이 중간에 사라지는 것을 확인했습니다.
    5. GKE 노드 재시작 테스트: 가장 결정적인 단서는 GKE 노드를 재시작할 때마다 문제가 발생한다는 것이었어요. 노드가 재시작되면 WireGuard DaemonSet 파드도 다시 시작되지만, 그 과정에서 네트워크 설정의 영속성(Persistence)이 깨지는 것이 분명했거든요.

    원인 분석: GKE의 자체 네트워킹과 WireGuard의 미묘한 충돌

    결론적으로 원인은 GKE의 자체 네트워킹 시스템과 WireGuard의 라우팅 및 iptables 설정이 충돌하거나, 혹은 부팅 시점의 Race Condition 때문이라는 것을 알게 되었습니다. GKE는 각 노드에 파드 IP를 할당하고, 복잡한 라우팅과 iptables 규칙을 관리합니다. 여기에 WireGuard가 별도의 터널 인터페이스를 만들고 라우팅 규칙을 추가하면서, 특정 상황에서 GKE의 기존 규칙이 WireGuard 규칙을 덮어쓰거나, WireGuard 규칙이 GKE 트래픽을 예상치 못한 곳으로 보내버리는 문제가 발생했던 거였거든요.

    특히 노드 재시작 시, GKE의 네트워킹이 먼저 설정을 완료하고 나서 WireGuard DaemonSet이 동작하면서, WireGuard가 추가하는 규칙들이 제대로 자리 잡지 못하는 경우가 있었습니다. 😩

    ✅ 해결책: 네트워크 설정의 영속성 확보

    이 문제를 해결하기 위해 여러 방법을 시도해봤는데, 가장 효과적이었던 방법은 네트워크 설정의 영속성을 강화하고, WireGuard 설정 스크립트가 GKE의 네트워킹 초기화 이후에 안정적으로 실행되도록 하는 것이었어요.

    1. PostUp/PostDown 스크립트 활용 및 영속성 강화

    WireGuard는 wg0.conf 파일 내에 PostUp 및 PostDown 명령어를 정의할 수 있습니다. 저는 여기에 필요한 라우팅 규칙과 iptables 규칙을 명시적으로 추가하고, wg-quick up wg0 명령어를 통해 이 스크립트들이 실행되도록 했습니다. DaemonSet의 command 섹션에서 이 부분을 좀 더 견고하게 만들었거든요.

    # /usr/local/bin/setup_wireguard.sh 내용 예시
    
    #!/bin/bash
    
    # WireGuard 커널 모듈 로드
    modprobe wireguard
    
    # GKE CNI 초기화를 위해 잠시 대기 (노드마다 다를 수 있음)
    sleep 10
    
    # /etc/wireguard/wg0.conf 파일에 PostUp/PostDown 설정 포함
    # 예시: PostUp = ip route add 10.10.0.0/16 dev wg0; iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT
    
    # WireGuard 인터페이스 활성화
    wg-quick up wg0
    
    # 설정이 유실되지 않도록 주기적으로 모니터링
    while true; do
      sleep 300
      # 필요시 wg0 상태 체크 및 재설정 로직
    done
    

    또한, WireGuard DaemonSet 파드가 시작될 때마다 이 setup_wireguard.sh 스크립트가 실행되도록 하고, 스크립트 내부에서 GKE 네트워킹이 완전히 준비될 때까지 일정 시간 대기하는 로직을 추가했습니다.

    2. AllowedIPs 정확한 설정

    wg0.conf 파일의 각 피어(Peer)에 대한 AllowedIPs 설정을 더욱 정확하게 지정했어요. 예를 들어, 특정 피어의 WireGuard IP만 허용하는 것이 아니라, 해당 피어 뒤에 있는 서브넷 전체를 AllowedIPs에 포함시켜 라우팅이 명확하게 이루어지도록 했습니다. 이건 WireGuard가 패킷을 어디로 포워딩할지 결정하는 정말 중요한 요소거든요.

    [Peer]
    PublicKey = <peer-public-key>
    Endpoint = <peer-endpoint>:51820
    AllowedIPs = 10.10.0.0/16, 192.168.10.0/24 # 정확한 서브넷 명시
    PersistentKeepalive = 25
    

    이 두 가지 방법을 적용하고 GKE 노드를 재부팅해보니, 드디어 문제가 해결되었습니다! 🎉 재부팅 후에도 WireGuard 터널을 통한 통신이 정상적으로 이루어졌고, 파드 간 연결도 원활했습니다. 정말이지 며칠 밤낮으로 씨름했던 문제가 해결되니 얼마나 기뻤는지 모릅니다.

    WireGuard 터널의 정상 작동을 확인하는 `wg show` 명령어 출력 및 네트워크 트래픽 모니터링 그래프

    WireGuard 터널의 정상 작동을 확인하는 wg show 명령어 출력과 정상적인 네트워크 트래픽 그래프입니다.

    마무리하며: GKE와 커스텀 네트워크 솔루션 통합 시 교훈

    이번 GKE WireGuard 네트워크 장애 해결 사례를 통해 제가 얻은 교훈은 다음과 같습니다.

    1. 관리형 서비스와 커스텀 솔루션의 경계 이해: GKE 같은 관리형 쿠버네티스 서비스는 자체적인 네트워크 관리 로직을 가지고 있습니다. 여기에 WireGuard 같은 커스텀 네트워크 솔루션을 통합할 때는 GKE의 기본 네트워킹 동작 방식을 정확히 이해하고, 충돌이 발생하지 않도록 주의해야 합니다. 특히 라우팅 테이블과 iptables 규칙은 가장 민감한 부분이거든요.
    2. 네트워크 설정의 영속성 확보: 쿠버네티스 노드는 언제든 재시작될 수 있습니다. 이때 수동으로 설정했던 네트워크 규칙들이 유실되지 않도록 DaemonSet의 command나 initContainer를 통해 영속적인 스크립트 실행 환경을 구축하는 것이 정말 중요합니다.
    3. 꼼꼼한 네트워크 디버깅 도구 활용: ip route, iptables, tcpdump, wg show 등 기본적인 네트워크 디버깅 도구들은 아무리 강조해도 지나치지 않습니다. 눈에 보이는 문제가 아니라, 실제 패킷의 흐름을 추적하는 것이 문제 해결의 핵심이거든요.
    4. 문서화 및 철저한 테스트: 복잡한 네트워크 설정을 적용할 때는 반드시 상세한 문서화를 해두고, 노드 재시작 등 다양한 시나리오에서 충분한 테스트를 거쳐야 합니다. 저도 이번에 테스트 시나리오를 좀 더 촘촘히 짰어야 했는데, 초기 검증이 미흡했던 점이 아쉬웠어요.

    GKE 운영 환경에서 커스텀 네트워크 솔루션을 도입하는 것은 분명 도전적인 일이지만, 잘만 활용하면 서비스의 유연성과 보안을 크게 향상시킬 수 있습니다. 이번 삽질 경험이 여러분의 GKE 운영에 작은 도움이 되기를 바라며, 다음번에는 또 다른 홈랩 삽질기를 들고 찾아오겠습니다! 😊

    GKE WireGuard 통합 시 고려해야 할 중요 체크리스트 인포그래픽

    GKE와 WireGuard를 통합할 때 고려해야 할 핵심 사항들을 요약한 체크리스트 인포그래픽입니다.