본문 바로가기
Back-end/Python

[Pyftpdlib] FTP 서버 만들기 (4) - SSL/TLS FTP 서버

by 허도치 2020. 2. 18.

2020/02/12 - [Back-end/Python] - [Pyftpdlib] FTP 서버 만들기 (1) - 설치 및 실행

2020/02/12 - [Back-end/Python] - [Pyftpdlib] FTP 서버 만들기 (2) - 사용자 인증 설정

2020/02/18 - [Back-end/Python] - [Pyftpdlib] FTP 서버 만들기 (3) - 사용자 패스워드 암호화

 

 

0. 서론

 지금까지 사용자가 접근할 수 있는 폴더를 지정하고 권한을 부여하고 패스워드를 암호화하면서 보안을 강화시켜 보았다. 하지만, 사용자가 서버와 데이터를 교환할 때 중간에 해커가 가로채간다면 그대로 데이터가 유출될 것이다.

 HTTP도 이런 취약점이 있었지만 SSL/TLS를 결합하여 서버와 클라이언트가 데이터를 교환할 때 암호화된 패킷으로 주고받는 HTTPS를 내놓으면서 해소되었다. HTTPS처럼 FTP도 SSL/TLS를 결합한 FTPS가 존재한다. SFTP와 혼동할 수 있는데 SFTPSSH(Secure Shell)을 이용한 Linux의 유사 FTP이다. FTP는 21번 포트를 사용하고 SFTP는 SSH의 22번 포트를 사용한다.

 그래서, 이번 포스트에서는 SSL/TLS 자체 인증서를 발급받고, pyftpdlib을 이용하여 FTPS를 설정하는 방법에 대해서 알아보도록 하겠다.

 

 

 

1. SSL/TLS 자체 인증서 발급
1-0. 시작하기전에...

[OpenSSL] SSL/TLS 인증서 발급받기 포스트에서 한번 다루었던 내용이므로 자세한 내용은 해당 포스틀 참조.

 

1-1. 서버 개인키(Private Key) 생성

openssl genrsa -aes256 -out private.key 2048

 

1-2. 서버 공개키(Public Key) 생성

openssl rsa -in private.key -pubout -out public.key

 

1-3. 인증 서명 요청서(CSR, Certificate Signing Request) 생성

openssl req -new -key private.key -out private.csr

 

1-4. 인증 기관(CA, Certificate Authority) 키 생성

openssl genrsa -aes256 -out root.ca.key 2048

 

1-5. 인증 기관의 인증 서명 요청서(CSR) 생성

openssl req -x509 -new -nodes -key root.ca.key -days 3650 -out root.ca.pem

 

1-6. 자체 인증 기관을 통한 인증서(CRT, Certificate) 발급

openssl x509 -req -in private.csr -CA root.ca.pem -CAkey root.ca.key -CAcreateserial -out private.crt -days 3650

 

 

 

2. FPTS 서버
2-1. 스크립트 작성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#ftp_server_tls.py
import os
import sys
from hashlib import md5
 
from pyftpdlib.authorizers import DummyAuthorizer, AuthenticationFailed
from pyftpdlib.handlers import TLS_FTPHandler
from pyftpdlib.servers import FTPServer
 
FTP_HOST = '0.0.0.0'
FTP_PORT = 9021
 
FTP_ADMIN_DIR = os.path.join(os.getcwd(), 'storage')
FTP_USERS_DIR = os.path.join(os.getcwd(), 'storage/users')
 
KEYFILE = os.path.abspath(os.path.join(os.path.dirname(__file__), "certs""private.key"))
CERTFILE = os.path.abspath(os.path.join(os.path.dirname(__file__), "certs""private.crt"))
 
class DummyMD5Authorizer(DummyAuthorizer):
    def validate_authentication(self, username, password, handler):
        if sys.version_info >= (30):
            password = password.encode('utf-8')
        hash = md5(password).hexdigest()
        try:
            if self.user_table[username]['pwd'!= hash:
                raise KeyError
        except KeyError:
            raise AuthenticationFailed
 
def main():
    authorizer = DummyMD5Authorizer()
    
    authorizer.add_user('admin''c93ccd78b2076528346216b3b2f701e6', FTP_ADMIN_DIR, perm='elradfmwMT')
    authorizer.add_user('user''b5b73fae0d87d8b4e2573105f8fbe7bc', FTP_USERS_DIR, perm='elr')
   
    handler = TLS_FTPHandler
    handler.banner = "Dochi's FTPS Server."
    
    handler.keyfile = KEYFILE
    handler.certfile = CERTFILE
    
    handler.authorizer = authorizer
    handler.passive_ports = range(6000065535)
    
    address = (FTP_HOST, FTP_PORT)
    server = FTPServer(address, handler)
    
    server.max_cons = 256
    server.max_cons_per_ip = 5
 
    server.serve_forever()
 
if __name__ == '__main__':
    main()
cs

- 7 ln: SSL/TLS FTP서버를 실행하기 위한 FTPHandler.
- 16~17 ln: 위에서 발급받은 개인키와 인증서의 경로.
- 36 ln: TLS_FTPHandler 객체 생성.
- 39~40 ln: 개인키와 인증서를 등록.

 

2-2. 실행

$ python ftp_server_tls.py

 

2-3. FTPS서버 실행 결과

[ FTPS 서버 실행 로그 ]

- 서버 실행시 인증서의 패스워드를 입력하는 프롬프트가 실행됨.
  * Enter PEM pass phrase: [패스워드 입력]

- 인증이 완료되면 'starting FTP+SSL server' 문구와 함께 FTPS 서버가 실행됨.
- 접속 프로토콜이 [ ftp:// ]에서 [ ftps:// ]로 변경됨.
  * FileZilla Client와 같은 일부 FTP Client에서는 [ ftpes:// ]로 프로토콜 사용.

 

2-4. FTP서버에 SSL/TLS 인증서 적용 확인

1) SSL/TLS 인증서 확인 명령어 실행.
   $ openssl s_client -connect localhost:9021 -state -debug -starttls ftp

2) SSL/TLS 인증서가 정상적으로 적용된 것을 확인할 수 있음.


* 이 명령어가 기본적으로 HTTPS 인증서를 확인하는 명령어 인데, FTP에 쓰다보니 HandShake에서 계속 오류가 발생하였다.
* FTPS를 확인하려면 반드시 [ -starttls ftp ] 옵션을 적용해주자.
* HTTPS가 적용된 웹사이트라면 다 확인해볼 수 있다. (HTTPS는 기본 포트로 443을 사용.)
* openssl s_client -connect google.com:443 -state -debug

 

2-5. FPTS 서버 접속 결과

[ FTPS 서버 접속 로그 ]

- FTP 명령어를 통해 접속하면 기존과 비교해서 달라진 점이 없음.
  * HTTPS사이트를 HTTP로 접속해도 알아서 HTTPS로 접속되는 것처럼
  * FTP로 접속해도 FTPS로 접속된 상태임.
- FileZilla와 같은 다른 FTP Client를 사용해보자.

[ 인증서 검증 알림창 ]
[ FileZilla Client FTPS 접속 로그 ]

- FileZilla Client를 통해 [ ftpes://localhost:9021 ]에 접속하면 위와 같이 '인증서 검증' 알림창이 뜸.
- 알림창에서 확인을 누르면 [ TLS 연결 수립 ] 로그와 함께 접속에 성공함.

 

 

 

마치며

- 사용자 패스워드 암호화에 이어 FTPS까지 설정해두니 든든하다.
- 그리고 지금은 자체 인증서를 통해서 FTPS를 설정했지만, 기회가 된다면 유료로 구매해서 써보는게 좋을것 같다.

[ FileZilla FTPS 접속 오류 ]

- 예제를 만들다가 중간에 다른 옵션을 추가했었는데, 그것 때문에 FileZilla에서 접속하면 [ -15: An unexpeted TLS packet was received. ] 오류가 발생했다.
- TLS_FTPHandlerDTP를 적용하려면, TLS_DTPHandler를 사용해야하는데, ThrottledDTPHandler를 적용해서 오류가 발생했다.

 

 

댓글