Published on

Azure VM SSH 접속 안됨 - NSG·UDR·JIT 점검 체크리스트

Authors

서버에 SSH가 갑자기 안 붙을 때 가장 흔한 함정은 **VM 내부(ssh 데몬, 방화벽)**가 아니라 **Azure 네트워크 계층(NSG/UDR/JIT)**에서 조용히 차단되는 경우입니다. 특히 “어제까지 되던 접속이 오늘부터 안 됨” 패턴은 JIT 만료, NSG 규칙 우선순위 변경, UDR의 다음 홉 오설정(방화벽/NVA)로 자주 설명됩니다.

이 글은 Azure VM SSH 접속 불가 상황을 빠르게 분류하고, NSG·UDR·JIT를 중심으로 원인을 재현 가능하게 좁히는 체크리스트를 제공합니다.

> 참고: 권한/정책 문제를 진단하는 접근은 클라우드 전반에 유사합니다. OIDC 기반 권한거부 패턴 정리는 GitHub Actions OIDC 403·권한거부 원인 7가지도 함께 보면 사고방식에 도움이 됩니다.

1) 증상 분류: “연결이 안 됨”을 3가지로 나누기

SSH 실패는 메시지에 따라 원인이 크게 갈립니다.

  • Timeout (Connection timed out): 네트워크 경로/정책 차단 가능성이 큼 (NSG, UDR, JIT, Public IP, 라우팅)
  • Connection refused: VM까지는 도달했지만 22/tcp 리스닝 없음(ssh 서비스 다운, OS 방화벽, 포트 변경)
  • No route to host / Host unreachable: 라우팅/서브넷/UDR/NVA 문제 가능성 큼

가장 먼저 로컬에서 포트 도달성을 확인합니다.

# 1) DNS 확인
nslookup <public-ip-or-fqdn>

# 2) 22번 포트 TCP 연결 확인
nc -vz <public-ip> 22
# 또는
curl -v telnet://<public-ip>:22

# 3) 경로 확인(참고용)
traceroute <public-ip>
  • nctimeout이면 NSG/UDR/JIT 쪽을 우선 의심합니다.
  • ncsucceeded인데 SSH만 실패하면 키/계정/sshd 설정 등 VM 내부로 넘어갑니다.

2) 가장 흔한 1순위: JIT(Just-In-Time) 접근 제어 만료/미승인

Azure Defender for Cloud(구 Security Center)의 JIT VM access를 켜두면, NSG에 22번 포트가 항상 열려 있지 않고 승인된 시간/소스 IP에만 임시로 허용 규칙이 생성됩니다.

2.1 JIT 사용 여부 확인

  • Azure Portal → Microsoft Defender for CloudJIT VM access
  • 해당 VM이 JIT 정책 대상인지 확인

2.2 흔한 실수

  • 승인 시간 만료(예: 1시간 승인 후 방치)
  • 승인한 소스 IP가 현재 내 공인 IP와 다름(집/회사/모바일 테더링 전환)
  • 승인 포트가 22가 아니라 커스텀 포트로 되어 있음

2.3 즉시 조치

  • JIT에서 Request access로 22/tcp를 현재 공인 IP에 대해 재승인
  • 운영 환경에서는 승인 시간을 짧게 유지하고, 점프박스/배스천을 도입해 소스 IP 변동 이슈를 줄입니다.

3) NSG 점검: “규칙은 있는데 왜 막히지?”의 정답은 우선순위

NSG(Network Security Group)는 서브넷NIC(네트워크 인터페이스) 양쪽에 붙을 수 있고, 둘 다 적용됩니다. 또한 규칙은 **우선순위(priority)**가 낮은 숫자부터 평가되며, 먼저 매칭되는 규칙이 승리합니다.

3.1 NSG 확인 순서(실전)

  1. VM → Networking → Effective security rules 확인
  2. NIC에 붙은 NSG, Subnet에 붙은 NSG 둘 다 확인
  3. Inbound에 Allow TCP 22가 있어도, 더 앞선 Deny가 있으면 차단됨

3.2 “Allow 22”가 있는데도 막히는 대표 케이스

  • DenyAllInbound 같은 광역 Deny가 더 높은 우선순위(숫자 낮음)
  • Source가 Any가 아니라 특정 IP/대역으로 제한되어 있는데 내 IP가 바뀜
  • Destination이 VM NIC IP가 아니라 다른 주소로 되어 있음
  • 포트가 22가 아니라 2222 등으로 변경되었는데 규칙은 22만 허용

3.3 Azure CLI로 NSG 규칙 빠르게 확인

# NSG 목록
az network nsg list -g <rg> -o table

# 특정 NSG 규칙
az network nsg rule list -g <rg> --nsg-name <nsg> -o table

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

3.4 Network Watcher: “왜 차단됐는지”를 도구로 끝내기

가장 효율적인 방법은 IP Flow Verify입니다.

  • Network Watcher → IP flow verify
    • Direction: Inbound
    • Protocol: TCP
    • Local IP: VM의 NIC private IP
    • Local port: 22
    • Remote IP: 내 공인 IP
    • Remote port: 임의(예: 50000)

결과가 Deny면 어떤 NSG 규칙이 막았는지까지 보여줍니다.

4) UDR(사용자 정의 라우트) 점검: 0.0.0.0/0의 다음 홉이 SSH를 삼킨다

UDR은 서브넷 단위로 적용되며, 잘못 설정되면 **VM의 응답 트래픽이 엉뚱한 곳(NVA/Firewall/VPN)**으로 빠져나가 SSH 세션이 성립하지 않습니다. 특히 아래 패턴이 많습니다.

  • 0.0.0.0/0 -> Virtual appliance로 강제 라우팅
  • NVA/방화벽이 22/tcp를 허용하지 않거나 SNAT/세션 테이블 정책이 맞지 않음
  • 온프레미스 경유(BGP/ExpressRoute/VPN)로 보내는데 리턴 경로가 비대칭

4.1 Effective routes로 실제 라우팅 확인

Azure Portal에서 VM → Networking → Effective routes를 확인하거나 CLI로 봅니다.

# NIC의 유효 라우트 확인
az network nic show-effective-route-table \
  -g <rg> -n <nic-name> \
  -o table

여기서 확인할 것:

  • 0.0.0.0/0의 Next hop type이 Internet인지, VirtualAppliance인지
  • 내 공인 IP(또는 내 대역)로 향하는 리턴 트래픽이 어디로 가는지

4.2 UDR이 원인일 때 빠른 검증법

  • (가능하면) 동일 서브넷의 다른 VM도 SSH가 안 되는지 확인
  • UDR을 임시로 분리한 테스트 서브넷/테스트 NIC로 재현
  • NVA/Firewall 로그에서 22/tcp 드롭 여부 확인

> 운영 환경에서는 UDR을 임시 제거하기 어려울 수 있습니다. 이 경우 Network Watcher의 Connection troubleshoot로 경로 중간에서 막히는 지점을 추적하는 것이 안전합니다.

5) Public IP/프론트 도어 구성 점검: “IP는 맞는데 VM이 아니었다”

SSH를 Public IP로 붙을 때, 다음을 확인합니다.

  • VM NIC에 Public IP가 실제로 연결되어 있는가
  • Public IP가 바뀌지 않았는가(동적 할당이면 재시작/재배포 시 변경 가능)
  • Load Balancer/NAT Rule을 통해 들어가는 구조라면
    • Inbound NAT rule이 22 → VM:22로 매핑되는지
    • 백엔드 풀/헬스 프로브가 정상인지

CLI로 Public IP 연결 관계를 확인할 수 있습니다.

# Public IP 리소스 확인
az network public-ip show -g <rg> -n <pip-name> -o jsonc

# NIC의 IP 구성에서 Public IP 연결 확인
az network nic show -g <rg> -n <nic-name> --query "ipConfigurations[].publicIpAddress.id" -o tsv

6) NSG/UDR/JIT이 모두 정상인데도 안 되면: VM 내부(SSHD/OS 방화벽)로 이동

여기까지 점검했는데도 nc -vz <ip> 22가 성공하거나, Network Watcher에서 Allow인데 SSH가 실패하면 VM 내부를 봐야 합니다.

6.1 Azure Run Command로 SSH 없이 점검

SSH가 막혀도 Portal/CLI의 Run Command로 내부 상태를 확인할 수 있습니다.

az vm run-command invoke \
  -g <rg> -n <vm-name> \
  --command-id RunShellScript \
  --scripts "sudo systemctl status ssh --no-pager" \
            "sudo ss -lntp | grep ':22' || true" \
            "sudo ufw status verbose || true" \
            "sudo iptables -S | sed -n '1,120p'"

확인 포인트:

  • sshd 서비스가 running인지
  • 22/tcp가 LISTEN 중인지
  • UFW/iptables가 22를 차단하는지
  • /etc/ssh/sshd_config에서 Port 22가 변경됐는지

7) 10~20분 컷 진단 루틴(요약)

운영에서 가장 빠른 순서로 정리하면 아래와 같습니다.

  1. 로컬에서 nc -vz <ip> 22timeout vs refused 구분
  2. JIT 사용 여부 확인 → 승인 만료/소스 IP 변경 점검
  3. VM의 Effective security rules에서 22/tcp inbound 허용 여부 확인
  4. Network Watcher IP flow verify로 Deny 규칙을 특정
  5. VM의 Effective routes로 UDR/NVA 강제 라우팅 여부 확인
  6. Public IP/LB/NAT 매핑이 맞는지 확인
  7. 마지막으로 Run Command로 sshd/방화벽 상태 확인

8) 재발 방지 팁: “열어두기”보다 “접속 경로를 단순화”

  • 가능하면 Azure Bastion 또는 점프박스로 SSH 진입점을 단일화
  • JIT를 쓴다면 승인 프로세스(시간/소스 IP)를 팀 표준으로 문서화
  • NSG는 규칙 이름에 의도를 명확히(예: allow-ssh-from-office, deny-all-inbound)하고 우선순위 충돌을 줄이기
  • UDR은 변경 시 영향 범위가 크므로, 서브넷 단위로 점진 적용하고 Effective routes 스냅샷을 남기기

SSH 장애는 종종 “한 군데만 보면 끝날 것 같은데” 실제로는 **NSG(정책) + UDR(경로) + JIT(시간/소스 제어)**가 겹쳐서 발생합니다. 위 루틴대로 도구(Network Watcher)로 판정하고, 그 다음에 설정을 수정하면 불필요한 시행착오를 크게 줄일 수 있습니다.