본문으로 건너뛰기
12.4 보안 강화

12.4 보안 강화

Patroni 클러스터의 공격 표면은 PostgreSQL 하나가 아니다. 노드마다 REST API가 열려 있고, 모든 노드가 DCS와 통신하며, Patroni 자신이 여러 데이터베이스 계정의 비밀번호를 파일로 들고 다닌다. REST API 쪽은 8.4에서 상세히 다뤘으므로, 이 절은 인증 계정 분리에서 시작해 DCS 통신 보호까지 클러스터 전체를 한 바퀴 도는 강화 순서로 정리한다.

인증 계정 3종 분리

postgresql.authentication 아래에 Patroni가 쓰는 계정 세 개를 나눠 정의한다.

계정용도
superuserPatroni가 로컬 인스턴스를 관리할 때 쓰는 접속 계정. initdb bootstrap 시 생성
replicationstreaming replication 접속 전용. pg_hba에 replication 라인 필요
rewindpg_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-caverify-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-password

REST 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.key

DCS 통신 보호

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.key

etcd3 통신은 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-fullsslrootcert를 배포한다.

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_tokensMinimal 이하로 줄이고, 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에는 비밀번호가 평문으로 담기므로 파일 권한 관리까지가 강화의 마지막 단계다.