Browse Source

Merge pull request #783 from AtzeDeVries/allow-gcp-cross-project-secrets

GCP: allow cluster to be in different project
paul-the-alien[bot] 4 years ago
parent
commit
439ecfaf9d

+ 1 - 0
apis/externalsecrets/v1alpha1/secretstore_gcpsm_types.go

@@ -35,6 +35,7 @@ type GCPWorkloadIdentity struct {
 	ServiceAccountRef esmeta.ServiceAccountSelector `json:"serviceAccountRef"`
 	ClusterLocation   string                        `json:"clusterLocation"`
 	ClusterName       string                        `json:"clusterName"`
+	ClusterProjectID  string                        `json:"clusterProjectID,omitempty"`
 }
 
 // GCPSMProvider Configures a store to sync secrets using the GCP Secret Manager provider.

+ 1 - 0
apis/externalsecrets/v1beta1/secretstore_gcpsm_types.go

@@ -35,6 +35,7 @@ type GCPWorkloadIdentity struct {
 	ServiceAccountRef esmeta.ServiceAccountSelector `json:"serviceAccountRef"`
 	ClusterLocation   string                        `json:"clusterLocation"`
 	ClusterName       string                        `json:"clusterName"`
+	ClusterProjectID  string                        `json:"clusterProjectID,omitempty"`
 }
 
 // GCPSMProvider Configures a store to sync secrets using the GCP Secret Manager provider.

+ 4 - 0
config/crds/bases/external-secrets.io_clustersecretstores.yaml

@@ -443,6 +443,8 @@ spec:
                                 type: string
                               clusterName:
                                 type: string
+                              clusterProjectID:
+                                type: string
                               serviceAccountRef:
                                 description: A reference to a ServiceAccount resource.
                                 properties:
@@ -1746,6 +1748,8 @@ spec:
                                 type: string
                               clusterName:
                                 type: string
+                              clusterProjectID:
+                                type: string
                               serviceAccountRef:
                                 description: A reference to a ServiceAccount resource.
                                 properties:

+ 4 - 0
config/crds/bases/external-secrets.io_secretstores.yaml

@@ -443,6 +443,8 @@ spec:
                                 type: string
                               clusterName:
                                 type: string
+                              clusterProjectID:
+                                type: string
                               serviceAccountRef:
                                 description: A reference to a ServiceAccount resource.
                                 properties:
@@ -1749,6 +1751,8 @@ spec:
                                 type: string
                               clusterName:
                                 type: string
+                              clusterProjectID:
+                                type: string
                               serviceAccountRef:
                                 description: A reference to a ServiceAccount resource.
                                 properties:

+ 8 - 0
deploy/crds/bundle.yaml

@@ -334,6 +334,8 @@ spec:
                                   type: string
                                 clusterName:
                                   type: string
+                                clusterProjectID:
+                                  type: string
                                 serviceAccountRef:
                                   description: A reference to a ServiceAccount resource.
                                   properties:
@@ -1295,6 +1297,8 @@ spec:
                                   type: string
                                 clusterName:
                                   type: string
+                                clusterProjectID:
+                                  type: string
                                 serviceAccountRef:
                                   description: A reference to a ServiceAccount resource.
                                   properties:
@@ -2775,6 +2779,8 @@ spec:
                                   type: string
                                 clusterName:
                                   type: string
+                                clusterProjectID:
+                                  type: string
                                 serviceAccountRef:
                                   description: A reference to a ServiceAccount resource.
                                   properties:
@@ -3739,6 +3745,8 @@ spec:
                                   type: string
                                 clusterName:
                                   type: string
+                                clusterProjectID:
+                                  type: string
                                 serviceAccountRef:
                                   description: A reference to a ServiceAccount resource.
                                   properties:

+ 2 - 0
docs/snippets/gcpsm-wi-secret-store.yaml

@@ -12,6 +12,8 @@ spec:
           clusterLocation: europe-central2
           # name of the GKE cluster
           clusterName: example-workload-identity
+          # projectID of the cluster (if omitted defaults to spec.provider.gcpsm.projectID)
+          clusterProjectID: my-cluster-project
           # reference the sa from above
           serviceAccountRef:
             name: team-a

+ 18 - 3
pkg/provider/gcp/secretmanager/secretsmanager_workload_identity.go

@@ -49,6 +49,7 @@ const (
 	errFetchPodToken  = "unable to fetch pod token: %w"
 	errFetchIBToken   = "unable to fetch identitybindingtoken: %w"
 	errGenAccessToken = "unable to generate gcp access token: %w"
+	errNoProjectID    = "unable to find ProjectID in storeSpec"
 )
 
 // workloadIdentity holds all clients and generators needed
@@ -114,17 +115,21 @@ func (w *workloadIdentity) TokenSource(ctx context.Context, store esv1beta1.Gene
 		saKey.Namespace = *wi.ServiceAccountRef.Namespace
 	}
 
+	clusterProjectID, err := clusterProjectID(spec)
+	if err != nil {
+		return nil, err
+	}
 	sa := &v1.ServiceAccount{}
-	err := kube.Get(ctx, saKey, sa)
+	err = kube.Get(ctx, saKey, sa)
 	if err != nil {
 		return nil, err
 	}
 
 	idProvider := fmt.Sprintf("https://container.googleapis.com/v1/projects/%s/locations/%s/clusters/%s",
-		spec.Provider.GCPSM.ProjectID,
+		clusterProjectID,
 		wi.ClusterLocation,
 		wi.ClusterName)
-	idPool := fmt.Sprintf("%s.svc.id.goog", spec.Provider.GCPSM.ProjectID)
+	idPool := fmt.Sprintf("%s.svc.id.goog", clusterProjectID)
 	gcpSA := sa.Annotations[gcpSAAnnotation]
 
 	resp, err := w.saTokenGenerator.Generate(ctx, idPool, saKey.Name, saKey.Namespace)
@@ -257,3 +262,13 @@ func (g *gcpIDBindTokenGenerator) Generate(ctx context.Context, client *http.Cli
 	}
 	return idBindToken, nil
 }
+
+func clusterProjectID(spec *esv1beta1.SecretStoreSpec) (string, error) {
+	if spec.Provider.GCPSM.Auth.WorkloadIdentity.ClusterProjectID != "" {
+		return spec.Provider.GCPSM.Auth.WorkloadIdentity.ClusterProjectID, nil
+	} else if spec.Provider.GCPSM.ProjectID != "" {
+		return spec.Provider.GCPSM.ProjectID, nil
+	} else {
+		return "", fmt.Errorf(errNoProjectID)
+	}
+}

+ 39 - 0
pkg/provider/gcp/secretmanager/secretsmanager_workload_identity_test.go

@@ -159,6 +159,15 @@ func TestWorkloadIdentity(t *testing.T) {
 	}
 }
 
+func TestClusterProjectID(t *testing.T) {
+	clusterID, err := clusterProjectID(defaultStore().GetSpec())
+	assert.Nil(t, err)
+	assert.Equal(t, clusterID, "1234")
+	externalClusterID, err := clusterProjectID(defaultExternalStore().GetSpec())
+	assert.Nil(t, err)
+	assert.Equal(t, externalClusterID, "5678")
+}
+
 func TestSATokenGen(t *testing.T) {
 	corev1 := &fakeK8sV1{}
 	g := &k8sSATokenGenerator{
@@ -292,6 +301,16 @@ func defaultStore() *esv1beta1.SecretStore {
 	}
 }
 
+func defaultExternalStore() *esv1beta1.SecretStore {
+	return &esv1beta1.SecretStore{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "foobar",
+			Namespace: "default",
+		},
+		Spec: defaultExternalStoreSpec(),
+	}
+}
+
 func defaultClusterStore() *esv1beta1.ClusterSecretStore {
 	return &esv1beta1.ClusterSecretStore{
 		TypeMeta: metav1.TypeMeta{
@@ -323,6 +342,26 @@ func defaultStoreSpec() esv1beta1.SecretStoreSpec {
 	}
 }
 
+func defaultExternalStoreSpec() esv1beta1.SecretStoreSpec {
+	return esv1beta1.SecretStoreSpec{
+		Provider: &esv1beta1.SecretStoreProvider{
+			GCPSM: &esv1beta1.GCPSMProvider{
+				Auth: esv1beta1.GCPSMAuth{
+					WorkloadIdentity: &esv1beta1.GCPWorkloadIdentity{
+						ServiceAccountRef: esmeta.ServiceAccountSelector{
+							Name: "example",
+						},
+						ClusterLocation:  "example",
+						ClusterName:      "foobar",
+						ClusterProjectID: "5678",
+					},
+				},
+				ProjectID: "1234",
+			},
+		},
+	}
+}
+
 type storeMutator func(spc esv1beta1.GenericStore)
 
 func composeStore(store esv1beta1.GenericStore, mutators ...storeMutator) esv1beta1.GenericStore {