PostgreSQL 18의 OAuth2 인증 미리보기 (1) – 작동 방식 탐구

작성자: Guang Yi Xu
2025년 9월 15일

**PostgreSQL 18 (RC1은 2025년 9월 5일 공개)**은 SASL OAUTHBEARER 메커니즘을 기반으로 한 네이티브 OAuth2 인증 방식을 도입했습니다. 비밀번호 대신, 클라이언트는 IdP(Identity Provider, 신원 공급자)가 발급한 베어러 토큰을 제시합니다. PostgreSQL은 플러그형 검증기 모듈을 통해 해당 토큰을 검증한 뒤, 인증된 신원을 데이터베이스 롤에 매핑하여 액세스를 허용합니다. 이 방식은 Postgres를 최신 SSO(Single Sign-On) 방식과 일치시키는 동시에, 롤 및 권한 관리는 여전히 DBA의 제어 아래 유지할 수 있게 합니다.

이 블로그 시리즈는 총 3편으로 구성되어 있습니다:

  • Part-1 (이 글): PostgreSQL 18 OAuth2 인증의 작동 방식 탐구
  • Part-2: Rust로 커스텀 검증기 작성하기
  • Part-3: PostgreSQL proto3 클라이언트 라이브러리에 OAUTHBEARER 지원 추가

필요한 것 (What you need)

OAuth2 인증을 처음부터 끝까지 활성화하려면 다음을 준비해야 합니다:

  • IdP(OAuth2/OIDC): 토큰 발급을 위해 사실상 필요하며, 서버는 최종적으로 검증기가 허용한 토큰을 신뢰합니다.
  • PostgreSQL v3 프로토콜 클라이언트: 현재 psql(libpq 기반)이 확인되었으며, 통합된 디바이스 인증 플로우를 지원합니다.
  • PostgreSQL 18: --with-openssl --with-libcurl 옵션으로 빌드되고, pg_hba.conf 및 (선택적으로) pg_ident.conf로 설정되어야 합니다.
  • 서버 측 검증기 모듈: 토큰을 검증하고 인증 결정을 반환합니다.

작동 방식 (How it works)

핸드셰이크는 SASL 교환으로 구성되며, 서버는 OAUTHBEARER를 광고합니다. 클라이언트는 즉시 토큰을 제시하거나 discovery 정보를 요청해 OAuth 플로우(예: 디바이스 코드)를 실행한 후 토큰과 함께 다시 시도할 수 있습니다. 서버는 구성된 검증기를 통해 토큰을 검증하고 인증을 완료합니다.

PostgreSQL 18 OAUTHBEARER SASL 흐름의 핵심 포인트

  • pg_hba.conf에서 oauth 메서드를 선택하면 서버는 OAUTHBEARER를 광고합니다.
  • 클라이언트는 즉시 베어러 토큰을 보낼 수도 있고, auth=""를 전송해 discovery 정보를 가져온 뒤 토큰을 획득하고 다시 시도할 수도 있습니다. (libpq는 디바이스 인증을 구현합니다.)
  • 토큰 획득 방법은 프로토콜과 무관하며, SASL은 결과만 전달합니다.

서버 설정 (Server configuration)

1) pg_hba.conf (새로운 oauth 메서드)

일반적인 규칙은 다음과 같습니다:

# TYPE  DATABASE  USER     ADDRESS    METHOD  OPTIONS...
host    all       tester   ::1/128    oauth   scope="openid profile" issuer=https://my-system.my-domain/auth validator=my_system_validator map="my_oauth_map"
  • method=oauth: 매칭되는 연결에 대해 기능 활성화
  • issuerscope: 서버가 토큰에 포함되기를 기대하는 값
  • validator: 사용할 검증기 이름
  • map: 고전적인 pg_ident.conf 매핑(사용자 ↔ 롤) 활성화

pg_ident.conf 예시는 다음과 같습니다:

# MAPNAME       SYSTEM-USERNAME         PG-USERNAME
my_oauth_map    abcdef_my_user_id       tester

2) postgresql.conf (검증기 로드)

# - Authentication -
oauth_validator_libraries = 'my_system_validator'
# comma-separated list of trusted validator modules
  • 신뢰할 수 있는 검증기 모듈을 쉼표로 구분해 나열합니다.
  • 여기에 나열된 검증기만 HBA 규칙에서 사용할 수 있습니다.

3) 아이덴티티 매핑 모드

  • pg_ident.conf 매핑 (기본): 검증기는 authn_id를 반환해야 하며, 이는 요청된 사용자 또는 pg_ident로 매핑된 롤과 정확히 일치해야 합니다. 그렇지 않으면 로그인 실패.
  • 위임 매핑 (Delegated mapping): HBA 규칙에서 delegate_ident_mapping=1을 설정합니다. PostgreSQL은 authn_id를 무시하고, 검증기가 authorized=true를 반환하면 요청된 사용자를 허용합니다. 이 방식은 토큰과 요청된 롤이 엄격히 바인딩된 경우에만 사용해야 합니다.

클라이언트 동작 (Client behavior)

psql/libpq는 두 가지 연결 매개변수가 필요하며, 누락되면 즉시 실패합니다:

  • oauth_issuer
  • oauth_client_id

libpq는 터미널 환경에서 디바이스 인증 플로우를 실행할 수 있습니다. 이 경우 verification_uriuser_code를 출력하고, 토큰이 발급될 때까지 폴링합니다.

기타 클라이언트/드라이버(jackc/pgx, launchbadge/sqlx)는 두 가지 패턴 중 하나를 채택할 수 있습니다:

  1. Full SASL OAUTHBEARER: 서버의 discovery/scope 힌트에 반응해 OAuth 플로우를 실행한 후 SASL 교환을 계속 진행
  2. Token-first: 외부에서 토큰을 미리 획득하고, 초기 SASL 메시지에 포함해 전송

👉 일반적으로 GUI 툴은 (1)을, 서비스 내 SDK/라이브러리는 (2)를 선호합니다.


검증기 모듈 (Validator module)

검증기는 Postgres와 IdP 간의 신뢰 앵커입니다.

그 역할은 다음과 같습니다:

  • 토큰 검증 (발급자, 서명/키(JWT의 경우), opaque 토큰의 경우 introspection, scope, 수명 등)
  • { authorized: true/false, authn_id: "username" } 반환
  • 위임 매핑이 활성화된 경우 ID-롤 바인딩을 강제

⚙️ 설계 참고:

  • 오프라인 JWT 검증은 지연 시간을 최소화합니다.
  • 온라인 introspection은 토큰 폐기를 단순화하지만 authentication_timeout 내에 완료되어야 합니다.
  • 자세한 내용은 공식 문서를 참고하세요.

토큰 형식 (Token format)

  • SASL OAUTHBEARER는 JWT를 요구하지 않습니다.
  • 허용된 문자 집합 내의 어떤 베어러 토큰도 프로토콜 수준에서 허용되며, 실제 검증 방식은 검증기에 따라 달라집니다.

롤과 권한 (Roles and privileges)

OAuth2 지원은 인증 방식만 변경합니다.

  • 롤 생성, 권한 부여, 회수 등은 기존 DBA 프로세스나 자동화 방식 그대로 유지됩니다.

종합하기 (Putting it Together)

  1. Postgres에 매핑할 롤을 프로비저닝합니다.
  2. IdP를 선택하고 JWT(오프라인)와 introspection(온라인) 중 방식을 결정합니다.
  3. 검증기를 구현하고 oauth_validator_libraries에 추가합니다.
  4. issuer, scope, map 또는 delegate_ident_mapping=1을 포함한 OAuth HBA 규칙을 작성합니다.
  5. 클라이언트 업데이트: psql의 경우 oauth_issueroauth_client_id를 설정하고, 필요한 경우 디바이스 플로우가 동작하는지 확인합니다.

PostgreSQL 18에서 psql은 어떻게 동작하는가?

PostgreSQL 18 서버에 OAuth2 인증 방식으로 연결하는 일반적인 psql 명령 예시는 다음과 같습니다:

psql "postgres://tester@my_pg.my-domain:5432/upm?oauth_issuer=https://my-system.my-domain/auth&oauth_client_id=my-app-client-id"

내부적으로 일어나는 과정은 아래 다이어그램으로 설명할 수 있습니다. 클라이언트는 두 가지 방식으로 인증할 수 있습니다:

  • 토큰을 처음부터 전송
  • discovery 정보를 가져와 디바이스 플로우를 실행한 뒤 계속 진행

PostgreSQL 18 OAuth2 psql 디바이스 코드 플로우

libpq의 디바이스 플로우 프롬프트(verification_uri, user_code)는 공식 libpq OAuth 섹션에 문서화되어 있습니다.


추가 읽을거리 (Further reading)

메일: salesinquiry@enterprisedb.com