콘텐츠로 이동

(KO) PostgreSQL 클라이언트 프로토콜 — 섹션 개요

목차

이 서브카테고리는 PostgreSQL FE/BE 와이어 프로토콜의 백엔드 절반이다. TCP/Unix 소켓의 accept() 시점부터 완성된 SQL 명령이 파서로 진입하는 순간까지가 그 범위다. 세 가지 관심사가 있다.

  1. 와이어 프로토콜 자체 — 길이 접두사와 타입 태그를 붙이는 메시지 형식(protocol.hPqMsg_* 바이트 코드), 그 메시지를 감싸는 버퍼드 I/O(libpq/pqcomm.c), 직렬화·역직렬화 헬퍼(libpq/pqformat.c), 그리고 PostgresMain(tcop/postgres.c) 안의 정상 상태 메시지 루프. 루프는 메시지 하나를 읽어 첫 바이트로 분기하고(Q 단순 질의, P/B/E 확장 질의, X 종료) 다시 돌아온다. 이것이 프로토콜의 운영 국면이다.
  2. 연결 수립과 인증 — 루프 이전에 연결 하나가 딱 한 번 밟는 초기 경사면. 사용자와 데이터베이스를 담은 스타트업 패킷(tcop/backend_startup.c 파싱), 인증 방법을 고르는 HBA 룩업, 그리고 해당 방법을 실행하는 libpq/auth.cClientAuthentication 디스패치가 여기 있다. 현대적 기본값은 SASL 프레이밍 위의 SCRAM-SHA-256 챌린지-응답(auth-sasl.c + auth-scram.c)이다.
  3. 전송 보안 — 스타트업 패킷보다 앞서 협상되는 선택적 암호화 계층. TLS(be-secure.c + be-secure-openssl.c)와 GSSAPI 암호화(be-secure-gssapi.c)가 여기 해당하며, 각각 매직 협상 코드로 요청된다. PG17 이상에서는 ALPN으로 TLS를 직접 수립하는 경로도 열렸다.

명확한 경계. 이 섹션은 의도적으로 좁다. 파이프를 소유하지, 파이프 안을 흐르는 내용물을 소유하지 않는다.

  • 상위 경계 (질의 파이프라인 쪽): 메시지 루프의 역할은 메시지 본문을 손에 쥐는 것으로 끝난다. Q/P 텍스트를 파스 트리로 바꾸는 일, 플래닝, 실행은 query-processing (postgres-overview-query-processing.md) 영역이다. 경계는 루프에서 exec_simple_query / exec_parse_message로 넘어가는 호출 지점이다.
  • 하위/측면 경계 (서버 아키텍처 쪽): 루프를 누가 돌리는가 — postmaster가 연결마다 B_BACKENDfork()하고 BackendInitialize를 거쳐 PostgresMain에 이르는 과정 — 는 server-architecture (postgres-overview-server-architecture.md) 소유다. 이 섹션은 fork된 소켓을 받아서 인증이 성공하면 InitPostgres로 돌려준다.
  • 복제 서브프로토콜: walsender의 COPY-both 스트리밍과 START_REPLICATION 명령 집합은 이 메시지 프레이밍을 재사용하지만, replication-ha (postgres-overview-replication-ha.md)에 기록된다. 이 섹션은 일반 클라이언트 경로만 다루고 연결 지점을 명시한다.
  • 프론트엔드 libpq 클라이언트 라이브러리 (src/interfaces/libpq)는 이 프로토콜의 피어 구현이며 범위 밖이다. 이 트리는 백엔드를 분석한다. 공유 계약은 protocol.h다.

모든 연결은 아래 직선형 스택을 정확히 한 번 올라간 뒤 메시지 루프에 자리 잡는다. 다이어그램의 각 구간은 그 구간을 소유하는 모듈 문서를 명시한다.

flowchart TB
  SOCK["수락된 소켓<br/>(postmaster가 B_BACKEND를 fork함)<br/>— server-architecture 소유"]

  subgraph TLSGSS["postgres-tls-gssapi.md"]
    NEG["협상 요청 패킷<br/>SSLRequest / GSSENCRequest<br/>(ProcessSSLStartup)"]
    ENC["암호화 계층 수립<br/>be_tls_open_server / secure_open_gssapi<br/>이후 모든 I/O를 secure_read / secure_write로 래핑"]
  end

  subgraph WIRE1["postgres-wire-protocol.md (수립 단계)"]
    SUP["스타트업 패킷<br/>ProcessStartupPacket<br/>(사용자, 데이터베이스, GUC)"]
  end

  subgraph AUTH["postgres-authentication.md"]
    HBA["HBA 룩업으로 인증 방법 결정<br/>(hba.c)"]
    CA["ClientAuthentication 디스패치<br/>(auth.c)"]
    SCRAM["SASL / SCRAM-SHA-256 교환<br/>CheckSASLAuth -> pg_be_scram_mech<br/>(auth-sasl.c, auth-scram.c)"]
  end

  HANDOFF["인증 성공<br/>-> InitPostgres / 백엔드 스타트업<br/>— server-architecture 소유"]

  subgraph WIRE2["postgres-wire-protocol.md (정상 상태)"]
    LOOP["PostgresMain 메시지 루프<br/>ReadCommand -> 첫 바이트로 분기<br/>Q 단순 / P,B,E 확장 / X 종료"]
    FRAME["프레이밍 + 직렬화·역직렬화<br/>pqcomm.c (버퍼드 I/O)<br/>pqformat.c (PqMsg_* 코드)"]
  end

  PIPE["파싱된 메시지가 파서/실행기로 진입<br/>— query-processing 소유"]

  SOCK --> NEG
  NEG --> ENC
  ENC --> SUP
  NEG -. "암호화 미요청" .-> SUP
  SUP --> HBA
  HBA --> CA
  CA --> SCRAM
  SCRAM --> HANDOFF
  HANDOFF --> LOOP
  LOOP <--> FRAME
  LOOP --> PIPE

다이어그램이 담고 있는 구조적 사실 두 가지를 짚어 두자.

  • 암호화는 스타트업 패킷보다 먼저 협상된다. TLS를 원하는 클라이언트는 SSLRequest(메시지 타입 바이트 없이 길이+매직 코드만 담은 특수 패킷)를 보내고 S/N 응답 한 바이트를 기다린다. 그 뒤에야 실제 스타트업 패킷이 암호화된 채널 안으로 흐른다. GSSAPI 암호화도 GSSENCRequest로 같은 방식으로 동작한다. PG17에서 추가된 다이렉트 TLS(ALPN)는 요청 바이트를 건너뛴다. backend_startup.c에서 ProcessSSLStartupProcessStartupPacket보다 앞에 실행되는 이유가 바로 이것이다.
  • 클라이언트에서 서버로 오는 모든 인증 응답은 동일한 첫 바이트 'p'(PqMsg_PasswordMessage)를 쓴다. 평문 패스워드, SASL 초기 응답, GSS 토큰이 모두 같은 코드를 쓰는데(protocol.hPqMsg_PasswordMessage, PqMsg_SASLInitialResponse, PqMsg_SASLResponse, PqMsg_GSSResponse를 동일 코드로 정의한다), 바이트가 아니라 인증 상태 기계가 어떤 응답인지 구분한다.

프로토콜 운영 국면을 먼저 읽고 초기 경사면을 나중에 읽는다. 인증과 TLS를 설명할 때 메시지 프레이밍 어휘를 전제하기 때문이다.

  1. postgres-wire-protocol.md — 여기서 시작한다. 메시지 형식, PqMsg_* 코드, pqcomm/pqformat, PostgresMain 루프가 나머지 두 문서의 공통 어휘다. 스타트업 패킷도 이 문서 소유이므로 수립-루프 전환을 끝까지 볼 수 있다.
  2. postgres-authentication.md — 스타트업 패킷과 루프 사이에 자리한 인증 경사면. HBA 방법 선택, ClientAuthentication 디스패치, SCRAM/SASL 챌린지-응답 교환(현대적 기본값), crypt.c 검증자 처리.
  3. postgres-tls-gssapi.md — 마지막으로 그 아래에 있는 전송 계층. TLS/GSS 협상이 스타트업 패킷 이전의 교환이라는 점을 이해한 뒤 읽고, GSSAPI가 암호화와 인증 사이 경계를 흐리는 이유는 인증 문서를 읽은 뒤에 살펴본다.

전방 참조 — 아래 모듈 문서들은 계획 단계에 있으며, 요약은 예측적이다.

모듈 문서한 줄 범위
postgres-wire-protocol.mdFE/BE 메시지 형식(protocol.h PqMsg_* 코드), pqcomm.c의 버퍼드 프레이밍, pqformat.c의 직렬화·역직렬화, 스타트업 패킷(backend_startup.c), tcop/postgres.cPostgresMain 단순-확장 질의 메시지 루프.
postgres-authentication.mdauth.c의 HBA 주도 ClientAuthentication 디스패치, auth-sasl.c의 SASL 프레이밍(pg_be_sasl_mech)이 담는 SCRAM-SHA-256 교환(auth-scram.c), 패스워드 검증자 처리(crypt.c) — 채널 바인딩의 TLS 연결 포함.
postgres-tls-gssapi.md스타트업 이전 암호화 협상과 secure_read/secure_write 추상화. OpenSSL 경유 TLS(be-secure.c, be-secure-openssl.c, README.SSLSSLRequest/다이렉트 ALPN 경로)와 GSSAPI 암호화(be-secure-gssapi.c, GSSENCRequest).
  • postgres-overview-server-architecture.md — 양쪽 끝에서 직접 맞닿는 이웃이다. B_BACKEND를 만들어 내는 postmaster fork, BackendInitialize, 백엔드 수명 주기 안에서 PostgresMain의 위치를 소유한다. 이 섹션은 fork된 소켓을 빌려와서 인증된 세션을 InitPostgres로 돌려준다. 경계는 “메시지 루프 이전 vs 메시지 루프 안”이다.
  • postgres-overview-query-processing.md — 메시지 루프가 먹여 살리는 섹션이다. ReadCommandQ 또는 P/B/E 메시지 본문을 반환하는 시점에 제어가 파싱→분석→재작성→플래닝→실행으로 넘어간다. 그 호출이 이 섹션의 상단 경계다.
  • postgres-overview-replication-ha.md — 스트리밍 복제 서브프로토콜(walsender COPY-both, START_REPLICATION)에서 이 메시지 프레이밍을 그대로 재사용한다. 와이어 형식은 공유되지만, 명령 어휘와 walsender 루프는 그쪽에 있다.
  • postgres-overview-base-infra.md — 프로토콜 계층이 세워지는 기반을 공급한다. pqformat의 뼈대인 StringInfo 버퍼, 메모리 컨텍스트, 그리고 와이어의 ErrorResponse(E) 메시지로 올라오는 elog/ereport 오류 보고가 여기 있다.