12.4 보안 강화
Patroni 클러스터의 공격 표면은 PostgreSQL 하나가 아니다. 노드마다 REST API가 열려 있고, 모든 노드가 DCS와 통신하며, Patroni 자신이 여러 데이터베이스 계정의 비밀번호를 파일로 들고 다닌다. REST API 쪽은 8.4에서 상세히 다뤘으므로, 이 절은 인증 계정 분리에서 시작해 DCS 통신 보호까지 클러스터 전체를 한 바퀴 도는 강화 순서로 정리한다.
인증 계정 3종 분리
postgresql.authentication 아래에 Patroni가 쓰는 계정 세 개를 나눠 정의한다.
| 계정 | 용도 |
|---|---|
superuser | Patroni가 로컬 인스턴스를 관리할 때 쓰는 접속 계정. initdb bootstrap 시 생성 |
replication | streaming replication 접속 전용. pg_hba에 replication 라인 필요 |
rewind | pg_rewind 전용 (PostgreSQL 11+). 초기화 시 생성되며 pg_rewind에 필요한 함수의 EXECUTE 권한이 자동 부여됨 |
rewind 계정을 따로 두는 이유는 권한 축소다. 이 계정은 pg_rewind에 필요한 함수의 EXECUTE 권한만 자동으로 부여받으므로, rewind 경로에 superuser 자격증명을 쓰지 않아도 된다. replication 계정도 마찬가지로, 복제 스트림에 superuser를 쓰지 않기 위한 분리다.
세 계정 모두 username과 password 외에 libpq 접속 옵션을 함께 받는다. sslmode(기본 prefer), sslcert, sslkey, sslpassword, sslrootcert, sslcrl, sslcrldir, sslnegotiation, gssencmode, channel_binding이다. 기본값 prefer는 서버가 TLS를 지원하지 않으면 평문으로 물러서므로, 노드 간 트래픽이 신뢰할 수 없는 구간을 지난다면 verify-ca나 verify-full로 올리고 sslrootcert를 배포하는 판단이 필요하다.
postgresql:
authentication:
superuser:
username: postgres
password: su-strong-password
sslmode: verify-full
sslrootcert: /etc/patroni/tls/ca.crt
replication:
username: replicator
password: repl-strong-password
sslmode: verify-full
sslrootcert: /etc/patroni/tls/ca.crt
rewind:
username: rewind_user
password: rewind-strong-passwordREST API와 patronictl
8.4의 결론을 강화 관점에서 요약하면 세 층이다. Basic auth(restapi.authentication)와 allowlist/allowlist_include_members는 unsafe 엔드포인트(PUT/POST/PATCH/DELETE)만 보호한다. 조회 계열인 safe 엔드포인트까지 지키는 방법은 공식 문서가 명시한 대로 TLS를 켜고 verify_client: required로 모든 호출에 클라이언트 인증서를 요구하는 조합뿐이다. 그 대가로 health check를 보내는 로드밸런서와 모니터링 수집기 전부가 클라이언트 인증서를 갖춰야 한다.
서버를 mTLS로 잠갔으면 patronictl도 클라이언트 인증서가 필요하다. ctl 섹션의 certfile/keyfile이 그 자리이고, 서버 인증서 검증용 CA는 cacert로 지정한다(미지정 시 restapi.cafile을 fallback으로 쓴다). verify_client: required를 쓰면서 ctl.certfile/ctl.keyfile을 빠뜨리면 patronictl 자신이 API에 접근하지 못하므로, 공식 문서도 이 둘을 필수로 경고한다. insecure: true로 검증을 끄는 대신 내부 CA를 cacert로 배포하는 쪽이 맞다.
restapi:
certfile: /etc/patroni/tls/server.crt
keyfile: /etc/patroni/tls/server.key
cafile: /etc/patroni/tls/ca.crt
verify_client: required
ctl:
cacert: /etc/patroni/tls/ca.crt
certfile: /etc/patroni/tls/client.crt
keyfile: /etc/patroni/tls/client.keyDCS 통신 보호
DCS에는 클러스터 토폴로지, 설정, leader lock이 전부 들어 있다. DCS에 쓰기 권한이 있으면 leader key를 조작하는 것과 같으므로, 백엔드별 인증과 암호화를 챙긴다.
etcd/etcd3 섹션은 username/password 인증과 TLS 파일 3종 cacert, cert, key를 받는다. protocol: https로 전송 구간을 암호화하고, 클라이언트 인증서(cert/key)까지 쓰면 mTLS가 된다.
etcd3:
hosts: 10.0.0.21:2379,10.0.0.22:2379,10.0.0.23:2379
protocol: https
username: patroni
password: etcd-strong-password
cacert: /etc/patroni/tls/etcd-ca.crt
cert: /etc/patroni/tls/etcd-client.crt
key: /etc/patroni/tls/etcd-client.keyetcd3 통신은 gRPC-gateway를 경유하므로 TLS common name 기반 인증은 쓰지 못한다는 제약이 문서에 있다.
노출 정보 축소
restapi.server_tokens는 REST API 응답의 Server 헤더에 실리는 버전 정보를 제어한다. 기본 Original은 Python/BaseHTTP 버전까지 드러내므로, 외부에 노출되는 환경이라면 Minimal(Patroni 버전만)이나 ProductOnly(제품명만)로 줄인다.
postgresql.pgpass로 지정한 경로에는 Patroni가 pg_basebackup이나 post_init 같은 하위 명령 실행 전에 pgpass 파일을 생성한다. 이 파일에는 위에서 정의한 계정들의 비밀번호가 평문으로 들어가므로 두는 위치가 곧 보안이다. libpq는 group/other에 읽기가 열린 password 파일을 무시하므로 권한은 0600 수준으로 유지되어야 하고, 디렉토리 자체도 Patroni 실행 유저만 접근하는 곳으로 잡는다. patroni.yml 파일 역시 password들이 담기므로 같은 기준으로 권한을 관리한다.
강화 체크 순서
계정 분리와 전송 암호화
superuser, replication, rewind 세 계정을 분리하고 강한 비밀번호를 부여한다. 노드 간 구간이 신뢰 경계를 넘으면 sslmode: verify-full과 sslrootcert를 배포한다.
REST API 잠그기
certfile/keyfile로 TLS를 켜고, unsafe만 지킬지 safe까지 지킬지 결정한다. unsafe만이면 Basic auth + allowlist, safe까지면 cafile + verify_client: required의 mTLS다.
patronictl 클라이언트 맞추기
ctl.cacert로 서버 검증을, mTLS라면 ctl.certfile/ctl.keyfile로 클라이언트 인증서를 배선한다. insecure는 쓰지 않는다.
DCS 인증과 최소 권한
etcd는 username/password + TLS 3종, Consul은 최소 ACL의 token + https, ZooKeeper는 auth_data와 set_acls를 구성한다. Patroni 계정에 DCS 전역 권한을 주지 않는다.
노출과 파일 권한 마무리
server_tokens를 Minimal 이하로 줄이고, pgpass 경로와 patroni.yml의 파일 권한을 Patroni 실행 유저 전용으로 확인한다.
정리
- 인증 계정은 superuser, replication, rewind 3종으로 분리하며, 각 계정에
sslmode등 libpq 옵션을 붙여 노드 간 전송을 암호화한다. - REST API의 safe 엔드포인트를 지키는 수단은 TLS +
verify_client: required조합뿐이고, 그 경우 patronictl을 포함한 모든 클라이언트가 인증서를 갖춰야 한다. - DCS는 백엔드별 인증(etcd username/password, Consul token, ZooKeeper auth_data)과 TLS, 그리고 scope 범위로 좁힌 최소 권한을 함께 적용한다.
- pgpass와
patroni.yml에는 비밀번호가 평문으로 담기므로 파일 권한 관리까지가 강화의 마지막 단계다.