diff --git a/config/config-feature-flags.yaml b/config/config-feature-flags.yaml index 2a04a0fd073..636563fb58b 100644 --- a/config/config-feature-flags.yaml +++ b/config/config-feature-flags.yaml @@ -91,3 +91,7 @@ data: # If set to "none", then Tekton will not have non-falsifiable provenance. # This is an experimental feature and thus should still be considered an alpha feature. enforce-nonfalsifiablity: "none" + # Setting this flag to "true" will limit privileges for containers injected by Tekton into TaskRuns. + # This allows TaskRuns to run in namespaces with "restricted" pod security standards. + # Not all Kubernetes implementations support this option. + set-security-context: "false" diff --git a/docs/additional-configs.md b/docs/additional-configs.md index 9a5a0ea2209..97cc3f0af6a 100644 --- a/docs/additional-configs.md +++ b/docs/additional-configs.md @@ -349,6 +349,14 @@ to better fit specific usecases. Out-of-the-box, Tekton Pipelines Controller is configured for relatively small-scale deployments but there have several options for configuring Pipelines' performance are available. See the [Performance Configuration](tekton-controller-performance-configuration.md) document which describes how to change the default ThreadsPerController, QPS and Burst settings to meet your requirements. +## Running TaskRuns and PipelineRuns with restricted pod security standards + +To allow TaskRuns and PipelineRuns to run in namespaces with [restricted pod security standards](https://kubernetes.io/docs/concepts/security/pod-security-standards/), +set the "set-security-context" feature flag to "true" in the [feature-flags configMap](#customizing-the-pipelines-controller-behavior). This configuration option applies a [SecurityContext](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/) +to any containers injected into TaskRuns by the Pipelines controller. This SecurityContext may not be supported in all Kubernetes implementations (for example, OpenShift). + +**Note**: running TaskRuns and PipelineRuns in the "tekton-pipelines" namespace is discouraged. + ## Platform Support The Tekton project provides support for running on x86 Linux Kubernetes nodes. diff --git a/pkg/apis/config/feature_flags.go b/pkg/apis/config/feature_flags.go index 3b5350db346..3f781f57183 100644 --- a/pkg/apis/config/feature_flags.go +++ b/pkg/apis/config/feature_flags.go @@ -76,6 +76,8 @@ const ( DefaultResultExtractionMethod = ResultExtractionMethodTerminationMessage // DefaultMaxResultSize is the default value in bytes for the size of a result DefaultMaxResultSize = 4096 + // DefaultSetSecurityContext is the default value for "set-security-context" + DefaultSetSecurityContext = false disableAffinityAssistantKey = "disable-affinity-assistant" disableCredsInitKey = "disable-creds-init" @@ -90,6 +92,7 @@ const ( enableProvenanceInStatus = "enable-provenance-in-status" resultExtractionMethod = "results-from" maxResultSize = "max-result-size" + setSecurityContextKey = "set-security-context" ) // DefaultFeatureFlags holds all the default configurations for the feature flags configmap. @@ -119,6 +122,7 @@ type FeatureFlags struct { EnableProvenanceInStatus bool ResultExtractionMethod string MaxResultSize int + SetSecurityContext bool } // GetFeatureFlagsConfigName returns the name of the configmap containing all @@ -182,6 +186,9 @@ func NewFeatureFlagsFromMap(cfgMap map[string]string) (*FeatureFlags, error) { if err := setEnforceNonFalsifiability(cfgMap, tc.EnableAPIFields, &tc.EnforceNonfalsifiability); err != nil { return nil, err } + if err := setFeature(setSecurityContextKey, DefaultSetSecurityContext, &tc.SetSecurityContext); err != nil { + return nil, err + } // Given that they are alpha features, Tekton Bundles and Custom Tasks should be switched on if // enable-api-fields is "alpha". If enable-api-fields is not "alpha" then fall back to the value of diff --git a/pkg/apis/config/feature_flags_test.go b/pkg/apis/config/feature_flags_test.go index 10cfa1150ea..2dded6be81d 100644 --- a/pkg/apis/config/feature_flags_test.go +++ b/pkg/apis/config/feature_flags_test.go @@ -51,6 +51,7 @@ func TestNewFeatureFlagsFromConfigMap(t *testing.T) { EnableProvenanceInStatus: config.DefaultEnableProvenanceInStatus, ResultExtractionMethod: config.DefaultResultExtractionMethod, MaxResultSize: config.DefaultMaxResultSize, + SetSecurityContext: config.DefaultSetSecurityContext, }, fileName: config.GetFeatureFlagsConfigName(), }, @@ -67,8 +68,8 @@ func TestNewFeatureFlagsFromConfigMap(t *testing.T) { VerificationNoMatchPolicy: config.FailNoMatchPolicy, EnableProvenanceInStatus: false, ResultExtractionMethod: "termination-message", - - MaxResultSize: 4096, + MaxResultSize: 4096, + SetSecurityContext: true, }, fileName: "feature-flags-all-flags-set", }, @@ -89,6 +90,7 @@ func TestNewFeatureFlagsFromConfigMap(t *testing.T) { EnableProvenanceInStatus: config.DefaultEnableProvenanceInStatus, ResultExtractionMethod: config.DefaultResultExtractionMethod, MaxResultSize: config.DefaultMaxResultSize, + SetSecurityContext: config.DefaultSetSecurityContext, }, fileName: "feature-flags-enable-api-fields-overrides-bundles-and-custom-tasks", }, @@ -107,6 +109,7 @@ func TestNewFeatureFlagsFromConfigMap(t *testing.T) { EnableProvenanceInStatus: config.DefaultEnableProvenanceInStatus, ResultExtractionMethod: config.DefaultResultExtractionMethod, MaxResultSize: config.DefaultMaxResultSize, + SetSecurityContext: config.DefaultSetSecurityContext, }, fileName: "feature-flags-bundles-and-custom-tasks", }, @@ -125,6 +128,7 @@ func TestNewFeatureFlagsFromConfigMap(t *testing.T) { EnableProvenanceInStatus: config.DefaultEnableProvenanceInStatus, ResultExtractionMethod: config.DefaultResultExtractionMethod, MaxResultSize: config.DefaultMaxResultSize, + SetSecurityContext: config.DefaultSetSecurityContext, }, fileName: "feature-flags-beta-api-fields", }, @@ -139,6 +143,7 @@ func TestNewFeatureFlagsFromConfigMap(t *testing.T) { EnableProvenanceInStatus: config.DefaultEnableProvenanceInStatus, ResultExtractionMethod: config.DefaultResultExtractionMethod, MaxResultSize: config.DefaultMaxResultSize, + SetSecurityContext: config.DefaultSetSecurityContext, }, fileName: "feature-flags-enforce-nonfalsifiability-spire", }, @@ -151,6 +156,7 @@ func TestNewFeatureFlagsFromConfigMap(t *testing.T) { EnableProvenanceInStatus: config.DefaultEnableProvenanceInStatus, ResultExtractionMethod: config.ResultExtractionMethodSidecarLogs, MaxResultSize: 8192, + SetSecurityContext: config.DefaultSetSecurityContext, }, fileName: "feature-flags-results-via-sidecar-logs", }, @@ -181,6 +187,7 @@ func TestNewFeatureFlagsFromEmptyConfigMap(t *testing.T) { EnableProvenanceInStatus: config.DefaultEnableProvenanceInStatus, ResultExtractionMethod: config.DefaultResultExtractionMethod, MaxResultSize: config.DefaultMaxResultSize, + SetSecurityContext: config.DefaultSetSecurityContext, } verifyConfigFileWithExpectedFeatureFlagsConfig(t, FeatureFlagsConfigEmptyName, expectedConfig) } diff --git a/pkg/apis/config/testdata/feature-flags-all-flags-set.yaml b/pkg/apis/config/testdata/feature-flags-all-flags-set.yaml index 696ca3c086c..f3395baf4f7 100644 --- a/pkg/apis/config/testdata/feature-flags-all-flags-set.yaml +++ b/pkg/apis/config/testdata/feature-flags-all-flags-set.yaml @@ -29,3 +29,4 @@ data: enforce-nonfalsifiability: "spire" trusted-resources-verification-no-match-policy: "fail" enable-provenance-in-status: "false" + set-security-context: "true" diff --git a/pkg/pod/pod.go b/pkg/pod/pod.go index e740d59796c..91af5c6cb14 100644 --- a/pkg/pod/pod.go +++ b/pkg/pod/pod.go @@ -57,6 +57,9 @@ const ( // SpiffeCsiDriver is the CSI storage plugin needed for injection of SPIFFE workload api. SpiffeCsiDriver = "csi.spiffe.io" + + // osSelectorLabel is the label Kubernetes uses for OS-specific workloads (https://kubernetes.io/docs/reference/labels-annotations-taints/#kubernetes-io-os) + osSelectorLabel = "kubernetes.io/os" ) // These are effectively const, but Go doesn't have such an annotation. @@ -99,6 +102,27 @@ var ( // MaxActiveDeadlineSeconds is a maximum permitted value to be used for a task with no timeout MaxActiveDeadlineSeconds = int64(math.MaxInt32) + + // Used in security context of pod init containers + allowPrivilegeEscalation = false + runAsNonRoot = true + + // The following security contexts allow init containers to run in namespaces + // with "restricted" pod security admission + // See https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted + linuxSecurityContext = &corev1.SecurityContext{ + AllowPrivilegeEscalation: &allowPrivilegeEscalation, + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{"ALL"}, + }, + RunAsNonRoot: &runAsNonRoot, + SeccompProfile: &corev1.SeccompProfile{ + Type: corev1.SeccompProfileTypeRuntimeDefault, + }, + } + windowsSecurityContext = &corev1.SecurityContext{ + RunAsNonRoot: &runAsNonRoot, + } ) // Builder exposes options to configure Pod construction from TaskSpecs/Runs. @@ -127,6 +151,7 @@ func (b *Builder) Build(ctx context.Context, taskRun *v1beta1.TaskRun, taskSpec defaultForbiddenEnv := config.FromContextOrDefaults(ctx).Defaults.DefaultForbiddenEnv alphaAPIEnabled := featureFlags.EnableAPIFields == config.AlphaAPIFields sidecarLogsResultsEnabled := config.FromContextOrDefaults(ctx).FeatureFlags.ResultExtractionMethod == config.ResultExtractionMethodSidecarLogs + setSecurityContext := config.FromContextOrDefaults(ctx).FeatureFlags.SetSecurityContext // Add our implicit volumes first, so they can be overridden by the user if they prefer. volumes = append(volumes, implicitVolumes...) @@ -161,9 +186,10 @@ func (b *Builder) Build(ctx context.Context, taskRun *v1beta1.TaskRun, taskSpec if alphaAPIEnabled && taskRun.Spec.ComputeResources != nil { tasklevel.ApplyTaskLevelComputeResources(steps, taskRun.Spec.ComputeResources) } + windows := usesWindows(taskRun) if sidecarLogsResultsEnabled && taskSpec.Results != nil { // create a results sidecar - resultsSidecar := createResultsSidecar(taskSpec, b.Images.SidecarLogResultsImage) + resultsSidecar := createResultsSidecar(taskSpec, b.Images.SidecarLogResultsImage, setSecurityContext, windows) taskSpec.Sidecars = append(taskSpec.Sidecars, resultsSidecar) commonExtraEntrypointArgs = append(commonExtraEntrypointArgs, "-result_from", config.ResultExtractionMethodSidecarLogs) } @@ -173,15 +199,15 @@ func (b *Builder) Build(ctx context.Context, taskRun *v1beta1.TaskRun, taskSpec } initContainers = []corev1.Container{ - entrypointInitContainer(b.Images.EntrypointImage, steps), + entrypointInitContainer(b.Images.EntrypointImage, steps, setSecurityContext, windows), } // Convert any steps with Script to command+args. // If any are found, append an init container to initialize scripts. if alphaAPIEnabled { - scriptsInit, stepContainers, sidecarContainers = convertScripts(b.Images.ShellImage, b.Images.ShellImageWin, steps, sidecars, taskRun.Spec.Debug) + scriptsInit, stepContainers, sidecarContainers = convertScripts(b.Images.ShellImage, b.Images.ShellImageWin, steps, sidecars, taskRun.Spec.Debug, setSecurityContext) } else { - scriptsInit, stepContainers, sidecarContainers = convertScripts(b.Images.ShellImage, "", steps, sidecars, nil) + scriptsInit, stepContainers, sidecarContainers = convertScripts(b.Images.ShellImage, "", steps, sidecars, nil, setSecurityContext) } if scriptsInit != nil { @@ -192,7 +218,7 @@ func (b *Builder) Build(ctx context.Context, taskRun *v1beta1.TaskRun, taskSpec volumes = append(volumes, debugScriptsVolume, debugInfoVolume) } // Initialize any workingDirs under /workspace. - if workingDirInit := workingDirInit(b.Images.WorkingDirInitImage, stepContainers); workingDirInit != nil { + if workingDirInit := workingDirInit(b.Images.WorkingDirInitImage, stepContainers, setSecurityContext, windows); workingDirInit != nil { initContainers = append(initContainers, *workingDirInit) } @@ -497,9 +523,9 @@ func runVolume(i int) corev1.Volume { } } -// entrypointInitContainer generates a few init containers based of a set of command (in images) and volumes to run +// entrypointInitContainer generates a few init containers based of a set of command (in images), volumes to run, and whether the pod will run on a windows node // This should effectively merge multiple command and volumes together. -func entrypointInitContainer(image string, steps []v1beta1.Step) corev1.Container { +func entrypointInitContainer(image string, steps []v1beta1.Step, setSecurityContext, windows bool) corev1.Container { // Invoke the entrypoint binary in "cp mode" to copy itself // into the correct location for later steps and initialize steps folder command := []string{"/ko-app/entrypoint", "init", "/ko-app/entrypoint", entrypointBinary} @@ -507,6 +533,10 @@ func entrypointInitContainer(image string, steps []v1beta1.Step) corev1.Containe command = append(command, StepName(s.Name, i)) } volumeMounts := []corev1.VolumeMount{binMount, internalStepsMount} + securityContext := linuxSecurityContext + if windows { + securityContext = windowsSecurityContext + } // Rewrite steps with entrypoint binary. Append the entrypoint init // container to place the entrypoint binary. Also add timeout flags @@ -521,20 +551,39 @@ func entrypointInitContainer(image string, steps []v1beta1.Step) corev1.Containe Command: command, VolumeMounts: volumeMounts, } + if setSecurityContext { + prepareInitContainer.SecurityContext = securityContext + } return prepareInitContainer } // createResultsSidecar creates a sidecar that will run the sidecarlogresults binary. -func createResultsSidecar(taskSpec v1beta1.TaskSpec, image string) v1beta1.Sidecar { +func createResultsSidecar(taskSpec v1beta1.TaskSpec, image string, setSecurityContext, windows bool) v1beta1.Sidecar { names := make([]string, 0, len(taskSpec.Results)) for _, r := range taskSpec.Results { names = append(names, r.Name) } + securityContext := linuxSecurityContext + if windows { + securityContext = windowsSecurityContext + } resultsStr := strings.Join(names, ",") command := []string{"/ko-app/sidecarlogresults", "-results-dir", pipeline.DefaultResultPath, "-result-names", resultsStr} - return v1beta1.Sidecar{ + sidecar := v1beta1.Sidecar{ Name: pipeline.ReservedResultsSidecarName, Image: image, Command: command, } + if setSecurityContext { + sidecar.SecurityContext = securityContext + } + return sidecar +} + +func usesWindows(tr *v1beta1.TaskRun) bool { + if tr.Spec.PodTemplate == nil || tr.Spec.PodTemplate.NodeSelector == nil { + return false + } + osSelector := tr.Spec.PodTemplate.NodeSelector[osSelectorLabel] + return osSelector == "windows" } diff --git a/pkg/pod/pod_test.go b/pkg/pod/pod_test.go index a0acc9eb9b8..ad94f752026 100644 --- a/pkg/pod/pod_test.go +++ b/pkg/pod/pod_test.go @@ -109,7 +109,7 @@ func TestPodBuild(t *testing.T) { }, want: &corev1.PodSpec{ RestartPolicy: corev1.RestartPolicyNever, - InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}})}, + InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}}, false /* setSecurityContext */, false /* windows */)}, Containers: []corev1.Container{{ Name: "step-name", Image: "image", @@ -156,7 +156,7 @@ func TestPodBuild(t *testing.T) { }, want: &corev1.PodSpec{ RestartPolicy: corev1.RestartPolicyNever, - InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}})}, + InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}}, false /* setSecurityContext */, false /* windows */)}, Containers: []corev1.Container{{ Name: "step-name", Image: "image", @@ -201,7 +201,7 @@ func TestPodBuild(t *testing.T) { }, want: &corev1.PodSpec{ RestartPolicy: corev1.RestartPolicyNever, - InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}})}, + InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}}, false /* setSecurityContext */, false /* windows */)}, Containers: []corev1.Container{{ Name: "step-name", Image: "image", @@ -247,7 +247,7 @@ func TestPodBuild(t *testing.T) { want: &corev1.PodSpec{ ServiceAccountName: "service-account", RestartPolicy: corev1.RestartPolicyNever, - InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}})}, + InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}}, false /* setSecurityContext */, false /* windows */)}, Containers: []corev1.Container{{ Name: "step-name", Image: "image", @@ -314,7 +314,7 @@ func TestPodBuild(t *testing.T) { }, want: &corev1.PodSpec{ RestartPolicy: corev1.RestartPolicyNever, - InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}})}, + InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}}, false /* setSecurityContext */, false /* windows */)}, Containers: []corev1.Container{{ Name: "step-name", Image: "image", @@ -371,7 +371,7 @@ func TestPodBuild(t *testing.T) { }, want: &corev1.PodSpec{ RestartPolicy: corev1.RestartPolicyNever, - InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "a-very-very-long-character-step-name-to-trigger-max-len----and-invalid-characters"}})}, + InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "a-very-very-long-character-step-name-to-trigger-max-len----and-invalid-characters"}}, false /* setSecurityContext */, false /* windows */)}, Containers: []corev1.Container{{ Name: "step-a-very-very-long-character-step-name-to-trigger-max-len", // step name trimmed. Image: "image", @@ -413,7 +413,7 @@ func TestPodBuild(t *testing.T) { }, want: &corev1.PodSpec{ RestartPolicy: corev1.RestartPolicyNever, - InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "ends-with-invalid-%%__$$"}})}, + InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "ends-with-invalid-%%__$$"}}, false /* setSecurityContext */, false /* windows */)}, Containers: []corev1.Container{{ Name: "step-ends-with-invalid", // invalid suffix removed. Image: "image", @@ -457,7 +457,7 @@ func TestPodBuild(t *testing.T) { want: &corev1.PodSpec{ RestartPolicy: corev1.RestartPolicyNever, InitContainers: []corev1.Container{ - entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}}), + entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}}, false /* setSecurityContext */, false /* windows */), { Name: "working-dir-initializer", Image: images.WorkingDirInitImage, @@ -514,7 +514,7 @@ func TestPodBuild(t *testing.T) { wantAnnotations: map[string]string{}, want: &corev1.PodSpec{ RestartPolicy: corev1.RestartPolicyNever, - InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "primary-name"}})}, + InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "primary-name"}}, false /* setSecurityContext */, false /* windows */)}, Containers: []corev1.Container{{ Name: "step-primary-name", Image: "primary-image", @@ -569,7 +569,7 @@ func TestPodBuild(t *testing.T) { want: &corev1.PodSpec{ RestartPolicy: corev1.RestartPolicyNever, InitContainers: []corev1.Container{ - entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "primary-name"}}), + entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "primary-name"}}, false /* setSecurityContext */, false /* windows */), { Name: "place-scripts", Image: "busybox", @@ -638,7 +638,7 @@ _EOF_ wantAnnotations: map[string]string{}, // no ready annotations on pod create since sidecars are present want: &corev1.PodSpec{ RestartPolicy: corev1.RestartPolicyNever, - InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "primary-name"}})}, + InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "primary-name"}}, false /* setSecurityContext */, false /* windows */)}, Containers: []corev1.Container{{ Name: "step-primary-name", Image: "primary-image", @@ -700,7 +700,7 @@ _EOF_ InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{ {Name: "unnamed-0"}, {Name: "unnamed-1"}, - })}, + }, false /* setSecurityContext */, false /* windows */)}, Containers: []corev1.Container{{ Name: "step-unnamed-0", Image: "image", @@ -798,7 +798,7 @@ _EOF_ RestartPolicy: corev1.RestartPolicyNever, InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{ {Name: "step1"}, - })}, + }, false /* setSecurityContext */, false /* windows */)}, Containers: []corev1.Container{{ Name: "step-step1", Image: "image", @@ -867,7 +867,7 @@ _EOF_ RestartPolicy: corev1.RestartPolicyNever, InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{ {Name: "step1"}, - })}, + }, false /* setSecurityContext */, false /* windows */)}, Containers: []corev1.Container{{ Name: "step-step1", Image: "image", @@ -937,7 +937,7 @@ _EOF_ wantAnnotations: map[string]string{}, want: &corev1.PodSpec{ RestartPolicy: corev1.RestartPolicyNever, - InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "primary-name"}})}, + InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "primary-name"}}, false /* setSecurityContext */, false /* windows */)}, Containers: []corev1.Container{{ Name: "step-primary-name", Image: "primary-image", @@ -1007,7 +1007,7 @@ print("Hello from Python")`, {Name: "one"}, {Name: "two"}, {Name: "regular-step"}, - }), + }, false /* setSecurityContext */, false /* windows */), { Name: "place-scripts", Image: images.ShellImage, @@ -1129,7 +1129,7 @@ _EOF_ }, want: &corev1.PodSpec{ RestartPolicy: corev1.RestartPolicyNever, - InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "one"}}), + InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "one"}}, false /* setSecurityContext */, false /* windows */), { Name: "place-scripts", Image: images.ShellImage, @@ -1192,7 +1192,7 @@ _EOF_ }, want: &corev1.PodSpec{ RestartPolicy: corev1.RestartPolicyNever, - InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "schedule-me"}})}, + InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "schedule-me"}}, false /* setSecurityContext */, false /* windows */)}, SchedulerName: "there-scheduler", Volumes: append(implicitVolumes, binVolume, runVolume(0), downwardVolume, corev1.Volume{ Name: "tekton-creds-init-home-0", @@ -1243,7 +1243,7 @@ _EOF_ }, want: &corev1.PodSpec{ RestartPolicy: corev1.RestartPolicyNever, - InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "image-pull"}})}, + InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "image-pull"}}, false /* setSecurityContext */, false /* windows */)}, Volumes: append(implicitVolumes, binVolume, runVolume(0), downwardVolume, corev1.Volume{ Name: "tekton-creds-init-home-0", VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{Medium: corev1.StorageMediumMemory}}, @@ -1293,7 +1293,7 @@ _EOF_ }, want: &corev1.PodSpec{ RestartPolicy: corev1.RestartPolicyNever, - InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "host-aliases"}})}, + InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "host-aliases"}}, false /* setSecurityContext */, false /* windows */)}, Volumes: append(implicitVolumes, binVolume, runVolume(0), downwardVolume, corev1.Volume{ Name: "tekton-creds-init-home-0", VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{Medium: corev1.StorageMediumMemory}}, @@ -1342,7 +1342,7 @@ _EOF_ }, want: &corev1.PodSpec{ RestartPolicy: corev1.RestartPolicyNever, - InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "use-my-hostNetwork"}})}, + InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "use-my-hostNetwork"}}, false /* setSecurityContext */, false /* windows */)}, HostNetwork: true, Volumes: append(implicitVolumes, binVolume, runVolume(0), downwardVolume, corev1.Volume{ Name: "tekton-creds-init-home-0", @@ -1386,7 +1386,7 @@ _EOF_ }, want: &corev1.PodSpec{ RestartPolicy: corev1.RestartPolicyNever, - InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}})}, + InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}}, false /* setSecurityContext */, false /* windows */)}, Containers: []corev1.Container{{ Name: "step-name", Image: "image", @@ -1434,7 +1434,7 @@ _EOF_ }, want: &corev1.PodSpec{ RestartPolicy: corev1.RestartPolicyNever, - InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}})}, + InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}}, false /* setSecurityContext */, false /* windows */)}, Containers: []corev1.Container{{ Name: "step-name", Image: "image", @@ -1481,7 +1481,7 @@ _EOF_ }, want: &corev1.PodSpec{ RestartPolicy: corev1.RestartPolicyNever, - InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}})}, + InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}}, false /* setSecurityContext */, false /* windows */)}, Containers: []corev1.Container{{ Name: "step-name", Image: "image", @@ -1526,7 +1526,7 @@ _EOF_ }, want: &corev1.PodSpec{ RestartPolicy: corev1.RestartPolicyNever, - InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}})}, + InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}}, false /* setSecurityContext */, false /* windows */)}, Containers: []corev1.Container{{ Name: "step-name", Image: "image", @@ -1579,7 +1579,7 @@ _EOF_ }, want: &corev1.PodSpec{ RestartPolicy: corev1.RestartPolicyNever, - InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}})}, + InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}}, false /* setSecurityContext */, false /* windows */)}, Containers: []corev1.Container{{ Name: "step-name", Image: "image", @@ -1630,7 +1630,7 @@ _EOF_ }, want: &corev1.PodSpec{ RestartPolicy: corev1.RestartPolicyNever, - InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}})}, + InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}}, false /* setSecurityContext */, false /* windows */)}, Containers: []corev1.Container{{ Name: "step-name", Image: "image", @@ -1680,7 +1680,7 @@ _EOF_ }, want: &corev1.PodSpec{ RestartPolicy: corev1.RestartPolicyNever, - InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}})}, + InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}}, false /* setSecurityContext */, false /* windows */)}, Containers: []corev1.Container{{ Name: "step-name", Image: "image", @@ -1746,7 +1746,7 @@ _EOF_ }, want: &corev1.PodSpec{ RestartPolicy: corev1.RestartPolicyNever, - InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}})}, + InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}}, false /* setSecurityContext */, false /* windows */)}, Containers: []corev1.Container{{ Name: "step-name", Image: "image", @@ -1791,7 +1791,7 @@ _EOF_ wantPodName: "task-run-0123456789-01234560d38957287bb0283c59440df14069f59-pod", want: &corev1.PodSpec{ RestartPolicy: corev1.RestartPolicyNever, - InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}})}, + InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}}, false /* setSecurityContext */, false /* windows */)}, Containers: []corev1.Container{{ Name: "step-name", Image: "image", @@ -1851,7 +1851,7 @@ _EOF_ }, want: &corev1.PodSpec{ RestartPolicy: corev1.RestartPolicyNever, - InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "use-topologySpreadConstraints"}})}, + InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "use-topologySpreadConstraints"}}, false /* setSecurityContext */, false /* windows */)}, TopologySpreadConstraints: []corev1.TopologySpreadConstraint{ { MaxSkew: 1, @@ -1911,7 +1911,7 @@ _EOF_ want: &corev1.PodSpec{ RestartPolicy: corev1.RestartPolicyNever, InitContainers: []corev1.Container{ - entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}}), + entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}}, false /* setSecurityContext */, false /* windows */), }, Containers: []corev1.Container{{ Name: "step-name", @@ -1964,6 +1964,120 @@ _EOF_ }), ActiveDeadlineSeconds: &defaultActiveDeadlineSeconds, }, + }, { + desc: "sidecar logs enabled with security context", + featureFlags: map[string]string{"results-from": "sidecar-logs", "set-security-context": "true"}, + ts: v1beta1.TaskSpec{ + Results: []v1beta1.TaskResult{{ + Name: "foo", + Type: v1beta1.ResultsTypeString, + }}, + Steps: []v1beta1.Step{{ + Name: "name", + Image: "image", + Command: []string{"cmd"}, // avoid entrypoint lookup. + }}, + }, + want: &corev1.PodSpec{ + RestartPolicy: corev1.RestartPolicyNever, + InitContainers: []corev1.Container{ + entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}}, true /* setSecurityContext */, false /* windows */), + }, + Containers: []corev1.Container{{ + Name: "step-name", + Image: "image", + Command: []string{"/tekton/bin/entrypoint"}, + Args: []string{ + "-wait_file", + "/tekton/downward/ready", + "-wait_file_content", + "-post_file", + "/tekton/run/0/out", + "-termination_path", + "/tekton/termination", + "-step_metadata_dir", + "/tekton/run/0/status", + "-result_from", + "sidecar-logs", + "-results", + "foo", + "-entrypoint", + "cmd", + "--", + }, + VolumeMounts: append([]corev1.VolumeMount{binROMount, runMount(0, false), downwardMount, { + Name: "tekton-creds-init-home-0", + MountPath: "/tekton/creds", + }}, implicitVolumeMounts...), + TerminationMessagePath: "/tekton/termination", + }, { + Name: pipeline.ReservedResultsSidecarContainerName, + Image: "", + Command: []string{ + "/ko-app/sidecarlogresults", + "-results-dir", + "/tekton/results", + "-result-names", + "foo", + }, + Resources: corev1.ResourceRequirements{ + Requests: nil, + }, + VolumeMounts: append([]v1.VolumeMount{ + {Name: "tekton-internal-bin", ReadOnly: true, MountPath: "/tekton/bin"}, + {Name: "tekton-internal-run-0", ReadOnly: true, MountPath: "/tekton/run/0"}, + }, implicitVolumeMounts...), + SecurityContext: linuxSecurityContext, + }}, + Volumes: append(implicitVolumes, binVolume, runVolume(0), downwardVolume, corev1.Volume{ + Name: "tekton-creds-init-home-0", + VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{Medium: corev1.StorageMediumMemory}}, + }), + ActiveDeadlineSeconds: &defaultActiveDeadlineSeconds, + }, + }, { + desc: "simple with security context", + ts: v1beta1.TaskSpec{ + Steps: []v1beta1.Step{{ + Name: "name", + Image: "image", + Command: []string{"cmd"}, // avoid entrypoint lookup. + }}, + }, + featureFlags: map[string]string{"set-security-context": "true"}, + want: &corev1.PodSpec{ + RestartPolicy: corev1.RestartPolicyNever, + InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}}, true /* setSecurityContext */, false /* windows */)}, + Containers: []corev1.Container{{ + Name: "step-name", + Image: "image", + Command: []string{"/tekton/bin/entrypoint"}, + Args: []string{ + "-wait_file", + "/tekton/downward/ready", + "-wait_file_content", + "-post_file", + "/tekton/run/0/out", + "-termination_path", + "/tekton/termination", + "-step_metadata_dir", + "/tekton/run/0/status", + "-entrypoint", + "cmd", + "--", + }, + VolumeMounts: append([]corev1.VolumeMount{downwardMount, { + Name: "tekton-creds-init-home-0", + MountPath: "/tekton/creds", + }, runMount(0, false), binROMount}, implicitVolumeMounts...), + TerminationMessagePath: "/tekton/termination", + }}, + Volumes: append(implicitVolumes, binVolume, downwardVolume, corev1.Volume{ + Name: "tekton-creds-init-home-0", + VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{Medium: corev1.StorageMediumMemory}}, + }, runVolume(0)), + ActiveDeadlineSeconds: &defaultActiveDeadlineSeconds, + }, }} { t.Run(c.desc, func(t *testing.T) { names.TestingSeed() @@ -2146,7 +2260,7 @@ debug-fail-continue-heredoc-randomly-generated-mz4c7 }, want: &corev1.PodSpec{ RestartPolicy: corev1.RestartPolicyNever, - InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}}), placeScriptsContainer}, + InitContainers: []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}}, false /* setSecurityContext */, false /* windows */), placeScriptsContainer}, Containers: []corev1.Container{{ Name: "step-name", Image: "image", @@ -2457,7 +2571,7 @@ func TestPodBuild_TaskLevelResourceRequirements(t *testing.T) { } func TestPodBuildwithSpireEnabled(t *testing.T) { - initContainers := []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}})} + initContainers := []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}}, false /* setSecurityContext */, false /* windows */)} readonly := true for i := range initContainers { c := &initContainers[i] @@ -2759,10 +2873,12 @@ func TestIsPodReadyImmediately(t *testing.T) { func TestPrepareInitContainers(t *testing.T) { tcs := []struct { - name string - steps []v1beta1.Step - want corev1.Container - featureFlags map[string]string + name string + steps []v1beta1.Step + windows bool + setSecurityContext bool + want corev1.Container + featureFlags map[string]string }{{ name: "nothing-special", steps: []v1beta1.Step{{ @@ -2789,13 +2905,97 @@ func TestPrepareInitContainers(t *testing.T) { Command: []string{"/ko-app/entrypoint", "init", "/ko-app/entrypoint", entrypointBinary, "step-foo", "step-bar"}, VolumeMounts: []corev1.VolumeMount{binMount, internalStepsMount}, }, + }, { + name: "nothing-special-two-steps-security-context", + steps: []v1beta1.Step{{ + Name: "foo", + }, { + Name: "bar", + }}, + setSecurityContext: true, + want: corev1.Container{ + Name: "prepare", + Image: images.EntrypointImage, + WorkingDir: "/", + Command: []string{"/ko-app/entrypoint", "init", "/ko-app/entrypoint", entrypointBinary, "step-foo", "step-bar"}, + VolumeMounts: []corev1.VolumeMount{binMount, internalStepsMount}, + SecurityContext: linuxSecurityContext, + }, + }, { + name: "nothing-special-two-steps-windows", + steps: []v1beta1.Step{{ + Name: "foo", + }, { + Name: "bar", + }}, + windows: true, + want: corev1.Container{ + Name: "prepare", + Image: images.EntrypointImage, + WorkingDir: "/", + Command: []string{"/ko-app/entrypoint", "init", "/ko-app/entrypoint", entrypointBinary, "step-foo", "step-bar"}, + VolumeMounts: []corev1.VolumeMount{binMount, internalStepsMount}, + }, + }, { + name: "nothing-special-two-steps-windows-security-context", + steps: []v1beta1.Step{{ + Name: "foo", + }, { + Name: "bar", + }}, + setSecurityContext: true, + windows: true, + want: corev1.Container{ + Name: "prepare", + Image: images.EntrypointImage, + WorkingDir: "/", + Command: []string{"/ko-app/entrypoint", "init", "/ko-app/entrypoint", entrypointBinary, "step-foo", "step-bar"}, + VolumeMounts: []corev1.VolumeMount{binMount, internalStepsMount}, + SecurityContext: windowsSecurityContext, + }, }} for _, tc := range tcs { t.Run(tc.name, func(t *testing.T) { - container := entrypointInitContainer(images.EntrypointImage, tc.steps) + container := entrypointInitContainer(images.EntrypointImage, tc.steps, tc.setSecurityContext, tc.windows) if d := cmp.Diff(tc.want, container); d != "" { t.Errorf("Diff %s", diff.PrintWantGot(d)) } }) } } + +func TestUsesWindows(t *testing.T) { + tcs := []struct { + name string + taskRun *v1beta1.TaskRun + want bool + }{{ + name: "no pod template", + taskRun: &v1beta1.TaskRun{Spec: v1beta1.TaskRunSpec{}}, + want: false, + }, { + name: "pod template w/out node selector", + taskRun: &v1beta1.TaskRun{Spec: v1beta1.TaskRunSpec{PodTemplate: &pod.Template{Env: []corev1.EnvVar{{Name: "foo", Value: "bar"}}}}}, + want: false, + }, { + name: "uses linux", + taskRun: &v1beta1.TaskRun{Spec: v1beta1.TaskRunSpec{PodTemplate: &pod.Template{NodeSelector: map[string]string{ + osSelectorLabel: "linux", + }}}}, + want: false, + }, { + name: "uses windows", + taskRun: &v1beta1.TaskRun{Spec: v1beta1.TaskRunSpec{PodTemplate: &pod.Template{NodeSelector: map[string]string{ + osSelectorLabel: "windows", + }}}}, + want: true, + }} + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + got := usesWindows(tc.taskRun) + if tc.want != got { + t.Errorf("wanted usesWindows to be %t but was %t", tc.want, got) + } + }) + } +} diff --git a/pkg/pod/script.go b/pkg/pod/script.go index a984cd72ca8..87edd1b5296 100644 --- a/pkg/pod/script.go +++ b/pkg/pod/script.go @@ -69,7 +69,7 @@ var ( ) // convertScripts converts any steps and sidecars that specify a Script field into a normal Container. -func convertScripts(shellImageLinux string, shellImageWin string, steps []v1beta1.Step, sidecars []v1beta1.Sidecar, debugConfig *v1beta1.TaskRunDebug) (*corev1.Container, []corev1.Container, []corev1.Container) { +func convertScripts(shellImageLinux string, shellImageWin string, steps []v1beta1.Step, sidecars []v1beta1.Sidecar, debugConfig *v1beta1.TaskRunDebug, setSecurityContext bool) (*corev1.Container, []corev1.Container, []corev1.Container) { // Place scripts is an init container used for creating scripts in the // /tekton/scripts directory which would be later used by the step containers // as a Command @@ -78,11 +78,13 @@ func convertScripts(shellImageLinux string, shellImageWin string, steps []v1beta shellImage := shellImageLinux shellCommand := "sh" shellArg := "-c" + securityContext := linuxSecurityContext // Set windows variants for Image, Command and Args if requiresWindows { shellImage = shellImageWin shellCommand = "pwsh" shellArg = "-Command" + securityContext = windowsSecurityContext } placeScriptsInit := corev1.Container{ @@ -92,6 +94,9 @@ func convertScripts(shellImageLinux string, shellImageWin string, steps []v1beta Args: []string{shellArg, ""}, VolumeMounts: []corev1.VolumeMount{writeScriptsVolumeMount, binMount}, } + if setSecurityContext { + placeScriptsInit.SecurityContext = securityContext + } breakpoints := []string{} diff --git a/pkg/pod/script_test.go b/pkg/pod/script_test.go index 3f9f8d0be08..99db1dbdbd9 100644 --- a/pkg/pod/script_test.go +++ b/pkg/pod/script_test.go @@ -31,7 +31,7 @@ func TestConvertScripts_NothingToConvert_EmptySidecars(t *testing.T) { Image: "step-1", }, { Image: "step-2", - }}, []v1beta1.Sidecar{}, nil) + }}, []v1beta1.Sidecar{}, nil, false) want := []corev1.Container{{ Image: "step-1", }, { @@ -54,7 +54,7 @@ func TestConvertScripts_NothingToConvert_NilSidecars(t *testing.T) { Image: "step-1", }, { Image: "step-2", - }}, nil, nil) + }}, nil, nil, false) want := []corev1.Container{{ Image: "step-1", }, { @@ -79,7 +79,7 @@ func TestConvertScripts_NothingToConvert_WithSidecar(t *testing.T) { Image: "step-2", }}, []v1beta1.Sidecar{{ Image: "sidecar-1", - }}, nil) + }}, nil, false) want := []corev1.Container{{ Image: "step-1", }, { @@ -107,7 +107,6 @@ func TestConvertScripts_NothingToConvert_WithSidecar(t *testing.T) { func TestConvertScripts_Steps(t *testing.T) { names.TestingSeed() - preExistingVolumeMounts := []corev1.VolumeMount{{ Name: "pre-existing-volume-mount", MountPath: "/mount/path", @@ -135,7 +134,7 @@ script-3`, Image: "step-3", VolumeMounts: preExistingVolumeMounts, Args: []string{"my", "args"}, - }}, []v1beta1.Sidecar{}, nil) + }}, []v1beta1.Sidecar{}, nil, true) wantInit := &corev1.Container{ Name: "place-scripts", Image: images.ShellImage, @@ -159,7 +158,8 @@ IyEvYmluL3NoCnNldCAtZQpuby1zaGViYW5n _EOF_ /tekton/bin/entrypoint decode-script "${scriptfile}" `}, - VolumeMounts: []corev1.VolumeMount{writeScriptsVolumeMount, binMount}, + VolumeMounts: []corev1.VolumeMount{writeScriptsVolumeMount, binMount}, + SecurityContext: linuxSecurityContext, } want := []corev1.Container{{ Image: "step-1", @@ -335,7 +335,7 @@ _EOF_ }}, }} { t.Run(tc.name, func(t *testing.T) { - gotInit, gotSteps, gotSidecars := convertScripts(images.ShellImage, images.ShellImageWin, []v1beta1.Step{}, tc.sidecars, nil) + gotInit, gotSteps, gotSidecars := convertScripts(images.ShellImage, images.ShellImageWin, []v1beta1.Step{}, tc.sidecars, nil, false) gotInitScripts := "" if gotInit != nil { gotInitScripts = gotInit.Args[1] @@ -385,7 +385,7 @@ script-3`, Args: []string{"my", "args"}, }}, []v1beta1.Sidecar{}, &v1beta1.TaskRunDebug{ Breakpoint: []string{breakpointOnFailure}, - }) + }, true) wantInit := &corev1.Container{ Name: "place-scripts", @@ -454,7 +454,8 @@ else fi debug-fail-continue-heredoc-randomly-generated-6nl7g `}, - VolumeMounts: []corev1.VolumeMount{writeScriptsVolumeMount, binMount, debugScriptsVolumeMount}, + VolumeMounts: []corev1.VolumeMount{writeScriptsVolumeMount, binMount, debugScriptsVolumeMount}, + SecurityContext: linuxSecurityContext, } want := []corev1.Container{{ @@ -527,7 +528,7 @@ script-3`, Script: `#!/bin/sh sidecar-1`, Image: "sidecar-1", - }}, nil) + }}, nil, true) wantInit := &corev1.Container{ Name: "place-scripts", Image: images.ShellImage, @@ -551,7 +552,8 @@ IyEvYmluL3NoCnNpZGVjYXItMQ== _EOF_ /tekton/bin/entrypoint decode-script "${scriptfile}" `}, - VolumeMounts: []corev1.VolumeMount{writeScriptsVolumeMount, binMount}, + VolumeMounts: []corev1.VolumeMount{writeScriptsVolumeMount, binMount}, + SecurityContext: linuxSecurityContext, } want := []corev1.Container{{ Image: "step-1", @@ -620,7 +622,7 @@ no-shebang`, Image: "step-3", VolumeMounts: preExistingVolumeMounts, Args: []string{"my", "args"}, - }}, []v1beta1.Sidecar{}, nil) + }}, []v1beta1.Sidecar{}, nil, true) wantInit := &corev1.Container{ Name: "place-scripts", Image: images.ShellImageWin, @@ -637,7 +639,8 @@ script-3 no-shebang "@ | Out-File -FilePath /tekton/scripts/script-3-mssqb.cmd `}, - VolumeMounts: []corev1.VolumeMount{writeScriptsVolumeMount, binMount}, + VolumeMounts: []corev1.VolumeMount{writeScriptsVolumeMount, binMount}, + SecurityContext: windowsSecurityContext, } want := []corev1.Container{{ Image: "step-1", @@ -701,7 +704,7 @@ script-3`, Script: `#!win pwsh -File sidecar-1`, Image: "sidecar-1", - }}, nil) + }}, nil, true) wantInit := &corev1.Container{ Name: "place-scripts", Image: images.ShellImageWin, @@ -719,7 +722,8 @@ script-3 sidecar-1 "@ | Out-File -FilePath /tekton/scripts/sidecar-script-0-mssqb `}, - VolumeMounts: []corev1.VolumeMount{writeScriptsVolumeMount, binMount}, + VolumeMounts: []corev1.VolumeMount{writeScriptsVolumeMount, binMount}, + SecurityContext: windowsSecurityContext, } want := []corev1.Container{{ Image: "step-1", @@ -770,7 +774,7 @@ func TestConvertScripts_Windows_SidecarOnly(t *testing.T) { Script: `#!win python sidecar-1`, Image: "sidecar-1", - }}, nil) + }}, nil, true) wantInit := &corev1.Container{ Name: "place-scripts", Image: images.ShellImageWin, @@ -780,7 +784,8 @@ sidecar-1`, sidecar-1 "@ | Out-File -FilePath /tekton/scripts/sidecar-script-0-9l9zj `}, - VolumeMounts: []corev1.VolumeMount{writeScriptsVolumeMount, binMount}, + VolumeMounts: []corev1.VolumeMount{writeScriptsVolumeMount, binMount}, + SecurityContext: windowsSecurityContext, } want := []corev1.Container{{ Image: "step-1", diff --git a/pkg/pod/workingdir_init.go b/pkg/pod/workingdir_init.go index 6a32c7cc787..ff369b4bc52 100644 --- a/pkg/pod/workingdir_init.go +++ b/pkg/pod/workingdir_init.go @@ -31,7 +31,7 @@ import ( // // If no such directories need to be created (i.e., no relative workingDirs // are specified), this method returns nil, as no init container is necessary. -func workingDirInit(workingdirinitImage string, stepContainers []corev1.Container) *corev1.Container { +func workingDirInit(workingdirinitImage string, stepContainers []corev1.Container, setSecurityContext, windows bool) *corev1.Container { // Gather all unique workingDirs. workingDirs := sets.NewString() for _, step := range stepContainers { @@ -56,8 +56,12 @@ func workingDirInit(workingdirinitImage string, stepContainers []corev1.Containe // There are no workingDirs to initialize. return nil } + securityContext := linuxSecurityContext + if windows { + securityContext = windowsSecurityContext + } - return &corev1.Container{ + c := &corev1.Container{ Name: "working-dir-initializer", Image: workingdirinitImage, Command: []string{"/ko-app/workingdirinit"}, @@ -65,4 +69,8 @@ func workingDirInit(workingdirinitImage string, stepContainers []corev1.Containe WorkingDir: pipeline.WorkspaceDir, VolumeMounts: implicitVolumeMounts, } + if setSecurityContext { + c.SecurityContext = securityContext + } + return c } diff --git a/pkg/pod/workingdir_init_test.go b/pkg/pod/workingdir_init_test.go index b3844da1558..05f1f65f133 100644 --- a/pkg/pod/workingdir_init_test.go +++ b/pkg/pod/workingdir_init_test.go @@ -29,9 +29,11 @@ import ( func TestWorkingDirInit(t *testing.T) { names.TestingSeed() for _, c := range []struct { - desc string - stepContainers []corev1.Container - want *corev1.Container + desc string + stepContainers []corev1.Container + windows bool + setSecurityContext bool + want *corev1.Container }{{ desc: "no workingDirs", stepContainers: []corev1.Container{{ @@ -63,9 +65,90 @@ func TestWorkingDirInit(t *testing.T) { WorkingDir: pipeline.WorkspaceDir, VolumeMounts: implicitVolumeMounts, }, + }, { + desc: "workingDirs are unique and sorted, absolute dirs are ignored, + securitycontext", + stepContainers: []corev1.Container{{ + WorkingDir: "zzz", + }, { + WorkingDir: "aaa", + }, { + WorkingDir: "/ignored", + }, { + WorkingDir: "/workspace", // ignored + }, { + WorkingDir: "zzz", + }, { + // Even though it's specified absolute, it's relative + // to /workspace, so we need to create it. + WorkingDir: "/workspace/bbb", + }}, + setSecurityContext: true, + want: &corev1.Container{ + Name: "working-dir-initializer", + Image: images.WorkingDirInitImage, + Command: []string{"/ko-app/workingdirinit"}, + Args: []string{"/workspace/bbb", "aaa", "zzz"}, + WorkingDir: pipeline.WorkspaceDir, + VolumeMounts: implicitVolumeMounts, + SecurityContext: linuxSecurityContext, + }, + }, { + desc: "workingDirs are unique and sorted, absolute dirs are ignored, uses windows", + stepContainers: []corev1.Container{{ + WorkingDir: "zzz", + }, { + WorkingDir: "aaa", + }, { + WorkingDir: "/ignored", + }, { + WorkingDir: "/workspace", // ignored + }, { + WorkingDir: "zzz", + }, { + // Even though it's specified absolute, it's relative + // to /workspace, so we need to create it. + WorkingDir: "/workspace/bbb", + }}, + windows: true, + want: &corev1.Container{ + Name: "working-dir-initializer", + Image: images.WorkingDirInitImage, + Command: []string{"/ko-app/workingdirinit"}, + Args: []string{"/workspace/bbb", "aaa", "zzz"}, + WorkingDir: pipeline.WorkspaceDir, + VolumeMounts: implicitVolumeMounts, + }, + }, { + desc: "workingDirs are unique and sorted, absolute dirs are ignored, uses windows, + securityContext", + stepContainers: []corev1.Container{{ + WorkingDir: "zzz", + }, { + WorkingDir: "aaa", + }, { + WorkingDir: "/ignored", + }, { + WorkingDir: "/workspace", // ignored + }, { + WorkingDir: "zzz", + }, { + // Even though it's specified absolute, it's relative + // to /workspace, so we need to create it. + WorkingDir: "/workspace/bbb", + }}, + windows: true, + setSecurityContext: true, + want: &corev1.Container{ + Name: "working-dir-initializer", + Image: images.WorkingDirInitImage, + Command: []string{"/ko-app/workingdirinit"}, + Args: []string{"/workspace/bbb", "aaa", "zzz"}, + WorkingDir: pipeline.WorkspaceDir, + VolumeMounts: implicitVolumeMounts, + SecurityContext: windowsSecurityContext, + }, }} { t.Run(c.desc, func(t *testing.T) { - got := workingDirInit(images.WorkingDirInitImage, c.stepContainers) + got := workingDirInit(images.WorkingDirInitImage, c.stepContainers, c.setSecurityContext, c.windows) if d := cmp.Diff(c.want, got); d != "" { t.Fatalf("Diff %s", diff.PrintWantGot(d)) }