Skip to content

feat(pam): connection test protocol#318

Open
x032205 wants to merge 3 commits into
mainfrom
PAM-297
Open

feat(pam): connection test protocol#318
x032205 wants to merge 3 commits into
mainfrom
PAM-297

Conversation

@x032205

@x032205 x032205 commented Jul 18, 2026

Copy link
Copy Markdown
Member

Description 📣

For postgres, ssh, windows.

SSH cert auth and Windows use a basic host reachability check via TLS probe.

Accounts without a password also use a basic TLS probe, but this case is for any future heartbeat functionality on discovered accounts w/o a password.

Type ✨

  • Bug fix
  • New feature
  • Improvement
  • Breaking change
  • Documentation

@x032205
x032205 requested a review from bernie-g July 18, 2026 01:27
@infisical-review-police

Copy link
Copy Markdown

💬 Discussion in Slack: #pr-review-cli-318-feat-pam-connection-test-protocol

Posted by Review Police — reviews, comments, new commits, and CI failures will stream into this channel.

@linear

linear Bot commented Jul 18, 2026

Copy link
Copy Markdown

PAM-297

@greptile-apps

greptile-apps Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a /v1/test-connection RPC endpoint to the gateway-v2 package, supporting Postgres (full auth + query), SSH (via the existing doSSHExec with a hardcoded true command), and TCP reachability probes. It also renames the discoveryTarget / discoveryTargetContextKey types to rpcTarget / rpcTargetContextKey to reflect their broader use.

  • Postgres path: dials pgx with caller-supplied credentials, database, and optional TLS settings; the TLS path has an insecure default (see inline comment on InsecureSkipVerify).
  • SSH path: reuses doSSHExec with command: \"true\" as a no-op probe; host key verification is bypassed via InsecureIgnoreHostKey() (pre-existing).
  • TCP path: plain dial to the cert-bound host/port, used as the fallback for Windows/RDP and SSH-cert targets.

Confidence Score: 3/5

The new handler should not be merged without fixing the Postgres TLS default — callers that enable SSL without explicitly opting into certificate verification will silently connect without verifying the server, exploitable by any MitM on the gateway-to-database network path.

The core logic is well-structured and the cert-bound target design correctly prevents SSRF from the request body. However, the InsecureSkipVerify field defaults to true whenever SSL is enabled, because SslRejectUnauthorized (a bool) defaults to false in Go. This creates a silent insecure state that any platform-side code sending sslEnabled: true without the extra field will hit in production, potentially exposing database credentials over an unverified TLS session.

packages/gateway-v2/test_connection_handler.go — specifically the TLS config block in doPostgresConnectionTest around the InsecureSkipVerify default

Security Review

  • SSRF (bounded) — probes connect to target.host:target.port from the signed gateway certificate; the request body cannot redirect to an arbitrary host, but the cert-binding must remain strict.
  • Unverified TLS default (Postgres)InsecureSkipVerify is !env.SslRejectUnauthorized; since the Go bool zero value is false, enabling SSL without explicitly setting sslRejectUnauthorized: true transmits credentials over an unverified TLS channel.
  • Verbatim error forwarding — pgx error strings including hostname, port, database, and role name are returned directly in 502 responses.

Important Files Changed

Filename Overview
packages/gateway-v2/test_connection_handler.go New handler for /v1/test-connection supporting postgres, ssh, and tcp modes; the postgres TLS path has a dangerous default where omitting sslRejectUnauthorized silently enables InsecureSkipVerify
packages/gateway-v2/discovery_handler.go Rename of discoveryTarget/discoveryTargetContextKey to rpcTarget/rpcTargetContextKey and registration of the new /v1/test-connection route; rename-only change with no functional impact
go.mod Adds pgpassfile and pgservicefile as indirect dependencies pulled in by the new pgx/v5 Postgres connection
go.sum Hash entries for the two new indirect dependencies and puddle/v2; no concerns

Reviews (1): Last reviewed commit: "connection test" | Re-trigger Greptile

Comment thread packages/gateway-v2/test_connection_handler.go Outdated
Comment thread packages/gateway-v2/test_connection_handler.go
}

if testErr != nil {
writeRPCError(w, http.StatusBadGateway, testErr.Error())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Internal error details surfaced verbatim to caller

testErr.Error() is forwarded directly in the 502 response. pgx error strings frequently include the target hostname, port, database name, role name, and the server's diagnostic text (e.g., FATAL: password authentication failed for user "admin" at host:5432). While the endpoint sits behind mTLS, surfacing this verbatim still leaks topology and credential-hint information that could be useful to an attacker who gains access to the response. Consider mapping known error categories (auth failure, unreachable, timeout, TLS) to fixed strings and logging the full error server-side only.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is fine, since these are provided by the account creator/editor anyways. It would be difficult to rewrite errors for all account and error types

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a fair point. Since the errors are returned to the same person who configured the connection in the first place, they already possess all the information being echoed back — hostname, credentials, database name — and seeing the verbatim error is genuinely useful for debugging misconfigured connections. I'll defer to your judgment here.

case testConnModePostgres:
testErr = doPostgresConnectionTest(ctx, target.host, target.port, env)
case testConnModeSSH:
_, testErr = doSSHExec(target.host, target.port, sshExecEnvelope{

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

High: SSH password can be sent to an unverified server

This passes connection-test credentials to doSSHExec, which uses ssh.InsecureIgnoreHostKey(). An on-path attacker can impersonate the certificate-bound target and receive the supplied password; require the platform to provide a trusted host key or host CA and verify it before authenticating.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way we do test is consistent with discovery. I don't think the attack surface is large especially since gateways are usually within regulated private networks.

@veria-ai

veria-ai Bot commented Jul 18, 2026

Copy link
Copy Markdown

PR overview

This pull request adds a PAM gateway connection-test protocol, including handler logic that attempts target connectivity using supplied credentials such as SSH login details.

The PR currently has one open security concern: SSH connection-test credentials may be sent before the SSH server identity is verified. An on-path attacker could impersonate the target during the test and capture the supplied password, creating a clear credential-exposure risk. No issues have been fixed or addressed yet.

Open issues (1)

Fixed/addressed: 0 · PR risk: 7/10

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant