From b42cf9564f3460907465fbf1cc1b4decd5c894bd Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Sun, 5 Apr 2026 11:30:45 -0300 Subject: [PATCH] security: use Vault injection for remote worker credentials --- pkg/service/remote.go | 122 ++++++++++++++++++++++++++++++++---------- 1 file changed, 93 insertions(+), 29 deletions(-) diff --git a/pkg/service/remote.go b/pkg/service/remote.go index c634573..e6fa34f 100644 --- a/pkg/service/remote.go +++ b/pkg/service/remote.go @@ -12,7 +12,13 @@ import ( "metis/pkg/inventory" ) -const hostTmpDevicePath = "hosttmp:///tmp" +const ( + hostTmpDevicePath = "hosttmp:///tmp" + vaultRoleMaintenance = "maintenance" + vaultRuntimeSecretPath = "kv/data/atlas/maintenance/metis-runtime" + vaultHarborSecretPath = "kv/data/atlas/harbor/harbor-core" + vaultSSHKeysSecretPath = "kv/data/atlas/maintenance/metis-ssh-keys" +) func (a *App) ListDevices(host string) ([]Device, error) { return a.cachedDevices(host) @@ -447,9 +453,10 @@ func (a *App) remoteBuildPodSpec(name, host, image, node, artifactRef, buildTag "apiVersion": "v1", "kind": "Pod", "metadata": map[string]any{ - "name": name, - "namespace": a.settings.Namespace, - "labels": map[string]string{"app": "metis-remote", "metis-run": "build"}, + "name": name, + "namespace": a.settings.Namespace, + "labels": map[string]string{"app": "metis-remote", "metis-run": "build"}, + "annotations": vaultRuntimeAnnotations(true), }, "spec": map[string]any{ "restartPolicy": "Never", @@ -462,22 +469,22 @@ func (a *App) remoteBuildPodSpec(name, host, image, node, artifactRef, buildTag "name": "remote-build", "image": image, "imagePullPolicy": "Always", - "command": []string{ - "metis", "remote-build", - "--inventory", a.settings.InventoryPath, - "--node", node, - "--cache", "/workspace/cache", - "--work-dir", "/workspace/build", - "--artifact-ref", artifactRef, - "--build-tag", buildTag, - "--harbor-registry", a.settings.HarborRegistry, + "command": []string{"/bin/sh", "-c"}, + "args": []string{ + remoteWorkerEntrypoint( + true, + "remote-build", + "--inventory", a.settings.InventoryPath, + "--node", node, + "--cache", "/workspace/cache", + "--work-dir", "/workspace/build", + "--artifact-ref", artifactRef, + "--build-tag", buildTag, + "--harbor-registry", a.settings.HarborRegistry, + ), }, "envFrom": []map[string]any{ {"configMapRef": map[string]any{"name": "metis"}}, - {"secretRef": map[string]any{"name": "metis-harbor"}}, - }, - "env": []map[string]any{ - {"name": "METIS_K3S_TOKEN", "valueFrom": map[string]any{"secretKeyRef": map[string]any{"name": "metis-runtime", "key": "k3s_token", "optional": true}}}, }, "volumeMounts": []map[string]any{ {"name": "workspace", "mountPath": "/workspace"}, @@ -497,9 +504,10 @@ func (a *App) remoteFlashPodSpec(name, host, image, node, device, artifactRef st "apiVersion": "v1", "kind": "Pod", "metadata": map[string]any{ - "name": name, - "namespace": a.settings.Namespace, - "labels": map[string]string{"app": "metis-remote", "metis-run": "flash"}, + "name": name, + "namespace": a.settings.Namespace, + "labels": map[string]string{"app": "metis-remote", "metis-run": "flash"}, + "annotations": vaultRuntimeAnnotations(false), }, "spec": map[string]any{ "restartPolicy": "Never", @@ -512,19 +520,22 @@ func (a *App) remoteFlashPodSpec(name, host, image, node, device, artifactRef st "name": "remote-flash", "image": image, "imagePullPolicy": "Always", - "command": []string{ - "metis", "remote-flash", - "--node", node, - "--device", device, - "--artifact-ref", artifactRef, - "--work-dir", "/workspace/flash", - "--harbor-registry", a.settings.HarborRegistry, - "--host-tmp-dir", mountedHostTmpDir(a.settings.HostTmpDir), + "command": []string{"/bin/sh", "-c"}, + "args": []string{ + remoteWorkerEntrypoint( + false, + "remote-flash", + "--node", node, + "--device", device, + "--artifact-ref", artifactRef, + "--work-dir", "/workspace/flash", + "--harbor-registry", a.settings.HarborRegistry, + "--host-tmp-dir", mountedHostTmpDir(a.settings.HostTmpDir), + ), }, "securityContext": map[string]any{"privileged": true, "runAsUser": 0}, "envFrom": []map[string]any{ {"configMapRef": map[string]any{"name": "metis"}}, - {"secretRef": map[string]any{"name": "metis-harbor"}}, }, "volumeMounts": []map[string]any{ {"name": "workspace", "mountPath": "/workspace"}, @@ -572,3 +583,56 @@ func mountedHostTmpDir(path string) string { return filepath.Join("/host-tmp", strings.TrimPrefix(path, "/")) } } + +func vaultRuntimeAnnotations(includeSSHKeys bool) map[string]string { + annotations := map[string]string{ + "vault.hashicorp.com/agent-inject": "true", + "vault.hashicorp.com/agent-pre-populate-only": "true", + "vault.hashicorp.com/role": vaultRoleMaintenance, + "vault.hashicorp.com/agent-inject-secret-metis-runtime-env.sh": vaultRuntimeSecretPath, + "vault.hashicorp.com/agent-inject-template-metis-runtime-env.sh": `{{ with secret "kv/data/atlas/maintenance/metis-runtime" }} +export METIS_K3S_TOKEN="{{ .Data.data.k3s_token }}" +{{ end }}`, + "vault.hashicorp.com/agent-inject-secret-metis-harbor-env.sh": vaultHarborSecretPath, + "vault.hashicorp.com/agent-inject-template-metis-harbor-env.sh": `{{ with secret "kv/data/atlas/harbor/harbor-core" }} +export METIS_HARBOR_PASSWORD="{{ .Data.data.harbor_admin_password }}" +{{ end }}`, + } + if includeSSHKeys { + annotations["vault.hashicorp.com/agent-inject-secret-metis-ssh-env.sh"] = vaultSSHKeysSecretPath + annotations["vault.hashicorp.com/agent-inject-template-metis-ssh-env.sh"] = `{{ with secret "kv/data/atlas/maintenance/metis-ssh-keys" }} +export METIS_SSH_KEY_BASTION="{{ .Data.data.bastion_pub }}" +export METIS_SSH_KEY_BRAD="{{ .Data.data.brad_pub }}" +export METIS_SSH_KEY_HECATE_TETHYS="{{ .Data.data.hecate_tethys_pub }}" +{{ end }}` + } + return annotations +} + +func remoteWorkerEntrypoint(includeSSHKeys bool, args ...string) string { + lines := []string{ + "set -e", + ". /vault/secrets/metis-runtime-env.sh", + ". /vault/secrets/metis-harbor-env.sh", + } + if includeSSHKeys { + lines = append(lines, ". /vault/secrets/metis-ssh-env.sh") + } + lines = append(lines, "exec "+shellJoin(append([]string{"metis"}, args...)...)) + return strings.Join(lines, "\n") +} + +func shellJoin(args ...string) string { + quoted := make([]string, 0, len(args)) + for _, arg := range args { + quoted = append(quoted, shellQuote(arg)) + } + return strings.Join(quoted, " ") +} + +func shellQuote(value string) string { + if value == "" { + return "''" + } + return "'" + strings.ReplaceAll(value, "'", `'"'"'`) + "'" +}