PostgreSQL은 강력한 기능을 제공하지만, 기본 설치 상태에서는 보안 설정이 미흡합니다. 프로덕션 서버에서 반드시 적용해야 할 설정들을 정리합니다.

1. postgres 계정 비밀번호 설정

PostgreSQL 설치 직후 postgres 수퍼유저 계정에는 비밀번호가 없습니다:

# postgres 유닉스 계정으로 전환
sudo -u postgres psql

# PostgreSQL 프롬프트에서 비밀번호 설정
ALTER USER postgres WITH PASSWORD '강력한비밀번호';
\q

2. 전용 데이터베이스 사용자 생성

애플리케이션에 postgres 수퍼유저를 사용하면 침해 시 전체 DB가 위험합니다:

-- 읽기/쓰기 전용 사용자 생성
CREATE USER appuser WITH PASSWORD '앱비밀번호';

-- 특정 데이터베이스에만 권한 부여
CREATE DATABASE myapp OWNER appuser;
GRANT CONNECT ON DATABASE myapp TO appuser;
GRANT USAGE ON SCHEMA public TO appuser;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO appuser;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO appuser;

-- 향후 생성될 테이블에도 자동 권한 부여
ALTER DEFAULT PRIVILEGES IN SCHEMA public
    GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO appuser;

3. 원격 접속 제어 (pg_hba.conf)

/etc/postgresql/*/main/pg_hba.conf 또는 /var/lib/pgsql/data/pg_hba.conf:

# TYPE  DATABASE  USER      ADDRESS         METHOD

# 로컬 소켓 접속 (OS 인증)
local   all       postgres                  peer
local   all       all                       md5

# 특정 IP에서만 원격 접속 허용
host    myapp     appuser   203.0.113.10/32  scram-sha-256

# 전체 허용 (보안상 비권장)
# host  all       all       0.0.0.0/0        md5

scram-sha-256은 PostgreSQL 10 이상에서 권장되는 인증 방식입니다. md5보다 안전합니다.

4. 네트워크 바인딩 설정

/etc/postgresql/*/main/postgresql.conf:

# 로컬만 허용 (기본값, 권장)
listen_addresses = 'localhost'

# 특정 IP에서도 허용할 경우
# listen_addresses = 'localhost,203.0.113.10'

# 모든 인터페이스 (비권장, 방화벽과 함께 사용 시에만)
# listen_addresses = '*'
# 설정 적용
sudo systemctl restart postgresql

5. 방화벽에서 PostgreSQL 포트 제한

# PostgreSQL 기본 포트 (5432)를 특정 IP에서만 허용
sudo ufw allow from 203.0.113.10 to any port 5432

# 또는 iptables
sudo iptables -A INPUT -p tcp --dport 5432 -s 203.0.113.10 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 5432 -j DROP

6. SSL/TLS 암호화 연결

# postgresql.conf
ssl = on
ssl_cert_file = 'server.crt'
ssl_key_file = 'server.key'
# 자체 서명 인증서 생성
sudo openssl req -new -x509 -days 365 -nodes \
    -out /etc/ssl/certs/postgresql.crt \
    -keyout /etc/ssl/private/postgresql.key

sudo chown postgres:postgres /etc/ssl/private/postgresql.key
sudo chmod 600 /etc/ssl/private/postgresql.key
# pg_hba.conf에서 SSL 강제
hostssl  myapp  appuser  0.0.0.0/0  scram-sha-256

7. 불필요한 언어 및 확장 비활성화

-- 설치된 확장 목록 확인
\dx

-- 불필요한 확장 제거 예시
DROP EXTENSION IF EXISTS plpython3u;
DROP EXTENSION IF EXISTS plperlu;

-- 신뢰할 수 없는 언어 목록 확인
SELECT lanname, lanpltrusted FROM pg_language;

8. 접속 로그 활성화

# postgresql.conf
logging_collector = on
log_directory = 'pg_log'
log_filename = 'postgresql-%Y-%m-%d.log'
log_rotation_age = 1d
log_rotation_size = 100MB

# 로그에 포함할 내용
log_connections = on
log_disconnections = on
log_failed_authentications = on
log_duration = off          # 모든 쿼리 시간 기록 (느린 서버 주의)
log_min_duration_statement = 1000  # 1초 이상 걸리는 쿼리 기록

9. 보안 설정 점검 쿼리

-- 비밀번호 없는 사용자 확인
SELECT usename, passwd IS NULL AS no_password
FROM pg_shadow
WHERE passwd IS NULL OR passwd = 'md5' || md5('');

-- 수퍼유저 목록
SELECT usename FROM pg_user WHERE usesuper = true;

-- 데이터베이스별 권한 확인
\l

-- 현재 접속 목록
SELECT pid, usename, application_name, client_addr, state
FROM pg_stat_activity;

10. 정기 백업 설정

# 데이터베이스 백업 스크립트
cat > /opt/pg_backup.sh << 'EOF'
#!/bin/bash
BACKUP_DIR="/backup/postgresql"
DATE=$(date +%Y%m%d)

mkdir -p $BACKUP_DIR
sudo -u postgres pg_dumpall | gzip > $BACKUP_DIR/pg_all_$DATE.sql.gz

# 7일 이상 된 백업 삭제
find $BACKUP_DIR -name "*.sql.gz" -mtime +7 -delete
EOF

chmod +x /opt/pg_backup.sh

# cron에 등록 (매일 새벽 3시)
echo "0 3 * * * root /opt/pg_backup.sh" | sudo tee /etc/cron.d/pg-backup

MySQL/MariaDB와 마찬가지로 PostgreSQL도 초기 보안 설정이 중요합니다. 특히 pg_hba.conf의 접근 제어와 전용 사용자 계정 생성은 기본 중의 기본입니다.