infra
Platform

모듈 맵

[Network] telnet과 nc(netcat) 명령어로 L4 포트 통신 상태 점검

0 / 35 완료

펼치기
0 / 35 완료0%

네트워크 트러블슈팅 · 12 / 35

[Network] telnet과 nc(netcat) 명령어로 L4 포트 통신 상태 점검

telnet과 nc(netcat)를 사용하여 TCP/UDP 포트의 개방 여부를 확인하고, Connection Refused와 Connection Timeout의 차이를 이해합니다.

🚨INCIDENT ALERT
HIGH

DB 접속 실패 로그가 쏟아지는데 애플리케이션 설정이 틀린 건지 포트가 막힌 건지 알 수 없습니다. ping은 성공하지만 TCP 3306 연결은 별개의 문제입니다.

L4 포트 점검을 빠르게 끝내야 앱, 방화벽, 서비스 중 어디를 볼지 결정할 수 있습니다.

L4 포트 통신 점검: telnet과 nc

서비스가 갑자기 안 된다는 신고를 받았을 때, 가장 먼저 해야 할 일은 네트워크 레이어를 단계적으로 좁혀 나가는 것입니다. L3(IP)까지는 ping으로 확인했다면, 다음은 L4(TCP/UDP 포트)가 열려 있는지 확인해야 합니다.

이 챕터에서는 현장에서 가장 많이 사용하는 두 도구 — telnetnc(netcat) — 를 사용하여 포트 통신을 점검하는 방법을 익힙니다.


💼
실무 맥락
현업 패턴

오전 9시, 개발팀에서 "새로 배포한 API 서버에 연결이 안 된다"는 티켓이 들어옵니다. 애플리케이션 로그에는 아무것도 없습니다. 이 상황에서 인프라 엔지니어가 가장 먼저 하는 것은 포트 레벨에서 통신이 되는지 확인하는 것입니다.

telnet api-server 8080 하나로 문제가 방화벽인지, 데몬인지, 아니면 네트워크 경로인지를 30초 안에 구분할 수 있습니다. 이 판단이 빠를수록 장애 복구 시간(MTTR)이 줄어듭니다.


telnet으로 포트 연결 확인

telnet은 원래 원격 터미널 접속 프로토콜이지만, 오늘날 인프라 엔지니어들은 포트 연결 테스트 도구로 주로 사용합니다.

이번 챕터에서 배울 것
  • 1telnet을 포트 연결 테스트 도구로 활용할 수 있다
  • 2TCP 3-Way Handshake의 성공·실패 유형별 결과를 해석할 수 있다
  • 3Connection Refused와 Connection Timeout의 근본적 차이를 구분할 수 있다
  • 4nc(netcat)의 -z/-v/-u/-l 옵션과 포트 스캔 패턴을 활용할 수 있다
  • 5iptables DROP과 REJECT의 차이를 이해하고 방화벽 동작을 시뮬레이션할 수 있다
  • 6포트 점검 표준 절차(SOP) 스크립트를 작성할 수 있다
실습 환경 준비
telnet 설치 확인
which telnet || apt-get install -y telnet
nc(netcat) 설치 확인
nc -h 2>&1 | head -3
현재 LISTEN 중인 포트 목록 확인
ss -tlnp

iptables 규칙 추가/삭제 실습에는 root(sudo) 권한이 필요

💡개념

telnet의 포트 연결 테스트 원리

서비스가 올라가 있는데 외부에서 접속이 안 됩니다. curl도 타임아웃, 브라우저도 응답 없음입니다. 방화벽인지, 프로세스가 안 떴는지, 바인딩 주소가 잘못됐는지 짐작만 됩니다. telnet <host> <port> 한 줄이면 포트까지 TCP 연결이 되는지 여부를 즉시 확인할 수 있습니다.

telnet 포트 연결 테스트 원리 — 서비스 접속 불가가 방화벽인지·프로세스 미기동인지·바인딩 오류인지 짐작만 될 때, telnet host port로 TCP 3-way 핸드셰이크까지 도달하는지 즉시 확인. 연결되면 포트는 열린 것(애플리케이션 계층 문제), 거부·타임아웃이면 네트워크·방화벽 문제로 범위 좁힘확대

telnet은 지정한 호스트와 포트로 TCP 3-way handshake를 시도합니다.

클라이언트 → SYN         → 서버:포트
클라이언트 ← SYN-ACK     ← 서버:포트  (포트가 열려있으면)
클라이언트 → ACK         → 서버:포트

handshake가 성공하면 포트가 열려 있는 것입니다. 실패하는 방식에 따라 원인이 달라집니다:

  • 즉시 "Connection refused" → TCP RST 패킷 수신 → 서버에 도달했으나 해당 포트에 LISTEN 프로세스 없음
  • 일정 시간 후 Timeout → 패킷이 응답 없이 사라짐 → 방화벽(ACL, Security Group, iptables)이 DROP
  • 연결 성공 후 빈 화면 → TCP 연결 수립됨 → 포트 정상 오픈

telnet은 TCP 전용입니다. UDP 포트 확인에는 nc를 사용해야 합니다.

포트를 두드리면 실제로 오가는 것 — SYN 한 발의 4단계

💡개념

telnet·nc 한 줄이 포트 여닫힘을 알아내는 실제 과정 — SYN 한 발의 4단계

telnet db 3306이나 nc -zv db 3306 한 줄이면 "포트가 열렸나"를 즉시 알 수 있습니다. 이 도구들이 하는 일은 단 하나 — 대상 IP:포트TCP SYN 한 발을 쏘고 무엇이 돌아오는지 보는 것입니다. 돌아오는 응답이 SYN-ACK냐, RST냐, 아무것도 아니냐 — 이 세 갈래가 각각 다른 원인을 가리키므로, 그 분기를 단계로 알면 방화벽·서비스·경로 중 어디가 문제인지 30초 안에 좁힙니다.

TEXT
[내 PC]  nc -zv 10.0.0.5 3306
   │
   ① 라우팅·ARP로 next-hop 결정 후 SYN 발사   (목적지 IP:3306으로 연결 요청)
   │
   ② 목적지(또는 경로)가 응답을 결정
   │
   ├─► SYN-ACK 회신 → ③a 내 커널이 ACK로 연결 완성 → "succeeded / Connected"  = 열림
   │
   ├─► RST 회신     → ③b 즉시 "Connection refused"                         = 닫힘/거부
   │
   └─► (무응답)     → ③c SYN 재전송하다 "Connection timed out"             = 드롭
   ▼
Connection to 10.0.0.5 3306 port [tcp/mysql] succeeded!

각 갈래가 하는 일과, 그 뜻(다음 확인):

단계하는 일무슨 뜻(다음 확인)
① SYN 발사라우팅으로 next-hop을 정하고 그 MAC을 ARP로 해석한 뒤 대상 IP:포트로 TCP SYN 전송라우팅·ARP 실패면 No route to host = L3부터 어긋남(포트 문제 아님)
②→③a SYN-ACK그 포트에 LISTEN 프로세스가 있어 커널이 SYN-ACK로 수락 → 내 커널이 ACK로 3-way 완성succeeded·Connected = L4 정상. 그 위 계층(앱·인증)으로 조사 이동
②→③b RST도달은 했으나 그 포트에 LISTEN이 없어 커널이 RST 회신 → 즉시 refused서비스 미기동·다른 포트 LISTEN. 단 방화벽 REJECT도 RST를 위장하니 ss -tlnp로 실제 LISTEN 대조
②→③c 무응답경로·목적지 방화벽이 SYN을 조용히 DROP → 응답이 없어 재전송만 반복몇 초 뒤 timeout = 방화벽·보안그룹 차단 또는 호스트 다운. traceroute·인바운드 규칙 확인
구분 포인트refused는 즉시·timeout은 지연 — 응답 속도가 두 원인을 가르는 1차 단서UDP는 무응답이 정상일 수 있어 이 판정이 부정확 → 실제 프로토콜로 확인(dig·ntpdate -q)

즉 포트 점검은 SYN 한 발에 돌아온 세 가지 응답을 읽는 일입니다 — SYN-ACK(열림)·RST(닫힘/거부)·무응답(드롭). 여기서 핵심은 refusedtimed out을 응답 속도로 가르는 것입니다: 즉시 거부면 패킷이 목적지까지 갔다는 뜻이라 "네트워크는 살아있고 서비스가 없다"로 좁혀 ss -tlnp로 LISTEN을 보고, 지연 후 timeout이면 "경계에서 삼켜졌다"로 보고 traceroute와 방화벽·보안그룹 인바운드 규칙부터 확인합니다. 다만 refused가 늘 서비스 부재는 아니라 방화벽 REJECT가 RST를 위장할 수 있고, UDP는 무응답이 정상일 수 있어 nc -zv -u의 '열림' 판정을 그대로 믿지 말고 실제 프로토콜 질의로 확인해야 합니다.

기본 사용법

로컬 터미널
# 실습 디렉토리 준비
mkdir -p /tmp/networking/part3/exam_12 && cd /tmp/networking/part3/exam_12

# 기본 형식
telnet [호스트] [포트]

# 예시: 웹 서버 80 포트 확인
telnet 192.168.1.100 80

# 예시: HTTPS 443 포트 확인
telnet google.com 443

# 예시: MySQL 3306 포트 확인
telnet db-server 3306
🔍실행 후 확인할 것
  • 응답 메시지 먼저 확인"Connection succeeded" 또는 "Connected to" 가 나오면 포트 열림 — "Connection refused"면 서비스 미기동, 타임아웃이면 방화벽 차단
  • 응답 속도로 차단 여부 구분즉시 Connection refused는 서버가 거절, 3~5초 후 타임아웃은 방화벽이 패킷을 조용히 버리는 것(DROP 정책)
  • nc exit code 조합nc -zv 성공 시 exit 0, 실패 시 exit 1 — 스크립트 자동화에서 $?로 분기할 때 기준값

결과 해석

Case 1 - 포트 열림 (연결 성공)

Trying 192.168.1.100...
Connected to 192.168.1.100.
Escape character is '^]'.

이 상태에서 빈 화면이 나타나면 TCP 연결이 수립된 것입니다. Ctrl+]quit으로 종료합니다.

Case 2 - Connection Refused (서비스 없음)

Trying 192.168.1.100...
telnet: Unable to connect to remote host: Connection refused

호스트에는 도달했지만 해당 포트에서 수신 대기 중인 프로세스가 없습니다.

Case 3 - Connection Timeout (방화벽 차단)

Trying 192.168.1.100...
(응답 없음... 시간 경과 후)
telnet: Unable to connect to remote host: Connection timed out

패킷이 방화벽에서 DROP되고 있습니다.

telnet으로 공개 서비스 포트 확인하기

실제 공개 서버로 telnet 테스트를 연습합니다.

1단계: HTTP 포트 확인

로컬 터미널
telnet google.com 80

연결되면 아래처럼 HTTP 요청을 직접 입력해볼 수 있습니다:

GET / HTTP/1.0
Host: google.com
(빈 줄 두 번 입력)

HTML 응답이 반환되면 telnet으로 HTTP 통신이 가능함을 확인한 것입니다.

2단계: 존재하지 않는 포트로 Refused 확인

로컬 터미널
# 로컬에서 열려있지 않은 포트로 테스트
telnet localhost 9999

예상 결과:

Trying ::1...
telnet: connect to address ::1: Connection refused

3단계: 방화벽 테스트용 Timeout 시뮬레이션

iptables로 임시 DROP 규칙을 추가한 후 테스트합니다 (root 필요):

위험 명령어

이 명령은 방화벽 정책을 변경해 현재 접속 중인 세션이나 운영 트래픽에 즉시 영향을 줄 수 있습니다. 적용 전 허용 대상 IP·포트와 롤백 명령을 확인하세요.

로컬 터미널
# DROP 규칙 추가
sudo iptables -I INPUT -p tcp --dport 8888 -j DROP

# 다른 터미널에서 nc로 8888 포트 열기
nc -l 8888 &

# telnet으로 접속 시도 (Timeout 발생)
timeout 10 telnet localhost 8888

# 규칙 제거
sudo iptables -D INPUT -p tcp --dport 8888 -j DROP

nc(netcat)로 포트 스캔

nc(netcat)는 "네트워크의 스위스 아미 나이프"라 불리는 도구입니다. TCP/UDP 포트 스캔, 간이 서버, 파일 전송 등 다양한 용도로 활용됩니다.

💡개념

nc -zv 옵션 이해

nc의 포트 스캔에 사용하는 핵심 옵션:

nc -zv 옵션 이해 — netcat 포트 스캔의 핵심 옵션: -z는 데이터를 보내지 않고 연결 여부만 확인(스캔 모드), -v는 결과를 상세 출력(succeeded/refused). nc -zv host 80-443처럼 포트 범위도 스캔 가능해 telnet보다 여러 포트를 빠르게 점검확대

옵션의미설명
-zZero I/O mode연결만 확인하고 데이터 전송 없음
-vVerbose상세 결과 출력
-uUDPUDP 프로토콜 사용 (기본값은 TCP)
-w NTimeoutN초 후 연결 시도 중단
-lListen서버 모드, 해당 포트에서 수신 대기

포트 범위 스캔:

로컬 터미널
nc -zv 192.168.1.1 20-25

20번부터 25번까지 순서대로 스캔합니다.

nc는 TCP와 UDP를 모두 지원하므로 DNS(53/UDP), NTP(123/UDP) 등 UDP 서비스 점검에도 활용됩니다.

nc 주요 사용 패턴

로컬 터미널
# TCP 포트 단일 스캔
nc -zv 192.168.1.10 80

# UDP 포트 스캔
nc -zv -u 8.8.8.8 53

# 포트 범위 스캔 (20-80번)
nc -zv 10.0.0.1 20-80

# 타임아웃 3초로 빠른 확인
nc -zv -w 3 192.168.1.10 443

# 여러 포트를 한 번에 확인
for port in 22 80 443 3306 6379; do
  nc -zv -w 2 192.168.1.10 $port 2>&1
done

nc 연결 결과 해석

로컬 터미널
# 성공
$ nc -zv 192.168.1.10 80
Connection to 192.168.1.10 80 port [tcp/http] succeeded!

# 실패 (Refused)
$ nc -zv 192.168.1.10 9999
nc: connect to 192.168.1.10 port 9999 (tcp) failed: Connection refused

# 실패 (Timeout)
$ nc -zv -w 5 10.0.0.1 8080
nc: connect to 10.0.0.1 port 8080 (tcp) failed: Connection timed out
nc로 포트 스캔 및 임시 서버 구성하기

nc의 핵심 기능을 실습합니다.

1단계: 로컬 포트 스캔

로컬 터미널
# SSH 포트 확인
nc -zv localhost 22

# 흔한 서비스 포트들을 한 번에 확인
for port in 22 80 443 3306 5432 6379 8080; do
  result=$(nc -zv -w 2 localhost $port 2>&1)
  echo "Port $port: $result"
done

2단계: nc로 임시 TCP 서버 열기

터미널 A (서버 역할):

로컬 터미널
nc -l 9000

터미널 B (클라이언트 역할):

로컬 터미널
nc localhost 9000

이후 어느 쪽에서든 텍스트를 입력하면 반대편으로 전송됩니다. 가장 단순한 양방향 채팅입니다.

3단계: nc로 파일 전송 테스트

터미널 A (수신측):

로컬 터미널
nc -l 9001 > received_file.txt

터미널 B (송신측):

로컬 터미널
echo "Hello from nc file transfer" | nc -w 3 localhost 9001

터미널 A에서 received_file.txt 내용 확인:

로컬 터미널
cat received_file.txt

4단계: UDP 포트 확인

로컬 터미널
# DNS 서버 UDP 53 포트 확인
nc -zv -u 8.8.8.8 53

# NTP 서버 UDP 123 포트 확인
nc -zv -u pool.ntp.org 123

Connection Refused vs Connection Timeout 심층 분석

이 두 오류를 정확히 구분하는 것이 빠른 장애 원인 파악의 핵심입니다.

💡개념

두 오류의 원인과 해결 방향

Connection Refused 발생 흐름:

클라이언트 → SYN → 방화벽(허용) → 서버 OS
서버 OS: "이 포트에 LISTEN 중인 소켓이 없다"
서버 OS → RST → 클라이언트
결과: 즉시 "Connection refused"

telnet 두 오류의 원인과 해결 — Connection refused는 대상 호스트까지 도달했으나 그 포트에 LISTEN 중인 프로세스가 없음(서비스 미기동·잘못된 포트)을 뜻하고, Connection timed out은 방화벽·보안그룹이 패킷을 버리거나 호스트에 도달 못 함을 뜻함. 두 오류를 구분하면 조치 방향이 갈림확대

원인: 데몬(서비스)이 실행 중이지 않거나, 잘못된 포트로 LISTEN 중이거나, 서비스가 크래시됨

해결 방향:

서버 터미널
# 서비스 상태 확인
systemctl status nginx
# 또는
ps aux | grep nginx

# 실제로 어느 포트에서 LISTEN 중인지 확인
ss -tlnp | grep nginx

Connection Timeout 발생 흐름:

클라이언트 → SYN → 방화벽(DROP) → 패킷 소멸
클라이언트: "응답이 없다... 기다린다..."
일정 시간 후: "Connection timed out"

원인: iptables DROP 규칙, AWS Security Group 미허용, 하드웨어 방화벽 ACL, 라우팅 경로 없음

해결 방향:

로컬 터미널
# iptables 규칙 확인
sudo iptables -L -n -v | grep DROP

# 클라우드 환경이면 Security Group / 방화벽 정책 확인
# traceroute로 어느 구간에서 패킷이 막히는지 확인
traceroute 목적지IP

REJECT vs DROP 차이: 방화벽 정책이 DROP이면 Timeout, REJECT이면 즉시 "Connection refused" 또는 "No route to host"가 반환됩니다. REJECT는 거부 패킷을 돌려보내고, DROP은 무응답으로 패킷을 버립니다.

포트 점검 판단 — 어떤 옵션·타임아웃으로, 증상이 무엇을 뜻하나
점검이 멈춰버린다(응답 없는 포트)nc -w 3 처럼 타임아웃을 명시(초). 안 주면 OS 기본 SYN 재전송(수십 초~127초)까지 매달린다. 스크립트·헬스체크는 -w 2~5가 실무 기본 — '무한 대기 금지'
여러 포트를 빠르게 스캔nc -zv host 20-100 (z=연결만, 데이터 안 보냄). 대상에 부하·로그를 최소화. 단 -z 스캔은 IDS에 잡힐 수 있어 운영 중 서버엔 신중
Connection refused 받음패킷이 호스트에 도달했고 그 포트에 LISTEN이 없다는 뜻 — 방화벽보다 '서비스 미기동/다른 포트'. ss -ltnp로 실제 LISTEN 포트 확인. 'refused=네트워크는 살아있다'
Timed out 받음응답 자체가 없음 — 중간 방화벽 DROP 또는 보안그룹 차단, 혹은 호스트 다운. 경로(traceroute)와 인바운드 규칙부터. 'timeout=경계에서 삼켜짐'
telnet vs nc 무엇으로단발 수동 확인은 telnet도 OK, 스크립트·타임아웃·스캔은 nc(-w·-z 지원). 대화형 프로토콜 직접 입력 테스트(HTTP·SMTP)는 telnet이 편함
UDP 포트 점검nc -u — 단 UDP는 무응답이 정상일 수 있어 '열림' 판정이 부정확. 응답 없으면 open|filtered로 해석, 애플리케이션 레벨 확인 병행
iptables로 Refused/Timeout 시뮬레이션

실제 방화벽 동작을 재현하여 두 오류의 차이를 체감합니다.

사전 준비: 테스트용 서비스 실행

로컬 터미널
# Python으로 8080 포트에 임시 서버 실행
python3 -m http.server 8080 &
SERVER_PID=$!
echo "서버 PID: $SERVER_PID"

시나리오 1: 정상 연결

로컬 터미널
nc -zv localhost 8080
# 예상: Connection succeeded

시나리오 2: 서비스 중지 → Connection Refused

위험 명령어

이 명령은 프로세스를 종료해 연결 중인 사용자나 배치 작업을 중단시킬 수 있습니다. PID와 프로세스 이름이 목표 서비스인지 확인한 뒤 실행하세요.

로컬 터미널
kill $SERVER_PID
nc -zv localhost 8080
# 예상: Connection refused (서비스 없음)

시나리오 3: iptables DROP → Connection Timeout

위험 명령어

이 명령은 방화벽 정책을 변경해 현재 접속 중인 세션이나 운영 트래픽에 즉시 영향을 줄 수 있습니다. 적용 전 허용 대상 IP·포트와 롤백 명령을 확인하세요.

로컬 터미널
# 서비스 재시작
python3 -m http.server 8080 &
SERVER_PID=$!

# DROP 규칙 추가
sudo iptables -I INPUT -p tcp --dport 8080 -j DROP

# Timeout 확인 (3초 타임아웃으로 빠르게)
nc -zv -w 3 localhost 8080
# 예상: Connection timed out

# DROP 규칙 제거
sudo iptables -D INPUT -p tcp --dport 8080 -j DROP

# 정리
kill $SERVER_PID

시나리오 4: iptables REJECT → 즉시 거부

위험 명령어

이 명령은 방화벽 정책을 변경해 현재 접속 중인 세션이나 운영 트래픽에 즉시 영향을 줄 수 있습니다. 적용 전 허용 대상 IP·포트와 롤백 명령을 확인하세요.

로컬 터미널
python3 -m http.server 8080 &
SERVER_PID=$!

# REJECT 규칙 추가
sudo iptables -I INPUT -p tcp --dport 8080 -j REJECT

# 즉시 거부 확인
nc -zv localhost 8080
# 예상: Connection refused (REJECT이므로 즉시 응답)

sudo iptables -D INPUT -p tcp --dport 8080 -j REJECT
kill $SERVER_PID

실전 트러블슈팅 시나리오

상황: 개발팀이 신규 Node.js 앱을 서버에 배포했지만 외부에서 http://서버IP:3000으로 접속이 안 됩니다.

1단계: 서버 내부에서 포트 확인

로컬 터미널
# 서버 내부에서 로컬호스트로 접속 테스트
nc -zv localhost 3000

결과: Connection succeeded → 앱은 실행 중

2단계: 외부에서 접속 테스트

로컬 터미널
# 다른 서버나 PC에서 실행
nc -zv 서버IP 3000

결과: Connection timed out → 방화벽 문제

3단계: iptables 확인

로컬 터미널
sudo iptables -L INPUT -n -v
# 3000번 포트 허용 규칙이 없음을 확인

4단계: 방화벽 규칙 추가

위험 명령어

이 명령은 방화벽 정책을 변경해 현재 접속 중인 세션이나 운영 트래픽에 즉시 영향을 줄 수 있습니다. 적용 전 허용 대상 IP·포트와 롤백 명령을 확인하세요.

로컬 터미널
sudo iptables -A INPUT -p tcp --dport 3000 -j ACCEPT
# 또는 firewalld 사용 환경이라면:
sudo firewall-cmd --permanent --add-port=3000/tcp
sudo firewall-cmd --reload

5단계: 재확인

로컬 터미널
nc -zv 서버IP 3000
# 예상: Connection succeeded

핵심 교훈: 내부에서는 되는데 외부에서 안 된다면, 먼저 방화벽(iptables, firewalld, Security Group)을 의심하세요.

상황: 애플리케이션 로그에 ECONNREFUSED 10.0.1.5:5432 에러가 반복됩니다.

1단계: 포트 연결 테스트

로컬 터미널
nc -zv 10.0.1.5 5432

결과: Connection refused

2단계: DB 서버에서 PostgreSQL 상태 확인

서버 터미널
# DB 서버에 SSH 접속 후
systemctl status postgresql

결과: Active: failed → PostgreSQL이 죽어있음!

3단계: 로그 확인

서버 터미널
journalctl -u postgresql -n 50
# 또는
tail -50 /var/log/postgresql/postgresql-14-main.log

4단계: PostgreSQL 재시작

위험 명령어

이 명령은 실행 중인 서비스 상태를 바꿔 순간적인 중단이나 설정 반영 실패를 만들 수 있습니다. 운영 트래픽 영향과 재시작 후 확인 명령을 먼저 준비하세요.

서버 터미널
systemctl start postgresql
systemctl status postgresql

5단계: 포트 재확인

로컬 터미널
nc -zv 10.0.1.5 5432
# 예상: Connection succeeded

추가 확인: PostgreSQL이 올바른 주소에서 LISTEN 중인지 확인

로컬 터미널
# postgresql.conf에서 listen_addresses 확인
grep listen_addresses /etc/postgresql/14/main/postgresql.conf
# 'localhost'만 있다면 외부 연결 불가 → '*' 또는 특정 IP로 변경 필요

핵심 교훈: Connection Refused는 "서비스가 없다"는 뜻입니다. 서비스 상태와 LISTEN 주소/포트 설정을 함께 확인하세요.

심화 — 거부(refused)의 출처를 의심하라

💡개념

심화: RST를 만드는 세 출처 — refused가 늘 '서비스 없음'은 아니다

이 모듈은 'refused=서비스 없음, timeout=차단'으로 가르치지만, 실무에는 그 경계를 흐리는 경우가 있습니다. refused를 만드는 RST 패킷은 죽은 서비스만 보내는 게 아니라 방화벽·프록시도 흉내 낼 수 있어서, 단순 규칙을 그대로 믿으면 멀쩡한 서비스를 붙잡고 시간을 버립니다.

  • 정상 닫힘: 포트에 LISTEN 프로세스가 없으면 커널이 RST를 돌려보내 즉시 refused (교과서 케이스).
  • 방화벽 REJECT: -j REJECT --reject-with tcp-reset 규칙은 방화벽이 대신 RST를 만들어 보냅니다. 서비스가 멀쩡히 떠 있어도 refused처럼 보입니다 — DROP(무응답=timeout)과 달리 REJECT는 refused로 위장합니다.
  • 프록시·LB가 handshake를 대신 받음: L4 로드밸런서·포트포워더는 앞단에서 TCP 3-way handshake를 자기가 완료합니다. 그래서 백엔드가 죽어도 nc -z는 성공(열림)으로 뜨고, 실제 데이터를 보내야 뒤늦게 RST나 무응답이 드러납니다.
  • 그래서 판정을 한 겹 더: refused여도 그 RST가 서비스에서 온 건지 방화벽에서 온 건지, 열림이어도 진짜 백엔드인지 앞단 프록시인지 구분해야 합니다. nc -v의 타이밍, 다른 위치에서의 재현, 실제 프로토콜 요청으로 교차 검증합니다.

즉 nc의 열림/거부 판정은 시작점일 뿐, '그 신호를 누가 보냈나'까지 물어야 오진을 피합니다.

상황: 앱 로그에 connection refused가 찍혀 DB가 죽은 줄 알았는데, DB 서버에 들어가 보니 서비스는 멀쩡히 떠서 5432를 LISTEN하고 로컬에서는 접속도 됩니다. 그런데 앱 서버에서 nc -zv DB 5432는 여전히 refused입니다.

원인: 경로의 방화벽에 -j REJECT --reject-with tcp-reset 규칙이 있어, 방화벽이 서비스 대신 RST를 돌려보낸 것입니다. 죽은 서비스가 보내는 RST와 방화벽이 만든 RST는 클라이언트 눈에 똑같이 refused로 보여, 'refused=서비스 없음'이라는 단순 규칙이 오진을 부릅니다.

진단: 먼저 DB 서버 로컬에서 실제 LISTEN을 확인해 서비스가 살아 있음을 못박고, 앱 서버에서 거부가 즉시 오는지(REJECT/죽은 서비스) 오래 걸린 뒤 timeout인지(DROP)를 구분합니다.

로컬 터미널
# DB 서버 로컬 — 서비스는 살아 있는가
ss -tlnp | grep 5432

# 경로 방화벽 — REJECT 규칙이 있는가
sudo iptables -L -n | grep -i reject

# RST가 DB 서버가 아니라 중간 장비에서 돌아오는지 대조
sudo tcpdump -ni any 'tcp port 5432 and tcp[tcpflags] & tcp-rst != 0'

해결: 방화벽에서 해당 출발지·포트를 ACCEPT로 허용(또는 REJECT 규칙 정정)합니다. '거부'의 출처를 서비스인지 방화벽인지 먼저 가르는 습관이, 멀쩡한 서비스를 재시작하며 시간을 버리는 일을 막습니다.


포트 점검 체크리스트

포트 점검 표준 절차 (SOP)

장애 발생 시 포트 레이어를 체계적으로 점검하는 표준 절차입니다.

로컬 터미널
#!/bin/bash
# port-check.sh - 포트 통신 종합 점검 스크립트

TARGET_HOST="${1:-localhost}"
TARGET_PORT="${2:-80}"
TIMEOUT=5

echo "===== 포트 통신 점검 시작 ====="
echo "대상: ${TARGET_HOST}:${TARGET_PORT}"
echo ""

# 1. DNS 해석 확인
echo "[1] DNS 해석 확인"
if host "${TARGET_HOST}" > /dev/null 2>&1; then
  RESOLVED_IP=$(host "${TARGET_HOST}" | awk '/has address/ {print $4}' | head -1)
  echo "    성공: ${TARGET_HOST} → ${RESOLVED_IP}"
else
  echo "    실패: DNS 해석 불가"
fi
echo ""

# 2. L3 ping 확인
echo "[2] L3 ping 확인 (ICMP)"
if ping -c 3 -W 2 "${TARGET_HOST}" > /dev/null 2>&1; then
  echo "    성공: ICMP 응답 있음"
else
  echo "    실패 또는 ICMP 차단됨 (ping 차단은 정상일 수 있음)"
fi
echo ""

# 3. L4 TCP 포트 확인
echo "[3] L4 TCP 포트 확인 (nc)"
NC_RESULT=$(nc -zv -w "${TIMEOUT}" "${TARGET_HOST}" "${TARGET_PORT}" 2>&1)
if echo "${NC_RESULT}" | grep -q "succeeded"; then
  echo "    성공: 포트 ${TARGET_PORT} OPEN"
elif echo "${NC_RESULT}" | grep -q "refused"; then
  echo "    실패: Connection Refused (서비스 미실행 또는 잘못된 포트)"
else
  echo "    실패: Connection Timeout (방화벽 DROP 의심)"
fi
echo ""

echo "===== 점검 완료 ====="

사용법:

로컬 터미널
chmod +x port-check.sh
./port-check.sh google.com 443
./port-check.sh 192.168.1.10 3306

핵심 정리

도구주용도프로토콜특징
telnet포트 연결 확인, HTTP 수동 테스트TCP만대부분의 시스템에 기본 설치
nc -zv포트 스캔, 범위 스캔TCP + UDP더 유연하고 스크립트에 적합
nc -l임시 서버, 연결 수신 대기TCP + UDP반대편 연결 테스트에 유용

오류 메시지 요약:

  • Connection refused = 패킷 도달 O, 서비스 없음 → 데몬 상태 확인
  • Connection timed out = 패킷 도달 X → 방화벽/라우팅 확인
  • Connection succeeded = L4 통신 정상 → 그 위 계층(앱, 인증) 확인

명령어·단축키 빠른 참조

이 모듈에서 다룬 L4 포트 점검 명령을 실전 옵션과 함께 모았습니다. "예" 열의 조합을 그대로 써도 됩니다.

명령어/옵션용도자주 쓰는 예
telnet <host> <port>단발 TCP 포트 연결 확인(수동)telnet db-server 3306
nc -zv <host> <port>데이터 없이 열림/거부 판정nc -zv 192.168.1.10 443
nc -zv <host> <범위>여러 포트 한 번에 스캔nc -zv 10.0.0.1 20-100
nc -zv -u <host> <port>UDP 포트 점검(무응답 주의)nc -zv -u 8.8.8.8 53
nc -w <초>응답 없는 포트에서 무한 대기 방지nc -zv -w 3 10.0.0.1 8080
nc -l <port>임시 리스닝 서버(반대편 테스트)nc -l 9000
ss -tlnp로컬 LISTEN 포트·프로세스 확인ss -tlnp | grep 5432
iptables -I INPUT … -j DROP/REJECTRefused·Timeout 재현·차단sudo iptables -I INPUT -p tcp --dport 8080 -j DROP
iptables -L -nREJECT/DROP 규칙 확인(거부 출처 판별)sudo iptables -L -n | grep -i reject
traceroute <IP>timeout 시 어느 구간에서 막히는지traceroute 10.0.0.1

관련 모듈로 더 깊이:

다음 모듈에서는 sslsof로 포트를 점유한 프로세스를 찾고 안전하게 종료하는 방법을 다룹니다.

지식 확인

퀴즈 — 8문제

Q1

telnet 192.168.1.10 80 명령 실행 시 'Connection refused' 메시지가 나타났습니다. 이것이 의미하는 것은?

Q2

DB 서버(10.0.0.5) 3306 포트 연결을 확인하려 합니다. `telnet 10.0.0.5 3306`을 쓰면 연결 성공 시 MySQL 배너가 화면에 출력되고 터미널이 멈춥니다. 이 문제 없이 포트 통신만 확인하는 방법은?

Q3

Connection Timeout과 Connection Refused의 근본적인 차이는?

Q4

nc를 사용하여 UDP 포트 53이 열려 있는지 확인하는 올바른 명령은?

Q5

nc -l 9999 명령의 동작은?

Q6

TCP 포트는 nc -zv로 열림/닫힘을 명확히 알 수 있는데, UDP 포트(예: DNS 53) 점검이 더 불확실한 이유는?

Q7

[심화] 'Connection refused는 서비스가 없다는 뜻'이라고 배웠지만, 서비스가 멀쩡히 떠 있는데도 refused가 나올 수 있다. 그 대표적 원인은?

Q8

[심화] 앱 서버에선 nc -zv DB 5432가 refused인데, DB 서버 로컬에선 서비스가 5432를 정상 LISTEN하고 접속도 된다. 원인을 가리기 위한 다음 단계로 가장 적절한 것은?

0 / 8 답변

🧪 실습으로 확인하기

DNS는 어떻게 동작하는가 — dig로 해부하는 도메인 해석

중급

도메인이 IP로 바뀌는 과정을 눈으로 직접 확인한다. Root → TLD → Authoritative까지 쿼리가 전달되는 경로를 dig +trace로 추적하고, CNAME 체인과 TTL 캐싱이 실제 서비스에 미치는 영향을 이해한다.

45📋 4단계💻 직접 환경
실습 시작하기 →

이것도 배워보세요