대상 OS: Debian 12 (bookworm)

SSH는 서버 운영의 기본 통로지만, 인터넷 전체에 열어두면 무차별 스캔/대입 시도가 끊이지 않습니다. 가장 효과가 큰 방법은 “인증(키)”도 중요하지만, 그 이전에 **네트워크 레벨에서 접속 가능한 IP를 제한(allowlist)** 하는 것입니다. Debian 12에서는 nftables로 이를 깔끔하게 구현할 수 있습니다.

1) 변경 전 확인(현재 노출/접속 상태)

# SSH 포트/프로세스
sudo ss -lntp | egrep 'sshd|:22'

# 최근 SSH 로그(실패 패턴 확인)
sudo journalctl -u ssh --since 'today' --no-pager | tail -n 120

# nftables 사용 여부/현재 룰셋
sudo systemctl status nftables --no-pager || true
sudo nft list ruleset | head -n 120

2) 전제: ‘키 인증 강제’가 먼저(방화벽은 2중 안전망)

IP 제한을 하더라도, SSH는 기본적으로 키 기반을 강제하는 게 안전합니다.

sudo cp -a /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.$(date +%F_%H%M)
sudoedit /etc/ssh/sshd_config

PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3
LoginGraceTime 20

sudo sshd -t && sudo systemctl reload ssh

3) nftables로 SSH allowlist 적용(예: 고정 IP 1개만 허용)

주의: 원격 서버에서 작업 중이면 **반드시 SSH 세션을 2개 유지**하고, 적용 직후 새 세션으로 재접속 테스트를 하세요.

# 패키지/서비스(이미 있다면 생략)
sudo apt-get update
sudo apt-get install -y nftables
sudo systemctl enable --now nftables

예시 정책(SSH만 allowlist, 나머지 인바운드는 기본 차단 가정이 아니라 “SSH만 추가로 잠그는” 형태):

# 허용할 관리자 IP로 교체
ADMIN_IP="203.0.113.10"

# 규칙 파일 생성/편집
sudoedit /etc/nftables.conf

`/etc/nftables.conf` 예시(핵심만):

#!/usr/sbin/nft -f

flush ruleset

table inet filter {
  chain input {
    type filter hook input priority 0;

    # 기본: 이미 성립된 연결은 허용
    ct state established,related accept

    # 로컬 루프백 허용
    iifname "lo" accept

    # (선택) ping 허용
    ip protocol icmp accept
    ip6 nexthdr icmpv6 accept

    # SSH: 관리자 IP만 허용
    tcp dport 22 ip saddr $ADMIN_IP accept

    # 그 외 SSH는 드롭
    tcp dport 22 drop

    # 나머지 인바운드는 정책에 따라(최소는 drop)
    counter drop
  }

  chain forward {
    type filter hook forward priority 0;
    drop
  }

  chain output {
    type filter hook output priority 0;
    accept
  }
}

적용:

sudo nft -f /etc/nftables.conf
sudo nft list ruleset | sed -n '1,160p'

4) 여러 IP/대역을 허용하고 싶을 때(집/회사/VPN)

# 예: 집/회사/VPN 대역을 여러 개 허용(개념)
# tcp dport 22 ip saddr { 203.0.113.10, 198.51.100.0/24, 10.8.0.0/24 } accept

5) 트러블슈팅(증상→원인→해결)

1) 적용 후 SSH가 끊김

- 원인: ADMIN_IP를 잘못 넣었거나, 실제 접속 IP가 다름(통신사 NAT/회사 프록시)
- 해결: 콘솔 접근(클라우드 콘솔/물리 콘솔) 확보 후 규칙 수정

2) nftables 서비스는 켜져 있는데 규칙이 안 보임

- 원인: conf 경로 불일치 또는 다른 도구(ufw/firewalld)가 혼재
- 해결: `nft list ruleset` 기준으로 단일 진실(source of truth) 만들기

3) IPv6로 접속이 계속 되거나 차단이 이상함

- 원인: IPv4만 제한하고 IPv6 경로를 놓침
- 해결: inet 테이블을 사용하거나 IPv6도 같이 제어

4) 특정 시간대에만 접속이 안 됨

- 원인: 접속 IP가 변동(유동IP)
- 해결: VPN 고정 대역으로 들어오게 설계하거나, DDNS 기반보단 VPN/배스천이 더 안정적

6) 사례(현장에서 자주 겪는 상황)

1) 키 인증을 했다고 방심했는데, 계정이 늘어나면서 퇴사자 계정이 남아 공격 표면이 커짐

2) UFW와 nftables를 동시에 만져서 어느 쪽이 실제로 막는지 모르는 상태가 됨

3) 회사에서 나가는 공인 IP가 여러 개라 allowlist가 자주 틀어짐 → VPN/배스천으로 해결하는 게 장기적으로 편함

결론: SSH 보안은 “키 인증 + 접근 IP 제한” 조합이 가장 효율이 좋습니다. nftables로 네트워크 레벨을 잠그면, 공격자가 인증을 시도할 기회 자체가 크게 줄어듭니다.