---
title: certificates.k8s.io Adoption
authors:
  - "@joshvanl"
reviewers:
  - "@joshvanl"
approvers:
  - "@munnerz"
  - @jetstack/team-cert-manager
editor: "@joshvanl"
creation-date: 2021-02-09
last-updated: 2021-07-26
status: implemented
---

# certificates.k8s.io Adoption

## Table of Contents

<!-- toc -->
- [Summary](#summary)
- [Motivation](#motivation)
  * [Third Party Projects](#third-party-projects)
  * [Security](#security)
  * [cert-manager Webhook Dependency](#cert-manager-webhook-dependency)
  * [CertificateSigningRequest Migration Path](#certificatesigningrequest-migration-path)
  * [Goals](#goals)
  * [Non-Goals](#non-goals)
- [Reasons for Approach](#reasons-for-approach)
  * [Read all access to CertificateSigningRequests](#read-all-access-to-certificatesigningrequests)
  * [Namespaced issuers referenced by CertificateSigningRequest](#namespaced-issuers-referenced-by-certificatesigningrequest)
  * [Duration Field](#duration-field)
  * [CA Field](#ca-field)
  * [External Issuers](#external-issuers)
- [Proposal](#proposal)
  * [Signers](#signers)
  * [Conditions](#conditions)
  * [API Changes](#api-changes)
  * [Upgrading](#upgrading)
  * [Graduation](#graduation)
<!-- /toc -->

## Summary

In Kubernetes v1.19 the
[`CertificateSigningRequest`](https://github.com/kubernetes/api/blob/48bd8381a38a486f8b3cdf28cf7334a45b182f2e/certificates/v1/types.go#L41)
resource graduated to `certificates.k8s.io/v1`. This makes the concept of
requesting, signing, and consuming certificates in Kubernetes first-class
objects. cert-manager is well placed to serve this resource type as it becomes
more popular in the wider community, whilst preserving all the features and
extensions that cert-manager has to offer.


## Motivation

### Third Party Projects

With the extensiveness of cert-manager’s ecosystem, it is well placed to manage
the core Kubernetes resource type. This gives cert-manager the ability to
integrate with components that it otherwise wouldn't have, without changes to the
third-party project.


### Security

Having the "request" resource managed inside the API server has a better
security profile (i.e. `UserInfo` fields are managed by the API server rather
than a configurable webhook,
[see](https://github.com/cert-manager/cert-manager/pull/3630)).


### cert-manager Webhook Dependency

Consumers of the certificates.k8s.io `CertificateRequest` do not require
cert-manager as a hard dependency. cert-manager can cause issues with the order
of operation scenarios during installation or upgrades. For example, the
`CertificateRequest` resource needs to be installed and the webhook ready for
consumers/clients of this API to make requests.

In some set-ups, the cert-manager installation is handled by two separate teams:

- The cert-manager CRDs and webhook are installed and managed by the
  operations team, which allows the operations team to enforce cluster
  policies;
- The cert-manager controller, cainjector and acmesolver are installed and
  managed by the developer team.

By supporting the built-in `CertificateSigningRequest` resource, we decrease the
coupling between the operations team and the developer team. Since the
`CertificateSigningRequest` resource is built-in, it does not need any
`CustomResourceDefinition` installation, and the webhook for this resource is
integrated to the API server.

### CertificateSigningRequest Migration Path

Support for the `CertificateSigningRequest` resource in cert-manager is a
stepping stone to migrating to this resource instead of the `CertificateRequest`
resource, if the project does indeed want to migrate.


### Goals

* Add `CertificateSigningRequest` signer controllers to cert-manager for all
    `Issuer` types
* Pave a way for replacement of the `CertificateRequest` resource for `CertificateSigningRequest`

### Non-Goals

* Remove the `CertificateRequest` resource before `CertificateSigningRequest` is
    fully supported and a migration path is in place

---

## Reasons for Approach

Below are a summary of properties of the `CertificateSigningRequest` which
cause issues or caveats which need to be addressed when adding support for the
resource.

### Read all access to CertificateSigningRequests

`CertificateSigningRequests` are cluster scoped resources, whereas
`CertificateRequests` are not. This means that in order for users to have the
same debugging and usage capabilities of the `CertificateRequest`, users will
have the ability to list all requests in the cluster, rather than being scoped to
specific namespaces. This is a regression in certificate privacy.

Whilst this is a regression in privacy, it could be an accepted limitation where
the real world consequences are minimal.

### Namespaced issuers referenced by CertificateSigningRequest

`CertificateSigningRequests` are cluster scoped resources, whereas
`CertificateRequests` are not. cert-manager has the concept of namespaced
issuers that may only be referenced by `CertificateRequests` that reside in
the same namespace. Since `CertificateSigningRequests` can reference any
namespaced `Issuer`, there must be some mechanism to prevent privilege
escalation (Alice references an issuer in Bob's namespace).

cert-manager will enforce an
[RBAC](https://kubernetes.io/docs/reference/access-authn-authz/rbac/) noun and
verb whereby the requester must have this role bound to them in order for the
`CertificateSigningRequest` referencing a namespaced `Issuer` be approved by the
cert-manager controller. See [here](#conditions).

This will be done via a
[`SubjectAccessReview`](https://github.com/kubernetes/api/blob/4a626d306b987a4096cf0784ec01af1be2f6d67f/authorization/v1/types.go#L52)
against the `UserInfo` fields on the `CertificateSigningRequest`. The user must
hold the following permissions to reference a namespaced signer:

```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: cert-manager-referencer:my-issuer
  namespace: sandbox
rules:
- apiGroups: ["cert-manager.io"]
  resources: ["signers"]
  verbs:     ["reference"]
  resourceNames:
  - "my-issuer" # To give permission to _only_ reference Issuers with the name 'my-issuer'
  - "*" # To give permission to reference Issuers with any name in this namespace
```

### Duration Field

[Until 1.22](https://github.com/kubernetes/kubernetes/pull/99494)
`CertificateSigningRequests` did not include a `duration` field. To have parity
with the `CertificateRequest` resource, the duration field will be moved to the
annotation `experimental.cert-manager.io/request-duration` who's value is a [Go
time duration string](https://golang.org/pkg/time/#Duration.String).

When 1.22 is released, cert-manager can optimistically read the
`expirationSeconds` `CertificateSigningRequest` field to discover the requested
duration. If this field hasn't been set or the user is using an older version of
Kubernetes, cert-manager can fallback to this annotation.

### CA Field

`CertificateSigningRequests` do not include a `ca` field. This field has been
proven to be problematic in cert-manager and controversial as it can encourage
TLS anti-patterns. Attempting to add a `ca` style field to
`CertificateSigningRequests` in the form of an annotation also causes issues in
controller reconciliation, since updating the status and metadata fields are
separate endpoints; if a successful status update occurs but the annotation
update fails, the `CertificateSigningRequest` is left in a bad state with no way
of recovery.

For these reasons, the `ca` field has been omitted with no intention of
including it. Users are advised to share CA data out of band to the
`CertificateSigningRequest` resource.

### External Issuers

All current [external
issuers](https://cert-manager.io/docs/configuration/external/) are built for the
`CertificateRequest` resource. The project should continue to support this
resource indefinitely, and provide an example project for creating external
signers if and when the migration to `CertificateSigningRequests` occurs.


---


## Proposal

cert-manager will support both `CertificateRequest` and
`CertificateSigningRequest` types until, and if, the project completely migrates
to the `CertificateSigningRequest` resource. The `Certificates` controller will
continue to create `CertificateRequest` resources. The `Certificates` controller
_could_ be changed in future to be optionally configured to create
`CertificateSigningRequests`.

The cert-manager controller will not enable the `CertificateSigningRequest`
controllers unless the `ExperimentalCertificateSigningRequestControllers=true`
environment variable is present on the cert-manager controller. This could be
changed in future by optimistically discovering whether the
`CertificateSigningRequest` resource is available and starting the controllers.

Instead of the concept of an `IssuerRef` for `CertificateRequests`,
`CertificateSigningRequests` have the concept of a `SignerName`. Since
`CertificateSigningRequests` are cluster scoped resources, the signer name must
include the namespace if the referenced `Issuer` is namespaced. The signer name
for cert-manager signers will be prefixed with `cert-manager.io` to prevent
conflicts with other external signer projects.

```yaml
  # Namespaced issuer reference
  # e.g. `issuers.cert-manager.io/my-namespace.my-issuer
  signerName: issuers.cert-manager.io/<namespace>.<issuer-name>

  # Cluster scoped issuer reference
  # e.g. `clusterissuers.cert-manager.io/my-issuer
  signerName: clusterissuers.cert-manager.io/<clusterissuer-name>
```

Using the same approach of referencing by _just_ name, rather than issuer type
(e.g. CA, Vault etc.), keeps the behaviour of this resource in line with
`CertificateRequests` for end users.

Each `CertificateSigningRequest` controller will behave in the same way as the
existing `CertificateRequest` resource, by getting the referenced signer, and
attempting to sign. If the issuer type is not managed by this controller, do
nothing, else sign.


### Signers

Some special cases for some `[Cluster]Issuers` that need to be addressed:

- SelfSigned: The `experimental.cert-manager.io/private-key-secret-name`
    annotation is used to reference a secret containing the private key of the
    self-signed certificate. The Secret must reside in either the namespace of
    the reference Issuer, or the cluster resource namespace in the case of a
    ClusterIssuer.

- Venafi:
  - The `venafi.experimental.cert-manager.io/custom-fields` annotation is set by
      the user to request a Venafi certificate with custom fields.
  - The `venafi.experimental.cert-manager.io/pickup-id` annotation is used by
      the cert-manager Venafi signer to keep track of the request against the
      Venafi API.

- ACME: The ACME controller creates sub-resources (`Orders`). Since
    `CertificateSigningRequests` are cluster scoped resources, when referencing
    a ClusterIssuer we should create Orders in the cluster resource namespace,
    else the namespace of the referenced Issuer.

### Conditions

The `CertificateSigningRequest` has [well-known condition
types](https://github.com/kubernetes/api/blob/4a626d306b987a4096cf0784ec01af1be2f6d67f/certificates/v1/types.go#L222)
of `Approved`, `Denied`, and `Failed`. `CertificateSigningRequest` signer
controllers should not begin computation of the request until the resource has
the `Approved` condition set to `True`.

It was found that nearly all users testing out these signers forgot about
needing to manually add the Approved condition. An event is fired on
`CertificateSigningRequest` which have neither a Denied or Approved condition.
This is to aid consumers in knowing that the resource _has_ been observed, but
is waiting for the Approved condition.

If a signer fails when processing a `CertificateSigningRequest`, the well-known
`Failed` condition should be set to `True`. No signer controllers should process
the request again.

When the request has been successfully signed, the `status.Certificate` field
will be updated with the sign certificate.

### API Changes

- IsCA: The `experimental.cert-manager.io/request-is-ca` annotation is used to
    request a certificate with the X.509 `IsCA` flag set.

- Duration: The `experimental.cert-manager.io/request-duration` annotation is used to
    request a certificate with a particular duration. Accepts a Go time duration
    string.

- Namespaced issuers: requesters must have the following RBAC in the same
    namespace to allow them to request from a namespaced issuer:

```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: cert-manager-referencer:my-issuer
  namespace: sandbox
rules:
- apiGroups: ["cert-manager.io"]
  resources: ["signers"]
  verbs:     ["reference"]
  resourceNames:
  - "my-issuer" # To give permission to _only_ reference Issuers with the name 'my-issuer'
  - "*" # To give permission to reference Issuers with any name in this namespace
```

### Upgrading

No effect to upgrades as only additional controllers added. No CRD API changes.

### Graduation

Below is each level of graduation for support of `CertificateSigningRequest` in
cert-manager.

1. Create signer implementations for each cert-manager.io issuer types. These
   controllers are active in a default cert-manager installation, though
   `CertificateRequests` are the resource created from `Certificate` resources.

1. [TBD] Swap `CertificateRequests` for `CertificateSigningRequests` as the resource
   created from `Certificates`.

1. [TBD] Remove the `CertificateRequest` resource from the cert-manager project,
   in favour of the `CertificateSigningRequest` resource.
