Skip to content

Commit dfa1778

Browse files
jagathprakashtekton-robot
authored andcommitted
[TEP-0089] SPIRE for non-falsifiable provenance.
This PR is a part of a larger set of PRs to provide non-falsifiable provenance through SPIRE. In particular this PR makes changes to the pod created to run a taskrun. This pod needs access to SpireApi which is mounted as a CSI volume into the pod. Signed-off-by: jagathprakash <31057312+jagathprakash@users.noreply.github.com>
1 parent 3d6ec63 commit dfa1778

File tree

2 files changed

+203
-0
lines changed

2 files changed

+203
-0
lines changed

pkg/pod/pod.go

+41
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
3232
"github.com/tektoncd/pipeline/pkg/internal/computeresources/tasklevel"
3333
"github.com/tektoncd/pipeline/pkg/names"
34+
"github.com/tektoncd/pipeline/pkg/spire"
3435
corev1 "k8s.io/api/core/v1"
3536
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3637
"k8s.io/apimachinery/pkg/runtime/schema"
@@ -53,6 +54,9 @@ const (
5354
// deadlineFactor is the factor we multiply the taskrun timeout with to determine the activeDeadlineSeconds of the Pod.
5455
// It has to be higher than the timeout (to not be killed before)
5556
deadlineFactor = 1.5
57+
58+
// SpiffeCsiDriver is the CSI storage plugin needed for injection of SPIFFE workload api.
59+
SpiffeCsiDriver = "csi.spiffe.io"
5660
)
5761

5862
// These are effectively const, but Go doesn't have such an annotation.
@@ -132,6 +136,10 @@ func (b *Builder) Build(ctx context.Context, taskRun *v1beta1.TaskRun, taskSpec
132136
// Secrets, along with any arguments needed by Step entrypoints to process
133137
// those secrets.
134138
commonExtraEntrypointArgs := []string{}
139+
// Entrypoint arg to enable or disable spire
140+
if config.IsSpireEnabled(ctx) {
141+
commonExtraEntrypointArgs = append(commonExtraEntrypointArgs, "-enable_spire")
142+
}
135143
credEntrypointArgs, credVolumes, credVolumeMounts, err := credsInit(ctx, taskRun.Spec.ServiceAccountName, taskRun.Namespace, b.KubeClient)
136144
if err != nil {
137145
return nil, err
@@ -322,6 +330,39 @@ func (b *Builder) Build(ctx context.Context, taskRun *v1beta1.TaskRun, taskSpec
322330
return nil, err
323331
}
324332

333+
readonly := true
334+
if config.IsSpireEnabled(ctx) {
335+
// add SPIRE's CSI volume to the explicitly declared use volumes
336+
volumes = append(volumes, corev1.Volume{
337+
Name: spire.WorkloadAPI,
338+
VolumeSource: corev1.VolumeSource{
339+
CSI: &corev1.CSIVolumeSource{
340+
Driver: SpiffeCsiDriver,
341+
ReadOnly: &readonly,
342+
},
343+
},
344+
})
345+
346+
// mount SPIRE's CSI volume to each Step Container
347+
for i := range stepContainers {
348+
c := &stepContainers[i]
349+
c.VolumeMounts = append(c.VolumeMounts, corev1.VolumeMount{
350+
Name: spire.WorkloadAPI,
351+
MountPath: spire.VolumeMountPath,
352+
ReadOnly: readonly,
353+
})
354+
}
355+
for i := range initContainers {
356+
// mount SPIRE's CSI volume to each Init Container
357+
c := &initContainers[i]
358+
c.VolumeMounts = append(c.VolumeMounts, corev1.VolumeMount{
359+
Name: spire.WorkloadAPI,
360+
MountPath: spire.VolumeMountPath,
361+
ReadOnly: readonly,
362+
})
363+
}
364+
}
365+
325366
mergedPodContainers := stepContainers
326367

327368
// Merge sidecar containers with step containers.

pkg/pod/pod_test.go

+162
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"github.com/tektoncd/pipeline/pkg/apis/pipeline"
3131
"github.com/tektoncd/pipeline/pkg/apis/pipeline/pod"
3232
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
33+
"github.com/tektoncd/pipeline/pkg/spire"
3334
"github.com/tektoncd/pipeline/test/diff"
3435
"github.com/tektoncd/pipeline/test/names"
3536
corev1 "k8s.io/api/core/v1"
@@ -2455,6 +2456,167 @@ func TestPodBuild_TaskLevelResourceRequirements(t *testing.T) {
24552456
}
24562457
}
24572458

2459+
func TestPodBuildwithSpireEnabled(t *testing.T) {
2460+
initContainers := []corev1.Container{entrypointInitContainer(images.EntrypointImage, []v1beta1.Step{{Name: "name"}})}
2461+
readonly := true
2462+
for i := range initContainers {
2463+
c := &initContainers[i]
2464+
c.VolumeMounts = append(c.VolumeMounts, corev1.VolumeMount{
2465+
Name: spire.WorkloadAPI,
2466+
MountPath: spire.VolumeMountPath,
2467+
ReadOnly: true,
2468+
})
2469+
}
2470+
2471+
for _, c := range []struct {
2472+
desc string
2473+
trs v1beta1.TaskRunSpec
2474+
trAnnotation map[string]string
2475+
ts v1beta1.TaskSpec
2476+
want *corev1.PodSpec
2477+
wantAnnotations map[string]string
2478+
}{{
2479+
desc: "simple",
2480+
ts: v1beta1.TaskSpec{
2481+
Steps: []v1beta1.Step{{
2482+
Name: "name",
2483+
Image: "image",
2484+
Command: []string{"cmd"}, // avoid entrypoint lookup.
2485+
}},
2486+
},
2487+
want: &corev1.PodSpec{
2488+
RestartPolicy: corev1.RestartPolicyNever,
2489+
InitContainers: initContainers,
2490+
Containers: []corev1.Container{{
2491+
Name: "step-name",
2492+
Image: "image",
2493+
Command: []string{"/tekton/bin/entrypoint"},
2494+
Args: []string{
2495+
"-wait_file",
2496+
"/tekton/downward/ready",
2497+
"-wait_file_content",
2498+
"-post_file",
2499+
"/tekton/run/0/out",
2500+
"-termination_path",
2501+
"/tekton/termination",
2502+
"-step_metadata_dir",
2503+
"/tekton/run/0/status",
2504+
"-enable_spire",
2505+
"-entrypoint",
2506+
"cmd",
2507+
"--",
2508+
},
2509+
VolumeMounts: append([]corev1.VolumeMount{binROMount, runMount(0, false), downwardMount, {
2510+
Name: "tekton-creds-init-home-0",
2511+
MountPath: "/tekton/creds",
2512+
}, {
2513+
Name: spire.WorkloadAPI,
2514+
MountPath: spire.VolumeMountPath,
2515+
ReadOnly: true,
2516+
}}, implicitVolumeMounts...),
2517+
TerminationMessagePath: "/tekton/termination",
2518+
}},
2519+
Volumes: append(implicitVolumes, binVolume, runVolume(0), downwardVolume, corev1.Volume{
2520+
Name: "tekton-creds-init-home-0",
2521+
VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{Medium: corev1.StorageMediumMemory}},
2522+
}, corev1.Volume{
2523+
Name: spire.WorkloadAPI,
2524+
VolumeSource: corev1.VolumeSource{
2525+
CSI: &corev1.CSIVolumeSource{
2526+
Driver: "csi.spiffe.io",
2527+
ReadOnly: &readonly,
2528+
},
2529+
},
2530+
}),
2531+
ActiveDeadlineSeconds: &defaultActiveDeadlineSeconds,
2532+
},
2533+
}} {
2534+
t.Run(c.desc, func(t *testing.T) {
2535+
featureFlags := map[string]string{
2536+
"enable-api-fields": "alpha",
2537+
"enforce-nonfalsifiability": "spire",
2538+
}
2539+
names.TestingSeed()
2540+
store := config.NewStore(logtesting.TestLogger(t))
2541+
store.OnConfigChanged(
2542+
&corev1.ConfigMap{
2543+
ObjectMeta: metav1.ObjectMeta{Name: config.GetFeatureFlagsConfigName(), Namespace: system.Namespace()},
2544+
Data: featureFlags,
2545+
},
2546+
)
2547+
kubeclient := fakek8s.NewSimpleClientset(
2548+
&corev1.ServiceAccount{ObjectMeta: metav1.ObjectMeta{Name: "default", Namespace: "default"}},
2549+
&corev1.ServiceAccount{ObjectMeta: metav1.ObjectMeta{Name: "service-account", Namespace: "default"},
2550+
Secrets: []corev1.ObjectReference{{
2551+
Name: "multi-creds",
2552+
}},
2553+
},
2554+
&corev1.Secret{
2555+
ObjectMeta: metav1.ObjectMeta{
2556+
Name: "multi-creds",
2557+
Namespace: "default",
2558+
Annotations: map[string]string{
2559+
"tekton.dev/docker-0": "https://us.gcr.io",
2560+
"tekton.dev/docker-1": "https://docker.io",
2561+
"tekton.dev/git-0": "github.com",
2562+
"tekton.dev/git-1": "gitlab.com",
2563+
}},
2564+
Type: "kubernetes.io/basic-auth",
2565+
Data: map[string][]byte{
2566+
"username": []byte("foo"),
2567+
"password": []byte("BestEver"),
2568+
},
2569+
},
2570+
)
2571+
var trAnnotations map[string]string
2572+
if c.trAnnotation == nil {
2573+
trAnnotations = map[string]string{
2574+
ReleaseAnnotation: fakeVersion,
2575+
}
2576+
} else {
2577+
trAnnotations = c.trAnnotation
2578+
trAnnotations[ReleaseAnnotation] = fakeVersion
2579+
}
2580+
tr := &v1beta1.TaskRun{
2581+
ObjectMeta: metav1.ObjectMeta{
2582+
Name: "taskrun-name",
2583+
Namespace: "default",
2584+
Annotations: trAnnotations,
2585+
},
2586+
Spec: c.trs,
2587+
}
2588+
2589+
// No entrypoints should be looked up.
2590+
entrypointCache := fakeCache{}
2591+
builder := Builder{
2592+
Images: images,
2593+
KubeClient: kubeclient,
2594+
EntrypointCache: entrypointCache,
2595+
}
2596+
2597+
got, err := builder.Build(store.ToContext(context.Background()), tr, c.ts)
2598+
if err != nil {
2599+
t.Fatalf("builder.Build: %v", err)
2600+
}
2601+
2602+
want := kmeta.ChildName(tr.Name, "-pod")
2603+
if d := cmp.Diff(got.Name, want); d != "" {
2604+
t.Errorf("Unexpected pod name, diff=%s", diff.PrintWantGot(d))
2605+
}
2606+
2607+
if d := cmp.Diff(c.want, &got.Spec, resourceQuantityCmp, volumeSort, volumeMountSort); d != "" {
2608+
t.Errorf("Diff %s", diff.PrintWantGot(d))
2609+
}
2610+
2611+
if c.wantAnnotations != nil {
2612+
if d := cmp.Diff(c.wantAnnotations, got.ObjectMeta.Annotations, cmpopts.IgnoreMapEntries(ignoreReleaseAnnotation)); d != "" {
2613+
t.Errorf("Annotation not expected, diff=%s", diff.PrintWantGot(d))
2614+
}
2615+
}
2616+
})
2617+
}
2618+
}
2619+
24582620
// verifyTaskLevelComputeResources verifies that the given TaskRun's containers have the expected compute resources.
24592621
func verifyTaskLevelComputeResources(expectedComputeResources []ExpectedComputeResources, containers []corev1.Container) error {
24602622
if len(expectedComputeResources) != len(containers) {

0 commit comments

Comments
 (0)