포트 포워딩으로 21번을 열어 두었고 로그인까지 정상인데, 디렉터리 목록을 받거나 파일을 전송하려 하면 멈춘다.
Status: Connection established, waiting for welcome message...
Status: Logged in
Status: Retrieving directory listing...
Error: Connection timed out after 20 seconds of inactivity
21번 포트만 열어 둔 상태에서 거의 반드시 나오는 증상이다.
FTP 는 명령 채널과 데이터 채널을 따로 연다. 21번은 명령 채널이고, 목록과 파일은 별도로 열리는 데이터 연결로 오간다. 이 데이터 연결을 누가 여느냐에 따라 모드가 갈린다.
| 모드 | 데이터 연결 방향 | NAT · 방화벽에서의 문제 |
|---|---|---|
Active (PORT) |
서버 → 클라이언트 | 클라이언트 쪽 방화벽이 인바운드를 막는다 |
Passive (PASV) |
클라이언트 → 서버 | 서버가 알려 준 IP · 포트로 접속해야 하는데, NAT 안쪽 사설 IP 를 알려 준다 |
요즘 클라이언트는 대부분 Passive 를 쓴다. 이때 서버는 227 Entering Passive Mode (a,b,c,d,p1,p2) 로 접속할 주소를 알려 주는데, NAT 뒤의 서버는 자기가 아는 사설 IP 를 그대로 말한다. 클라이언트는 192.168.x.x 로 접속하려다 타임아웃에 걸린다.
vsftpd 의 경우다.
pasv_enable=YES
pasv_address=203.0.113.10
pasv_min_port=50000
pasv_max_port=50100
IIS FTP 에서는 FTP 방화벽 지원 에서 방화벽의 외부 IP 주소 를 넣는다. 서버 수준과 사이트 수준 두 곳에 설정 자리가 있으므로 둘 다 확인한다.
21번만 열어서는 안 된다. 위에서 정한 범위를 방화벽에서 열고 NAT 에서도 같은 범위를 포워딩한다.
firewall-cmd --permanent --add-service=ftp
firewall-cmd --permanent --add-port=50000-50100/tcp
firewall-cmd --reload
범위를 너무 넓게 잡으면 포워딩 규칙이 커지고 너무 좁게 잡으면 동시 전송 수가 제한된다. 동시 세션 수의 두어 배로 잡는다.
가정용 회선처럼 공인 IP 가 유동이면 pasv_address 를 고정값으로 둘 수 없다. vsftpd 는 이름으로 해석하도록 할 수 있다.
pasv_addr_resolve=YES
pasv_address=ftp.example.com
방화벽의 FTP ALG(application level gateway)는 평문 명령 채널을 들여다보며 PASV 응답의 주소를 자동으로 고쳐 준다. FTPS 로 암호화하면 ALG 가 내용을 볼 수 없으므로 이 자동 보정이 사라진다. 암호화를 켠 순간 멈추기 시작한다면 이것이 원인이다. 위의 세 가지를 모두 명시적으로 맞춰야 한다.
# 명령 채널과 응답을 눈으로 본다
ftp -d ftp.example.com
# 227 응답에 무엇이 오는지 확인한다
tcpdump -i any -n -A 'tcp port 21' | grep -a '227 Entering'
# 데이터 포트가 실제로 열려 있는가
nmap -Pn -p 21,50000-50010 ftp.example.com
227 응답에 사설 IP 가 보이면 1번, 공인 IP 인데 여전히 안 되면 2번 문제다.
SFTP 는 SSH 위에서 도는 단일 연결이라 22번 하나만 열면 된다. NAT 와 방화벽에서 겪는 위 문제가 통째로 사라지고 전송도 암호화된다. 상대가 FTP 만 지원하는 장비가 아니라면 옮기는 편이 낫다. 이름이 비슷한 FTPS 와는 다른 프로토콜이라는 점에 주의한다.