Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Workload Identity #414

Merged
merged 3 commits into from
Feb 13, 2022

Conversation

micnncim
Copy link
Contributor

@micnncim micnncim commented Feb 6, 2022

Description of your changes

Adds support for Workload Identity, with which credentials no longer need to be present in Secrets.

If InjectedIdentity is specified, a token source for application default credentials by a GCP Service Account, which is specified in the iam.gke.io/gcp-service-account annotation of a provider's Kubernetes Service Account, is used for authentication.

Signed-off-by: micnncim micnncim@gmail.com

Fixes #173

I have:

  • Read and followed Crossplane's contribution process.
  • Run make reviewable test to ensure this PR is ready for review.

How has this code been tested

Tested this in the following environment and process.

$ kubectl version --short
Client Version: v1.20.7
Server Version: v1.20.12-gke.1500
$ gcloud container clusters describe $CLUSTER --format="value(workloadIdentityConfig.workloadPool)"
$PROJECT_ID.svc.id.goog
$ kubectl get deploy crossplane \
    -o jsonpath="{.spec.template.spec.containers[*].image}" \
    -n crossplane-system
crossplane/crossplane:v1.6.2

Created a Provider and ProviderConfig with InjectedIdentity and then created a Topic managed resource:

$ cat <<EOF | kubectl apply -f -
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
  name: gcp-provider
spec:
  package: $PACKAGE # Use this version
---
apiVersion: gcp.crossplane.io/v1beta1
kind: ProviderConfig
metadata:
  name: default
spec:
  projectID: $PROJECT_ID
  credentials:
    source: InjectedIdentity
EOF

$ cat <<EOF | kubectl apply -f -
apiVersion: pubsub.gcp.crossplane.io/v1alpha1
kind: Topic
metadata:
  name: foo
spec:
  forProvider: {}
EOF

Note that Pods are emitting authentication errors until Workload Identity is configured:

$ kubectl logs gcp-provider-f2edfcc2cd84-f68bc774f-7qscf
// ...
2022-02-06T20:07:06.014Z        DEBUG   provider-gcp    Cannot create external resource {"controller": "managed/topic.pubsub.gcp.crossplane.io", "request": "/foo", "uid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "version": "123456789", "external-name": "foo", "error": "cannot create Topic: googleapi: Error 403: User not authorized to perform this action., forbidden"}

Ensured the Service Account is specified in Deployment though it's obvious:

$ kubectl get sa -o custom-columns=":metadata.name" | grep 'gcp-provider'
gcp-provider-xxxxxxxxxxxx
$ kubectl get deploy gcp-provider-xxxxxxxxxxxx \
    -o jsonpath="{.spec.template.spec.serviceAccountName}" \
gcp-provider-xxxxxxxxxxxx

Created a GCP Service Account and configured Workload Identity:

$ gcloud iam service-accounts create crossplane
$ gcloud projects add-iam-policy-binding $PROJECT_ID \
    --member "serviceAccount:crossplane@$PROJECT_ID.iam.gserviceaccount.com" \
    --role roles/admin
$ gcloud iam service-accounts add-iam-policy-binding \
    crossplane@$PROJECT_ID.iam.gserviceaccount.com \
    --role roles/iam.workloadIdentityUser \
    --member "serviceAccount:$PROJECT_ID.svc.id.goog[crossplane-system/gcp-provider-xxxxxxxxxxxx]"
$ kubectl annotate sa gcp-provider-xxxxxxxxxxxx \
    iam.gke.io/gcp-service-account=crossplane@$PROJECT_ID.iam.gserviceaccount.com

Waited for a little while and then confirmed it was successfully created:

$ gcloud pubsub topics describe foo
name: projects/$PROJECT_ID/topics/foo

Note that since Service Accounts managed by a controller are named the same as the current ProviderRevision and they are inconstant, it's necessary to fix a name for production use cases. crossplane/crossplane#2880 proposes a change for this.

Adds support for Workload Identity, with which credentials no longer
need to be present in Secrets.

If `InjectedIdentity` is specified, a token source for application
default credentials by a GCP Service Account, which is specified in the
`iam.gke.io/gcp-service-account` annotation of a provider's Kubernetes
Service Account.

Tested this in the following environment and process.

```
$ kubectl version --short
Client Version: v1.20.7
Server Version: v1.20.12-gke.1500
```

```
$ gcloud container clusters describe $CLUSTER --format="value(workloadIdentityConfig.workloadPool)"
$PROJECT_ID.svc.id.goog
```

```
$ kubectl get deploy crossplane \
    -o jsonpath="{.spec.template.spec.containers[*].image}" \
    -n crossplane-system
crossplane/crossplane:v1.6.2
```

Created a `Provider` and `ProviderConfig` with `InjectedIdentity` and
then created a `Topic` managed resource:

```
$ cat <<EOF | kubectl apply -f -
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
  name: gcp-provider
spec:
  package: $PACKAGE # Use this version
---
apiVersion: gcp.crossplane.io/v1beta1
kind: ProviderConfig
metadata:
  name: default
spec:
  projectID: $PROJECT_ID
  credentials:
    source: InjectedIdentity
EOF

$ cat <<EOF | kubectl apply -f -
apiVersion: pubsub.gcp.crossplane.io/v1alpha1
kind: Topic
metadata:
  name: foo
spec:
  forProvider: {}
EOF
```

Note that Pods are emitting authentication errors until Workload
Identity is configured:

```console
$ kubectl logs gcp-provider-f2edfcc2cd84-f68bc774f-7qscf
// ...
2022-02-06T20:07:06.014Z        DEBUG   provider-gcp    Cannot create
external resource {"controller":
"managed/topic.pubsub.gcp.crossplane.io", "request": "/foo", "uid":
"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "version": "123456789",
"external-name": "foo", "error": "cannot create Topic: googleapi: Error
403: User not authorized to perform this action., forbidden"}
```

Ensured the Service Account is specified in Deployment though it's
obvious:

```
$ kubectl get sa -o custom-columns=":metadata.name" | grep 'gcp-provider'
gcp-provider-xxxxxxxxxxxx
$ kubectl get deploy gcp-provider-xxxxxxxxxxxx \
    -o jsonpath="{.spec.template.spec.serviceAccountName}" \
gcp-provider-xxxxxxxxxxxx
```

Created a GCP Service Account and configured Workload Identity:

```
$ gcloud iam service-accounts create crossplane
$ gcloud projects add-iam-policy-binding $PROJECT_ID \
    --member
"serviceAccount:crossplane@$PROJECT_ID.iam.gserviceaccount.com" \
    --role roles/admin
$ gcloud iam service-accounts add-iam-policy-binding \
    crossplane@$PROJECT_ID.iam.gserviceaccount.com \
    --role roles/iam.workloadIdentityUser \
    --member "serviceAccount:$PROJECT_ID.svc.id.goog[crossplane-system/gcp-provider-xxxxxxxxxxxx]"
$ kubectl annotate sa gcp-provider-xxxxxxxxxxxx \
    iam.gke.io/gcp-service-account=crossplane@$PROJECT_ID.iam.gserviceaccount.com
```

Waited for a little while and then confirmed it was successfully created:

```
$ gcloud pubsub topics describe foo
name: projects/$PROJECT_ID/topics/foo
```

Note that since Service Accounts managed by a controller are named the
same as the current `ProviderRevision` and they are inconstant, it's
necessary to fix a name for production use cases.

Signed-off-by: micnncim <micnncim@gmail.com>
Copy link
Contributor

@turkenh turkenh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking great, this is a highly requested feature.
Thanks a lot for your contribution @micnncim!

Your PR description looks awesome, would you mind migrating it to a similar document as AUTHENTICATION.md in provider-aws?

@micnncim
Copy link
Contributor Author

micnncim commented Feb 7, 2022

@turkenh Thanks for your approval. I’m happy to provide a document, but want to make sure if crossplane/crossplane#2880 looks good to you before that since the process (and then a document) will change depending on that change.

@turkenh
Copy link
Contributor

turkenh commented Feb 8, 2022

@micnncim left a comment there. /cc @hasheddan

@turkenh
Copy link
Contributor

turkenh commented Feb 9, 2022

@micnncim my suggestion would be to document with existing Crossplane versions similar to provider-aws and merge this PR.
Because I believe it may take some time to get your change discussed/merged/released. We can follow up with an update once/if there are changes as you suggested.

@micnncim
Copy link
Contributor Author

micnncim commented Feb 9, 2022

@micnncim my suggestion would be to document with existing Crossplane versions similar to provider-aws and merge this PR. Because I believe it may take some time to get your change discussed/merged/released. We can follow up with an update once/if there are changes as you suggested.

Yeah, that makes sense. I created a document for authentication.

Adds a guide for configuring authentication to Google Cloud APIs.

Though this provides enough information to cover the feature added in
this PR, we should improve the way to configure a
`ServiceAccount` for practical use cases since in any methods users need
to reconfigure IAM stuff every time a new `ProviderRevision` is created.

Signed-off-by: micnncim <micnncim@gmail.com>
Copy link
Contributor

@turkenh turkenh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking awesome, thanks a lot @micnncim!

docs/AUTHENTICATION.md Show resolved Hide resolved
Adds a step to define variables' names so that users can smoothly set up
authentication with Workload Identity in `provider-gcp`.

Signed-off-by: micnncim <micnncim@gmail.com>
@turkenh turkenh merged commit 27f804f into crossplane-contrib:master Feb 13, 2022
@micnncim micnncim deleted the workload-identity branch February 14, 2022 01:55
@ethai
Copy link

ethai commented Dec 14, 2022

this is a great feature, but is there any thoughts to augmenting this so it can support multi-tenant project infra. I asked about this on slack and was suggested to use vcluster as supplementary to address. Current implementation of ProviderConfig only support the use of Secret / keys but wondering if there are thoughts on how to achieve it workload identity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Enable Workload Identity for provider-gcp when running on GKE
3 participants