|
#!/bin/sh |
|
set -eu |
|
|
|
VAULT_ADDR="http://vault:8200" |
|
VAULT_TOKEN="root" |
|
NOMAD_ADDR="http://nomad:4646" |
|
|
|
# Wait for Vault |
|
echo "Waiting for Vault..." |
|
until curl -sSf "$VAULT_ADDR/v1/sys/health" >/dev/null 2>&1; do |
|
sleep 1 |
|
done |
|
|
|
echo "Vault is up" |
|
|
|
# Wait for Nomad |
|
echo "Waiting for Nomad..." |
|
until curl -sSf "$NOMAD_ADDR/v1/status/leader" >/dev/null 2>&1; do |
|
sleep 1 |
|
done |
|
|
|
echo "Nomad is up" |
|
|
|
# enable vault audit logging to file |
|
jo type=file options=$(jo file_path="/tmp/vault_audit.log") | tee /tmp/audit-backend.json | jq . |
|
curl -sSf -H "X-Vault-Token: $VAULT_TOKEN" "$VAULT_ADDR/v1/sys/audit/file" -d @/tmp/audit-backend.json |
|
|
|
# Ensure KV v2 is mounted at "secret/" |
|
if ! curl -sSf -H "X-Vault-Token: $VAULT_TOKEN" "$VAULT_ADDR/v1/sys/mounts" | grep -q '"secret/"'; then |
|
echo "Enabling KV v2 at path secret" |
|
curl -sSf -X POST -H "X-Vault-Token: $VAULT_TOKEN" -d '{"type":"kv","options":{"version":"2"}}' "$VAULT_ADDR/v1/sys/mounts/secret" |
|
else |
|
echo "KV mount at secret/ already present" |
|
fi |
|
|
|
# Write foo=bar to secret path secret/s1 (KV v2) |
|
cat > /tmp/secret-data.json <<EOF |
|
{ |
|
"data": { |
|
"foo": "bar" |
|
} |
|
} |
|
EOF |
|
|
|
echo "Writing secret to secret/data/s1" |
|
curl -sSf -H "X-Vault-Token: $VAULT_TOKEN" -H "Content-Type: application/json" -X POST -d @/tmp/secret-data.json "$VAULT_ADDR/v1/secret/data/s1" |
|
echo "Secret written" |
|
|
|
|
|
# Mount jwt auth method if not mounted |
|
if ! curl -sSf -H "X-Vault-Token: $VAULT_TOKEN" "$VAULT_ADDR/v1/sys/auth" | grep -q "jwt-nomad/"; then |
|
echo "Enabling JWT auth method at path jwt-nomad" |
|
curl -sSf -X POST -H "X-Vault-Token: $VAULT_TOKEN" -d '{"type":"jwt"}' "$VAULT_ADDR/v1/sys/auth/jwt-nomad" |
|
else |
|
echo "JWT auth already enabled" |
|
fi |
|
|
|
# Configure the JWT auth method to use Nomad JWKS endpoint |
|
JWKS_URL="$NOMAD_ADDR/.well-known/jwks.json" |
|
|
|
echo "Configuring JWT auth method to use JWKS URL: $JWKS_URL" |
|
cat > /tmp/auth-config.json <<EOF |
|
{ |
|
"jwks_url": "${JWKS_URL}", |
|
"jwt_supported_algs": ["RS256"], |
|
"default_role": "nomad-workloads" |
|
} |
|
EOF |
|
|
|
curl -sSf -H "X-Vault-Token: $VAULT_TOKEN" -X POST -d @/tmp/auth-config.json "$VAULT_ADDR/v1/auth/jwt-nomad/config" |
|
|
|
# Create a policy for nomad workloads |
|
cat > /tmp/nomad-workloads.rule <<'EOF' |
|
path "auth/jwt-nomad/role/nomad-workloads" { |
|
capabilities = ["read"] |
|
} |
|
path "secret/*" { |
|
capabilities = ["read"] |
|
} |
|
EOF |
|
jo policy=@/tmp/nomad-workloads.rule > /tmp/nomad-workloads.hcl |
|
|
|
curl -sSf -H "X-Vault-Token: $VAULT_TOKEN" -X PUT -d @/tmp/nomad-workloads.hcl "$VAULT_ADDR/v1/sys/policies/acl/nomad-workloads" |
|
|
|
# Create a role that binds to nomad claims |
|
cat > /tmp/acl-role.json <<EOF |
|
{ |
|
"role_type": "jwt", |
|
"bound_audiences": ["nomad"], |
|
"bound_claims": { |
|
"nomad_namespace": "default" |
|
}, |
|
"user_claim": "/nomad_job_id", |
|
"user_claim_json_pointer": true, |
|
"claim_mappings": { |
|
"nomad_namespace": "nomad_namespace", |
|
"nomad_job_id": "nomad_job_id", |
|
"nomad_task": "nomad_task" |
|
}, |
|
"token_policies": ["nomad-workloads"], |
|
"token_type": "service", |
|
"token_period": "30m", |
|
"token_explicit_max_ttl": 0 |
|
} |
|
EOF |
|
|
|
curl -sSf -H "X-Vault-Token: $VAULT_TOKEN" -X POST -d @/tmp/acl-role.json "$VAULT_ADDR/v1/auth/jwt-nomad/role/nomad-workloads" |
|
|
|
echo "Bootstrap complete" |
|
|
|
cat > /tmp/job.hcl <<'EOF' |
|
job "j1" { |
|
type = "batch" |
|
group "g1" { |
|
task "t1" { |
|
driver = "docker" |
|
config { |
|
image = "ubuntu:22.04" |
|
args = ["cat", "local/foo.txt"] |
|
} |
|
# XXX: YOU need vault section here for vault access |
|
vault { |
|
cluster = "default" |
|
} |
|
template { |
|
destination = "local/foo.txt" |
|
data = <<EOD |
|
{{ with secret "secret/data/s1" }} |
|
{{ .Data.data.foo }} |
|
{{ end }} |
|
EOD |
|
} |
|
} |
|
} |
|
} |
|
EOF |
|
|
|
cat /tmp/job.hcl |
|
jo JobHCL=@/tmp/job.hcl | tee /tmp/job-req.json | jq . |
|
|
|
curl -v -s $NOMAD_ADDR/v1/jobs/parse -H Content-type:application/json -d @/tmp/job-req.json > /tmp/job-p.json |
|
|
|
#cat /tmp/job-p.json | jq -r '{"Job":.}' | tee /tmp/job-p-req.json |
|
jq -n --rawfile sub /tmp/job.hcl --slurpfile job /tmp/job-p.json -r '{"Job":$job[0], "Submission":{"Format":"hcl2","Source":$sub}}' | tee /tmp/job-p-req.json |
|
|
|
curl -v -D - $NOMAD_ADDR/v1/jobs -H Content-type:application/json -d @/tmp/job-p-req.json |
|
|
|
for q in $(seq 1 10); do |
|
ST=$(curl -s -X GET $NOMAD_ADDR/v1/job/j1/evaluations | jq -r '.[].Status') |
|
if [ "$ST" = "complete" ]; then |
|
break |
|
fi |
|
echo "Evaluation status: $ST. Waiting..." |
|
sleep 2 |
|
done |
|
|
|
echo "=========================" |
|
|
|
AID=$(curl -s $NOMAD_ADDR/v1/job/j1/allocations | jq -r '.[0].ID') |
|
OUT=$(curl -s "$NOMAD_ADDR/v1/client/fs/logs/$AID?task=t1&type=stdout" | jq -r .Data | base64 -d | tr -d '[:space:]\r\n') |
|
if [ "$OUT" = "bar" ]; then |
|
echo "Success: Retrieved secret value '$OUT' from Vault via Nomad" |
|
else |
|
echo "Failure: Unexpected output '$OUT'" |
|
exit 1 |
|
fi |