8.3 클라이언트 TLS/SSL 접속
앞 절에서 본 client CA를 이용하면, 클라이언트는 비밀번호 대신 TLS client certificate로 데이터베이스에 인증할 수 있다. 비밀번호가 오가지 않으므로 유출 위험이 줄고, 인증서 폐기로 접근을 한번에 끊을 수 있다. 이 절에서는 client 인증서를 발급하고, psql로 검증하고, 애플리케이션 Pod에 인증서를 마운트해 접속하는 흐름을 실습 관점에서 정리한다.
인증 흐름
flowchart TD
KUBECTL["kubectl cnpg<br/>certificate"]
SECRET["client cert Secret<br/>tls.crt·tls.key"]
POD["애플리케이션 Pod<br/>volume mount"]
RW["cluster-rw 서비스"]
PG["PostgreSQL<br/>pg_hba: cert"]
KUBECTL -->|발급| SECRET
SECRET -->|마운트| POD
POD -->|sslmode=verify-full| RW --> PG
classDef node fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
class KUBECTL,SECRET,POD,RW,PG node
1. client 인증서 발급
kubectl cnpg 플러그인으로 특정 사용자용 client 인증서를 발급한다. Cluster의 client CA가 서명하며, 결과는 지정한 이름의 Secret으로 만들어진다.
kubectl cnpg certificate cluster-app \
--cnpg-cluster cluster-example \
--cnpg-user app발급된 인증서의 common name은 PostgreSQL 사용자명(app)과 같아야 한다. cert 인증은 인증서 CN을 데이터베이스 롤로 매핑하기 때문이다. 인증서 내용은 다음으로 확인한다.
kubectl get secret cluster-app \
-o jsonpath="{.data['tls\.crt']}" \
| base64 -d | openssl x509 -text -noout | head -n 11기본 유효기간은 90일이며 CERTIFICATE_DURATION·EXPIRE_CHECK_THRESHOLD로 조정한다.
2. psql로 접속 검증
클라이언트는 세 개의 파일이 필요하다.
| 파라미터 | 파일 | 역할 |
|---|---|---|
sslcert | tls.crt | client 공개 인증서 |
sslkey | tls.key | client 개인 키 |
sslrootcert | ca.crt | 서버 검증용 CA |
인증서를 마운트한 Pod 안에서 다음처럼 접속한다. sslmode=verify-full은 서버 인증서와 호스트명까지 검증하는 가장 엄격한 모드다.
kubectl exec -it cert-test -- bash -c \
"psql 'sslkey=/etc/secrets/app/tls.key \
sslcert=/etc/secrets/app/tls.crt \
sslrootcert=/etc/secrets/ca/ca.crt \
host=cluster-example-rw.default.svc \
dbname=app user=app sslmode=verify-full' \
-c 'select version();'"sslmode는 검증 강도를 정한다. require는 암호화만 하고 서버 신원은 확인하지 않으며, verify-ca는 CA를, verify-full은 CA와 호스트명까지 확인한다. 프로덕션에서는 verify-full을 기본으로 삼는다.3. 애플리케이션 Pod에 인증서 마운트
애플리케이션에서는 CA Secret과 client 인증서 Secret을 볼륨으로 마운트하고, 접속 문자열을 환경 변수로 넣는다. 아래는 CloudNativePG 문서의 cert-test 예제를 간추린 형태다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: cert-test
spec:
replicas: 1
selector:
matchLabels:
app: cert-test
template:
metadata:
labels:
app: cert-test
spec:
containers:
- name: cert-test
image: ghcr.io/cloudnative-pg/webtest:1.6.0
env:
- name: DATABASE_URL
value: >-
sslkey=/etc/secrets/app/tls.key
sslcert=/etc/secrets/app/tls.crt
sslrootcert=/etc/secrets/ca/ca.crt
host=cluster-example-rw.default.svc
dbname=app user=app sslmode=verify-full
volumeMounts:
- name: app-cert
mountPath: /etc/secrets/app
- name: ca-cert
mountPath: /etc/secrets/ca
volumes:
- name: app-cert
secret:
secretName: cluster-app
- name: ca-cert
secret:
secretName: cluster-example-caclient 인증서 Secret(cluster-app)에는 tls.crt·tls.key가, CA Secret(cluster-example-ca)에는 ca.crt가 들어 있어, 위 마운트 경로가 그대로 psql 파라미터와 맞물린다.
TLS 프로토콜 버전
CloudNativePG는 최소·최대 TLS 버전 모두 기본 TLSv1.3을 쓴다. 구형 클라이언트 라이브러리가 1.3을 지원하지 않으면 접속이 실패할 수 있으므로, 접속 오류가 나면 클라이언트의 TLS 지원 버전을 먼저 확인한다.
정리
kubectl cnpg certificate로 사용자별 client 인증서를 Secret으로 발급한다- 인증서 CN = PostgreSQL 사용자명.
cert인증이 CN을 롤로 매핑 - 클라이언트는
sslcert·sslkey·sslrootcert세 파일로 접속, 프로덕션은sslmode=verify-full - 애플리케이션은 CA·client Secret을 볼륨 마운트하고 접속 문자열을 주입
- 기본 TLS 버전은 1.3 — 구형 클라이언트 호환성에 주의
다음 절(8.4)에서는 인증서 없이 Secret 기반으로 애플리케이션을 연결하는 표준 방법을 본다.