The fundamental rule is to leave no secrets behind in repositories or runners, and to consume short-lived tokens and dynamic secrets on the fly. Vault fits CI/CD naturally and handles authentication, retrieval, and auditing end to end.
This article focuses on stable patterns grounded in the official documentation. We prioritize designs that hold up across providers and versions, and the points most often probed on operations and certification exams.
CI runners authenticate to Vault with short-lived credentials, fetch only the minimum required secrets on demand, and consume them in place. Never persist secrets in repositories, artifacts, or logs. When you need to hand off between stages, use response wrapping and keep the wrap token TTL short.
Separate retrieval paths and permissions between build (dependency fetch, container build) and deploy (Kubernetes/Helm, etc.). Build is mostly read; deploy injects into environments. Split their policies and enforce least privilege on each.
Data flow: CI → Vault → Secret Engine → runtime environment
Hand off between stages with response wrapping (for example, wrap KV data for 5 minutes)
vault write -field=wrapping_token -wrap-ttl=5m sys/wrapping/wrap \
secret=@<(vault kv get -format=json kv/build/npmrc | jq -c '.data.data')
# 受け取り側ステージ(ラップ解除は 1 回のみ有効)
vault write sys/wrapping/unwrap wrapping_token=$WRAPPING_TOKEN > unwrapped.json
cat unwrapped.json | jq '.'Pick the entry point from CI into Vault based on operability and trust boundaries. For SaaS CI (GitHub Actions and similar), OIDC(JWT) is the first choice. AppRole fits self-hosted runners, and Kubernetes auth is natural for Pods inside the cluster.
In every case, define least-privilege policies and keep token TTL and max_ttl short. Federation via OIDC removes the need for any long-lived shared secret, which also helps auditing.
| Pattern | Primary use case | Strengths | Watch-outs |
|---|---|---|---|
| OIDC (JWT) | SaaS CI (GitHub Actions, GitLab) | No long-lived shared key; easy to audit | Hinges on IdP claim design and role constraints |
| AppRole | Self-hosted runners / on-prem | Simple, with few dependencies | Requires careful SecretID protection and wrapped distribution |
| Kubernetes | Pods (Injector / CSI / Agent) | Per-Pod least privilege with automatic rotation | Managing the scope of ServiceAccount/JWT |
Log in to Vault via GitHub Actions OIDC and fetch KV
jobs:
build:
permissions:
id-token: write # OIDC 発行に必要
contents: read
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Get OIDC token
id: oidc
run: echo "token=$(curl -H 'Authorization: bearer $ACTIONS_ID_TOKEN' 2>/dev/null)" >> $GITHUB_OUTPUT
env:
ACTIONS_ID_TOKEN: ${{ steps.generate_token.outputs.token }}
- name: Vault Login (JWT)
env:
VAULT_ADDR: https://vault.example.com
JWT: ${{ steps.oidc.outputs.token }}
run: |
vault write -format=json auth/jwt/login role=gha-build jwt="$JWT" > login.json
export VAULT_TOKEN=$(jq -r .auth.client_token login.json)
vault kv get -format=json kv/build/npmrc | jq -r .data.data.authToken >> $GITHUB_ENVEmit npm/pip/maven credentials to a temporary file and delete them when the job ends. For container builds, use Docker BuildKit secrets so nothing lands in the Dockerfile in plaintext.
CI tokens should be non-renewable with a short TTL. Account for retries, but keep max_ttl tight so leaks via build caches or re-runs are unlikely.
Example of safely passing Vault-fetched secrets through BuildKit
# 1) CI で Vault から取得しファイル化(短命)
vault kv get -format=json kv/build/npmrc | jq -r .data.data.npmrc > .npmrc.tmp
# 2) BuildKit 経由で Dockerfile に渡す(イメージには残らない)
DOCKER_BUILDKIT=1 docker build \
--secret id=npmrc,src=.npmrc.tmp \
-t app:build .
# Dockerfile (抜粋)
# syntax=docker/dockerfile:1.6
RUN --mount=type=secret,id=npmrc,dst=/root/.npmrc \
npm ci && rm -f /root/.npmrc
# 3) 後始末
shred -u .npmrc.tmpOn Kubernetes, use the Vault Agent Injector (sidecar) or the CSI Driver to deliver secrets as files inside the Pod. Nothing lands in etcd in plaintext, which is safer than relying on Kubernetes Secrets alone.
Tie a ServiceAccount to a role through the Kubernetes auth method to enforce per-Pod least privilege. Let the Agent rotate credentials automatically and design the app to reload them via file watching — that is the practical pattern.
Deployment example using Vault Agent Injector (template rendering)
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "k8s-web"
vault.hashicorp.com/agent-inject-secret-config.yaml: "kv/app/config"
vault.hashicorp.com/agent-inject-template-config.yaml: |
{{- with secret "kv/app/config" -}}
DB_URL={{ .Data.data.DB_URL }}
API_KEY={{ .Data.data.API_KEY }}
{{- end -}}
spec:
replicas: 2
selector:
matchLabels: { app: web }
template:
metadata:
labels: { app: web }
spec:
serviceAccountName: web-sa
containers:
- name: app
image: ghcr.io/acme/web:1.0
envFrom:
- secretRef: { name: dummy } # 実際の値はファイルから読み込む
volumeMounts:
- name: config
mountPath: /run/secrets
volumes:
- name: config
emptyDir: {}Vault offers service tokens (renewable) and batch tokens (lightweight, non-renewable); batch tokens fit CI's one-shot use perfectly. Keep TTL and max_ttl short, and use periodic tokens when planned renewal is necessary.
Dynamic secrets are managed through leases — revoke them explicitly at job end or let them expire by TTL. Always enable an audit device so you have a record of who accessed which path under which policy.
Common operational commands (audit, tokens, leases)
# 監査ログを有効化(ファイル例)
vault audit enable file file_path=/var/log/vault_audit.log
# バッチトークン(軽量・非更新)を短 TTL で発行
vault token create -type=batch -policy=ci-readonly -ttl=15m -explicit-max-ttl=30m
# 動的シークレットの強制失効(接頭辞でまとめて)
vault lease revoke -prefix database/creds/ci-test
# レスポンスラッピング(5 分)で安全に受け渡し
vault write -wrap-ttl=5m sys/wrapping/wrap [email protected]Heavy concurrency in CI spikes traffic to Vault. Use rate limiting, connection reuse, and the Agent cache to absorb it. Design HA (with an officially recommended storage backend) and auto-unseal to match your availability targets.
Define behavior under Vault outages. The Injector/Agent cache buys you some headroom, but when strong consistency is required, failing closed (halting the deploy) is the safer choice.
Minimal Vault Agent configuration (auto_auth + cache)
auto_auth {
method {
type = "kubernetes"
config = {
role = "k8s-web"
}
}
sink {
type = "file"
config = {
path = "/home/vault/.token"
}
}
}
cache {
use_auto_auth_token = true
}
template {
source = "/vault/templates/config.tpl"
destination = "/run/secrets/config.env"
# 更新間隔の下限を指定して過剰な再読込を抑制
min_refresh_interval = "15s"
}Ops
問題 1
A CI build needs database credentials for unit tests. Requirements: no long-lived shared keys, automatic invalidation after the job ends, and a safe handoff between stages. Which approach fits best?
正解: A
Vault dynamic secrets shine with short TTLs and automatic expiry, making them ideal for one-shot CI use. Handoffs between stages can be done safely via sys/wrapping response wrapping. The other options rely on long-lived keys or image embedding, which are inappropriate from both an exposure and auditing standpoint.
Should I use OIDC or AppRole with GitHub Actions?
OIDC is generally preferred. Vault verifies a signed JWT from the IdP, so you do not need a long-lived shared secret and auditing is straightforward. On self-hosted runners or in environments without OIDC support, use AppRole and distribute the SecretID via response wrapping.
Should secrets be passed as environment variables or as files?
Both at build time and runtime, file delivery is preferred. Environment variables can leak via the process tree, crash dumps, or logs. Render them to files via Vault Agent/Injector templates and design the app to reload them.
How should deployments behave when Vault is down?
If confidentiality matters most, fail closed (abort deploys when Vault is unreachable). If availability matters more and brief staleness is acceptable, lean on Agent/Injector caching and reconcile with the real rotation later. Either way, leave an audit trail.
Practice with certification-focused question sets
無料で問題を解いてみるNicheeLab Editorial Team
NicheeLab editorial team focused on data engineering and cloud certification learning. Content is structured around practical study needs and official exam domains.
Vault Core Concepts: Sealed/Unsealed, Auth, Secrets (2026)
Vault fundamentals — sealed/unsealed state, auth methods, se...
Vault Operations Professional (VOP-003): Complete Guide (2026)
Pass the Vault Operations Professional exam — enterprise pat...
Vault Path-Based Routing: API URL Structure (2026)
How Vault's path-based routing works — mount points, sub-pat...
Vault Tokens: Auth Token Mechanics (2026)
Token fundamentals — service vs. batch tokens, accessor, ren...
Vault Token Types: Service, Batch, Periodic (2026)
Service vs. batch tokens compared — performance, ACL behavio...