Skip to content

Vault backend integration#958

Draft
larrywax wants to merge 4 commits intopgdogdev:mainfrom
larrywax:vault-backend-integration
Draft

Vault backend integration#958
larrywax wants to merge 4 commits intopgdogdev:mainfrom
larrywax:vault-backend-integration

Conversation

@larrywax
Copy link
Copy Markdown

@larrywax larrywax commented May 4, 2026

Draft implementation as discussed in #938

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented May 4, 2026

CLA assistant check
All committers have signed the CLA.

@larrywax larrywax mentioned this pull request May 4, 2026
Comment thread pgdog-config/src/vault.rs
/// Path to a PEM-encoded CA certificate bundle used to verify the Vault
/// server's TLS certificate. When provided, the bundle is added on top of
/// the system-wide certificate store. When omitted, only system certs are used.
pub tls_server_ca_certificate: Option<String>,
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.

nit: you can use PathBuf, it implements Deserialize.

/// Authenticate to Vault via AppRole and return a client token.
pub async fn approle_login(
client: &reqwest::Client,
addr: &str,
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.

nit: when you have so many args of the same type, the caller can get confused and pass the wrong one. Couple ways around this:

  1. Define a struct that keeps those as its own fields and make this method use &self.
  2. Combine the args into a struct, so the caller has to do something like this:
struct Args<'a> {
    addr: &'a str,
    role_id: &'a str,
}

approve_login(&Args { addr: "localhost", role_id: "1234" }).await?;

Comment thread pgdog/src/main.rs

// Fetch Vault credentials BEFORE creating pools so Address::new() picks up
// the vault-generated username/password from the very first connection.
let cfg = config::config();
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.

nit: re-use cfg below for let general =

@levkk
Copy link
Copy Markdown
Collaborator

levkk commented May 4, 2026

Really cool, thanks for implementing this! I'm still reviewing, will get back to you with more comments if any asap.

"vault: kubernetes_role is required for Kubernetes auth".into(),
)
})?;
let jwt = tokio::fs::read_to_string(self.cfg.jwt_path())
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.

nit: consider importing functions and structs, e.g.:

use tokio::fs::read_to_string;

read_to_string(&path).await?;

if cred.lease_duration == 0 {
tracing::warn!(
pool = %pool_name,
"vault: lease_duration is 0 — credentials may not be renewable; check Vault backend config"
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.

Suggested change
"vault: lease_duration is 0credentials may not be renewable; check Vault backend config"
"[vault] "lease_duration" is 0, credentials may not be renewable, check Vault backend configuration"


match client.fetch_credentials(&pools, update_config).await {
Ok(min_lease) => {
info!(pools = pools.len(), "vault: initial credentials fetched");
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.

Suggested change
info!(pools = pools.len(), "vault: initial credentials fetched");
info!(pools = pools.len(), "[vault] initial credentials fetched for {} users", users.len());

Comment thread pgdog/src/main.rs
// the vault-generated username/password from the very first connection.
let cfg = config::config();
let vault_initial_delay = if let Some(vault_cfg) = cfg.config.vault.as_ref() {
pgdog::backend::auth::vault::init(vault_cfg, &cfg.users.users).await
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.

Reloading the config (RELOAD admin command) will erase these I think. You may want to run the vault loop externally and fetch credentials from an in-memory cache instead, using the database and user as key.

@levkk
Copy link
Copy Markdown
Collaborator

levkk commented May 5, 2026

Great start. A few comments on style and one important architectural issue: the vault credentials are loaded on boot only, but pgdog config is dynamic and can be reloaded without restarting, I think the current implementation will reset the credentials when this happens.

Fixed my incorrect merge conflict fix in 884f14e6391a69b11b8da22ca074ca62e5e36190 (you should be able to cherry-pick it from #960).

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.

3 participants