Linux 포트 개념 및 ss 명령어로 listening 확인 방법
포트의 정의와 TCP 및 UDP 통신 원리를 설명합니다. ss -tulnp 명령어를 사용하여 현재 서버에서 어떤 서비스가 특정 포트를 점유하고 있는지 확인하는 실무적인 방법을 정리했습니다. 로컬 포트와 외부 공개 포트를 구분하는 기준을 확인하십시오.
포트란 무엇인가
포트는 하나의 서버에서 여러 서비스를 동시에 실행하면서 트래픽이 섞이지 않게 구분하는 번호입니다. VPS는 하나의 IP 주소를 가지지만, 웹 서버, SSH 서버, 데이터베이스를 동시에 실행할 수 있습니다. 패킷이 도착하면 운영체제는 해당 패킷이 어떤 서비스로 향하는지 알아야 합니다. 이때 포트 번호가 그 역할을 합니다. 웹 트래픽은 443번 포트로, SSH는 22번 포트로 전달됩니다. IP 주소는 패킷을 서버까지 전달하고, 포트는 서버 내의 올바른 프로그램으로 전달합니다.
포트는 16비트 숫자입니다. 16비트는 0부터 65535까지 표현할 수 있으며, 0은 예약되어 있으므로 실제 포트는 1부터 65535까지 사용합니다. 1024번 미만의 포트는 "잘 알려진(well-known)" 포트로, 열기 위해서는 root 권한이 필요합니다. 이것이 SSH(22), HTTP(80), HTTPS(443), DNS(53)와 같은 표준 서비스가 이 영역에 위치하는 이유입니다. 1024번 이상의 포트는 일반 프로그램이 자유롭게 사용할 수 있습니다.
포트에는 TCP와 UDP 두 종류가 있으며, 같은 번호라도 프로토콜에 따라 별개로 취급됩니다. TCP는 SSH, HTTP, 데이터베이스 등 대부분의 서비스가 사용하는 연결 지향 프로토콜입니다. UDP는 비연결형 프로토콜로 DNS나 일부 VPN 등에 사용됩니다. 방화벽을 설정할 때는 보통 22/tcp와 같이 어떤 프로토콜을 사용할지 명시해야 합니다.
Listening은 프로그램이 포트에서 대기 중임을 의미합니다
연결을 수신하려는 프로그램은 커널에 특정 포트에서 "listen"하도록 요청합니다. 그 순간부터 해당 포트는 시스템에서 열리며, 방화벽 설정에 따라 프로그램은 해당 포트로 들어오는 모든 요청에 응답합니다. 아무것도 listening하지 않는 포트는 연결을 단순히 거부합니다. 따라서 모든 보안 점검의 첫 번째 질문은 "무엇이, 어디에서 listening하고 있는가"입니다.
이를 확인하기 위한 명령어는 ss입니다:
sudo ss -tlnp각 플래그는 TCP(t), listening 상태인 소켓만 표시(l), 포트 번호를 숫자로 표시(n, 따라서 ssh 대신 22로 표시됨), 소유 프로세스 표시(p)를 의미합니다. 일반적인 결과는 다음과 같습니다:
State Recv-Q Local Address:Port Process
LISTEN 0 0.0.0.0:22 sshd
LISTEN 0 127.0.0.1:5432 postgres
LISTEN 0 [::]:80 nginxProcess 열은 각 포트를 소유한 프로그램을 알려줍니다. 이를 통해 의도치 않게 실행 중인 프로세스를 추적할 수 있습니다.
Local Address 열이 중요한 이유
각 포트 앞의 주소를 주의 깊게 읽어야 합니다. 이 주소가 서비스에 접근할 수 있는 대상을 결정하기 때문입니다. 자주 보게 될 세 가지 경우가 있습니다.
0.0.0.0:22은 "이 장비가 가진 모든 IPv4 주소에서 수신 대기"를 의미하며, 공인 IP도 포함됩니다. 방화벽이 허용한다면 인터넷을 통해 IPv4로 해당 서비스에 접근할 수 있습니다.
[::]:80는 IPv6에 대해 동일한 의미를 갖습니다. 공인 주소를 포함한 모든 IPv6 주소에서 수신 대기합니다. 많은 프로그램이 기본적으로 이 주소에 바인딩되며, 리눅스에서 :: 소켓은 종종 IPv4 연결도 함께 수락합니다.
127.0.0.1:5432는 "루프백에서만 수신 대기"를 의미합니다. 127.0.0.1는 장비가 자기 자신과 통신할 때 사용하는 주소이며, 외부 어디에서도 라우팅할 수 없습니다. 여기에 바인딩된 서비스는 방화벽 설정과 관계없이 오직 동일한 서버 내부에서만 접근할 수 있으며, 인터넷에서는 절대 접근할 수 없습니다. 앞서 언급한 Postgres 행이 구조적으로 안전한 이유가 바로 이것입니다.
실무적인 규칙은 여기서 바로 도출됩니다. 자체 애플리케이션만 사용하는 데이터베이스, 캐시, 관리자 패널은 0.0.0.0이 아닌 127.0.0.1에 바인딩해야 합니다. 공인 주소에서 수신 대기하지 않는다면 공격자가 접근할 경로 자체가 존재하지 않게 됩니다.
리스닝 상태와 도달 가능성은 다릅니다
한 가지 더 구분해야 할 점은 혼란을 크게 줄여줍니다. 프로그램이 포트를 열고 리스닝(listening) 중이라는 것과, 방화벽이 허용하여 외부에서 해당 포트에 도달할 수 있다는 것은 서로 다른 개념입니다. 원격 클라이언트가 연결하려면 두 조건이 모두 충족되어야 합니다. 클라이언트 입장에서 두 실패 사례는 증상도 다릅니다. 연결이 거부(refused)된다면 리스닝 중인 프로세스가 없다는 뜻이고, 연결이 지연되다가 타임아웃(timed out)된다면 보통 방화벽이 패킷을 차단한 경우입니다. 이는 두 가지 SSH 오류 메시지가 의미하는 바와 정확히 일치합니다.
따라서 제어 계층은 두 단계로 나뉩니다. 각 서비스의 바인드 주소를 설정하여 무엇을 리스닝할지 결정하고, 방화벽을 통해 외부 세계가 무엇에 도달할 수 있을지 결정합니다. 잘 관리되는 서버는 이 두 가지를 모두 사용합니다. 서비스는 필요한 곳에만 바인드하고, 방화벽은 명시적으로 허용하지 않은 모든 트래픽을 거부합니다. ss 명령으로 리스닝 상태를 확인하는 것이 첫 번째 단계이며, 방화벽 허용 정책을 결정하는 두 번째 단계는 UFW를 이용한 방화벽 기초에서 다룹니다.
이 과정에서 흔히 간과하는 미묘한 문제가 있습니다. IPv4와 IPv6는 별개이므로, IPv4만 방화벽으로 보호하면 [::] 서비스가 IPv6를 통해 노출될 수 있습니다. 이 특정 함정에 대해서는 별도의 게시물인 IPv6 방화벽의 함정에서 다룹니다.
FAQ
Linux 서버에서 열려 있는 포트를 확인하려면 어떻게 해야 합니까?
TCP의 경우 sudo ss -tlnp을, UDP의 경우 sudo ss -ulnp을 실행하십시오. 이 명령은 수신 대기 중인 모든 소켓, 포트, 바인딩된 로컬 주소, 그리고 해당 소켓을 소유한 프로세스를 나열합니다. Local Address 열을 확인하십시오. 0.0.0.0 또는 [::]는 서비스가 네트워크에 노출되어 있음을 의미하며, 127.0.0.1는 해당 기기 내부에서만 수신 대기 중임을 의미합니다.
0.0.0.0과 127.0.0.1의 차이점은 무엇입니까?
0.0.0.0은 공인 IP를 포함한 "모든 IPv4 주소에서 수신 대기"함을 의미하므로, 네트워크를 통해 서비스에 접근할 수 있습니다. 127.0.0.1은 루프백 주소로, 기기가 자기 자신과 통신할 때 사용하며 외부에서는 라우팅할 수 없습니다. 따라서 이 주소에 바인딩된 서비스는 로컬에서만 접근 가능합니다. 내부 서비스는 127.0.0.1에 바인딩하여 외부로 노출되지 않도록 하십시오.
서비스가 작동하려면 방화벽에서 포트를 열어야 합니까?
다른 기기에서 접근해야 하는 서비스에만 해당합니다. 127.0.0.1에 바인딩된 서비스는 외부에서 접근할 수 없으므로 방화벽 규칙이 필요 없습니다. 0.0.0.0 또는 [::]에서 실행되는 서비스는 트래픽을 허용하기 위한 방화벽 규칙이 필요하며, 규칙을 추가하기 전까지는 기본적으로 거부되어야 합니다.
TCP 포트와 UDP 포트의 차이점은 무엇입니까?
TCP는 연결 지향적이며 SSH, 웹 서버, 데이터베이스 등 대부분의 서비스에서 사용합니다. UDP는 비연결형이며 DNS나 일부 VPN에서 사용합니다. TCP와 UDP에서 동일한 번호를 사용하더라도 서로 다른 포트이므로, 53/tcp와 53/udp은 서로 다릅니다. 방화벽 규칙을 작성할 때는 22/tcp와 같이 프로토콜을 명시해야 합니다.