Skip to content

Commit

Permalink
[TEP-0089] Add a config map to support SPIRE initialization.
Browse files Browse the repository at this point in the history
This PR is one among the set of PRs done to implement TEP-0089.
This PR has been derived from the larger PRs  PR#5715 and PR#4759 by
@pxp928 and @lumjjb.
This PR has the following changes
1. Modify the feature flag to control non-falsifiability from enable-spire to enforce-nonfalsifiability="spire".
   This is in accordance to the approved TEP.
2. Add a configmap config-spire to initialize SPIRE.

Signed-off-by: jagathprakash <31057312+jagathprakash@users.noreply.github.com>
  • Loading branch information
jagathprakash committed Jan 5, 2023
1 parent 963aa40 commit faf549a
Show file tree
Hide file tree
Showing 20 changed files with 398 additions and 21 deletions.
5 changes: 5 additions & 0 deletions config/config-feature-flags.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,8 @@ data:
# Acceptable values are "v1beta1" and "v1alpha1".
# The default is "v1alpha1".
custom-task-version: "v1alpha1"
# Setting this flag will determine how Tekton pipelines will handle non-falsifiable provenance.
# If set to "spire", then SPIRE will be used to ensure non-falsifiable provenance.
# 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"
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 @@ -117,6 +117,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
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
37 changes: 31 additions & 6 deletions pkg/apis/config/feature_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,12 @@ const (
DefaultSendCloudEventsForRuns = false
// DefaultEmbeddedStatus is the default value for "embedded-status".
DefaultEmbeddedStatus = FullEmbeddedStatus
// DefaultEnableSpire is the default value for "enable-spire".
DefaultEnableSpire = false
// EnforceNonfalsifiabilityWithSpire is the value used for "enable-nonfalsifiability" when SPIRE is used to enable non-falsifiability.
EnforceNonfalsifiabilityWithSpire = "spire"
// EnforceNonfalsifiabilityNone is the value used for "enable-nonfalsifiability" when non-falsifiability is not enabled.
EnforceNonfalsifiabilityNone = ""
// DefaultEnforceNonfalsifiability is the default value for "enforce-nonfalsifiability".
DefaultEnforceNonfalsifiability = EnforceNonfalsifiabilityNone
// DefaultResourceVerificationMode is the default value for "resource-verification-mode".
DefaultResourceVerificationMode = SkipResourceVerificationMode
// DefaultEnableProvenanceInStatus is the default value for "enable-provenance-status".
Expand All @@ -103,7 +107,7 @@ const (
enableAPIFields = "enable-api-fields"
sendCloudEventsForRuns = "send-cloudevents-for-runs"
embeddedStatus = "embedded-status"
enableSpire = "enable-spire"
enforceNonfalsifiability = "enforce-nonfalsifiability"
verificationMode = "resource-verification-mode"
enableProvenanceInStatus = "enable-provenance-in-status"
resultExtractionMethod = "results-from"
Expand All @@ -125,7 +129,7 @@ type FeatureFlags struct {
SendCloudEventsForRuns bool
AwaitSidecarReadiness bool
EmbeddedStatus string
EnableSpire bool
EnforceNonfalsifiability string
ResourceVerificationMode string
EnableProvenanceInStatus bool
ResultExtractionMethod string
Expand All @@ -142,6 +146,14 @@ func GetFeatureFlagsConfigName() string {
return "feature-flags"
}

func getEnforceNonfalsifiabilityValues() map[string]struct{} {
var value struct{}
return map[string]struct{}{
EnforceNonfalsifiabilityNone: value,
EnforceNonfalsifiabilityWithSpire: value,
}
}

// NewFeatureFlagsFromMap returns a Config given a map corresponding to a ConfigMap
func NewFeatureFlagsFromMap(cfgMap map[string]string) (*FeatureFlags, error) {
setFeature := func(key string, defaultValue bool, feature *bool) error {
Expand All @@ -157,6 +169,19 @@ func NewFeatureFlagsFromMap(cfgMap map[string]string) (*FeatureFlags, error) {
return nil
}

setStringFeature := func(key string, defaultValue string, acceptedValues map[string]struct{}, feature *string) error {
value := defaultValue
if cfg, ok := cfgMap[key]; ok {
value = strings.ToLower(cfg)
}
if _, ok := acceptedValues[value]; !ok {
*feature = defaultValue
return fmt.Errorf("invalid value for feature flag %q: %q", key, value)
}
*feature = value
return nil
}

tc := FeatureFlags{}
if err := setFeature(disableAffinityAssistantKey, DefaultDisableAffinityAssistant, &tc.DisableAffinityAssistant); err != nil {
return nil, err
Expand Down Expand Up @@ -207,15 +232,15 @@ func NewFeatureFlagsFromMap(cfgMap map[string]string) (*FeatureFlags, error) {
if tc.EnableAPIFields == AlphaAPIFields {
tc.EnableTektonOCIBundles = true
tc.EnableCustomTasks = true
tc.EnableSpire = true
tc.EnforceNonfalsifiability = EnforceNonfalsifiabilityWithSpire
} else {
if err := setFeature(enableTektonOCIBundles, DefaultEnableTektonOciBundles, &tc.EnableTektonOCIBundles); err != nil {
return nil, err
}
if err := setFeature(enableCustomTasks, DefaultEnableCustomTasks, &tc.EnableCustomTasks); err != nil {
return nil, err
}
if err := setFeature(enableSpire, DefaultEnableSpire, &tc.EnableSpire); err != nil {
if err := setStringFeature(enforceNonfalsifiability, DefaultEnforceNonfalsifiability, getEnforceNonfalsifiabilityValues(), &tc.EnforceNonfalsifiability); err != nil {
return nil, err
}
}
Expand Down
17 changes: 9 additions & 8 deletions pkg/apis/config/feature_flags_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func TestNewFeatureFlagsFromConfigMap(t *testing.T) {
EnableAPIFields: "alpha",
SendCloudEventsForRuns: true,
EmbeddedStatus: "both",
EnableSpire: true,
EnforceNonfalsifiability: "spire",
ResourceVerificationMode: "enforce",
EnableProvenanceInStatus: true,
ResultExtractionMethod: "termination-message",
Expand All @@ -78,10 +78,9 @@ func TestNewFeatureFlagsFromConfigMap(t *testing.T) {
EnableAPIFields: "alpha",
// These are prescribed as true by enabling "alpha" API fields, even
// if the submitted text value is "false".
EnableTektonOCIBundles: true,
EnableCustomTasks: true,
EnableSpire: true,

EnableTektonOCIBundles: true,
EnableCustomTasks: true,
EnforceNonfalsifiability: "spire",
DisableAffinityAssistant: config.DefaultDisableAffinityAssistant,
DisableCredsInit: config.DefaultDisableCredsInit,
RunningInEnvWithInjectedSidecars: config.DefaultRunningInEnvWithInjectedSidecars,
Expand Down Expand Up @@ -141,15 +140,15 @@ func TestNewFeatureFlagsFromConfigMap(t *testing.T) {
EnableAPIFields: "stable",
EmbeddedStatus: "full",
EnableCustomTasks: config.DefaultEnableCustomTasks,
EnableSpire: true,
EnforceNonfalsifiability: "spire",
ResourceVerificationMode: config.DefaultResourceVerificationMode,
RunningInEnvWithInjectedSidecars: config.DefaultRunningInEnvWithInjectedSidecars,
AwaitSidecarReadiness: config.DefaultAwaitSidecarReadiness,
ResultExtractionMethod: config.DefaultResultExtractionMethod,
MaxResultSize: config.DefaultMaxResultSize,
CustomTaskVersion: config.DefaultCustomTaskVersion,
},
fileName: "feature-flags-enable-spire",
fileName: "feature-flags-enforce-nonfalsifiability-spire",
},
{
expectedConfig: &config.FeatureFlags{
Expand Down Expand Up @@ -189,7 +188,7 @@ func TestNewFeatureFlagsFromEmptyConfigMap(t *testing.T) {
EnableAPIFields: config.DefaultEnableAPIFields,
SendCloudEventsForRuns: config.DefaultSendCloudEventsForRuns,
EmbeddedStatus: config.DefaultEmbeddedStatus,
EnableSpire: config.DefaultEnableSpire,
EnforceNonfalsifiability: config.DefaultEnforceNonfalsifiability,
ResourceVerificationMode: config.DefaultResourceVerificationMode,
EnableProvenanceInStatus: config.DefaultEnableProvenanceInStatus,
ResultExtractionMethod: config.DefaultResultExtractionMethod,
Expand Down Expand Up @@ -245,6 +244,8 @@ func TestNewFeatureFlagsConfigMapErrors(t *testing.T) {
fileName: "feature-flags-invalid-max-result-size-bad-value",
}, {
fileName: "feature-flags-invalid-custom-task-version",
}, {
fileName: "feature-flags-enforce-nonfalsifiability-bad-flag",
}} {
t.Run(tc.fileName, func(t *testing.T) {
cm := test.ConfigMapFromTestFile(t, tc.fileName)
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
}
71 changes: 71 additions & 0 deletions pkg/apis/config/spire_config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
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 {
want *sc.SpireConfig
fileName string
}

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

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

func verifyConfigFileWithExpectedSpireConfig(t *testing.T, fileName string, want *sc.SpireConfig) {
t.Helper()
cm := test.ConfigMapFromTestFile(t, fileName)
if got, err := config.NewSpireConfigFromConfigMap(cm); err == nil {
if d := cmp.Diff(got, want); d != "" {
t.Errorf("Diff:\n%s", diff.PrintWantGot(d))
}
} else {
t.Errorf("NewSpireConfigFromConfigMap(actual) = %v", err)
}
}
Loading

0 comments on commit faf549a

Please sign in to comment.