In my current project, the PKI heirarchy I want to create needs to be dynamic as automated and agile certificate topologies bring a lot of value to the defensive security story.

With the security hat on, I want the root of the certificate hierarchy, the root CA, rooted with its private key in an HSM. Since I'm running this on GKE it makes sense to leverage GCP's services here, and indeed there is a cert-manager issuer controller for googles Private CA Services.

google-cas-issuer/README.md at main · jetstack/google-cas-issuer
cert-manager issuer for Google CA Service. Contribute to jetstack/google-cas-issuer development by creating an account on GitHub.

Yikes, it's not cheap, and I couldn't figure out if you can sign intermediates that reside ouside their CA product (external certs can be imported). Choosing KMS can offer us much better pricing to get an HMS-backed key if we manage the CA operations ourselves, and that's a big point of using cert-manager in the first place.

Searching for "cert-manager kms" lands you on Skyscanner's excellent opensource Issuer controller that gives cert-manager the power to sign certs with an AWS KMS key, but I'm not on AWS.

GitHub - Skyscanner/kms-issuer: KMS issuer is a cert-manager Certificate Request controller that uses AWS KMS to sign the certificate request.
KMS issuer is a cert-manager Certificate Request controller that uses AWS KMS to sign the certificate request. - Skyscanner/kms-issuer

This is where the opensource spirit comes in and I make a project based on Skyscanner's. I initially tried to make a PR to add GCP KMS capabilities to the project, but while the code was able to act as an excellent framework for implementing an external Issuer, it's a bit too tightly woven with AWS semantics to be extended.

GitHub - drzzlio/kms-issuer: GCP KMS issuer is a cert-manager Certificate Request controller that uses GCP KMS to sign the certificate request.
GCP KMS issuer is a cert-manager Certificate Request controller that uses GCP KMS to sign the certificate request. - drzzlio/kms-issuer

With this I can keep my root certificate long-lived and secure in a Google HSM (and managed via config connector), while keeping my intermediate and leaf certificates agile and oft updated. Give it a try and lmk what you think.

Here is an example deployment with config connector used to create the KSMCrypto(Ring|Key) and setup workload identity and policy to give the issuer access to run the sign operation via the GCP KMS API:

gitops/apps/yoke/base/issuer at master · drzzlio/gitops
Smooth is fast. Contribute to drzzlio/gitops development by creating an account on GitHub.

Basically it boils down to these resources:

apiVersion: kms.cnrm.cloud.google.com/v1beta1
kind: KMSKeyRing
metadata:
  name: yoke
  annotations:
    cnrm.cloud.google.com/deletion-policy: "abandon"
spec:
  location: us-central1
---
apiVersion: kms.cnrm.cloud.google.com/v1beta1
kind: KMSCryptoKey
metadata:
  name: yokeroot
spec:
  keyRingRef:
    name: yoke
  purpose: ASYMMETRIC_SIGN
  versionTemplate:
    algorithm: RSA_SIGN_PSS_2048_SHA256
    protectionLevel: HSM
  importOnly: false
---
apiVersion: iam.cnrm.cloud.google.com/v1beta1
kind: IAMServiceAccount
metadata:
  name: yoke-kms-issuer
spec:
  displayName: yoke-kms-issuer
---
apiVersion: iam.cnrm.cloud.google.com/v1beta1
kind: IAMPolicyMember
metadata:
  name: yoke-kms-issuer-key-signerverifier
spec:
  member: serviceAccount:yoke-kms-issuer@gptops-playground.iam.gserviceaccount.com
  role: roles/cloudkms.signerVerifier
  resourceRef:
    apiVersion: kms.cnrm.cloud.google.com/v1beta1
    kind: KMSCryptoKey
    name: yokeroot
---
apiVersion: iam.cnrm.cloud.google.com/v1beta1
kind: IAMPolicy
metadata:
  name: yoke-kms-issuer-workloadidentity
spec:
  resourceRef:
    apiVersion: iam.cnrm.cloud.google.com/v1beta1
    kind: IAMServiceAccount
    name: yoke-kms-issuer
  bindings:
    - role: roles/iam.workloadIdentityUser
      members:
        - serviceAccount:gptops-playground.svc.id.goog[yoke/kms-issuer-controller-manager]
---
apiVersion: iam.cnrm.cloud.google.com/v1beta1
kind: IAMPolicyMember
metadata:
  name: yoke-kms-issuer-key-viewer
spec:
  member: serviceAccount:yoke-kms-issuer@gptops-playground.iam.gserviceaccount.com
  role: roles/cloudkms.viewer
  resourceRef:
    apiVersion: kms.cnrm.cloud.google.com/v1beta1
    kind: KMSCryptoKey
    name: yokeroot  
---
apiVersion: cert-manager.drzzl.io/v1alpha1
kind: KMSIssuer
metadata:
  name: yokeroot
spec:
  keyRef: 
    name: yokeroot
  commonName: Yoke Ephemeral Cluster Root
  duration: 87600h # 10 years
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: kms-test-ca
spec:
  isCA: true
  commonName: Test Cluster KMS CA
  secretName: test-ca-pki
  privateKey:
    algorithm: RSA
    size: 2048
  issuerRef:
    name: yokeroot
    kind: KMSIssuer
    group: cert-manager.drzzl.io