서버 설정 · 5 min read · Dec 17, 2025

PostgreSQL과 Proftpd

FTP 서버를 설정하고 싶으신가요? 고급 기능이 필요하고 어디로 가야 할지 모르겠다면? proftpd를 사용해 보세요.

1부: PostgreSQL 백엔드로 proftpd 설정하기

이 튜토리얼은 매우 특화된 내용입니다. 저는 proftpd만 다룰 것이며, Postgres와 함께 작동하도록 설정하는 것에 대해서만 설명할 것입니다. proftpd와 Postgres에 대한 기본적인 이해가 필요합니다. 이 튜토리얼을 통해 proftpd를 빠르게 설정하고 데이터베이스를 활용할 수 있습니다. 데이터베이스에 트리거가 있다면 이 구성의 가능성은 거의 무한합니다.

Proftpd 가져오기

먼저 proftpd가 필요합니다. 저는 Ubuntu의 Dapper를 사용할 것이며, Francesco Paolo Lovergine이 준비한 메타 패키지인 “proftpd-pgsql”을 사용할 것입니다. 이 패키지는 다음 모듈로 컴파일된 proftpd의 복사본을 설정합니다. 참고로, proftpd는 모듈과 함께 제공되지만, conf 파일은 표준 proftpd 보일러플레이트입니다. mod_sql 문서를 확인하는 것을 강력히 권장합니다. 비록 품질이 떨어지지만 여전히 유용합니다. proftpd 사이트의 문서는 매우 오래되었으니 대신 이 링크를 사용하세요: Link to docs. 또한 버전에 주의하세요. proftpd는 현재 1.3에 있지만, 저는 1.2.10을 사용할 것입니다. Dapper에서 사용되는 버전입니다.

이 proftpd 버전에 컴파일된 모듈은 다음과 같습니다.

  mod_core.c  
  mod_xfer.c  
  mod_auth_unix.c  
  mod_auth_file.c  
  mod_auth.c  
  mod_ls.c  
  mod_log.c  
  mod_site.c  
  mod_auth_pam.c  
  mod_quotatab.c  
  mod_sql.c  
  mod_sql_postgres.c  
  mod_quotatab_sql.c  
  mod_ratio.c  
  mod_tls.c  
  mod_rewrite.c  
  mod_radius.c  
  mod_wrap.c  
  mod_quotatab_file.c  
  mod_delay.c  
  mod_readme.c  
  mod_ifsession.c  
  mod_cap.c

proftpd-pgsql을 가져오는 명령어를 입력하세요:

apt-get install proftpd-pgsql

권한 설정하기

제 목적을 위해 (기억하세요, 저는 우주의 중심입니다) 필요한 것은 일차원 구조뿐이었습니다. 그룹은 유닉스 권한이 아닌 다른 수준에 저장되기 때문입니다. 따라서 모든 파일은 정적 권한으로 들어와야 합니다. 저는 소유자와 소유자의 그룹만 신경 쓰면 됩니다. 제 계획은 proftpd의 기본 사용자 ftp가 제가 만들 그룹인 ‘ftp’에 속하도록 하는 것이었습니다. 그런 다음 Postgres를 이 그룹의 구성원으로 만들 예정입니다. 이유는 2부에서 확인하세요. 두 개체가 동일한 그룹의 구성원이 되어야 하며, proftpd chroot 폴더는 775여야 합니다. 그래야 전체 그룹이 파일을 추가(이동)할 수 있습니다. 또한 이 폴더는 사용자 ‘ftp’가 소유하고 그룹 ‘ftp’에 속하도록 설정했습니다. 다음 스크립트를 사용하여 이를 달성했습니다.

groupadd ftp
usermod -G ftp ftp
usermod -G ftp,postgres postgres
chown ftp:ftp ./ftp_directory/
chmod 775 ./ftp_directory/

Postgres 설정하기

이제 SQL 테이블을 준비할 것입니다. 저는 스키마를 활용하여 설계하며, 테이블의 접두사 ‘ftp.’는 이를 위한 것입니다. 모든 테이블을 ftp 스키마에 넣었습니다. 한 가지 주목할 점은, 이 프로젝트에 대해 권한을 매우 제한적으로 설정했습니다. mod_sql이 얼마나 안전한지 확신할 수 없기 때문입니다. 그렇게 하면서 저는 데이터베이스에서 proftpd를 위한 특별한 사용자 proftp를 만들었습니다(이 사용자보다 더 많은 애플리케이션이 사용합니다). 이 사용자는 auth_table에서 선택할 수 있고 file_log 테이블에 삽입할 수 있도록 제한됩니다. 이 정책의 어떤 형태를 채택하는 것을 강력히 권장합니다. 최소한 이로 인해 mod_sql 코드가 우리가 생각하는 것만큼 안전하지 않을 경우에도 어느 정도의 파괴적인 SQL 인젝션 공격이나 정보 유출을 방지할 수 있습니다. 구글에서 검색해보면 mod_sql 코드베이스의 이전 보안 문제를 확인할 수 있습니다. mod_sql이 데이터베이스에 보내는 각 쿼리는 “LIMIT 1” 절이 추가되며, mod_delay가 기본적으로 구성되어 있기 때문에 이 프로젝트에는 충분하다고 생각합니다.

제 SQL 초기화 스크립트는 다음과 같습니다.

ALTER USER proftp UNENCRYPTED PASSWORD 'dealermadeftp';
CREATE SCHEMA ftp;
GRANT USAGE ON SCHEMA ftp TO proftp;

CREATE TABLE ftp.users (
        pkid      serial      PRIMARY KEY,
        userid    text        NOT NULL UNIQUE,
        passwd    text,
        uid       int,
        gid       int,
        homedir   text,
        shell     text
);
GRANT SELECT ON ftp.users TO proftp;
INSERT INTO ftp.users ( userid, passwd ) VALUES ( 'ecarroll', 'adm1n' );
INSERT INTO ftp.users ( userid, passwd ) VALUES ( 'jgallagher', 'adm1n' );

CREATE TABLE ftp.file_log (
        pkid               serial      PRIMARY KEY,
        userid             text        REFERENCES ftp.users(userid),
        abs_path           text,
        file               text,
        dns                text,
        time_transaction   text,
        ts_in              timestamp with time zone   NOT NULL DEFAULT CURRENT_TIMESTAMP
);
GRANT INSERT ON ftp.file_log TO proftp;
GRANT UPDATE ON TABLE ftp.file_log_pkid_seq TO proftp;

데이터베이스에 연결하고 다음과 같이 실행하세요:

\i script_name

또는, 명령줄 인수로 psql에 ‘–file script_name’을 보낼 수 있습니다.

더 낮은 데이터베이스를 사용하는 경우(이 튜토리얼을 mysql과 함께 사용하는 경우), text를 varchar로 교체할 수 있습니다. 그러나 Postgres 설계 때문에 이점은 없습니다.

proftpd 설정하기

이 부분은 까다롭고 1.3을 사용하는 경우 약간 다릅니다. 다시 말하지만 이 튜토리얼은 1.2.10을 위한 것입니다.

저는 /etc/proftpd.conf에 있는 기본 proftpd 보일러플레이트 구성에 이 부분을 추가했습니다:

AuthOrder            mod_sql.c
SQLAuthTypes         Plaintext Empty
SQLAuthenticate      users
SQLConnectInfo       proftpd@localhost proftp dealermadeftp

SQLDefaultUID        110   # /etc/passwd에서 찾은 FTP 사용자 UID로 변경
SQLDefaultGID        1001  # /etc/groups에서 찾은 FTP 사용자 GID로 변경
SQLDefaultHomedir    /home/ftp
RequireValidShell    off

SQLUserInfo          ftp.users userid passwd uid gid homedir shell

SQLNegativeCache     off
SQLLogFile           /var/log/proftpd-sql
SQLLog               STOR newfile
SQLNamedQuery        newfile FREEFORM "INSERT INTO ftp.file_log(userid,abs_path,file,dns,time_transaction) VALUES ('%U','%f','%J','%V','%T')"

# %U => userid
# %D => --아무것도,
# %f => abs_path
# %J => file
# %h => dns_remote, %V => dns_local
# %a => remote_ip, %L => local_ip
# %t => localtime
# %T => transfer_time
  • 템플릿 변수는 주석 처리되어 있으며, 귀하의 참조용입니다. 구성에 대한 간단한 설명은 다음과 같습니다:
  1. 여기서 AuthOrder를 mod_sql.c로 설정하여 mod_sql을 활성화합니다.
  2. 평문 비밀번호를 사용하도록 선택했으며, 비밀번호를 지정하지 않을 경우 빈 비밀번호를 허용합니다. (나중에 데이터베이스에서 비밀번호를 SHA1로 암호화하는 트리거를 구현할 것입니다.)
  3. SQLAuthenticate를 users로 설정했습니다. 이 프로젝트에서는 그룹을 사용하지 않기로 선택했습니다. 그룹에 대한 더 많은 정보는 다음 웹사이트에서 확인할 수 있습니다: This website for docs on groups
  4. SQLConnectInfo는 “database@host user password” 형식의 DSN입니다.
  5. SQLDefaultUID, SQLDefaultGID, SQLDefaultHomedir는 기본값을 구성하며, 대부분의 열은 NULL로 남겨두고 거의 재정의하지 않을 것입니다.
  6. RequireValidShell도 꺼져 있습니다. 이를 켜고 유효하지 않은 셸을 제공하면 인증에 실패합니다.
  7. SQLUserInfo는 Postgres 테이블에 해당하며, 문자 그대로 매핑됩니다. 다시 말해 “ftp.”는 인증 테이블의 스키마를 지정합니다.
  8. SQLNegativeCache는 인증 실패를 캐시합니다.
  9. SQLLogFile은 서버를 구성하는 동안 자세한 로깅을 위해 사용됩니다. 프로덕션으로 전환할 때 비활성화할 것입니다.
  10. SQLLog는 어떤 FTP 작업을 기록할지 지정합니다. 사용자들이 업로드만 할 수 있기 때문에 STOR만 기록합니다. 형식은 ‘ftpaction query_to_execute’입니다.
  11. SQLNamedQuery는 이 쿼리에 대한 문서에서 구문을 확인하세요. 제가 사용하는 형식은 ‘qryname FREEFORM custom_query_here’입니다. 이는 proftpd의 많은 템플릿 변수를 캡처하고 싶기 때문입니다. 더 간단한 구문도 있습니다.

그게 전부입니다. 모든 것이 계획대로 진행된다면, 이제 작동해야 합니다. 누군가 파일을 업로드하면 다음과 같은 결과가 나와야 합니다:

proftpd=# select * from ftp.file_log;
 pkid |  userid  |        abs_path        |     file      |  dns  | time_transaction |             ts_in 
------+----------+------------------------+---------------+-------+------------------+-------------------------------
    1 | ecarroll | /home/ftp/foo/testfile | /foo/testfile | AMD64 | 0.000            | 2006-06-11 20:49:12.623375-05
(1 row)

2부: ‘작동’ 이상의 것

… 하지만 작동하는 것만으로는 충분하지 않죠? proftpd의 구성 FAQ에 게시된 다음 질문에 답해 보겠습니다:
업로드 후 업로드 디렉토리에서 파일을 회전할 수 있나요?

답은, 물론 가능합니다. 이를 저장 프로시저를 통해 수행할 것입니다. 그렇게 하면 파일이 올바른 방법으로 – 즉시 – 이동됩니다.

먼저 시스템에 신뢰할 수 없는 plperlu를 설치해야 합니다. 이는 다음과 같이 수행할 수 있습니다:

apt-get install postgresql-plperl-8.1

그런 다음 데이터베이스에 plperlu를 설치해야 합니다:

createlang plperlu your_database_here

다음으로, ftp_log 테이블에 각 삽입 시 작동할 간단한 perl 스크립트 트리거를 만들어야 합니다:

CREATE OR REPLACE FUNCTION ftp_file() RETURNS TRIGGER AS $$
        use warnings;
        use Cwd;
        use File::Basename;
        use File::Spec;
        use File::Copy;
        move (
                $_TD->{new}{abs_path},
                File::Spec->catfile( '/home/ftp/', basename($_TD->{new}{file}) )
        );
        return;
$$ LANGUAGE 'plperlu' VOLATILE;

CREATE TRIGGER ftp_file
        BEFORE INSERT
        ON ftp.file_log
        FOR EACH ROW
        EXECUTE PROCEDURE ftp_file()
;

이 SQL 스크립트는 이전 스크립트와 동일한 방법으로 설치할 수 있습니다. (테이블을 생성하는 데 사용된 스크립트)

Share: X/Twitter LinkedIn

새 게시물을 받은 편지함에서 받기

스팸은 없습니다. 언제든지 구독 해지 가능합니다.