Published on

Azure VM SSH 접속 끊김 - NSG·UDR·Bastion 진단법

Authors

서버가 살아 있고 CPU도 여유로운데 SSH만 툭툭 끊기는 문제는 생각보다 흔합니다. 특히 Azure에서는 NSG(Network Security Group), UDR(User Defined Route), Azure Bastion, 그리고 SNAT 포트/세션 타임아웃이 얽히면서 “어느 순간부터 접속이 끊김” 같은 증상이 발생합니다.

이 글은 “SSH가 아예 안 됨”이 아니라, 접속은 되는데 일정 시간 후 끊기거나, 특정 네트워크에서만 불안정하거나, Bastion으로는 되는데 직접 접속은 끊김 같은 케이스를 대상으로 합니다. 원인을 빠르게 좁히기 위한 순서와, Azure 포털/CLI로 확인하는 구체적인 명령을 함께 제공합니다.

증상 유형부터 분류하기

진단은 증상을 분류하면 속도가 빨라집니다.

1) 접속 후 1~5분 내 끊김

  • 중간 경로(방화벽/프록시/NVA) 세션 타임아웃
  • NAT/SNAT 장비의 idle timeout
  • TCP keepalive 설정 부재

2) 10~30분 또는 특정 시간대에 끊김

  • Azure Firewall/NVA의 세션 테이블/리소스 압박
  • SNAT 포트 고갈(대량 아웃바운드)로 인해 재접속/재전송이 불안정
  • 라우팅 비대칭(UDR로 나가고 시스템 라우트로 들어오는 형태)

3) Bastion에서는 안정적, 공인 IP 직접 SSH는 불안정

  • NSG 인바운드/아웃바운드 규칙 또는 우선순위 문제
  • Public IP 경로에만 존재하는 방화벽/IPS 장비
  • 클라이언트 ISP/회사망에서 22번 차단/세션 강제 종료

4) 특정 서브넷/피어링/VPN에서만 끊김

  • UDR로 트래픽이 NVA로 강제 경유되며 세션 유지 실패
  • VNet Peering에서 Use remote gateways/Allow forwarded traffic 설정 불일치

1단계: VM 자체 문제인지 네트워크 문제인지 빠른 분리

가장 먼저 “VM은 정상인데 네트워크만 끊기는지”를 확인합니다.

VM에서 SSH 데몬/커널 로그 확인

# 최근 SSH 관련 로그(ubuntu/debian)
sudo journalctl -u ssh -S -2h --no-pager

# RHEL/CentOS 계열
sudo journalctl -u sshd -S -2h --no-pager

# 커널 네트워크/드롭 관련
sudo dmesg -T | egrep -i 'tcp|reset|drop|conntrack' | tail -n 50
  • 서버 측 로그에 Connection reset by peer가 반복되면 중간 장비가 세션을 끊는 경우가 많습니다.
  • 로그가 조용한데 클라이언트만 끊기면 경로/보안 정책(NSG/UDR/NVA) 쪽으로 무게가 실립니다.

TCP keepalive(Idle 세션 끊김 완화)

중간 장비가 idle 세션을 정리한다면 keepalive로 체감이 크게 좋아집니다.

# 현재 값 확인
sysctl net.ipv4.tcp_keepalive_time net.ipv4.tcp_keepalive_intvl net.ipv4.tcp_keepalive_probes

# 예시: 60초마다 keepalive 시작, 10초 간격 6회
sudo sysctl -w net.ipv4.tcp_keepalive_time=60
sudo sysctl -w net.ipv4.tcp_keepalive_intvl=10
sudo sysctl -w net.ipv4.tcp_keepalive_probes=6

# 영구 적용
cat <<'EOF' | sudo tee /etc/sysctl.d/99-ssh-keepalive.conf
net.ipv4.tcp_keepalive_time=60
net.ipv4.tcp_keepalive_intvl=10
net.ipv4.tcp_keepalive_probes=6
EOF
sudo sysctl --system

SSH 서버 설정도 함께 조정할 수 있습니다.

sudo sed -i 's/^#\?ClientAliveInterval.*/ClientAliveInterval 30/' /etc/ssh/sshd_config
sudo sed -i 's/^#\?ClientAliveCountMax.*/ClientAliveCountMax 6/' /etc/ssh/sshd_config
sudo systemctl restart ssh || sudo systemctl restart sshd

> keepalive는 “근본 원인 해결”이라기보다 네트워크 장비의 idle timeout과의 마찰을 줄이는 완충재입니다. 그래도 운영 환경에서는 매우 실용적입니다.

2단계: NSG 규칙 — 인바운드만 보지 말고 아웃바운드를 같이 봐야 함

SSH는 인바운드 22만 열면 끝이라고 생각하기 쉽지만, 응답 트래픽(아웃바운드)이 막히면 세션이 이상해집니다. 또한 NSG는 서브넷 NSG + NIC NSG가 동시에 적용될 수 있고, 우선순위도 중요합니다.

Azure CLI로 NSG 유효 규칙 확인

# NIC에 연결된 NSG 확인
az network nic show -g <rg> -n <nicName> --query "networkSecurityGroup.id" -o tsv

# NSG 규칙 목록
az network nsg rule list -g <rg> --nsg-name <nsgName> -o table

흔한 실수 체크리스트

  • Deny All Outbound를 낮은 우선순위(숫자 큼)로 둔 줄 알았는데, 실제로는 우선순위가 더 높아서(숫자 작음) 응답이 막힘
  • Source를 회사 공인 IP로 제한했는데 회사 NAT가 바뀌어 접속이 간헐적으로 실패
  • 서브넷 NSG는 허용인데 NIC NSG가 차단(또는 반대)

포털의 “유효 보안 규칙(Effective security rules)” 확인

  • VM → Networking → NIC 선택 → Effective security rules
  • 규칙이 “어디(NSG)에서” 적용되는지까지 보여주므로, 다중 NSG 환경에서 거의 필수입니다.

3단계: UDR(사용자 정의 라우트) — 비대칭 라우팅이 SSH를 죽인다

SSH가 끊기는 문제에서 UDR은 자주 범인입니다. 특히 아래 패턴이 치명적입니다.

  • 인바운드(클라이언트→VM)는 Public IP로 정상 유입
  • 아웃바운드(VM→클라이언트 응답)는 UDR 때문에 NVA/Firewall로 우회
  • NVA는 해당 세션을 “내가 시작한 연결이 아님”으로 보고 드롭하거나, 상태 테이블이 맞지 않아 RST/드롭

서브넷 라우트 테이블 연결 확인

# 서브넷에 연결된 라우트 테이블 확인
az network vnet subnet show -g <rg> --vnet-name <vnet> -n <subnet> --query "routeTable.id" -o tsv

# 라우트 테이블의 라우트 확인
az network route-table route list -g <rg> --route-table-name <rtName> -o table

“Effective routes(유효 라우트)”로 최종 경로 확인

az network nic show-effective-route-table -g <rg> -n <nicName> -o table

여기서 확인해야 할 핵심은:

  • 0.0.0.0/0(기본 경로)가 Virtual appliance로 가는지
  • 클라이언트 대역(회사망/VPN대역)으로 가는 경로가 의도대로인지
  • 시스템 라우트와 UDR이 충돌하지 않는지

해결 방향

  • SSH 관리 트래픽만이라도 대칭 경로로 만들기(관리 서브넷 분리)
  • NVA를 경유해야 한다면 NVA에서 해당 흐름을 허용 + 상태 추적 정상 동작 확인
  • 필요 시 UDR에 예외 경로(관리자 IP 대역)를 추가해 직접 인터넷 경로로 돌리기

4단계: Azure Bastion — “되는데 불편”이 아니라 “진단 도구”로 쓰기

Bastion은 단순 대안이 아니라 원인 분리를 위한 강력한 기준점입니다.

Bastion으로는 안정적이라면

  • VM 내부(SSHD/OS)는 정상일 가능성이 큼
  • Public IP 경로 또는 외부 네트워크(회사 방화벽, ISP, 중간 보안장비)에서 문제가 발생할 확률이 큼

Bastion 사용 시 체크 포인트

  • Bastion Subnet은 AzureBastionSubnet 이름 규칙과 /26 이상 권장 등 요구사항이 있음
  • NSG가 Bastion 서브넷/대상 VM NIC에 과도하게 걸려 있으면 연결이 불안정할 수 있음

Bastion과 직접 SSH 비교 테스트

  • 동일 VM에 대해
    • Bastion 접속: 안정
    • 공인 IP 직접: 끊김
  • 이 경우 NSG/UDR이 “인터넷 경로”에서만 다르게 적용되는지(예: Public IP가 Standard + NAT Gateway 조합, 혹은 방화벽 경유 등) 집중 확인합니다.

5단계: SNAT/아웃바운드 경로 — “SSH 끊김”이 사실은 포트 고갈/세션 압박일 수 있음

SSH 자체는 연결 수가 많지 않지만, VM에서 동시에 대량 아웃바운드(패키지 업데이트, 크롤러, API 호출, 컨테이너 이미지 pull 등)가 발생하면 SNAT 포트 고갈로 네트워크가 전체적으로 불안정해지고 SSH 체감도 나빠질 수 있습니다.

의심 시그널

  • SSH만이 아니라 apt/yum, curl, 외부 API 호출도 간헐 실패
  • 특정 시간대(배치/크론)와 함께 발생

점검 포인트

  • 아웃바운드가 Load Balancer SNAT에 의존하는지
  • NAT Gateway를 쓰는지(대규모 아웃바운드 안정화에 유리)
  • Azure Firewall/NVA가 세션/포트 리소스를 압박받는지

6단계: 패킷/흐름 단위로 확정하기 — NSG Flow Logs + tcpdump

“느낌상 NSG/UDR 같다”에서 끝내지 말고, 흐름 로그와 패킷 캡처로 확정하면 재발을 줄일 수 있습니다.

VM에서 tcpdump로 RST/재전송 확인

# eth0는 환경에 맞게 변경
sudo tcpdump -i eth0 -nn 'tcp port 22' -vv
  • 끊기는 순간 RST가 어디서 오는지(상대/중간) 추정 가능
  • 재전송이 길게 반복되면 중간 드롭 가능성

NSG Flow Logs(네트워크 관찰)

  • Network Watcher에서 NSG Flow Logs를 켜면 허용/거부 흐름을 확인할 수 있습니다.
  • 운영 환경에서는 로그 비용이 발생하므로, 재현 시간대에만 임시 활성화하는 전략이 좋습니다.

운영에서 자주 쓰는 “결론까지 가는” 진단 순서

  1. Bastion으로 접속이 안정적인지 확인(원인 분리)
  2. VM 로그에서 SSHD/커널 레벨 에러 확인
  3. NSG의 Effective rules로 인바운드/아웃바운드 동시 점검
  4. NIC의 Effective routes로 UDR/비대칭 라우팅 확인
  5. NVA/Azure Firewall 경유 시 세션 타임아웃/상태 추적/규칙 확인
  6. 필요 시 keepalive로 완화 + SNAT/아웃바운드 아키텍처(NAT Gateway 등) 재검토

재발 방지 아키텍처 팁(관리 트래픽을 별도로 설계)

  • 관리용 서브넷 분리: 운영 트래픽(UDR로 NVA 경유)과 관리 트래픽(직접 경로)을 분리하면 비대칭 문제를 크게 줄입니다.
  • Bastion 표준화: 공인 22 포트를 열지 않고 Bastion을 기본 경로로 삼으면, 외부망 변수(ISP/회사 방화벽)도 제거됩니다.
  • NSG는 최소 허용 + 명시적 아웃바운드 정책: “기본 허용”에 기대면 나중에 규칙이 추가되며 예기치 않은 차단이 생깁니다.

같이 읽으면 좋은 글(진단 관점 확장)

네트워크 문제는 결국 “어디에서 막히는지”를 로그와 관찰로 좁히는 게임입니다. 비슷한 방식으로 원인을 체계적으로 분해하는 글로 아래도 참고할 만합니다.

마무리

Azure VM SSH 끊김은 단일 원인보다 **NSG(정책) + UDR(경로) + Bastion(접속 경로) + 세션 타임아웃/SNAT(상태/리소스)**가 함께 작동하면서 나타나는 경우가 많습니다.

핵심은 “추측”이 아니라 Bastion으로 분리 → Effective rules/routes로 최종 적용 상태 확인 → 흐름/패킷으로 확정의 순서로 접근하는 것입니다. 이 흐름대로만 따라가도, ‘가끔 끊김’ 같은 애매한 장애를 재현/증명 가능한 형태로 바꿀 수 있고, 결과적으로 가장 비용 적은 수정(NSG 우선순위 조정, UDR 예외, keepalive, NAT Gateway 도입 등)을 선택할 수 있습니다.