diff --git a/pkg/apis/build/v1alpha2/build_pod.go b/pkg/apis/build/v1alpha2/build_pod.go index 3a0ead662..3a2ab2acb 100644 --- a/pkg/apis/build/v1alpha2/build_pod.go +++ b/pkg/apis/build/v1alpha2/build_pod.go @@ -645,6 +645,10 @@ func boolPointer(b bool) *bool { return &b } +func intPointer(i int64) *int64 { + return &i +} + func containerSecurityContext(config BuildPodBuilderConfig) *corev1.SecurityContext { if config.OS == "windows" { return nil @@ -739,17 +743,16 @@ func (b *Build) useImageExtensions(pod *corev1.Pod) { container.VolumeMounts = append(container.VolumeMounts, kanikoMount) container.Args = append(container.Args, fmt.Sprintf("-build-image=%s", b.Spec.Builder.Image)) case BuildContainerName: - runAsNonRoot := false - rootUser := int64(0) container.Name = ExtendContainerName container.Command = []string{"/cnb/lifecycle/extender"} container.VolumeMounts = append(container.VolumeMounts, kanikoMount) - container.SecurityContext.RunAsNonRoot = &runAsNonRoot - container.SecurityContext.RunAsUser = &rootUser + container.SecurityContext.RunAsUser = intPointer(0) + container.SecurityContext.RunAsGroup = intPointer(0) + container.SecurityContext.RunAsNonRoot = boolPointer(false) + container.SecurityContext.Capabilities = &corev1.Capabilities{Add: []corev1.Capability{"SETGID", "SETUID"}} // TODO: check if this is needed if not using kind } pod.Spec.InitContainers[idx] = container } - } func (b *Build) useStandardContainers(buildWaiterImage string, pod *corev1.Pod) *corev1.Pod { diff --git a/pkg/apis/build/v1alpha2/build_pod_test.go b/pkg/apis/build/v1alpha2/build_pod_test.go index e89ee8d10..9044da036 100644 --- a/pkg/apis/build/v1alpha2/build_pod_test.go +++ b/pkg/apis/build/v1alpha2/build_pod_test.go @@ -2520,15 +2520,22 @@ func testBuildPod(t *testing.T, when spec.G, it spec.S) { assert.Equal(t, []string{"/cnb/lifecycle/extender"}, pod.Spec.InitContainers[4].Command) for _, container := range pod.Spec.InitContainers { + // every phase should be unprivileged + actualPrivileged := container.SecurityContext.Privileged + assert.Equal(t, false, *actualPrivileged) + // extend phase should run as root actualRunAsNonRoot := container.SecurityContext.RunAsNonRoot actualRunAsUser := container.SecurityContext.RunAsUser + actualRunAsGroup := container.SecurityContext.RunAsGroup switch container.Name { case buildapi.ExtendContainerName: assert.Equal(t, false, *actualRunAsNonRoot) assert.Equal(t, int64(0), *actualRunAsUser) + assert.Equal(t, int64(0), *actualRunAsGroup) default: assert.Equal(t, true, *actualRunAsNonRoot) - assert.NotEqual(t, nil, actualRunAsUser) // in real life this would be the user from the builder + assert.NotEqual(t, nil, actualRunAsUser) // in real life this would be the uid from the builder + assert.NotEqual(t, nil, actualRunAsGroup) // in real life this would be the gid from the builder } } }) diff --git a/test/execute_build_test.go b/test/execute_build_test.go index 3cad5ddf6..4da628868 100644 --- a/test/execute_build_test.go +++ b/test/execute_build_test.go @@ -135,6 +135,7 @@ func testCreateImage(t *testing.T, when spec.G, it spec.S) { require.NoError(t, err) expectImage := func(t *testing.T, image v1.Image) {} + expectLogs := func(t *testing.T, logs string) {} if builderHasExtensions { expectImage = func(t *testing.T, image v1.Image) { cfg, err := image.ConfigFile() @@ -143,11 +144,15 @@ func testCreateImage(t *testing.T, when spec.G, it spec.S) { require.True(t, ok) var lifecycleMD files.LayersMetadata require.NoError(t, json.Unmarshal([]byte(lifecycleMDLabel), &lifecycleMD)) - require.Equal(t, "gcr.io/paketo-buildpacks/run-jammy-tiny", lifecycleMD.Stack.RunImage.Image) + runImageReference := lifecycleMD.RunImage.Reference + require.Contains(t, runImageReference, "gcr.io/paketo-buildpacks/run-jammy-tiny") + } + expectLogs = func(t *testing.T, logs string) { + require.Contains(t, logs, "Setting up curl") } } - builtImages[validateImageCreate(t, clients, image, expectedResources, expectImage)] = struct{}{} + builtImages[validateImageCreate(t, clients, image, expectedResources, expectImage, expectLogs)] = struct{}{} validateRebase(t, ctx, clients, image.Name, testNamespace) }) } @@ -881,7 +886,7 @@ func generateRebuild(ctx *context.Context, t *testing.T, cfg config, clients *cl }, metav1.CreateOptions{}) require.NoError(t, err) - originalImageTag := validateImageCreate(t, clients, image, expectedResources, func(t *testing.T, image v1.Image) {}) + originalImageTag := validateImageCreate(t, clients, image, expectedResources, func(t *testing.T, image v1.Image) {}, func(t *testing.T, logs string) {}) list, err := clients.client.KpackV1alpha2().Builds(testNamespace).List(*ctx, metav1.ListOptions{ LabelSelector: fmt.Sprintf("image.kpack.io/image=%s", imageName), @@ -902,7 +907,7 @@ func generateRebuild(ctx *context.Context, t *testing.T, cfg config, clients *cl return len(list.Items) == 2 }, 5*time.Second, 1*time.Minute) - rebuiltImageTag := validateImageCreate(t, clients, image, expectedResources, func(t *testing.T, image v1.Image) {}) + rebuiltImageTag := validateImageCreate(t, clients, image, expectedResources, func(t *testing.T, image v1.Image) {}, func(t *testing.T, logs string) {}) require.Equal(t, originalImageTag, rebuiltImageTag) return originalImageTag @@ -928,11 +933,11 @@ func readNamespaceLabelsFromEnv() map[string]string { func waitUntilReady(t *testing.T, ctx context.Context, clients *clients, objects ...kmeta.OwnerRefable) { for _, ob := range objects { namespace := ob.GetObjectMeta().GetNamespace() - name := ob.GetObjectMeta().GetName() + imageName := ob.GetObjectMeta().GetName() gvr, _ := meta.UnsafeGuessKindToResource(ob.GetGroupVersionKind()) eventually(t, func() bool { - unstructured, err := clients.dynamicClient.Resource(gvr).Namespace(namespace).Get(ctx, name, metav1.GetOptions{}) + unstructured, err := clients.dynamicClient.Resource(gvr).Namespace(namespace).Get(ctx, imageName, metav1.GetOptions{}) require.NoError(t, err) kResource := &duckv1.KResource{} @@ -944,13 +949,7 @@ func waitUntilReady(t *testing.T, ctx context.Context, clients *clients, objects } } -func validateImageCreate( - t *testing.T, - clients *clients, - image *buildapi.Image, - expectedResources corev1.ResourceRequirements, // TODO: this seems to no longer be used? - expectImage func(*testing.T, v1.Image), -) string { +func validateImageCreate(t *testing.T, clients *clients, image *buildapi.Image, expectedResources corev1.ResourceRequirements, expectImage func(*testing.T, v1.Image), expectLogs func(*testing.T, string)) string { ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -971,8 +970,8 @@ func validateImageCreate( return strings.Contains(logTail.String(), "Build successful") }, 1*time.Second, 10*time.Second) - // TODO: expect extend build image with kaniko expectImage(t, builtImage) + expectLogs(t, logTail.String()) buildList, err := clients.client.KpackV1alpha2().Builds(image.Namespace).List(ctx, metav1.ListOptions{ LabelSelector: fmt.Sprintf("image.kpack.io/image=%s", image.Name),