- Published on
Azure VM SSH 타임아웃 10분 진단 - NSG·UDR·DNS
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
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/Deny와 ruleName이 나옵니다.
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/0→VirtualAppliance(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 sshdsudo ss -lntp | grep :22sudo 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분 안에 결론이 납니다.
- 대상 확인: 공인 IP/VM/NIC가 맞나
- NSG Effective rules에서 22/tcp 인바운드 Allow인가
test-ip-flow결과가 Allow인가(Deny면 ruleName으로 즉시 수정)- 서브넷에 UDR이 붙어 있나,
0.0.0.0/0 -> NVA가 있나 show-next-hop에서 인터넷 목적지의 nextHopType이 기대와 같은가- DNS로 접속한다면 실제 해석 IP가 무엇인가(공인/사설 혼동)
7. 마무리: “타임아웃”은 대부분 경로 문제다
Azure VM SSH 타임아웃은 VM을 재부팅한다고 해결되는 경우가 드뭅니다. NSG(정책) → UDR(경로) → DNS(대상) 3가지만 체계적으로 보면, “어디서 드롭되는지”가 빠르게 드러납니다.
비슷한 방식으로 ‘10분 진단’ 접근을 다른 장애에도 적용할 수 있습니다. 예를 들어 네트워크 비용/경로 이슈를 빠르게 좁히는 방법은 VPC NAT Gateway 비용 폭증 10분 진단·절감 글의 체크리스트 구조도 참고할 만합니다. 또한 타임아웃을 재현하고 원인을 분해하는 접근은 OpenAI Responses API 504 Timeout 재현·해결처럼 “재현 → 관측 → 가설 검증” 흐름으로 가져가면 훨씬 빨라집니다.
부록: 가장 흔한 원인 TOP 3
- NSG에 22 인바운드 Allow가 없거나, Source IP가 내 IP가 아님
- UDR로 기본 경로가 NVA로 빠지면서 비대칭 라우팅 발생
- FQDN이 Private DNS로 사설 IP를 반환(공인 SSH를 시도하는 줄 알았는데 내부 IP로 가는 중)