Published on

Azure VM SSH 타임아웃 10분 진단 - NSG·UDR·DNS

Authors

Azure VM에 SSH(22/tcp)가 **"Connection timed out"**로 끊기면, 대부분은 VM 자체 문제가 아니라 네트워크 경로(인바운드/라우팅/DNS) 어딘가에서 패킷이 사라지는 케이스입니다. 이 글은 “지금 당장 접속이 안 된다”는 상황에서 NSG → UDR → DNS 순으로 10분 안에 원인을 좁히는 흐름으로 구성했습니다.

> 목표: 1) 어디서 막히는지(클라이언트/인터넷/서브넷/VM) 2) 무엇을 바꿔야 하는지(NSG/UDR/DNS) 3) 바꾼 뒤 어떻게 검증하는지(Network Watcher)까지 한 번에 끝내기

0. 증상 분류: 타임아웃 vs 거부 vs 인증 실패

먼저 메시지로 범위를 줄입니다.

  • Timeout: 경로 상에서 드롭(방화벽/NSG/UDR/NVA/온프레 FW) 가능성이 큼
  • Connection refused: 22 포트까지 도달했는데 SSH 데몬이 안 뜨거나 로컬 방화벽에서 리젝트
  • No route to host: 로컬/중간 라우팅 문제 가능성
  • Permission denied (publickey): 네트워크는 정상, 인증 문제

이 글은 Timeout을 전제로 합니다.

1. 1분 확인: VM에 공인 IP가 있고, 대상이 맞는가

의외로 “다른 VM IP로 접속” “공인 IP 변경” “NIC가 여러 개” 같은 단순 실수가 많습니다.

Azure CLI로 공인 IP/NIC 확인

# VM의 NIC와 공인 IP 확인
az vm show -g <rg> -n <vm> --query "networkProfile.networkInterfaces[].id" -o tsv

# NIC에서 IP 구성 확인
az network nic show -g <rg> -n <nicName> --query "ipConfigurations[].{name:name, private:privateIPAddress, pip:publicIPAddress.id}" -o jsonc

# 공인 IP 리소스에서 실제 IP 확인
az network public-ip show --ids <publicIpResourceId> --query "ipAddress" -o tsv
  • 공인 IP가 없다면 인터넷에서 직접 SSH는 불가합니다. 이 경우는 Bastion, VPN/ExpressRoute, 점프박스, Azure Serial Console 같은 경로로 접근해야 합니다.

2. 3분 진단: NSG 인바운드 규칙(서브넷/니크 둘 다)

SSH 타임아웃의 1순위는 NSG입니다. 특히 Azure는 NIC NSG + Subnet NSG가 동시에 걸릴 수 있고, 둘 중 하나라도 막으면 드롭됩니다.

NSG 규칙에서 반드시 보는 포인트

  • 적용 위치: NIC에 붙은 NSG인지, Subnet에 붙은 NSG인지
  • 우선순위(Priority): 작은 숫자가 먼저 적용
  • Source: *로 열어둔 게 아니라면 “내 공인 IP/대역”이 맞는지
  • Destination port: 22인지(커스텀 포트면 그 포트)
  • Deny 규칙: DenyAllInbound보다 높은 우선순위로 Allow가 있어야 함

CLI로 NSG 유효 규칙(Effective) 확인

# NIC에 대해 '유효 보안 규칙'을 조회
az network nic list-effective-nsg -g <rg> -n <nicName> -o jsonc

여기서 destinationPortRange/destinationPortRanges에 22가 허용되는지, access: Allow인지 확인합니다.

Network Watcher로 “이 트래픽이 허용되는가?” 즉시 판정

NSG 규칙을 눈으로 읽는 대신, Azure가 제공하는 판정기를 쓰면 빠릅니다.

# NSG 흐름 확인(인바운드) - source는 내 공인 IP
az network watcher test-ip-flow \
  --resource-group <rg> \
  --vm <vm> \
  --direction Inbound \
  --protocol TCP \
  --local 10.0.1.4:22 \
  --remote <MY_PUBLIC_IP>:54321

출력에서 access: Allow/DenyruleName이 나옵니다.

  • Deny면: 해당 규칙을 수정/추가하면 됩니다.
  • Allow인데도 타임아웃이면: **NSG 바깥(UDR/NVA/온프레 방화벽/경로)**를 봐야 합니다.

3. 3분 진단: UDR(사용자 정의 라우트)로 인해 “인터넷에서 들어온 패킷의 복귀 경로”가 깨졌는가

SSH는 TCP이므로 3-way handshake가 성립해야 합니다.

  • 클라이언트 → VM (SYN)
  • VM → 클라이언트 (SYN-ACK)
  • 클라이언트 → VM (ACK)

UDR/가상 어플라이언스(NVA)/방화벽을 쓰는 환경에서 흔한 문제는 **비대칭 라우팅(asymmetric routing)**입니다.

  • 인바운드는 공인 IP/NAT를 통해 VM까지 왔는데
  • 아웃바운드(응답)가 UDR 때문에 다른 경로(예: NVA, 온프레)로 빠져나가면서
  • 상태 기반 방화벽이 세션을 모르는 패킷으로 보고 드롭 → 결과는 타임아웃

서브넷 라우팅 테이블 연결 여부 확인

# 서브넷에 연결된 route table 확인
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

특히 아래 패턴이면 의심하세요.

  • 0.0.0.0/0VirtualAppliance(NVA)로 강제 라우팅
  • Internet으로 나가야 할 트래픽을 온프레/방화벽으로 보내는 규칙

Network Watcher: 다음 홉(Next hop)으로 경로 확인

az network watcher show-next-hop \
  --resource-group <rg> \
  --vm <vm> \
  --source-ip 10.0.1.4 \
  --dest-ip 8.8.8.8 -o jsonc
  • nextHopType: Internet이 기대인데 VirtualAppliance가 나오면, SSH 응답이 NVA로 나가며 세션이 깨질 수 있습니다.

빠른 수정 아이디어(상황별)

  • 특정 관리 IP 대역만 NVA를 타게 하고 나머지는 인터넷으로: UDR를 더 구체적으로 분리
  • 방화벽/NVA를 반드시 거쳐야 한다면:
    • 상태 테이블/정책에서 22/tcp 인바운드 세션을 허용
    • SNAT/DNAT 정책 및 리턴 트래픽 경로를 일치
  • 급한 복구 목적이라면(권장 X):
    • 임시로 UDR 연결 해제(서브넷에서 route table detach) 후 접속 확인

4. 2분 진단: DNS 문제로 “SSH 타임아웃처럼 보이는” 케이스

DNS는 보통 “호스트명을 못 찾는다”로 끝나지만, 다음 상황에서는 결과적으로 타임아웃처럼 보일 수 있습니다.

  • 접속 대상이 FQDN인데, DNS가 오래 걸리거나 잘못된 IP를 반환
  • 사설 DNS(커스텀 DNS 서버)가 다운/방화벽에 막힘
  • Private Endpoint/Private DNS Zone 구성으로 인해 공인 IP 대신 사설 IP로 해석

클라이언트에서 실제로 어디로 붙는지 확인

# macOS/Linux
nslookup <hostname>
dig +short <hostname>

# Windows
nslookup <hostname>

반환된 IP가:

  • 기대한 공인 IP인지
  • 혹은 VNet 내부용 사설 IP로 떨어지는지 확인합니다.

Azure 측 DNS 설정 확인 포인트

  • VNet의 DNS 서버가 Azure-provided인지, 커스텀 DNS인지
  • 커스텀 DNS라면 해당 DNS 서버로의 경로(NSG/UDR)가 살아있는지
az network vnet show -g <rg> -n <vnet> --query "dhcpOptions.dnsServers" -o jsonc

5. 그래도 안 되면: “NSG/UDR/DNS 외” 5분 추가 체크

여기까지가 10분 진단의 핵심이지만, 실무에서 자주 엮이는 항목을 짧게 덧붙입니다.

5.1 Azure Bastion/점프박스로는 되는데, 공인 SSH만 안 된다

  • 공인 인바운드만 막혀 있는 상태일 가능성이 큼
  • NSG에서 AzureBastionSubnet/점프박스 대역은 허용했지만 인터넷 대역은 막았을 수 있음

5.2 VM 내부 방화벽/sshd 상태

네트워크가 열려도 VM에서 22를 안 듣고 있으면 접속이 안 됩니다(보통 refused지만 정책에 따라 드롭처럼 보일 수 있음).

  • Linux:
    • sudo systemctl status sshd
    • sudo ss -lntp | grep :22
    • sudo ufw status / firewalld 확인

5.3 Network Security Group Flow Logs / Packet Capture

정말 애매하면 Network Watcher로 패킷 캡처를 떠서 SYN이 들어오는지, SYN-ACK가 나가는지를 보면 결론이 납니다.

  • SYN이 안 들어옴: 인바운드(NSG/상위 방화벽)
  • SYN은 들어오는데 SYN-ACK가 안 나감: UDR/아웃바운드/로컬 방화벽
  • SYN-ACK는 나가는데 클라이언트가 못 받음: 리턴 경로/중간 방화벽

6. 실전 체크리스트(복붙용)

아래 순서대로 보면 대부분 10분 안에 결론이 납니다.

  1. 대상 확인: 공인 IP/VM/NIC가 맞나
  2. NSG Effective rules에서 22/tcp 인바운드 Allow인가
  3. test-ip-flow 결과가 Allow인가(Deny면 ruleName으로 즉시 수정)
  4. 서브넷에 UDR이 붙어 있나, 0.0.0.0/0 -> NVA가 있나
  5. show-next-hop에서 인터넷 목적지의 nextHopType이 기대와 같은가
  6. DNS로 접속한다면 실제 해석 IP가 무엇인가(공인/사설 혼동)

7. 마무리: “타임아웃”은 대부분 경로 문제다

Azure VM SSH 타임아웃은 VM을 재부팅한다고 해결되는 경우가 드뭅니다. NSG(정책) → UDR(경로) → DNS(대상) 3가지만 체계적으로 보면, “어디서 드롭되는지”가 빠르게 드러납니다.

비슷한 방식으로 ‘10분 진단’ 접근을 다른 장애에도 적용할 수 있습니다. 예를 들어 네트워크 비용/경로 이슈를 빠르게 좁히는 방법은 VPC NAT Gateway 비용 폭증 10분 진단·절감 글의 체크리스트 구조도 참고할 만합니다. 또한 타임아웃을 재현하고 원인을 분해하는 접근은 OpenAI Responses API 504 Timeout 재현·해결처럼 “재현 → 관측 → 가설 검증” 흐름으로 가져가면 훨씬 빨라집니다.


부록: 가장 흔한 원인 TOP 3

  1. NSG에 22 인바운드 Allow가 없거나, Source IP가 내 IP가 아님
  2. UDR로 기본 경로가 NVA로 빠지면서 비대칭 라우팅 발생
  3. FQDN이 Private DNS로 사설 IP를 반환(공인 SSH를 시도하는 줄 알았는데 내부 IP로 가는 중)