13년차의 서버실

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

[태그:] 리눅스 서버 보안

  • [Linux] firewalld 보안 체크리스트: Linux 서버 방화벽 설정 모범 사례

    [Linux] firewalld 보안 체크리스트: Linux 서버 방화벽 설정 모범 사례

    firewalld 보안 체크리스트: Linux 서버 방화벽 설정 모범 사례

    리눅스 서버를 오래 운영하다 보면, 서비스는 멀쩡한데 방화벽이 애매하게 열려 있어서 뒤늦게 식은땀이 나는 순간이 꼭 옵니다. 저도 홈랩이랑 운영 서버를 같이 보면서 몇 번이나 겪었거든요. 특히 배포 직후에는 패키지 설치, TLS, 애플리케이션 기동에 신경이 쏠리다 보니 firewalld 보안 체크리스트가 뒤로 밀리기 쉽습니다. 그런데 실제 사고는 대개 여기서 납니다. CVE가 바로 터지지 않더라도, 불필요하게 열린 관리 포트 하나가 공격 표면을 넓히고, 나중에는 “왜 이 포트가 열려 있지?”를 추적하는 운영 비용으로 돌아오더라고요.

    이 글은 명령어만 늘어놓는 글이 아닙니다. 저는 firewalld를 볼 때 “무엇을 열까”보다 “왜 이 경계가 필요한가, 어디서 망가지기 쉬운가, 변경 후 어떻게 검증할까”를 더 중요하게 봅니다. 그래서 이번 글은 Linux 방화벽 설정을 점검하는 순서, 자주 꼬이는 실패 모드의 원인, 단일 NIC 서버와 듀얼 NIC 서버에서 판단이 갈리는 지점까지 실무 기준으로 묶었습니다. 체크박스만 채우는 요약이 아니라, 실제로 복붙해서 점검하고 운영 습관까지 바꿀 수 있는 내용으로 정리해보겠습니다.

    firewalld 보안 체크리스트를 설명하는 Linux 서버 방화벽 아키텍처 이미지

    firewalld의 Zone(존), Service(서비스), Port(포트)가 어떻게 맞물려 동작하는지 한눈에 보여주는 개요 이미지입니다.

    1. 왜 firewalld 보안 체크리스트가 먼저냐

    제가 운영 초기에 가장 많이 본 실수는 “애플리케이션이 정상 응답하니 네트워크도 정리된 줄 아는 상태”였습니다. 실제로는 반대인 경우가 많습니다. 앱은 443만 써야 하는데, 테스트 때 열어둔 8080, 관리용 9090, 배포판 기본 서비스까지 같이 살아 있는 식이죠. firewalld는 이런 상태를 정리하기에 꽤 좋은 도구입니다. 다만 편한 도구라고 해서 자동으로 안전해지지는 않습니다. 규칙을 추가하기 쉬운 도구일수록, 나중에 덜어내는 기준이 더 중요합니다.

    제가 실무에서 기준으로 삼는 건 네 가지입니다. 첫째, 인터넷에 공개할 이유가 없는 포트는 기본적으로 닫는다. 둘째, 관리 트래픽은 서비스 트래픽과 다른 경계로 본다. 셋째, 런타임 실험과 영구 반영을 섞지 않는다. 넷째, 명령 성공이 아니라 실제 패킷 경로 기준으로 검증한다. 방화벽은 설정 자체보다 운영 중 일관성이 더 중요합니다.

    • 최소 허용: 서비스가 실제로 수신해야 하는 프로토콜과 포트만 엽니다.
    • 경계 분리: 공개망, 관리망, 백업망을 같은 존 정책으로 처리하지 않습니다.
    • 상태 분리: 런타임 테스트와 --permanent 반영을 구분합니다.
    • 검증 우선: firewall-cmd 출력만 보지 말고 리슨 상태와 외부 접속 결과까지 확인합니다.

    2. firewalld 보안 체크리스트의 핵심 개념

    처음에 많이 헷갈리는 부분이 Zone, Service, Port, Rich Rule이 각각 뭘 책임지는지입니다. 저는 이걸 “정책의 단위가 다르다”로 이해하는 편이 가장 덜 꼬였습니다. Zone은 경계, Service는 의미 있는 허용 단위, Port는 예외 처리, Rich Rule은 조건부 허용입니다. 이걸 섞어 쓰더라도 각자 맡는 역할이 분명해야 나중에 규칙 해석이 쉬워집니다.

    2-1. Zone(존, 신뢰 수준별 정책 그룹)

    Zone은 인터페이스 또는 소스 대역에 붙는 정책 경계입니다. 중요한 건 “서버 한 대 = 정책 한 개”가 아니라는 점입니다. NIC가 하나여도 출발지 대역이 다르면 다른 경계로 볼 수 있고, NIC가 둘 이상이면 더더욱 분리하는 게 맞습니다. 운영하면서 제일 위험한 패턴은 외부 공개 NIC와 관리자 전용 네트워크를 둘 다 public에 묶어두는 경우였습니다. 이렇게 해두면 나중에 SSH, 백업, 모니터링 예외가 전부 public에 쌓이면서 정책이 지저분해집니다.

    2-2. Service(서비스, 미리 정의된 포트 집합)

    ssh, http, https 같은 표준 서비스는 Service로 여는 편이 낫습니다. 숫자 포트보다 의도가 남기 때문입니다. 나중에 누가 봐도 “이 서버가 왜 이 포트를 열었는지”를 해석하기 쉽습니다. 다만 모든 걸 Service로 해결하려고 하면 오히려 애매해질 수 있습니다. 예를 들어 커스텀 앱 8443/tcp를 장기 운영하면서 이름 없는 포트로만 남겨두면 의미를 잃고, 반대로 잠깐 쓸 포트까지 서비스 XML로 만드는 건 과합니다.

    2-3. Runtime과 Permanent

    여기가 제일 많이 사고 나는 지점입니다. 런타임은 현재 커널에 적용된 메모리 상태이고, 영구 설정은 디스크에 저장된 상태입니다. 즉, 테스트할 때는 런타임이 빠르지만 운영 기준은 결국 영구 설정입니다. 문제는 두 상태가 잠시 어긋나도 눈으로는 잘 안 보인다는 점이죠. 그래서 저는 “테스트는 런타임, 반영은 영구, 마감은 reload 후 재검증” 순서를 고정해둡니다. 이 흐름을 지키면 재부팅 뒤 접속 불가 같은 사고가 확 줄어듭니다.

    선택지 언제 쓰면 좋은가 장점 언제 피해야 하는가 운영상 주의점
    Service 추가 SSH, HTTP, HTTPS 같은 표준 포트 의도가 명확하고 읽기 쉽습니다 실제 포트 구성이 표준과 다를 때 --info-service로 정의 내용을 확인해두면 좋습니다
    Port 직접 허용 커스텀 앱, 단기 테스트, 비표준 포트 즉시 반영이 쉽습니다 장기 운영 포트를 설명 없이 누적할 때 운영 문서에 포트 용도를 같이 남겨야 합니다
    Rich Rule 출발지 IP 제한, 로그, 세밀한 예외 처리 서비스 공개 범위를 정확히 좁힐 수 있습니다 단순한 허용 규칙까지 전부 Rich Rule로 만들 때 규칙이 많아지면 읽기 어려워지므로 목적별로 묶어야 합니다
    Zone 분리 NIC가 둘 이상이거나 관리망/서비스망이 분리된 환경 경계가 명확해지고 예외가 줄어듭니다 작은 단일 NIC 서버에서 과도하게 복잡도를 키울 때 인터페이스 매핑과 소스 대역 매핑을 혼동하지 않아야 합니다
    커스텀 Service XML 장기 운영하는 사내 표준 앱 포트 포트 의미를 이름으로 보존할 수 있습니다 일회성 포트 하나만 잠깐 열 때 /etc/firewalld/services/에 정의를 남기고 변경 이력을 관리하면 편합니다

    3. 실전 체크리스트 1단계: 현재 상태부터 정확히 파악하기

    방화벽 작업에서 제일 위험한 건 규칙 추가 자체가 아니라, 현재 상태를 오해한 채 손대는 겁니다. 원격 SSH 세션 하나로 작업 중인데 현재 세션이 어느 존 정책에 의해 살아 있는지, 같은 서버에 다른 관리 포트가 떠 있는지, 런타임과 영구 설정이 같은지 모르고 건드리면 끊어먹기 쉽습니다. 그래서 저는 항상 “서비스 상태 → 활성 존 → 인터페이스 매핑 → 허용 항목 → 리슨 상태” 순서로 봅니다. 이 순서가 의외로 사고를 많이 줄여줍니다.

    1. firewalld 서비스가 실제로 올라와 있는지 확인합니다.
    2. 기본 Zone과 활성 Zone이 같은지 봅니다.
    3. 어떤 인터페이스가 어느 Zone에 붙었는지 확인합니다.
    4. Zone별 허용 서비스, 포트, Rich Rule을 나눠 봅니다.
    5. 실제 프로세스가 어떤 주소와 포트에 바인딩되어 있는지 비교합니다.
    sudo systemctl status firewalld
    sudo firewall-cmd --state
    sudo firewall-cmd --get-default-zone
    sudo firewall-cmd --get-active-zones
    sudo firewall-cmd --zone=public --list-all
    sudo firewall-cmd --list-all-zones
    sudo ss -tulpn

    여기서 읽는 기준이 중요합니다. --get-active-zones는 “정책 이름”보다 “어느 인터페이스나 소스가 어디에 매핑됐는지”를 보는 명령이라고 생각하시면 됩니다. --list-all에서는 services, ports, rich rules를 따로 봐야 합니다. 운영 현장에서 진짜 자주 보는 실수가 서비스만 보고 안심하는 겁니다. 예전에 열어둔 포트가 ports:에 남아 있는데 services:만 보고 지나가면 정책 오판이 생깁니다.

    한 가지 더 보셔야 할 게 있습니다. ss -tulpn 결과와 firewalld 허용 목록이 맞는지 비교해야 합니다. 앱이 0.0.0.0:9090에 떠 있는데 방화벽에서 9090이 닫혀 있으면 외부 노출은 막히겠지만, 내부 망이나 로컬 프록시 경로에서는 다른 문제가 생길 수 있습니다. 반대로 앱이 127.0.0.1에만 바인딩되어 있으면 포트를 아무리 열어도 외부 접속은 안 됩니다. 보안 점검과 장애 점검이 이 지점에서 만납니다.

    제가 실무에서 자주 쓰는 확인 패턴은 아래처럼 “방화벽 상태와 실제 리슨 상태를 한 번에 비교 가능한 형태”로 보는 방식입니다.

    sudo firewall-cmd --get-active-zones
    sudo firewall-cmd --zone=public --list-services
    sudo firewall-cmd --zone=public --list-ports
    sudo firewall-cmd --zone=public --list-rich-rules
    sudo ss -lntp
    sudo ss -lnup
    firewalld 보안 체크리스트 점검을 위한 Linux 방화벽 설정 터미널 이미지

    활성 Zone, 인터페이스 매핑, 허용 서비스와 포트를 점검하는 실제 운영자 시점의 터미널 화면을 표현한 이미지입니다.

    4. 실전 체크리스트 2단계: 필요한 것만 열고 나머지는 닫기

    이 단계부터는 정책을 정리합니다. 기준은 단순합니다. “지금 열려 있는 것”이 아니라 “이 서버 역할에 꼭 필요한 것”만 남기는 겁니다. 웹 서버라면 대개 공개 포트는 80/443 중 필요한 것만 남고, SSH는 관리 경로로만 제한합니다. 이때 중요한 건 보안을 이유로 운영성을 망치지 않는 겁니다. 예를 들어 SSH 전체 차단은 멋있어 보일 수 있지만, 콘솔 대체 수단이 없으면 사고 복구 시간을 키웁니다. 그래서 저는 외부 공개 서버에서 가장 현실적인 기본형을 HTTPS 공개 + SSH 출발지 제한 + 불필요한 기본 서비스 제거로 잡습니다.

    4-1. 기본 서비스만 먼저 허용

    sudo firewall-cmd --permanent --zone=public --add-service=https
    sudo firewall-cmd --permanent --zone=public --add-service=http
    sudo firewall-cmd --permanent --zone=public --remove-service=cockpit
    sudo firewall-cmd --permanent --zone=public --remove-service=dhcpv6-client
    sudo firewall-cmd --reload
    sudo firewall-cmd --zone=public --list-all

    여기서 판단 기준을 분명히 하셔야 합니다. http는 80에서 443으로 리다이렉트하는 프런트가 있거나 ACME HTTP-01 검증이 필요하면 열고, 그렇지 않으면 빼도 됩니다. cockpit은 명시적으로 운영할 때만 여는 편이 안전합니다. 다만 cockpit이나 dhcpv6-client가 실제로 기본 등록되어 있는지는 배포판과 초기 설정에 따라 다를 수 있으니, 제거 전에 --list-all로 현재 상태를 먼저 보세요. 운영 보안은 결국 기본값을 그대로 믿지 않는 데서 시작합니다.

    4-2. SSH는 전체 공개보다 출발지 IP 제한

    SSH는 “열까 말까”보다 “누구에게 열까”가 더 중요합니다. 운영 서버에서 22/tcp 전체 공개는 공격 표면을 넓히는 데 비해 얻는 편의가 적습니다. 고정 관리 IP나 VPN 대역이 있다면 Rich Rule로 제한하는 쪽이 좋습니다. 특히 fail2ban 같은 보조 수단을 쓰더라도, 방화벽에서 먼저 줄이는 게 낫습니다. 로그인 실패를 막는 것보다 애초에 소켓 도달 범위를 좁히는 편이 더 근본적이거든요.

    sudo firewall-cmd --permanent --zone=public \
      --add-rich-rule='rule family="ipv4" source address="203.0.113.10/32" service name="ssh" accept'
    
    sudo firewall-cmd --permanent --zone=public --remove-service=ssh
    sudo firewall-cmd --reload
    sudo firewall-cmd --zone=public --list-rich-rules

    이 규칙의 핵심은 단순합니다. ssh 서비스를 전체 공개하지 않고, 특정 출발지에서만 받아줍니다. 관리 대역이 VPN 서브넷이라면 203.0.113.10/32 대신 실제 CIDR을 넣으시면 됩니다. 다만 여기서 흔한 실패는 “회사 공인 IP라고 생각했던 주소가 실제로는 NAT 뒤에서 바뀌는 주소”인 경우입니다. 그러면 방화벽 문제처럼 보이는데 사실은 출발지 식별 가정이 틀린 겁니다. 그래서 이 단계는 현재 세션을 유지한 채 새 터미널에서 재접속 테스트까지 같이 하시는 게 안전합니다.

    4-3. 커스텀 포트는 무조건 열지 말고, 수명에 따라 처리 방식 나누기

    8443, 9090, 3000 같은 커스텀 포트는 운영에서 자주 나옵니다. 저는 이걸 수명 기준으로 나눕니다. 일시적인 디버그 포트면 짧게 열고 바로 닫습니다. 장기 운영 포트면 처음부터 이름 있는 서비스로 승격할지, 포트로 두되 문서화할지 정합니다. 이걸 안 정하면 몇 달 뒤 포트 목록이 기술 부채가 됩니다.

    sudo firewall-cmd --permanent --zone=public --add-port=8443/tcp
    sudo firewall-cmd --permanent --zone=public --remove-port=9090/tcp
    sudo firewall-cmd --reload
    sudo firewall-cmd --zone=public --list-ports

    장기 운영 포트가 있고 팀원이 여러 명이라면 커스텀 서비스 정의를 고려할 만합니다. 포트를 숫자로만 남겨두면 “8443이 무슨 용도였지?”가 반복됩니다. 반대로 운영 수명이 짧은 포트는 XML 정의까지 만들 필요는 없습니다. 이런 선택 기준이 실무에서 시간을 아껴줍니다.

    <?xml version="1.0" encoding="utf-8"?>
    <service>
      <short>internal-api</short>
      <description>Internal API exposed via TCP 8443</description>
      <port protocol="tcp" port="8443"/>
    </service>

    위 같은 파일을 /etc/firewalld/services/internal-api.xml에 두고 reload한 뒤 --add-service=internal-api로 관리하면, 숫자 포트보다 정책 의미가 오래 남습니다. 저는 포트가 배포 문서, 모니터링, 보안 정책에 반복 등장하기 시작하면 이 단계로 올립니다.

    5. 실전 체크리스트 3단계: Zone 분리와 인터페이스 매핑

    NIC가 둘 이상인 서버에서는 여기서 보안 수준이 꽤 갈립니다. 공개 트래픽과 관리 트래픽을 같은 존에 넣어두면, 나중에 허용 규칙이 죄다 public으로 몰립니다. 그 결과 “관리 편의를 위해 public 예외를 하나 더 추가”하는 패턴이 반복되고, 결국 원래 분리했어야 할 경계가 사라집니다. 제가 듀얼 NIC 서버에서 가장 먼저 정리하는 게 이 부분입니다.

    sudo firewall-cmd --permanent --new-zone=management
    sudo firewall-cmd --permanent --zone=management --add-service=ssh
    sudo firewall-cmd --permanent --zone=management --add-source=192.168.10.0/24
    sudo firewall-cmd --permanent --zone=public --change-interface=eth0
    sudo firewall-cmd --permanent --zone=management --change-interface=eth1
    sudo firewall-cmd --reload
    sudo firewall-cmd --get-active-zones

    이 구성이 좋은 이유는 규칙을 줄여주기 때문입니다. 외부 서비스망 eth0는 공개 포트만 남기고, 내부 관리망 eth1은 SSH, 백업, 모니터링처럼 성격이 다른 트래픽을 따로 받습니다. 운영하면서 예외가 생기더라도 public을 더럽히지 않고 management 쪽에서 해결할 수 있습니다.

    다만 언제나 Zone 분리가 정답은 아닙니다. NIC가 하나뿐이고 관리도 VPN 하나로만 들어온다면, 억지로 존을 많이 만드는 것보다 public 하나에 출발지 제한 Rich Rule을 두는 편이 단순하고 덜 위험합니다. 정책은 강한 것보다 읽기 쉬운 것이 오래 갑니다. 제가 권하는 기준은 이렇습니다. 인터페이스가 다르면 Zone 분리, 인터페이스는 같고 출발지만 다르면 Rich Rule 우선. 이게 대부분의 중소형 서버에서 가장 덜 꼬입니다.

    한 가지 더 실무 팁을 드리면, NetworkManager가 인터페이스-존 매핑을 함께 관리하는 환경에서는 firewalld 쪽 변경과 네트워크 연결 프로필 설정이 엇갈리지 않게 맞춰야 합니다. 그래서 저는 관리망이 명확한 환경에서는 “관리 NIC는 management로 고정, VPN이나 사내 대역은 source로 보강” 정도까지만 씁니다. 규칙 해석이 복잡해지는 순간, 보안 자체보다 운영 오류가 더 큰 리스크가 됩니다.

    Linux 방화벽 설정에서 firewalld 존 분리를 보여주는 서버 보안 강화 이미지

    외부 서비스망과 내부 관리망을 분리한 듀얼 NIC 환경의 Zone 설계를 이해하기 위한 다이어그램입니다.

    6. 실제로 많이 꼬이는 포인트와 트러블슈팅

    이 섹션은 공식 문서보다 운영에서 더 많이 필요합니다. 명령 자체는 맞는데 결과가 기대와 다를 때, 대개 원인은 문법보다 가정이 틀린 데 있습니다. 제가 실제로 자주 본 실패 모드만 추렸습니다. 이 부분을 미리 알고 들어가면 대응 속도가 확실히 빨라집니다.

    6-1. 재부팅 후 규칙이 사라진다

    대부분 원인은 단순합니다. 런타임에만 넣고 영구 반영을 안 했거나, 반대로 런타임과 영구 상태가 이미 달라져 있었는데 작업자가 둘을 같은 것으로 착각한 경우입니다. 근본 원인은 “테스트 상태와 배포 상태를 구분하지 않는 습관”입니다.

    sudo firewall-cmd --list-all
    sudo firewall-cmd --permanent --zone=public --list-all
    sudo firewall-cmd --runtime-to-permanent
    sudo firewall-cmd --reload

    --runtime-to-permanent는 편하지만 무심코 쓰면 임시로 열어둔 포트까지 저장해버릴 수 있습니다. 그래서 저는 이 명령을 “좋은 상태가 이미 런타임에 올라와 있다는 걸 확인한 뒤”에만 씁니다. 확신이 없으면 아예 영구 설정에 다시 명시하는 편이 낫습니다.

    6-2. SSH 접속이 갑자기 끊긴다

    이건 규칙 문법 문제보다 작업 순서 문제입니다. 보통은 --remove-service=ssh를 너무 일찍 적용했거나, 허용해야 할 출발지 IP를 잘못 넣었거나, 현재 접속 세션이 예상과 다른 경로를 타고 있었던 경우입니다. 예를 들어 평소엔 VPN으로 들어오지만 오늘은 외부 회선에서 접속한 상태라면, 머릿속 관리 IP와 실제 출발지 IP가 다를 수 있습니다.

    • 현재 SSH 세션을 끊지 말고, 새 터미널에서 재접속 테스트를 먼저 합니다.
    • 허용 규칙 추가 후 --remove-service=ssh를 마지막에 실행합니다.
    • 클라우드 서버면 웹 콘솔이나 시리얼 콘솔 경로를 먼저 확보합니다.
    • who, ss -tnp | grep :22 같은 보조 확인으로 현재 세션 상태를 같이 봅니다.

    6-3. 애플리케이션은 떠 있는데 외부 접속이 안 된다

    이럴 때 방화벽만 붙잡고 있으면 오래 갑니다. 실제 원인은 앱이 127.0.0.1에만 바인딩되어 있거나, 컨테이너 프록시와 호스트 포트가 다르거나, 다른 존에 인터페이스가 붙어 있어서 예상한 정책을 안 타는 경우가 많습니다. 즉, 근본 원인은 “포트 개방”과 “서비스 바인딩”을 같은 것으로 보는 데 있습니다.

    sudo ss -tulpn
    sudo firewall-cmd --get-active-zones
    sudo firewall-cmd --zone=public --list-ports
    sudo firewall-cmd --zone=public --list-services

    읽는 기준은 이렇습니다. ss에서 앱이 실제 외부 IP 또는 0.0.0.0에 바인딩되어 있어야 하고, 해당 포트가 맞는 존에서 허용되어 있어야 합니다. 둘 중 하나라도 빠지면 접속이 안 됩니다. 간단해 보이는데, 장애 대응에서는 이 두 층을 분리해서 보지 않아서 시간이 오래 가더라고요.

    6-4. 규칙은 맞는 것 같은데 이상하게 다른 트래픽도 통과한다

    이건 종종 방화벽이 아니라 경로 문제입니다. 예를 들어 애플리케이션이 같은 호스트의 로컬 프록시를 통해 우회하고 있거나, 컨테이너 네트워크 규칙이 별도로 적용되거나, 클라우드 보안 그룹이 firewalld보다 앞단에서 이미 허용하거나 차단하고 있을 수 있습니다. firewalld는 유일한 경계가 아닙니다. 제가 운영 점검할 때 항상 같이 묻는 질문이 “이 트래픽이 진짜 호스트 ingress 경로를 타는가?”입니다.

    7. firewalld 보안 체크리스트의 검증 방법

    보안 설정은 명령 성공으로 끝나면 안 됩니다. 핵심은 “허용된 대상에게만, 필요한 포트만, 예상한 존을 통해 열려 있는가”입니다. 저는 검증을 네 층으로 나눕니다. 방화벽 상태, 프로세스 리슨 상태, 출발지별 접속 결과, 운영 문서와의 일치 여부입니다. 이 네 개가 맞아야 비로소 설정이 끝난 겁니다.

    1. 서버 내부에서 활성 Zone과 허용 목록을 확인합니다.
    2. 프로세스가 실제 어떤 주소/포트에 리슨하는지 확인합니다.
    3. 허용된 네트워크와 비허용 네트워크에서 각각 접속 테스트를 합니다.
    4. 문서화된 서비스 목록과 실제 오픈 포트가 일치하는지 봅니다.
    sudo firewall-cmd --get-active-zones
    sudo firewall-cmd --zone=public --list-all
    sudo firewall-cmd --zone=management --list-all
    sudo ss -tulpn

    외부 테스트는 같은 서버 안이 아니라 관리 PC, 점프 호스트, VPN 외부 클라이언트처럼 서로 다른 출발지에서 해보는 게 좋습니다. 예를 들어 HTTPS는 어디서나 접속되어야 하지만, SSH는 허용된 관리 IP에서만 붙어야 합니다. 허용되지 않은 네트워크에서 SSH가 붙는다면 Rich Rule이나 Zone 매핑이 기대와 다르게 동작하는 겁니다. 반대로 HTTPS가 내부에서만 되고 외부에서 안 되면 firewalld보다 앞단 LB, 보안 그룹, 라우팅까지 같이 봐야 합니다.

    검증 결과 해석 기준도 애매하게 두지 않는 편이 좋습니다.

    • list-all에 문서화하지 않은 서비스나 포트가 보이면 정리 대상입니다.
    • public Zone에 SSH, Cockpit, DB 포트가 남아 있으면 노출 과다로 봅니다.
    • ss -tulpn에서 외부 바인딩된 포트가 의도보다 많으면 앱 설정부터 줄여야 합니다.
    • 허용 대상이 아닌 출발지에서도 접속되면 Zone 매핑, Rich Rule, 앞단 네트워크 정책을 다시 확인합니다.
    • 재부팅 후 결과가 바뀌면 영구 설정 관리에 구멍이 있는 겁니다.

    실무에서는 “포트가 열렸는가”보다 “왜 열려 있는가”를 물어야 합니다. 이 질문이 빠지면 방화벽은 언젠가 예외 규칙 창고가 됩니다. 관련 글로 SELinux, SSH 하드닝, Nginx TLS 점검 가이드도 내부 링크로 함께 묶어두면 검색 유입과 체류 시간 관리에 꽤 도움이 됩니다.

    firewalld 보안 체크리스트 검증 결과를 보여주는 서버 보안 강화 이미지

    허용된 서비스만 남고 관리 포트는 제한된 상태를 검증하는 결과 중심의 대시보드/터미널 이미지입니다.

    8. 운영하면서 유지보수할 때 체크할 항목

    방화벽은 한 번 잠갔다고 끝나는 장치가 아닙니다. 서비스가 늘고 담당자가 바뀌고, 장애 대응 중 임시 예외가 생기면 규칙은 반드시 불어납니다. 그래서 저는 월간 점검이나 배포 체크리스트에 firewalld 보안 체크리스트 항목을 따로 넣습니다. 보안을 강화하는 가장 싼 방법은 새로운 솔루션을 들이는 게 아니라, 이미 열린 예외를 줄이는 겁니다.

    • 사용하지 않는 서비스 제거: 예전에 열었던 포트가 아직 실제 트래픽을 받는지 확인합니다.
    • 출발지 제한 재검토: 퇴역한 사무실 IP, 종료된 VPN 대역이 남아 있지 않은지 봅니다.
    • Zone-인터페이스 매핑 확인: NIC 추가, 이름 변경, 가상 인터페이스 생성 후 매핑이 흐트러지지 않았는지 확인합니다.
    • 앱 배포 문서 동기화: 애플리케이션 포트 변경이 방화벽 정책과 함께 업데이트됐는지 맞춰봅니다.
    • reload 후 재검증: 설정 반영 뒤 실제 접속 테스트까지 끝내야 점검 완료로 봅니다.
    • 커스텀 서비스 정리: /etc/firewalld/services/ 아래 정의가 현재 운영과 맞는지 점검합니다.

    작은 팀일수록 문서화가 귀찮게 느껴질 수 있는데, 방화벽은 문서화하지 않으면 팀이 바뀌는 순간 바로 리스크가 됩니다. 특히 포트를 숫자로만 열어둔 환경은 인수인계 비용이 큽니다. 저는 장기 운영 서비스라면 “서비스명, 포트, 출발지, 이유” 네 가지는 최소한 남겨두는 편이 좋다고 봅니다. 이거 해두면 나중에 진짜 편합니다.

    운영 중 반복 점검해야 할 항목을 한 장으로 압축한 요약 인포그래픽 이미지입니다.

    9. 자주 묻는 질문과 바로 적용할 추천안

    9-1. SSH는 아예 닫는 게 맞을까요?

    콘솔 대체 수단이 안정적으로 있고, 배포 자동화와 장애 복구 루틴이 충분히 성숙했다면 가능합니다. 하지만 대부분의 서버 운영에서는 완전 차단보다 출발지 제한이 더 현실적입니다. 보안과 복구 시간을 같이 봐야 하니까요. 제 추천은 단순합니다. 인터넷 전체 공개는 피하고, 고정 IP 또는 VPN 대역으로 묶으세요.

    9-2. 서비스 추가와 포트 추가 중 뭐가 더 좋나요?

    표준 포트면 서비스가 낫고, 커스텀 앱이면 포트가 빠릅니다. 다만 커스텀 포트가 장기 운영 대상이라면 서비스 XML로 승격하거나 최소한 운영 문서에 의미를 남기세요. “지금 빨리 열기”와 “나중에 이해 가능하기”는 다른 문제입니다.

    9-3. 서버 보안 강화를 시작하는 최소 기준은?

    제가 운영 서버에서 최소선으로 잡는 기준은 이렇습니다. 외부 공개는 HTTPS 중심, SSH는 특정 출발지 제한, 관리망은 공개망과 분리, 불필요한 기본 서비스 제거, reload 후 외부 검증. 이 다섯 개만 지켜도 방화벽 상태는 훨씬 예측 가능해집니다.

    환경별로 추천을 딱 정리해보면 이렇습니다. 단일 NIC 소형 서버라면 public Zone 하나 + HTTPS 공개 + SSH Rich Rule 제한 조합이 가장 실용적입니다. 규칙이 단순하고 운영자가 바뀌어도 이해가 쉽습니다. 반대로 NIC가 둘 이상이거나 관리망이 분리된 서버라면 Zone 분리를 바로 적용하는 편이 낫습니다. public에는 서비스 트래픽만, management에는 관리 트래픽만 남기세요. 이 구조가 예외 규칙 누적을 가장 잘 막아줍니다.

    한 문장으로 요약하면, firewalld 보안 체크리스트의 핵심은 “포트를 여는 기술”이 아니라 “열린 이유를 끝까지 설명할 수 있는 상태를 유지하는 것”입니다. 저는 방화벽을 잘 짠 서버가 보안적으로만 좋은 게 아니라 운영도 덜 아프다고 봅니다. 필요한 것만 열고, 경계를 분리하고, 재부팅 후에도 같은 결과가 나오게 만들고, 마지막엔 실제 접속으로 확인하세요. 이 네 가지를 습관으로 만들면 리눅스 서버는 눈에 띄게 단단해집니다.

  • [보안] 리눅스 서버 보안 강화 체크리스트: SSH부터 시스템까지 10가지 필수 점검

    [보안] 리눅스 서버 보안 강화 체크리스트: SSH부터 시스템까지 10가지 필수 점검

    [보안] 리눅스 서버 보안 강화 체크리스트: SSH부터 시스템까지 10가지 필수 점검

    리눅스 서버 보안은 서버를 한두 대 운영할 때보다, 서비스가 붙고 계정이 늘어나기 시작할 때 더 절실해집니다. 저도 처음엔 “일단 서비스부터 띄우자” 모드로 달렸었는데요. 그러다가 SSH(에스에스에이치, 원격 접속 프로토콜) 설정 하나 느슨했던 탓에 로그가 지저분하게 쌓이고, 괜히 마음까지 불안해졌던 적이 있습니다. 그 뒤로는 새 서버를 올릴 때마다 꼭 보는 리눅스 서버 보안 체크리스트를 따로 만들어서 관리하고 있습니다. 오늘은 그 체크리스트를 기준으로 서버 하드닝, SSH 보안 강화, 그리고 시스템 전반에서 꼭 확인해야 할 10가지를 한 번에 정리해보겠습니다.

    새 서버 구축 시 SSH, 방화벽, 계정, 로그, 업데이트 순서로 점검하는 전체 흐름을 보여주는 개요 이미지입니다.

    1. 왜 리눅스 서버 보안 체크리스트가 필요한가

    쉽게 말해 체크리스트는 “내가 뭘 빼먹었는지”를 막아주는 안전장치입니다. 보안 사고가 꼭 대단한 취약점 때문에만 나는 건 아니거든요. 실제로는 기본 계정 방치, 불필요한 포트 오픈, 로그 미확인, 패키지 업데이트 누락 같은 아주 평범한 실수에서 시작되는 경우가 많습니다. 저도 홈랩에서 VM(버추얼 머신, 가상 머신)을 자주 갈아엎다 보니, 사람이 하는 일은 결국 반복 실수가 나오더라고요. 그래서 설정을 잘 아는 것보다 항상 같은 기준으로 확인하는 습관이 더 중요했습니다.

    특히 리눅스 보안 체크리스트는 아래 같은 상황에서 힘을 발휘합니다.

    • 새 VPS나 클라우드 인스턴스를 처음 올렸을 때
    • 운영 중인 서버를 다른 사람이 함께 관리하게 됐을 때
    • 서비스는 커졌는데 초기 설정이 그대로 남아 있을 때
    • 문제 없던 서버를 장기간 방치했다가 다시 점검할 때

    2. 서버 하드닝, 어디까지 해야 하나

    서버 하드닝(Server Hardening, 시스템을 더 단단하게 만드는 작업)은 무조건 복잡하게 가는 게 정답은 아닙니다. 과한 보안은 운영 피로도를 높이고, 반대로 너무 느슨하면 공격 표면(Attack Surface, 공격 가능한 면적)이 넓어집니다. 여기서 중요한 포인트! 운영 가능한 수준에서 꾸준히 유지되는 보안이 제일 현실적입니다.

    제가 실무와 홈랩에서 기준으로 삼는 우선순위는 이렇습니다.

    1. 원격 접속 통제
    2. 계정과 권한 최소화
    3. 네트워크 노출 최소화
    4. 업데이트와 취약점 패치
    5. 로그와 감사(Audit, 추적 기록) 확보
    6. 자동 차단과 복구 준비

    즉, 리눅스 서버 보안은 좋은 툴을 많이 붙이는 것보다, 기본기를 빠짐없이 닫아주는 쪽이 먼저입니다.

    3. 리눅스 서버 보안 체크리스트 10가지

    아래 10가지는 제가 새 서버를 만들 때 거의 습관처럼 점검하는 항목입니다. 배포판마다 명령은 조금 다를 수 있지만 원리는 같습니다.

    항목 왜 중요한가 우선도
    1. 패키지 업데이트 기본 취약점 패치 매우 높음
    2. SSH 포트/접속 정책 무차별 대입 공격 감소 매우 높음
    3. root 직접 로그인 차단 고위험 계정 보호 매우 높음
    4. 비밀번호 대신 키 인증 인증 강도 향상 매우 높음
    5. sudo 최소 권한 권한 오남용 방지 높음
    6. 방화벽 기본 정책 불필요한 포트 차단 매우 높음
    7. 불필요한 서비스 비활성화 공격 표면 축소 높음
    8. 로그/감사 활성화 이상 징후 추적 높음
    9. Fail2ban 등 자동 방어 반복 공격 완화 중간 이상
    10. 백업과 복구 점검 사고 이후 복원 가능 매우 높음

    4. 실전 구현: SSH 보안 강화부터 시스템 보안까지

    이제 실제로 손대는 순서입니다. Ubuntu 계열 기준으로 예시를 들겠지만, Debian이나 Rocky Linux 계열도 큰 흐름은 비슷합니다.

    4-1. 패키지 최신 상태 유지

    가장 먼저 하는 작업입니다. 별거 아닌 것 같아도 이걸 미루면 뒤에 하는 설정이 다 무색해질 수 있습니다.

    sudo apt update
    sudo apt upgrade -y
    sudo apt autoremove -y

    RHEL 계열이면 dnf update -y 또는 yum update -y 형태로 하시면 됩니다.

    4-2. SSH 설정 파일 점검

    /etc/ssh/sshd_config는 진짜 자주 봅니다. 처음엔 옵션 이름이 낯설어서 저도 헷갈렸는데, 몇 번 손으로 바꿔보니까 감이 오더라고요.

    sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
    sudo editor /etc/ssh/sshd_config
    Port 2222
    PermitRootLogin no
    PasswordAuthentication no
    PubkeyAuthentication yes
    ChallengeResponseAuthentication no
    UsePAM yes
    X11Forwarding no
    MaxAuthTries 3
    LoginGraceTime 30
    AllowUsers adminuser

    Port 변경은 보안의 전부가 아닙니다. 하지만 자동 스캔 로그를 줄이는 데는 체감이 꽤 됩니다. 다만 포트만 바꾸고 방심하면 안 됩니다. 핵심은 root 직접 로그인 차단과 키 기반 인증입니다.

    리눅스 서버 보안에서 SSH 보안 강화를 설명하는 키 기반 인증 구성 이미지

    sshd_config 핵심 옵션과 공개키 인증 흐름을 한눈에 보여주는 설명 이미지입니다.

    4-3. SSH 키 인증 적용

    로컬 PC에서 키를 만들고 서버에 공개키를 넣어줍니다.

    ssh-keygen -t ed25519 -C "admin@homelab"
    ssh-copy-id -p 2222 adminuser@server-ip

    서버에서 권한도 꼭 맞춰주세요.

    chmod 700 ~/.ssh
    chmod 600 ~/.ssh/authorized_keys

    여기서 많이 하는 실수가 하나 있습니다. 키 로그인 테스트 전에 기존 세션을 끊어버리는 거예요. 저도 예전에 자신 있게 설정 저장했다가 접속 끊겨서 콘솔로 복구한 적 있습니다. 삽질 좀 했습니다 ㅎㅎ 기존 SSH 세션은 유지한 채로 새 터미널에서 먼저 접속 테스트하세요.

    4-4. sudo 권한 최소화

    모든 사용자를 관리자처럼 쓰는 순간, 사고 나면 추적도 어렵고 범위도 커집니다. 운영 계정은 분리하고, 필요한 사용자만 sudo 그룹에 넣습니다.

    sudo adduser adminuser
    sudo usermod -aG sudo adminuser
    id adminuser

    가능하면 공용 계정보다는 개인별 계정을 두는 게 좋습니다. 누가 어떤 작업을 했는지 남기기 쉽거든요.

    4-5. 방화벽(UFW 또는 firewalld) 적용

    리눅스 서버 보안에서 방화벽은 기본 중의 기본입니다. 외부에 꼭 필요한 포트만 열어두세요.

    sudo ufw default deny incoming
    sudo ufw default allow outgoing
    sudo ufw allow 2222/tcp
    sudo ufw allow 80/tcp
    sudo ufw allow 443/tcp
    sudo ufw enable
    sudo ufw status verbose

    웹 서버가 아니라면 80, 443도 열 필요가 없겠죠. 제가 직접 해보니, “나중에 쓸 수도 있으니 열어두자”가 제일 위험한 생각이더라고요.

    4-6. 불필요한 서비스 정리

    열려 있지 않아야 할 서비스는 생각보다 많습니다. 먼저 현재 리스닝(Listening, 포트 대기) 상태를 확인합니다.

    sudo ss -tulpn
    sudo systemctl list-unit-files --type=service | grep enabled

    사용하지 않는 서비스는 비활성화합니다.

    sudo systemctl disable --now avahi-daemon
    sudo systemctl disable --now cups

    물론 어떤 서비스가 필요한지는 서버 역할에 따라 다릅니다. 그래서 무조건 지우기보다 “왜 필요한가”를 먼저 확인해야 합니다.

    4-7. 로그와 감사 설정

    보안은 막는 것도 중요하지만, 나중에 되짚어볼 수 있어야 합니다. 최소한 인증 로그와 시스템 로그는 꾸준히 확인해야 합니다.

    sudo journalctl -p warning -b
    sudo journalctl -u ssh --since today
    sudo tail -f /var/log/auth.log

    auditd(Audit Daemon, 감사 로그 데몬)를 쓰는 환경이라면 주요 파일 변경까지 추적할 수 있습니다.

    sudo apt install auditd -y
    sudo systemctl enable --now auditd
    리눅스 서버 보안 모니터링 화면, 방화벽 규칙과 로그 확인 예시 이미지

    허용 포트, 차단 트래픽, SSH 로그인 시도 로그를 함께 보여주는 운영 관점의 보안 모니터링 이미지입니다.

    4-8. Fail2ban으로 반복 공격 완화

    Fail2ban은 로그를 보고 반복 실패한 IP를 자동 차단하는 도구입니다. 엄청 화려한 건 아닌데, 실무적으로 꽤 든든합니다.

    sudo apt install fail2ban -y
    sudo systemctl enable --now fail2ban
    sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
    sudo editor /etc/fail2ban/jail.local
    [sshd]
    enabled = true
    port = 2222
    logpath = %(sshd_log)s
    maxretry = 5
    findtime = 10m
    bantime = 1h

    설정 후 상태를 확인합니다.

    sudo fail2ban-client status
    sudo fail2ban-client status sshd

    4-9. 자동 업데이트 검토

    운영 정책에 따라 다르지만, 보안 업데이트 자동 적용은 꽤 유용합니다. 다만 서비스 영향도를 꼭 고려하세요.

    sudo apt install unattended-upgrades -y
    sudo dpkg-reconfigure -plow unattended-upgrades

    저는 인터넷에 직접 노출된 소규모 서버는 자동 보안 업데이트를 선호하고, 민감한 서비스는 점검 창구를 따로 둡니다.

    4-10. 백업과 복구 테스트

    여기서 진짜 많이 놓칩니다. 백업이 있는 것과 복구가 되는 것은 다릅니다. 설정 파일, 애플리케이션 데이터, 데이터베이스 덤프를 분리해서 챙기고, 실제로 복원 테스트까지 해보셔야 합니다.

    sudo tar czf /backup/etc-$(date +%F).tar.gz /etc
    sudo rsync -av /backup backupuser@backup-host:/data/server1/

    시스템 보안은 사고를 막는 것만이 아니라, 사고 이후 버틸 수 있는 상태를 만드는 일입니다.

    5. ⚠️ 실제로 자주 겪는 문제와 해결 방법

    여기서는 제가 많이 겪었거나 주변에서 자주 보는 함정을 정리해보겠습니다.

    • SSH 포트만 바꾸고 방화벽에 새 포트를 안 열어둠: 설정 반영 후 바로 접속 불가가 납니다. SSH 재시작 전에 방화벽부터 먼저 수정하세요.
    • PasswordAuthentication no를 너무 빨리 적용: 공개키 권한이 틀리면 바로 잠깁니다. 새 세션으로 키 로그인 테스트 후 적용하세요.
    • Fail2ban이 로그 경로를 못 읽음: 배포판마다 SSH 로그 위치가 다를 수 있습니다. journalctl 기반인지 파일 기반인지 확인해야 합니다.
    • 불필요한 서비스라고 생각하고 무작정 disable: 의존 서비스까지 영향을 줄 수 있습니다. systemctl status와 사용 목적을 먼저 보세요.
    • 백업만 있고 복구 절차 문서가 없음: 복원 명령, 순서, 인증 정보 위치를 적어두지 않으면 실제 장애 때 더 혼란스럽습니다.

    혹시 이런 경험 있으신가요? 저는 예전에 원격지 장비에서 SSH와 UFW를 한 번에 바꾸다가, 접속이 안 돼서 콘솔 접속권 있는지부터 확인했던 적이 있습니다. 그 뒤로는 꼭 “현재 세션 유지, 새 세션 테스트, 그 다음 재시작” 순서를 지킵니다.

    6. 검증: 설정이 제대로 적용됐는지 확인하는 방법

    보안 설정은 했다고 끝이 아닙니다. 검증이 빠지면 체크리스트가 아니라 희망사항이 되거든요. 아래 명령으로 확인해보세요.

    1. SSH가 원하는 포트로만 열려 있는지 확인
    2. root 직접 로그인 차단 여부 확인
    3. 비밀번호 로그인 차단 여부 확인
    4. 방화벽 허용 포트 목록 확인
    5. Fail2ban 차단 상태 확인
    6. 로그에 반복 실패 흔적이 줄었는지 확인
    sudo ss -tulpn | grep ssh
    sudo sshd -t
    sudo ufw status numbered
    sudo fail2ban-client status sshd
    sudo grep "Failed password" /var/log/auth.log | tail

    외부 호스트에서 포트 스캔을 점검할 수도 있습니다.

    nmap -Pn server-ip

    정상이라면 꼭 필요한 포트만 보이고, 인증 실패 로그가 과하게 쌓이지 않으며, root 직접 로그인은 막혀 있어야 합니다. 이런 상태가 되면 드디어 됐다! 싶은 순간이 옵니다. 눈에 띄는 성능 향상은 없지만, 운영자 마음은 훨씬 편해집니다.

    리눅스 서버 보안 점검 완료 후 검증 결과를 보여주는 이미지

    포트 점검, Fail2ban 상태, 인증 로그 확인 결과가 정리된 검증 완료 이미지입니다.

    7. 리눅스 서버 보안 운영 팁

    한 번 설정해두고 끝나는 보안은 거의 없습니다. 운영하면서 계속 봐야 합니다.

    • 월 1회 이상 보안 점검 루틴 만들기
    • 사용하지 않는 계정 즉시 잠금 또는 삭제
    • 새 서비스 배포 시 포트와 권한부터 검토
    • 로그 보관 기간과 백업 정책 분리
    • 가능하면 테스트 서버에서 설정 검증 후 운영 반영

    이전 글에서 다뤘던 Docker 격리와 reverse proxy 구성도 함께 보면 더 도움이 됩니다. 다음 글에서는 리눅스 서버 보안의 연장선으로 침입 탐지(IDS, Intrusion Detection System)와 로그 중앙화 쪽도 다뤄볼 예정입니다.

    8. 자주 묻는 질문 FAQ

    SSH 포트 변경만으로 충분한가요?

    아닙니다. 포트 변경은 소음 감소에는 도움이 되지만 본질적인 인증 강화를 대신하진 못합니다. 공개키 인증, root 로그인 차단, 방화벽 정책이 함께 가야 합니다.

    비밀번호 로그인을 꼭 꺼야 하나요?

    가능하면 그렇습니다. 특히 외부 공개 서버라면 더 그렇고요. 다만 운영 절차상 필요한 경우엔 MFA(다중 인증)나 IP 제한 같은 추가 통제가 필요합니다.

    소규모 홈서버도 이렇게까지 해야 하나요?

    인터넷에 노출된다면 규모와 상관없이 기본 점검은 필요합니다. 실제로 홈랩이 더 느슨해지기 쉬워서 오히려 체크리스트가 더 유용합니다.

    9. 마무리: 기본기만 잘 지켜도 보안 수준이 달라집니다

    오늘 정리한 리눅스 서버 보안 체크리스트 10가지는 화려한 고급 기술이 아닙니다. 그런데 신기하게도, 사고 예방에는 이런 기본기가 제일 오래 갑니다. 저도 처음엔 서버 하드닝이 너무 번거롭게 느껴졌는데, 한 항목씩 루틴으로 만들고 나니까 운영이 훨씬 안정적이더라고요. 특히 SSH 보안 강화, 방화벽 정책, 로그 확인, 백업 검증 이 네 가지는 체감 효과가 큽니다.

    처음부터 완벽하게 다 하려고 하기보다, 오늘 바로 할 수 있는 것부터 적용해보세요. 공개키 로그인 전환, root 차단, UFW 기본 deny, 이 세 가지만 해도 출발은 충분합니다. 그리고 마지막으로, 꼭 기억하셔야 할 건 하나입니다. 보안은 설정이 아니라 습관입니다.

    리눅스 서버 보안 체크리스트 10가지를 요약한 인포그래픽 이미지

    10가지 필수 점검 항목을 한 장으로 정리한 요약 인포그래픽 이미지입니다.

  • [Linux] SSH 하드닝 실전 가이드: 무단 접근 방어 5단계 보안 강화

    [Linux] SSH 하드닝 실전 가이드: 무단 접근 방어 5단계 보안 강화

    SSH 하드닝 실전 가이드: 무단 접근 방어 5단계 보안 강화

    홈랩이든 업무 서버든, 인터넷에 붙어 있는 리눅스 서버라면 결국 한 번쯤은 SSH 보안 강화를 제대로 해야 할 순간이 옵니다. 저도 처음엔 “포트만 바꾸면 되지 않을까?” 싶었는데요, 실제로 운영하다 보니 그 정도로는 부족하더라고요. 특히 봇(bot, 자동화된 공격 도구)이 SSH 포트에 무차별 로그인 시도를 넣는 걸 보고 나서는 생각이 확 바뀌었습니다. 오늘은 제가 홈랩과 실제 운영 환경에서 반복해서 적용했던 SSH 하드닝(SSH Hardening, SSH 보안 강화) 방법을 기준으로, 무단 접근 방어를 위한 5단계 보안 강화 절차를 정리해보겠습니다.

    핵심은 복잡한 장비를 들이는 게 아니라, 기본 기능을 제대로 조합하는 겁니다. OpenSSH 설정, 공개키 인증, 접근 제한, 침입 시도 차단, 그리고 마지막 검증까지요. 혹시 아직도 비밀번호 로그인만 열어두고 계셨다면, 여기서 중요한 포인트! 오늘 글만 따라가셔도 리눅스 서버 보안 수준이 꽤 달라질 겁니다.

    SSH 보안 강화 전체 구조를 보여주는 다이어그램 이미지

    SSH 보안 강화의 전체 흐름을 한눈에 보여주는 개요 이미지입니다. 외부 공격 시도, 방화벽, 공개키 인증, 접근 제한, 로그 모니터링 순서를 시각화하면 이해가 훨씬 빠릅니다.

    1. SSH 하드닝이 정말 필요한 이유

    SSH(Secure Shell, 암호화된 원격 접속 프로토콜)는 서버 운영에서 거의 기본입니다. 문제는 이 기본이 공격자에게도 너무 잘 알려져 있다는 점이죠. 인터넷에 노출된 서버는 생각보다 빨리 스캔(scan, 포트 탐색) 대상이 됩니다. 제가 집에서 굴리던 작은 홈랩 서버도 공개 IP를 붙이고 나서 얼마 안 돼서 인증 실패 로그가 쌓이기 시작했거든요. 처음엔 “누가 내 서버를 보겠어” 했었는데, 봇은 그런 감정이 없습니다 ㅎㅎ 포트가 열려 있으면 그냥 때려봅니다.

    쉽게 말해 SSH 하드닝은 문에 자물쇠 하나 다는 수준이 아니라, 현관 비밀번호를 바꾸고, 출입 가능한 사람을 줄이고, 이상 행동이 보이면 자동으로 막는 작업입니다. 이걸 해두면 무차별 대입 공격(brute-force attack, 비밀번호 반복 추측), 계정 탈취 시도, 설정 실수로 인한 노출 위험을 꽤 줄일 수 있습니다.

    2. SSH 보안 강화의 핵심 개념

    SSH 하드닝에서 중요한 건 한 가지 기술이 아닙니다. 여러 방어층(defense in depth, 다층 방어)을 쌓는 게 핵심입니다. 저도 예전엔 공개키 인증만 쓰면 끝인 줄 알았는데, 실제로 써보니까 접근 가능한 계정 제한이나 방화벽 정책까지 같이 묶어야 훨씬 안정적이더라고요.

    항목 기본 상태 SSH 보안 강화 후
    인증 방식 비밀번호 로그인 허용 공개키 인증 중심
    관리자 접속 root 직접 접속 가능 일반 계정 후 sudo 사용
    접근 범위 모든 계정 시도 가능 허용 계정만 접속
    네트워크 노출 전체 인터넷 개방 방화벽으로 IP 또는 포트 제어
    공격 대응 로그만 남음 자동 차단 도구 연동

    정리하면 이런 방향입니다.

    • 인증을 강하게: 비밀번호보다 공개키 인증(Public Key Authentication, 공개키 기반 인증)을 우선합니다.
    • 권한을 작게: root 직접 로그인 대신 일반 계정 + sudo 구조를 씁니다.
    • 노출을 줄이기: 방화벽(Firewall, 네트워크 접근 제어)과 AllowUsers 같은 SSH 설정으로 접속 대상을 좁힙니다.
    • 자동 대응하기: Fail2ban 같은 도구로 반복 공격을 차단합니다.
    • 검증까지 마무리: 설정 바꾸고 끝이 아니라 실제로 재접속과 로그 확인을 해야 합니다.

    3. 실전 구현: 무단 접근 방어 5단계

    여기부터는 제가 실제로 자주 쓰는 순서입니다. 중요한 건 기존 SSH 세션을 끊지 않은 상태로 하나씩 적용하는 겁니다. 저도 예전에 원격 서버에서 너무 자신 있게 설정 바꿨다가 그대로 문 닫힌 적 있습니다. 그날 진짜 식은땀 났습니다.

    3-1. 1단계: 공개키 인증으로 전환하기

    가장 먼저 할 일은 공개키를 준비하는 겁니다. 로컬 PC에서 키를 만들고, 서버에 배포합니다.

    ssh-keygen -t ed25519 -C "admin@homelab"
    ssh-copy-id admin@your-server-ip

    ed25519는 현재 OpenSSH 환경에서 널리 쓰이는 공개키 알고리즘 중 하나입니다. 생성이 간단하고 관리도 편합니다. 서버에 키가 들어갔으면 먼저 새 터미널에서 접속 테스트를 해보세요.

    ssh admin@your-server-ip

    비밀번호 대신 키로 잘 들어가면 첫 단계는 성공입니다. 여기서 중요한 포인트! 아직 비밀번호 로그인은 바로 끄지 마세요. 키 접속이 확실히 되는 걸 먼저 확인해야 합니다.

    3-2. 2단계: sshd_config 설정으로 기본값 강화하기

    다음은 SSH 데몬 설정 파일인 /etc/ssh/sshd_config를 조정합니다. 배포판마다 약간 다를 수 있지만, 핵심 방향은 거의 같습니다.

    sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
    sudo vi /etc/ssh/sshd_config

    제가 자주 넣는 기본 설정 예시는 이렇습니다.

    Port 22
    PermitRootLogin no
    PasswordAuthentication no
    PubkeyAuthentication yes
    ChallengeResponseAuthentication no
    UsePAM yes
    MaxAuthTries 3
    LoginGraceTime 30
    X11Forwarding no
    AllowUsers admin

    각 항목은 이렇게 보시면 됩니다.

    1. PermitRootLogin no: root 직접 로그인 차단
    2. PasswordAuthentication no: 비밀번호 로그인 비활성화
    3. PubkeyAuthentication yes: 공개키 인증 사용
    4. MaxAuthTries 3: 인증 시도 횟수 제한
    5. LoginGraceTime 30: 로그인 유예 시간 축소
    6. AllowUsers admin: 접속 가능한 계정 제한

    포트 변경은 선택 사항입니다. 예를 들어 22 대신 다른 포트를 쓰는 경우가 많죠. 다만 이건 보안의 본질이라기보다 노이즈 감소 효과에 가깝습니다. 즉, 스캐너 봇을 조금 피하는 정도라고 보시면 됩니다. 그래서 저는 포트 변경만 믿지는 않습니다.

    SSH 설정 파일 기반의 SSH 보안 강화 예시 이미지

    실제 SSH 설정 파일에서 어떤 옵션을 바꾸는지 보여주는 이미지입니다. PermitRootLogin, PasswordAuthentication, AllowUsers 같은 항목을 강조하면 독자가 따라오기 좋습니다.

    3-3. 3단계: 방화벽으로 SSH 접근 범위 줄이기

    SSH 하드닝에서 종종 놓치는 부분이 방화벽입니다. SSH 설정만 좋아도, 네트워크 레벨에서 아무나 접속 시도를 넣을 수 있으면 로그는 계속 더러워집니다. 저도 처음엔 이게 뭔가 싶었는데, 실제로 써보니까 방화벽을 함께 걸어두는 게 체감이 큽니다.

    Ubuntu 계열이라면 UFW(Uncomplicated Firewall, 간편 방화벽)로 쉽게 적용할 수 있습니다.

    sudo ufw default deny incoming
    sudo ufw default allow outgoing
    sudo ufw allow from 203.0.113.10 to any port 22 proto tcp
    sudo ufw enable
    sudo ufw status verbose

    만약 고정 IP가 있다면 특정 관리 IP만 허용하는 방식이 가장 깔끔합니다. 고정 IP가 없으면 VPN(Virtual Private Network, 가상 사설망) 뒤에 SSH를 두는 방법도 좋고요. 이 부분은 다음 글에서 홈랩 기준으로 따로 다뤄볼 예정입니다.

    Rocky Linux나 RHEL 계열에서는 firewalld를 많이 씁니다.

    sudo firewall-cmd --permanent --add-service=ssh
    sudo firewall-cmd --reload
    sudo firewall-cmd --list-all

    여기서 한 가지. 포트를 바꿨다면 방화벽 정책도 꼭 같이 바꿔야 합니다. 예전에 SSH 포트는 바꿨는데 UFW 허용 포트는 그대로 둬서, 왜 접속이 안 되나 한참 삽질한 적 있습니다 ㅎㅎ

    3-4. 4단계: 반복 공격 자동 차단하기

    무차별 대입 공격은 로그를 보면 금방 티가 납니다. 이런 시도는 사람이 일일이 대응하기보다 자동 차단이 낫습니다. 이럴 때 많이 쓰는 게 Fail2ban입니다. 로그를 보고 일정 횟수 이상 실패하면 해당 IP를 잠시 차단합니다.

    sudo apt update
    sudo apt install fail2ban -y

    기본 설정은 배포판마다 다를 수 있어서, 보통은 로컬 설정 파일을 따로 둡니다.

    sudo vi /etc/fail2ban/jail.local
    [sshd]
    enabled = true
    port = 22
    logpath = %(sshd_log)s
    maxretry = 5
    findtime = 10m
    bantime = 1h

    설정 후에는 서비스를 재시작하고 상태를 확인합니다.

    sudo systemctl restart fail2ban
    sudo fail2ban-client status
    sudo fail2ban-client status sshd

    이거 진짜 편하더라고요. 특히 외부에 노출된 테스트 서버에서 체감이 큽니다. 다만 회사 VPN이나 NAT 환경처럼 여러 명이 같은 공인 IP를 쓰는 경우엔 차단 정책을 너무 공격적으로 잡지 않는 게 좋습니다.

    무단 접근 방어를 위한 SSH 하드닝 자동 차단 이미지

    인증 실패가 누적되면 IP가 자동 차단되는 과정을 보여주는 이미지입니다. 로그 분석, 차단 룰 적용, 일정 시간 후 해제 흐름을 함께 표현하면 좋습니다.

    3-5. 5단계: 로그와 실제 재접속으로 검증하기

    설정을 다 바꿨다면 마지막은 검증입니다. 여기서 대충 넘어가면 나중에 더 크게 고생합니다. 저는 항상 기존 세션은 유지한 채 새 터미널로 다시 붙어보고, 실패 로그와 서비스 상태까지 확인합니다.

    sudo sshd -t
    sudo systemctl restart sshd
    sudo systemctl status sshd
    sudo journalctl -u sshd -n 50 --no-pager

    배포판에 따라 서비스 이름이 ssh 또는 sshd일 수 있습니다. Debian/Ubuntu 계열은 ssh, RHEL 계열은 sshd인 경우가 많으니 현재 환경에 맞춰 확인해보세요.

    ssh -i ~/.ssh/id_ed25519 admin@your-server-ip

    검증할 때는 다음 항목을 체크합니다.

    • 공개키 인증으로 정상 접속되는지
    • root 직접 로그인은 막혔는지
    • 비밀번호 로그인 시도가 거절되는지
    • 허용하지 않은 계정은 접근 불가인지
    • 로그에 이상한 반복 시도가 남는지

    4. ⚠️ 주의사항과 트러블슈팅

    이 섹션은 진짜 경험에서 나옵니다. 문서만 보면 쉬워 보이는데, 실제 적용할 땐 자잘한 함정이 꽤 있습니다.

    4-1. 공개키 권한 문제

    키를 넣었는데도 접속이 안 되면 파일 권한부터 보세요. SSH는 권한이 느슨하면 키를 무시하는 경우가 있습니다.

    chmod 700 ~/.ssh
    chmod 600 ~/.ssh/authorized_keys

    서버 쪽 홈 디렉터리 권한까지 확인하면 더 좋습니다.

    4-2. 설정 테스트 없이 재시작한 경우

    이건 정말 위험합니다. 문법 오류가 있으면 SSH 서비스가 안 올라올 수 있습니다. 그래서 재시작 전에 꼭 아래처럼 테스트합니다.

    sudo sshd -t

    저도 예전에 옵션 하나 오타 내고 바로 재시작했다가 콘솔 붙을 때까지 식겁했습니다.

    4-3. AllowUsers 설정 실수

    AllowUsers는 강력하지만, 자기 계정을 빼먹으면 그대로 잠깁니다. 여러 관리자 계정을 운영한다면 미리 목록을 정리해두세요.

    4-4. 포트 변경 후 SELinux 또는 방화벽 누락

    RHEL 계열에서 SELinux(Security-Enhanced Linux, 보안 정책 프레임워크)를 쓰고 있다면, SSH 포트를 바꿀 때 추가 설정이 필요한 경우가 있습니다. 방화벽만 열고 끝이라고 생각하면 안 됩니다. 이 구간은 환경마다 차이가 있어서 변경 후 반드시 실제 접속 테스트를 해야 합니다.

    5. 검증 결과: 적용 후 무엇이 달라졌나

    SSH 보안 강화를 마치고 나면 가장 먼저 보이는 변화는 로그의 질이 달라진다는 점입니다. 무작정 인증 실패가 쌓이던 서버도, 허용 계정 제한과 공개키 인증을 적용하면 의미 없는 로그인 시도가 대부분 초기에 걸러집니다. Fail2ban까지 붙이면 반복적인 시도는 자동으로 정리되고요.

    제가 직접 해보니 운영 피로도가 줄어드는 게 제일 컸습니다. 예전엔 auth.log나 journalctl을 볼 때 괜히 찝찝했는데, 하드닝 후에는 “들어오려 해도 못 들어오게 해놨다”는 안정감이 생기더라고요. 물론 보안은 한 번 세팅했다고 끝이 아닙니다. 계정 관리, 패치, 키 교체 주기 같은 운영 습관까지 같이 가야 합니다.

    SSH 보안 강화 적용 후 검증 결과를 보여주는 이미지

    정상 접속은 성공하고, 반복 공격 IP는 차단되는 결과를 보여주는 이미지입니다. 운영자가 검증해야 할 포인트를 한 화면에 담는 구성이 좋습니다.

    6. 보안 조치별 효과와 주의사항

    보안 조치 효과 주의할 점
    공개키 인증 비밀번호 추측 공격 감소 키 백업과 권한 관리 필수
    root 로그인 차단 관리자 계정 직접 공격 표면 축소 sudo 가능한 일반 계정 필요
    AllowUsers 허용 계정만 접속 가능 계정 누락 시 본인도 차단
    방화벽 제한 접속 가능한 출발지 축소 원격 관리 IP 변경 시 규칙 수정 필수
    Fail2ban 반복 공격 자동 차단 공유 IP 환경에서는 과차단 주의

    결국 SSH 하드닝은 하나만 바꾸는 게임이 아닙니다. 네트워크, 인증, 계정, 로그가 함께 움직여야 리눅스 서버 보안 수준이 진짜 올라갑니다.

    7. 자주 묻는 질문

    Q1. SSH 포트만 바꾸면 충분한가요?

    아닙니다. 포트 변경은 노이즈를 줄이는 데는 도움이 되지만, 근본적인 SSH 보안 강화가 아닙니다. 공개키 인증, root 로그인 차단, 방화벽 제한을 같이 보셔야 합니다.

    Q2. 비밀번호 로그인을 바로 꺼도 되나요?

    새 터미널에서 공개키 접속이 정상 확인된 뒤에 끄는 걸 권장합니다. 저도 처음엔 성급하게 껐다가 다시 들어가지 못할 뻔했습니다.

    Q3. 홈랩에도 이 정도까지 해야 하나요?

    인터넷에 노출되어 있다면 네, 하는 게 맞습니다. 특히 포트 포워딩(port forwarding, 공유기 포트 개방)을 해둔 환경이라면 더 그렇습니다.

    8. 마무리: SSH 보안 강화, 기본이 최고

    오늘 정리한 5단계는 화려한 고급 보안 장비 이야기가 아닙니다. 하지만 실제 운영에서는 이런 기본기가 제일 오래 갑니다. 저도 13년 가까이 인프라 쪽 일을 하면서 느낀 게, 큰 사고를 막는 건 대단한 한 방보다 이런 기본 설정의 누적이더라고요. 처음엔 귀찮아 보여도 한 번 잡아두면 이후 운영이 훨씬 편해집니다. 드디어 됐다! 하는 순간이 옵니다.

    정리하면 이렇습니다.

    1. 공개키 인증으로 전환합니다.
    2. root 로그인과 비밀번호 로그인을 줄입니다.
    3. 허용 계정과 방화벽으로 노출 범위를 좁힙니다.
    4. Fail2ban으로 반복 공격을 자동 차단합니다.
    5. 마지막으로 반드시 재접속과 로그 검증까지 합니다.

    혹시 지금 운영 중인 서버에서 SSH 보안 강화를 손봐야 하는데 어디부터 건드려야 할지 막막하셨다면, 오늘 글 순서대로 하나씩 해보시면 됩니다. 다음 글에서는 VPN 뒤에 SSH 숨기기, 그리고 홈랩 기준 점프 서버(Jump Server, 중간 접속 서버) 구성도 다뤄볼 예정입니다. 이전 글에서 다뤘던 로그 모니터링과 함께 묶으면 더 탄탄해집니다.

    SSH 하드닝 5단계 요약 인포그래픽 이미지

    글 전체 내용을 5단계 체크리스트로 요약한 인포그래픽 이미지입니다. 마무리 섹션 직전에 배치해 독자가 핵심을 빠르게 복습할 수 있게 합니다.