Mount secrets and configmaps¶
The short version¶
apiVersion: gitlab.k8s.alekc.dev/v1beta2
kind: Runner
metadata:
name: runner-sample
spec:
authentication:
token:
secret_key_ref:
name: gitlab-runner-token
executor_config:
volumes:
secret:
- name: deploy-ssh-key
mount_path: /etc/ci/ssh
read_only: true
items:
# secret key -> filename inside mount_path
"id_ed25519": "id_ed25519"
config_map:
- name: ci-settings
mount_path: /etc/ci/conf
Every container in the job pod gets the mount, so the build script can read
/etc/ci/ssh/id_ed25519 directly.
The three fields that matter¶
| Field | Notes |
|---|---|
name |
The Secret or ConfigMap, read from the namespace the job pod runs in. |
mount_path |
Directory inside the containers. Mounting over a populated path hides what was there. |
items |
Maps a key in the object to a filename in the mount. Without it, every key becomes a file named after the key. |
sub_path mounts a single key at a file path instead of a directory, and
read_only is worth setting on anything a job has no business writing to.
Worked example: git submodules over SSH¶
spec:
environment:
- "GIT_SSH_COMMAND=ssh -i /root/.ssh/id_ed25519 -o StrictHostKeyChecking=accept-new"
executor_config:
volumes:
secret:
- name: deploy-ssh-key
mount_path: /etc/ci/ssh
read_only: true
items:
"id_ed25519": "id_ed25519"
A key mounted from a Secret arrives mode 0644, and OpenSSH refuses a private
key that is group or world readable. The secret volume type exposes no file-mode
setting, and fs_group sets group ownership rather than the mode, so it does not
help here. Copy the key and fix the mode in a before_script:
before_script:
- install -m 600 -D /etc/ci/ssh/id_ed25519 /root/.ssh/id_ed25519
GIT_SSH_COMMAND above points at the copy, not at the mount. Pointing it at the
mounted path defeats the exercise, since that file is still 0644.
This covers git operations in your own script. The runner clones the repository
and fetches submodules before before_script runs, and the CRD exposes no
pre-clone hook, so a submodule over SSH cannot use this key during checkout. Set
GIT_SUBMODULE_STRATEGY: none and fetch them yourself in the script, or rewrite
the submodule URL to use CI_JOB_TOKEN over HTTPS.
Gotchas¶
A wrong key name fails silently. The mount succeeds, the file simply is not
there, and the job fails much later with an unrelated error. When wiring one up
for the first time, put ls -la <mount_path> in the script and look once.
Namespace. Secrets are read from the job pod's namespace, which is
executor_config.namespace if set and the runner's namespace otherwise. This is
the most common reason a mount that "should work" does not. See
dedicated build namespace.
Do not mount the runner's own token. The runner's authentication token lives in the config Secret the operator manages. Mounting it into job pods hands every CI job the credential for the runner itself.
This is not how you authenticate image pulls. A registry credential mounted
as a volume is readable by tools inside the job (kaniko, docker login). The
kubelet pulling the job's own image needs image_pull_secrets instead. See
pull from a private registry.
Related¶
- Pull from a private registry
- Rootless builds, which is this mechanism plus an
itemsrename - Persistent and ephemeral storage, for volumes that hold data rather than credentials