Skip to main content

Command Palette

Search for a command to run...

rfc 959 읽어보면서 ftp이해하기

Updated
rfc 959 읽어보면서 ftp이해하기
K

backend developer interested in technical problem

들어가며

프로젝트에서 FTP를 통해 파일을 전송하는 클라이언트를 개발했을 때 이야기입니다. 프로토콜에 대한 이해 없이 개발을 진행하다 보니 불필요하게 시간을 소모한 경험이 있어서 RFC 원문을 읽어보게 되었습니다.

읽게 된 계기

당시 FTP 프로토콜에 대한 이해가 부족한 상태로 개발을 시작했습니다. '제어를 위한 포트와 데이터 전송을 위한 포트가 분리되어 있다' 정도만 알고 있었고 Active 모드와 Passive 모드의 차이를 제대로 이해하지 못했습니다. 폐쇄망 환경에서 문제가 발생했습니다. 특정 포트만 데이터 전송용으로 포트포워딩된 상황이었는데 Active 모드로 연결을 시도하다 보니 파일 전송이 되지 않는문제였습니다. Passive 모드로 변경하고 나서야 정상 동작했는데 원인을 파악하는 데 시간이 걸렸고 이건 FTP에 대한 이해가 있었다면 쉽게 해결할 문제였습니다. 회고를 하면서 FTP를 제대로 이해해두면 좋겠다는 생각이들었습니다. 그래서 이번기회에 RFC를 정독해보자는 생각이 들었고 RFC 959 문서를 직접 읽어보게 되었습니다.

왜 RFC인가

FTP에 대한 정보는 검색하면 많이 나옵니다. 하지만 대부분 'Active는 이렇고 Passive는 이렇다'라고 설명하는 좋은 글은 이미 많습니다. 다만 왜 이렇게 설계했는지 어떤 문제를 해결하려고 했는지를 이해하려면 원본 명세를 읽는 게 가장 확실하다고 생각했습니다. 이 글은 RFC 원문을 번역하거나 내용을 그대로 옮기는 것이 아니고 읽으면서 메모한 핵심 설계 원리와 실무에서 의미 있는 부분을 정리한 것입니다.

다루는 내용

  • FTP의 이중 연결 구조 (Control Connection + Data Connection)

  • 데이터 타입: ASCII vs Binary 전송 모드

  • Active vs Passive 모드의 차이와 선택 기준

  • 응답 코드 체계

  • 프로젝트에서 겪은 Active/Passive 모드 선택과 그 이유

RFC 959읽고 FTP 설계 이해하기

읽으면서 몇가지 왜 이렇게 설개했나 에 대한 인사이트를 얻을 수 있었습니다. 간단하게 4가지 정도만 살펴보겠습니다.

1.이중 연결 구조

문서를 보면 전송은 데이터커넥션으로만 이어지고 명령어는 컨트롤커넥션으로만 전달된다는 내용이 있습니다.

  • Control Connection: 명령과 응답을 주고받는 경로 (Telnet 프로토콜 기반)

  • Data Connection: 실제 파일 데이터를 전송하는 경로

왜 분리했을까?

명령 처리와 데이터 전송을 독립적으로 관리하기 위함입니다.

  • 대용량 파일 전송 중에도 ABORT 같은 명령을 보낼 수 있음

  • Control Connection은 데이터 전송이 끝날 때까지 유지되어야 함

Control Connection이 Telnet 기반이라는 것

FTP 명령어(USER, PASS, RETR 등)는 Telnet 프로토콜 위에서 동작합니다. 명령은 텍스트 기반이고 줄 끝은 CRLF(\r\n)로 구분됩니다.

                                            -------------
                                            |/---------\|
                                            ||   User  ||    --------
                                            ||Interface|<--->| User |
                                            |\----^----/|    --------
                  ----------                |     |     |
                  |/------\|  FTP Commands  |/----V----\|
                  ||Server|<---------------->|   User  ||
                  ||  PI  ||   FTP Replies  ||    PI   ||
                  |\--^---/|                |\----^----/|
                  |   |    |                |     |     |
      --------    |/--V---\|      Data      |/----V----\|    --------
      | File |<--->|Server|<---------------->|  User   |<--->| File |
      |System|    || DTP  ||   Connection   ||   DTP   ||    |System|
      --------    |\------/|                |\---------/|    --------
                  ----------                -------------

                  Server-FTP                   USER-FTP

Server-to-Server 전송 (3자 전송)

FTP는 User가 직접 파일을 받지 않고, 두 서버 간 직접 전송을 중개하는 것도 가능합니다. Control Connection과 Data Connection이 분리되어 있기 때문에 아래와 같이 서버간 파일 전송 과정에서 유저는 이를 중개하는 구조가 가능합니다.

                    Control     ------------   Control
                    ---------->| User-FTP |<-----------
                    |          | User-PI  |           |
                    |          |   "C"    |           |
                    V          ------------           V
            --------------                        --------------
            | Server-FTP |   Data Connection      | Server-FTP |
            |    "A"     |<---------------------->|    "B"     |
            -------------- Port (A)      Port (B) --------------

User-PI(PI: Protocol Interface)가 양쪽 서버에 Control Connection을 맺고, 서버 A와 B가 직접 Data Connection으로 파일을 주고받는 구조입니다. 다만 현대에서는 보안 이슈로 대부분의 FTP 서버가 이 기능을 비활성화해두고 있습니다. 그냥 이런 구조도 가능하구나 인지하고 넘어가시면 될 것 같습니다.


2.데이터 타입: ASCII vs Binary

RFC 959에서는 데이터 전송 타입을 여러 가지 정의하고 있습니다.

타입설명
ASCII텍스트 파일용, 줄바꿈 변환 O (기본값)
Image (Binary)바이너리 파일용, 변환 없이 그대로 전송(현대에 대부분 Image타입 사용)
EBCDICIBM 메인프레임용
Local특수 바이트 크기 지정용

현대에서는 ASCIIImage(Binary) 두 가지만 알면 됩니다. 그중에서도 거의 Image만 씁니다.

ASCII 모드가 필요했던 이유

1985년 당시에는 시스템마다 줄바꿈 문자가 달랐습니다.

  • Windows: CRLF (\r\n)

  • Unix/Linux: LF (\n)

  • Mac (Classic): CR (\r)

FTP ASCII 모드는 이 차이를 자동으로 변환해주는 역할을 했습니다. Unix에서 Windows로 텍스트 파일을 보내면 LF → CRLF로 변환되는 식입니다.

현대에서는 Binary를 씁니다

요즘은 대부분의 텍스트 에디터나 애플리케이션이 줄바꿈 차이를 알아서 처리합니다. 굳이 FTP가 변환해줄 필요가 없어졌습니다. 오히려 ASCII 모드는 문제를 일으킵니다.

  • 실행 파일, 이미지, 압축 파일 등을 ASCII 모드로 전송하면 데이터가 손상됩니다.

  • 바이트 중에 줄바꿈 문자와 같은 값이 있으면 변환되어 버리기 때문입니다.

또한 설계당시에는 Image를 전송하기 위한 모드였겠지만 이게 Binary데이터를 전송하기 때문에 거의 모든 모드에서 쓰이는 계기가 된것 같습니다. 그래서 모드 이름도 Image이고요. 실제로 모드를 지정할 떄 TYPE I (IMAGE) 라고 씁니다. 대부분의 현대 FTP클라이언트는 파일을 다운로드하기 전 TYPE I 명령어를 자동으로 전송한다고합니다.

또한 데이터타입과 별개로 파일구조를 설정하는 STRU나 데이터전송방식(Active/Passive아님)MODE가 있습니다. 이 둘은 현대에서는 하나의값만 사용됩니다. 현대에 와서는 STRU는 F(File) MODE는 S(Stream) 가 거의 고정으로 사용됩니다. 중요하지 않은내용이라 어떤 값들이 있고 어떤 내용인지는 다루지 않겠습니다. 궁금하시면 문서의 3.1.2.DATA STRUCTURES3.4.TRANSMISSION MODES 섹션을 확인해주세요.


3.응답 코드(Reply Code) 체계: HTTP Status Code의 시초

FTP 응답은 3자리 숫자 + 텍스트로 구성됩니다.

숫자는 프로그램이 파싱하고, 텍스트는 사람이 읽으라는 설계입니다. 코드에 대한 자세한 내용은 문서를 참고해주세요.

Filezilla로 FTP연결 해보면서 응답코드 확인하기

Filezilla에 로그인 하는 과정의 로그입니다. 이 과정을 보면서 응답코드체계에 대해 간단히 살펴보겠습니다.

  1. 처음에 USER 커맨드에 admin1을 인자로 전달했습니다

  2. 비밀번호가 필요하기에 331 Password required 응답을 반환합니다

  3. 클라이언트는 PASS 인자로 비밀번호를 전달합니다

  4. 서버는 성공적으로 로그인 됐다는 230 User logged in 응답을 반환합니다

  5. CLNT(이는 RFC문서에 없었습니다 찾아보니 비표준이지만 클라이언트를 설정하는 널리쓰이는 확장명령어 라고합니다)를 설정해줍니다.

  6. 인코딩 설정도해줍니다. OPTS또한 RFC 959 에는 명세되어 있지 않았습니다. 찾아보니RFC 2389에 추가된 설정값입니다.

HTTP 응답 코드체계의 기반

응답코드를 보시면 HTTP StatusCode와 상당히 유사하다는것을 알 수 있습니다. 시기적으로 HTTP보다 먼저 명세되었고 이를 통해 HTTP 응답코드의 기반이 되었다는걸 알 수 있었습니다.

FTP (1985)           HTTP (1991)
-----------          -----------
1xx 예비             1xx Informational
2xx 성공             2xx Success  
3xx 추가정보필요      3xx Redirection
4xx 일시오류         4xx Client Error
5xx 영구오류         5xx Server Error

4.Active/Passive 모드

이미 Active모드와 Passive모드의 특성과 차이점에 대해서 설명한 좋은 글이 많으니 생략하겠습니다.

RFC 959에서의 설명

RFC 959에 passive와 active라는 용어는 등장합니다. 하지만 자세한 설명은 없었습니다.

PORT 명령

"The argument is a HOST-PORT specification for the data port to be used in data connection."

Client가 자신의 IP와 포트를 Server에게 알려줍니다. Server가 해당 포트로 연결을 시작합니다. (Server = Active, Client = Passive)

PASV 명령

"This command requests the server-DTP to 'listen' on a data port... and to wait for a connection rather than initiate one"

Server가 데이터 포트에서 listen하며 기다립니다. Client가 Server에게 연결을 시작합니다. (Client = Active, Server = Passive)

자세한 설명이 없는 이유는?

RFC 959에는 '언제 PORT를 쓰고 언제 PASV를 써야 하는지'에 대한 가이드가 없습니다.

당시에는 NAT/방화벽이 일반적이지 않았기 때문입니다.

  • 대부분의 호스트가 public ip를 가지고 있었음

  • Server가 Client에게 연결해도 문제될 상황이 드물었음

오늘날의 관점에서의 정리

구분PORT 사용 (Active)PASV 사용 (Passive)
ServerActive (연결 시작)Passive (연결 대기)
ClientPassive (연결 대기)Active (연결 시작)
연결 방향Server → ClientClient → Server
방화벽/NATClient측 인바운드 차단 시 실패문제없음

현대 환경에서는 Client가 NAT/방화벽 뒤에 있는 경우가 대부분이므로, PASV(Passive 모드)를 주로 사용합니다.

실무에서의 경험: 왜 Passive 모드를 선택했는가

실무에서 FTP를 사용했던 경험과 상황에 대해서 이야기하려고합니다.

상황

K-City 프로젝트에서 여러 대의 교통교차로에 위치한 PC에 로깅된 레이더 데이터를 NAS로 중앙집중화해야 했습니다. 아래는 당시 아키텍처를 간단하게 나타낸 그림입니다. 문제는 인프라 환경이었습니다. 사전에 신청해둔 포트만 사용 가능했습니다.

Active 모드를 사용할 수 없는 이유

Active 모드에서는 Server가 Client에게 Data Connection을 시작합니다.이때 Client측 포트는 랜덤하게 할당됩니다.

Client (로컬 PC)                    Server (NAS)
      |                                 |
      |---- PORT 10.0.0.5,xxx,yyy ----> |  Random POrt
      |                                 |
      |<===== Data Connection ========= |  Server가 Client의 랜덤 포트로 연결 시도
      |      (차단)                      |

사전에 신청한 포트가 아니기 때문에 Client측 인바운드에서 차단됩니다.어떤 포트가 할당될지 알 수 없으니 미리 신청할 수도 없습니다.

Passive 모드로 해결

Passive 모드에서는 Client가 Server에게 Data Connection을 시작합니다.Server측 포트를 고정된 범위로 지정할 수 있습니다.

Client (로컬 PC)                    Server (NAS)
      |                                |
      |------------ PASV ------------->|
      |                                |
      |<-- 227 (10.0.0.100,195,88) ----|  "포트 50000 listen"
      |                                |   (195×256 + 88 = 50000)
      |                                |
      |===== Data Connection =========>|  Client가 Server:50000으로 연결
      |      (허용)                    |

사전에 신청해둔 포트:

  • Control Connection: 21번 (고정)

  • Data Connection: 50000~50100 범위 (Passive 모드용)

Server(NAS)의 FTP 설정에서 Passive 포트 범위를 예를들어 50000~50100으로 지정해두면 해당 범위 내에서만 Data Connection이 생성됩니다.

마치며

RFC 959를 직접 읽어보면서 FTP의 설계 의도를 이해할 수 있었습니다.또한 몇가지 새로운 것들을 알게 됐습니다.

  • 이중 연결 구조의 이유: 명령과 데이터를 분리해서 대용량 전송 중에도 제어가 가능

  • 응답 코드 체계: HTTP 응답 코드의 원형이 1985년 FTP에서 시작되었다는 것

  • ASCII/Binary 모드의 존재 이유: 시스템마다 다른 줄바꿈 문자를 처리하기 위한 설계

또한 다음에 비슷한 상황을 만나면 원인 파악에 시간을 낭비하지 않을 것 같습니다.원본 명세를 읽는 건 시간이 걸리지만, 그만큼 깊은 이해를 얻을 수 있습니다.

생략할곳은 생략해도 될 것같다

RFC문서 서두에 용어정리, 관련문서를 언급하는 부분도 양이 상당한데 배경지식만 있다면 건너뛰어도 좋을 것 같습니다.

시대적 맥락

자세히 설명되지 않은 내용이 있습니다.

Active/Passive 모드처럼 현대에서는 중요한 개념이지만, 당시에는 문제가 되지 않아서 간략히만 다룬 경우가 있습니다. 1985년에는 NAT/방화벽이 일반적이지 않았기 때문입니다.

현대에서는 불필요한 내용도 있습니다.

EBCDIC 타입, Page Structure, Block/Compressed 모드 등은 1980년대 메인프레임 환경을 위한 것으로, 현대 환경에서는 거의 사용되지 않습니다.

RFC를 읽을 때는 "왜 이렇게 설계했는가"와 함께 "이게 작성된 시대는 어떤 환경이었는가"를 고려해야 할 것 같습니다.

추후에 더 해보고 싶은 것

  • 와이어샤크로 실제로 패킷을 보면서 FTP 과정을 이해하면 더 깊은 이해를 할 수 있을것같습니다.

  • 글에서는 생략했지만 문서에 커맨드나, 상태다이어그램이 상당부분 분량을 차지하는데 이부분을 참고하면 이해하는데 더 도움이 될 것 같습니다. 길어서생략했습니다.

참고

https://www.rfc-editor.org/rfc/rfc959

https://www.rfc-editor.org/rfc/rfc2389

https://en.wikipedia.org/wiki/List_of_FTP_commands