Skip to content

Commit

Permalink
[TEP-0089] Enable SPIRE for signing taskrun results in alpha.
Browse files Browse the repository at this point in the history
Breaking down PR tektoncd#4759 originally proposed by @pxp928 to address TEP-0089 according @lumjjb suggestions. Plan for breaking down PR is PR 1.1: api PR 1.2: entrypointer (+cmd line + test/entrypointer) Entrypoint takes results and signs the results (termination message). PR 1.3: reconciler + pod + cmd/controller + integration tests Controller will verify the signed result. This commit corresponds to 1.3 above.
  • Loading branch information
jagathprakash committed Dec 2, 2022
1 parent f83cd1f commit bad5cc3
Show file tree
Hide file tree
Showing 42 changed files with 2,737 additions and 89 deletions.
3 changes: 2 additions & 1 deletion cmd/imagedigestexporter/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ var (
terminationMessagePath = flag.String("terminationMessagePath", "/tekton/termination", "Location of file containing termination message")
)

/* The input of this go program will be a JSON string with all the output PipelineResources of type
/*
The input of this go program will be a JSON string with all the output PipelineResources of type
Image, which will include the path to where the index.json file will be located. The program will
read the related index.json file(s) and log another JSON string including the name of the image resource
and the digests.
Expand Down
4 changes: 4 additions & 0 deletions config/config-feature-flags.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,7 @@ data:
# in the TaskRun/PipelineRun such as the source from where a remote Task/Pipeline
# definition was fetched.
enable-provenance-in-status: "false"
# Setting this flag to "true" enables spire integration with pipeline.
# This is an experimental feature and thus should still be considered
# an alpha feature.
enable-spire: "false"
49 changes: 49 additions & 0 deletions config/config-spire.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Copyright 2022 The Tekton Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

apiVersion: v1
kind: ConfigMap
metadata:
name: config-spire
namespace: tekton-pipelines
labels:
app.kubernetes.io/instance: default
app.kubernetes.io/part-of: tekton-pipelines
data:
_example: |
################################
# #
# EXAMPLE CONFIGURATION #
# #
################################
# This block is not actually functional configuration,
# but serves to illustrate the available configuration
# options and document them in a way that is accessible
# to users that `kubectl edit` this config map.
#
# These sample configuration options may be copied out of
# this example block and unindented to be in the data block
# to actually change the configuration.
#
# spire-trust-domain specifies the SPIRE trust domain to use.
# spire-trust-domain: "example.org"
#
# spire-socket-path specifies the SPIRE agent socket for SPIFFE workload API.
# spire-socket-path: "unix:///spiffe-workload-api/spire-agent.sock"
#
# spire-server-addr specifies the SPIRE server address for workload/node registration.
# spire-server-addr: "spire-server.spire.svc.cluster.local:8081"
#
# spire-node-alias-prefix specifies the SPIRE node alias prefix to use.
# spire-node-alias-prefix: "/tekton-node/"
2 changes: 2 additions & 0 deletions config/controller.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ spec:
value: feature-flags
- name: CONFIG_LEADERELECTION_NAME
value: config-leader-election
- name: CONFIG_SPIRE
value: config-spire
- name: CONFIG_TRUSTED_RESOURCES_NAME
value: config-trusted-resources
- name: SSL_CERT_FILE
Expand Down
287 changes: 287 additions & 0 deletions docs/spire.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion examples/v1beta1/pipelineruns/4808-regression.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,4 @@ spec:
name: result-test
params:
- name: RESULT_STRING_LENGTH
value: "3000"
value: "2000"
5 changes: 5 additions & 0 deletions hack/update-codegen.sh
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ ${PREFIX}/deepcopy-gen \
--go-header-file ${REPO_ROOT_DIR}/hack/boilerplate/boilerplate.go.txt \
-i github.com/tektoncd/pipeline/pkg/apis/config

${PREFIX}/deepcopy-gen \
-O zz_generated.deepcopy \
--go-header-file ${REPO_ROOT_DIR}/hack/boilerplate/boilerplate.go.txt \
-i github.com/tektoncd/pipeline/pkg/spire/config

${PREFIX}/deepcopy-gen \
-O zz_generated.deepcopy \
--go-header-file ${REPO_ROOT_DIR}/hack/boilerplate/boilerplate.go.txt \
Expand Down
83 changes: 83 additions & 0 deletions pkg/apis/config/spire_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
Copyright 2022 The Tekton Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package config

import (
"fmt"
"os"

sc "github.com/tektoncd/pipeline/pkg/spire/config"
corev1 "k8s.io/api/core/v1"
)

const (
// SpireConfigMapName is the name of the trusted resources configmap
SpireConfigMapName = "config-spire"

// SpireTrustDomain is the key to extract out the SPIRE trust domain to use
SpireTrustDomain = "spire-trust-domain"
// SpireSocketPath is the key to extract out the SPIRE agent socket for SPIFFE workload API
SpireSocketPath = "spire-socket-path"
// SpireServerAddr is the key to extract out the SPIRE server address for workload/node registration
SpireServerAddr = "spire-server-addr"
// SpireNodeAliasPrefix is the key to extract out the SPIRE node alias prefix to use
SpireNodeAliasPrefix = "spire-node-alias-prefix"

// SpireTrustDomainDefault is the default value for the SpireTrustDomain
SpireTrustDomainDefault = "example.org"
// SpireSocketPathDefault is the default value for the SpireSocketPath
SpireSocketPathDefault = "unix:///spiffe-workload-api/spire-agent.sock"
// SpireServerAddrDefault is the default value for the SpireServerAddr
SpireServerAddrDefault = "spire-server.spire.svc.cluster.local:8081"
// SpireNodeAliasPrefixDefault is the default value for the SpireNodeAliasPrefix
SpireNodeAliasPrefixDefault = "/tekton-node/"
)

// NewSpireConfigFromMap creates a Config from the supplied map
func NewSpireConfigFromMap(data map[string]string) (*sc.SpireConfig, error) {
cfg := &sc.SpireConfig{}
var ok bool
if cfg.TrustDomain, ok = data[SpireTrustDomain]; !ok {
cfg.TrustDomain = SpireTrustDomainDefault
}
if cfg.SocketPath, ok = data[SpireSocketPath]; !ok {
cfg.SocketPath = SpireSocketPathDefault
}
if cfg.ServerAddr, ok = data[SpireServerAddr]; !ok {
cfg.ServerAddr = SpireServerAddrDefault
}
if cfg.NodeAliasPrefix, ok = data[SpireNodeAliasPrefix]; !ok {
cfg.NodeAliasPrefix = SpireNodeAliasPrefixDefault
}
if err := cfg.Validate(); err != nil {
return nil, fmt.Errorf("failed to parse SPIRE configmap: %w", err)
}
return cfg, nil
}

// NewSpireConfigFromConfigMap creates a Config from the supplied ConfigMap
func NewSpireConfigFromConfigMap(configMap *corev1.ConfigMap) (*sc.SpireConfig, error) {
return NewSpireConfigFromMap(configMap.Data)
}

// GetSpireConfigName returns the name of Spire ConfigMap
func GetSpireConfigName() string {
if e := os.Getenv("CONFIG_SPIRE"); e != "" {
return e
}
return SpireConfigMapName
}
72 changes: 72 additions & 0 deletions pkg/apis/config/spire_config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
Copyright 2021 The Tekton Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package config_test

import (
"testing"

"github.com/google/go-cmp/cmp"
"github.com/tektoncd/pipeline/pkg/apis/config"
test "github.com/tektoncd/pipeline/pkg/reconciler/testing"
sc "github.com/tektoncd/pipeline/pkg/spire/config"
"github.com/tektoncd/pipeline/test/diff"
)

func TestNewSpireConfigFromConfigMap(t *testing.T) {
type testCase struct {
expectedConfig *sc.SpireConfig
fileName string
}

testCases := []testCase{
{
expectedConfig: &sc.SpireConfig{
TrustDomain: "test.com",
SocketPath: "unix:///spiffe-workload-api/test-spire-agent.sock",
ServerAddr: "test-spire-server.spire.svc.cluster.local:8081",
NodeAliasPrefix: "/test-tekton-node/",
},
fileName: config.GetSpireConfigName(),
},
}

for _, tc := range testCases {
verifyConfigFileWithExpectedSpireConfig(t, tc.fileName, tc.expectedConfig)
}
}

func TestNewSpireConfigFromEmptyConfigMap(t *testing.T) {
SpireConfigEmptyName := "config-spire-empty"
expectedConfig := &sc.SpireConfig{
TrustDomain: "example.org",
SocketPath: "unix:///spiffe-workload-api/spire-agent.sock",
ServerAddr: "spire-server.spire.svc.cluster.local:8081",
NodeAliasPrefix: "/tekton-node/",
}
verifyConfigFileWithExpectedSpireConfig(t, SpireConfigEmptyName, expectedConfig)
}

func verifyConfigFileWithExpectedSpireConfig(t *testing.T, fileName string, expectedConfig *sc.SpireConfig) {
cm := test.ConfigMapFromTestFile(t, fileName)
if ab, err := config.NewSpireConfigFromConfigMap(cm); err == nil {
if d := cmp.Diff(ab, expectedConfig); d != "" {
t.Errorf("Diff:\n%s", diff.PrintWantGot(d))
}
} else {
t.Errorf("NewSpireConfigFromConfigMap(actual) = %v", err)
}
}
11 changes: 11 additions & 0 deletions pkg/apis/config/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package config
import (
"context"

sc "github.com/tektoncd/pipeline/pkg/spire/config"
"knative.dev/pkg/configmap"
)

Expand All @@ -33,6 +34,7 @@ type Config struct {
ArtifactPVC *ArtifactPVC
Metrics *Metrics
TrustedResources *TrustedResources
SpireConfig *sc.SpireConfig
}

// FromContext extracts a Config from the provided context.
Expand All @@ -56,13 +58,16 @@ func FromContextOrDefaults(ctx context.Context) *Config {
artifactPVC, _ := NewArtifactPVCFromMap(map[string]string{})
metrics, _ := newMetricsFromMap(map[string]string{})
trustedresources, _ := NewTrustedResourcesConfigFromMap(map[string]string{})
spireconfig, _ := NewSpireConfigFromMap(map[string]string{})

return &Config{
Defaults: defaults,
FeatureFlags: featureFlags,
ArtifactBucket: artifactBucket,
ArtifactPVC: artifactPVC,
Metrics: metrics,
TrustedResources: trustedresources,
SpireConfig: spireconfig,
}
}

Expand Down Expand Up @@ -91,6 +96,7 @@ func NewStore(logger configmap.Logger, onAfterStore ...func(name string, value i
GetArtifactPVCConfigName(): NewArtifactPVCFromConfigMap,
GetMetricsConfigName(): NewMetricsFromConfigMap,
GetTrustedResourcesConfigName(): NewTrustedResourcesConfigFromConfigMap,
GetSpireConfigName(): NewSpireConfigFromConfigMap,
},
onAfterStore...,
),
Expand Down Expand Up @@ -131,6 +137,10 @@ func (s *Store) Load() *Config {
if trustedresources == nil {
trustedresources, _ = NewTrustedResourcesConfigFromMap(map[string]string{})
}
spireconfig := s.UntypedLoad(GetSpireConfigName())
if spireconfig == nil {
spireconfig, _ = NewSpireConfigFromMap(map[string]string{})
}

return &Config{
Defaults: defaults.(*Defaults).DeepCopy(),
Expand All @@ -139,5 +149,6 @@ func (s *Store) Load() *Config {
ArtifactPVC: artifactPVC.(*ArtifactPVC).DeepCopy(),
Metrics: metrics.(*Metrics).DeepCopy(),
TrustedResources: trustedresources.(*TrustedResources).DeepCopy(),
SpireConfig: spireconfig.(*sc.SpireConfig).DeepCopy(),
}
}
6 changes: 6 additions & 0 deletions pkg/apis/config/store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,15 @@ func TestStoreLoadWithContext(t *testing.T) {
artifactPVCConfig := test.ConfigMapFromTestFile(t, "config-artifact-pvc")
metricsConfig := test.ConfigMapFromTestFile(t, "config-observability")
trustedresourcesConfig := test.ConfigMapFromTestFile(t, "config-trusted-resources")
spireConfig := test.ConfigMapFromTestFile(t, "config-spire")

expectedDefaults, _ := config.NewDefaultsFromConfigMap(defaultConfig)
expectedFeatures, _ := config.NewFeatureFlagsFromConfigMap(featuresConfig)
expectedArtifactBucket, _ := config.NewArtifactBucketFromConfigMap(artifactBucketConfig)
expectedArtifactPVC, _ := config.NewArtifactPVCFromConfigMap(artifactPVCConfig)
metrics, _ := config.NewMetricsFromConfigMap(metricsConfig)
expectedTrustedResources, _ := config.NewTrustedResourcesConfigFromConfigMap(trustedresourcesConfig)
expectedSpireConfig, _ := config.NewSpireConfigFromConfigMap(spireConfig)

expected := &config.Config{
Defaults: expectedDefaults,
Expand All @@ -50,6 +52,7 @@ func TestStoreLoadWithContext(t *testing.T) {
ArtifactPVC: expectedArtifactPVC,
Metrics: metrics,
TrustedResources: expectedTrustedResources,
SpireConfig: expectedSpireConfig,
}

store := config.NewStore(logtesting.TestLogger(t))
Expand All @@ -59,6 +62,7 @@ func TestStoreLoadWithContext(t *testing.T) {
store.OnConfigChanged(artifactPVCConfig)
store.OnConfigChanged(metricsConfig)
store.OnConfigChanged(trustedresourcesConfig)
store.OnConfigChanged(spireConfig)

cfg := config.FromContext(store.ToContext(context.Background()))

Expand All @@ -74,6 +78,7 @@ func TestStoreLoadWithContext_Empty(t *testing.T) {
artifactPVC, _ := config.NewArtifactPVCFromMap(map[string]string{})
metrics, _ := config.NewMetricsFromConfigMap(&corev1.ConfigMap{Data: map[string]string{}})
trustedresources, _ := config.NewTrustedResourcesConfigFromMap(map[string]string{})
spireConfig, _ := config.NewSpireConfigFromMap(map[string]string{})

expected := &config.Config{
Defaults: defaults,
Expand All @@ -82,6 +87,7 @@ func TestStoreLoadWithContext_Empty(t *testing.T) {
ArtifactPVC: artifactPVC,
Metrics: metrics,
TrustedResources: trustedresources,
SpireConfig: spireConfig,
}

store := config.NewStore(logtesting.TestLogger(t))
Expand Down
29 changes: 29 additions & 0 deletions pkg/apis/config/testdata/config-spire-empty.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Copyright 2022 The Tekton Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

apiVersion: v1
kind: ConfigMap
metadata:
name: config-spire
namespace: tekton-pipelines
labels:
app.kubernetes.io/instance: default
app.kubernetes.io/part-of: tekton-pipelines
data:
_example: |
################################
# #
# EXAMPLE CONFIGURATION #
# #
################################
Loading

0 comments on commit bad5cc3

Please sign in to comment.