본문 바로가기
Back-end/Python

[Pyftpdlib] FTP 서버 만들기 (2) - 사용자 인증 설정

by 허도치 2020. 2. 12.

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

 

 

0.서론

 지난 포스트에서 pyftpdlib 라이브러리를 설치하고 커맨드를 통해 간단하게 FTP서버를 실행하여 보았다. 그러나, 누구나 다 접속할 수 있는 서버라면 보안에 취약할 수 밖에 없다. 그래서 이번 포스트에서는 파이썬 스크립트를 작성하여 서버를 만들면서 유저를 생성하고 유저별로 접근 가능한 폴더를 설정하는 방법에 대해서 알아보도록 하겠다.

 

 

1. 프로젝트 구조

[ storage/* ]
: 유저별로 접근권한을 부여할 폴더.

[ ftp_server.py ]
: FTP 서버 스크립트를 작성할 파일.

 

 

 

2. 기본 FTP 서버
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
#ftp_server.py
import os
from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.handlers import FTPHandler
from pyftpdlib.servers import FTPServer
 
FTP_HOST = '0.0.0.0'
FTP_PORT = 9021
FTP_DIRECTORY = os.path.join(os.getcwd(), 'storage/anonymous')
 
def main():
    authorizer = DummyAuthorizer()
    
    authorizer.add_anonymous(FTP_DIRECTORY)
 
    handler = FTPHandler
    handler.banner = "Dochi's FTP Server."
 
    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

- 3 ln: 사용자 인증을 생성하는 모듈.
- 4 ln: 사용자 인증, 파일 전송, 로깅 등 FTP서버를 조작하는 모듈.
  * FTPS를 사용할 때는 TLS_FTPHandler 모듈을 사용.
- 5 ln: FTP서버를 실행하는 모듈.
- 7~8 ln: FTP서버의 주소와 포트.
- 9 ln: 공유폴더 지정.
- 12 ln: 사용자 인증 모듈 객체 생성.
- 14 ln: add_anonymous함수로 미인증 사용자에게 공유폴더 지정.
- 16 ln: FTPHandler 객체 생성.
- 17 ln: FTP서버의 배너 설정.
- 19 ln: 사용자 인증 모듈 적용.
- 20 ln: TCP 통신 포트 범위 설정.
- 23 ln: FTPServer 객체 생성.
- 25~26 ln: 서버 설정.
  * server.max_cons: 최대 연결 개수.
  * server.max_cons_per_ip: IP당 최대 연결 개수.
- 28 ln: 서버 실행.

 

2-2. 실행

$ python ftp_server.py

 

2-3. 실행 결과

[ FTP서버 실행 로그 ]

- FTP서버가 [ 0.0.0.0:9021 ]로 실행되었으며, [ ftp://localhost:9021 ]로 접속가능.
  * [ 0.0.0.0 ]은 외부에서도 접속이 가능하도록 설정.
- 실행되면서 PID도 함께 출력되는데, FTP서버를 종료할 때 필요함.
  * windows: taskkill /F /PID [PID]
  * unix기반: kill -9 [PID]

 

[ Chrome 브라우저에서 FTP서버 접속 ]
[ FTP 서버 접속 로그  ]

- 사용자 인증없이 FTP 서버에 접근할 수 있음.
- 공유한 폴더가 정상적으로 노출됨.
- 파일을 클릭하면 다운로드 받을 수 있음.
- 쓰기(Write)권한이 없기 때문에 업로드는 할 수 없음.

 

 

 

3. 사용자 인증 FTP 서버
3-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
#ftp_server_auth.py
import os
 
from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.handlers import 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')
FTP_ANONY_DIR = os.path.join(os.getcwd(), 'storage/anonymous')
 
def main():
    authorizer = DummyAuthorizer()
    
    authorizer.add_user('admin''admin1234', FTP_ADMIN_DIR, perm='elradfmwMT')
    authorizer.add_user('dochi''dochi1234', FTP_USERS_DIR, perm='elr')
    authorizer.add_anonymous(FTP_ANONY_DIR)
 
    handler = FTPHandler
    handler.banner = "Dochi's FTP Server."
 
    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

- 11~13 ln: 사용자 별로 공유 폴더를 지정.
- 18 ln: 'admin' 유저를 생성하고, 모든 권한(elradfm)을 부여.
- 19 ln: 'dochi' 유저를 생성하고, 탐색(읽기) 권한만 부여.
- 20 ln: 미인증 사용자의 공유 폴더 설정.

* 권한에 대한 자세한 내용은 공식 문서를 참조.

 

3-2. 실행

$ python ftp_server.py

 

3-3. 실행 결과

- FTP서버의 실행로그는 이전 예제와 같음.

 

[ Chrome 브라우저에서 FTP서버 접속 ]
[ FTP 서버 접속 로그  ]

- Chrome 브라우저로 FTP 서버에 접속하면 사용자 인증없이 바로 접속됨.
- 로그를 확인해보면 이전 예제처럼 'anonymous' 유저로 접속되는 것을 알 수 있음.
- 다른 FTP Client를 통해 접속하여 확인해보자.

 

3-4. FTP 커맨드 접속 하기

[ FTP 명령어로 접속 ]

1) 명령 프롬프트 또는 터미널 실행.

2) ftp 프롬프트로 진입.
  > ftp

3) ftp 서버 접속.
  ftp> open localhost 9021
  # open [HOST] [PORT]
  # [ '도메인주소'에 연결되었습니다. ]
  # [ 220 Dochi's FTP Server. ]: FTP서버에 설정한 배너.
  # [ 530 Log in with USER and PASS first. ]: 사용자명과 패스워드를 요청.
  # [ 사용자( 도메인:(none) ): ]: 사용자명 입력 프롬프트 발생.

4) 사용자명 입력.
  > 사용자( 도메인:(none) ): admin
  # [ 331 Username ok, send password. ]: 유저명을 입력완료.
  # [ 암호: ]: 패스워드 입력 프롬프트 발생.

5) 패스워드 입력.
  > 암호: [패스워드]
  # [ 230 Login successful ]: 로그인 성공 로그.
  # 패스워드는 입력할 때 화면에 보이지 않지만 입력되고 있음.

6) 현재 경로 확인.
   ftp> pwd
   # [ 257 "/" is the current directory. ]: 현재 경로가 루트(/, 최상위)인 것을 알 수 있음.


* 로그인에 실패했을 경우, 접속을 해제하고 3번부터 다시 진행.
  ftp> close 또는 disconnect
* 사용자명을 'anonymous'로 입력하고, 패스워드는 입력하지 않으면 'anonymous'로 로그인됨.

[ dir 명령어 실행 결과 ]

7) 현재 경로의 파일/폴더 목록 조회.
   ftp> dir
   # 맨 앞에 [ drwxrwxrwx ]에서 'd'는 디렉토리를 의미함. 파일은 '-'
   # admin, anonymous, users 디렉토리가 확인됨.
   # ftp> ls 명령어는 파일명만 보여줌.

[ cd 명령어 실행 결과 ]

8) 경로 이동.
   ftp> cd admin
   # cd [경로]
   # [ 250 "/admin" is the current directory. ]: 경로가 '/admin'으로 변경됨.

9) 현재 경로의 파일/폴더 목록 조회.
   ftp> dir
   # README-admin.md 파일이 확인됨.


* 'admin' 유저는 쓰기(Write)권한이 있기 때문에 put명령어로 파일을 업로드 할 수 있음.
  ftp> put [파일명]

 

3-5. 접속 결과 확인

- 'admin' 유저로 접속된 것을 확인할 수 있음.
- 서버 설정에서 '
add_anonymous'를 생략하면 Chrome 브라우저에서도 로그인할 수 있음.

 

 

 

마치며

- 이것으로 FTP서버 스크립트를 작성하고 실행해보았다. FTP에 접속을 확인할 때 Chrome 브라우저를 통해서 확인하려고 했는데, FTP 기능이 없어진다고 하니 미리 준비할겸 FTP명령어를 이용하기로 하였다.
- 명령어를 통해 접속하는게 좀 더 빠르긴한데 한글이 깨져서 보이는 단점이 있는게 아쉽다.
- 그리고, 서버를 serve_forever로 실행하는데 이것 때문인지 KeyboardInterrupt( ctrl+c )로 서버가 죽지 않는다. 매번 Process를 Kill하는데 방법을 찾아봐야겠다.
- 문서에 따르면 serve_forever(timeout=None, blocking=True, handle_exit=True)가 기본값인데 handle_exit이 True인 경우 KeyboardInterrupt가 가능하고 로그도 출력해준다. 그래서 단순히 윈도우에서의 문제인 것으로 파악된다.

댓글