Skip to content

feat(scripts): vault CA bootstrap helper for local dev#359

Open
BjoernSchotte wants to merge 2 commits into
BerriAI:mainfrom
BjoernSchotte:feat/vault-ca-bootstrap-script
Open

feat(scripts): vault CA bootstrap helper for local dev#359
BjoernSchotte wants to merge 2 commits into
BerriAI:mainfrom
BjoernSchotte:feat/vault-ca-bootstrap-script

Conversation

@BjoernSchotte
Copy link
Copy Markdown

Summary

vault/README.md documents vault CA setup as five manual steps:

  1. openssl genpkey with specific RSA params (WebCrypto needs PKCS#8)
  2. openssl req -x509 with three -addext flags (basicConstraints, keyUsage, …)
  3. kubectl create secret tls vault-ca
  4. Bake vault/ca.crt into harness images at build time
  5. Repeat steps 1–3 when CAs need to roll

Easy to get wrong: missing extensions silently break GnuTLS clients, wrong key format breaks vault's WebCrypto import, a stale baked-in cert silently breaks TLS verification across all sandbox traffic with no clear error.

This PR adds bin/vault-ca-bootstrap-local.sh — a single idempotent script that does steps 1–3 and reminds the operator about step 4.

Behaviour

bin/vault-ca-bootstrap-local.sh           # generate if missing, deploy secret
bin/vault-ca-bootstrap-local.sh --force   # regenerate from scratch (e.g. cluster recreate)
KUBE_CONTEXT=kind-other ./bin/...         # override target cluster
  • Local material lands in .local/vault/tls.{key,crt} (.local/ is already in .gitignore).
  • vault/ca.crt is overwritten with the freshly generated cert so the next harness build picks it up.
  • The vault-ca TLS secret is deleted + recreated on each run when the cluster is reachable, so the in-cluster material always matches what's on disk.
  • If the cluster (kind-agent-sbx by default) isn't up yet, the cluster step is gracefully skipped with a reminder.

Doc change

vault/README.md gets a "Local dev quick start" section pointing at the script. The existing manual steps stay as the canonical reference so the script never becomes the only source of truth.

Test plan

  • Fresh run on a clean checkout — generates CA, syncs cert, deploys secret
  • Re-run without --force — both reused, idempotent (no rotation)
  • Re-run with --force — fresh CA, secret rotated
  • Run before bin/kind-up.sh — graceful skip of the cluster step
  • End-to-end: bootstrap → rebuild harness → spawn sandbox → vault MITM verifies against the rotated CA

The vault sidecar needs a TLS secret 'vault-ca' in the cluster and the
matching CA cert baked into the harness images' trust store.
vault/README.md documents this as five manual steps (openssl genpkey,
openssl req with three addext flags, kubectl create secret tls, plus
image rebuild). Easy to get wrong: wrong key format breaks WebCrypto
import in vault, missing extensions break GnuTLS validation, mismatched
cert vs baked-in cert breaks TLS verify silently.

bin/vault-ca-bootstrap-local.sh collapses this into one idempotent
command. It:

  1. Generates a fresh CA into .local/vault/ if one isn't there
     (--force regenerates on demand)
  2. Syncs the cert into vault/ca.crt so the next harness build bakes
     the matching CA into the image trust store
  3. Creates (or recreates) the vault-ca TLS secret in the cluster
     specified by KUBE_CONTEXT (default kind-agent-sbx), or skips
     this step gracefully when the cluster is not yet up

Re-running is safe: existing key + cert are reused unless --force is
passed, and the cluster secret step is delete+recreate so it always
reflects the current local material.

vault/README.md gets a 'Local dev quick start' section pointing at the
script; the manual steps remain as the canonical reference.
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 28, 2026

Greptile Summary

This PR adds bin/vault-ca-bootstrap-local.sh, a single idempotent helper that automates the five-step Vault CA setup described in vault/README.md, and updates the README with a quick-start section.

  • The script generates an RSA CA with the correct PKCS#8 key format and X.509 extensions (basicConstraints, keyUsage), syncs it to vault/ca.crt for harness image builds, and conditionally deploys a vault-ca TLS secret to the kind cluster — gracefully skipping the cluster step when the cluster is not yet running.
  • Two minor hardening gaps: both openssl invocations suppress stderr via 2>/dev/null with no || err fallback, so a version mismatch (e.g., macOS LibreSSL lacking -addext) produces a silent exit; and the private key file is created with default umask permissions for a brief window before chmod 600 tightens them.

Confidence Score: 4/5

Safe to merge; the script is a local-dev convenience tool with no production impact and correct cryptographic parameters.

The OpenSSL commands suppress all stderr output without a fallback error handler, meaning a developer on macOS (where system openssl is LibreSSL and lacks -addext) will see the script silently exit with no explanation. The private key is also written with world-readable permissions for a brief moment before chmod 600 corrects them. Both are quality issues for a developer-facing script, not regressions in production code.

bin/vault-ca-bootstrap-local.sh — the silent-failure and key-permission issues are both in the CA generation block.

Important Files Changed

Filename Overview
bin/vault-ca-bootstrap-local.sh New idempotent CA-bootstrap script; correct OpenSSL params and kubectl flow, but openssl failures are silently swallowed by 2>/dev/null with no fallback error message, and the private key file is briefly world-readable before chmod 600.
vault/README.md Adds a Local dev quick start section pointing to the new script while preserving the canonical manual steps; documentation is accurate and well-structured.

Reviews (1): Last reviewed commit: "feat(scripts): vault CA bootstrap helper..." | Re-trigger Greptile

Comment thread bin/vault-ca-bootstrap-local.sh Outdated
Comment thread bin/vault-ca-bootstrap-local.sh Outdated
Comment on lines +42 to +47
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out "$KEY" 2>/dev/null
openssl req -new -x509 -key "$KEY" -out "$CRT" -sha256 -days 3650 \
-subj "/CN=vault/O=LiteLLM" \
-addext "basicConstraints=critical,CA:TRUE" \
-addext "keyUsage=critical,keyCertSign,digitalSignature" 2>/dev/null
chmod 600 "$KEY"
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 openssl genpkey creates the key file with umask-derived permissions (typically 644) before chmod 600 narrows them. On a multi-user machine the private key is briefly world-readable. Wrapping the generation in a subshell with umask 077 ensures the file is never created wider than 600.

Suggested change
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out "$KEY" 2>/dev/null
openssl req -new -x509 -key "$KEY" -out "$CRT" -sha256 -days 3650 \
-subj "/CN=vault/O=LiteLLM" \
-addext "basicConstraints=critical,CA:TRUE" \
-addext "keyUsage=critical,keyCertSign,digitalSignature" 2>/dev/null
chmod 600 "$KEY"
(umask 077; openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out "$KEY" 2>/dev/null) \
|| err "openssl genpkey failed — ensure OpenSSL ≥ 1.1.1 is installed (macOS system LibreSSL is not supported)"
openssl req -new -x509 -key "$KEY" -out "$CRT" -sha256 -days 3650 \
-subj "/CN=vault/O=LiteLLM" \
-addext "basicConstraints=critical,CA:TRUE" \
-addext "keyUsage=critical,keyCertSign,digitalSignature" 2>/dev/null \
|| err "openssl req -addext failed — ensure OpenSSL ≥ 1.1.1 is installed (macOS system LibreSSL is not supported)"
chmod 600 "$KEY"

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
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