Skip to content

Latest commit

 

History

History
335 lines (257 loc) · 13.3 KB

20210209.certificates.k8s.io-adoption.md

File metadata and controls

335 lines (257 loc) · 13.3 KB
Error in user YAML: (<unknown>): found character that cannot start any token while scanning for the next token at line 8 column 5
---
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

Summary

In Kubernetes v1.19 the CertificateSigningRequest 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).

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 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.

This will be done via a SubjectAccessReview against the UserInfo fields on the CertificateSigningRequest. The user must hold the following permissions to reference a namespaced signer:

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 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.

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 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.

  # 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 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:

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.

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

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