시스템에서의 FTP 설정
오늘은ftp에서의 acess control을 마무리 해보겠다.
환경 설정 파일 위치
vi /etc/vsftpd/vsftpd.conf
1. Active vs Passive
FTP가 연결을 두 개 쓴다.
왜 연결이 두 개인가
FTP는 1971년에 설계된 프로토콜이다. 그 시절엔 NAT도 방화벽도 없었고, 모든 호스트가 공인 IP를 가지고 있었다. 그래서 FTP는 명령용 채널과 데이터용 채널을 분리하는 설계를 했다.
[Control Connection] ← 명령 (USER, PASS, LIST, RETR...)
항상 서버 21번 포트
[Data Connection] ← 실제 파일 데이터, 디렉터리 목록
모드에 따라 다름
문제는 데이터 연결을 누가 시작(initiate)하느냐 인데, 이걸 결정하는 게 바로 Active 모드와 Passive 모드다.
Active 모드 — 서버가 클라이언트로 연결을 건다
1. 클라이언트가 서버 21번으로 control connection
[Client : random] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━► [Server : 21]
(명령 채널 수립)
2. 클라이언트가 자기 random 포트(N+1)를 열고
"내 N+1 포트로 데이터 받겠다" PORT 명령 전송
[Client : N+1] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━► [Server]
(listen 중)
3. 서버가 자기 20번 포트에서 클라이언트로 연결 시작
[Client : N+1] ◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ [Server : 20]
(데이터 채널 — 서버가 initiate, 클라이언트 입장에선 inbound!)
3단계가 핵심이다. 데이터 연결을 서버가 클라이언트로 거는 구조다.
이게 왜 문제냐면, 현대의 클라이언트는 대부분 NAT 뒤에 있거나 stateful firewall로 보호되고 있다. 클라이언트 방화벽 입장에서 보면 서버가 갑자기 연결을 새로 시작해서 들어오는 거니까, "인증되지 않은 inbound 연결" 로 보고 차단해버린다.
Stateful firewall은 자신이 시작한 연결(outbound)과 그에 대한 응답은 자동으로 통과시키지만, 외부에서 새로 시작하는 inbound 연결은 명시적 허용 규칙이 없으면 차단한다. Active 모드의 데이터 연결은 클라이언트 입장에서 후자에 해당한다.
이래서 로그인은 되는데 디렉터리 목록이 안 뜨는 그 유명한 FTP 미스터리가 발생한다.
Passive 모드 — 클라이언트가 서버로만 연결한다
1. 클라이언트가 서버 21번으로 control connection
[Client : random] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━► [Server : 21]
2. 클라이언트가 PASV 명령 전송
3. 서버가 자기 random high port(P) 열고 "P로 와" 응답
[Client] ◄━━━ "227 Entering Passive Mode (P)" ━━━ [Server : P listen]
4. 클라이언트가 서버 P 포트로 데이터 연결 시작
[Client : N+1] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━► [Server : P]
(데이터 채널 — 클라이언트가 initiate, 모두 outbound)
이번엔 두 연결 모두 클라이언트가 서버로 향한다. 클라이언트 방화벽 입장에선 전부 정상적인 outbound 연결이라 통과시킨다. 그래서 현대의 모든 FTP 클라이언트는 기본적으로 Passive 모드를 쓴다.
그렇다고 Passive는 만능이지는 않다. 이번엔 서버 방화벽이 골때린다. 서버는 데이터 채널용 high port 범위를 미리 열어둬야 하는데, 어떤 포트가 쓰일지 모르니까 보통 1024~65535 전체를 열거나, 특정 범위를 지정해서 그 범위만 열어둔다. 이 범위 지정에 쓰이는 게 아래의 pasv_min_port, pasv_max_port다.
비교
| 구분 | Control Connect | Data Connect | Client Firewall | Server Firewall |
| Active | Client → Server:21 | Server:20 → Client:random | 차단됨 (보통) | 21, 20만 열면 됨 |
| Passive | Client → Server:21 | Client → Server:high-port | 문제없음 | high port 범위 열어야됨 |
데이터 연결을 누가 거느냐의 차이. Active는 서버가 → 클라이언트, Passive는 클라이언트 → 서버.
2. 포트 변경 — listen_port와 passive
vsftpd 기본 포트는 21번이지만, 자동화된 스캔 봇이 21번을 우선적으로 노리니까 보안 차원에서 바꿔두는 경우가 많다.
# /etc/vsftpd/vsftpd.conf
listen_port=2121
pasv_max_port=5000
pasv_min_port=5000

옵션별 의미.
● listen_port=2121 — control 포트를 21에서 2121로 변경 (standalone 모드, listen=YES일 때만 동작)
● pasv_max_port / pasv_min_port — Passive 모드 데이터 채널이 사용할 포트 범위 지정
위 예시처럼 max와 min을 똑같이 5000으로 잡으면 데이터 채널이 5000번 포트 단 하나만 사용하게 된다. 방화벽 규칙 관리가 가장 단순해진다. 다만 동시에 데이터 전송이 많아지면 충돌이 날 수 있어서, 실제 운영에선 pasv_min_port=40000, pasv_max_port=40100 같은 식으로 좁은 범위를 잡는 게 보통이라고 한다.
방화벽도 같이 열어줘야 한다
vsftpd 설정만 바꾸고 끝나면 안 된다. firewalld도 같이 열어줘야 클라이언트가 접속할 수 있다.
firewall-cmd --permanent --add-port=2121/tcp # control 포트
firewall-cmd --permanent --add-port=5000/tcp # passive 데이터 포트
firewall-cmd --reload # 규칙 적용
규칙을 삭제하고 싶으면 --remove-port.
firewall-cmd --permanent --remove-port=2121/tcp
firewall-cmd --reload
3. 대역폭 속도 제한 — local_max_rate
공용 FTP 서버를 운영하다 보면 한 사람당 대역폭을 제한해야 할 때가 있다. 한 명이 다 끌어다 써버리면 다른 사용자가 느려지니까.
local_max_rate=0 # 제한 없음 (기본)
local_max_rate=300000 # 사용자당 약 300 KB/s 로 제한

단위는 bytes per second다. bits/sec(bps)가 아니라 BYTES/sec다.
이걸 걸어놓으면 한 사용자가 회선을 다 잡아먹는 유사 DOS공격을 막을 수 있다.
Anonymous 사용자에겐 anon_max_rate라는 별도 옵션이 있어서 더 빡시게 잡을 수 있다.
4. 동시 접속자 제한 — max_clients / max_per_ip
서버의 자원과 라이센스에 따라 동시 접속자 수를 제한해야 할 때가 있다.
max_clients=30 # 전체 동시 접속 사용자 상한
max_per_ip=3 # 같은 IP에서의 동시 접속 상한
● max_clients — 서버 전체에서 동시에 접속할 수 있는 사용자 수. 0이면 무제한
● max_per_ip — 같은 IP 한 곳에서 여러 접속을 허용할지. 한 사용자가 여러 세션을 열어서 자원을 독점하는 걸 막는 데 유용
5. 세션 타임아웃
오래 켜놓고 안 쓰는 세션은 자원만 잡아먹는다. 정해진 시간 동안 활동 없으면 끊어버리는 옵션이 두 개 있다.
idle_session_timeout=300 # 명령어 사이 idle 시간 (기본 300초 = 5분)
data_connection_timeout=300 # 데이터 전송이 멈춘 시간 (기본 300초 = 5분)

● idle_session_timeout — 클라이언트가 명령을 보내지 않고 가만히 있는 시간의 상한. ls, cd 같은 명령 사이의 간격이 이 시간을 넘으면 끊긴다
● data_connection_timeout — 데이터 전송 중인데 전송이 멈춰서 stall 상태가 된 시간의 상한. 불안정한 네트워크에서 끊어진 채로 자원을 잡고 있는 걸 방지
idle_session_timeout=30으로 설정하고 접속해서 30초 동안 아무 명령도 안 친 다음 ls -l을 시도하면 timeout 응답이 뜨면서 끊긴 걸 확인할 수 있다.
Linux 셸의 자체 타임아웃 TMOUT
이건 vsftpd 옵션이 아니라 bash 셸의 환경 변수다. SSH나 콘솔 세션에 적용된다.
export TMOUT=30
이렇게 하면 30초 동안 아무 키 입력이 없으면 셸 자체가 종료된다. 운영 서버에서 root 셸을 켜놓고 자리 비우는 사고를 방지하는 데 유용하다.
6. 사용자 디렉터리 이동 제한 — chroot
사용자가 자기 홈 디렉터리 위로 못 올라가게 가둬두는 설정이다.
chroot 없이 FTP 서버를 운영하면 사용자가 cd /etc, cd /root 같은 식으로 시스템 전체를 살펴볼 수 있다.
1) 모든 사용자를 chroot로 가두기
chroot_local_user=YES
allow_writeable_chroot=YES

chroot_local_user=YES 하나만 켜두면 접속이 아예 안 된다. 왜냐하면 vsftpd 2.3.5부터 chroot 디렉터리가 쓰기 가능하면 보안상 위험하다는 이유로 거부하기 때문이다. 그래서 allow_writeable_chroot=YES를 같이 줘야 한다.
왜 쓰기 가능한 chroot가 위험한가?
chroot 환경 안에서 사용자가 .bash_profile이나 비슷한 시작 스크립트를 자기 홈에 직접 쓸 수 있다면, 그 안에 악성 코드를 심어두고 다음에 root가 실수로 그 사용자의 셸 환경을 평가할 때 권한 상승이 일어날 수 있다.
이 설정이 적용되면 사용자는 자기 홈 디렉터리 위로 절대 못 올라간다. cd ..를 해도 자기 홈에서 멈춘다.
2) 특정 사용자만 chroot로 가두기
특정 사용자만 가두고 나머지는 자유롭게 두려면.
chroot_local_user=NO
allow_writeable_chroot=YES
chroot_list_enable=YES
chroot_list_file=/etc/vsftpd/chroot_list
그리고 chroot_list 파일을 만들고 안에 가둘 사용자 이름을 적는다.
vi /etc/vsftpd/chroot_list
pse1
pse2
이러면 pse1, pse2만 chroot에 갇히고, 다른 사용자는 자유롭게 이동한다.
3) 특정 사용자만 자유 이동 허용
대부분을 가두고 일부만 풀어주려면 chroot_local_user를 다시 YES로 바꾼다.
chroot_local_user=YES
allow_writeable_chroot=YES
chroot_list_enable=YES
chroot_list_file=/etc/vsftpd/chroot_list
chroot_list에 적힌 사용자만 chroot에서 제외되고, 나머지 사용자는 모두 갇힌다. 관리자 계정 같은 걸 자유롭게 두고 일반 사용자는 모두 가두는 패턴이다.
정리 표
| chroot_local_user | chroot_list_enable | chroot_list 안의 사용자 | 나머지 사용자 |
| YES | NO | (적용 안 됨) | 모두 chroot |
| NO | YES | chroot됨 | 자유 |
| YES | YES | 자유 | chroot됨 |
7. 특정 파일 접근 제한 — deny_file와 hide_file
마지막으로 파일 단위의 접근 제어다. 이름이 비슷한 두 옵션이 있는데, 동작이 완전히 다르다.
deny_file={*.mp3,*.mp4} # 해당 파일에 모든 접근 거부
hide_file={*.mp3,*.mp4} # 해당 파일을 디렉터리 목록에서 숨김

| 옵션 | 디렉터리 목록 | 직접 접근 | |
| deny_file | 보임 | 거부됨 | 보이지만 못 만짐 |
| hide_file | 안 보임 | 파일명 알면 가능 | 안 보이지만 만질 수 있음 |
이게 보안적으로 정말 중요한 차이다.
hide_file로 .config나 .password 같은 민감한 파일을 가린다고 안심하면 안 된다. 파일명을 추측하거나 알아낸 공격자는 그대로 다운로드 가능하다. 진짜 차단하려면 deny_file을 써야 한다.
더 정확히는 — deny_file도 "간단한 차단 도구"일 뿐이고, vsftpd 공식 문서도 "심각한 접근 제어에는 쓰지 말고 파일시스템 권한을 쓰라"고 권한다. 진짜 보안은 chmod, chown, 그리고 chroot로 풀어야 한다.
패턴 문법
두 옵션 모두 같은 패턴 문법을 쓴다. 단순화된 정규식의 일종이다.
hide_file={*.mp3,*.mp4,.hidden,hide*}
* 임의의 문자열
? 임의의 한 글자
{a,b,c} OR 패턴
예시.
deny_file={*.exe,*.bat,*.cmd,*.sh} # 실행파일 업로드/다운로드 차단
hide_file={.*} # 모든 숨김 파일을 목록에서 한 번 더 숨김
vsftpd.conf 옵션 정리
이 글에서 다룬 옵션들을 한꺼번에 정리해두면 이런 형태가 된다.
# 포트 설정
listen=YES
listen_port=2121
pasv_min_port=40000
pasv_max_port=40100
# 대역폭 / 동시 접속
local_max_rate=300000
max_clients=30
max_per_ip=3
# 타임아웃
idle_session_timeout=300
data_connection_timeout=300
# chroot (모든 로컬 사용자 가두기)
chroot_local_user=YES
allow_writeable_chroot=YES
# 파일 접근 제어
deny_file={*.exe,*.bat,*.cmd}
hide_file={.*}
설정 변경 후엔 반드시 vsftpd를 재시작해서 적용해줘야 한다.
systemctl restart vsftpd
'시스템보안' 카테고리의 다른 글
| 티렉터리, 파일에서의 권한 관리에 대하여 (0) | 2026.06.29 |
|---|---|
| Xinetd에서의 Access Control에 대하여 (0) | 2026.06.26 |
| 시스템 내부에서의 Ping 차단에 대하여 (0) | 2026.06.25 |
| ftp, telnet, ssh에서의 사용자 Access Control에 대하여 (0) | 2026.06.23 |
| John the Ripper를 사용한 Password Crack과 mkpasswd를 사용한 패스워드 만들기에 대하여 (0) | 2026.06.19 |