diff --git a/cmd/cofidectl/cmd/trustzone/trustzone.go b/cmd/cofidectl/cmd/trustzone/trustzone.go index d857bd8..f272bdd 100644 --- a/cmd/cofidectl/cmd/trustzone/trustzone.go +++ b/cmd/cofidectl/cmd/trustzone/trustzone.go @@ -114,6 +114,7 @@ type addOpts struct { context string profile string jwtIssuer string + externalServer bool } func (c *TrustZoneCommand) GetAddCommand() *cobra.Command { @@ -156,6 +157,7 @@ func (c *TrustZoneCommand) GetAddCommand() *cobra.Command { Profile: &opts.profile, JwtIssuer: &opts.jwtIssuer, BundleEndpointProfile: &bundleEndpointProfile, + ExternalServer: &opts.externalServer, } _, err = ds.AddTrustZone(newTrustZone) @@ -173,6 +175,7 @@ func (c *TrustZoneCommand) GetAddCommand() *cobra.Command { f.StringVar(&opts.context, "kubernetes-context", "", "Kubernetes context to use for this trust zone") f.StringVar(&opts.profile, "profile", "kubernetes", "Cofide profile used in the installation (e.g. kubernetes, istio)") f.StringVar(&opts.jwtIssuer, "jwt-issuer", "", "JWT issuer to use for this trust zone") + f.BoolVar(&opts.externalServer, "external-server", false, "If the SPIRE server runs externally") cobra.CheckErr(cmd.MarkFlagRequired("trust-domain")) cobra.CheckErr(cmd.MarkFlagRequired("kubernetes-cluster")) diff --git a/go.mod b/go.mod index cdc9b32..0b93a7f 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ toolchain go1.23.4 require ( buf.build/go/protoyaml v0.3.0 cuelang.org/go v0.10.1 - github.com/cofide/cofide-api-sdk v0.4.1-0.20241213113045-864bfb581c91 + github.com/cofide/cofide-api-sdk v0.4.1-0.20241218164200-fd9d4ce5088c github.com/fatih/color v1.18.0 github.com/gofrs/flock v0.12.1 github.com/google/go-cmp v0.6.0 @@ -25,7 +25,7 @@ require ( require ( buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.35.2-20241127180247-a33202765966.1 // indirect - cel.dev/expr v0.18.0 // indirect + cel.dev/expr v0.18.0 // indirect dario.cat/mergo v1.0.1 // indirect github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect diff --git a/go.sum b/go.sum index 6b711e7..c00376d 100644 --- a/go.sum +++ b/go.sum @@ -88,6 +88,8 @@ github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEa github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc= github.com/cofide/cofide-api-sdk v0.4.1-0.20241213113045-864bfb581c91 h1:We5sn+o74dce2gkO63ola8RFSD92jsud3pAIwVbzAq8= github.com/cofide/cofide-api-sdk v0.4.1-0.20241213113045-864bfb581c91/go.mod h1:u2iATR4IbZm9ruIBN734UjVuO3XQKPAFViIY3Xr6kTA= +github.com/cofide/cofide-api-sdk v0.4.1-0.20241218164200-fd9d4ce5088c h1:nEdnzITNrjaBjP6VRCEsCyFF5nUI5M0nBDB98JZrZEI= +github.com/cofide/cofide-api-sdk v0.4.1-0.20241218164200-fd9d4ce5088c/go.mod h1:uChrsD6JvNDSCsAxXil3gGqTHxee0VJ1MbCT7xFZg6g= github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= github.com/containerd/containerd v1.7.23 h1:H2CClyUkmpKAGlhQp95g2WXHfLYc7whAuvZGBNYOOwQ= diff --git a/internal/pkg/config/schema.cue b/internal/pkg/config/schema.cue index 1900609..d67d511 100644 --- a/internal/pkg/config/schema.cue +++ b/internal/pkg/config/schema.cue @@ -14,6 +14,7 @@ jwt_issuer?: string extra_helm_values?: #HelmValues bundle_endpoint_profile?: #BundleEndpointProfile + external_server?: bool } #TrustProvider: { diff --git a/internal/pkg/config/testdata/config/full.yaml b/internal/pkg/config/testdata/config/full.yaml index 265649d..8e6626e 100644 --- a/internal/pkg/config/testdata/config/full.yaml +++ b/internal/pkg/config/testdata/config/full.yaml @@ -24,6 +24,7 @@ trust_zones: logLevel: INFO bundle_endpoint_profile: BUNDLE_ENDPOINT_PROFILE_HTTPS_SPIFFE profile: kubernetes + external_server: false - name: tz2 trust_domain: td2 kubernetes_cluster: local2 @@ -42,6 +43,7 @@ trust_zones: jwt_issuer: https://tz2.example.com bundle_endpoint_profile: BUNDLE_ENDPOINT_PROFILE_HTTPS_WEB profile: kubernetes + external_server: false attestation_policies: - name: ap1 kubernetes: diff --git a/internal/pkg/test/fixtures/fixtures.go b/internal/pkg/test/fixtures/fixtures.go index fbdffcc..a75b6cb 100644 --- a/internal/pkg/test/fixtures/fixtures.go +++ b/internal/pkg/test/fixtures/fixtures.go @@ -62,6 +62,7 @@ var trustZoneFixtures map[string]*trust_zone_proto.TrustZone = map[string]*trust return value }(), BundleEndpointProfile: trust_zone_proto.BundleEndpointProfile_BUNDLE_ENDPOINT_PROFILE_HTTPS_SPIFFE.Enum(), + ExternalServer: BoolPtr(false), }, "tz2": { Name: "tz2", @@ -88,6 +89,7 @@ var trustZoneFixtures map[string]*trust_zone_proto.TrustZone = map[string]*trust }, JwtIssuer: StringPtr("https://tz2.example.com"), BundleEndpointProfile: trust_zone_proto.BundleEndpointProfile_BUNDLE_ENDPOINT_PROFILE_HTTPS_WEB.Enum(), + ExternalServer: BoolPtr(false), }, // tz3 has no federations or bound attestation policies. "tz3": { @@ -118,6 +120,21 @@ var trustZoneFixtures map[string]*trust_zone_proto.TrustZone = map[string]*trust Federations: []*federation_proto.Federation{}, AttestationPolicies: []*ap_binding_proto.APBinding{}, }, + // tz5 has no federations or bound attestation policies and has an external SPIRE server. + "tz5": { + Name: "tz5", + TrustDomain: "td5", + KubernetesCluster: StringPtr("local5"), + KubernetesContext: StringPtr("kind-local5"), + TrustProvider: &trust_provider_proto.TrustProvider{ + Kind: StringPtr("kubernetes"), + }, + Profile: StringPtr("kubernetes"), + BundleEndpointUrl: StringPtr("127.0.0.5"), + Federations: []*federation_proto.Federation{}, + AttestationPolicies: []*ap_binding_proto.APBinding{}, + ExternalServer: BoolPtr(true), + }, } var attestationPolicyFixtures map[string]*attestation_policy_proto.AttestationPolicy = map[string]*attestation_policy_proto.AttestationPolicy{ @@ -275,3 +292,7 @@ func Plugins(name string) *pluginspb.Plugins { func StringPtr(s string) *string { return &s } + +func BoolPtr(b bool) *bool { + return &b +} diff --git a/pkg/plugin/provision/spirehelm/spirehelm.go b/pkg/plugin/provision/spirehelm/spirehelm.go index 648aaa3..f86aa5b 100644 --- a/pkg/plugin/provision/spirehelm/spirehelm.go +++ b/pkg/plugin/provision/spirehelm/spirehelm.go @@ -150,10 +150,17 @@ func (h *SpireHelm) InstallSPIREStack(ctx context.Context, ds datasource.DataSou func (h *SpireHelm) WatchAndConfigure(ctx context.Context, ds datasource.DataSource, trustZones []*trust_zone_proto.TrustZone, kubeCfgFile string, statusCh chan<- *provisionpb.Status) error { // Wait for SPIRE servers to be available and update status before applying federation(s) for _, trustZone := range trustZones { + if trustZone.GetExternalServer() { + sb := provision.NewStatusBuilder(trustZone.Name, trustZone.GetKubernetesCluster()) + statusCh <- sb.Done("Ready", "Skipped waiting for external SPIRE server pod and service") + continue + } + if err := h.GetBundleAndEndpoint(ctx, statusCh, ds, trustZone, kubeCfgFile); err != nil { return err } } + return nil } diff --git a/pkg/plugin/provision/spirehelm/spirehelm_test.go b/pkg/plugin/provision/spirehelm/spirehelm_test.go index b539b9e..0127a8e 100644 --- a/pkg/plugin/provision/spirehelm/spirehelm_test.go +++ b/pkg/plugin/provision/spirehelm/spirehelm_test.go @@ -50,6 +50,36 @@ func TestSpireHelm_Deploy(t *testing.T) { assert.EqualExportedValues(t, want, statuses) } +func TestSpireHelm_Deploy_ExternalServer(t *testing.T) { + providerFactory := newFakeHelmSPIREProviderFactory() + spireHelm := NewSpireHelm(providerFactory) + + config := &config.Config{ + TrustZones: []*trust_zone_proto.TrustZone{ + fixtures.TrustZone("tz5"), + }, + AttestationPolicies: []*attestation_policy_proto.AttestationPolicy{}, + Plugins: fixtures.Plugins("plugins1"), + } + + ds := newFakeDataSource(t, config) + + statusCh, err := spireHelm.Deploy(context.Background(), ds, "fake-kube.cfg") + require.NoError(t, err, err) + + statuses := collectStatuses(statusCh) + want := []*provisionpb.Status{ + provision.StatusOk("Preparing", "Adding SPIRE Helm repo"), + provision.StatusDone("Prepared", "Added SPIRE Helm repo"), + provision.StatusOk("Installing", "Installing SPIRE CRDs for local5 in tz5"), + provision.StatusOk("Installing", "Installing SPIRE chart for local5 in tz5"), + provision.StatusDone("Installed", "Installation completed for local5 in tz5"), + provision.StatusDone("Ready", "Skipped waiting for external SPIRE server pod and service for local5 in tz5"), + provision.StatusDone("Ready", "Skipped waiting for external SPIRE server pod and service for local5 in tz5"), + } + assert.EqualExportedValues(t, want, statuses) +} + func TestSpireHelm_TearDown(t *testing.T) { providerFactory := newFakeHelmSPIREProviderFactory() spireHelm := NewSpireHelm(providerFactory) diff --git a/pkg/provider/helm/values.go b/pkg/provider/helm/values.go index d43c324..e7f537c 100644 --- a/pkg/provider/helm/values.go +++ b/pkg/provider/helm/values.go @@ -41,6 +41,7 @@ type spireServerValues struct { caKeyType string caTTL string controllerManagerEnabled bool + enabled bool fullnameOverride string logLevel string serverConfig trustprovider.TrustProviderServerConfig @@ -101,10 +102,13 @@ func (g *HelmValuesGenerator) GenerateValues() (map[string]any, error) { return nil, err } + spireServerEnabled := !g.trustZone.GetExternalServer() + ssv := spireServerValues{ caKeyType: "rsa-2048", caTTL: "12h", controllerManagerEnabled: true, + enabled: spireServerEnabled, fullnameOverride: "spire-server", logLevel: "DEBUG", serverConfig: tp.ServerConfig, @@ -330,6 +334,14 @@ func (s *spireAgentValues) generateValues() (map[string]any, error) { // generateValues generates the spire-server Helm values map. func (s *spireServerValues) generateValues() (map[string]any, error) { + if !s.enabled { + return map[string]any{ + "spire-server": map[string]any{ + "enabled": s.enabled, + }, + }, nil + } + if s.caKeyType == "" { return nil, fmt.Errorf("caKeyType value is empty") } @@ -364,6 +376,7 @@ func (s *spireServerValues) generateValues() (map[string]any, error) { return map[string]any{ "spire-server": map[string]any{ + "enabled": s.enabled, "caKeyType": s.caKeyType, "caTTL": s.caTTL, "controllerManager": map[string]any{ diff --git a/pkg/provider/helm/values_test.go b/pkg/provider/helm/values_test.go index c35af9e..2ecd30c 100644 --- a/pkg/provider/helm/values_test.go +++ b/pkg/provider/helm/values_test.go @@ -99,6 +99,7 @@ func TestHelmValuesGenerator_GenerateValues_success(t *testing.T) { }, }, }, + "enabled": true, "fullnameOverride": "spire-server", "logLevel": "DEBUG", "nodeAttestor": Values{ @@ -209,6 +210,7 @@ func TestHelmValuesGenerator_GenerateValues_success(t *testing.T) { }, }, }, + "enabled": true, "federation": Values{ "enabled": true, }, @@ -298,6 +300,7 @@ func TestHelmValuesGenerator_GenerateValues_success(t *testing.T) { }, }, }, + "enabled": true, "fullnameOverride": "spire-server", "logLevel": "DEBUG", "nodeAttestor": Values{ @@ -444,6 +447,7 @@ func TestHelmValuesGenerator_GenerateValues_AdditionalValues(t *testing.T) { }, }, }, + "enabled": true, "fullnameOverride": "spire-server", "logLevel": "DEBUG", "nodeAttestor": Values{ @@ -840,6 +844,7 @@ func TestMergeMaps(t *testing.T) { }, }, }, + "enabled": true, }, }, want: map[string]any{ @@ -864,6 +869,7 @@ func TestMergeMaps(t *testing.T) { }, }, }, + "enabled": true, }, }, }, @@ -1286,6 +1292,7 @@ func TestSpireServerValues_GenerateValues(t *testing.T) { caKeyType: "rsa-2048", caTTL: "12h", controllerManagerEnabled: true, + enabled: true, fullnameOverride: "spire-server", logLevel: "DEBUG", serverConfig: trustprovider.TrustProviderServerConfig{ @@ -1308,6 +1315,7 @@ func TestSpireServerValues_GenerateValues(t *testing.T) { "controllerManager": map[string]any{ "enabled": true, }, + "enabled": true, "fullnameOverride": "spire-server", "logLevel": "DEBUG", "nodeAttestor": Values{ @@ -1330,12 +1338,25 @@ func TestSpireServerValues_GenerateValues(t *testing.T) { }, wantErr: false, }, + { + name: "valid SPIRE server values, enabled set to false", + input: spireServerValues{ + enabled: false, + }, + want: map[string]any{ + "spire-server": map[string]any{ + "enabled": false, + }, + }, + wantErr: false, + }, { name: "invalid SPIRE server values, empty serviceType value", input: spireServerValues{ caKeyType: "rsa-2048", caTTL: "12h", controllerManagerEnabled: true, + enabled: true, fullnameOverride: "spire-server", logLevel: "DEBUG", serverConfig: trustprovider.TrustProviderServerConfig{ @@ -1361,6 +1382,7 @@ func TestSpireServerValues_GenerateValues(t *testing.T) { caKeyType: "rsa-2048", caTTL: "12h", controllerManagerEnabled: true, + enabled: true, fullnameOverride: "spire-server", logLevel: "DEBUG", serverConfig: trustprovider.TrustProviderServerConfig{