Kubernetes
Use SikkerKey secrets in Kubernetes pods.
Approach
Store the machine identity as a Kubernetes Secret, mount it into your pods, and use either the CLI or an SDK to fetch secrets at runtime.
SikkerKey secrets are fetched on demand, not synced to Kubernetes Secrets. This means your secrets are never stored in etcd and are always up to date.
Setup
1. Bootstrap a Kubernetes machine
From the dashboard, go to Machines > + Validate and select the Kubernetes tab. Run the command:
curl -sSL https://api.sikkerkey.com/v1/bootstrap/YOUR_TOKEN/k8s | sh
This generates a keypair locally, registers the machine as k8s-{hostname}, and outputs a ready-to-use kubectl create secret command:
kubectl create secret generic sikkerkey-identity \
--from-literal=vault-id=vault_abc123 \
--from-literal=machine-id=9daae125-... \
--from-literal=api-url=https://api.sikkerkey.com \
--from-literal=machine-name=k8s-myhost \
--from-literal=private-key="-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIHN...
-----END PRIVATE KEY-----"
2. Create the Kubernetes Secret
Copy and run the kubectl create secret command output by the bootstrap script in your cluster.
3. Approve and grant access
Approve the machine from the dashboard, add it to your project, and grant it access to the secrets your pods need.
4. Create a ConfigMap for the project
kubectl create configmap sikkerkey-config \
--from-literal=project-id=proj_xyz789
Init Container Pattern
Use an init container to fetch secrets and write them to a shared volume. Your application reads from the volume.
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
template:
spec:
volumes:
- name: secrets
emptyDir:
medium: Memory
initContainers:
- name: fetch-secrets
image: node:20-slim
command:
- sh
- -c
- |
npm install -g @sikkerkey/cli
VAULT_DIR="$HOME/.sikkerkey/vaults/$VAULT_ID"
mkdir -p "$VAULT_DIR"
echo "$PRIVATE_KEY" > "$VAULT_DIR/private.pem"
chmod 600 "$VAULT_DIR/private.pem"
cat > "$VAULT_DIR/identity.json" <<EOF
{"machineId":"$MACHINE_ID","machineName":"$MACHINE_NAME","vaultId":"$VAULT_ID","apiUrl":"$API_URL","privateKeyPath":"$VAULT_DIR/private.pem"}
EOF
sikkerkey connect "$VAULT_ID"
sikkerkey unlock "$PROJECT_ID"
sikkerkey export --format dotenv > /secrets/.env
env:
- name: VAULT_ID
valueFrom:
secretKeyRef:
name: sikkerkey-identity
key: vault-id
- name: MACHINE_ID
valueFrom:
secretKeyRef:
name: sikkerkey-identity
key: machine-id
- name: MACHINE_NAME
valueFrom:
secretKeyRef:
name: sikkerkey-identity
key: machine-name
- name: API_URL
valueFrom:
secretKeyRef:
name: sikkerkey-identity
key: api-url
- name: PRIVATE_KEY
valueFrom:
secretKeyRef:
name: sikkerkey-identity
key: private-key
- name: PROJECT_ID
valueFrom:
configMapKeyRef:
name: sikkerkey-config
key: project-id
volumeMounts:
- name: secrets
mountPath: /secrets
containers:
- name: app
image: myapp:latest
command: ["sh", "-c", "source /secrets/.env && exec node server.js"]
volumeMounts:
- name: secrets
mountPath: /secrets
readOnly: true
The secrets volume uses emptyDir with medium: Memory so secrets are never written to disk on the node.
Sidecar Pattern
For applications that need fresh secrets (e.g. secrets with automatic rotation), run the CLI as a sidecar that periodically refreshes:
containers:
- name: app
image: myapp:latest
command: ["sh", "-c", "source /secrets/.env && exec node server.js"]
volumeMounts:
- name: secrets
mountPath: /secrets
readOnly: true
- name: sikkerkey-sidecar
image: node:20-slim
command:
- sh
- -c
- |
npm install -g @sikkerkey/cli
VAULT_DIR="$HOME/.sikkerkey/vaults/$VAULT_ID"
mkdir -p "$VAULT_DIR"
echo "$PRIVATE_KEY" > "$VAULT_DIR/private.pem"
chmod 600 "$VAULT_DIR/private.pem"
cat > "$VAULT_DIR/identity.json" <<EOF
{"machineId":"$MACHINE_ID","machineName":"$MACHINE_NAME","vaultId":"$VAULT_ID","apiUrl":"$API_URL","privateKeyPath":"$VAULT_DIR/private.pem"}
EOF
sikkerkey connect "$VAULT_ID"
sikkerkey unlock "$PROJECT_ID"
while true; do
sikkerkey export --format dotenv > /secrets/.env
sleep 60
done
env:
- name: VAULT_ID
valueFrom:
secretKeyRef:
name: sikkerkey-identity
key: vault-id
- name: MACHINE_ID
valueFrom:
secretKeyRef:
name: sikkerkey-identity
key: machine-id
- name: MACHINE_NAME
valueFrom:
secretKeyRef:
name: sikkerkey-identity
key: machine-name
- name: API_URL
valueFrom:
secretKeyRef:
name: sikkerkey-identity
key: api-url
- name: PRIVATE_KEY
valueFrom:
secretKeyRef:
name: sikkerkey-identity
key: private-key
- name: PROJECT_ID
valueFrom:
configMapKeyRef:
name: sikkerkey-config
key: project-id
volumeMounts:
- name: secrets
mountPath: /secrets
SDK in Application
If you prefer, use an SDK directly in your application. Pass the identity values as environment variables from the Kubernetes secret and assemble the identity at application startup:
import os, json
# Write identity from env vars at startup
vault_id = os.environ["VAULT_ID"]
vault_dir = os.path.expanduser(f"~/.sikkerkey/vaults/{vault_id}")
os.makedirs(vault_dir, exist_ok=True)
with open(f"{vault_dir}/private.pem", "w") as f:
f.write(os.environ["PRIVATE_KEY"])
os.chmod(f"{vault_dir}/private.pem", 0o600)
with open(f"{vault_dir}/identity.json", "w") as f:
json.dump({
"machineId": os.environ["MACHINE_ID"],
"machineName": os.environ["MACHINE_NAME"],
"vaultId": vault_id,
"apiUrl": os.environ["API_URL"],
"privateKeyPath": f"{vault_dir}/private.pem",
}, f)
# Now use the SDK
from sikkerkey import SikkerKey
sk = SikkerKey(vault_id)
db_password = sk.get_field("sk_db_prod", "password")
Security Notes
- The Kubernetes Secret containing the private key should be restricted via RBAC to only the namespaces and service accounts that need it
- Use
emptyDirwithmedium: Memoryfor the secrets volume to avoid writing to node disk - Mount the identity as read-only
- Each pod using the same machine identity appears as the same machine in audit logs. For per-pod attribution, bootstrap a separate machine per deployment
- Secrets are fetched from SikkerKey on demand and never stored in etcd