Documentation
¶
Overview ¶
Credential handoff (design "skybridge-go-wire-proxy" §7 phase 3) for Postgres.
In the default proxy path the agent forwards the client->upstream auth handshake verbatim, so the native client presents a real database credential. Credential *injection* flips that: the client presents an opaque curlix session token as its password, the agent terminates that client auth locally, exchanges the token for a freshly-minted upstream credential, and then ORIGINATES its own upstream auth handshake with that credential. The client therefore never holds a credential the database would accept directly.
This file implements the two halves of that, stdlib-only:
- client-side termination: read the StartupMessage, request a cleartext password, return it as the presented secret (the agent answers AuthenticationOk only after upstream auth succeeds).
- upstream-side origination: send our own StartupMessage and complete the server's challenge, supporting AuthenticationOk (trust), AuthenticationCleartextPassword, AuthenticationMD5Password and SASL/SCRAM-SHA-256 (the Postgres 14+ default, used by modern RDS/Aurora and pgvector:pg15).
SCRAM channel binding is not used (we speak plaintext to the upstream over the trusted in-network path for now; client/upstream TLS is a later phase), so the advertised/selected mechanism is "SCRAM-SHA-256", never "SCRAM-SHA-256-PLUS".
Package postgres implements the PostgreSQL v3 frontend/backend wire protocol as a masking proxy.
Shape: the client connects to us; we dial the upstream DB. The client->server direction is forwarded verbatim (we never rewrite queries). The server->client direction is parsed message by message; RowDescription ('T') is tracked for column names/formats and DataRow ('D') values are run through the masker before being re-encoded and forwarded. Everything else (auth, errors, command completion) passes through unchanged.
Client-side SSL: when the engine is built with a TLS config the proxy *terminates* the client's SSLRequest (answers 'S' and completes a TLS handshake), so the StartupMessage — and, in the credential-injection path, the session token sent as the cleartext password — travel encrypted. Without a TLS config the proxy declines SSL ('N') as before. GSSAPI encryption is always declined. The proxy still speaks plaintext to the upstream (the agent reaches the DB over the trusted in-network path); upstream TLS is a separate later addition.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func StartUpstreamTLS ¶
StartUpstreamTLS performs the Postgres client side of upstream SSL negotiation: it sends an SSLRequest, reads the single-byte reply, and — when the server answers 'S' — upgrades conn to TLS using cfg, returning the encrypted connection. The caller then proceeds with the StartupMessage and auth over the returned conn exactly as before (so both the verbatim and credential-injection paths work unchanged over TLS).
This is what lets the agent reach managed databases that require encryption and, in particular, unblocks rds_iam injection: the RDS IAM auth token is only accepted over a TLS connection.
- 'S' → handshake with cfg and return the *tls.Conn.
- 'N' → the server does not offer SSL: error when required, otherwise return conn (plaintext).
cfg carries the verification posture (ServerName / RootCAs / InsecureSkipVerify), set by the caller per upstream host.
Types ¶
type Engine ¶
type Engine struct {
// contains filtered or unexported fields
}
Engine is the Postgres wire-proxy engine.
func New ¶
func New() *Engine
New returns a Postgres engine that declines client SSL (plaintext client link).
func NewWithClientTLS ¶
NewWithClientTLS returns a Postgres engine that terminates client TLS using cfg, so the startup handshake (and the injected-credential session token) is encrypted on the client link.
func (*Engine) ProxyInject ¶
func (e *Engine) ProxyInject(ctx context.Context, client, upstream net.Conn, masker mask.Masker, resolve wire.CredentialResolver) error
ProxyInject implements wire.InjectingEngine: credential handoff (design phase 3). Rather than forwarding the client's auth verbatim, the agent terminates the client login locally (cleartext password = an opaque curlix session token), resolves an upstream credential via resolve, and ORIGINATES its own upstream auth (trust / cleartext / md5 / SCRAM-SHA-256). The client never holds a credential the database would accept directly; after upstream auth succeeds, result rows are masked exactly as in the verbatim path.