Skip to content

Deleting a Kubernetes secret does not remove the corresponding GCP Certificate Manager certificate #50

@aniketwdubey

Description

@aniketwdubey

Summary

When a Kubernetes TLS secret that has been synced to GCP Certificate Manager is deleted, the corresponding remote GCP certificate is not cleaned up. This leaves orphaned certificates in GCP that require manual deletion.

Current Behavior

  • The informer in cmd/cert-manager-sync/main.go only registers AddFunc and UpdateFunc — there is no DeleteFunc.
  • The GCP store (stores/gcpcm/gcpcm.go) only implements CreateCert and UpdateCert — there is no DeleteCert.
  • HandleSecret in pkg/certmanagersync/certmanagersync.go only reconciles existing watched secrets; it has no awareness of deletions.
  • The operator writes the remote GCP certificate name back to the secret annotation (cert-manager-sync.lestak.sh/gcp-certificate-name) for future updates, but there is no reverse cleanup path when the source secret disappears.

Expected Behavior

When a Kubernetes TLS secret with GCP sync annotations is deleted, the operator should delete the corresponding certificate from GCP Certificate Manager automatically.

Proposed Solution

Minimum viable change

  1. Add a DeleteFunc to the informer.
  2. On secret delete, detect whether the secret had GCP sync enabled.
  3. Read the stored GCP certificate name from the annotation cert-manager-sync.lestak.sh/gcp-certificate-name.
  4. Call GCP Certificate Manager delete for that certificate.
  5. Treat NotFound as success so cleanup is idempotent.

Better long-term design

Use a finalizer on watched secrets. This would let the operator:

  1. See the delete intent before the secret is fully removed.
  2. Read annotations and any required config directly from the live object.
  3. Delete remote certificates reliably.
  4. Remove the finalizer only after cleanup succeeds (or after a clear failure policy).

Suggested GCP store change

Add DeleteCert(ctx context.Context) error to stores/gcpcm/gcpcm.go:

  • Build a DeleteCertificateRequest using s.CertificateName.
  • Treat NotFound as success.

Optionally extend the store contract with a delete-capable interface:

type DeletableRemoteStore interface {
    RemoteStore
    Delete(ctx context.Context) error
}

Design Questions for Maintainers

  • Should delete reconciliation apply only to GCP, or to every remote store (AWS ACM, Vault, etc.)?
  • What should happen if the remote delete fails — block finalizer removal and retry, or emit a warning and allow the delete to proceed?
  • Should the operator delete only resources it created, or any resource referenced by annotation?
  • If the same remote certificate is shared across multiple secrets, how should ownership be tracked?

Testing Considerations

  • Unit test GCP delete request generation.
  • Unit test delete reconciliation when annotation is present.
  • Unit test idempotent delete when the remote cert is already gone.
  • Unit test that non-GCP secrets are ignored during delete.
  • If finalizers are added, test retry and finalizer removal behavior.

Context

We discovered this gap while running cert-manager-sync in production with the GCP Certificate Manager backend. Currently, manual cleanup in GCP is required whenever synced secrets are deleted from Kubernetes.

Happy to contribute a PR if the maintainers agree on the approach.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions