리눅스 telnet 명령어 사용법 - 포트 열림 확인 용도로만 살아남은 이유
이 명령어를 언제 쓰는가
먼저 분명히 해두자. telnet을 원격 접속에 쓰는 일은 없다. telnet 프로토콜은 아이디와 비밀번호를 포함한 모든 내용을 평문으로 보낸다. 같은 네트워크에서 패킷을 뜨는 것만으로 계정이 털린다. 원격 접속은 예외 없이 ssh다. 서버에 telnet 서버(telnetd)가 떠 있다면 그건 점검해서 꺼야 할 항목이다.
그럼에도 telnet 클라이언트가 아직 실무에서 보이는 이유는 딱 하나다. TCP 포트가 열려 있는지 확인하는 가장 흔한 수단이기 때문이다. 오래된 서버나 최소 설치 환경에는 nc도 curl도 없는데 telnet은 있는 경우가 있다. 윈도우에서도 기능만 켜면 같은 문법으로 쓸 수 있어서, 개발자·인프라 담당자 사이에서 "telnet 쳐보세요"가 관용어처럼 남았다.
정리하면 포트 확인 도구로만 쓴다. 그것도 nc가 있으면 nc를 쓰는 것이 낫다.
기본 형식
telnet 호스트 포트
- 포트를 반드시 명시한다. 생략하면 telnet 기본 포트인 23으로 붙으려 하는데, 요즘 서버에 그 포트는 열려 있지 않다.
- 연결에 성공하면 대화형 세션으로 들어가 입력한 내용이 그대로 상대에게 전달된다.
- 배포판 기본 설치가 아닌 경우가 많다. Ubuntu/Debian은
apt install telnet, RHEL/Rocky는dnf install telnet이다. 여기서 설치하는 것은 클라이언트뿐이고 서버(telnet-server)는 별개다. 서버 패키지는 설치하지 않는다.
결과를 읽는 법
| 출력 | 의미 | 다음 조치 |
|---|---|---|
Connected to ... | TCP 연결 성공. 포트가 열려 있다 | 애플리케이션 계층을 본다 |
Connection refused | 패킷은 도달했으나 그 포트에 리스닝이 없다 | 서비스 기동 상태와 바인딩 주소 확인 |
Connection timed out | 응답 자체가 없다 | 중간 방화벽·보안그룹이 조용히 버렸을 가능성 |
Name or service not known | DNS 해석 실패 | 포트가 아니라 DNS 문제다 |
No route to host | 라우팅이나 L2 단계에서 막힘 | 라우팅 테이블과 게이트웨이 확인 |
refused와 timed out을 구분하는 것이 핵심이다. refused면 경로는 뚫려 있다는 뜻이므로 서버 쪽 문제이고, timed out이면 중간에서 막힌 것이므로 방화벽 담당자와 이야기할 사안이다.
실전 예제
1. 웹 서버 포트가 열려 있는지 확인한다. 가장 흔한 사용이다.
telnet devin.kr 80
Trying 182.222.121.235...
Connected to devin.kr.
Escape character is '^]'.
여기까지 나오면 포트는 열려 있다. 확인은 끝났으니 빠져나온다. Ctrl과 ]를 동시에 눌러 telnet> 프롬프트로 나온 뒤 quit을 친다.
2. DB 포트 개방을 검증한다. 애플리케이션 서버에서 DB 서버로 붙는지 확인할 때.
telnet 10.0.0.30 3306
MySQL·MariaDB는 접속 즉시 서버가 버전 문자열을 먼저 보내기 때문에, 화면에 알아볼 수 없는 문자가 섞여 나오면 연결이 성공했을 뿐 아니라 실제로 DB가 응답한 것이다. 이 경우 프로토콜이 어긋나 곧 끊긴다. 정상이다.
3. HTTP 요청을 직접 보내본다. 리다이렉트나 헤더를 눈으로 확인할 때 쓴다. 접속 후 아래 두 줄을 치고 마지막에 빈 줄을 한 번 더 넣는다.
telnet devin.kr 80
GET / HTTP/1.0
Host: devin.kr
실제로는 이렇게 파이프로 넘기는 편이 오타 걱정이 없다.
(printf 'HEAD / HTTP/1.0\nHost: devin.kr\n\n'; sleep 2) | telnet devin.kr 80
여기서는 \r\n이 아니라 \n을 쓴다. telnet은 NVT 규칙에 따라 입력의 LF를 알아서 CRLF로 바꿔 보내는데, \r\n을 직접 넣으면 CR 뒤에 NUL이 끼어들어 서버가 400 Bad request로 거절한다. 반대로 nc는 그런 변환을 하지 않으므로 \r\n을 직접 써야 한다.
다만 이 정도 작업이라면 curl -I https://devin.kr 이 훨씬 정확하다. telnet은 TLS를 못 다루므로 443 포트에는 사실상 쓸모가 없다. 연결은 되지만 그 이후는 암호화 협상이라 아무것도 읽을 수 없다.
4. 메일 서버가 응답하는지 본다. SMTP는 접속하면 서버가 먼저 인사말을 보낸다.
telnet mail.example.com 25
220 ...으로 시작하는 줄이 나오면 SMTP 서버가 살아 있는 것이다. 확인 후 QUIT을 치고 나온다.
5. 스크립트에서는 telnet을 쓰지 않는다. 굳이 자동화가 필요하면 이렇게 감싸야 하는데, 지저분하고 종료 코드도 못 믿는다.
# 권장하지 않는 방식
(sleep 1; echo quit) | timeout 5 telnet 10.0.0.30 3306 2>&1 | grep -q Connected
같은 일을 nc로 하면 이렇게 끝난다. 종료 코드가 정확하다.
nc -z -w 3 10.0.0.30 3306
함정과 주의점
빠져나오는 법을 모르면 세션에 갇힌다. 이게 telnet 최대의 불편이다. Ctrl+C가 안 먹는 상황이 흔하다. 접속 시 안내되는 Escape character is '^]'가 탈출 방법이다. Ctrl+] → telnet> 프롬프트 → quit. 그래도 안 되면 다른 터미널에서 pkill telnet으로 죽인다.
타임아웃을 지정할 수 없다. telnet 클라이언트에는 연결 타임아웃 옵션이 없어서, 필터링된 포트를 두드리면 OS 기본값까지 몇 분을 기다려야 한다. timeout 5 telnet ...처럼 외부에서 감싸는 수밖에 없다. nc의 -w가 있는 것과 대비된다.
UDP는 확인할 수 없다. telnet은 TCP 전용이다. DNS(53/udp), SNMP(161/udp), syslog(514/udp)는 telnet으로 확인이 불가능하다. 이 경우 프로토콜에 맞는 도구를 써야 한다.
연결됐다고 서비스가 정상인 것은 아니다. 프로세스가 포트를 열고만 있고 실제 요청은 처리 못 하는 상태가 있다. 예를 들어 커넥션 풀이 고갈된 애플리케이션은 TCP 연결은 받지만 응답을 안 준다. telnet은 3-way 핸드셰이크 성공까지만 보증한다. 서비스 정상 여부는 curl이나 실제 프로토콜로 확인해야 한다.
telnet 서버가 열려 있으면 즉시 조치한다. 점검 중 23번 포트가 LISTEN 상태로 발견되면 보안 사고 대기 상태다. ss -tlnp | grep :23으로 확인하고, 있다면 서비스를 중지·비활성화한 뒤 패키지를 제거한다.
함께 보면 좋은 명령어
- nc — 같은 포트 확인을 타임아웃과 정확한 종료 코드로 처리한다. 있으면 이쪽을 쓴다.
- ss — 서버 쪽에서 그 포트가 어느 주소에 묶여 LISTEN 중인지 본다.
- curl — HTTP/HTTPS라면 telnet 대신 쓴다. TLS도 처리한다.
- ssh — 원격 접속은 언제나 이쪽이다.