diff --git a/examples/config.yaml b/examples/config.yaml index 267b8f7d..c14b2857 100644 --- a/examples/config.yaml +++ b/examples/config.yaml @@ -11,8 +11,14 @@ org: my-buildkite-org cluster-uuid: beefcafe-abbe-baba-abba-deedcedecade tags: -- queue=my-queue -- priority=high + - queue=my-queue + - priority=high + +# ssh-credentials-secret are the secret with the ssh credentials +# configured on the agent with docker-ssh-config +# @see https://github.com/buildkite/docker-ssh-env-config +ssh-credentials-secret: buildkite-ssh-github-creds + # This will be applied to the job's podSpec as a strategic merge patch # See https://kubernetes.io/docs/tasks/manage-kubernetes-objects/update-api-object-kubectl-patch diff --git a/internal/controller/config/config.go b/internal/controller/config/config.go index ac5a7fd5..17052631 100644 --- a/internal/controller/config/config.go +++ b/internal/controller/config/config.go @@ -34,6 +34,7 @@ type Config struct { ClusterUUID string `json:"cluster-uuid" validate:"omitempty"` AdditionalRedactedVars stringSlice `json:"additional-redacted-vars" validate:"omitempty"` PodSpecPatch *corev1.PodSpec `json:"pod-spec-patch" validate:"omitempty"` + SSHCredentialsSecret string `mapstructure:"ssh-credentials-secret" validate:"omitempty"` } type stringSlice []string @@ -47,6 +48,7 @@ func (s stringSlice) MarshalLogArray(enc zapcore.ArrayEncoder) error { func (c Config) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddString("agent-token-secret", c.AgentTokenSecret) + enc.AddString("ssh-credentials-secret", c.SSHCredentialsSecret) enc.AddBool("debug", c.Debug) enc.AddString("image", c.Image) enc.AddDuration("job-ttl", c.JobTTL) diff --git a/internal/controller/controller.go b/internal/controller/controller.go index 31928e0d..ef29c3b9 100644 --- a/internal/controller/controller.go +++ b/internal/controller/controller.go @@ -55,6 +55,7 @@ func Run( JobTTL: cfg.JobTTL, AdditionalRedactedVars: cfg.AdditionalRedactedVars, PodSpecPatch: cfg.PodSpecPatch, + SSHCredentialsSecret: cfg.SSHCredentialsSecret, }) limiter := scheduler.NewLimiter(logger.Named("limiter"), sched, cfg.MaxInFlight) diff --git a/internal/controller/scheduler/scheduler.go b/internal/controller/scheduler/scheduler.go index 9d3bbbb0..7274a522 100644 --- a/internal/controller/scheduler/scheduler.go +++ b/internal/controller/scheduler/scheduler.go @@ -38,6 +38,7 @@ type Config struct { JobTTL time.Duration AdditionalRedactedVars []string PodSpecPatch *corev1.PodSpec + SSHCredentialsSecret string } func New(logger *zap.Logger, client kubernetes.Interface, cfg Config) *worker { @@ -49,13 +50,14 @@ func New(logger *zap.Logger, client kubernetes.Interface, cfg Config) *worker { } type KubernetesPlugin struct { - PodSpec *corev1.PodSpec `json:"podSpec,omitempty"` - PodSpecPatch *corev1.PodSpec `json:"podSpecPatch,omitempty"` - GitEnvFrom []corev1.EnvFromSource `json:"gitEnvFrom,omitempty"` - Sidecars []corev1.Container `json:"sidecars,omitempty"` - Metadata Metadata `json:"metadata,omitempty"` - ExtraVolumeMounts []corev1.VolumeMount `json:"extraVolumeMounts,omitempty"` - Checkout Checkout `json:"checkout,omitempty"` + PodSpec *corev1.PodSpec `json:"podSpec,omitempty"` + PodSpecPatch *corev1.PodSpec `json:"podSpecPatch,omitempty"` + SSHCredentialsSecret string + GitEnvFrom []corev1.EnvFromSource `json:"gitEnvFrom,omitempty"` + Sidecars []corev1.Container `json:"sidecars,omitempty"` + Metadata Metadata `json:"metadata,omitempty"` + ExtraVolumeMounts []corev1.VolumeMount `json:"extraVolumeMounts,omitempty"` + Checkout Checkout `json:"checkout,omitempty"` } type Checkout struct { @@ -111,6 +113,7 @@ type jobWrapper struct { logger *zap.Logger job *api.CommandJob envMap map[string]string + envFrom []corev1.EnvFromSource err error k8sPlugin KubernetesPlugin otherPlugins []map[string]json.RawMessage @@ -119,10 +122,11 @@ type jobWrapper struct { func NewJobWrapper(logger *zap.Logger, job *api.CommandJob, config Config) *jobWrapper { return &jobWrapper{ - logger: logger, - job: job, - cfg: config, - envMap: make(map[string]string), + logger: logger, + job: job, + cfg: config, + envMap: make(map[string]string), + envFrom: make([]corev1.EnvFromSource, 0), } } @@ -230,6 +234,24 @@ func (w *jobWrapper) Build(skipCheckout bool) (*batchv1.Job, error) { Value: w.job.Uuid, }, } + + // Generate env from configuration for git credentials + secretName := w.cfg.SSHCredentialsSecret + if w.k8sPlugin.SSHCredentialsSecret != "" { + secretName = w.k8sPlugin.SSHCredentialsSecret + } + + if secretName != "" && len(w.k8sPlugin.GitEnvFrom) == 0 { + w.envFrom = append(w.envFrom, corev1.EnvFromSource{ + SecretRef: &corev1.SecretEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{Name: secretName}, + }, + }) + } else if len(w.k8sPlugin.GitEnvFrom) > 0 { + w.logger.Warn("git-env-from is deprecated, please use ssh-credentials-secret instead") + w.envFrom = append(w.envFrom, w.k8sPlugin.GitEnvFrom...) + } + if w.otherPlugins != nil { otherPluginsJson, err := json.Marshal(w.otherPlugins) if err != nil { @@ -332,7 +354,7 @@ func (w *jobWrapper) Build(skipCheckout bool) (*batchv1.Job, error) { c.WorkingDir = "/workspace" } c.VolumeMounts = append(c.VolumeMounts, volumeMounts...) - c.EnvFrom = append(c.EnvFrom, w.k8sPlugin.GitEnvFrom...) + c.EnvFrom = append(c.EnvFrom, w.envFrom...) podSpec.Containers[i] = c } @@ -366,7 +388,7 @@ func (w *jobWrapper) Build(skipCheckout bool) (*batchv1.Job, error) { c.Name = fmt.Sprintf("%s-%d", "sidecar", i) } c.VolumeMounts = append(c.VolumeMounts, volumeMounts...) - c.EnvFrom = append(c.EnvFrom, w.k8sPlugin.GitEnvFrom...) + c.EnvFrom = append(c.EnvFrom, w.envFrom...) podSpec.Containers = append(podSpec.Containers, c) } @@ -561,7 +583,7 @@ func (w *jobWrapper) createCheckoutContainer( Value: w.k8sPlugin.Checkout.FetchFlags, }, }, - EnvFrom: w.k8sPlugin.GitEnvFrom, + EnvFrom: w.envFrom, } checkoutContainer.Env = append(checkoutContainer.Env, env...) diff --git a/internal/integration/fixtures/secretref.yaml b/internal/integration/fixtures/secretref.yaml index b1aa12d9..73669207 100644 --- a/internal/integration/fixtures/secretref.yaml +++ b/internal/integration/fixtures/secretref.yaml @@ -14,6 +14,19 @@ steps: - echo - hello world +- label: ":git::console::superhero: checkout private git with ssh credentials secret as root" + agents: + queue: {{.queue}} + plugins: + - kubernetes: + ssh-credentials-secret: agent-stack-k8s + podSpec: + containers: + - image: alpine:latest + command: + - echo + - hello world + - label: ":git::console::student: checkout private git repo as user" agents: queue: {{.queue}} @@ -32,3 +45,20 @@ steps: runAsNonRoot: true runAsUser: 1000 runAsGroup: 1001 + +- label: ":git::console::student: checkout private git with ssh credentials secret repo as user" + agents: + queue: {{.queue}} + plugins: + - kubernetes: + ssh-credentials-secret: agent-stack-k8s + podSpec: + containers: + - image: alpine:latest + command: + - echo + - hello world + securityContext: + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 1001 \ No newline at end of file