Skip to content

Support AWS IAM authentication for PostgreSQL connections (RDS) #58

Description

@aesslinger

Summary

Issue tabularis#694 asks for AWS IAM authentication support for PostgreSQL connections (MySQL already has it via use_iam_auth). This plugin has no IAM auth support at all — confirmed by grepping src/models.rs and src/client.rs for iam, which returns nothing.

The builtin driver's MySQL implementation establishes the pattern this plugin would need to mirror for Postgres:

  • ConnectionParams.use_iam_auth: Option<bool> (src-tauri/src/models.rs:219)
  • build_mysql_options in src-tauri/src/pool_manager.rs:
    • Refuses IAM auth over Disabled SSL (pool_manager.rs:222-229)
    • Force-upgrades PreferredRequired so the pre-signed token can never fall back to a plaintext handshake (pool_manager.rs:235-237)
    • Auto-escalates Required/Preferred + user-supplied ssl_ca to VerifyCa (pool_manager.rs:211-216)
    • Rejects an empty password/token unconditionally (pool_manager.rs:243-250)
  • require_iam_token in src-tauri/src/commands.rs:217 — guards test_connection/list_databases so an empty token surfaces an actionable error instead of an opaque "Access denied"
  • find_connection_by_id/duplicate_connection skip the OS keychain for IAM connections (commands.rs:626-630, commands.rs:1312-1315) since RDS tokens are 15-minute pre-signed and must come from the form on every connect, never a cached password
  • build_connection_key's pool cache key includes the IAM flag so IAM and non-IAM connections to the same host never share a pool (pool_manager.rs:104-116)
  • Frontend gates the "Use AWS IAM Authentication (RDS)" checkbox to an enforced TLS mode (src/components/modals/NewConnectionModal.tsx:3177-3227)

For PostgreSQL, AWS RDS IAM auth works differently at the wire level than MySQL's mysql_clear_password plugin negotiation, but the net effect is the same: the DB user must have the rds_iam role granted, RDS then forces the standard PostgreSQL cleartext-password auth method for that user, and the "password" sent over the wire is actually a short-lived (15 min) pre-signed token from aws rds generate-db-auth-token. tokio_postgres already handles AuthenticationCleartextPassword (tokio-postgres/src/connect_raw.rs:173) with no changes needed — this is purely a "send the token as password, over an encrypted link" feature, not a new auth mechanism to implement.

Security note: why "cleartext password" is acceptable here

The wire-level mechanism above ("RDS forces cleartext-password auth") sounds alarming out of context, so this was verified against primary sources rather than left as an assertion:

  • PostgreSQL's own docs for the password auth method are explicit: it "sends the password in clear-text... If the connection is protected by SSL encryption then password can be used safely" (auth-password.html). This is different from md5/scram-sha-256, which are challenge-response and never put the password/token on the wire at all.
  • pgjdbc's ConnectionFactoryImpl confirms there's no hashing step for this auth type — on AUTH_REQ_PASSWORD it sends the value straight back via a plain PASSWORD_REQUEST message.
  • AWS's IAM auth token is exactly that plain string (a ~1KB pre-signed SigV4 token, from aws rds generate-db-auth-token), validated server-side against IAM instead of a stored password hash. As far as the wire protocol is concerned it's an ordinary cleartext password.
  • AWS's docs state network traffic for IAM-authenticated connections is (and must be) SSL/TLS-encrypted, and a live example — rust-postgres#1108 — shows a user's IAM-auth connection failing under NoTls and succeeding once they switched to a real TLS implementation.

So this is the same tradeoff PostgreSQL's docs describe as safe: cleartext auth is fine specifically when TLS is enforced, since TLS — not the auth method — is what protects the token in transit. The 15-minute token lifetime bounds the damage if it did leak. This is why the proposed approach below requires an enforced TLS mode (require/verify-ca/verify-full) whenever use_iam_auth is true and rejects disable/unset outright — mirroring the identical safeguard the MySQL builtin driver already applies for its own cleartext-plugin exposure (pool_manager.rs:222-229).

Proposed approach

  1. src/models.rs: add use_iam_auth: Option<bool> to ConnectionParams, parsed via get_str/a bool equivalent in from_value (mirror the builtin's models.rs:219 field and its #[serde(default)] intent, adapted to this file's manual Value parsing style).
  2. src/client.rs (build_pool):
    • When use_iam_auth is true, require an enforced TLS mode (require/verify-ca/verify-full) — reject disable/prefer/unset with an actionable error, mirroring build_mysql_options's refusal (pool_manager.rs:222-229). Postgres has no Preferred-style opportunistic-then-silent-downgrade auto-escalation to worry about the same way MySQL does, but the "must be enforced TLS" rule is identical in spirit.
    • Reject an empty password when use_iam_auth is true, with a message pointing at aws rds generate-db-auth-token (mirror pool_manager.rs:243-250).
    • Fold use_iam_auth into connection_key (client.rs:223-236) alongside the existing TLS params, so IAM and non-IAM pools to the same host:port:database:user never collide — same rationale as the builtin's build_connection_key (pool_manager.rs:111-115).
  3. Host-side (tabularis repo, out of scope for this plugin but noted for cross-repo coordination): the require_iam_token guard in commands.rs:217 and the keychain-skip logic in commands.rs:626-630/commands.rs:1312-1315 are host-side and already driver-agnostic (they check params.use_iam_auth directly, not params.driver) — they should start working for Postgres connections automatically once this plugin declares/accepts the field, provided the frontend checkbox at NewConnectionModal.tsx:3177 is also updated to render for driver === "postgres" (it's currently hard-gated to driver === "mysql" only). That frontend change and any manifest/capability declaration this plugin needs to advertise (use_iam_auth support) belongs to a follow-up PR/issue in tabularis itself, filed once this plugin implements the field.
  4. Tests: unit tests in src/client_tests.rs covering:
    • IAM + disable/unset TLS → rejected with the actionable error (no live server needed, mirrors the TDD pattern in CLAUDE.md's "TDD for parity bugs without a live database" — this is a pure config-validation branch in build_pool/a small extracted helper, testable without a live server).
    • IAM + empty password → rejected.
    • IAM + require + non-empty password → does not hit the TLS/password guards (can't assert full pool success without a real server, but can assert the guard functions return Ok).
    • connection_key differs for IAM vs non-IAM given otherwise-identical params.

Why the plugin repo, not tabularis

Per tabularis#694's discussion, @aesslinger proposed migrating this request here since the Postgres driver now lives in the plugin, not the builtin — no further work is planned on a builtin Postgres implementation.

Activity

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

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions