Connection refused와 Connection timed out은 형제처럼 보입니다. 어느 쪽이든 SSH가 연결되지 않았으니까요. 하지만 둘은 정반대의 소견이고, 스택의 서로 다른 절반을 가리킵니다. 지금 보는 게 어느 쪽인지 아는 것만으로 진단 명령을 한 줄도 입력하기 전에 용의자 목록이 절반으로 줄어듭니다. 이건 2분짜리 루틴입니다. 각 오류가 무엇을 증명하는지, 둘을 나머지 모든 것과 갈라 주는 단 하나의 탐침, 그리고 각 갈래의 체크리스트입니다. (TCP 연결이 성공한 뒤에 일어나는 오류 — 인증 실패, 호스트 키 경고, 죽는 세션 — 는 오류 해독기를 보세요.)
| 오류 | 무엇을 증명하나 | 유력한 원인 | 첫 명령 |
|---|---|---|---|
Connection refused | 호스트가 떠 있고 적극적으로 거절함(RST가 돌아옴) | 그 포트에 수신 중인 것이 없거나, REJECT 방화벽 규칙 | 콘솔에서 ss -tlnp | grep sshd |
Connection timed out | 아무것도 돌아오지 않음 — 경로의 침묵 | 틀린 IP, 죽은 호스트, 또는 조용한 DROP(클라우드 보안 그룹 / 방화벽) | nc -vz host 22, 그다음 클라우드 방화벽 확인 |
각 오류가 실제로 증명하는 것
Connection refused는 머신이 응답했다는 뜻입니다. TCP SYN이 살아 있는 호스트에 닿았고, 호스트가 적극적으로 거절했습니다. RST 패킷이나 ICMP port-unreachable이 돌아왔습니다. 이건 놀랄 만큼 많은 좋은 소식입니다. IP가 맞고, 경로가 통하고, 호스트가 떠 있으며, 경로의 무엇도 내 패킷을 먹지 않습니다. 문제는 좁습니다. 그 포트에 수신 중인 것이 없거나, 방화벽 규칙이 그것을 (드롭이 아니라) 거부하고 있습니다.
Connection timed out은 침묵을 뜻합니다. 내 패킷은 떠났는데 아무것도 돌아오지 않았습니다. 이것은 서버에 대해 거의 아무것도 증명하지 못합니다. 죽었을 수도, IP가 틀렸을 수도, 아니면 (가장 흔하게) 나와 서버 사이의 무언가가 조용히 패킷을 드롭하고 있을 수도 있습니다. 클라우드 보안 그룹, DROP 정책 방화벽, NAT나 VPN 블랙홀입니다.
암기 요령 하나: refused = 머신이 내게 말을 걸었다; timed out = 끝내 말이 없었다. refused는 호스트 문제입니다. timed out은 대개 경로 문제입니다.
1단계: 호스트가 아니라 포트를 탐침하기
첫 수로 ping은 건너뛰세요. 양쪽 모두에서 엉뚱한 것을 테스트합니다. ping에 응답하는 호스트도 포트 22를 드롭할 수 있고(클라우드 방화벽은 포트별로 거릅니다), ping을 무시하는 호스트도 SSH를 멀쩡히 제공할 수 있습니다(ICMP는 곳곳에서 막혀 있습니다). ping은 필요한 것을 아무것도 증명하지 못합니다.
실제 포트를 탐침하세요:
nc -vz example.com 22
(netcat이 없나요? telnet example.com 22도 됩니다. SSH-2.0-...로 시작하는 출력이 나오면 sshd가 응답한 것입니다.) 결과는 셋입니다:
Connection refused— 아래 refused 갈래로 가세요.- 멈췄다가 시간 초과 — timed-out 갈래로 가세요.
- 성공 — TCP는 멀쩡하고 문제는 SSH 자체 안에 있습니다. 잘못된 사용자나 키(
Permission denied (publickey)는 별도 안내가 있습니다(영어)), 호스트 키 불일치, 또는ssh -v가 대놓고 이름을 알려 줄 무언가입니다.
refused 갈래: 무엇이 수신(안) 하는지 찾기
상자에 들어갈 다른 경로가 필요합니다. 공급사의 웹 콘솔이 바로 이 순간을 위해 존재합니다.
sshd가 돌고 있나요?
systemctl status sshd # the unit is "ssh" on Debian/Ubuntu
죽었다면 그냥 재시작하지 말고 왜인지 확인하세요. journalctl -u sshd(또는 -u ssh)가 어떻게 종료됐는지 보여 줍니다. 흔한 원인은 파싱되지 않는 설정 편집입니다. sshd -t는 파일을 검증하고 같은 실패로 재시작하기 전에 잘못된 줄의 이름을 알려 줍니다.
생각한 곳에서 수신 중인가요?
sudo ss -tlnp | grep sshd
이 출력에 함정이 둘 숨어 있습니다. 포트: 누군가 SSH를 2222로 옮겼는데 22를 두드리고 있는 것 — ssh -p 2222로 연결하고, 다시는 유추하지 않도록 ~/.ssh/config에(영어) 호스트별로 고정하세요. 주소: ListenAddress 줄이 sshd를 한 인터페이스에만 묶을 수 있어서, 사설 IP에서는 응답하고 공인 IP에서는 거절합니다.
로컬 방화벽이 거부하고 있나요? sudo ufw status, sudo iptables -L -n, 또는 nft list ruleset — 내 포트를 덮는 REJECT 규칙을 찾으세요. REJECT 규칙은 정확히 이 오류를 냅니다. 호스트는 떠 있고, sshd가 수신 중일 수도 있는데, 방화벽이 대신 응답합니다.
차단당한 건 아닌가요? fail2ban의 기본 동작은 거부라서, 차단은 Connection refused처럼 보입니다. 내 IP에서만요. 단서: 휴대폰 핫스팟에서는 되는데 내 네트워크에서는 안 됩니다. 콘솔에서: fail2ban-client status sshd, 그다음 fail2ban-client unban <your-ip> — 그리고 차단을 벌 만큼 잘못된 자격 증명을 자주 재시도한 원인을 고치세요.
timed-out 갈래: 패킷이 죽는 곳 찾기
먼저 주소를 확인하세요. 딱 한 번 창피한 일입니다. ssh -v는 실제로 다이얼하는 IP를 출력합니다. 공급사 대시보드와 대조하세요. 마이그레이션 뒤의 낡은 DNS, 그리고 인스턴스 정지를 넘기지 못한 탄력적 IP가 둘 다 고전입니다.
다음으로 클라우드 방화벽을 확인하세요. 대부분 그게 답이니까요. 보안 그룹(AWS), NSG(Azure), VPC 방화벽 규칙(GCP)은 기본적으로 인바운드 트래픽을 조용히 드롭하는데, 그게 바로 timeout입니다. 내 주소에서 TCP 22를 허용하는 인바운드 규칙을 찾으세요. 교활한 변형: 몇 달 전 ‘My IP’로 잠가 둔 규칙인데 그 뒤로 내 IP가 바뀐 경우입니다. 서브넷 수준의 네트워크 ACL은 보안 그룹이 맞아 보일 때 들여다볼 만한, 덜 찾아가는 두 번째 계층입니다.
클라우드 규칙이 멀쩡하면 경로를 추적하세요:
traceroute example.com # mtr example.com for a live view
sudo traceroute -T -p 22 example.com # TCP to the actual port — closest to the truth
순정 traceroute는 UDP나 ICMP를 쓰는데, 방화벽은 이를 TCP 22와 다르게 취급합니다. -T -p 22 형태는 SSH가 실제로 겪는 것을 테스트합니다. 홉이 응답을 멈추는 지점이 대략 내 패킷이 죽는 곳입니다.
내 쪽을 의심하세요. 호텔, 카페, 사무실 게스트 네트워크는 으레 아웃바운드 22를 막고, 회사 VPN은 그걸 다시 라우팅하거나 드롭합니다. 격리 테스트는 휴대폰 핫스팟입니다. 거기서 SSH가 되면 서버는 멀쩡하고 있던 네트워크가 문제입니다.
호스트 자체의 DROP 정책 방화벽은 바깥에서 보면 클라우드 쪽 드롭과 똑같아 보입니다. 콘솔 접근이 가능하면 상자의 iptables/nftables도 확인하세요.
이색적인 경우: 포트 노킹. knockd를 돌리는 서버는 다른 포트들에 대한 비밀 연결 시도 순서를 볼 때까지 모든 것을 드롭합니다. 전임자가 상자를 세팅했고 모든 포트가 어디서나 timeout이라면, 라우팅 이론에 오후 한나절을 태우기 전에 주변에 물어보세요.
2분짜리 버전 간직하기
루틴 전체는 잘 압축됩니다: nc -vz로 포트를 탐침한다. Refused → 콘솔로 들어가 sshd, 수신 포트, REJECT 규칙, fail2ban을 확인한다. Timed out → IP를 확인하고, 클라우드 보안 그룹을 확인한 다음 traceroute와 내 네트워크를 본다. ping은 어디에도 나오지 않습니다.
반복되는 사실 — 어느 포트, 어느 사용자, 어느 호스트에 keepalive가 필요한지 — 는 내 기억이 아니라 ~/.ssh/config에(영어) 있어야 합니다. 그리고 진단의 일부는 장애 한복판의 허둥댐이 아니라 상시 상태일 수 있습니다. Termalin은 저장된 호스트마다 포트, 사용자, 키, keepalive 설정을 함께 저장하고 호스트별 실시간 상태를 보여 줍니다. 그래서 ‘상자가 떠 있기나 한가?‘와 ‘포트가 뭐였더라?‘가 연결하기 전에 답이 되고, 2222에서만 수신하는 서버가 분기마다 다시 발견하는 대상이 아니게 됩니다.
Termalin은 무료 크로스 플랫폼 SSH 클라이언트로, 호스트별 포트·사용자·키·keepalive를 호스트와 함께 보관하고 호스트의 실시간 상태를 보여 줍니다. 다운로드하거나, 전체 기능 목록을 둘러보세요.