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


- 사용자 인증없이 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(60000, 65535)     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 서버에 접속하면 사용자 인증없이 바로 접속됨.
- 로그를 확인해보면 이전 예제처럼 'anonymous' 유저로 접속되는 것을 알 수 있음.
- 다른 FTP Client를 통해 접속하여 확인해보자.
3-4. 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'로 로그인됨.

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

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가 가능하고 로그도 출력해준다. 그래서 단순히 윈도우에서의 문제인 것으로 파악된다.
'Back-end > Python' 카테고리의 다른 글
| [Pyftpdlib] FTP 서버 만들기 (4) - SSL/TLS FTP 서버 (0) | 2020.02.18 | 
|---|---|
| [Pyftpdlib] FTP 서버 만들기 (3) - 사용자 패스워드 암호화 (0) | 2020.02.18 | 
| [Pyftpdlib] FTP 서버 만들기 (1) - 설치 및 실행 (0) | 2020.02.12 | 
| [크롤링] Selenium으로 특가 상품 수집 (5) - Telegram Bot (0) | 2020.02.03 | 
| [크롤링] Selenium으로 특가 상품 수집 (4) - 모듈화 (0) | 2020.02.02 | 
 
										
									 
										
									 
										
									 
										
									
댓글