Google Certificate Authority Service Issuer
Issue certificates using Google CA Service.
Getting started
Prerequisites
CAS enabled GCP project
Enable the Certificate Authority API (privateca.googleapis.com) in your GCP project by following the official documentation.
CAS managed Certificate Authorities
You can create a ca pool containing a certificate authority in your current Google project with:
gcloud privateca pools create my-pool --location us-east1gcloud privateca roots create my-ca --pool my-pool --key-algorithm "ec-p384-sha384" --subject="CN=my-root,O=my-ca,OU=my-ou" --max-chain-length=2 --location us-east1
You should also enable the root CA you just created when prompted by gcloud.
It is recommended to create subordinate CAs for signing leaf certificates. See the official documentation.
cert-manager
If not already running in the cluster, install cert-manager by following the official documentation.
Installing Google CAS Issuer for cert-manager
Assuming that you have installed cert-manager in the cert-manager namespace, you can use a single kubectl command to install Google CAS Issuer. Visit the GitHub releases, select the latest release and copy the command, e.g.
kubectl apply -f https://github.com/jetstack/google-cas-issuer/releases/download/v0.5.2/google-cas-issuer-v0.5.2.yaml
You can then skip to the Setting up Google Cloud IAM section.
Customize the deployment (for developers)
Examine the ClusterRole and ClusterRoleBinding in config/rbac/role.yaml and config/rbac/role_binding.yaml. By default, these give the ksa-google-cas-issuer Kubernetes service account in the cert-manager namespace all the necessary permissions. Customize these to your needs.
kubectl create serviceaccount -n cert-manager ksa-google-cas-issuerkubectl apply -f config/rbac/role.yamlkubectl apply -f config/rbac/role_binding.yaml
Install the Google CAS Issuer CRDs in config/crd. These manifests use Kustomize (hence the -k option).
kubectl apply -k config/crd
Build and push the controller image
Note: you can skip this step if using the public images at quay.io.
To build the image, ensure you have kubebuilder installed.
Build the docker image:
make docker-build
Push the docker image or load it into kind for testing
make docker-push || kind load docker-image quay.io/jetstack/cert-manager-google-cas-issuer:latest
Deploy the controller
Deploy the issuer controller:
cat <<EOF | kubectl apply -f -apiVersion: apps/v1kind: Deploymentmetadata:name: google-cas-issuernamespace: cert-managerlabels:app: google-cas-issuerspec:selector:matchLabels:app: google-cas-issuerreplicas: 1template:metadata:labels:app: google-cas-issuerspec:serviceAccountName: ksa-google-cas-issuercontainers:# update the image to your registry if you built and pushed your own image.- image: quay.io/jetstack/cert-manager-google-cas-issuer:latestimagePullPolicy: IfNotPresentname: google-cas-issuerresources:limits:cpu: 100mmemory: 30Mirequests:cpu: 100mmemory: 20MiterminationGracePeriodSeconds: 10EOF
By default, the Google CAS Issuer controller will be deployed into the cert-manager namespace.
NAME READY STATUS RESTARTS AGEcert-manager-6cd8cb4b7c-m8q4k 1/1 Running 0 34hcert-manager-cainjector-685b87b86-4jvtb 1/1 Running 1 34hcert-manager-webhook-76978fbd4c-rrx85 1/1 Running 0 34hgoogle-cas-issuer-687685dc46-lrjkc 1/1 Running 0 28h
Setting up Google Cloud IAM
Firstly, create a Google Cloud IAM service account. This service account will be used by the CAS Issuer to access the Google Cloud CAS APIs.
gcloud iam service-accounts create sa-google-cas-issuer
Apply the appropriate IAM bindings to this account. This example permits the least privilege, to create certificates (i.e. roles/privateca.certificates.create) from a specified CA pool (my-pool), but you can use other roles as necessary (see Predefined Roles for more details).
gcloud privateca pools add-iam-policy-binding my-pool --role=roles/privateca.certificateRequester --member="serviceAccount:sa-google-cas-issuer@$(gcloud config get-value project | tr ':' '/').iam.gserviceaccount.com" --location=us-east1
Inside GKE with workload identity
Workload identity lets you bind a Kubernetes service account to a Google Cloud service account. In order to take advantage of this, your GKE cluster must be set up to use it. If you want to create a cluster from scratch to test the issuer, you can enable it like so:
gcloud container clusters create test --region us-east1 --num-nodes=1 --preemptible \--workload-pool=$(gcloud config get-value project | tr ':' '/').svc.id.goog
If you want to use the CAS issuer in an existing cluster, you can still enable the workload identity feature with:
gcloud container clusters update CLUSTER_NAME --region=CLUSTER_REGION \--workload-pool="$(gcloud config get-value project | tr ':' '/').svc.id.goog"
Bind the Kubernetes service account (ksa-google-cas-issuer) to the Google Cloud service account:
export PROJECT=$(gcloud config get-value project | tr ':' '/')gcloud iam service-accounts add-iam-policy-binding \--role roles/iam.workloadIdentityUser \--member "serviceAccount:$PROJECT.svc.id.goog[cert-manager/ksa-google-cas-issuer]" \sa-google-cas-issuer@${PROJECT:?PROJECT is not set}.iam.gserviceaccount.comkubectl annotate serviceaccount \--namespace cert-manager \ksa-google-cas-issuer \iam.gke.io/gcp-service-account=sa-google-cas-issuer@${PROJECT:?PROJECT is not set}.iam.gserviceaccount.com \--overwrite=true
Outside GKE or in an unrelated GCP project
Create a key for the service account and download it to a local JSON file.
gcloud iam service-accounts keys create $(gcloud config get-value project | tr ':' '/')-key.json \--iam-account sa-google-cas-issuer@$(gcloud config get-value project | tr ':' '/').iam.gserviceaccount.com
The service account key should be stored in a Kubernetes secret in your cluster so it can be accessed by the CAS Issuer controller.
kubectl -n cert-manager create secret generic googlesa --from-file $(gcloud config get-value project | tr ':' '/')-key.json
Configuring the Issuer
cert-manager is configured for Google CAS using either a GoogleCASIssuer (namespace-scoped) or a GoogleCASClusterIssuer (cluster-wide).
Inspect the sample configurations below and update the PROJECT_ID as appropriate. Credentials can be omitted if you have configured the CAS issuer controller with Workload Identity.
# googlecasissuer-sample.yamlapiVersion: cas-issuer.jetstack.io/v1beta1kind: GoogleCASIssuermetadata:name: googlecasissuer-samplespec:project: $PROJECT_IDlocation: us-east1caPoolId: my-pool# credentials are optional if workload identity is enabledcredentials:name: "googlesa"key: "$PROJECT_ID-key.json"
kubectl apply -f googlecasissuer-sample.yaml
or
# googlecasclusterissuer-sample.yamlapiVersion: cas-issuer.jetstack.io/v1beta1kind: GoogleCASClusterIssuermetadata:name: googlecasclusterissuer-samplespec:project: $PROJECT_IDlocation: us-east1caPoolId: my-pool# credentials are optional if workload identity is enabledcredentials:name: "googlesa"key: "$PROJECT_ID-key.json"
kubectl apply -f googlecasclusterissuer-sample.yaml
Creating your first certificate
You can now create certificates as normal, but ensure the IssuerRef is set to the GoogleCASIssuer or GoogleCASClusterIssuer created in the previous step.
apiVersion: cert-manager.io/v1kind: Certificatemetadata:name: demo-certificatenamespace: defaultspec:# The secret name to store the signed certificatesecretName: demo-cert-tls# Common NamecommonName: cert-manager.io.demo# DNS SANdnsNames:- cert-manager.io- jetstack.io# Duration of the certificateduration: 24h# Renew 8 hours before the certificate expirationrenewBefore: 8h# Important: Ensure the issuerRef is set to the issuer or cluster issuer configured earlierissuerRef:group: cas-issuer.jetstack.iokind: GoogleCASClusterIssuer # or GoogleCASIssuername: googlecasclusterissuer-sample # or googlecasissuer-sample
kubectl apply -f demo-certificate.yaml
In short time, the certificate will be requested and made available to the cluster.
kubectl get certificates,secretNAME READY SECRET AGEcertificate.cert-manager.io/demo-certificate True demo-cert-tls 1mNAME TYPE DATA AGEsecret/demo-cert-tls kubernetes.io/tls 3 1m