Skip to content

feat: Hashicorp Vault static role credentials#1130

Open
larrywax wants to merge 8 commits into
pgdogdev:mainfrom
larrywax:vault-static-credentials
Open

feat: Hashicorp Vault static role credentials#1130
larrywax wants to merge 8 commits into
pgdogdev:mainfrom
larrywax:vault-static-credentials

Conversation

@larrywax

@larrywax larrywax commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Hey, it's me again 😃

Summary

  • Add Vault static role authentication, for both client-side password verification (client_vault_path) and backend PgDog-to-Postgres connections (server_auth = "vault_static" + backend_vault_path)
  • Rename server_auth = "vault""vault_dynamic" and vault_pathbackend_vault_path for clarity/symmetry with client_vault_path. Both old names still parse via #[serde(alias = ...)], no config breaking change
  • Backend static-role credentials behave like RDS IAM / Azure Workload Identity: username is operator-supplied (server_user/name), not generated by Vault, and there's no proactive percentage-based refresh
  • Prevent a tight refresh loop: Vault only issues a new static-role password once the old one's TTL truly expires and echoes back the same password (shrinking TTL) if read early. Scheduling the refresh shortly before expiry (like RDS/Azure) caused continuous refetching once TTL dropped under the buffer. Static backend credentials set their own refresh_at.
  • Extract shared Vault login/token-cache/HTTP-fetch logic into pgdog/src/auth/vault.rs, reused by both pgdog/src/backend/auth/vault.rs and pgdog/src/frontend/client/mod.rs

@larrywax larrywax force-pushed the vault-static-credentials branch from 1294ef5 to 6900dc6 Compare July 2, 2026 08:50
@codecov

codecov Bot commented Jul 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.01914% with 50 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
pgdog/src/backend/pool/token_cache.rs 63.04% 17 Missing ⚠️
pgdog/src/auth/vault.rs 97.16% 15 Missing ⚠️
pgdog-config/src/users.rs 84.41% 12 Missing ⚠️
pgdog/src/backend/pool/monitor.rs 0.00% 5 Missing ⚠️
pgdog/src/backend/pool/connection/mod.rs 0.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

Comment thread pgdog-config/src/users.rs Outdated
Comment thread pgdog-config/src/users.rs Outdated
Comment thread pgdog/src/auth/vault.rs Outdated

Ok(VaultToken {
token: auth.auth.client_token,
expires_at: SystemTime::now() + Duration::from_secs(auth.auth.lease_duration),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

You kind of want to use Instant here because its guaranteed to never be affected but clock skew.

@larrywax larrywax Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Are you sure you meant here for the vault auth token, or did you actually mean in the token cache?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Good question. So, if the expiration date is external - agreed, let's use system time or parsed time. If it's our own, e.g., we pull a duration from somewhere and add it to now(), let's use Instant because that will guarantee, in the unlikely scenario someone updates the clock, that the token cache won't blow up.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Changed the auth token and refresh_at cache field to Instant 👍
I made the expire_at field, used by RDS and Azure, optional to avoid this cumbersome logic in the vault code

expire_at: SystemTime::now() + ttl
refresh_at: Instant:now() + ttl

Let me know what you think

@levkk

levkk commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Is the static vs dynamic credentials a known Vault thing that we can point people to, e.g., in their docs? Just trying to understand if this is something you guys developed internally or is more of an "industry standard" (quotes for dramatic effect, I don't think Vault is that ubiquitous).

@larrywax

larrywax commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

Is the static vs dynamic credentials a known Vault thing that we can point people to, e.g., in their docs? Just trying to understand if this is something you guys developed internally or is more of an "industry standard" (quotes for dramatic effect, I don't think Vault is that ubiquitous).

Both are native Vault features and are documented here:

@levkk

levkk commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Sounds good. Will review asap

Comment thread pgdog-config/src/users.rs Outdated
Comment thread pgdog-config/src/users.rs Outdated
Comment thread pgdog/src/frontend/client/mod.rs Outdated
// Resolve any Vault static role entries to plaintext before starting
// the auth exchange. MD5, SCRAM, and plain all need the actual
// password, so this must happen before the first message is sent.
let resolved;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I understood what happens here, but maybe there is a cleaner way to write this? e.g., if vault, do x, else do y, or maybe move this into its own testable method?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Moved the logic in the auth::vault crate and moved the call before check_password
Should look way cleaner now

Comment thread README.md Outdated

@levkk levkk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Looks good, just a few naming convention updates and a bit of code cleanup. Happy to merge this afterwards.

We should also start running tests for this in CI (i.e. integration tests with actual vault server) if we can. Maybe as a follow up PR, just to make sure we don't break anything here going forward. This is definitely more complex than RDS IAM and friends.

@levkk

levkk commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator

Also, I just found this: https://docs.rs/vaultrs/latest/vaultrs/

Any reason to not use an existing vault client and implement our own? I get that it's just an HTTP API with JWT, so pretty simple, but still worth considering.

@larrywax

larrywax commented Jul 4, 2026

Copy link
Copy Markdown
Contributor Author

Also, I just found this: https://docs.rs/vaultrs/latest/vaultrs/

Any reason to not use an existing vault client and implement our own? I get that it's just an HTTP API with JWT, so pretty simple, but still worth considering.

Didn't want to import an entire library for just two endpoint calls, but happy to switch to it if you prefer 👍

@larrywax larrywax force-pushed the vault-static-credentials branch 2 times, most recently from 2200b1c to f9e9f7a Compare July 4, 2026 22:48
@larrywax larrywax force-pushed the vault-static-credentials branch from f9e9f7a to 4ede7bd Compare July 4, 2026 22:51
@levkk

levkk commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator

Also, I just found this: https://docs.rs/vaultrs/latest/vaultrs/
Any reason to not use an existing vault client and implement our own? I get that it's just an HTTP API with JWT, so pretty simple, but still worth considering.

Didn't want to import an entire library for just two endpoint calls, but happy to switch to it if you prefer 👍

All good. I just wanted to confirm

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.

2 participants