/*
Copyright 2024 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package pem provides utility functions for safely decoding PEM data, placing upper limits on the size
// of data that will be processed. It functions as an extension to the standard library "encoding/pem" functions.
package pem
import (
stdpem "encoding/pem"
"errors"
"fmt"
)
// The constants below are estimates at reasonable upper bounds for sizes of PEM data that cert-manager might encounter.
// cert-manager supports RSA, ECDSA and Ed25519 keys, of which RSA keys are by far the largest.
// We'll aim to support RSA certs / keys which are larger than the maximum size (defined in pkg/util/pki.MaxRSAKeySize).
// RSA keys grow proportional to the size of the RSA key used. For example:
// PEM-encoded RSA Keys: 4096-bit is ~3kB, 8192-bit is ~6kB and a 16k-bit key is ~12kB.
// Certificates have two variables that we can estimate easily; the public key of the cert, and the signature from the signing cert.
// An N-bit key produces an (N/8)-byte signature, so as a worst case for us, a 16kB RSA key will create a 2kB signature.
// PEM-encoded RSA X.509 certificates:
// Signed with 1k-bit RSA key: 4096-bit is ~1.4kB, 8192-bit is ~2kB, 16k-bit is ~3.5kB
// Signed with 16k-bit RSA key: 4096-bit is ~3.3kB, 8192-bit is ~4kB, 16k-bit is ~5.4kB
// See https://fm4dd.com/openssl/certexamples.shtm for examples of large RSA certs / keys
const (
// maxCertificatePEMSize is the maximum size, in bytes, of a single PEM-encoded X.509 certificate which SafeDecodeSingleCertificate will accept.
// The value is based on how large a "realistic" (but still very large) self-signed 16k-bit RSA certificate might be.
// 16k-bit RSA keys are impractical on most on modern hardware due to how slow they can be,
// so we can reasonably assume that no real-world PEM-encoded X.509 cert will be this large.
// Note that X.509 certificates can contain extra arbitrary data (e.g., DNS names, policy names, etc) whose size is hard to predict.
// So we guess at how much of that data we'll allow in very large certs and allow about 1kB of such data.
maxCertificatePEMSize = 6500
// maxPrivateKeyPEMSize is the maximum size, in bytes, of PEM-encoded private keys which SafeDecodePrivateKey will accept.
// cert-manager supports RSA, ECDSA and Ed25519 keys, of which RSA is by far the largest.
// The value is based on how large a "realistic" (but very large) 16k-bit RSA private key might be.
// Given that 16k-bit RSA keys are so slow to use as to be impractical on modern hardware,
// we can reasonably assume that no real-world PEM-encoded key will be this large.
maxPrivateKeyPEMSize = 13000
// maxChainSize is the maximum number of 16k-bit RSA certificates signed by 16k-bit RSA CAs we'll allow in a given call to SafeDecodeCertificateChain.
// This is _not_ the maximum number of certificates cert-manager will process in a given chain, which could be much larger.
// This is simply the maximum number of worst-case certificates we'll accept in a chain.
maxChainSize = 10
// maxCertsInTrustBundle is an estimated upper-bound for how many large certs might appear in a PEM-encoded trust bundle,
// based on the cert-manager `cert-manager-package-debian` bundle [1] which contains 129 certificates.
// This isn't an upper bound on how many certificates can appear and be parsed; just a reasonable upper bound if using
// exclusively large RSA certs (see estimatedCACertSize)
// In practice, trust stores will contain ECDSA/EdDSA certificates which are smaller than RSA certs, and so will be able to have more certificates
// than maxCertsInTrustBundle if needed.
// [1] quay.io/jetstack/cert-manager-package-debian:20210119.0@sha256:116133f68938ef568aca17a0c691d5b1ef73a9a207029c9a068cf4230053fed5
maxCertsInTrustBundle = 150
// estimatedCACertSize is a guess of how many bytes a large realistic trust bundle cert might be. This is slightly larger
// than a typical self-signed 4096-bit RSA cert (which is just under 2kB).
// For other estimates (such as maxCertificatePEMSize) we use a much larger RSA key, but using such a large RSA key would make
// maxBundleSize's estimate unrealistically large.
estimatedCACertSize = 2200
// maxBundleSize is an estimate for the max reasonable size for a PEM-encoded TLS trust bundle.
// See also comments for maxCertsInTrustBundle and estimatedCACertSize.
// This estimate is ultimately based on the cert-manager `cert-manager-package-debian` bundle [1] which contains 129 certificates, totalling ~196kB of data.
// [1] quay.io/jetstack/cert-manager-package-debian:20210119.0@sha256:116133f68938ef568aca17a0c691d5b1ef73a9a207029c9a068cf4230053fed5
maxBundleSize = maxCertsInTrustBundle * estimatedCACertSize
)
var (
// ErrNoPEMData is returned when the given data contained no PEM
ErrNoPEMData = errors.New("no PEM data was found in given input")
)
// ErrPEMDataTooLarge is returned when the given data is larger than the maximum allowed
type ErrPEMDataTooLarge int
// Error returns an error string
func (e ErrPEMDataTooLarge) Error() string {
return fmt.Sprintf("provided PEM data was larger than the maximum %dB", int(e))
}
func safeDecodeInternal(b []byte, maxSize int) (*stdpem.Block, []byte, error) {
if len(b) > maxSize {
return nil, b, ErrPEMDataTooLarge(maxSize)
}
block, rest := stdpem.Decode(b)
if block == nil {
return nil, rest, ErrNoPEMData
}
return block, rest, nil
}
// SafeDecodePrivateKey calls [encoding/pem.Decode] on the given input as long as it's within a sensible range for
// how large we expect a private key to be. The baseline is a 16k-bit RSA private key, which is larger than the maximum
// supported by cert-manager for key generation.
func SafeDecodePrivateKey(b []byte) (*stdpem.Block, []byte, error) {
return safeDecodeInternal(b, maxPrivateKeyPEMSize)
}
// SafeDecodeCSR calls [encoding/pem.Decode] on the given input as long as it's within a sensible range for
// how large we expect a single PEM-encoded PKCS#10 CSR to be.
// We assume that a PKCS#12 CSR is smaller than a single certificate because our assumptions are that
// a certificate has a large public key and a large signature, which is roughly the case for a CSR.
// We also assume that we'd only ever decode one CSR which is the case in practice.
func SafeDecodeCSR(b []byte) (*stdpem.Block, []byte, error) {
return safeDecodeInternal(b, maxCertificatePEMSize)
}
// SafeDecodeSingleCertificate calls [encoding/pem.Decode] on the given input as long as it's within a sensible range for
// how large we expect a single PEM-encoded X.509 certificate to be.
// The baseline is a 16k-bit RSA certificate signed by a different 16k-bit RSA CA, which is larger than the maximum
// supported by cert-manager for key generation.
func SafeDecodeSingleCertificate(b []byte) (*stdpem.Block, []byte, error) {
return safeDecodeInternal(b, maxCertificatePEMSize)
}
// SafeDecodeCertificateChain calls [encoding/pem.Decode] on the given input as long as it's within a sensible range for
// how large we expect a reasonable-length PEM-encoded X.509 certificate chain to be.
// The baseline is several 16k-bit RSA certificates, all signed by 16k-bit RSA keys, which is larger than the maximum
// supported by cert-manager for key generation.
// The maximum number of chains supported by this function is not reflective of the maximum chain length supported by
// cert-manager; a larger chain of smaller certificate should be supported.
func SafeDecodeCertificateChain(b []byte) (*stdpem.Block, []byte, error) {
return safeDecodeInternal(b, maxCertificatePEMSize*maxChainSize)
}
// SafeDecodeCertificateBundle calls [encoding/pem.Decode] on the given input as long as it's within a sensible range for
// how large we expect a reasonable-length PEM-encoded X.509 certificate bundle (such as a TLS trust store) to be.
// The baseline is a bundle of 4k-bit RSA certificates, all self-signed. This is smaller than the 16k-bit RSA keys
// we use in other functions, because using such large keys would make our estimate several times
// too large for a realistic bundle which would be used in practice.
func SafeDecodeCertificateBundle(b []byte) (*stdpem.Block, []byte, error) {
return safeDecodeInternal(b, maxBundleSize)
}
/*
Copyright 2021 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package approval
// CertificateRequestApproval is a plugin that ensures entities that are attempting to
// modify `status.conditions[type="Approved"]` or `status.conditions[type="Denied"]`
// have permission to do so (granted via RBAC).
// Entities will need to be able to `approve` (verb) `signers` (resource type) in
// `cert-manager.io` (group) with the name `<issuer-type>.<issuer-group>/[<certificaterequest-namespace>.]<issuer-name>`.
// For example: `issuers.cert-manager.io/my-namespace.my-issuer-name`.
// A wildcard signerName format is also supported: `issuers.cert-manager.io/*`.
import (
"context"
"fmt"
"sync"
admissionv1 "k8s.io/api/admission/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/client-go/discovery"
"github.com/cert-manager/cert-manager/internal/apis/certmanager"
"github.com/cert-manager/cert-manager/internal/apis/certmanager/validation/util"
"github.com/cert-manager/cert-manager/pkg/webhook/admission"
)
type certificateRequestApproval struct {
*admission.Handler
authorizer authorizer.Authorizer
discovery discovery.DiscoveryInterface
// resourceInfo stores the associated resource info for a given GroupKind
// to prevent making multiple queries to the API server for every approval.
resourceInfo map[schema.GroupKind]resourceInfo
mutex sync.RWMutex
}
type resourceInfo struct {
schema.GroupResource
Namespaced bool
}
var _ admission.ValidationInterface = &certificateRequestApproval{}
func NewPlugin(authz authorizer.Authorizer, discoveryClient discovery.DiscoveryInterface) admission.Interface {
return &certificateRequestApproval{
Handler: admission.NewHandler(admissionv1.Update),
resourceInfo: map[schema.GroupKind]resourceInfo{},
authorizer: authz,
discovery: discoveryClient,
}
}
func (c *certificateRequestApproval) Validate(ctx context.Context, request admissionv1.AdmissionRequest, oldObj, obj runtime.Object) (warnings []string, err error) {
if request.RequestResource.Group != "cert-manager.io" ||
request.RequestResource.Resource != "certificaterequests" ||
request.RequestSubResource != "status" {
return nil, nil
}
oldCR, cr := oldObj.(*certmanager.CertificateRequest), obj.(*certmanager.CertificateRequest)
if !approvalConditionsHaveChanged(oldCR, cr) {
return nil, nil
}
group := cr.Spec.IssuerRef.Group
kind := cr.Spec.IssuerRef.Kind
// TODO: move this defaulting into the Scheme (registered as default functions) so
// these will be set when the CertificateRequest is decoded.
if group == "" {
group = "cert-manager.io"
}
if kind == "" {
kind = "Issuer"
}
// We got the GroupKind, now we need to get the Resource name.
apiResource, err := c.apiResourceForGroupKind(schema.GroupKind{Group: group, Kind: kind})
switch {
case err == errNoResourceExists:
return nil, field.Forbidden(field.NewPath("spec.issuerRef"),
fmt.Sprintf("referenced signer resource does not exist: %v", cr.Spec.IssuerRef))
case err != nil:
return nil, err
}
signerNames := signerNamesForAPIResource(cr.Spec.IssuerRef.Name, cr.Namespace, *apiResource)
if !isAuthorizedForSignerNames(ctx, c.authorizer, userInfoForRequest(request), signerNames) {
return nil, field.Forbidden(field.NewPath("status.conditions"),
fmt.Sprintf("user %q does not have permissions to set approved/denied conditions for issuer %v", request.UserInfo.Username, cr.Spec.IssuerRef))
}
return nil, nil
}
// approvalConditionsHaveChanged returns true if either the Approved or Denied conditions
// have been added to the CertificateRequest.
func approvalConditionsHaveChanged(oldCR, cr *certmanager.CertificateRequest) bool {
oldCRApproving := util.GetCertificateRequestCondition(oldCR.Status.Conditions, certmanager.CertificateRequestConditionApproved)
newCRApproving := util.GetCertificateRequestCondition(cr.Status.Conditions, certmanager.CertificateRequestConditionApproved)
oldCRDenying := util.GetCertificateRequestCondition(oldCR.Status.Conditions, certmanager.CertificateRequestConditionDenied)
newCRDenying := util.GetCertificateRequestCondition(cr.Status.Conditions, certmanager.CertificateRequestConditionDenied)
return (oldCRApproving == nil && newCRApproving != nil) || (oldCRDenying == nil && newCRDenying != nil)
}
// apiResourceForGroupKind returns the metav1.APIResource descriptor for a given GroupKind.
// This is required to properly construct the `signerName` used as part of validating
// requests that approve or deny the CertificateRequest.
// namespaced will be true if the resource is namespaced.
// 'resource' may be nil even if err is also nil.
func (c *certificateRequestApproval) apiResourceForGroupKind(groupKind schema.GroupKind) (info *resourceInfo, err error) {
// fast path if resource is in the cache already
if resource := c.readAPIResourceFromCache(groupKind); resource != nil {
return resource, nil
}
// otherwise, query the apiserver
// TODO: we should enhance caching here to avoid performing discovery queries
// many times if many CertificateRequest resources exist that reference
// a resource that doesn't exist
groups, err := c.discovery.ServerGroups()
if err != nil {
return nil, err
}
for _, apiGroup := range groups.Groups {
if apiGroup.Name != groupKind.Group {
continue
}
for _, version := range apiGroup.Versions {
apiResources, err := c.discovery.ServerResourcesForGroupVersion(version.GroupVersion)
if err != nil {
return nil, err
}
for _, resource := range apiResources.APIResources {
if resource.Kind != groupKind.Kind {
continue
}
return c.cacheAPIResource(groupKind, resource.Name, resource.Namespaced), nil
}
}
}
return nil, errNoResourceExists
}
func (c *certificateRequestApproval) readAPIResourceFromCache(groupKind schema.GroupKind) *resourceInfo {
c.mutex.RLock()
defer c.mutex.RUnlock()
if info, ok := c.resourceInfo[groupKind]; ok {
return &info
}
return nil
}
func (c *certificateRequestApproval) cacheAPIResource(groupKind schema.GroupKind, resourceName string, namespaced bool) *resourceInfo {
c.mutex.Lock()
defer c.mutex.Unlock()
info := resourceInfo{
GroupResource: schema.GroupResource{
Group: groupKind.Group,
Resource: resourceName,
},
Namespaced: namespaced,
}
c.resourceInfo[groupKind] = info
return &info
}
var errNoResourceExists = fmt.Errorf("no resource registered")
// signerNamesForAPIResource returns the computed signerName for a given API resource
// referenced by a CertificateRequest in a namespace.
func signerNamesForAPIResource(name, namespace string, info resourceInfo) []string {
signerNames := make([]string, 0, 2)
signerNames = append(signerNames, fmt.Sprintf("%s.%s/*", info.Resource, info.Group))
if info.Namespaced {
signerNames = append(signerNames, fmt.Sprintf("%s.%s/%s.%s", info.Resource, info.Group, namespace, name))
} else {
signerNames = append(signerNames, fmt.Sprintf("%s.%s/%s", info.Resource, info.Group, name))
}
return signerNames
}
// userInfoForRequest constructs a user.Info suitable for using with the authorizer interface
// from an AdmissionRequest.
func userInfoForRequest(req admissionv1.AdmissionRequest) user.Info {
extra := make(map[string][]string)
for k, v := range req.UserInfo.Extra {
extra[k] = v
}
return &user.DefaultInfo{
Name: req.UserInfo.Username,
UID: req.UserInfo.UID,
Groups: req.UserInfo.Groups,
Extra: extra,
}
}
// isAuthorizedForSignerNames checks whether an entity is authorized to 'approve' certificaterequests
// for a given set of signerNames.
// We absorb errors from the authorizer because they are already retried by the underlying authorization
// client, so we shouldn't ever see them unless the context webhook doesn't have the ability to submit
// SARs or the context is cancelled (in which case, the AdmissionResponse won't ever be returned to the apiserver).
func isAuthorizedForSignerNames(ctx context.Context, authz authorizer.Authorizer, info user.Info, signerNames []string) bool {
verb := "approve"
for _, signerName := range signerNames {
attr := buildAttributes(info, verb, signerName)
decision, _, err := authz.Authorize(ctx, attr)
switch {
case err != nil:
return false
case decision == authorizer.DecisionAllow:
return true
}
}
return false
}
func buildAttributes(info user.Info, verb, signerName string) authorizer.Attributes {
return authorizer.AttributesRecord{
User: info,
Verb: verb,
Name: signerName,
APIGroup: "cert-manager.io",
APIVersion: "*",
Resource: "signers",
ResourceRequest: true,
}
}
func (c *certificateRequestApproval) ValidateInitialization() error {
if c.authorizer == nil {
return fmt.Errorf("authorizer not set")
}
if c.discovery == nil {
return fmt.Errorf("discovery client not set")
}
_, err := c.discovery.ServerGroups()
if err != nil {
return err
}
return nil
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"github.com/cert-manager/cert-manager/pkg/acme/webhook/apis/acme"
)
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: acme.GroupName, Version: "v1alpha1"}
// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
var (
// SchemeBuilder should be declared in packages that will have generated deep
// copy or conversion functions.
SchemeBuilder runtime.SchemeBuilder
localSchemeBuilder = &SchemeBuilder
AddToScheme = localSchemeBuilder.AddToScheme
)
func init() {
// We only register manually written functions here. The registration of the
// generated functions takes place in the generated files. The separation
// makes the code compile even when the generated files are missing.
localSchemeBuilder.Register(addKnownTypes)
}
// Adds the list of known types to api.Scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&ChallengePayload{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*
Copyright The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by deepcopy-gen. DO NOT EDIT.
package v1alpha1
import (
v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ChallengePayload) DeepCopyInto(out *ChallengePayload) {
*out = *in
out.TypeMeta = in.TypeMeta
if in.Request != nil {
in, out := &in.Request, &out.Request
*out = new(ChallengeRequest)
(*in).DeepCopyInto(*out)
}
if in.Response != nil {
in, out := &in.Response, &out.Response
*out = new(ChallengeResponse)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ChallengePayload.
func (in *ChallengePayload) DeepCopy() *ChallengePayload {
if in == nil {
return nil
}
out := new(ChallengePayload)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ChallengePayload) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ChallengeRequest) DeepCopyInto(out *ChallengeRequest) {
*out = *in
if in.Config != nil {
in, out := &in.Config, &out.Config
*out = new(v1.JSON)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ChallengeRequest.
func (in *ChallengeRequest) DeepCopy() *ChallengeRequest {
if in == nil {
return nil
}
out := new(ChallengeRequest)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ChallengeResponse) DeepCopyInto(out *ChallengeResponse) {
*out = *in
if in.Result != nil {
in, out := &in.Result, &out.Result
*out = new(metav1.Status)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ChallengeResponse.
func (in *ChallengeResponse) DeepCopy() *ChallengeResponse {
if in == nil {
return nil
}
out := new(ChallengeResponse)
in.DeepCopyInto(out)
return out
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package api
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
whapi "github.com/cert-manager/cert-manager/pkg/acme/webhook/apis/acme/v1alpha1"
cmacmev1 "github.com/cert-manager/cert-manager/pkg/apis/acme/v1"
cmapiv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
)
// This package defines a Scheme and Codec that has the *external* API types
// registered.
// This means that the scheme will *not* perform defaulting or conversions for
// cert-manager API resources.
// This is to ensure a clean separation between API semantics and controllers.
// Only the webhook should utilise a scheme with conversions and defaults
// registered in order to ensure all controllers have a consistent view of
// resource types in the apiserver.
var Scheme = runtime.NewScheme()
var Codecs = serializer.NewCodecFactory(Scheme)
var ParameterCodec = runtime.NewParameterCodec(Scheme)
var localSchemeBuilder = runtime.SchemeBuilder{
cmapiv1.AddToScheme,
cmacmev1.AddToScheme,
cmmeta.AddToScheme,
whapi.AddToScheme,
}
// AddToScheme adds all types of this clientset into the given scheme. This allows composition
// of clientsets, like in:
//
// import (
// "k8s.io/client-go/kubernetes"
// clientsetscheme "k8s.io/client-go/kubernetes/scheme"
// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
// )
//
// kclientset, _ := kubernetes.NewForConfig(c)
// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
//
// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types
// correctly.
var AddToScheme = localSchemeBuilder.AddToScheme
func init() {
metav1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"})
utilruntime.Must(AddToScheme(Scheme))
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/klog/v2"
"k8s.io/utils/clock"
cmapi "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
logf "github.com/cert-manager/cert-manager/pkg/logs"
)
// Clock is defined as a package var so it can be stubbed out during tests.
var Clock clock.Clock = clock.RealClock{}
// IssuerHasCondition will return true if the given GenericIssuer has a
// condition matching the provided IssuerCondition.
// Only the Type and Status field will be used in the comparison, meaning that
// this function will return 'true' even if the Reason, Message and
// LastTransitionTime fields do not match.
func IssuerHasCondition(i cmapi.GenericIssuer, c cmapi.IssuerCondition) bool {
if i == nil {
return false
}
existingConditions := i.GetStatus().Conditions
for _, cond := range existingConditions {
if c.Type == cond.Type && c.Status == cond.Status {
return true
}
}
return false
}
// SetIssuerCondition will set a 'condition' on the given GenericIssuer.
// - If no condition of the same type already exists, the condition will be
// inserted with the LastTransitionTime set to the current time.
// - If a condition of the same type and state already exists, the condition
// will be updated but the LastTransitionTime will not be modified.
// - If a condition of the same type and different state already exists, the
// condition will be updated and the LastTransitionTime set to the current
// time.
//
// This function works with both Issuer and ClusterIssuer resources.
func SetIssuerCondition(i cmapi.GenericIssuer, observedGeneration int64, conditionType cmapi.IssuerConditionType, status cmmeta.ConditionStatus, reason, message string) {
newCondition := cmapi.IssuerCondition{
Type: conditionType,
Status: status,
Reason: reason,
Message: message,
}
nowTime := metav1.NewTime(Clock.Now())
newCondition.LastTransitionTime = &nowTime
// Set the condition generation
newCondition.ObservedGeneration = observedGeneration
// Search through existing conditions
for idx, cond := range i.GetStatus().Conditions {
// Skip unrelated conditions
if cond.Type != conditionType {
continue
}
// If this update doesn't contain a state transition, we don't update
// the conditions LastTransitionTime to Now()
if cond.Status == status {
newCondition.LastTransitionTime = cond.LastTransitionTime
} else {
logf.Log.V(logf.InfoLevel).Info("Found status change for Issuer condition; setting lastTransitionTime",
"issuer", klog.KObj(i),
"condition", conditionType,
"oldStatus", cond.Status,
"status", status,
"lastTransitionTime", nowTime.Time)
}
// Overwrite the existing condition
i.GetStatus().Conditions[idx] = newCondition
return
}
// If we've not found an existing condition of this type, we simply insert
// the new condition into the slice.
i.GetStatus().Conditions = append(i.GetStatus().Conditions, newCondition)
logf.Log.V(logf.InfoLevel).Info("Setting lastTransitionTime for Issuer condition",
"issuer", klog.KObj(i),
"condition", conditionType,
"lastTransitionTime", nowTime.Time)
}
// CertificateHasCondition will return true if the given Certificate has a
// condition matching the provided CertificateCondition.
// Only the Type and Status field will be used in the comparison, meaning that
// this function will return 'true' even if the Reason, Message and
// LastTransitionTime fields do not match.
func CertificateHasCondition(crt *cmapi.Certificate, c cmapi.CertificateCondition) bool {
if crt == nil {
return false
}
existingConditions := crt.Status.Conditions
for _, cond := range existingConditions {
if c.Type == cond.Type && c.Status == cond.Status {
return true
}
}
return false
}
// CertificateHasConditionWithObservedGeneration will return true if the given Certificate has a
// condition matching the provided CertificateCondition with a ObservedGeneration
// that is bigger or equal to the ObservedGeneration of the provided CertificateCondition.
// Only the Type, Status and ObservedGeneration field will be used in the comparison,
// meaning that this function will return 'true' even if the Reason, Message and
// LastTransitionTime fields do not match.
func CertificateHasConditionWithObservedGeneration(crt *cmapi.Certificate, c cmapi.CertificateCondition) bool {
if crt == nil {
return false
}
existingConditions := crt.Status.Conditions
for _, cond := range existingConditions {
if c.Type == cond.Type && c.Status == cond.Status && c.ObservedGeneration <= cond.ObservedGeneration {
return true
}
}
return false
}
func GetCertificateCondition(crt *cmapi.Certificate, conditionType cmapi.CertificateConditionType) *cmapi.CertificateCondition {
for _, cond := range crt.Status.Conditions {
if cond.Type == conditionType {
return &cond
}
}
return nil
}
func GetCertificateRequestCondition(req *cmapi.CertificateRequest, conditionType cmapi.CertificateRequestConditionType) *cmapi.CertificateRequestCondition {
for _, cond := range req.Status.Conditions {
if cond.Type == conditionType {
return &cond
}
}
return nil
}
// SetCertificateCondition will set a 'condition' on the given Certificate.
// - If no condition of the same type already exists, the condition will be
// inserted with the LastTransitionTime set to the current time.
// - If a condition of the same type and state already exists, the condition
// will be updated but the LastTransitionTime will not be modified.
// - If a condition of the same type and different state already exists, the
// condition will be updated with the LastTransitionTime set to the current
// time.
//
// The given ObservedGeneration will always set on the condition, whether the
// lastTransitionTime is modified or not.
func SetCertificateCondition(crt *cmapi.Certificate, observedGeneration int64, conditionType cmapi.CertificateConditionType,
status cmmeta.ConditionStatus, reason, message string) {
newCondition := cmapi.CertificateCondition{
Type: conditionType,
Status: status,
Reason: reason,
Message: message,
}
nowTime := metav1.NewTime(Clock.Now())
newCondition.LastTransitionTime = &nowTime
// Set the condition generation
newCondition.ObservedGeneration = observedGeneration
// Search through existing conditions
for idx, cond := range crt.Status.Conditions {
// Skip unrelated conditions
if cond.Type != conditionType {
continue
}
// If this update doesn't contain a state transition, we don't update the
// conditions LastTransitionTime to Now()
if cond.Status == status {
newCondition.LastTransitionTime = cond.LastTransitionTime
} else {
logf.Log.V(logf.InfoLevel).Info("Found status change for Certificate condition; setting lastTransitionTime",
"certificate", klog.KObj(crt),
"condition", conditionType,
"oldStatus", cond.Status,
"status", status,
"lastTransitionTime", nowTime.Time)
}
// Overwrite the existing condition
crt.Status.Conditions[idx] = newCondition
return
}
// If we've not found an existing condition of this type, we simply insert
// the new condition into the slice.
crt.Status.Conditions = append(crt.Status.Conditions, newCondition)
logf.Log.V(logf.InfoLevel).Info("Setting lastTransitionTime for Certificate condition",
"certificate", klog.KObj(crt),
"condition", conditionType,
"lastTransitionTime", nowTime.Time)
}
// RemoveCertificateCondition will remove any condition with this condition type
func RemoveCertificateCondition(crt *cmapi.Certificate, conditionType cmapi.CertificateConditionType) {
var updatedConditions []cmapi.CertificateCondition
// Search through existing conditions
for _, cond := range crt.Status.Conditions {
// Only add unrelated conditions
if cond.Type != conditionType {
updatedConditions = append(updatedConditions, cond)
}
}
crt.Status.Conditions = updatedConditions
}
// SetCertificateRequestCondition will set a 'condition' on the given CertificateRequest.
// - If no condition of the same type already exists, the condition will be
// inserted with the LastTransitionTime set to the current time.
// - If a condition of the same type and state already exists, the condition
// will be updated but the LastTransitionTime will not be modified.
// - If a condition of the same type and different state already exists, the
// condition will be updated and the LastTransitionTime set to the current
// time.
func SetCertificateRequestCondition(cr *cmapi.CertificateRequest, conditionType cmapi.CertificateRequestConditionType, status cmmeta.ConditionStatus, reason, message string) {
newCondition := cmapi.CertificateRequestCondition{
Type: conditionType,
Status: status,
Reason: reason,
Message: message,
}
nowTime := metav1.NewTime(Clock.Now())
newCondition.LastTransitionTime = &nowTime
// Search through existing conditions
for idx, cond := range cr.Status.Conditions {
// Skip unrelated conditions
if cond.Type != conditionType {
continue
}
// If this update doesn't contain a state transition, we don't update
// the conditions LastTransitionTime to Now()
if cond.Status == status {
newCondition.LastTransitionTime = cond.LastTransitionTime
} else {
logf.Log.V(logf.InfoLevel).Info("Found status change for CertificateRequest condition; setting lastTransitionTime",
"certificateRequest", klog.KObj(cr),
"condition", conditionType,
"oldStatus", cond.Status,
"status", status,
"lastTransitionTime", nowTime.Time)
}
// Overwrite the existing condition
cr.Status.Conditions[idx] = newCondition
return
}
// If we've not found an existing condition of this type, we simply insert
// the new condition into the slice.
cr.Status.Conditions = append(cr.Status.Conditions, newCondition)
logf.Log.V(logf.InfoLevel).Info("Setting lastTransitionTime for CertificateRequest condition",
"certificateRequest", klog.KObj(cr),
"condition", conditionType,
"lastTransitionTime", nowTime.Time)
}
// CertificateRequestHasCondition will return true if the given
// CertificateRequest has a condition matching the provided
// CertificateRequestCondition.
// Only the Type and Status field will be used in the comparison, meaning that
// this function will return 'true' even if the Reason, Message and
// LastTransitionTime fields do not match.
func CertificateRequestHasCondition(cr *cmapi.CertificateRequest, c cmapi.CertificateRequestCondition) bool {
if cr == nil {
return false
}
existingConditions := cr.Status.Conditions
for _, cond := range existingConditions {
if c.Type == cond.Type && c.Status == cond.Status {
if c.Reason == "" || c.Reason == cond.Reason {
return true
}
}
}
return false
}
// This returns the status reason of a CertificateRequest. The order of reason
// hierarchy is 'Failed' -> 'Ready' -> 'Pending' -> ”
func CertificateRequestReadyReason(cr *cmapi.CertificateRequest) string {
for _, reason := range []string{
cmapi.CertificateRequestReasonFailed,
cmapi.CertificateRequestReasonIssued,
cmapi.CertificateRequestReasonPending,
cmapi.CertificateRequestReasonDenied,
} {
for _, con := range cr.Status.Conditions {
if con.Type == cmapi.CertificateRequestConditionReady &&
con.Reason == reason {
return reason
}
}
}
return ""
}
// This returns with the message if the CertificateRequest contains an
// InvalidRequest condition, and returns "" otherwise.
func CertificateRequestInvalidRequestMessage(cr *cmapi.CertificateRequest) string {
if cr == nil {
return ""
}
for _, con := range cr.Status.Conditions {
if con.Type == cmapi.CertificateRequestConditionInvalidRequest &&
con.Status == cmmeta.ConditionTrue {
return con.Message
}
}
return ""
}
// This returns with true if the CertificateRequest contains an InvalidRequest
// condition, and returns false otherwise.
func CertificateRequestHasInvalidRequest(cr *cmapi.CertificateRequest) bool {
if cr == nil {
return false
}
for _, con := range cr.Status.Conditions {
if con.Type == cmapi.CertificateRequestConditionInvalidRequest &&
con.Status == cmmeta.ConditionTrue {
return true
}
}
return false
}
// CertificateRequestIsApproved returns true if the CertificateRequest is
// approved via an Approved condition of status `True`, returns false
// otherwise.
func CertificateRequestIsApproved(cr *cmapi.CertificateRequest) bool {
if cr == nil {
return false
}
for _, con := range cr.Status.Conditions {
if con.Type == cmapi.CertificateRequestConditionApproved &&
con.Status == cmmeta.ConditionTrue {
return true
}
}
return false
}
// CertificateRequestIsDenied returns true if the CertificateRequest is denied
// via a Denied condition of status `True`, returns false otherwise.
func CertificateRequestIsDenied(cr *cmapi.CertificateRequest) bool {
if cr == nil {
return false
}
for _, con := range cr.Status.Conditions {
if con.Type == cmapi.CertificateRequestConditionDenied &&
con.Status == cmmeta.ConditionTrue {
return true
}
}
return false
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
)
// DefaultCertDuration returns d.Duration if set, otherwise returns
// cert-manager's default certificate duration (90 days).
func DefaultCertDuration(d *metav1.Duration) time.Duration {
certDuration := v1.DefaultCertificateDuration
if d != nil {
certDuration = d.Duration
}
return certDuration
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"fmt"
cmapi "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
)
const (
// IssuerACME is the name of the ACME issuer
IssuerACME string = "acme"
// IssuerCA is the name of the simple issuer
IssuerCA string = "ca"
// IssuerVault is the name of the Vault issuer
IssuerVault string = "vault"
// IssuerSelfSigned is a self signing issuer
IssuerSelfSigned string = "selfsigned"
// IssuerVenafi uses Venafi Trust Protection Platform and Venafi Cloud
IssuerVenafi string = "venafi"
)
// NameForIssuer determines the name of the Issuer implementation given an
// Issuer resource.
func NameForIssuer(i cmapi.GenericIssuer) (string, error) {
switch {
case i.GetSpec().ACME != nil:
return IssuerACME, nil
case i.GetSpec().CA != nil:
return IssuerCA, nil
case i.GetSpec().Vault != nil:
return IssuerVault, nil
case i.GetSpec().SelfSigned != nil:
return IssuerSelfSigned, nil
case i.GetSpec().Venafi != nil:
return IssuerVenafi, nil
}
return "", fmt.Errorf("no issuer specified for Issuer '%s/%s'", i.GetObjectMeta().Namespace, i.GetObjectMeta().Name)
}
// IssuerKind returns the kind of issuer for a certificate.
func IssuerKind(ref cmmeta.ObjectReference) string {
if ref.Kind == "" {
return cmapi.IssuerKind
}
return ref.Kind
}
/*
Copyright 2021 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"crypto/x509"
"math/bits"
certificatesv1 "k8s.io/api/certificates/v1"
)
var keyUsagesKube = map[certificatesv1.KeyUsage]x509.KeyUsage{
certificatesv1.UsageSigning: x509.KeyUsageDigitalSignature,
certificatesv1.UsageDigitalSignature: x509.KeyUsageDigitalSignature,
certificatesv1.UsageContentCommitment: x509.KeyUsageContentCommitment,
certificatesv1.UsageKeyEncipherment: x509.KeyUsageKeyEncipherment,
certificatesv1.UsageKeyAgreement: x509.KeyUsageKeyAgreement,
certificatesv1.UsageDataEncipherment: x509.KeyUsageDataEncipherment,
certificatesv1.UsageCertSign: x509.KeyUsageCertSign,
certificatesv1.UsageCRLSign: x509.KeyUsageCRLSign,
certificatesv1.UsageEncipherOnly: x509.KeyUsageEncipherOnly,
certificatesv1.UsageDecipherOnly: x509.KeyUsageDecipherOnly,
}
var extKeyUsagesKube = map[certificatesv1.KeyUsage]x509.ExtKeyUsage{
certificatesv1.UsageAny: x509.ExtKeyUsageAny,
certificatesv1.UsageServerAuth: x509.ExtKeyUsageServerAuth,
certificatesv1.UsageClientAuth: x509.ExtKeyUsageClientAuth,
certificatesv1.UsageCodeSigning: x509.ExtKeyUsageCodeSigning,
certificatesv1.UsageEmailProtection: x509.ExtKeyUsageEmailProtection,
certificatesv1.UsageSMIME: x509.ExtKeyUsageEmailProtection,
certificatesv1.UsageIPsecEndSystem: x509.ExtKeyUsageIPSECEndSystem,
certificatesv1.UsageIPsecTunnel: x509.ExtKeyUsageIPSECTunnel,
certificatesv1.UsageIPsecUser: x509.ExtKeyUsageIPSECUser,
certificatesv1.UsageTimestamping: x509.ExtKeyUsageTimeStamping,
certificatesv1.UsageOCSPSigning: x509.ExtKeyUsageOCSPSigning,
certificatesv1.UsageMicrosoftSGC: x509.ExtKeyUsageMicrosoftServerGatedCrypto,
certificatesv1.UsageNetscapeSGC: x509.ExtKeyUsageNetscapeServerGatedCrypto,
}
// KeyUsageTypeKube returns the relevant x509.KeyUsage or false if not found
func KeyUsageTypeKube(usage certificatesv1.KeyUsage) (x509.KeyUsage, bool) {
u, ok := keyUsagesKube[usage]
return u, ok
}
// ExtKeyUsageTypeKube returns the relevant x509.KeyUsage or false if not found
func ExtKeyUsageTypeKube(usage certificatesv1.KeyUsage) (x509.ExtKeyUsage, bool) {
eu, ok := extKeyUsagesKube[usage]
return eu, ok
}
// KubeKeyUsageStrings returns the certificatesv1.KeyUsage and "unknown" if not
// found
func KubeKeyUsageStrings(usage x509.KeyUsage) []certificatesv1.KeyUsage {
var usageStr []certificatesv1.KeyUsage
for i := range bits.UintSize {
if v := usage & (1 << i); v != 0 {
usageStr = append(usageStr, kubeKeyUsageString(v))
}
}
return usageStr
}
// KubeExtKeyUsageStrings returns the certificatesv1.KeyUsage and "unknown" if not found
func KubeExtKeyUsageStrings(usage []x509.ExtKeyUsage) []certificatesv1.KeyUsage {
var usageStr []certificatesv1.KeyUsage
for _, u := range usage {
usageStr = append(usageStr, kubeExtKeyUsageString(u))
}
return usageStr
}
// kubeKeyUsageString returns the cmapi.KeyUsage and "unknown" if not found
func kubeKeyUsageString(usage x509.KeyUsage) certificatesv1.KeyUsage {
if usage == x509.KeyUsageDigitalSignature {
return certificatesv1.UsageDigitalSignature // we have two keys that map to KeyUsageDigitalSignature in our map, we should be consistent when parsing
}
for k, v := range keyUsagesKube {
if usage == v {
return k
}
}
return "unknown"
}
// kubeExtKeyUsageString returns the cmapi.ExtKeyUsage and "unknown" if not found
func kubeExtKeyUsageString(usage x509.ExtKeyUsage) certificatesv1.KeyUsage {
if usage == x509.ExtKeyUsageEmailProtection {
return certificatesv1.UsageEmailProtection // we have two keys that map to ExtKeyUsageEmailProtection in our map, we should be consistent when parsing
}
for k, v := range extKeyUsagesKube {
if usage == v {
return k
}
}
return "unknown"
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"crypto/sha256"
"encoding/json"
"fmt"
"hash/fnv"
"regexp"
)
// ComputeName hashes the given object and prefixes it with prefix.
// The algorithm in use is Fowler–Noll–Vo hash function and is not
// cryptographically secure. Using a cryptographically secure hash is
// not necessary.
func ComputeName(prefix string, obj interface{}) (string, error) {
objectBytes, err := json.Marshal(obj)
if err != nil {
return "", err
}
hashF := fnv.New32()
_, err = hashF.Write(objectBytes)
if err != nil {
return "", err
}
// we're shortening to stay under 64 as we use this in services
// and pods down the road for ACME resources.
prefix = DNSSafeShortenTo52Characters(prefix)
// the prefix is <= 52 characters, the decimal representation of
// the hash is <= 10 characters, and the hyphen is 1 character.
// 52 + 10 + 1 = 63, so we're good.
return fmt.Sprintf("%s-%d", prefix, hashF.Sum32()), nil
}
// ComputeSecureUniqueDeterministicNameFromData computes a deterministic name from the given data.
// The algorithm in use is SHA256 and is cryptographically secure.
// The output is a string that is safe to use as a DNS label.
// The output is guaranteed to be unique for the given input.
// The output will be at least 64 characters long.
func ComputeSecureUniqueDeterministicNameFromData(fullName string, maxNameLength int) (string, error) {
const hashLength = 64
if maxNameLength < hashLength {
return "", fmt.Errorf("maxNameLength must be at least %d", hashLength)
}
if len(fullName) <= maxNameLength {
return fullName, nil
}
hash := sha256.New()
_, err := hash.Write([]byte(fullName))
if err != nil {
return "", err
}
// Although fullName is already a DNS subdomain, we can't just cut it
// at N characters and expect another DNS subdomain. That's because
// we might cut it right after a ".", which would give an invalid DNS
// subdomain (e.g., test.-<hash>). So we make sure the last character
// is an alpha-numeric character.
prefix := DNSSafeShortenToNCharacters(fullName, maxNameLength-hashLength-1)
hashResult := hash.Sum(nil)
if len(prefix) == 0 {
return fmt.Sprintf("%08x", hashResult), nil
}
return fmt.Sprintf("%s-%08x", prefix, hashResult), nil
}
// DNSSafeShortenToNCharacters shortens the input string to N chars and ensures the last char is an alpha-numeric character.
func DNSSafeShortenToNCharacters(in string, maxLength int) string {
var alphaNumeric = regexp.MustCompile(`[a-zA-Z\d]`)
if len(in) < maxLength {
return in
}
if maxLength <= 0 {
return ""
}
validCharIndexes := alphaNumeric.FindAllStringIndex(in[:maxLength], -1)
if len(validCharIndexes) == 0 {
return ""
}
return in[:validCharIndexes[len(validCharIndexes)-1][1]]
}
// DNSSafeShortenTo52Characters shortens the input string to 52 chars and ensures the last char is an alpha-numeric character.
func DNSSafeShortenTo52Characters(in string) string {
return DNSSafeShortenToNCharacters(in, 52)
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"crypto/x509"
"math/bits"
cmapi "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
)
var keyUsages = map[cmapi.KeyUsage]x509.KeyUsage{
cmapi.UsageSigning: x509.KeyUsageDigitalSignature,
cmapi.UsageDigitalSignature: x509.KeyUsageDigitalSignature,
cmapi.UsageContentCommitment: x509.KeyUsageContentCommitment,
cmapi.UsageKeyEncipherment: x509.KeyUsageKeyEncipherment,
cmapi.UsageKeyAgreement: x509.KeyUsageKeyAgreement,
cmapi.UsageDataEncipherment: x509.KeyUsageDataEncipherment,
cmapi.UsageCertSign: x509.KeyUsageCertSign,
cmapi.UsageCRLSign: x509.KeyUsageCRLSign,
cmapi.UsageEncipherOnly: x509.KeyUsageEncipherOnly,
cmapi.UsageDecipherOnly: x509.KeyUsageDecipherOnly,
}
var extKeyUsages = map[cmapi.KeyUsage]x509.ExtKeyUsage{
cmapi.UsageAny: x509.ExtKeyUsageAny,
cmapi.UsageServerAuth: x509.ExtKeyUsageServerAuth,
cmapi.UsageClientAuth: x509.ExtKeyUsageClientAuth,
cmapi.UsageCodeSigning: x509.ExtKeyUsageCodeSigning,
cmapi.UsageEmailProtection: x509.ExtKeyUsageEmailProtection,
cmapi.UsageSMIME: x509.ExtKeyUsageEmailProtection,
cmapi.UsageIPsecEndSystem: x509.ExtKeyUsageIPSECEndSystem,
cmapi.UsageIPsecTunnel: x509.ExtKeyUsageIPSECTunnel,
cmapi.UsageIPsecUser: x509.ExtKeyUsageIPSECUser,
cmapi.UsageTimestamping: x509.ExtKeyUsageTimeStamping,
cmapi.UsageOCSPSigning: x509.ExtKeyUsageOCSPSigning,
cmapi.UsageMicrosoftSGC: x509.ExtKeyUsageMicrosoftServerGatedCrypto,
cmapi.UsageNetscapeSGC: x509.ExtKeyUsageNetscapeServerGatedCrypto,
}
// KeyUsageType returns the relevant x509.KeyUsage or false if not found
func KeyUsageType(usage cmapi.KeyUsage) (x509.KeyUsage, bool) {
u, ok := keyUsages[usage]
return u, ok
}
// ExtKeyUsageType returns the relevant x509.ExtKeyUsage or false if not found
func ExtKeyUsageType(usage cmapi.KeyUsage) (x509.ExtKeyUsage, bool) {
eu, ok := extKeyUsages[usage]
return eu, ok
}
// KeyUsageStrings returns the cmapi.KeyUsage and "unknown" if not found
func KeyUsageStrings(usage x509.KeyUsage) []cmapi.KeyUsage {
var usageStr []cmapi.KeyUsage
for i := range bits.UintSize {
if v := usage & (1 << i); v != 0 {
usageStr = append(usageStr, keyUsageString(v))
}
}
return usageStr
}
// ExtKeyUsageStrings returns the cmapi.KeyUsage and "unknown" if not found
func ExtKeyUsageStrings(usage []x509.ExtKeyUsage) []cmapi.KeyUsage {
var usageStr []cmapi.KeyUsage
for _, u := range usage {
usageStr = append(usageStr, extKeyUsageString(u))
}
return usageStr
}
// keyUsageString returns the cmapi.KeyUsage and "unknown" if not found
func keyUsageString(usage x509.KeyUsage) cmapi.KeyUsage {
if usage == x509.KeyUsageDigitalSignature {
return cmapi.UsageDigitalSignature // we have two keys that map to KeyUsageDigitalSignature in our map, we should be consistent when parsing
}
for k, v := range keyUsages {
if usage == v {
return k
}
}
return "unknown"
}
// extKeyUsageString returns the cmapi.ExtKeyUsage and "unknown" if not found
func extKeyUsageString(usage x509.ExtKeyUsage) cmapi.KeyUsage {
if usage == x509.ExtKeyUsageEmailProtection {
return cmapi.UsageEmailProtection // we have two keys that map to ExtKeyUsageEmailProtection in our map, we should be consistent when parsing
}
for k, v := range extKeyUsages {
if usage == v {
return k
}
}
return "unknown"
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"github.com/cert-manager/cert-manager/pkg/apis/acme"
)
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: acme.GroupName, Version: "v1"}
// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
var (
SchemeBuilder runtime.SchemeBuilder
localSchemeBuilder = &SchemeBuilder
AddToScheme = localSchemeBuilder.AddToScheme
)
func init() {
// We only register manually written functions here. The registration of the
// generated functions takes place in the generated files. The separation
// makes the code compile even when the generated files are missing.
localSchemeBuilder.Register(addKnownTypes)
}
// Adds the list of known types to api.Scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&Order{},
&OrderList{},
&Challenge{},
&ChallengeList{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*
Copyright The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by deepcopy-gen. DO NOT EDIT.
package v1
import (
metav1 "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
corev1 "k8s.io/api/core/v1"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apismetav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
apisv1 "sigs.k8s.io/gateway-api/apis/v1"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ACMEAuthorization) DeepCopyInto(out *ACMEAuthorization) {
*out = *in
if in.Wildcard != nil {
in, out := &in.Wildcard, &out.Wildcard
*out = new(bool)
**out = **in
}
if in.Challenges != nil {
in, out := &in.Challenges, &out.Challenges
*out = make([]ACMEChallenge, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ACMEAuthorization.
func (in *ACMEAuthorization) DeepCopy() *ACMEAuthorization {
if in == nil {
return nil
}
out := new(ACMEAuthorization)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ACMEChallenge) DeepCopyInto(out *ACMEChallenge) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ACMEChallenge.
func (in *ACMEChallenge) DeepCopy() *ACMEChallenge {
if in == nil {
return nil
}
out := new(ACMEChallenge)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ACMEChallengeSolver) DeepCopyInto(out *ACMEChallengeSolver) {
*out = *in
if in.Selector != nil {
in, out := &in.Selector, &out.Selector
*out = new(CertificateDNSNameSelector)
(*in).DeepCopyInto(*out)
}
if in.HTTP01 != nil {
in, out := &in.HTTP01, &out.HTTP01
*out = new(ACMEChallengeSolverHTTP01)
(*in).DeepCopyInto(*out)
}
if in.DNS01 != nil {
in, out := &in.DNS01, &out.DNS01
*out = new(ACMEChallengeSolverDNS01)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ACMEChallengeSolver.
func (in *ACMEChallengeSolver) DeepCopy() *ACMEChallengeSolver {
if in == nil {
return nil
}
out := new(ACMEChallengeSolver)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ACMEChallengeSolverDNS01) DeepCopyInto(out *ACMEChallengeSolverDNS01) {
*out = *in
if in.Akamai != nil {
in, out := &in.Akamai, &out.Akamai
*out = new(ACMEIssuerDNS01ProviderAkamai)
**out = **in
}
if in.CloudDNS != nil {
in, out := &in.CloudDNS, &out.CloudDNS
*out = new(ACMEIssuerDNS01ProviderCloudDNS)
(*in).DeepCopyInto(*out)
}
if in.Cloudflare != nil {
in, out := &in.Cloudflare, &out.Cloudflare
*out = new(ACMEIssuerDNS01ProviderCloudflare)
(*in).DeepCopyInto(*out)
}
if in.Route53 != nil {
in, out := &in.Route53, &out.Route53
*out = new(ACMEIssuerDNS01ProviderRoute53)
(*in).DeepCopyInto(*out)
}
if in.AzureDNS != nil {
in, out := &in.AzureDNS, &out.AzureDNS
*out = new(ACMEIssuerDNS01ProviderAzureDNS)
(*in).DeepCopyInto(*out)
}
if in.DigitalOcean != nil {
in, out := &in.DigitalOcean, &out.DigitalOcean
*out = new(ACMEIssuerDNS01ProviderDigitalOcean)
**out = **in
}
if in.AcmeDNS != nil {
in, out := &in.AcmeDNS, &out.AcmeDNS
*out = new(ACMEIssuerDNS01ProviderAcmeDNS)
**out = **in
}
if in.RFC2136 != nil {
in, out := &in.RFC2136, &out.RFC2136
*out = new(ACMEIssuerDNS01ProviderRFC2136)
**out = **in
}
if in.Webhook != nil {
in, out := &in.Webhook, &out.Webhook
*out = new(ACMEIssuerDNS01ProviderWebhook)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ACMEChallengeSolverDNS01.
func (in *ACMEChallengeSolverDNS01) DeepCopy() *ACMEChallengeSolverDNS01 {
if in == nil {
return nil
}
out := new(ACMEChallengeSolverDNS01)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ACMEChallengeSolverHTTP01) DeepCopyInto(out *ACMEChallengeSolverHTTP01) {
*out = *in
if in.Ingress != nil {
in, out := &in.Ingress, &out.Ingress
*out = new(ACMEChallengeSolverHTTP01Ingress)
(*in).DeepCopyInto(*out)
}
if in.GatewayHTTPRoute != nil {
in, out := &in.GatewayHTTPRoute, &out.GatewayHTTPRoute
*out = new(ACMEChallengeSolverHTTP01GatewayHTTPRoute)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ACMEChallengeSolverHTTP01.
func (in *ACMEChallengeSolverHTTP01) DeepCopy() *ACMEChallengeSolverHTTP01 {
if in == nil {
return nil
}
out := new(ACMEChallengeSolverHTTP01)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ACMEChallengeSolverHTTP01GatewayHTTPRoute) DeepCopyInto(out *ACMEChallengeSolverHTTP01GatewayHTTPRoute) {
*out = *in
if in.Labels != nil {
in, out := &in.Labels, &out.Labels
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.ParentRefs != nil {
in, out := &in.ParentRefs, &out.ParentRefs
*out = make([]apisv1.ParentReference, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.PodTemplate != nil {
in, out := &in.PodTemplate, &out.PodTemplate
*out = new(ACMEChallengeSolverHTTP01IngressPodTemplate)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ACMEChallengeSolverHTTP01GatewayHTTPRoute.
func (in *ACMEChallengeSolverHTTP01GatewayHTTPRoute) DeepCopy() *ACMEChallengeSolverHTTP01GatewayHTTPRoute {
if in == nil {
return nil
}
out := new(ACMEChallengeSolverHTTP01GatewayHTTPRoute)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ACMEChallengeSolverHTTP01Ingress) DeepCopyInto(out *ACMEChallengeSolverHTTP01Ingress) {
*out = *in
if in.IngressClassName != nil {
in, out := &in.IngressClassName, &out.IngressClassName
*out = new(string)
**out = **in
}
if in.Class != nil {
in, out := &in.Class, &out.Class
*out = new(string)
**out = **in
}
if in.PodTemplate != nil {
in, out := &in.PodTemplate, &out.PodTemplate
*out = new(ACMEChallengeSolverHTTP01IngressPodTemplate)
(*in).DeepCopyInto(*out)
}
if in.IngressTemplate != nil {
in, out := &in.IngressTemplate, &out.IngressTemplate
*out = new(ACMEChallengeSolverHTTP01IngressTemplate)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ACMEChallengeSolverHTTP01Ingress.
func (in *ACMEChallengeSolverHTTP01Ingress) DeepCopy() *ACMEChallengeSolverHTTP01Ingress {
if in == nil {
return nil
}
out := new(ACMEChallengeSolverHTTP01Ingress)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ACMEChallengeSolverHTTP01IngressObjectMeta) DeepCopyInto(out *ACMEChallengeSolverHTTP01IngressObjectMeta) {
*out = *in
if in.Annotations != nil {
in, out := &in.Annotations, &out.Annotations
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.Labels != nil {
in, out := &in.Labels, &out.Labels
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ACMEChallengeSolverHTTP01IngressObjectMeta.
func (in *ACMEChallengeSolverHTTP01IngressObjectMeta) DeepCopy() *ACMEChallengeSolverHTTP01IngressObjectMeta {
if in == nil {
return nil
}
out := new(ACMEChallengeSolverHTTP01IngressObjectMeta)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ACMEChallengeSolverHTTP01IngressPodObjectMeta) DeepCopyInto(out *ACMEChallengeSolverHTTP01IngressPodObjectMeta) {
*out = *in
if in.Annotations != nil {
in, out := &in.Annotations, &out.Annotations
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.Labels != nil {
in, out := &in.Labels, &out.Labels
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ACMEChallengeSolverHTTP01IngressPodObjectMeta.
func (in *ACMEChallengeSolverHTTP01IngressPodObjectMeta) DeepCopy() *ACMEChallengeSolverHTTP01IngressPodObjectMeta {
if in == nil {
return nil
}
out := new(ACMEChallengeSolverHTTP01IngressPodObjectMeta)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ACMEChallengeSolverHTTP01IngressPodSecurityContext) DeepCopyInto(out *ACMEChallengeSolverHTTP01IngressPodSecurityContext) {
*out = *in
if in.SELinuxOptions != nil {
in, out := &in.SELinuxOptions, &out.SELinuxOptions
*out = new(corev1.SELinuxOptions)
**out = **in
}
if in.RunAsUser != nil {
in, out := &in.RunAsUser, &out.RunAsUser
*out = new(int64)
**out = **in
}
if in.RunAsGroup != nil {
in, out := &in.RunAsGroup, &out.RunAsGroup
*out = new(int64)
**out = **in
}
if in.RunAsNonRoot != nil {
in, out := &in.RunAsNonRoot, &out.RunAsNonRoot
*out = new(bool)
**out = **in
}
if in.SupplementalGroups != nil {
in, out := &in.SupplementalGroups, &out.SupplementalGroups
*out = make([]int64, len(*in))
copy(*out, *in)
}
if in.FSGroup != nil {
in, out := &in.FSGroup, &out.FSGroup
*out = new(int64)
**out = **in
}
if in.Sysctls != nil {
in, out := &in.Sysctls, &out.Sysctls
*out = make([]corev1.Sysctl, len(*in))
copy(*out, *in)
}
if in.FSGroupChangePolicy != nil {
in, out := &in.FSGroupChangePolicy, &out.FSGroupChangePolicy
*out = new(corev1.PodFSGroupChangePolicy)
**out = **in
}
if in.SeccompProfile != nil {
in, out := &in.SeccompProfile, &out.SeccompProfile
*out = new(corev1.SeccompProfile)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ACMEChallengeSolverHTTP01IngressPodSecurityContext.
func (in *ACMEChallengeSolverHTTP01IngressPodSecurityContext) DeepCopy() *ACMEChallengeSolverHTTP01IngressPodSecurityContext {
if in == nil {
return nil
}
out := new(ACMEChallengeSolverHTTP01IngressPodSecurityContext)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ACMEChallengeSolverHTTP01IngressPodSpec) DeepCopyInto(out *ACMEChallengeSolverHTTP01IngressPodSpec) {
*out = *in
if in.NodeSelector != nil {
in, out := &in.NodeSelector, &out.NodeSelector
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.Affinity != nil {
in, out := &in.Affinity, &out.Affinity
*out = new(corev1.Affinity)
(*in).DeepCopyInto(*out)
}
if in.Tolerations != nil {
in, out := &in.Tolerations, &out.Tolerations
*out = make([]corev1.Toleration, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.ImagePullSecrets != nil {
in, out := &in.ImagePullSecrets, &out.ImagePullSecrets
*out = make([]corev1.LocalObjectReference, len(*in))
copy(*out, *in)
}
if in.SecurityContext != nil {
in, out := &in.SecurityContext, &out.SecurityContext
*out = new(ACMEChallengeSolverHTTP01IngressPodSecurityContext)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ACMEChallengeSolverHTTP01IngressPodSpec.
func (in *ACMEChallengeSolverHTTP01IngressPodSpec) DeepCopy() *ACMEChallengeSolverHTTP01IngressPodSpec {
if in == nil {
return nil
}
out := new(ACMEChallengeSolverHTTP01IngressPodSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ACMEChallengeSolverHTTP01IngressPodTemplate) DeepCopyInto(out *ACMEChallengeSolverHTTP01IngressPodTemplate) {
*out = *in
in.ACMEChallengeSolverHTTP01IngressPodObjectMeta.DeepCopyInto(&out.ACMEChallengeSolverHTTP01IngressPodObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ACMEChallengeSolverHTTP01IngressPodTemplate.
func (in *ACMEChallengeSolverHTTP01IngressPodTemplate) DeepCopy() *ACMEChallengeSolverHTTP01IngressPodTemplate {
if in == nil {
return nil
}
out := new(ACMEChallengeSolverHTTP01IngressPodTemplate)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ACMEChallengeSolverHTTP01IngressTemplate) DeepCopyInto(out *ACMEChallengeSolverHTTP01IngressTemplate) {
*out = *in
in.ACMEChallengeSolverHTTP01IngressObjectMeta.DeepCopyInto(&out.ACMEChallengeSolverHTTP01IngressObjectMeta)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ACMEChallengeSolverHTTP01IngressTemplate.
func (in *ACMEChallengeSolverHTTP01IngressTemplate) DeepCopy() *ACMEChallengeSolverHTTP01IngressTemplate {
if in == nil {
return nil
}
out := new(ACMEChallengeSolverHTTP01IngressTemplate)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ACMEExternalAccountBinding) DeepCopyInto(out *ACMEExternalAccountBinding) {
*out = *in
out.Key = in.Key
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ACMEExternalAccountBinding.
func (in *ACMEExternalAccountBinding) DeepCopy() *ACMEExternalAccountBinding {
if in == nil {
return nil
}
out := new(ACMEExternalAccountBinding)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ACMEIssuer) DeepCopyInto(out *ACMEIssuer) {
*out = *in
if in.CABundle != nil {
in, out := &in.CABundle, &out.CABundle
*out = make([]byte, len(*in))
copy(*out, *in)
}
if in.ExternalAccountBinding != nil {
in, out := &in.ExternalAccountBinding, &out.ExternalAccountBinding
*out = new(ACMEExternalAccountBinding)
**out = **in
}
out.PrivateKey = in.PrivateKey
if in.Solvers != nil {
in, out := &in.Solvers, &out.Solvers
*out = make([]ACMEChallengeSolver, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ACMEIssuer.
func (in *ACMEIssuer) DeepCopy() *ACMEIssuer {
if in == nil {
return nil
}
out := new(ACMEIssuer)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ACMEIssuerDNS01ProviderAcmeDNS) DeepCopyInto(out *ACMEIssuerDNS01ProviderAcmeDNS) {
*out = *in
out.AccountSecret = in.AccountSecret
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ACMEIssuerDNS01ProviderAcmeDNS.
func (in *ACMEIssuerDNS01ProviderAcmeDNS) DeepCopy() *ACMEIssuerDNS01ProviderAcmeDNS {
if in == nil {
return nil
}
out := new(ACMEIssuerDNS01ProviderAcmeDNS)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ACMEIssuerDNS01ProviderAkamai) DeepCopyInto(out *ACMEIssuerDNS01ProviderAkamai) {
*out = *in
out.ClientToken = in.ClientToken
out.ClientSecret = in.ClientSecret
out.AccessToken = in.AccessToken
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ACMEIssuerDNS01ProviderAkamai.
func (in *ACMEIssuerDNS01ProviderAkamai) DeepCopy() *ACMEIssuerDNS01ProviderAkamai {
if in == nil {
return nil
}
out := new(ACMEIssuerDNS01ProviderAkamai)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ACMEIssuerDNS01ProviderAzureDNS) DeepCopyInto(out *ACMEIssuerDNS01ProviderAzureDNS) {
*out = *in
if in.ClientSecret != nil {
in, out := &in.ClientSecret, &out.ClientSecret
*out = new(metav1.SecretKeySelector)
**out = **in
}
if in.ManagedIdentity != nil {
in, out := &in.ManagedIdentity, &out.ManagedIdentity
*out = new(AzureManagedIdentity)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ACMEIssuerDNS01ProviderAzureDNS.
func (in *ACMEIssuerDNS01ProviderAzureDNS) DeepCopy() *ACMEIssuerDNS01ProviderAzureDNS {
if in == nil {
return nil
}
out := new(ACMEIssuerDNS01ProviderAzureDNS)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ACMEIssuerDNS01ProviderCloudDNS) DeepCopyInto(out *ACMEIssuerDNS01ProviderCloudDNS) {
*out = *in
if in.ServiceAccount != nil {
in, out := &in.ServiceAccount, &out.ServiceAccount
*out = new(metav1.SecretKeySelector)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ACMEIssuerDNS01ProviderCloudDNS.
func (in *ACMEIssuerDNS01ProviderCloudDNS) DeepCopy() *ACMEIssuerDNS01ProviderCloudDNS {
if in == nil {
return nil
}
out := new(ACMEIssuerDNS01ProviderCloudDNS)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ACMEIssuerDNS01ProviderCloudflare) DeepCopyInto(out *ACMEIssuerDNS01ProviderCloudflare) {
*out = *in
if in.APIKey != nil {
in, out := &in.APIKey, &out.APIKey
*out = new(metav1.SecretKeySelector)
**out = **in
}
if in.APIToken != nil {
in, out := &in.APIToken, &out.APIToken
*out = new(metav1.SecretKeySelector)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ACMEIssuerDNS01ProviderCloudflare.
func (in *ACMEIssuerDNS01ProviderCloudflare) DeepCopy() *ACMEIssuerDNS01ProviderCloudflare {
if in == nil {
return nil
}
out := new(ACMEIssuerDNS01ProviderCloudflare)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ACMEIssuerDNS01ProviderDigitalOcean) DeepCopyInto(out *ACMEIssuerDNS01ProviderDigitalOcean) {
*out = *in
out.Token = in.Token
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ACMEIssuerDNS01ProviderDigitalOcean.
func (in *ACMEIssuerDNS01ProviderDigitalOcean) DeepCopy() *ACMEIssuerDNS01ProviderDigitalOcean {
if in == nil {
return nil
}
out := new(ACMEIssuerDNS01ProviderDigitalOcean)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ACMEIssuerDNS01ProviderRFC2136) DeepCopyInto(out *ACMEIssuerDNS01ProviderRFC2136) {
*out = *in
out.TSIGSecret = in.TSIGSecret
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ACMEIssuerDNS01ProviderRFC2136.
func (in *ACMEIssuerDNS01ProviderRFC2136) DeepCopy() *ACMEIssuerDNS01ProviderRFC2136 {
if in == nil {
return nil
}
out := new(ACMEIssuerDNS01ProviderRFC2136)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ACMEIssuerDNS01ProviderRoute53) DeepCopyInto(out *ACMEIssuerDNS01ProviderRoute53) {
*out = *in
if in.Auth != nil {
in, out := &in.Auth, &out.Auth
*out = new(Route53Auth)
(*in).DeepCopyInto(*out)
}
if in.SecretAccessKeyID != nil {
in, out := &in.SecretAccessKeyID, &out.SecretAccessKeyID
*out = new(metav1.SecretKeySelector)
**out = **in
}
out.SecretAccessKey = in.SecretAccessKey
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ACMEIssuerDNS01ProviderRoute53.
func (in *ACMEIssuerDNS01ProviderRoute53) DeepCopy() *ACMEIssuerDNS01ProviderRoute53 {
if in == nil {
return nil
}
out := new(ACMEIssuerDNS01ProviderRoute53)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ACMEIssuerDNS01ProviderWebhook) DeepCopyInto(out *ACMEIssuerDNS01ProviderWebhook) {
*out = *in
if in.Config != nil {
in, out := &in.Config, &out.Config
*out = new(apiextensionsv1.JSON)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ACMEIssuerDNS01ProviderWebhook.
func (in *ACMEIssuerDNS01ProviderWebhook) DeepCopy() *ACMEIssuerDNS01ProviderWebhook {
if in == nil {
return nil
}
out := new(ACMEIssuerDNS01ProviderWebhook)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ACMEIssuerStatus) DeepCopyInto(out *ACMEIssuerStatus) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ACMEIssuerStatus.
func (in *ACMEIssuerStatus) DeepCopy() *ACMEIssuerStatus {
if in == nil {
return nil
}
out := new(ACMEIssuerStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AzureManagedIdentity) DeepCopyInto(out *AzureManagedIdentity) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AzureManagedIdentity.
func (in *AzureManagedIdentity) DeepCopy() *AzureManagedIdentity {
if in == nil {
return nil
}
out := new(AzureManagedIdentity)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CertificateDNSNameSelector) DeepCopyInto(out *CertificateDNSNameSelector) {
*out = *in
if in.MatchLabels != nil {
in, out := &in.MatchLabels, &out.MatchLabels
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.DNSNames != nil {
in, out := &in.DNSNames, &out.DNSNames
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.DNSZones != nil {
in, out := &in.DNSZones, &out.DNSZones
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CertificateDNSNameSelector.
func (in *CertificateDNSNameSelector) DeepCopy() *CertificateDNSNameSelector {
if in == nil {
return nil
}
out := new(CertificateDNSNameSelector)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Challenge) DeepCopyInto(out *Challenge) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
out.Status = in.Status
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Challenge.
func (in *Challenge) DeepCopy() *Challenge {
if in == nil {
return nil
}
out := new(Challenge)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Challenge) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ChallengeList) DeepCopyInto(out *ChallengeList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Challenge, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ChallengeList.
func (in *ChallengeList) DeepCopy() *ChallengeList {
if in == nil {
return nil
}
out := new(ChallengeList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ChallengeList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ChallengeSpec) DeepCopyInto(out *ChallengeSpec) {
*out = *in
in.Solver.DeepCopyInto(&out.Solver)
out.IssuerRef = in.IssuerRef
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ChallengeSpec.
func (in *ChallengeSpec) DeepCopy() *ChallengeSpec {
if in == nil {
return nil
}
out := new(ChallengeSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ChallengeStatus) DeepCopyInto(out *ChallengeStatus) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ChallengeStatus.
func (in *ChallengeStatus) DeepCopy() *ChallengeStatus {
if in == nil {
return nil
}
out := new(ChallengeStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Order) DeepCopyInto(out *Order) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Order.
func (in *Order) DeepCopy() *Order {
if in == nil {
return nil
}
out := new(Order)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Order) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OrderList) DeepCopyInto(out *OrderList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Order, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OrderList.
func (in *OrderList) DeepCopy() *OrderList {
if in == nil {
return nil
}
out := new(OrderList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *OrderList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OrderSpec) DeepCopyInto(out *OrderSpec) {
*out = *in
if in.Request != nil {
in, out := &in.Request, &out.Request
*out = make([]byte, len(*in))
copy(*out, *in)
}
out.IssuerRef = in.IssuerRef
if in.DNSNames != nil {
in, out := &in.DNSNames, &out.DNSNames
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.IPAddresses != nil {
in, out := &in.IPAddresses, &out.IPAddresses
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Duration != nil {
in, out := &in.Duration, &out.Duration
*out = new(apismetav1.Duration)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OrderSpec.
func (in *OrderSpec) DeepCopy() *OrderSpec {
if in == nil {
return nil
}
out := new(OrderSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OrderStatus) DeepCopyInto(out *OrderStatus) {
*out = *in
if in.Authorizations != nil {
in, out := &in.Authorizations, &out.Authorizations
*out = make([]ACMEAuthorization, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.Certificate != nil {
in, out := &in.Certificate, &out.Certificate
*out = make([]byte, len(*in))
copy(*out, *in)
}
if in.FailureTime != nil {
in, out := &in.FailureTime, &out.FailureTime
*out = (*in).DeepCopy()
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OrderStatus.
func (in *OrderStatus) DeepCopy() *OrderStatus {
if in == nil {
return nil
}
out := new(OrderStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Route53Auth) DeepCopyInto(out *Route53Auth) {
*out = *in
if in.Kubernetes != nil {
in, out := &in.Kubernetes, &out.Kubernetes
*out = new(Route53KubernetesAuth)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Route53Auth.
func (in *Route53Auth) DeepCopy() *Route53Auth {
if in == nil {
return nil
}
out := new(Route53Auth)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Route53KubernetesAuth) DeepCopyInto(out *Route53KubernetesAuth) {
*out = *in
if in.ServiceAccountRef != nil {
in, out := &in.ServiceAccountRef, &out.ServiceAccountRef
*out = new(ServiceAccountRef)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Route53KubernetesAuth.
func (in *Route53KubernetesAuth) DeepCopy() *Route53KubernetesAuth {
if in == nil {
return nil
}
out := new(Route53KubernetesAuth)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ServiceAccountRef) DeepCopyInto(out *ServiceAccountRef) {
*out = *in
if in.TokenAudiences != nil {
in, out := &in.TokenAudiences, &out.TokenAudiences
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceAccountRef.
func (in *ServiceAccountRef) DeepCopy() *ServiceAccountRef {
if in == nil {
return nil
}
out := new(ServiceAccountRef)
in.DeepCopyInto(out)
return out
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
cmacme "github.com/cert-manager/cert-manager/pkg/apis/acme/v1"
)
type GenericIssuer interface {
runtime.Object
metav1.Object
GetObjectMeta() *metav1.ObjectMeta
GetSpec() *IssuerSpec
GetStatus() *IssuerStatus
}
var _ GenericIssuer = &Issuer{}
var _ GenericIssuer = &ClusterIssuer{}
func (c *ClusterIssuer) GetObjectMeta() *metav1.ObjectMeta {
return &c.ObjectMeta
}
func (c *ClusterIssuer) GetSpec() *IssuerSpec {
return &c.Spec
}
func (c *ClusterIssuer) GetStatus() *IssuerStatus {
return &c.Status
}
func (c *ClusterIssuer) SetSpec(spec IssuerSpec) {
c.Spec = spec
}
func (c *ClusterIssuer) SetStatus(status IssuerStatus) {
c.Status = status
}
func (c *ClusterIssuer) Copy() GenericIssuer {
return c.DeepCopy()
}
func (c *Issuer) GetObjectMeta() *metav1.ObjectMeta {
return &c.ObjectMeta
}
func (c *Issuer) GetSpec() *IssuerSpec {
return &c.Spec
}
func (c *Issuer) GetStatus() *IssuerStatus {
return &c.Status
}
func (c *Issuer) SetSpec(spec IssuerSpec) {
c.Spec = spec
}
func (c *Issuer) SetStatus(status IssuerStatus) {
c.Status = status
}
func (c *Issuer) Copy() GenericIssuer {
return c.DeepCopy()
}
// TODO: refactor these functions away
func (i *IssuerStatus) ACMEStatus() *cmacme.ACMEIssuerStatus {
// this is an edge case, but this will prevent panics
if i == nil {
return &cmacme.ACMEIssuerStatus{}
}
if i.ACME == nil {
i.ACME = &cmacme.ACMEIssuerStatus{}
}
return i.ACME
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"github.com/cert-manager/cert-manager/pkg/apis/certmanager"
)
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: certmanager.GroupName, Version: "v1"}
// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
var (
SchemeBuilder runtime.SchemeBuilder
localSchemeBuilder = &SchemeBuilder
AddToScheme = localSchemeBuilder.AddToScheme
)
func init() {
// We only register manually written functions here. The registration of the
// generated functions takes place in the generated files. The separation
// makes the code compile even when the generated files are missing.
localSchemeBuilder.Register(addKnownTypes)
}
// Adds the list of known types to api.Scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&Certificate{},
&CertificateList{},
&Issuer{},
&IssuerList{},
&ClusterIssuer{},
&ClusterIssuerList{},
&CertificateRequest{},
&CertificateRequestList{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1
const (
// Common label keys added to resources
// Label key that indicates that a resource is of interest to
// cert-manager controller By default this is set on
// certificate.spec.secretName secret as well as on the temporary
// private key Secret. If using SecretsFilteredCaching feature, you
// might want to set this (with a value of 'true') to any other Secrets
// that cert-manager controller needs to read, such as issuer
// credentials Secrets.
// fao = 'for attention of'
// See https://github.com/cert-manager/cert-manager/blob/master/design/20221205-memory-management.md#risks-and-mitigations
PartOfCertManagerControllerLabelKey = "controller.cert-manager.io/fao"
// Common annotation keys added to resources
// Annotation key for DNS subjectAltNames.
AltNamesAnnotationKey = "cert-manager.io/alt-names"
// Annotation key for IP subjectAltNames.
IPSANAnnotationKey = "cert-manager.io/ip-sans"
// Annotation key for URI subjectAltNames.
URISANAnnotationKey = "cert-manager.io/uri-sans"
// Annotation key for certificate common name.
CommonNameAnnotationKey = "cert-manager.io/common-name"
// Duration key for certificate duration.
DurationAnnotationKey = "cert-manager.io/duration"
// Annotation key for certificate renewBefore.
RenewBeforeAnnotationKey = "cert-manager.io/renew-before"
// Annotation key for certificate renewBeforePercentage.
RenewBeforePercentageAnnotationKey = "cert-manager.io/renew-before-percentage"
// Annotation key for emails subjectAltNames.
EmailsAnnotationKey = "cert-manager.io/email-sans"
// Annotation key for subject organization.
SubjectOrganizationsAnnotationKey = "cert-manager.io/subject-organizations"
// Annotation key for subject organizational units.
SubjectOrganizationalUnitsAnnotationKey = "cert-manager.io/subject-organizationalunits"
// Annotation key for subject organizational units.
SubjectCountriesAnnotationKey = "cert-manager.io/subject-countries"
// Annotation key for subject provinces.
SubjectProvincesAnnotationKey = "cert-manager.io/subject-provinces"
// Annotation key for subject localities.
SubjectLocalitiesAnnotationKey = "cert-manager.io/subject-localities"
// Annotation key for subject provinces.
SubjectStreetAddressesAnnotationKey = "cert-manager.io/subject-streetaddresses"
// Annotation key for subject postal codes.
SubjectPostalCodesAnnotationKey = "cert-manager.io/subject-postalcodes"
// Annotation key for subject serial number.
SubjectSerialNumberAnnotationKey = "cert-manager.io/subject-serialnumber"
// Annotation key for certificate key usages.
UsagesAnnotationKey = "cert-manager.io/usages"
// Annotation key the 'name' of the Issuer resource.
IssuerNameAnnotationKey = "cert-manager.io/issuer-name"
// Annotation key for the 'kind' of the Issuer resource.
IssuerKindAnnotationKey = "cert-manager.io/issuer-kind"
// Annotation key for the 'group' of the Issuer resource.
IssuerGroupAnnotationKey = "cert-manager.io/issuer-group"
// Annotation key for the name of the certificate that a resource is related to.
CertificateNameKey = "cert-manager.io/certificate-name"
// Annotation key used to denote whether a Secret is named on a Certificate
// as a 'next private key' Secret resource.
IsNextPrivateKeySecretLabelKey = "cert-manager.io/next-private-key"
// Annotation key used to limit the number of CertificateRequests to be kept for a Certificate.
// Minimum value is 1.
// If unset all CertificateRequests will be kept.
RevisionHistoryLimitAnnotationKey = "cert-manager.io/revision-history-limit"
// Annotation key used to set the PrivateKeyAlgorithm for a Certificate.
// If PrivateKeyAlgorithm is specified and `size` is not provided,
// key size of 256 will be used for `ECDSA` key algorithm and
// key size of 2048 will be used for `RSA` key algorithm.
// key size is ignored when using the `Ed25519` key algorithm.
// If unset an algorithm `RSA` will be used.
PrivateKeyAlgorithmAnnotationKey = "cert-manager.io/private-key-algorithm"
// Annotation key used to set the PrivateKeyEncoding for a Certificate.
// If provided, allowed values are `PKCS1` and `PKCS8` standing for PKCS#1
// and PKCS#8, respectively.
// If unset an encoding `PKCS1` will be used.
PrivateKeyEncodingAnnotationKey = "cert-manager.io/private-key-encoding"
// Annotation key used to set the size of the private key for a Certificate.
// If PrivateKeyAlgorithm is set to `RSA`, valid values are `2048`, `4096` or `8192`,
// and will default to `2048` if not specified.
// If PrivateKeyAlgorithm is set to `ECDSA`, valid values are `256`, `384` or `521`,
// and will default to `256` if not specified.
// If PrivateKeyAlgorithm is set to `Ed25519`, Size is ignored.
// No other values are allowed.
PrivateKeySizeAnnotationKey = "cert-manager.io/private-key-size"
// Annotation key used to set the PrivateKeyRotationPolicy for a Certificate.
// If unset a policy `Never` will be used.
PrivateKeyRotationPolicyAnnotationKey = "cert-manager.io/private-key-rotation-policy"
)
const (
// IngressIssuerNameAnnotationKey holds the issuerNameAnnotation value which can be
// used to override the issuer specified on the created Certificate resource.
IngressIssuerNameAnnotationKey = "cert-manager.io/issuer"
// IngressClusterIssuerNameAnnotationKey holds the clusterIssuerNameAnnotation value which
// can be used to override the issuer specified on the created Certificate resource. The Certificate
// will reference the specified *ClusterIssuer* instead of normal issuer.
IngressClusterIssuerNameAnnotationKey = "cert-manager.io/cluster-issuer"
// IngressACMEIssuerHTTP01IngressClassAnnotationKey holds the acmeIssuerHTTP01IngressClassAnnotation value
// which can be used to override the http01 ingressClass if the challenge type is set to http01
IngressACMEIssuerHTTP01IngressClassAnnotationKey = "acme.cert-manager.io/http01-ingress-class"
// IngressClassAnnotationKey picks a specific "class" for the Ingress. The
// controller only processes Ingresses with this annotation either unset, or
// set to either the configured value or the empty string.
IngressClassAnnotationKey = "kubernetes.io/ingress.class"
// IngressSecretTemplate can be used to set the secretTemplate field in the generated Certificate.
// The value is a JSON representation of secretTemplate and must not have any unknown fields.
IngressSecretTemplate = "cert-manager.io/secret-template"
)
// Annotation names for CertificateRequests
const (
// Annotation added to CertificateRequest resources to denote the name of
// a Secret resource containing the private key used to sign the CSR stored
// on the resource.
// This annotation *may* not be present, and is used by the 'self signing'
// issuer type to self-sign certificates.
CertificateRequestPrivateKeyAnnotationKey = "cert-manager.io/private-key-secret-name"
// Annotation to declare the CertificateRequest "revision", belonging to a Certificate Resource
CertificateRequestRevisionAnnotationKey = "cert-manager.io/certificate-revision"
)
const (
// IssueTemporaryCertificateAnnotation is an annotation that can be added to
// Certificate resources.
// If it is present, a temporary internally signed certificate will be
// stored in the target Secret resource whilst the real Issuer is processing
// the certificate request.
IssueTemporaryCertificateAnnotation = "cert-manager.io/issue-temporary-certificate"
)
// Common/known resource kinds.
const (
ClusterIssuerKind = "ClusterIssuer"
IssuerKind = "Issuer"
CertificateKind = "Certificate"
CertificateRequestKind = "CertificateRequest"
)
const (
// WantInjectAnnotation is the annotation that specifies that a particular
// object wants injection of CAs. It takes the form of a reference to a certificate
// as namespace/name. The certificate is expected to have the is-serving-for annotations.
WantInjectAnnotation = "cert-manager.io/inject-ca-from"
// WantInjectAPIServerCAAnnotation will - if set to "true" - make the cainjector
// inject the CA certificate for the Kubernetes apiserver into the resource.
// It discovers the apiserver's CA by inspecting the service account credentials
// mounted into the cainjector pod.
WantInjectAPIServerCAAnnotation = "cert-manager.io/inject-apiserver-ca"
// WantInjectFromSecretAnnotation is the annotation that specifies that a particular
// object wants injection of CAs. It takes the form of a reference to a Secret
// as namespace/name.
WantInjectFromSecretAnnotation = "cert-manager.io/inject-ca-from-secret"
// AllowsInjectionFromSecretAnnotation is an annotation that must be added
// to Secret resource that want to denote that they can be directly
// injected into injectables that have a `inject-ca-from-secret` annotation.
// If an injectable references a Secret that does NOT have this annotation,
// the cainjector will refuse to inject the secret.
AllowsInjectionFromSecretAnnotation = "cert-manager.io/allow-direct-injection"
)
// Issuer specific Annotations
const (
// VenafiCustomFieldsAnnotationKey is the annotation that passes on JSON encoded custom fields to the Venafi issuer
// This will only work with Venafi TPP v19.3 and higher
// The value is an array with objects containing the name and value keys
// for example: `[{"name": "custom-field", "value": "custom-value"}]`
VenafiCustomFieldsAnnotationKey = "venafi.cert-manager.io/custom-fields"
// VenafiPickupIDAnnotationKey is the annotation key used to record the
// Venafi Pickup ID of a certificate signing request that has been submitted
// to the Venafi API for collection later.
VenafiPickupIDAnnotationKey = "venafi.cert-manager.io/pickup-id"
)
// KeyUsage specifies valid usage contexts for keys.
// See:
// https://tools.ietf.org/html/rfc5280#section-4.2.1.3
// https://tools.ietf.org/html/rfc5280#section-4.2.1.12
//
// Valid KeyUsage values are as follows:
// "signing",
// "digital signature",
// "content commitment",
// "key encipherment",
// "key agreement",
// "data encipherment",
// "cert sign",
// "crl sign",
// "encipher only",
// "decipher only",
// "any",
// "server auth",
// "client auth",
// "code signing",
// "email protection",
// "s/mime",
// "ipsec end system",
// "ipsec tunnel",
// "ipsec user",
// "timestamping",
// "ocsp signing",
// "microsoft sgc",
// "netscape sgc"
// +kubebuilder:validation:Enum="signing";"digital signature";"content commitment";"key encipherment";"key agreement";"data encipherment";"cert sign";"crl sign";"encipher only";"decipher only";"any";"server auth";"client auth";"code signing";"email protection";"s/mime";"ipsec end system";"ipsec tunnel";"ipsec user";"timestamping";"ocsp signing";"microsoft sgc";"netscape sgc"
type KeyUsage string
const (
UsageSigning KeyUsage = "signing"
UsageDigitalSignature KeyUsage = "digital signature"
UsageContentCommitment KeyUsage = "content commitment"
UsageKeyEncipherment KeyUsage = "key encipherment"
UsageKeyAgreement KeyUsage = "key agreement"
UsageDataEncipherment KeyUsage = "data encipherment"
UsageCertSign KeyUsage = "cert sign"
UsageCRLSign KeyUsage = "crl sign"
UsageEncipherOnly KeyUsage = "encipher only"
UsageDecipherOnly KeyUsage = "decipher only"
UsageAny KeyUsage = "any"
UsageServerAuth KeyUsage = "server auth"
UsageClientAuth KeyUsage = "client auth"
UsageCodeSigning KeyUsage = "code signing"
UsageEmailProtection KeyUsage = "email protection"
UsageSMIME KeyUsage = "s/mime"
UsageIPsecEndSystem KeyUsage = "ipsec end system"
UsageIPsecTunnel KeyUsage = "ipsec tunnel"
UsageIPsecUser KeyUsage = "ipsec user"
UsageTimestamping KeyUsage = "timestamping"
UsageOCSPSigning KeyUsage = "ocsp signing"
UsageMicrosoftSGC KeyUsage = "microsoft sgc"
UsageNetscapeSGC KeyUsage = "netscape sgc"
)
// Keystore specific secret keys
const (
// PKCS12SecretKey is the name of the data entry in the Secret resource
// used to store the p12 file.
PKCS12SecretKey = "keystore.p12"
// Data Entry Name in the Secret resource for PKCS12 containing Certificate Authority
PKCS12TruststoreKey = "truststore.p12"
// JKSSecretKey is the name of the data entry in the Secret resource
// used to store the jks file.
JKSSecretKey = "keystore.jks"
// Data Entry Name in the Secret resource for JKS containing Certificate Authority
JKSTruststoreKey = "truststore.jks"
// The password used to encrypt the keystore and truststore
KeystorePassword = "keystorePassword"
)
// DefaultKeyUsages contains the default list of key usages
func DefaultKeyUsages() []KeyUsage {
// The serverAuth EKU is required as of Mac OS Catalina: https://support.apple.com/en-us/HT210176
// Without this usage, certificates will _always_ flag a warning in newer Mac OS browsers.
// We don't explicitly add it here as it leads to strange behaviour when a user sets isCA: true
// (in which case, 'serverAuth' on the CA can break a lot of clients).
// CAs can (and often do) opt to automatically add usages.
return []KeyUsage{UsageDigitalSignature, UsageKeyEncipherment}
}
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*
Copyright The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by deepcopy-gen. DO NOT EDIT.
package v1
import (
acmev1 "github.com/cert-manager/cert-manager/pkg/apis/acme/v1"
apismetav1 "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CAIssuer) DeepCopyInto(out *CAIssuer) {
*out = *in
if in.CRLDistributionPoints != nil {
in, out := &in.CRLDistributionPoints, &out.CRLDistributionPoints
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.OCSPServers != nil {
in, out := &in.OCSPServers, &out.OCSPServers
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.IssuingCertificateURLs != nil {
in, out := &in.IssuingCertificateURLs, &out.IssuingCertificateURLs
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CAIssuer.
func (in *CAIssuer) DeepCopy() *CAIssuer {
if in == nil {
return nil
}
out := new(CAIssuer)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Certificate) DeepCopyInto(out *Certificate) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Certificate.
func (in *Certificate) DeepCopy() *Certificate {
if in == nil {
return nil
}
out := new(Certificate)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Certificate) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CertificateAdditionalOutputFormat) DeepCopyInto(out *CertificateAdditionalOutputFormat) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CertificateAdditionalOutputFormat.
func (in *CertificateAdditionalOutputFormat) DeepCopy() *CertificateAdditionalOutputFormat {
if in == nil {
return nil
}
out := new(CertificateAdditionalOutputFormat)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CertificateCondition) DeepCopyInto(out *CertificateCondition) {
*out = *in
if in.LastTransitionTime != nil {
in, out := &in.LastTransitionTime, &out.LastTransitionTime
*out = (*in).DeepCopy()
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CertificateCondition.
func (in *CertificateCondition) DeepCopy() *CertificateCondition {
if in == nil {
return nil
}
out := new(CertificateCondition)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CertificateKeystores) DeepCopyInto(out *CertificateKeystores) {
*out = *in
if in.JKS != nil {
in, out := &in.JKS, &out.JKS
*out = new(JKSKeystore)
(*in).DeepCopyInto(*out)
}
if in.PKCS12 != nil {
in, out := &in.PKCS12, &out.PKCS12
*out = new(PKCS12Keystore)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CertificateKeystores.
func (in *CertificateKeystores) DeepCopy() *CertificateKeystores {
if in == nil {
return nil
}
out := new(CertificateKeystores)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CertificateList) DeepCopyInto(out *CertificateList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Certificate, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CertificateList.
func (in *CertificateList) DeepCopy() *CertificateList {
if in == nil {
return nil
}
out := new(CertificateList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *CertificateList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CertificatePrivateKey) DeepCopyInto(out *CertificatePrivateKey) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CertificatePrivateKey.
func (in *CertificatePrivateKey) DeepCopy() *CertificatePrivateKey {
if in == nil {
return nil
}
out := new(CertificatePrivateKey)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CertificateRequest) DeepCopyInto(out *CertificateRequest) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CertificateRequest.
func (in *CertificateRequest) DeepCopy() *CertificateRequest {
if in == nil {
return nil
}
out := new(CertificateRequest)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *CertificateRequest) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CertificateRequestCondition) DeepCopyInto(out *CertificateRequestCondition) {
*out = *in
if in.LastTransitionTime != nil {
in, out := &in.LastTransitionTime, &out.LastTransitionTime
*out = (*in).DeepCopy()
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CertificateRequestCondition.
func (in *CertificateRequestCondition) DeepCopy() *CertificateRequestCondition {
if in == nil {
return nil
}
out := new(CertificateRequestCondition)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CertificateRequestList) DeepCopyInto(out *CertificateRequestList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]CertificateRequest, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CertificateRequestList.
func (in *CertificateRequestList) DeepCopy() *CertificateRequestList {
if in == nil {
return nil
}
out := new(CertificateRequestList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *CertificateRequestList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CertificateRequestSpec) DeepCopyInto(out *CertificateRequestSpec) {
*out = *in
if in.Duration != nil {
in, out := &in.Duration, &out.Duration
*out = new(metav1.Duration)
**out = **in
}
out.IssuerRef = in.IssuerRef
if in.Request != nil {
in, out := &in.Request, &out.Request
*out = make([]byte, len(*in))
copy(*out, *in)
}
if in.Usages != nil {
in, out := &in.Usages, &out.Usages
*out = make([]KeyUsage, len(*in))
copy(*out, *in)
}
if in.Groups != nil {
in, out := &in.Groups, &out.Groups
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Extra != nil {
in, out := &in.Extra, &out.Extra
*out = make(map[string][]string, len(*in))
for key, val := range *in {
var outVal []string
if val == nil {
(*out)[key] = nil
} else {
in, out := &val, &outVal
*out = make([]string, len(*in))
copy(*out, *in)
}
(*out)[key] = outVal
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CertificateRequestSpec.
func (in *CertificateRequestSpec) DeepCopy() *CertificateRequestSpec {
if in == nil {
return nil
}
out := new(CertificateRequestSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CertificateRequestStatus) DeepCopyInto(out *CertificateRequestStatus) {
*out = *in
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]CertificateRequestCondition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.Certificate != nil {
in, out := &in.Certificate, &out.Certificate
*out = make([]byte, len(*in))
copy(*out, *in)
}
if in.CA != nil {
in, out := &in.CA, &out.CA
*out = make([]byte, len(*in))
copy(*out, *in)
}
if in.FailureTime != nil {
in, out := &in.FailureTime, &out.FailureTime
*out = (*in).DeepCopy()
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CertificateRequestStatus.
func (in *CertificateRequestStatus) DeepCopy() *CertificateRequestStatus {
if in == nil {
return nil
}
out := new(CertificateRequestStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CertificateSecretTemplate) DeepCopyInto(out *CertificateSecretTemplate) {
*out = *in
if in.Annotations != nil {
in, out := &in.Annotations, &out.Annotations
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.Labels != nil {
in, out := &in.Labels, &out.Labels
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CertificateSecretTemplate.
func (in *CertificateSecretTemplate) DeepCopy() *CertificateSecretTemplate {
if in == nil {
return nil
}
out := new(CertificateSecretTemplate)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CertificateSpec) DeepCopyInto(out *CertificateSpec) {
*out = *in
if in.Subject != nil {
in, out := &in.Subject, &out.Subject
*out = new(X509Subject)
(*in).DeepCopyInto(*out)
}
if in.Duration != nil {
in, out := &in.Duration, &out.Duration
*out = new(metav1.Duration)
**out = **in
}
if in.RenewBefore != nil {
in, out := &in.RenewBefore, &out.RenewBefore
*out = new(metav1.Duration)
**out = **in
}
if in.RenewBeforePercentage != nil {
in, out := &in.RenewBeforePercentage, &out.RenewBeforePercentage
*out = new(int32)
**out = **in
}
if in.DNSNames != nil {
in, out := &in.DNSNames, &out.DNSNames
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.IPAddresses != nil {
in, out := &in.IPAddresses, &out.IPAddresses
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.URIs != nil {
in, out := &in.URIs, &out.URIs
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.OtherNames != nil {
in, out := &in.OtherNames, &out.OtherNames
*out = make([]OtherName, len(*in))
copy(*out, *in)
}
if in.EmailAddresses != nil {
in, out := &in.EmailAddresses, &out.EmailAddresses
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.SecretTemplate != nil {
in, out := &in.SecretTemplate, &out.SecretTemplate
*out = new(CertificateSecretTemplate)
(*in).DeepCopyInto(*out)
}
if in.Keystores != nil {
in, out := &in.Keystores, &out.Keystores
*out = new(CertificateKeystores)
(*in).DeepCopyInto(*out)
}
out.IssuerRef = in.IssuerRef
if in.Usages != nil {
in, out := &in.Usages, &out.Usages
*out = make([]KeyUsage, len(*in))
copy(*out, *in)
}
if in.PrivateKey != nil {
in, out := &in.PrivateKey, &out.PrivateKey
*out = new(CertificatePrivateKey)
**out = **in
}
if in.EncodeUsagesInRequest != nil {
in, out := &in.EncodeUsagesInRequest, &out.EncodeUsagesInRequest
*out = new(bool)
**out = **in
}
if in.RevisionHistoryLimit != nil {
in, out := &in.RevisionHistoryLimit, &out.RevisionHistoryLimit
*out = new(int32)
**out = **in
}
if in.AdditionalOutputFormats != nil {
in, out := &in.AdditionalOutputFormats, &out.AdditionalOutputFormats
*out = make([]CertificateAdditionalOutputFormat, len(*in))
copy(*out, *in)
}
if in.NameConstraints != nil {
in, out := &in.NameConstraints, &out.NameConstraints
*out = new(NameConstraints)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CertificateSpec.
func (in *CertificateSpec) DeepCopy() *CertificateSpec {
if in == nil {
return nil
}
out := new(CertificateSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CertificateStatus) DeepCopyInto(out *CertificateStatus) {
*out = *in
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]CertificateCondition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.LastFailureTime != nil {
in, out := &in.LastFailureTime, &out.LastFailureTime
*out = (*in).DeepCopy()
}
if in.NotBefore != nil {
in, out := &in.NotBefore, &out.NotBefore
*out = (*in).DeepCopy()
}
if in.NotAfter != nil {
in, out := &in.NotAfter, &out.NotAfter
*out = (*in).DeepCopy()
}
if in.RenewalTime != nil {
in, out := &in.RenewalTime, &out.RenewalTime
*out = (*in).DeepCopy()
}
if in.Revision != nil {
in, out := &in.Revision, &out.Revision
*out = new(int)
**out = **in
}
if in.NextPrivateKeySecretName != nil {
in, out := &in.NextPrivateKeySecretName, &out.NextPrivateKeySecretName
*out = new(string)
**out = **in
}
if in.FailedIssuanceAttempts != nil {
in, out := &in.FailedIssuanceAttempts, &out.FailedIssuanceAttempts
*out = new(int)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CertificateStatus.
func (in *CertificateStatus) DeepCopy() *CertificateStatus {
if in == nil {
return nil
}
out := new(CertificateStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClusterIssuer) DeepCopyInto(out *ClusterIssuer) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterIssuer.
func (in *ClusterIssuer) DeepCopy() *ClusterIssuer {
if in == nil {
return nil
}
out := new(ClusterIssuer)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ClusterIssuer) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClusterIssuerList) DeepCopyInto(out *ClusterIssuerList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]ClusterIssuer, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterIssuerList.
func (in *ClusterIssuerList) DeepCopy() *ClusterIssuerList {
if in == nil {
return nil
}
out := new(ClusterIssuerList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ClusterIssuerList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Issuer) DeepCopyInto(out *Issuer) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Issuer.
func (in *Issuer) DeepCopy() *Issuer {
if in == nil {
return nil
}
out := new(Issuer)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Issuer) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *IssuerCondition) DeepCopyInto(out *IssuerCondition) {
*out = *in
if in.LastTransitionTime != nil {
in, out := &in.LastTransitionTime, &out.LastTransitionTime
*out = (*in).DeepCopy()
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IssuerCondition.
func (in *IssuerCondition) DeepCopy() *IssuerCondition {
if in == nil {
return nil
}
out := new(IssuerCondition)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *IssuerConfig) DeepCopyInto(out *IssuerConfig) {
*out = *in
if in.ACME != nil {
in, out := &in.ACME, &out.ACME
*out = new(acmev1.ACMEIssuer)
(*in).DeepCopyInto(*out)
}
if in.CA != nil {
in, out := &in.CA, &out.CA
*out = new(CAIssuer)
(*in).DeepCopyInto(*out)
}
if in.Vault != nil {
in, out := &in.Vault, &out.Vault
*out = new(VaultIssuer)
(*in).DeepCopyInto(*out)
}
if in.SelfSigned != nil {
in, out := &in.SelfSigned, &out.SelfSigned
*out = new(SelfSignedIssuer)
(*in).DeepCopyInto(*out)
}
if in.Venafi != nil {
in, out := &in.Venafi, &out.Venafi
*out = new(VenafiIssuer)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IssuerConfig.
func (in *IssuerConfig) DeepCopy() *IssuerConfig {
if in == nil {
return nil
}
out := new(IssuerConfig)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *IssuerList) DeepCopyInto(out *IssuerList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Issuer, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IssuerList.
func (in *IssuerList) DeepCopy() *IssuerList {
if in == nil {
return nil
}
out := new(IssuerList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *IssuerList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *IssuerSpec) DeepCopyInto(out *IssuerSpec) {
*out = *in
in.IssuerConfig.DeepCopyInto(&out.IssuerConfig)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IssuerSpec.
func (in *IssuerSpec) DeepCopy() *IssuerSpec {
if in == nil {
return nil
}
out := new(IssuerSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *IssuerStatus) DeepCopyInto(out *IssuerStatus) {
*out = *in
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]IssuerCondition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.ACME != nil {
in, out := &in.ACME, &out.ACME
*out = new(acmev1.ACMEIssuerStatus)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IssuerStatus.
func (in *IssuerStatus) DeepCopy() *IssuerStatus {
if in == nil {
return nil
}
out := new(IssuerStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *JKSKeystore) DeepCopyInto(out *JKSKeystore) {
*out = *in
if in.Alias != nil {
in, out := &in.Alias, &out.Alias
*out = new(string)
**out = **in
}
out.PasswordSecretRef = in.PasswordSecretRef
if in.Password != nil {
in, out := &in.Password, &out.Password
*out = new(string)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JKSKeystore.
func (in *JKSKeystore) DeepCopy() *JKSKeystore {
if in == nil {
return nil
}
out := new(JKSKeystore)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NameConstraintItem) DeepCopyInto(out *NameConstraintItem) {
*out = *in
if in.DNSDomains != nil {
in, out := &in.DNSDomains, &out.DNSDomains
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.IPRanges != nil {
in, out := &in.IPRanges, &out.IPRanges
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.EmailAddresses != nil {
in, out := &in.EmailAddresses, &out.EmailAddresses
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.URIDomains != nil {
in, out := &in.URIDomains, &out.URIDomains
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NameConstraintItem.
func (in *NameConstraintItem) DeepCopy() *NameConstraintItem {
if in == nil {
return nil
}
out := new(NameConstraintItem)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NameConstraints) DeepCopyInto(out *NameConstraints) {
*out = *in
if in.Permitted != nil {
in, out := &in.Permitted, &out.Permitted
*out = new(NameConstraintItem)
(*in).DeepCopyInto(*out)
}
if in.Excluded != nil {
in, out := &in.Excluded, &out.Excluded
*out = new(NameConstraintItem)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NameConstraints.
func (in *NameConstraints) DeepCopy() *NameConstraints {
if in == nil {
return nil
}
out := new(NameConstraints)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OtherName) DeepCopyInto(out *OtherName) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OtherName.
func (in *OtherName) DeepCopy() *OtherName {
if in == nil {
return nil
}
out := new(OtherName)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PKCS12Keystore) DeepCopyInto(out *PKCS12Keystore) {
*out = *in
out.PasswordSecretRef = in.PasswordSecretRef
if in.Password != nil {
in, out := &in.Password, &out.Password
*out = new(string)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PKCS12Keystore.
func (in *PKCS12Keystore) DeepCopy() *PKCS12Keystore {
if in == nil {
return nil
}
out := new(PKCS12Keystore)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SelfSignedIssuer) DeepCopyInto(out *SelfSignedIssuer) {
*out = *in
if in.CRLDistributionPoints != nil {
in, out := &in.CRLDistributionPoints, &out.CRLDistributionPoints
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SelfSignedIssuer.
func (in *SelfSignedIssuer) DeepCopy() *SelfSignedIssuer {
if in == nil {
return nil
}
out := new(SelfSignedIssuer)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ServiceAccountRef) DeepCopyInto(out *ServiceAccountRef) {
*out = *in
if in.TokenAudiences != nil {
in, out := &in.TokenAudiences, &out.TokenAudiences
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceAccountRef.
func (in *ServiceAccountRef) DeepCopy() *ServiceAccountRef {
if in == nil {
return nil
}
out := new(ServiceAccountRef)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VaultAppRole) DeepCopyInto(out *VaultAppRole) {
*out = *in
out.SecretRef = in.SecretRef
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VaultAppRole.
func (in *VaultAppRole) DeepCopy() *VaultAppRole {
if in == nil {
return nil
}
out := new(VaultAppRole)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VaultAuth) DeepCopyInto(out *VaultAuth) {
*out = *in
if in.TokenSecretRef != nil {
in, out := &in.TokenSecretRef, &out.TokenSecretRef
*out = new(apismetav1.SecretKeySelector)
**out = **in
}
if in.AppRole != nil {
in, out := &in.AppRole, &out.AppRole
*out = new(VaultAppRole)
**out = **in
}
if in.ClientCertificate != nil {
in, out := &in.ClientCertificate, &out.ClientCertificate
*out = new(VaultClientCertificateAuth)
**out = **in
}
if in.Kubernetes != nil {
in, out := &in.Kubernetes, &out.Kubernetes
*out = new(VaultKubernetesAuth)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VaultAuth.
func (in *VaultAuth) DeepCopy() *VaultAuth {
if in == nil {
return nil
}
out := new(VaultAuth)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VaultClientCertificateAuth) DeepCopyInto(out *VaultClientCertificateAuth) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VaultClientCertificateAuth.
func (in *VaultClientCertificateAuth) DeepCopy() *VaultClientCertificateAuth {
if in == nil {
return nil
}
out := new(VaultClientCertificateAuth)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VaultIssuer) DeepCopyInto(out *VaultIssuer) {
*out = *in
in.Auth.DeepCopyInto(&out.Auth)
if in.CABundle != nil {
in, out := &in.CABundle, &out.CABundle
*out = make([]byte, len(*in))
copy(*out, *in)
}
if in.CABundleSecretRef != nil {
in, out := &in.CABundleSecretRef, &out.CABundleSecretRef
*out = new(apismetav1.SecretKeySelector)
**out = **in
}
if in.ClientCertSecretRef != nil {
in, out := &in.ClientCertSecretRef, &out.ClientCertSecretRef
*out = new(apismetav1.SecretKeySelector)
**out = **in
}
if in.ClientKeySecretRef != nil {
in, out := &in.ClientKeySecretRef, &out.ClientKeySecretRef
*out = new(apismetav1.SecretKeySelector)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VaultIssuer.
func (in *VaultIssuer) DeepCopy() *VaultIssuer {
if in == nil {
return nil
}
out := new(VaultIssuer)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VaultKubernetesAuth) DeepCopyInto(out *VaultKubernetesAuth) {
*out = *in
out.SecretRef = in.SecretRef
if in.ServiceAccountRef != nil {
in, out := &in.ServiceAccountRef, &out.ServiceAccountRef
*out = new(ServiceAccountRef)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VaultKubernetesAuth.
func (in *VaultKubernetesAuth) DeepCopy() *VaultKubernetesAuth {
if in == nil {
return nil
}
out := new(VaultKubernetesAuth)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VenafiCloud) DeepCopyInto(out *VenafiCloud) {
*out = *in
out.APITokenSecretRef = in.APITokenSecretRef
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VenafiCloud.
func (in *VenafiCloud) DeepCopy() *VenafiCloud {
if in == nil {
return nil
}
out := new(VenafiCloud)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VenafiIssuer) DeepCopyInto(out *VenafiIssuer) {
*out = *in
if in.TPP != nil {
in, out := &in.TPP, &out.TPP
*out = new(VenafiTPP)
(*in).DeepCopyInto(*out)
}
if in.Cloud != nil {
in, out := &in.Cloud, &out.Cloud
*out = new(VenafiCloud)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VenafiIssuer.
func (in *VenafiIssuer) DeepCopy() *VenafiIssuer {
if in == nil {
return nil
}
out := new(VenafiIssuer)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VenafiTPP) DeepCopyInto(out *VenafiTPP) {
*out = *in
out.CredentialsRef = in.CredentialsRef
if in.CABundle != nil {
in, out := &in.CABundle, &out.CABundle
*out = make([]byte, len(*in))
copy(*out, *in)
}
if in.CABundleSecretRef != nil {
in, out := &in.CABundleSecretRef, &out.CABundleSecretRef
*out = new(apismetav1.SecretKeySelector)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VenafiTPP.
func (in *VenafiTPP) DeepCopy() *VenafiTPP {
if in == nil {
return nil
}
out := new(VenafiTPP)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *X509Subject) DeepCopyInto(out *X509Subject) {
*out = *in
if in.Organizations != nil {
in, out := &in.Organizations, &out.Organizations
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Countries != nil {
in, out := &in.Countries, &out.Countries
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.OrganizationalUnits != nil {
in, out := &in.OrganizationalUnits, &out.OrganizationalUnits
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Localities != nil {
in, out := &in.Localities, &out.Localities
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Provinces != nil {
in, out := &in.Provinces, &out.Provinces
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.StreetAddresses != nil {
in, out := &in.StreetAddresses, &out.StreetAddresses
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.PostalCodes != nil {
in, out := &in.PostalCodes, &out.PostalCodes
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new X509Subject.
func (in *X509Subject) DeepCopy() *X509Subject {
if in == nil {
return nil
}
out := new(X509Subject)
in.DeepCopyInto(out)
return out
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta"
)
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: cmmeta.GroupName, Version: "v1"}
// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
var (
SchemeBuilder runtime.SchemeBuilder
localSchemeBuilder = &SchemeBuilder
AddToScheme = localSchemeBuilder.AddToScheme
)
func init() {
// We only register manually written functions here. The registration of the
// generated functions takes place in the generated files. The separation
// makes the code compile even when the generated files are missing.
localSchemeBuilder.Register(addKnownTypes)
}
// Adds the list of known types to api.Scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
// No types to register in the meta group
return nil
}
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*
Copyright The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by deepcopy-gen. DO NOT EDIT.
package v1
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *LocalObjectReference) DeepCopyInto(out *LocalObjectReference) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LocalObjectReference.
func (in *LocalObjectReference) DeepCopy() *LocalObjectReference {
if in == nil {
return nil
}
out := new(LocalObjectReference)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ObjectReference) DeepCopyInto(out *ObjectReference) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectReference.
func (in *ObjectReference) DeepCopy() *ObjectReference {
if in == nil {
return nil
}
out := new(ObjectReference)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SecretKeySelector) DeepCopyInto(out *SecretKeySelector) {
*out = *in
out.LocalObjectReference = in.LocalObjectReference
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretKeySelector.
func (in *SecretKeySelector) DeepCopy() *SecretKeySelector {
if in == nil {
return nil
}
out := new(SecretKeySelector)
in.DeepCopyInto(out)
return out
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package vault
import (
"context"
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
internalinformers "github.com/cert-manager/cert-manager/internal/informers"
vaultinternal "github.com/cert-manager/cert-manager/internal/vault"
apiutil "github.com/cert-manager/cert-manager/pkg/api/util"
v1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
controllerpkg "github.com/cert-manager/cert-manager/pkg/controller"
"github.com/cert-manager/cert-manager/pkg/controller/certificaterequests"
crutil "github.com/cert-manager/cert-manager/pkg/controller/certificaterequests/util"
"github.com/cert-manager/cert-manager/pkg/issuer"
logf "github.com/cert-manager/cert-manager/pkg/logs"
cmerrors "github.com/cert-manager/cert-manager/pkg/util/errors"
)
const (
// CRControllerName is the name of Vault certificate requests controller.
CRControllerName = "certificaterequests-issuer-vault"
)
// Vault is a Vault-specific implementation of
// pkg/controller/certificaterequests.Issuer interface.
type Vault struct {
issuerOptions controllerpkg.IssuerOptions
createTokenFn func(ns string) vaultinternal.CreateToken
secretsLister internalinformers.SecretLister
reporter *crutil.Reporter
vaultClientBuilder vaultinternal.ClientBuilder
}
func init() {
// create certificate request controller for vault issuer
controllerpkg.Register(CRControllerName, func(ctx *controllerpkg.ContextFactory) (controllerpkg.Interface, error) {
return controllerpkg.NewBuilder(ctx, CRControllerName).
For(certificaterequests.New(apiutil.IssuerVault, NewVault)).
Complete()
})
}
// NewVault returns a new Vault instance with the given controller context.
func NewVault(ctx *controllerpkg.Context) certificaterequests.Issuer {
return &Vault{
issuerOptions: ctx.IssuerOptions,
createTokenFn: func(ns string) vaultinternal.CreateToken {
return ctx.Client.CoreV1().ServiceAccounts(ns).CreateToken
},
secretsLister: ctx.KubeSharedInformerFactory.Secrets().Lister(),
reporter: crutil.NewReporter(ctx.Clock, ctx.Recorder),
vaultClientBuilder: vaultinternal.New,
}
}
// Sign will connect to Vault server associated with the provided issuer to sign
// the X.509 certificate from the Certificate Request.
func (v *Vault) Sign(ctx context.Context, cr *v1.CertificateRequest, issuerObj v1.GenericIssuer) (*issuer.IssueResponse, error) {
log := logf.FromContext(ctx, "sign")
log = logf.WithRelatedResource(log, issuerObj)
resourceNamespace := v.issuerOptions.ResourceNamespace(issuerObj)
client, err := v.vaultClientBuilder(ctx, resourceNamespace, v.createTokenFn, v.secretsLister, issuerObj)
if k8sErrors.IsNotFound(err) {
message := "Required secret resource not found"
v.reporter.Pending(cr, err, "SecretMissing", message)
log.Error(err, message)
return nil, nil
}
if err != nil {
message := "Failed to initialise vault client for signing"
v.reporter.Pending(cr, err, "VaultInitError", message)
log.Error(err, message)
if cmerrors.IsInvalidData(err) {
return nil, nil // Don't retry, wait for the issuer to be updated
}
return nil, err // Return error to requeue and retry
}
certDuration := apiutil.DefaultCertDuration(cr.Spec.Duration)
certPem, caPem, err := client.Sign(cr.Spec.Request, certDuration)
if err != nil {
message := "Vault failed to sign certificate"
v.reporter.Failed(cr, err, "SigningError", message)
log.Error(err, message)
return nil, nil
}
log.V(logf.DebugLevel).Info("certificate issued")
return &issuer.IssueResponse{
Certificate: certPem,
CA: caPem,
}, nil
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package venafi
import (
"context"
"encoding/json"
"fmt"
"github.com/Venafi/vcert/v5/pkg/endpoint"
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
internalinformers "github.com/cert-manager/cert-manager/internal/informers"
apiutil "github.com/cert-manager/cert-manager/pkg/api/util"
cmapi "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
clientset "github.com/cert-manager/cert-manager/pkg/client/clientset/versioned"
controllerpkg "github.com/cert-manager/cert-manager/pkg/controller"
"github.com/cert-manager/cert-manager/pkg/controller/certificaterequests"
crutil "github.com/cert-manager/cert-manager/pkg/controller/certificaterequests/util"
issuerpkg "github.com/cert-manager/cert-manager/pkg/issuer"
venaficlient "github.com/cert-manager/cert-manager/pkg/issuer/venafi/client"
"github.com/cert-manager/cert-manager/pkg/issuer/venafi/client/api"
logf "github.com/cert-manager/cert-manager/pkg/logs"
"github.com/cert-manager/cert-manager/pkg/metrics"
utilpki "github.com/cert-manager/cert-manager/pkg/util/pki"
)
const (
CRControllerName = "certificaterequests-issuer-venafi"
)
type Venafi struct {
issuerOptions controllerpkg.IssuerOptions
secretsLister internalinformers.SecretLister
reporter *crutil.Reporter
cmClient clientset.Interface
clientBuilder venaficlient.VenafiClientBuilder
metrics *metrics.Metrics
// userAgent is the string used as the UserAgent when making HTTP calls.
userAgent string
}
func init() {
// create certificate request controller for venafi issuer
controllerpkg.Register(CRControllerName, func(ctx *controllerpkg.ContextFactory) (controllerpkg.Interface, error) {
return controllerpkg.NewBuilder(ctx, CRControllerName).
For(certificaterequests.New(apiutil.IssuerVenafi, NewVenafi)).
Complete()
})
}
func NewVenafi(ctx *controllerpkg.Context) certificaterequests.Issuer {
return &Venafi{
issuerOptions: ctx.IssuerOptions,
secretsLister: ctx.KubeSharedInformerFactory.Secrets().Lister(),
reporter: crutil.NewReporter(ctx.Clock, ctx.Recorder),
clientBuilder: venaficlient.New,
metrics: ctx.Metrics,
cmClient: ctx.CMClient,
userAgent: ctx.RESTConfig.UserAgent,
}
}
func (v *Venafi) Sign(ctx context.Context, cr *cmapi.CertificateRequest, issuerObj cmapi.GenericIssuer) (*issuerpkg.IssueResponse, error) {
log := logf.FromContext(ctx, "sign")
log = logf.WithRelatedResource(log, issuerObj)
client, err := v.clientBuilder(v.issuerOptions.ResourceNamespace(issuerObj), v.secretsLister, issuerObj, v.metrics, log, v.userAgent)
if k8sErrors.IsNotFound(err) {
message := "Required secret resource not found"
v.reporter.Pending(cr, err, "SecretMissing", message)
log.Error(err, message)
return nil, nil
}
if err != nil {
message := "Failed to initialise venafi client for signing"
v.reporter.Pending(cr, err, "VenafiInitError", message)
log.Error(err, message)
return nil, err
}
var customFields []api.CustomField
if annotation, exists := cr.GetAnnotations()[cmapi.VenafiCustomFieldsAnnotationKey]; exists && annotation != "" {
err := json.Unmarshal([]byte(annotation), &customFields)
if err != nil {
message := fmt.Sprintf("Failed to parse %q annotation", cmapi.VenafiCustomFieldsAnnotationKey)
v.reporter.Failed(cr, err, "CustomFieldsError", message)
log.Error(err, message)
return nil, nil
}
}
duration := apiutil.DefaultCertDuration(cr.Spec.Duration)
pickupID := cr.ObjectMeta.Annotations[cmapi.VenafiPickupIDAnnotationKey]
// check if the pickup ID annotation is there, if not set it up.
if pickupID == "" {
pickupID, err = client.RequestCertificate(cr.Spec.Request, duration, customFields)
// Check some known error types
if err != nil {
switch err.(type) {
case venaficlient.ErrCustomFieldsType:
v.reporter.Failed(cr, err, "CustomFieldsError", err.Error())
log.Error(err, err.Error())
return nil, nil
default:
message := "Failed to request venafi certificate"
v.reporter.Failed(cr, err, "RequestError", message)
log.Error(err, message)
return nil, err
}
}
v.reporter.Pending(cr, err, "IssuancePending", "Venafi certificate is requested")
metav1.SetMetaDataAnnotation(&cr.ObjectMeta, cmapi.VenafiPickupIDAnnotationKey, pickupID)
return nil, nil
}
certPem, err := client.RetrieveCertificate(pickupID, cr.Spec.Request, duration, customFields)
if err != nil {
switch err.(type) {
case endpoint.ErrCertificatePending, endpoint.ErrRetrieveCertificateTimeout:
message := "Venafi certificate still in a pending state, the request will be retried"
v.reporter.Pending(cr, err, "IssuancePending", message)
log.Error(err, message)
return nil, err
default:
message := "Failed to obtain venafi certificate"
v.reporter.Failed(cr, err, "RetrieveError", message)
log.Error(err, message)
return nil, err
}
}
log.V(logf.DebugLevel).Info("certificate issued")
bundle, err := utilpki.ParseSingleCertificateChainPEM(certPem)
if err != nil {
message := "Failed to parse returned certificate bundle"
v.reporter.Failed(cr, err, "ParseError", message)
log.Error(err, message)
return nil, err
}
return &issuerpkg.IssueResponse{
Certificate: bundle.ChainPEM,
CA: bundle.CAPEM,
}, nil
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package issuing
import (
"context"
"crypto"
"fmt"
"time"
"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/record"
"k8s.io/client-go/util/workqueue"
"k8s.io/utils/clock"
internalcertificates "github.com/cert-manager/cert-manager/internal/controller/certificates"
"github.com/cert-manager/cert-manager/internal/controller/certificates/policies"
"github.com/cert-manager/cert-manager/internal/controller/feature"
internalinformers "github.com/cert-manager/cert-manager/internal/informers"
apiutil "github.com/cert-manager/cert-manager/pkg/api/util"
cmapi "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
cmclient "github.com/cert-manager/cert-manager/pkg/client/clientset/versioned"
cmlisters "github.com/cert-manager/cert-manager/pkg/client/listers/certmanager/v1"
controllerpkg "github.com/cert-manager/cert-manager/pkg/controller"
"github.com/cert-manager/cert-manager/pkg/controller/certificates"
"github.com/cert-manager/cert-manager/pkg/controller/certificates/issuing/internal"
logf "github.com/cert-manager/cert-manager/pkg/logs"
utilfeature "github.com/cert-manager/cert-manager/pkg/util/feature"
utilkube "github.com/cert-manager/cert-manager/pkg/util/kube"
utilpki "github.com/cert-manager/cert-manager/pkg/util/pki"
"github.com/cert-manager/cert-manager/pkg/util/predicate"
)
const (
ControllerName = "certificates-issuing"
)
type localTemporarySignerFn func(crt *cmapi.Certificate, pk []byte) ([]byte, error)
// This controller observes the state of the certificate's 'Issuing' condition,
// which will then copy the signed certificates and private key to the target
// Secret resource.
type controller struct {
certificateLister cmlisters.CertificateLister
certificateRequestLister cmlisters.CertificateRequestLister
secretLister internalinformers.SecretLister
recorder record.EventRecorder
clock clock.Clock
client cmclient.Interface
// secretsUpdateData is used by the SecretTemplate controller for
// re-reconciling Secrets where the SecretTemplate is not up to date with a
// Certificate's secret.
secretsUpdateData func(context.Context, *cmapi.Certificate, internal.SecretData) error
// postIssuancePolicyChain is the policies chain to ensure that all Secret
// metadata and output formats are kept are present and correct.
postIssuancePolicyChain policies.Chain
// fieldManager is the string which will be used as the Field Manager on
// fields created or edited by the cert-manager Kubernetes client during
// Apply API calls.
fieldManager string
// localTemporarySigner signs a certificate that is stored temporarily
localTemporarySigner localTemporarySignerFn
}
func NewController(
log logr.Logger,
ctx *controllerpkg.Context,
) (*controller, workqueue.TypedRateLimitingInterface[types.NamespacedName], []cache.InformerSynced, error) {
// create a queue used to queue up items to be processed
queue := workqueue.NewTypedRateLimitingQueueWithConfig(
controllerpkg.DefaultCertificateRateLimiter(),
workqueue.TypedRateLimitingQueueConfig[types.NamespacedName]{
Name: ControllerName,
},
)
// obtain references to all the informers used by this controller
certificateInformer := ctx.SharedInformerFactory.Certmanager().V1().Certificates()
certificateRequestInformer := ctx.SharedInformerFactory.Certmanager().V1().CertificateRequests()
secretsInformer := ctx.KubeSharedInformerFactory.Secrets()
if _, err := certificateInformer.Informer().AddEventHandler(&controllerpkg.QueuingEventHandler{Queue: queue}); err != nil {
return nil, nil, nil, fmt.Errorf("error setting up event handler: %v", err)
}
if _, err := certificateRequestInformer.Informer().AddEventHandler(&controllerpkg.BlockingEventHandler{
WorkFunc: certificates.EnqueueCertificatesForResourceUsingPredicates(log, queue, certificateInformer.Lister(), labels.Everything(), predicate.ResourceOwnerOf),
}); err != nil {
return nil, nil, nil, fmt.Errorf("error setting up event handler: %v", err)
}
if _, err := secretsInformer.Informer().AddEventHandler(&controllerpkg.BlockingEventHandler{
// Issuer reconciles on changes to the Secret named `spec.nextPrivateKeySecretName`
WorkFunc: certificates.EnqueueCertificatesForResourceUsingPredicates(log, queue, certificateInformer.Lister(), labels.Everything(),
predicate.ResourceOwnerOf,
predicate.ExtractResourceName(predicate.CertificateNextPrivateKeySecretName)),
}); err != nil {
return nil, nil, nil, fmt.Errorf("error setting up event handler: %v", err)
}
if _, err := secretsInformer.Informer().AddEventHandler(&controllerpkg.BlockingEventHandler{
// Issuer reconciles on changes to the Secret named `spec.secretName`
WorkFunc: certificates.EnqueueCertificatesForResourceUsingPredicates(log, queue, certificateInformer.Lister(), labels.Everything(),
predicate.ExtractResourceName(predicate.CertificateSecretName)),
}); err != nil {
return nil, nil, nil, fmt.Errorf("error setting up event handler: %v", err)
}
// build a list of InformerSynced functions that will be returned by the Register method.
// the controller will only begin processing items once all of these informers have synced.
mustSync := []cache.InformerSynced{
certificateRequestInformer.Informer().HasSynced,
secretsInformer.Informer().HasSynced,
certificateInformer.Informer().HasSynced,
}
secretsManager := internal.NewSecretsManager(
ctx.Client.CoreV1(), secretsInformer.Lister(),
ctx.FieldManager, ctx.CertificateOptions.EnableOwnerRef,
)
return &controller{
certificateLister: certificateInformer.Lister(),
certificateRequestLister: certificateRequestInformer.Lister(),
secretLister: secretsInformer.Lister(),
client: ctx.CMClient,
recorder: ctx.Recorder,
clock: ctx.Clock,
secretsUpdateData: secretsManager.UpdateData,
postIssuancePolicyChain: policies.NewSecretPostIssuancePolicyChain(
ctx.CertificateOptions.EnableOwnerRef,
ctx.FieldManager,
),
fieldManager: ctx.FieldManager,
localTemporarySigner: utilpki.GenerateLocallySignedTemporaryCertificate,
}, queue, mustSync, nil
}
func (c *controller) ProcessItem(ctx context.Context, key types.NamespacedName) error {
// TODO: Change to globals.DefaultControllerContextTimeout as part of a wider effort to ensure we have
// failsafe timeouts in every controller
ctx, cancel := context.WithTimeout(ctx, time.Second*10)
defer cancel()
log := logf.FromContext(ctx).WithValues("key", key)
namespace, name := key.Namespace, key.Name
crt, err := c.certificateLister.Certificates(namespace).Get(name)
if err != nil && !apierrors.IsNotFound(err) {
return err
}
if crt == nil || crt.DeletionTimestamp != nil {
// If the Certificate object was/ is being deleted, we don't want to update its status or
// create/ update any Secret resources.
return nil
}
log = logf.WithResource(log, crt)
ctx = logf.NewContext(ctx, log)
if !apiutil.CertificateHasCondition(crt, cmapi.CertificateCondition{
Type: cmapi.CertificateConditionIssuing,
Status: cmmeta.ConditionTrue,
}) {
// If Certificate doesn't have Issuing=true condition then we should check
// to ensure all non-issuing related SecretData is correct on the
// Certificate's secret.
return c.ensureSecretData(ctx, log, crt)
}
if crt.Status.NextPrivateKeySecretName == nil ||
len(*crt.Status.NextPrivateKeySecretName) == 0 {
// Do nothing if the next private key secret name is not set
return nil
}
// Fetch and parse the 'next private key secret'
nextPrivateKeySecret, err := c.secretLister.Secrets(crt.Namespace).Get(*crt.Status.NextPrivateKeySecretName)
if apierrors.IsNotFound(err) {
log.V(logf.DebugLevel).Info("Next private key secret does not exist, waiting for keymanager controller")
// If secret does not exist, do nothing (keymanager will handle this).
return nil
}
if err != nil {
return err
}
if nextPrivateKeySecret.Data == nil || len(nextPrivateKeySecret.Data[corev1.TLSPrivateKeyKey]) == 0 {
logf.WithResource(log, nextPrivateKeySecret).Info("Next private key secret does not contain any private key data, waiting for keymanager controller")
return nil
}
pk, _, err := utilkube.ParseTLSKeyFromSecret(nextPrivateKeySecret, corev1.TLSPrivateKeyKey)
if err != nil {
// If the private key cannot be parsed here, do nothing as the key manager will handle this.
logf.WithResource(log, nextPrivateKeySecret).Error(err, "failed to parse next private key, waiting for keymanager controller")
return nil
}
pkViolations := utilpki.PrivateKeyMatchesSpec(pk, crt.Spec)
if len(pkViolations) > 0 {
logf.WithResource(log, nextPrivateKeySecret).Info("stored next private key does not match requirements on Certificate resource, waiting for keymanager controller", "violations", pkViolations)
return nil
}
// CertificateRequest revisions begin from 1. If no revision is set on the
// status then assume no revision yet set.
nextRevision := 1
if crt.Status.Revision != nil {
nextRevision = *crt.Status.Revision + 1
}
reqs, err := certificates.ListCertificateRequestsMatchingPredicates(c.certificateRequestLister.CertificateRequests(crt.Namespace),
labels.Everything(),
predicate.CertificateRequestRevision(nextRevision),
predicate.ResourceOwnedBy(crt),
)
if err != nil || len(reqs) != 1 {
// If error return.
// if no error but none exist do nothing.
// If no error but multiple exist, then leave to requestmanager controller
// to clean up.
return err
}
req := reqs[0]
log = logf.WithResource(log, req)
// Verify the CSR options match what is requested in certificate.spec.
// If there are violations in the spec, then the requestmanager will handle this.
requestViolations, err := utilpki.RequestMatchesSpec(req, crt.Spec)
if err != nil {
return err
}
if len(requestViolations) > 0 {
log.V(logf.DebugLevel).Info("CertificateRequest does not match Certificate, waiting for keymanager controller")
return nil
}
certIssuingCond := apiutil.GetCertificateCondition(crt, cmapi.CertificateConditionIssuing)
crReadyCond := apiutil.GetCertificateRequestCondition(req, cmapi.CertificateRequestConditionReady)
if certIssuingCond == nil {
// This should never happen
log.V(logf.ErrorLevel).Info("Certificate does not have an issuing condition")
return nil
}
// If the CertificateRequest for this revision failed before the
// Issuing condition was last updated on the Certificate, then it must be a
// failed CertificateRequest from the previous issuance for the same
// revision. Leave it to the certificate-requests controller to delete the
// CertificateRequest and create a new one.
if req.Status.FailureTime != nil &&
req.Status.FailureTime.Before(certIssuingCond.LastTransitionTime) && crReadyCond.Reason == cmapi.CertificateRequestReasonFailed {
log.V(logf.InfoLevel).Info("Found a failed CertificateRequest from previous issuance, waiting for it to be deleted...")
return nil
}
// Now check if CertificateRequest is in any of the final states so that
// this issuance can be completed as either succeeded or failed. Failed
// issuance will be retried with a delay (the logic for that lives in
// certificates-trigger controller). Final states are: Denied condition
// with status True => fail issuance InvalidRequest condition with
// status True => fail issuance Ready condition with reason Failed =>
// fail issuance Ready condition with reason Issued => finalize issuance
// as succeeded.
// In case of a non-compliant issuer, a CertificateRequest can have both
// Denied status True (set by an approver) and Ready condition with
// reason Issued (set by the issuer). In this case, we prioritize the
// Denied condition and fail the issuance. This is done for consistency
// and also to avoid race conditions between the non-compliant issuer
// and this control loop.
// If the certificate request was denied, set the last failure time to
// now, bump the issuance attempts and set the Issuing status condition
// to False.
if apiutil.CertificateRequestIsDenied(req) {
return c.failIssueCertificate(ctx, log, crt, apiutil.GetCertificateRequestCondition(req, cmapi.CertificateRequestConditionDenied))
}
// If the certificate request is invalid, set the last failure time to
// now, bump the issuance attempts and set the Issuing status condition
// to False.
if apiutil.CertificateRequestHasInvalidRequest(req) {
return c.failIssueCertificate(ctx, log, crt, apiutil.GetCertificateRequestCondition(req, cmapi.CertificateRequestConditionInvalidRequest))
}
if crReadyCond == nil {
log.V(logf.DebugLevel).Info("CertificateRequest does not have Ready condition, waiting...")
return nil
}
// If the certificate request has failed, set the last failure time to
// now, bump the issuance attempts and set the Issuing status condition
// to False.
if crReadyCond.Reason == cmapi.CertificateRequestReasonFailed {
return c.failIssueCertificate(ctx, log, crt, apiutil.GetCertificateRequestCondition(req, cmapi.CertificateRequestConditionReady))
}
// If public key does not match, do nothing (requestmanager will handle this).
csr, err := utilpki.DecodeX509CertificateRequestBytes(req.Spec.Request)
if err != nil {
return err
}
publicKeyMatchesCSR, err := utilpki.PublicKeyMatchesCSR(pk.Public(), csr)
if err != nil {
return err
}
if !publicKeyMatchesCSR {
logf.WithResource(log, nextPrivateKeySecret).Info("next private key does not match CSR public key, waiting for requestmanager controller")
return nil
}
// If the CertificateRequest is valid and ready, verify its status and issue
// accordingly.
if crReadyCond.Reason == cmapi.CertificateRequestReasonIssued {
return c.issueCertificate(ctx, nextRevision, crt, req, pk)
}
// Issue temporary certificate if needed. If a certificate was issued, then
// return early - we will sync again since the target Secret has been
// updated.
if issued, err := c.ensureTemporaryCertificate(ctx, crt, pk); err != nil || issued {
return err
}
// CertificateRequest is not in a final state so do nothing.
log.V(logf.DebugLevel).Info("CertificateRequest not in final state, waiting...", "reason", crReadyCond.Reason)
return nil
}
// failIssueCertificate will mark the Issuing condition of this Certificate as
// false, set the Certificate's last failure time and issuance attempts, and log
// an appropriate event. The reason and message of the Issuing condition will be that of
// the CertificateRequest condition passed.
func (c *controller) failIssueCertificate(ctx context.Context, log logr.Logger, crt *cmapi.Certificate, condition *cmapi.CertificateRequestCondition) error {
nowTime := metav1.NewTime(c.clock.Now())
crt.Status.LastFailureTime = &nowTime
failedIssuanceAttempts := 1
if crt.Status.FailedIssuanceAttempts != nil {
failedIssuanceAttempts = *crt.Status.FailedIssuanceAttempts + 1
}
crt.Status.FailedIssuanceAttempts = &failedIssuanceAttempts
log.V(logf.DebugLevel).Info("CertificateRequest in failed state so retrying issuance later")
var reason, message string
reason = condition.Reason
message = fmt.Sprintf("The certificate request has failed to complete and will be retried: %s",
condition.Message)
crt = crt.DeepCopy()
apiutil.SetCertificateCondition(crt, crt.Generation, cmapi.CertificateConditionIssuing, cmmeta.ConditionFalse, reason, message)
if err := c.updateOrApplyStatus(ctx, crt, false); err != nil {
return err
}
c.recorder.Event(crt, corev1.EventTypeWarning, reason, message)
return nil
}
// issueCertificate will ensure the public key of the CSR matches the signed
// certificate, and then store the certificate, CA and private key into the
// Secret in the appropriate format type.
func (c *controller) issueCertificate(ctx context.Context, nextRevision int, crt *cmapi.Certificate, req *cmapi.CertificateRequest, pk crypto.Signer) error {
crt = crt.DeepCopy()
if crt.Spec.PrivateKey == nil {
crt.Spec.PrivateKey = &cmapi.CertificatePrivateKey{}
}
pkData, err := utilpki.EncodePrivateKey(pk, crt.Spec.PrivateKey.Encoding)
if err != nil {
return err
}
secretData := internal.SecretData{
PrivateKey: pkData,
Certificate: req.Status.Certificate,
CA: req.Status.CA,
CertificateName: crt.Name,
IssuerName: req.Spec.IssuerRef.Name,
IssuerKind: req.Spec.IssuerRef.Kind,
IssuerGroup: req.Spec.IssuerRef.Group,
}
if err := c.secretsUpdateData(ctx, crt, secretData); err != nil {
return err
}
// Set status.revision to revision of the CertificateRequest
crt.Status.Revision = &nextRevision
// Remove Issuing status condition
// TODO @joshvanl: Once we move to only server-side apply API calls, this
// should be changed to setting the Issuing condition to False.
apiutil.RemoveCertificateCondition(crt, cmapi.CertificateConditionIssuing)
// Clear status.failedIssuanceAttempts (if set)
crt.Status.FailedIssuanceAttempts = nil
// Clear status.lastFailureTime (if set)
crt.Status.LastFailureTime = nil
if err := c.updateOrApplyStatus(ctx, crt, true); err != nil {
return err
}
message := "The certificate has been successfully issued"
c.recorder.Event(crt, corev1.EventTypeNormal, "Issuing", message)
return nil
}
// updateOrApplyStatus will update the controller status. If the
// ServerSideApply feature is enabled, the managed fields will instead get
// applied using the relevant Patch API call.
// conditionRemove should be true if the Issuing condition has been removed by
// this controller. If the ServerSideApply feature is enabled and condition
// have been removed, the Issuing condition will be set to False before
// applying.
func (c *controller) updateOrApplyStatus(ctx context.Context, crt *cmapi.Certificate, conditionRemoved bool) error {
if utilfeature.DefaultFeatureGate.Enabled(feature.ServerSideApply) {
// TODO @joshvanl: Once we move to only server-side apply API calls,
// `conditionRemoved` can be removed and setting the Issuing condition to
// False can be moved to the `issueCertificate` func.
if conditionRemoved {
message := "The certificate has been successfully issued"
apiutil.SetCertificateCondition(crt, crt.Generation, cmapi.CertificateConditionIssuing, cmmeta.ConditionFalse, "Issued", message)
}
var conditions []cmapi.CertificateCondition
if cond := apiutil.GetCertificateCondition(crt, cmapi.CertificateConditionIssuing); cond != nil {
conditions = []cmapi.CertificateCondition{*cond}
}
return internalcertificates.ApplyStatus(ctx, c.client, c.fieldManager, &cmapi.Certificate{
ObjectMeta: metav1.ObjectMeta{Namespace: crt.Namespace, Name: crt.Name},
Status: cmapi.CertificateStatus{
Revision: crt.Status.Revision,
LastFailureTime: crt.Status.LastFailureTime,
Conditions: conditions,
},
})
} else {
_, err := c.client.CertmanagerV1().Certificates(crt.Namespace).UpdateStatus(ctx, crt, metav1.UpdateOptions{})
return err
}
}
// controllerWrapper wraps the `controller` structure to make it implement
// the controllerpkg.queueingController interface
type controllerWrapper struct {
*controller
}
func (c *controllerWrapper) Register(ctx *controllerpkg.Context) (workqueue.TypedRateLimitingInterface[types.NamespacedName], []cache.InformerSynced, error) {
// construct a new named logger to be reused throughout the controller
log := logf.FromContext(ctx.RootContext, ControllerName)
ctrl, queue, mustSync, err := NewController(log, ctx)
c.controller = ctrl
return queue, mustSync, err
}
func init() {
controllerpkg.Register(ControllerName, func(ctx *controllerpkg.ContextFactory) (controllerpkg.Interface, error) {
return controllerpkg.NewBuilder(ctx, ControllerName).
For(&controllerWrapper{}).
Complete()
})
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package issuing
import (
"context"
"errors"
"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"github.com/cert-manager/cert-manager/internal/controller/certificates/policies"
cmapi "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
"github.com/cert-manager/cert-manager/pkg/controller/certificates/issuing/internal"
logf "github.com/cert-manager/cert-manager/pkg/logs"
)
// ensureSecretData ensures that the Certificate's Secret is up to date with
// non-issuing condition related data.
// Reconciles over the Certificate's SecretTemplate, and
// AdditionalOutputFormats.
func (c *controller) ensureSecretData(ctx context.Context, log logr.Logger, crt *cmapi.Certificate) error {
// Retrieve the Secret which is associated with this Certificate.
secret, err := c.secretLister.Secrets(crt.Namespace).Get(crt.Spec.SecretName)
// Secret doesn't exist so we can't do anything. The Certificate will be
// marked for a re-issuance and the resulting Secret will be evaluated again.
if apierrors.IsNotFound(err) {
log.V(logf.DebugLevel).Info("secret not found", "error", err.Error())
return nil
}
// This error is transient, return error to be retried on the rate limiting
// queue.
if err != nil {
return err
}
log = log.WithValues("secret", secret.Name)
// If there is no certificate or private key data available at the target
// Secret then exit early. The absence of these keys should cause an issuance
// of the Certificate, so there is no need to run post issuance checks.
if secret.Data == nil ||
len(secret.Data[corev1.TLSCertKey]) == 0 ||
len(secret.Data[corev1.TLSPrivateKeyKey]) == 0 {
log.V(logf.DebugLevel).Info("secret doesn't contain both certificate and private key data",
"cert_data_len", len(secret.Data[corev1.TLSCertKey]), "key_data_len", len(secret.Data[corev1.TLSPrivateKeyKey]))
return nil
}
data := internal.SecretData{
PrivateKey: secret.Data[corev1.TLSPrivateKeyKey],
Certificate: secret.Data[corev1.TLSCertKey],
CA: secret.Data[cmmeta.TLSCAKey],
CertificateName: secret.Annotations[cmapi.CertificateNameKey],
IssuerName: secret.Annotations[cmapi.IssuerNameAnnotationKey],
IssuerKind: secret.Annotations[cmapi.IssuerKindAnnotationKey],
IssuerGroup: secret.Annotations[cmapi.IssuerGroupAnnotationKey],
}
// Check whether the Certificate's Secret has correct output format and
// metadata.
reason, message, isViolation := c.postIssuancePolicyChain.Evaluate(policies.Input{
Certificate: crt,
Secret: secret,
})
if isViolation {
switch reason {
case policies.InvalidCertificate, policies.ManagedFieldsParseError:
// An error here indicates that the managed fields are malformed and the
// decoder doesn't understand the managed fields on the Secret, or the
// signed certificate data could not be decoded. There is nothing more the
// controller can do here, so we exit nil so this controller doesn't end in
// an infinite loop.
log.Error(errors.New(message), "failed to determine whether the SecretTemplate matches Secret")
return nil
default:
// Here the Certificate need to be re-reconciled.
log.Info("applying Secret data", "message", message)
return c.secretsUpdateData(ctx, crt, data)
}
}
// No Secret violations, nothing to do.
return nil
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package issuing
import (
"context"
"crypto"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"github.com/cert-manager/cert-manager/internal/controller/certificates/policies"
cmapi "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
"github.com/cert-manager/cert-manager/pkg/controller/certificates/issuing/internal"
utilpki "github.com/cert-manager/cert-manager/pkg/util/pki"
)
// ensureTemporaryCertificate will create a temporary certificate and store it
// into the target Secret if:
// - The temporary certificate annotation is present
// - The target Secret does not exist yet, or the certificate/key data there is not valid
// - If the Certificate/Key pair does not match the 'NextPrivateKey'
// Returns true is a temporary certificate was issued
func (c *controller) ensureTemporaryCertificate(ctx context.Context, crt *cmapi.Certificate, pk crypto.Signer) (bool, error) {
crt = crt.DeepCopy()
if crt.Spec.PrivateKey == nil {
crt.Spec.PrivateKey = &cmapi.CertificatePrivateKey{}
}
// If certificate does not have temporary certificate annotation, do nothing
if !certificateHasTemporaryCertificateAnnotation(crt) {
return false, nil
}
// Attempt to fetch the Secret being managed but tolerate NotFound errors.
secret, err := c.secretLister.Secrets(crt.Namespace).Get(crt.Spec.SecretName)
if err != nil && !apierrors.IsNotFound(err) {
return false, err
}
input := policies.Input{Secret: secret}
// If the target Secret exists with a signed certificate and matching private
// key, do not issue.
if _, _, invalid := policies.NewTemporaryCertificatePolicyChain().Evaluate(input); !invalid {
return false, nil
}
// Issue temporary certificate
pkData, err := utilpki.EncodePrivateKey(pk, crt.Spec.PrivateKey.Encoding)
if err != nil {
return false, err
}
certData, err := c.localTemporarySigner(crt, pkData)
if err != nil {
return false, err
}
secretData := internal.SecretData{
Certificate: certData,
PrivateKey: pkData,
CertificateName: crt.Name,
}
if err := c.secretsUpdateData(ctx, crt, secretData); err != nil {
return false, err
}
c.recorder.Event(crt, corev1.EventTypeNormal, "Issuing", "Issued temporary certificate")
return true, nil
}
func certificateHasTemporaryCertificateAnnotation(crt *cmapi.Certificate) bool {
if crt.Annotations == nil {
return false
}
if val, ok := crt.Annotations[cmapi.IssueTemporaryCertificateAnnotation]; ok && val == "true" {
return true
}
return false
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package keymanager
import (
"context"
"crypto"
"fmt"
"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/selection"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/record"
"k8s.io/client-go/util/workqueue"
cminternal "github.com/cert-manager/cert-manager/internal/apis/certmanager/v1"
internalcertificates "github.com/cert-manager/cert-manager/internal/controller/certificates"
"github.com/cert-manager/cert-manager/internal/controller/feature"
internalinformers "github.com/cert-manager/cert-manager/internal/informers"
apiutil "github.com/cert-manager/cert-manager/pkg/api/util"
cmapi "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
cmclient "github.com/cert-manager/cert-manager/pkg/client/clientset/versioned"
cmlisters "github.com/cert-manager/cert-manager/pkg/client/listers/certmanager/v1"
controllerpkg "github.com/cert-manager/cert-manager/pkg/controller"
"github.com/cert-manager/cert-manager/pkg/controller/certificates"
logf "github.com/cert-manager/cert-manager/pkg/logs"
utilfeature "github.com/cert-manager/cert-manager/pkg/util/feature"
"github.com/cert-manager/cert-manager/pkg/util/pki"
"github.com/cert-manager/cert-manager/pkg/util/predicate"
)
const (
ControllerName = "certificates-key-manager"
reasonDecodeFailed = "DecodeFailed"
reasonCannotRegenerateKey = "CannotRegenerateKey"
reasonDeleted = "Deleted"
)
var (
certificateGvk = cmapi.SchemeGroupVersion.WithKind("Certificate")
)
type controller struct {
certificateLister cmlisters.CertificateLister
secretLister internalinformers.SecretLister
client cmclient.Interface
coreClient kubernetes.Interface
recorder record.EventRecorder
// fieldManager is the string which will be used as the Field Manager on
// fields created or edited by the cert-manager Kubernetes client during
// Apply API calls.
fieldManager string
}
func NewController(
log logr.Logger, ctx *controllerpkg.Context,
) (*controller, workqueue.TypedRateLimitingInterface[types.NamespacedName], []cache.InformerSynced, error) {
// create a queue used to queue up items to be processed
queue := workqueue.NewTypedRateLimitingQueueWithConfig(
controllerpkg.DefaultCertificateRateLimiter(),
workqueue.TypedRateLimitingQueueConfig[types.NamespacedName]{
Name: ControllerName,
},
)
// obtain references to all the informers used by this controller
certificateInformer := ctx.SharedInformerFactory.Certmanager().V1().Certificates()
secretsInformer := ctx.KubeSharedInformerFactory.Secrets()
if _, err := certificateInformer.Informer().AddEventHandler(&controllerpkg.QueuingEventHandler{Queue: queue}); err != nil {
return nil, nil, nil, fmt.Errorf("error setting up event handler: %v", err)
}
if _, err := secretsInformer.Informer().AddEventHandler(&controllerpkg.BlockingEventHandler{
// Trigger reconciles on changes to any 'owned' secret resources
WorkFunc: certificates.EnqueueCertificatesForResourceUsingPredicates(log, queue, certificateInformer.Lister(), labels.Everything(),
predicate.ResourceOwnerOf,
),
}); err != nil {
return nil, nil, nil, fmt.Errorf("error setting up event handler: %v", err)
}
if _, err := secretsInformer.Informer().AddEventHandler(&controllerpkg.BlockingEventHandler{
// Trigger reconciles on changes to certificates named as spec.secretName
WorkFunc: certificates.EnqueueCertificatesForResourceUsingPredicates(log, queue, certificateInformer.Lister(), labels.Everything(),
predicate.ExtractResourceName(predicate.CertificateSecretName),
),
}); err != nil {
return nil, nil, nil, fmt.Errorf("error setting up event handler: %v", err)
}
// build a list of InformerSynced functions that will be returned by the Register method.
// the controller will only begin processing items once all of these informers have synced.
mustSync := []cache.InformerSynced{
secretsInformer.Informer().HasSynced,
certificateInformer.Informer().HasSynced,
}
return &controller{
certificateLister: certificateInformer.Lister(),
secretLister: secretsInformer.Lister(),
client: ctx.CMClient,
coreClient: ctx.Client,
recorder: ctx.Recorder,
fieldManager: ctx.FieldManager,
}, queue, mustSync, nil
}
// isNextPrivateKeyLabelSelector is a label selector used to match Secret
// resources with the `cert-manager.io/next-private-key: "true"` label.
var isNextPrivateKeyLabelSelector labels.Selector
func init() {
r, err := labels.NewRequirement(cmapi.IsNextPrivateKeySecretLabelKey, selection.Equals, []string{"true"})
if err != nil {
panic(err)
}
isNextPrivateKeyLabelSelector = labels.NewSelector().Add(*r)
}
func (c *controller) ProcessItem(ctx context.Context, key types.NamespacedName) error {
log := logf.FromContext(ctx).WithValues("key", key)
ctx = logf.NewContext(ctx, log)
namespace, name := key.Namespace, key.Name
crt, err := c.certificateLister.Certificates(namespace).Get(name)
if err != nil && !apierrors.IsNotFound(err) {
return err
}
if crt == nil || crt.DeletionTimestamp != nil {
// If the Certificate object was/ is being deleted, we don't want to create any
// new Secret resources.
return nil
}
// Apply runtime defaults to apply default values that are governed by
// controller feature gates, such as DefaultPrivateKeyRotationPolicyAlways.
// We deep copy the object to avoid mutating the client-go cache.
crt = crt.DeepCopy()
cminternal.SetRuntimeDefaults_Certificate(crt)
// Discover all 'owned' secrets that have the `next-private-key` label
secrets, err := certificates.ListSecretsMatchingPredicates(c.secretLister.Secrets(crt.Namespace), isNextPrivateKeyLabelSelector, predicate.ResourceOwnedBy(crt))
if err != nil {
return err
}
if !apiutil.CertificateHasCondition(crt, cmapi.CertificateCondition{
Type: cmapi.CertificateConditionIssuing,
Status: cmmeta.ConditionTrue,
}) {
log.V(logf.DebugLevel).Info("Cleaning up Secret resources and unsetting nextPrivateKeySecretName as issuance is no longer in progress")
if err := c.deleteSecretResources(ctx, secrets); err != nil {
return err
}
return c.setNextPrivateKeySecretName(ctx, crt, nil)
}
// if there is no existing Secret resource, create a new one
if len(secrets) == 0 {
// PrivateKey is a pointer, but it will never be nil because we called
// the SetRuntimeDefaults function at the start of this function.
rotationPolicy := crt.Spec.PrivateKey.RotationPolicy
switch rotationPolicy {
case cmapi.RotationPolicyNever:
return c.createNextPrivateKeyRotationPolicyNever(ctx, crt)
case cmapi.RotationPolicyAlways:
log.V(logf.DebugLevel).Info("Creating new nextPrivateKeySecretName Secret because no existing Secret found")
return c.createAndSetNextPrivateKey(ctx, crt)
default:
log.V(logf.WarnLevel).Info("Certificate with unknown certificate.spec.privateKey.rotationPolicy value", "rotation_policy", rotationPolicy)
return nil
}
}
// always clean up if multiple are found
if len(secrets) > 1 {
// TODO: if nextPrivateKeySecretName is set, we should skip deleting that one Secret resource
log.V(logf.DebugLevel).Info("Cleaning up Secret resources as multiple nextPrivateKeySecretName candidates found")
return c.deleteSecretResources(ctx, secrets)
}
secret := secrets[0]
log = logf.WithRelatedResource(log, secret)
ctx = logf.NewContext(ctx, log)
if crt.Status.NextPrivateKeySecretName == nil {
log.V(logf.DebugLevel).Info("Adopting existing private key Secret")
return c.setNextPrivateKeySecretName(ctx, crt, &secret.Name)
}
if *crt.Status.NextPrivateKeySecretName != secrets[0].Name {
log.V(logf.DebugLevel).Info("Deleting existing private key secret as name does not match status.nextPrivateKeySecretName")
return c.deleteSecretResources(ctx, secrets)
}
if secret.Data == nil || len(secret.Data[corev1.TLSPrivateKeyKey]) == 0 {
log.V(logf.DebugLevel).Info("Deleting Secret resource as it contains no data")
return c.deleteSecretResources(ctx, secrets)
}
pkData := secret.Data[corev1.TLSPrivateKeyKey]
pk, err := pki.DecodePrivateKeyBytes(pkData)
if err != nil {
log.Error(err, "Deleting existing private key secret due to error decoding data")
return c.deleteSecretResources(ctx, secrets)
}
violations := pki.PrivateKeyMatchesSpec(pk, crt.Spec)
if len(violations) > 0 {
log.V(logf.DebugLevel).Info("Regenerating private key due to change in fields", "violations", violations)
c.recorder.Eventf(crt, corev1.EventTypeNormal, reasonDeleted, "Regenerating private key due to change in fields: %v", violations)
return c.deleteSecretResources(ctx, secrets)
}
return nil
}
func (c *controller) createNextPrivateKeyRotationPolicyNever(ctx context.Context, crt *cmapi.Certificate) error {
log := logf.FromContext(ctx)
s, err := c.secretLister.Secrets(crt.Namespace).Get(crt.Spec.SecretName)
if apierrors.IsNotFound(err) {
log.V(logf.DebugLevel).Info("Creating new nextPrivateKeySecretName Secret because no existing Secret found and rotation policy is Never")
return c.createAndSetNextPrivateKey(ctx, crt)
}
if err != nil {
return err
}
if s.Data == nil || len(s.Data[corev1.TLSPrivateKeyKey]) == 0 {
log.V(logf.DebugLevel).Info("Creating new nextPrivateKeySecretName Secret because existing Secret contains empty data and rotation policy is Never")
return c.createAndSetNextPrivateKey(ctx, crt)
}
existingPKData := s.Data[corev1.TLSPrivateKeyKey]
pk, err := pki.DecodePrivateKeyBytes(existingPKData)
if err != nil {
c.recorder.Eventf(crt, corev1.EventTypeWarning, reasonDecodeFailed, "Failed to decode private key stored in Secret %q - generating new key", crt.Spec.SecretName)
return c.createAndSetNextPrivateKey(ctx, crt)
}
violations := pki.PrivateKeyMatchesSpec(pk, crt.Spec)
if len(violations) > 0 {
c.recorder.Eventf(crt, corev1.EventTypeWarning, reasonCannotRegenerateKey, "User intervention required: existing private key in Secret %q does not match requirements on Certificate resource, mismatching fields: %v, but cert-manager cannot create new private key as the Certificate's .spec.privateKey.rotationPolicy is unset or set to Never. To allow cert-manager to create a new private key you can set .spec.privateKey.rotationPolicy to 'Always' (this will result in the private key being regenerated every time a cert is renewed) ", crt.Spec.SecretName, violations)
return nil
}
nextPkSecret, err := c.createNewPrivateKeySecret(ctx, crt, pk)
if err != nil {
return err
}
c.recorder.Event(crt, corev1.EventTypeNormal, "Reused", fmt.Sprintf("Reusing private key stored in existing Secret resource %q", s.Name))
return c.setNextPrivateKeySecretName(ctx, crt, &nextPkSecret.Name)
}
func (c *controller) createAndSetNextPrivateKey(ctx context.Context, crt *cmapi.Certificate) error {
pk, err := pki.GeneratePrivateKeyForCertificate(crt)
if err != nil {
return err
}
s, err := c.createNewPrivateKeySecret(ctx, crt, pk)
if err != nil {
return err
}
c.recorder.Event(crt, corev1.EventTypeNormal, "Generated", fmt.Sprintf("Stored new private key in temporary Secret resource %q", s.Name))
return c.setNextPrivateKeySecretName(ctx, crt, &s.Name)
}
// deleteSecretResources will delete the given secret resources
func (c *controller) deleteSecretResources(ctx context.Context, secrets []*corev1.Secret) error {
log := logf.FromContext(ctx)
for _, s := range secrets {
if err := c.coreClient.CoreV1().Secrets(s.Namespace).Delete(ctx, s.Name, metav1.DeleteOptions{}); err != nil {
return err
}
logf.WithRelatedResource(log, s).V(logf.DebugLevel).Info("Deleted 'next private key' Secret resource")
}
return nil
}
func (c *controller) setNextPrivateKeySecretName(ctx context.Context, crt *cmapi.Certificate, name *string) error {
// skip updates if there has been no change
if name == nil && crt.Status.NextPrivateKeySecretName == nil {
return nil
}
if name != nil && crt.Status.NextPrivateKeySecretName != nil {
if *name == *crt.Status.NextPrivateKeySecretName {
return nil
}
}
crt = crt.DeepCopy()
crt.Status.NextPrivateKeySecretName = name
return c.updateOrApplyStatus(ctx, crt)
}
// updateOrApplyStatus will update the controller status. If the
// ServerSideApply feature is enabled, the managed fields will instead get
// applied using the relevant Patch API call.
func (c *controller) updateOrApplyStatus(ctx context.Context, crt *cmapi.Certificate) error {
if utilfeature.DefaultFeatureGate.Enabled(feature.ServerSideApply) {
return internalcertificates.ApplyStatus(ctx, c.client, c.fieldManager, &cmapi.Certificate{
ObjectMeta: metav1.ObjectMeta{Namespace: crt.Namespace, Name: crt.Name},
Status: cmapi.CertificateStatus{NextPrivateKeySecretName: crt.Status.NextPrivateKeySecretName},
})
} else {
_, err := c.client.CertmanagerV1().Certificates(crt.Namespace).UpdateStatus(ctx, &cmapi.Certificate{
ObjectMeta: crt.ObjectMeta,
Status: crt.Status,
}, metav1.UpdateOptions{})
return err
}
}
func (c *controller) createNewPrivateKeySecret(ctx context.Context, crt *cmapi.Certificate, pk crypto.Signer) (*corev1.Secret, error) {
// if the 'nextPrivateKeySecretName' field is already set, use this as the
// name of the Secret resource.
name := ""
if crt.Status.NextPrivateKeySecretName != nil {
name = *crt.Status.NextPrivateKeySecretName
}
pkData, err := pki.EncodePrivateKey(pk, cmapi.PKCS8)
if err != nil {
return nil, err
}
s := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: crt.Namespace,
Name: name,
OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(crt, certificateGvk)},
Labels: map[string]string{
cmapi.IsNextPrivateKeySecretLabelKey: "true",
cmapi.PartOfCertManagerControllerLabelKey: "true",
},
},
Data: map[string][]byte{
corev1.TLSPrivateKeyKey: pkData,
},
}
if s.Name == "" {
// TODO: handle certificate resources that have especially long names
s.GenerateName = crt.Name + "-"
}
s, err = c.coreClient.CoreV1().Secrets(s.Namespace).Create(ctx, s, metav1.CreateOptions{})
if err != nil {
return nil, err
}
return s, nil
}
// controllerWrapper wraps the `controller` structure to make it implement
// the controllerpkg.queueingController interface
type controllerWrapper struct {
*controller
}
func (c *controllerWrapper) Register(ctx *controllerpkg.Context) (workqueue.TypedRateLimitingInterface[types.NamespacedName], []cache.InformerSynced, error) {
// construct a new named logger to be reused throughout the controller
log := logf.FromContext(ctx.RootContext, ControllerName)
ctrl, queue, mustSync, err := NewController(log, ctx)
c.controller = ctrl
return queue, mustSync, err
}
func init() {
controllerpkg.Register(ControllerName, func(ctx *controllerpkg.ContextFactory) (controllerpkg.Interface, error) {
return controllerpkg.NewBuilder(ctx, ControllerName).
For(&controllerWrapper{}).
Complete()
})
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package readiness
import (
"context"
"fmt"
"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
apiequality "k8s.io/apimachinery/pkg/api/equality"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/workqueue"
internalcertificates "github.com/cert-manager/cert-manager/internal/controller/certificates"
"github.com/cert-manager/cert-manager/internal/controller/certificates/policies"
"github.com/cert-manager/cert-manager/internal/controller/feature"
internalinformers "github.com/cert-manager/cert-manager/internal/informers"
apiutil "github.com/cert-manager/cert-manager/pkg/api/util"
cmapi "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
cmclient "github.com/cert-manager/cert-manager/pkg/client/clientset/versioned"
cmlisters "github.com/cert-manager/cert-manager/pkg/client/listers/certmanager/v1"
controllerpkg "github.com/cert-manager/cert-manager/pkg/controller"
"github.com/cert-manager/cert-manager/pkg/controller/certificates"
logf "github.com/cert-manager/cert-manager/pkg/logs"
utilfeature "github.com/cert-manager/cert-manager/pkg/util/feature"
"github.com/cert-manager/cert-manager/pkg/util/pki"
"github.com/cert-manager/cert-manager/pkg/util/predicate"
)
const (
// ControllerName is the name of the certificate readiness controller.
ControllerName = "certificates-readiness"
// ReadyReason is the 'Ready' reason of a Certificate.
ReadyReason = "Ready"
)
type controller struct {
// the policies to use to define readiness - named here to make testing simpler
policyChain policies.Chain
certificateLister cmlisters.CertificateLister
certificateRequestLister cmlisters.CertificateRequestLister
secretLister internalinformers.SecretLister
client cmclient.Interface
gatherer *policies.Gatherer
// policyEvaluator builds Ready condition of a Certificate based on policy evaluation
policyEvaluator policyEvaluatorFunc
// renewalTimeCalculator calculates renewal time of a certificate
renewalTimeCalculator pki.RenewalTimeFunc
// fieldManager is the string which will be used as the Field Manager on
// fields created or edited by the cert-manager Kubernetes client during
// Apply API calls.
fieldManager string
}
// readyConditionFunc is custom function type that builds certificate's Ready condition
type policyEvaluatorFunc func(policies.Chain, policies.Input) cmapi.CertificateCondition
// NewController returns a new certificate readiness controller.
func NewController(
log logr.Logger,
ctx *controllerpkg.Context,
chain policies.Chain,
renewalTimeCalculator pki.RenewalTimeFunc,
policyEvaluator policyEvaluatorFunc,
) (*controller, workqueue.TypedRateLimitingInterface[types.NamespacedName], []cache.InformerSynced, error) {
// create a queue used to queue up items to be processed
queue := workqueue.NewTypedRateLimitingQueueWithConfig(
controllerpkg.DefaultCertificateRateLimiter(),
workqueue.TypedRateLimitingQueueConfig[types.NamespacedName]{
Name: ControllerName,
},
)
// obtain references to all the informers used by this controller
certificateInformer := ctx.SharedInformerFactory.Certmanager().V1().Certificates()
certificateRequestInformer := ctx.SharedInformerFactory.Certmanager().V1().CertificateRequests()
secretsInformer := ctx.KubeSharedInformerFactory.Secrets()
if _, err := certificateInformer.Informer().AddEventHandler(&controllerpkg.QueuingEventHandler{Queue: queue}); err != nil {
return nil, nil, nil, fmt.Errorf("error setting up event handler: %v", err)
}
// When a CertificateRequest resource changes, enqueue the Certificate resource that owns it.
if _, err := certificateRequestInformer.Informer().AddEventHandler(&controllerpkg.BlockingEventHandler{
WorkFunc: certificates.EnqueueCertificatesForResourceUsingPredicates(log, queue, certificateInformer.Lister(), labels.Everything(), predicate.ResourceOwnerOf),
}); err != nil {
return nil, nil, nil, fmt.Errorf("error setting up event handler: %v", err)
}
// When a Secret resource changes, enqueue any Certificate resources that name it as spec.secretName.
if _, err := secretsInformer.Informer().AddEventHandler(&controllerpkg.BlockingEventHandler{
// Trigger reconciles on changes to the Secret named `spec.secretName`
WorkFunc: certificates.EnqueueCertificatesForResourceUsingPredicates(log, queue, certificateInformer.Lister(), labels.Everything(),
predicate.ExtractResourceName(predicate.CertificateSecretName)),
}); err != nil {
return nil, nil, nil, fmt.Errorf("error setting up event handler: %v", err)
}
// build a list of InformerSynced functions that will be returned by the Register method.
// the controller will only begin processing items once all of these informers have synced.
mustSync := []cache.InformerSynced{
certificateRequestInformer.Informer().HasSynced,
secretsInformer.Informer().HasSynced,
certificateInformer.Informer().HasSynced,
}
return &controller{
policyChain: chain,
certificateLister: certificateInformer.Lister(),
certificateRequestLister: certificateRequestInformer.Lister(),
secretLister: secretsInformer.Lister(),
client: ctx.CMClient,
gatherer: &policies.Gatherer{
CertificateRequestLister: certificateRequestInformer.Lister(),
SecretLister: secretsInformer.Lister(),
},
policyEvaluator: policyEvaluator,
renewalTimeCalculator: renewalTimeCalculator,
fieldManager: ctx.FieldManager,
}, queue, mustSync, nil
}
// ProcessItem is a worker function that will be called when a new key
// corresponding to a Certificate to be re-synced is pulled from the workqueue.
// ProcessItem will update the Ready condition of a Certificate.
func (c *controller) ProcessItem(ctx context.Context, key types.NamespacedName) error {
log := logf.FromContext(ctx).WithValues("key", key)
ctx = logf.NewContext(ctx, log)
namespace, name := key.Namespace, key.Name
crt, err := c.certificateLister.Certificates(namespace).Get(name)
if err != nil && !apierrors.IsNotFound(err) {
return err
}
if crt == nil || crt.DeletionTimestamp != nil {
// If the Certificate object was/ is being deleted, we don't want to update its status.
return nil
}
input, err := c.gatherer.DataForCertificate(ctx, crt)
if err != nil {
return err
}
condition := c.policyEvaluator(c.policyChain, input)
oldCrt := crt
crt = crt.DeepCopy()
apiutil.SetCertificateCondition(crt, crt.Generation, condition.Type, condition.Status, condition.Reason, condition.Message)
switch {
case input.Secret != nil && input.Secret.Data != nil:
x509cert, err := pki.DecodeX509CertificateBytes(input.Secret.Data[corev1.TLSCertKey])
if err != nil {
// clear status fields if we cannot decode the certificate bytes
crt.Status.NotAfter = nil
crt.Status.NotBefore = nil
crt.Status.RenewalTime = nil
break
}
notBefore := metav1.NewTime(x509cert.NotBefore)
notAfter := metav1.NewTime(x509cert.NotAfter)
renewalTime := c.renewalTimeCalculator(x509cert.NotBefore, x509cert.NotAfter, crt.Spec.RenewBefore, crt.Spec.RenewBeforePercentage)
// update Certificate's Status
crt.Status.NotBefore = ¬Before
crt.Status.NotAfter = ¬After
crt.Status.RenewalTime = renewalTime
default:
// clear status fields if the secret does not have any data
crt.Status.NotAfter = nil
crt.Status.NotBefore = nil
crt.Status.RenewalTime = nil
}
if !apiequality.Semantic.DeepEqual(oldCrt.Status, crt.Status) {
log.V(logf.DebugLevel).Info("updating status fields", "notAfter",
crt.Status.NotAfter, "notBefore", crt.Status.NotBefore, "renewalTime",
crt.Status.RenewalTime)
return c.updateOrApplyStatus(ctx, crt)
}
return nil
}
// updateOrApplyStatus will update the controller status. If the
// ServerSideApply feature is enabled, the managed fields will instead get
// applied using the relevant Patch API call.
func (c *controller) updateOrApplyStatus(ctx context.Context, crt *cmapi.Certificate) error {
if utilfeature.DefaultFeatureGate.Enabled(feature.ServerSideApply) {
var conditions []cmapi.CertificateCondition
if cond := apiutil.GetCertificateCondition(crt, cmapi.CertificateConditionReady); cond != nil {
conditions = []cmapi.CertificateCondition{*cond}
}
return internalcertificates.ApplyStatus(ctx, c.client, c.fieldManager, &cmapi.Certificate{
ObjectMeta: metav1.ObjectMeta{Namespace: crt.Namespace, Name: crt.Name},
Status: cmapi.CertificateStatus{
NotAfter: crt.Status.NotAfter,
NotBefore: crt.Status.NotBefore,
RenewalTime: crt.Status.RenewalTime,
Conditions: conditions,
},
})
} else {
_, err := c.client.CertmanagerV1().Certificates(crt.Namespace).UpdateStatus(ctx, crt, metav1.UpdateOptions{})
return err
}
}
// BuildReadyConditionFromChain builds Certificate's Ready condition using the result of policy chain evaluation
func BuildReadyConditionFromChain(chain policies.Chain, input policies.Input) cmapi.CertificateCondition {
reason, message, violationsFound := chain.Evaluate(input)
if !violationsFound {
return cmapi.CertificateCondition{
Type: cmapi.CertificateConditionReady,
Status: cmmeta.ConditionTrue,
Reason: ReadyReason,
Message: "Certificate is up to date and has not expired",
}
}
return cmapi.CertificateCondition{
Type: cmapi.CertificateConditionReady,
Status: cmmeta.ConditionFalse,
Reason: reason,
Message: message,
}
}
// controllerWrapper wraps the `controller` structure to make it implement
// the controllerpkg.queueingController interface
type controllerWrapper struct {
*controller
}
func (c *controllerWrapper) Register(ctx *controllerpkg.Context) (workqueue.TypedRateLimitingInterface[types.NamespacedName], []cache.InformerSynced, error) {
// construct a new named logger to be reused throughout the controller
log := logf.FromContext(ctx.RootContext, ControllerName)
ctrl, queue, mustSync, err := NewController(log,
ctx,
policies.NewReadinessPolicyChain(ctx.Clock),
pki.RenewalTime,
BuildReadyConditionFromChain,
)
c.controller = ctrl
return queue, mustSync, err
}
func init() {
controllerpkg.Register(ControllerName, func(ctx *controllerpkg.ContextFactory) (controllerpkg.Interface, error) {
return controllerpkg.NewBuilder(ctx, ControllerName).
For(&controllerWrapper{}).
Complete()
})
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package requestmanager
import (
"bytes"
"context"
"crypto"
"encoding/pem"
"fmt"
"strconv"
"time"
"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/record"
"k8s.io/client-go/util/workqueue"
"k8s.io/utils/clock"
"github.com/cert-manager/cert-manager/internal/controller/feature"
internalinformers "github.com/cert-manager/cert-manager/internal/informers"
apiutil "github.com/cert-manager/cert-manager/pkg/api/util"
cmapi "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
cmclient "github.com/cert-manager/cert-manager/pkg/client/clientset/versioned"
cmlisters "github.com/cert-manager/cert-manager/pkg/client/listers/certmanager/v1"
controllerpkg "github.com/cert-manager/cert-manager/pkg/controller"
"github.com/cert-manager/cert-manager/pkg/controller/certificates"
logf "github.com/cert-manager/cert-manager/pkg/logs"
utilfeature "github.com/cert-manager/cert-manager/pkg/util/feature"
"github.com/cert-manager/cert-manager/pkg/util/pki"
"github.com/cert-manager/cert-manager/pkg/util/predicate"
)
const (
ControllerName = "certificates-request-manager"
reasonRequestFailed = "RequestFailed"
reasonRequested = "Requested"
)
var (
certificateGvk = cmapi.SchemeGroupVersion.WithKind("Certificate")
)
type controller struct {
certificateLister cmlisters.CertificateLister
certificateRequestLister cmlisters.CertificateRequestLister
secretLister internalinformers.SecretLister
client cmclient.Interface
recorder record.EventRecorder
clock clock.Clock
copiedAnnotationPrefixes []string
// fieldManager is the string which will be used as the Field Manager on
// fields created or edited by the cert-manager Kubernetes client during
// Create or Apply API calls.
fieldManager string
}
func NewController(
log logr.Logger, ctx *controllerpkg.Context) (*controller, workqueue.TypedRateLimitingInterface[types.NamespacedName], []cache.InformerSynced, error) {
// create a queue used to queue up items to be processed
queue := workqueue.NewTypedRateLimitingQueueWithConfig(
controllerpkg.DefaultCertificateRateLimiter(),
workqueue.TypedRateLimitingQueueConfig[types.NamespacedName]{
Name: ControllerName,
},
)
// obtain references to all the informers used by this controller
certificateInformer := ctx.SharedInformerFactory.Certmanager().V1().Certificates()
certificateRequestInformer := ctx.SharedInformerFactory.Certmanager().V1().CertificateRequests()
secretsInformer := ctx.KubeSharedInformerFactory.Secrets()
if _, err := certificateInformer.Informer().AddEventHandler(&controllerpkg.QueuingEventHandler{Queue: queue}); err != nil {
return nil, nil, nil, fmt.Errorf("error setting up event handler: %v", err)
}
if _, err := certificateRequestInformer.Informer().AddEventHandler(&controllerpkg.BlockingEventHandler{
// Trigger reconciles on changes to any 'owned' CertificateRequest resources
WorkFunc: certificates.EnqueueCertificatesForResourceUsingPredicates(log, queue, certificateInformer.Lister(), labels.Everything(),
predicate.ResourceOwnerOf,
),
}); err != nil {
return nil, nil, nil, fmt.Errorf("error setting up event handler: %v", err)
}
if _, err := secretsInformer.Informer().AddEventHandler(&controllerpkg.BlockingEventHandler{
// Trigger reconciles on changes to any 'owned' secret resources
WorkFunc: certificates.EnqueueCertificatesForResourceUsingPredicates(log, queue, certificateInformer.Lister(), labels.Everything(),
predicate.ResourceOwnerOf,
),
}); err != nil {
return nil, nil, nil, fmt.Errorf("error setting up event handler: %v", err)
}
// build a list of InformerSynced functions that will be returned by the Register method.
// the controller will only begin processing items once all of these informers have synced.
mustSync := []cache.InformerSynced{
secretsInformer.Informer().HasSynced,
certificateRequestInformer.Informer().HasSynced,
certificateInformer.Informer().HasSynced,
}
return &controller{
certificateLister: certificateInformer.Lister(),
certificateRequestLister: certificateRequestInformer.Lister(),
secretLister: secretsInformer.Lister(),
client: ctx.CMClient,
recorder: ctx.Recorder,
clock: ctx.Clock,
copiedAnnotationPrefixes: ctx.CertificateOptions.CopiedAnnotationPrefixes,
fieldManager: ctx.FieldManager,
}, queue, mustSync, nil
}
func (c *controller) ProcessItem(ctx context.Context, key types.NamespacedName) error {
log := logf.FromContext(ctx).WithValues("key", key)
ctx = logf.NewContext(ctx, log)
namespace, name := key.Namespace, key.Name
crt, err := c.certificateLister.Certificates(namespace).Get(name)
if err != nil && !apierrors.IsNotFound(err) {
return err
}
if crt == nil || crt.DeletionTimestamp != nil {
// If the Certificate object was/ is being deleted, we don't want to create any
// new CertificateRequests objects
return nil
}
if !apiutil.CertificateHasCondition(crt, cmapi.CertificateCondition{
Type: cmapi.CertificateConditionIssuing,
Status: cmmeta.ConditionTrue,
}) {
return nil
}
// Check for and fetch the 'status.nextPrivateKeySecretName' secret
if crt.Status.NextPrivateKeySecretName == nil {
log.V(logf.DebugLevel).Info("status.nextPrivateKeySecretName not yet set, waiting for keymanager before processing certificate")
return nil
}
nextPrivateKeySecret, err := c.secretLister.Secrets(crt.Namespace).Get(*crt.Status.NextPrivateKeySecretName)
if apierrors.IsNotFound(err) {
log.V(logf.DebugLevel).Info("nextPrivateKeySecretName Secret resource does not exist, waiting for keymanager to create it before continuing")
return nil
}
if err != nil {
return err
}
if nextPrivateKeySecret.Data == nil || len(nextPrivateKeySecret.Data[corev1.TLSPrivateKeyKey]) == 0 {
log.V(logf.DebugLevel).Info("Next private key secret does not contain any valid data, waiting for keymanager before processing certificate")
return nil
}
pk, err := pki.DecodePrivateKeyBytes(nextPrivateKeySecret.Data[corev1.TLSPrivateKeyKey])
if err != nil {
log.Error(err, "Failed to decode next private key secret data, waiting for keymanager before processing certificate")
return nil
}
// Discover all 'owned' CertificateRequests
requests, err := certificates.ListCertificateRequestsMatchingPredicates(c.certificateRequestLister.CertificateRequests(crt.Namespace), labels.Everything(), predicate.ResourceOwnedBy(crt))
if err != nil {
return err
}
// delete any existing CertificateRequest resources that do not have a
// revision annotation
if requests, err = c.deleteRequestsWithoutRevision(ctx, requests...); err != nil {
return err
}
currentCertificateRevision := 0
if crt.Status.Revision != nil {
currentCertificateRevision = *crt.Status.Revision
}
nextRevision := currentCertificateRevision + 1
requests, err = requestsWithRevision(requests, nextRevision)
if err != nil {
return err
}
requests, err = c.deleteRequestsNotMatchingSpec(ctx, crt, pk.Public(), requests...)
if err != nil {
return err
}
requests, err = c.deleteCurrentFailedRequests(ctx, crt, requests...)
if err != nil {
return err
}
if len(requests) > 1 {
// TODO: we should handle this case better, but for now do nothing to
// avoid getting into loops where we keep creating multiple requests
// and deleting them again.
log.V(logf.ErrorLevel).Info("Multiple matching CertificateRequest resources exist, delete one of them. This is likely an error and should be reported on the issue tracker!")
return nil
}
if len(requests) == 1 {
// Nothing to do as we've already verified that the CertificateRequest
// is up to date above.
return nil
}
return c.createNewCertificateRequest(ctx, crt, pk, nextRevision, nextPrivateKeySecret.Name)
}
func (c *controller) deleteCurrentFailedRequests(ctx context.Context, crt *cmapi.Certificate, reqs ...*cmapi.CertificateRequest) ([]*cmapi.CertificateRequest, error) {
log := logf.FromContext(ctx).WithValues("Certificate", crt.Name)
var remaining []*cmapi.CertificateRequest
for _, req := range reqs {
log = logf.WithRelatedResource(log, req)
// Check if there are any 'current' CertificateRequests that
// failed during the previous issuance cycle. Those should be
// deleted so that a new one gets created and the issuance is
// re-tried. In practice no more than one CertificateRequest is
// expected at this point.
crReadyCond := apiutil.GetCertificateRequestCondition(req, cmapi.CertificateRequestConditionReady)
if crReadyCond == nil || crReadyCond.Status != cmmeta.ConditionFalse || crReadyCond.Reason != cmapi.CertificateRequestReasonFailed {
remaining = append(remaining, req)
continue
}
certIssuingCond := apiutil.GetCertificateCondition(crt, cmapi.CertificateConditionIssuing)
if certIssuingCond == nil {
// This should never happen
log.V(logf.ErrorLevel).Info("Certificate does not have Issuing condition")
return nil, nil
}
// If the Issuing condition on the Certificate is newer than the
// failure time on CertificateRequest, it means that the
// CertificateRequest failed during the previous issuance (for the
// same revision). If it is a CertificateRequest that failed
// during the previous issuance, then it should be deleted so
// that we create a new one for this issuance.
if req.Status.FailureTime.Before(certIssuingCond.LastTransitionTime) {
log.V(logf.DebugLevel).Info("Found a failed CertificateRequest for previous issuance of this revision, deleting...")
if err := c.client.CertmanagerV1().CertificateRequests(req.Namespace).Delete(ctx, req.Name, metav1.DeleteOptions{}); err != nil {
return nil, err
}
continue
}
remaining = append(remaining, req)
}
return remaining, nil
}
func (c *controller) deleteRequestsWithoutRevision(ctx context.Context, reqs ...*cmapi.CertificateRequest) ([]*cmapi.CertificateRequest, error) {
log := logf.FromContext(ctx)
var remaining []*cmapi.CertificateRequest
for _, req := range reqs {
log := logf.WithRelatedResource(log, req)
if req.Annotations == nil || req.Annotations[cmapi.CertificateRequestRevisionAnnotationKey] == "" {
log.V(logf.DebugLevel).Info("Deleting CertificateRequest as it does not contain a revision annotation")
if err := c.client.CertmanagerV1().CertificateRequests(req.Namespace).Delete(ctx, req.Name, metav1.DeleteOptions{}); err != nil {
return nil, err
}
continue
}
reqRevisionStr := req.Annotations[cmapi.CertificateRequestRevisionAnnotationKey]
_, err := strconv.ParseInt(reqRevisionStr, 10, 0)
if err != nil {
log.V(logf.DebugLevel).Info("Deleting CertificateRequest as it contains an invalid revision annotation")
if err := c.client.CertmanagerV1().CertificateRequests(req.Namespace).Delete(ctx, req.Name, metav1.DeleteOptions{}); err != nil {
return nil, err
}
continue
}
remaining = append(remaining, req)
}
return remaining, nil
}
func requestsWithRevision(reqs []*cmapi.CertificateRequest, revision int) ([]*cmapi.CertificateRequest, error) {
var remaining []*cmapi.CertificateRequest
for _, req := range reqs {
if req.Annotations == nil || req.Annotations[cmapi.CertificateRequestRevisionAnnotationKey] == "" {
return nil, fmt.Errorf("certificaterequest %q does not contain revision annotation", req.Name)
}
reqRevisionStr := req.Annotations[cmapi.CertificateRequestRevisionAnnotationKey]
reqRevision, err := strconv.ParseInt(reqRevisionStr, 10, 0)
if err != nil {
return nil, err
}
if reqRevision == int64(revision) {
remaining = append(remaining, req)
}
}
return remaining, nil
}
func (c *controller) deleteRequestsNotMatchingSpec(ctx context.Context, crt *cmapi.Certificate, publicKey crypto.PublicKey, reqs ...*cmapi.CertificateRequest) ([]*cmapi.CertificateRequest, error) {
log := logf.FromContext(ctx)
var remaining []*cmapi.CertificateRequest
for _, req := range reqs {
log := logf.WithRelatedResource(log, req)
violations, err := pki.RequestMatchesSpec(req, crt.Spec)
if err != nil {
log.Error(err, "Failed to check if CertificateRequest matches spec, deleting CertificateRequest")
if err := c.client.CertmanagerV1().CertificateRequests(req.Namespace).Delete(ctx, req.Name, metav1.DeleteOptions{}); err != nil {
return nil, err
}
continue
}
if len(violations) > 0 {
log.V(logf.InfoLevel).WithValues("violations", violations).Info("CertificateRequest does not match requirements on certificate.spec, deleting CertificateRequest", "violations", violations)
if err := c.client.CertmanagerV1().CertificateRequests(req.Namespace).Delete(ctx, req.Name, metav1.DeleteOptions{}); err != nil {
return nil, err
}
continue
}
x509Req, err := pki.DecodeX509CertificateRequestBytes(req.Spec.Request)
if err != nil {
// this case cannot happen as RequestMatchesSpec would have returned an error too
return nil, err
}
matches, err := pki.PublicKeyMatchesCSR(publicKey, x509Req)
if err != nil {
return nil, err
}
if !matches {
log.V(logf.DebugLevel).Info("CertificateRequest contains a CSR that does not have the same public key as the stored next private key secret, deleting CertificateRequest")
if err := c.client.CertmanagerV1().CertificateRequests(req.Namespace).Delete(ctx, req.Name, metav1.DeleteOptions{}); err != nil {
return nil, err
}
continue
}
remaining = append(remaining, req)
}
return remaining, nil
}
func (c *controller) createNewCertificateRequest(ctx context.Context, crt *cmapi.Certificate, pk crypto.Signer, nextRevision int, nextPrivateKeySecretName string) error {
log := logf.FromContext(ctx)
x509CSR, err := pki.GenerateCSR(
crt,
pki.WithUseLiteralSubject(utilfeature.DefaultMutableFeatureGate.Enabled(feature.LiteralCertificateSubject)),
pki.WithEncodeBasicConstraintsInRequest(utilfeature.DefaultMutableFeatureGate.Enabled(feature.UseCertificateRequestBasicConstraints)),
pki.WithNameConstraints(utilfeature.DefaultMutableFeatureGate.Enabled(feature.NameConstraints)),
pki.WithOtherNames(utilfeature.DefaultMutableFeatureGate.Enabled(feature.OtherNames)),
)
if err != nil {
log.Error(err, "Failed to generate CSR - will not retry")
return nil
}
csrDER, err := pki.EncodeCSR(x509CSR, pk)
if err != nil {
return err
}
csrPEM := bytes.NewBuffer([]byte{})
err = pem.Encode(csrPEM, &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: csrDER})
if err != nil {
return err
}
annotations := controllerpkg.BuildAnnotationsToCopy(crt.Annotations, c.copiedAnnotationPrefixes)
annotations[cmapi.CertificateRequestRevisionAnnotationKey] = strconv.Itoa(nextRevision)
annotations[cmapi.CertificateRequestPrivateKeyAnnotationKey] = nextPrivateKeySecretName
annotations[cmapi.CertificateNameKey] = crt.Name
cr := &cmapi.CertificateRequest{
ObjectMeta: metav1.ObjectMeta{
Namespace: crt.Namespace,
// We limit the GenerateName to 52 + 1 characters to stay within the 63 - 5 character limit that
// is used in Kubernetes when generating names.
// see https://github.com/kubernetes/apiserver/blob/696768606f546f71a1e90546613be37d1aa37f64/pkg/storage/names/generate.go
GenerateName: apiutil.DNSSafeShortenTo52Characters(crt.Name) + "-",
Annotations: annotations,
Labels: crt.Labels,
OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(crt, certificateGvk)},
},
Spec: cmapi.CertificateRequestSpec{
Duration: crt.Spec.Duration,
IssuerRef: crt.Spec.IssuerRef,
Request: csrPEM.Bytes(),
IsCA: crt.Spec.IsCA,
Usages: crt.Spec.Usages,
},
}
if utilfeature.DefaultFeatureGate.Enabled(feature.StableCertificateRequestName) {
cr.ObjectMeta.GenerateName = ""
// The CertificateRequest name is limited to 253 characters, assuming the nextRevision and hyphen
// can be represented using 20 characters, we can directly accept certificate names up to 233
// characters. Certificate names that are longer than this will be hashed to a shorter name. We want
// to make crafting two Certificates with the same truncated name as difficult as possible, so we
// use a cryptographic hash function to hash the full certificate name to 64 characters.
// Finally, for Certificates with a name longer than 233 characters, we build the CertificateRequest
// name as follows: <first-168-chars-of-certificate-name>-<64-char-hash>-<19-char-nextRevision>
crName, err := apiutil.ComputeSecureUniqueDeterministicNameFromData(crt.Name, 233)
if err != nil {
return err
}
cr.ObjectMeta.Name = fmt.Sprintf("%s-%d", crName, nextRevision)
}
cr, err = c.client.CertmanagerV1().CertificateRequests(cr.Namespace).Create(ctx, cr, metav1.CreateOptions{FieldManager: c.fieldManager})
if err != nil {
c.recorder.Eventf(crt, corev1.EventTypeWarning, reasonRequestFailed, "Failed to create CertificateRequest: "+err.Error())
return err
}
c.recorder.Eventf(crt, corev1.EventTypeNormal, reasonRequested, "Created new CertificateRequest resource %q", cr.Name)
// If the StableCertificateRequestName feature gate is enabled, skip waiting for our informer cache/lister to
// observe the creation event and instead rely on an AlreadyExists error being returned if we do attempt a
// CREATE for the same CertificateRequest name again early.
if utilfeature.DefaultFeatureGate.Enabled(feature.StableCertificateRequestName) {
return nil
}
if err := c.waitForCertificateRequestToExist(ctx, cr.Namespace, cr.Name); err != nil {
return fmt.Errorf("failed whilst waiting for CertificateRequest to exist - this may indicate an apiserver running slowly. Request will be retried. %w", err)
}
return nil
}
func (c *controller) waitForCertificateRequestToExist(ctx context.Context, namespace, name string) error {
return wait.PollUntilContextTimeout(ctx, time.Millisecond*100, time.Second*5, false, func(_ context.Context) (bool, error) {
_, err := c.certificateRequestLister.CertificateRequests(namespace).Get(name)
if apierrors.IsNotFound(err) {
return false, nil
}
if err != nil {
return false, err
}
return true, nil
})
}
// controllerWrapper wraps the `controller` structure to make it implement
// the controllerpkg.queueingController interface
type controllerWrapper struct {
*controller
}
func (c *controllerWrapper) Register(ctx *controllerpkg.Context) (workqueue.TypedRateLimitingInterface[types.NamespacedName], []cache.InformerSynced, error) {
// construct a new named logger to be reused throughout the controller
log := logf.FromContext(ctx.RootContext, ControllerName)
ctrl, queue, mustSync, err := NewController(log, ctx)
c.controller = ctrl
return queue, mustSync, err
}
func init() {
controllerpkg.Register(ControllerName, func(ctx *controllerpkg.ContextFactory) (controllerpkg.Interface, error) {
return controllerpkg.NewBuilder(ctx, ControllerName).
For(&controllerWrapper{}).
Complete()
})
}
/*
Copyright 2021 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package revisionmanager
import (
"context"
"errors"
"fmt"
"sort"
"strconv"
"github.com/go-logr/logr"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/workqueue"
apiutil "github.com/cert-manager/cert-manager/pkg/api/util"
cmapi "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
cmclient "github.com/cert-manager/cert-manager/pkg/client/clientset/versioned"
cmlisters "github.com/cert-manager/cert-manager/pkg/client/listers/certmanager/v1"
controllerpkg "github.com/cert-manager/cert-manager/pkg/controller"
"github.com/cert-manager/cert-manager/pkg/controller/certificates"
logf "github.com/cert-manager/cert-manager/pkg/logs"
"github.com/cert-manager/cert-manager/pkg/util/predicate"
)
const (
ControllerName = "certificates-revision-manager"
)
type controller struct {
certificateLister cmlisters.CertificateLister
certificateRequestLister cmlisters.CertificateRequestLister
client cmclient.Interface
}
type revision struct {
rev int
types.NamespacedName
}
func NewController(log logr.Logger, ctx *controllerpkg.Context) (*controller, workqueue.TypedRateLimitingInterface[types.NamespacedName], []cache.InformerSynced, error) {
// create a queue used to queue up items to be processed
queue := workqueue.NewTypedRateLimitingQueueWithConfig(
controllerpkg.DefaultCertificateRateLimiter(),
workqueue.TypedRateLimitingQueueConfig[types.NamespacedName]{
Name: ControllerName,
},
)
// obtain references to all the informers used by this controller
certificateInformer := ctx.SharedInformerFactory.Certmanager().V1().Certificates()
certificateRequestInformer := ctx.SharedInformerFactory.Certmanager().V1().CertificateRequests()
if _, err := certificateInformer.Informer().AddEventHandler(&controllerpkg.QueuingEventHandler{Queue: queue}); err != nil {
return nil, nil, nil, fmt.Errorf("error setting up event handler: %v", err)
}
if _, err := certificateRequestInformer.Informer().AddEventHandler(&controllerpkg.BlockingEventHandler{
// Trigger reconciles on changes to any 'owned' CertificateRequest resources
WorkFunc: certificates.EnqueueCertificatesForResourceUsingPredicates(log, queue, certificateInformer.Lister(), labels.Everything(),
predicate.ResourceOwnerOf,
),
}); err != nil {
return nil, nil, nil, fmt.Errorf("error setting up event handler: %v", err)
}
// build a list of InformerSynced functions that will be returned by the Register method.
// the controller will only begin processing items once all of these informers have synced.
mustSync := []cache.InformerSynced{
certificateRequestInformer.Informer().HasSynced,
certificateInformer.Informer().HasSynced,
}
return &controller{
certificateLister: certificateInformer.Lister(),
certificateRequestLister: certificateRequestInformer.Lister(),
client: ctx.CMClient,
}, queue, mustSync, nil
}
// ProcessItem will attempt to garbage collect old CertificateRequests based
// upon `spec.revisionHistoryLimit`. This controller will only act on
// Certificates which are in a Ready state and this value is set.
func (c *controller) ProcessItem(ctx context.Context, key types.NamespacedName) error {
log := logf.FromContext(ctx).WithValues("key", key)
ctx = logf.NewContext(ctx, log)
namespace, name := key.Namespace, key.Name
crt, err := c.certificateLister.Certificates(namespace).Get(name)
if err != nil && !apierrors.IsNotFound(err) {
return err
}
if crt == nil || crt.DeletionTimestamp != nil {
// If the Certificate object was/ is being deleted, we don't want to start deleting
// CertificateRequests last minute in the same namespace.
return nil
}
log = logf.WithResource(log, crt)
// Only garbage collect over Certificates that are in a Ready=True condition.
if !apiutil.CertificateHasCondition(crt, cmapi.CertificateCondition{
Type: cmapi.CertificateConditionReady,
Status: cmmeta.ConditionTrue,
}) {
return nil
}
// Get all CertificateRequests that are owned by this Certificate
requests, err := certificates.ListCertificateRequestsMatchingPredicates(
c.certificateRequestLister.CertificateRequests(crt.Namespace), labels.Everything(), predicate.ResourceOwnedBy(crt))
if err != nil {
return err
}
// Fetch and delete all CertificateRequests that need to be deleted
// If RevisionHistoryLimit is nil, then default to 1
var limit int
if crt.Spec.RevisionHistoryLimit == nil {
limit = 1
} else {
limit = int(*crt.Spec.RevisionHistoryLimit)
}
toDelete := certificateRequestsToDelete(log, limit, requests)
for _, req := range toDelete {
logf.WithRelatedResourceName(log, req.Name, req.Namespace, cmapi.CertificateRequestKind).
WithValues("revision", req.rev).Info("garbage collecting old certificate request revision")
err = c.client.CertmanagerV1().CertificateRequests(req.Namespace).Delete(ctx, req.Name, metav1.DeleteOptions{})
if apierrors.IsNotFound(err) {
continue
}
if err != nil {
return err
}
}
return nil
}
// certificateRequestsToDelete will prune the given CertificateRequests for
// those that have a valid revision number set, and return a slice of requests
// that should be deleted according to the limit given. Oldest
// CertificateRequests by revision will be returned.
func certificateRequestsToDelete(log logr.Logger, limit int, requests []*cmapi.CertificateRequest) []revision {
// If the number of requests is the same or below the limit, return nothing.
if limit >= len(requests) {
return nil
}
// Prune and sort all CertificateRequests by their revision number.
var revisions []revision
for _, req := range requests {
log = logf.WithRelatedResource(log, req)
if req.Annotations == nil || req.Annotations[cmapi.CertificateRequestRevisionAnnotationKey] == "" {
log.Error(errors.New("skipping processing request with missing revision"), "")
continue
}
rn, err := strconv.Atoi(req.Annotations[cmapi.CertificateRequestRevisionAnnotationKey])
if err != nil {
log.Error(err, "failed to parse request revision")
continue
}
revisions = append(revisions, revision{rn, types.NamespacedName{Namespace: req.Namespace, Name: req.Name}})
}
sort.SliceStable(revisions, func(i, j int) bool {
return revisions[i].rev < revisions[j].rev
})
// Return the oldest revisions which are over the limit
remaining := len(revisions) - limit
if remaining < 0 {
return nil
}
return revisions[:remaining]
}
// controllerWrapper wraps the `controller` structure to make it implement
// the controllerpkg.queueingController interface
type controllerWrapper struct {
*controller
}
func (c *controllerWrapper) Register(ctx *controllerpkg.Context) (workqueue.TypedRateLimitingInterface[types.NamespacedName], []cache.InformerSynced, error) {
// construct a new named logger to be reused throughout the controller
log := logf.FromContext(ctx.RootContext, ControllerName)
ctrl, queue, mustSync, err := NewController(log, ctx)
c.controller = ctrl
return queue, mustSync, err
}
func init() {
controllerpkg.Register(ControllerName, func(ctx *controllerpkg.ContextFactory) (controllerpkg.Interface, error) {
return controllerpkg.NewBuilder(ctx, ControllerName).
For(&controllerWrapper{}).
Complete()
})
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package trigger
import (
"context"
"fmt"
"math"
"time"
"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/record"
"k8s.io/client-go/util/workqueue"
"k8s.io/utils/clock"
internalcertificates "github.com/cert-manager/cert-manager/internal/controller/certificates"
"github.com/cert-manager/cert-manager/internal/controller/certificates/policies"
"github.com/cert-manager/cert-manager/internal/controller/feature"
internalinformers "github.com/cert-manager/cert-manager/internal/informers"
apiutil "github.com/cert-manager/cert-manager/pkg/api/util"
cmapi "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
cmclient "github.com/cert-manager/cert-manager/pkg/client/clientset/versioned"
cmlisters "github.com/cert-manager/cert-manager/pkg/client/listers/certmanager/v1"
controllerpkg "github.com/cert-manager/cert-manager/pkg/controller"
"github.com/cert-manager/cert-manager/pkg/controller/certificates"
logf "github.com/cert-manager/cert-manager/pkg/logs"
"github.com/cert-manager/cert-manager/pkg/scheduler"
utilfeature "github.com/cert-manager/cert-manager/pkg/util/feature"
"github.com/cert-manager/cert-manager/pkg/util/pki"
"github.com/cert-manager/cert-manager/pkg/util/predicate"
)
const (
ControllerName = "certificates-trigger"
// stopIncreaseBackoff is the number of issuance attempts after which the backoff period should stop to increase
stopIncreaseBackoff = 6 // 2 ^ (6 - 1) = 32 = maxDelay
// maxDelay is the maximum backoff period
maxDelay = 32 * time.Hour
)
// This controller observes the state of the certificate's currently
// issued `spec.secretName` and the rest of the `certificate.spec` fields to
// determine whether a re-issuance is required.
// It triggers re-issuance by adding the `Issuing` status condition when a new
// certificate is required.
type controller struct {
certificateLister cmlisters.CertificateLister
certificateRequestLister cmlisters.CertificateRequestLister
secretLister internalinformers.SecretLister
client cmclient.Interface
recorder record.EventRecorder
scheduledWorkQueue scheduler.ScheduledWorkQueue[types.NamespacedName]
// fieldManager is the string which will be used as the Field Manager on
// fields created or edited by the cert-manager Kubernetes client during
// Apply API calls.
fieldManager string
// The following are used for testing purposes.
clock clock.Clock
shouldReissue policies.Func
dataForCertificate func(context.Context, *cmapi.Certificate) (policies.Input, error)
}
func NewController(
log logr.Logger,
ctx *controllerpkg.Context,
shouldReissue policies.Func,
) (*controller, workqueue.TypedRateLimitingInterface[types.NamespacedName], []cache.InformerSynced, error) {
// create a queue used to queue up items to be processed
queue := workqueue.NewTypedRateLimitingQueueWithConfig(
controllerpkg.DefaultCertificateRateLimiter(),
workqueue.TypedRateLimitingQueueConfig[types.NamespacedName]{
Name: ControllerName,
},
)
// obtain references to all the informers used by this controller
certificateInformer := ctx.SharedInformerFactory.Certmanager().V1().Certificates()
certificateRequestInformer := ctx.SharedInformerFactory.Certmanager().V1().CertificateRequests()
secretsInformer := ctx.KubeSharedInformerFactory.Secrets()
if _, err := certificateInformer.Informer().AddEventHandler(&controllerpkg.QueuingEventHandler{Queue: queue}); err != nil {
return nil, nil, nil, fmt.Errorf("error setting up event handler: %v", err)
}
// When a CertificateRequest resource changes, enqueue the Certificate resource that owns it.
if _, err := certificateRequestInformer.Informer().AddEventHandler(&controllerpkg.BlockingEventHandler{
WorkFunc: certificates.EnqueueCertificatesForResourceUsingPredicates(log, queue, certificateInformer.Lister(), labels.Everything(), predicate.ResourceOwnerOf),
}); err != nil {
return nil, nil, nil, fmt.Errorf("error setting up event handler: %v", err)
}
// When a Secret resource changes, enqueue any Certificate resources that name it as spec.secretName.
if _, err := secretsInformer.Informer().AddEventHandler(&controllerpkg.BlockingEventHandler{
// Trigger reconciles on changes to the Secret named `spec.secretName`
WorkFunc: certificates.EnqueueCertificatesForResourceUsingPredicates(log, queue, certificateInformer.Lister(), labels.Everything(),
predicate.ExtractResourceName(predicate.CertificateSecretName)),
}); err != nil {
return nil, nil, nil, fmt.Errorf("error setting up event handler: %v", err)
}
// build a list of InformerSynced functions that will be returned by the Register method.
// the controller will only begin processing items once all of these informers have synced.
mustSync := []cache.InformerSynced{
certificateRequestInformer.Informer().HasSynced,
secretsInformer.Informer().HasSynced,
certificateInformer.Informer().HasSynced,
}
return &controller{
certificateLister: certificateInformer.Lister(),
certificateRequestLister: certificateRequestInformer.Lister(),
secretLister: secretsInformer.Lister(),
client: ctx.CMClient,
recorder: ctx.Recorder,
scheduledWorkQueue: scheduler.NewScheduledWorkQueue(ctx.Clock, queue.Add),
fieldManager: ctx.FieldManager,
// The following are used for testing purposes.
clock: ctx.Clock,
shouldReissue: shouldReissue,
dataForCertificate: (&policies.Gatherer{
CertificateRequestLister: certificateRequestInformer.Lister(),
SecretLister: secretsInformer.Lister(),
}).DataForCertificate,
}, queue, mustSync, nil
}
func (c *controller) ProcessItem(ctx context.Context, key types.NamespacedName) error {
log := logf.FromContext(ctx).WithValues("key", key)
ctx = logf.NewContext(ctx, log)
namespace, name := key.Namespace, key.Name
crt, err := c.certificateLister.Certificates(namespace).Get(name)
if err != nil && !k8sErrors.IsNotFound(err) {
return err
}
if crt == nil || crt.DeletionTimestamp != nil {
// If the Certificate object was/ is being deleted, we don't want to start scheduling
// renewals.
return nil
}
if apiutil.CertificateHasCondition(crt, cmapi.CertificateCondition{
Type: cmapi.CertificateConditionIssuing,
Status: cmmeta.ConditionTrue,
}) {
// Do nothing if an issuance is already in progress.
return nil
}
// It is possible for multiple Certificates to reference the same Secret. In that case, without this check,
// the duplicate Certificates would each be issued and store their version of the X.509 certificate in the
// target Secret, triggering the re-issuance of the other Certificate resources whose spec no longer matches
// what is in the Secret. This would cause a flood of re-issuance attempts and overloads the Kubernetes API
// and the API server of the issuing CA.
isOwner, duplicates, err := internalcertificates.CertificateOwnsSecret(ctx, c.certificateLister, c.secretLister, crt)
if err != nil {
return err
}
if !isOwner {
log.V(logf.DebugLevel).Info("Certificate.Spec.SecretName refers to the same Secret as other Certificates in the same namespace, skipping trigger.", "duplicates", duplicates)
// If the Certificate is not the owner of the Secret, we requeue the Certificate and wait for the
// Certificate to become the owner of the Secret. This can happen if the Certificate is updated to
// reference a different Secret, or if the conflicting Certificate is deleted or updated to no longer
// reference the Secret.
c.scheduledWorkQueue.Add(key, 3*time.Minute)
return nil
}
input, err := c.dataForCertificate(ctx, crt)
if err != nil {
return err
}
// Don't trigger issuance if we need to back off due to previous failures and Certificate's spec has not changed.
backoff, delay := shouldBackoffReissuingOnFailure(log, c.clock, input.Certificate, input.NextRevisionRequest)
if backoff {
nextIssuanceRetry := c.clock.Now().Add(delay)
message := fmt.Sprintf("Backing off from issuance due to previously failed issuance(s). Issuance will next be attempted at %v", nextIssuanceRetry)
log.V(logf.InfoLevel).Info(message)
c.scheduleRecheckOfCertificateIfRequired(log, key, delay)
return nil
}
if crt.Status.RenewalTime != nil {
// ensure a resync is scheduled in the future so that we re-check
// Certificate resources and trigger them near expiry time
c.scheduleRecheckOfCertificateIfRequired(log, key, crt.Status.RenewalTime.Time.Sub(c.clock.Now()))
}
reason, message, reissue := c.shouldReissue(input)
if !reissue {
// no re-issuance required, return early
return nil
}
// Although the below recorder.Event already logs the event, the log
// line is quite unreadable (very long). Since this information is very
// important for the user and the operator, we log the following
// message.
log.V(logf.InfoLevel).Info("Certificate must be re-issued", "reason", reason, "message", message)
crt = crt.DeepCopy()
apiutil.SetCertificateCondition(crt, crt.Generation, cmapi.CertificateConditionIssuing, cmmeta.ConditionTrue, reason, message)
if err := c.updateOrApplyStatus(ctx, crt); err != nil {
return err
}
c.recorder.Event(crt, corev1.EventTypeNormal, "Issuing", message)
return nil
}
// updateOrApplyStatus will update the controller status. If the
// ServerSideApply feature is enabled, the managed fields will instead get
// applied using the relevant Patch API call.
func (c *controller) updateOrApplyStatus(ctx context.Context, crt *cmapi.Certificate) error {
if utilfeature.DefaultFeatureGate.Enabled(feature.ServerSideApply) {
var conditions []cmapi.CertificateCondition
if cond := apiutil.GetCertificateCondition(crt, cmapi.CertificateConditionIssuing); cond != nil {
conditions = []cmapi.CertificateCondition{*cond}
}
return internalcertificates.ApplyStatus(ctx, c.client, c.fieldManager, &cmapi.Certificate{
ObjectMeta: metav1.ObjectMeta{Namespace: crt.Namespace, Name: crt.Name},
Status: cmapi.CertificateStatus{Conditions: conditions},
})
} else {
_, err := c.client.CertmanagerV1().Certificates(crt.Namespace).UpdateStatus(ctx, crt, metav1.UpdateOptions{})
return err
}
}
// shouldBackOffReissuingOnFailure returns true if an issuance needs to be
// delayed and the required delay after calculating the exponential backoff.
// The backoff periods are 1h, 2h, 4h, 8h, 16h and 32h counting from when the last
// failure occurred,
// so the returned delay will be backoff_period - (current_time - last_failure_time)
//
// Notably, it returns no back-off when the certificate doesn't
// match the "next" certificate (since a mismatch means that this certificate
// gets re-issued immediately).
//
// Note that the request can be left nil: in that case, the returned back-off
// will be 0 since it means the CR must be created immediately.
func shouldBackoffReissuingOnFailure(log logr.Logger, c clock.Clock, crt *cmapi.Certificate, nextCR *cmapi.CertificateRequest) (bool, time.Duration) {
if crt.Status.LastFailureTime == nil {
return false, 0
}
// We want to immediately trigger a re-issuance when the certificate
// changes. In order to detect a "change", we compare the "next" CR with the
// certificate spec and reissue if there is a mismatch. To understand this
// mechanism, take a look at the diagram of the scenario C at the top of the
// gatherer.go file.
//
// Note that the "next" CR is the only CR that matters when looking at
// whether the certificate still matches its CR. The "current" CR matches
// the previous spec of the certificate, so we don't want to be looking at
// the current CR.
if nextCR == nil {
log.V(logf.InfoLevel).Info("next CertificateRequest not available, skipping checking if Certificate matches the CertificateRequest")
} else {
mismatches, err := pki.RequestMatchesSpec(nextCR, crt.Spec)
if err != nil {
log.V(logf.InfoLevel).Info("next CertificateRequest cannot be decoded, skipping checking if Certificate matches the CertificateRequest")
return false, 0
}
if len(mismatches) > 0 {
log.V(logf.ExtendedInfoLevel).WithValues("mismatches", mismatches).Info("Certificate is failing but the Certificate differs from CertificateRequest, backoff is not required")
return false, 0
}
}
now := c.Now()
durationSinceFailure := now.Sub(crt.Status.LastFailureTime.Time)
initialDelay := time.Hour
delay := initialDelay
failedIssuanceAttempts := 0
// It is possible that crt.Status.LastFailureTime != nil &&
// crt.Status.FailedIssuanceAttempts == nil (in case of the Certificate having
// failed for an installation of cert-manager before the issuance
// attempts were introduced). In such case delay = initialDelay.
if crt.Status.FailedIssuanceAttempts != nil {
failedIssuanceAttempts = *crt.Status.FailedIssuanceAttempts
delay = time.Hour * time.Duration(math.Pow(2, float64(failedIssuanceAttempts-1)))
}
// Ensure that maximum returned delay is 32 hours
// delay cannot be calculated for large issuance numbers, so we
// cannot reliably check if delay > maxDelay directly
// (see i.e the result of time.Duration(math.Pow(2, 99)))
if failedIssuanceAttempts > stopIncreaseBackoff {
delay = maxDelay
}
// Ensure that minimum returned delay is 1 hour. This is here to guard
// against an edge case where the delay duration got messed
// up as a result of maths misuse in the previous calculations
if delay < initialDelay {
delay = initialDelay
}
if durationSinceFailure >= delay {
log.V(logf.ExtendedInfoLevel).WithValues("since_failure", durationSinceFailure).Info("Certificate has been in failure state long enough, no need to back off")
return false, 0
}
return true, delay - durationSinceFailure
}
// scheduleRecheckOfCertificateIfRequired will schedule the resource with the
// given key to be re-queued for processing after the given amount of time
// has elapsed.
// If the 'durationUntilRenewalTime' is less than zero, it will not be
// queued again.
func (c *controller) scheduleRecheckOfCertificateIfRequired(log logr.Logger, key types.NamespacedName, durationUntilRenewalTime time.Duration) {
// don't schedule a re-queue if the time is in the past.
// if it is in the past, the resource will be triggered during the
// current call to the ProcessItem method. If we added the item to the
// queue with a duration of <=0, we would otherwise continually re-queue
// in a tight loop whilst we wait for the caching listers to observe
// the 'Triggered' status condition changing to 'True'.
if durationUntilRenewalTime < 0 {
return
}
log.V(logf.DebugLevel).Info("scheduling renewal", "duration_until_renewal", durationUntilRenewalTime.String())
c.scheduledWorkQueue.Add(key, durationUntilRenewalTime)
}
// controllerWrapper wraps the `controller` structure to make it implement
// the controllerpkg.queueingController interface
type controllerWrapper struct {
*controller
}
func (c *controllerWrapper) Register(ctx *controllerpkg.Context) (workqueue.TypedRateLimitingInterface[types.NamespacedName], []cache.InformerSynced, error) {
// construct a new named logger to be reused throughout the controller
log := logf.FromContext(ctx.RootContext, ControllerName)
ctrl, queue, mustSync, err := NewController(log,
ctx,
policies.NewTriggerPolicyChain(ctx.Clock).Evaluate,
)
c.controller = ctrl
return queue, mustSync, err
}
func init() {
controllerpkg.Register(ControllerName, func(ctx *controllerpkg.ContextFactory) (controllerpkg.Interface, error) {
return controllerpkg.NewBuilder(ctx, ControllerName).
For(&controllerWrapper{}).
Complete()
})
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package logs
import (
"context"
"flag"
"fmt"
"github.com/go-logr/logr"
"github.com/spf13/pflag"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/component-base/logs"
logsapi "k8s.io/component-base/logs/api/v1"
"k8s.io/klog/v2"
"github.com/cert-manager/cert-manager/pkg/api"
_ "k8s.io/component-base/logs/json/register"
)
var Log = klog.TODO().WithName("cert-manager")
const (
// Following analog to https://github.com/kubernetes/community/blob/master/contributors/devel/sig-instrumentation/logging.md
ErrorLevel = 0
WarnLevel = 1
InfoLevel = 2
ExtendedInfoLevel = 3
DebugLevel = 4
TraceLevel = 5
)
// InitLogs initializes logs the way we want for kubernetes.
func InitLogs() {
logs.InitLogs()
klog.EnableContextualLogging(true) // Enable contextual logging
}
func AddFlagsNonDeprecated(opts *logsapi.LoggingConfiguration, fs *pflag.FlagSet) {
var allFlags pflag.FlagSet
logsapi.AddFlags(opts, &allFlags)
allFlags.VisitAll(func(f *pflag.Flag) {
switch f.Name {
case "logging-format", "log-flush-frequency", "v", "vmodule":
fs.AddFlag(f)
}
})
}
func AddFlags(opts *logsapi.LoggingConfiguration, fs *pflag.FlagSet) {
var allFlags flag.FlagSet
klog.InitFlags(&allFlags)
allFlags.VisitAll(func(f *flag.Flag) {
switch f.Name {
case "add_dir_header", "alsologtostderr", "log_backtrace_at", "log_dir", "log_file", "log_file_max_size",
"logtostderr", "one_output", "skip_headers", "skip_log_headers", "stderrthreshold":
pf := pflag.PFlagFromGoFlag(f)
pf.Deprecated = "this flag may be removed in the future"
pf.Hidden = true
fs.AddFlag(pf)
}
})
AddFlagsNonDeprecated(opts, fs)
}
func ValidateAndApply(opts *logsapi.LoggingConfiguration) error {
return logsapi.ValidateAndApply(opts, nil)
}
// FlushLogs flushes logs immediately.
func FlushLogs() {
logs.FlushLogs()
}
const (
ResourceNameKey = "resource_name"
ResourceNamespaceKey = "resource_namespace"
ResourceKindKey = "resource_kind"
ResourceVersionKey = "resource_version"
RelatedResourceNameKey = "related_resource_name"
RelatedResourceNamespaceKey = "related_resource_namespace"
RelatedResourceKindKey = "related_resource_kind"
RelatedResourceVersionKey = "related_resource_version"
)
func WithResource(l logr.Logger, obj metav1.Object) logr.Logger {
var gvk schema.GroupVersionKind
if runtimeObj, ok := obj.(runtime.Object); ok {
gvks, _, _ := api.Scheme.ObjectKinds(runtimeObj)
if len(gvks) > 0 {
gvk = gvks[0]
}
}
return l.WithValues(
ResourceNameKey, obj.GetName(),
ResourceNamespaceKey, obj.GetNamespace(),
ResourceKindKey, gvk.Kind,
ResourceVersionKey, gvk.Version,
)
}
func WithRelatedResource(l logr.Logger, obj metav1.Object) logr.Logger {
var gvk schema.GroupVersionKind
if runtimeObj, ok := obj.(runtime.Object); ok {
gvks, _, _ := api.Scheme.ObjectKinds(runtimeObj)
if len(gvks) > 0 {
gvk = gvks[0]
}
}
return l.WithValues(
RelatedResourceNameKey, obj.GetName(),
RelatedResourceNamespaceKey, obj.GetNamespace(),
RelatedResourceKindKey, gvk.Kind,
RelatedResourceVersionKey, gvk.Version,
)
}
func WithRelatedResourceName(l logr.Logger, name, namespace, kind string) logr.Logger {
return l.WithValues(
RelatedResourceNameKey, name,
RelatedResourceNamespaceKey, namespace,
RelatedResourceKindKey, kind,
)
}
func FromContext(ctx context.Context, names ...string) logr.Logger {
l, err := logr.FromContext(ctx)
if err != nil {
l = Log
}
for _, n := range names {
l = l.WithName(n)
}
return l
}
func NewContext(ctx context.Context, l logr.Logger, names ...string) context.Context {
for _, n := range names {
l = l.WithName(n)
}
return logr.NewContext(ctx, l)
}
// LogWithFormat is a wrapper for logger that adds Infof method to log messages
// with the given format and arguments.
//
// Used as a patch to the controller eventBroadcaster for sending non-string objects.
type LogWithFormat struct {
logr.Logger
}
func WithInfof(l logr.Logger) *LogWithFormat {
return &LogWithFormat{l}
}
// Infof logs message with the given format and arguments.
func (l *LogWithFormat) Infof(format string, a ...interface{}) {
l.Info(fmt.Sprintf(format, a...))
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package errors
import "fmt"
type invalidDataError struct{ error }
func NewInvalidData(str string, obj ...interface{}) error {
return &invalidDataError{error: fmt.Errorf(str, obj...)}
}
func IsInvalidData(err error) bool {
if _, ok := err.(*invalidDataError); !ok {
return false
}
return true
}
/*
Copyright 2023 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// This file contains some code copied from the Go standard library under the following license: https://github.com/golang/go/blob/c95fe91d0715dc0a8d55ac80a80f383c3635548b/LICENSE
package pki
import (
"encoding/asn1"
"errors"
"fmt"
"strconv"
"strings"
"unicode"
"unicode/utf8"
)
// ParseObjectIdentifier parses an object identifier from its string representation.
func ParseObjectIdentifier(oidString string) (oid asn1.ObjectIdentifier, err error) {
if len(oidString) == 0 {
return nil, errors.New("zero length OBJECT IDENTIFIER")
}
parts := strings.Split(oidString, ".")
oid = make(asn1.ObjectIdentifier, 0, len(parts))
for _, part := range parts {
value, err := strconv.Atoi(part)
if err != nil {
return nil, err
}
oid = append(oid, value)
}
return oid, nil
}
type UniversalValueType int
const (
UniversalValueTypeBytes UniversalValueType = iota
UniversalValueTypeIA5String
UniversalValueTypeUTF8String
UniversalValueTypePrintableString
)
type UniversalValue struct {
Bytes []byte
IA5String string
UTF8String string
PrintableString string
}
func (uv UniversalValue) Type() UniversalValueType {
isBytes := uv.Bytes != nil
isIA5String := uv.IA5String != ""
isUTF8String := uv.UTF8String != ""
isPrintableString := uv.PrintableString != ""
switch {
case isBytes && !isIA5String && !isUTF8String && !isPrintableString:
return UniversalValueTypeBytes
case !isBytes && isIA5String && !isUTF8String && !isPrintableString:
return UniversalValueTypeIA5String
case !isBytes && !isIA5String && isUTF8String && !isPrintableString:
return UniversalValueTypeUTF8String
case !isBytes && !isIA5String && !isUTF8String && isPrintableString:
return UniversalValueTypePrintableString
}
return -1 // Either no field is set or two fields are set.
}
func MarshalUniversalValue(uv UniversalValue) ([]byte, error) {
// Make sure we have only one field set
uvType := uv.Type()
var bytes []byte
switch uvType {
case -1:
return nil, errors.New("UniversalValue should have exactly one field set")
case UniversalValueTypeBytes:
bytes = uv.Bytes
default:
rawValue := asn1.RawValue{
Class: asn1.ClassUniversal,
IsCompound: false,
}
switch uvType {
case UniversalValueTypeIA5String:
if err := isIA5String(uv.IA5String); err != nil {
return nil, errors.New("asn1: invalid IA5 string")
}
rawValue.Tag = asn1.TagIA5String
rawValue.Bytes = []byte(uv.IA5String)
case UniversalValueTypeUTF8String:
if !utf8.ValidString(uv.UTF8String) {
return nil, errors.New("asn1: invalid UTF-8 string")
}
rawValue.Tag = asn1.TagUTF8String
rawValue.Bytes = []byte(uv.UTF8String)
case UniversalValueTypePrintableString:
if !isPrintable(uv.PrintableString) {
return nil, errors.New("asn1: invalid PrintableString string")
}
rawValue.Tag = asn1.TagPrintableString
rawValue.Bytes = []byte(uv.PrintableString)
}
universalBytes, err := asn1.Marshal(rawValue)
if err != nil {
return nil, err
}
bytes = universalBytes
}
return bytes, nil
}
func UnmarshalUniversalValue(rawValue asn1.RawValue) (UniversalValue, error) {
var uv UniversalValue
if rawValue.FullBytes == nil {
fullBytes, err := asn1.Marshal(rawValue)
if err != nil {
return uv, err
}
rawValue.FullBytes = fullBytes
}
var rest []byte
var err error
switch rawValue.Tag {
case asn1.TagIA5String:
rest, err = asn1.UnmarshalWithParams(rawValue.FullBytes, &uv.IA5String, "ia5")
case asn1.TagUTF8String:
rest, err = asn1.UnmarshalWithParams(rawValue.FullBytes, &uv.UTF8String, "utf8")
case asn1.TagPrintableString:
rest, err = asn1.UnmarshalWithParams(rawValue.FullBytes, &uv.PrintableString, "printable")
default:
uv.Bytes = rawValue.FullBytes
}
if err != nil {
return uv, err
}
if len(rest) != 0 {
return uv, fmt.Errorf("trailing data")
}
return uv, nil
}
// Copied from: https://github.com/golang/go/blob/c95fe91d0715dc0a8d55ac80a80f383c3635548b/src/crypto/x509/x509.go#L1093
func isIA5String(s string) error {
for _, r := range s {
// Per RFC5280 "IA5String is limited to the set of ASCII characters"
if r > unicode.MaxASCII {
return fmt.Errorf("x509: %q cannot be encoded as an IA5String", s)
}
}
return nil
}
// isPrintable reports whether the given b is in the ASN.1 PrintableString set.
// '*' and '&' are also allowed, reflecting existing practice.
// Copied from: https://github.com/golang/go/blob/c95fe91d0715dc0a8d55ac80a80f383c3635548b/src/crypto/x509/parser.go#L34
func isPrintable(s string) bool {
for _, b := range s {
if 'a' <= b && b <= 'z' ||
'A' <= b && b <= 'Z' ||
'0' <= b && b <= '9' ||
'\'' <= b && b <= ')' ||
'+' <= b && b <= '/' ||
b == ' ' ||
b == ':' ||
b == '=' ||
b == '?' ||
// This is technically not allowed in a PrintableString.
// However, x509 certificates with wildcard strings don't
// always use the correct string type so we permit it.
b == '*' ||
// This is not technically allowed either. However, not
// only is it relatively common, but there are also a
// handful of CA certificates that contain it. At least
// one of which will not expire until 2027.
b == '&' {
continue
}
return false
}
return true
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package pki
import (
"crypto/x509/pkix"
"encoding/asn1"
"errors"
)
// Copied from x509.go
var (
OIDExtensionBasicConstraints = []int{2, 5, 29, 19}
)
// Copied from x509.go
type basicConstraints struct {
IsCA bool `asn1:"optional"`
MaxPathLen int `asn1:"optional,default:-1"`
}
// Adapted from x509.go
func MarshalBasicConstraints(isCA bool, maxPathLen *int) (pkix.Extension, error) {
ext := pkix.Extension{Id: OIDExtensionBasicConstraints, Critical: true}
// A value of -1 causes encoding/asn1 to omit the value as desired.
maxPathLenValue := -1
if maxPathLen != nil {
maxPathLenValue = *maxPathLen
}
var err error
ext.Value, err = asn1.Marshal(basicConstraints{isCA, maxPathLenValue})
return ext, err
}
// Adapted from x509.go
func UnmarshalBasicConstraints(value []byte) (isCA bool, maxPathLen *int, err error) {
var constraints basicConstraints
var rest []byte
if rest, err = asn1.Unmarshal(value, &constraints); err != nil {
return isCA, maxPathLen, err
} else if len(rest) != 0 {
return isCA, maxPathLen, errors.New("x509: trailing data after X.509 BasicConstraints")
}
isCA = constraints.IsCA
if constraints.MaxPathLen >= 0 {
maxPathLen = new(int)
*maxPathLen = constraints.MaxPathLen
}
return isCA, maxPathLen, nil
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package pki
import (
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"fmt"
"slices"
"strings"
"time"
certificatesv1 "k8s.io/api/certificates/v1"
apiutil "github.com/cert-manager/cert-manager/pkg/api/util"
v1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
experimentalapi "github.com/cert-manager/cert-manager/pkg/apis/experimental/v1alpha1"
)
type CertificateTemplateValidatorMutator func(*x509.CertificateRequest, *x509.Certificate) error
func hasExtension(checkReq *x509.CertificateRequest, extensionID asn1.ObjectIdentifier) bool {
for _, ext := range checkReq.Extensions {
if ext.Id.Equal(extensionID) {
return true
}
}
for _, ext := range checkReq.ExtraExtensions {
if ext.Id.Equal(extensionID) {
return true
}
}
return false
}
// CertificateTemplateOverrideDuration returns a CertificateTemplateValidatorMutator that overrides the
// certificate duration.
func CertificateTemplateOverrideDuration(duration time.Duration) CertificateTemplateValidatorMutator {
return func(req *x509.CertificateRequest, cert *x509.Certificate) error {
cert.NotBefore = time.Now()
cert.NotAfter = cert.NotBefore.Add(duration)
return nil
}
}
// CertificateTemplateValidateAndOverrideBasicConstraints returns a CertificateTemplateValidatorMutator that overrides
// the certificate basic constraints.
func CertificateTemplateValidateAndOverrideBasicConstraints(isCA bool, maxPathLen *int) CertificateTemplateValidatorMutator {
return func(req *x509.CertificateRequest, cert *x509.Certificate) error {
if hasExtension(req, OIDExtensionBasicConstraints) {
if !cert.BasicConstraintsValid {
return fmt.Errorf("encoded CSR error: BasicConstraintsValid is not true")
}
if cert.IsCA != isCA {
return fmt.Errorf("encoded CSR error: IsCA %v does not match expected value %v", cert.IsCA, isCA)
}
// We explicitly do not check the MaxPathLen and MaxPathLenZero fields here, as there is no way to
// configure these fields in a CertificateRequest or CSR object yet. If we ever add a way to configure
// these fields, we should add a check here to ensure that the values match the expected values.
// The provided maxPathLen is only used to override the value, not to validate it.
// TODO: if we add support for maxPathLen, we should add a check here to ensure that the value in the
// CertificateRequest or CSR matches the value encoded in the CSR blob.
}
cert.BasicConstraintsValid = true
cert.IsCA = isCA
if maxPathLen != nil {
cert.MaxPathLen = *maxPathLen
cert.MaxPathLenZero = *maxPathLen == 0
} else {
cert.MaxPathLen = 0
cert.MaxPathLenZero = false
}
return nil
}
}
// CertificateTemplateValidateAndOverrideKeyUsages returns a CertificateTemplateValidatorMutator that overrides the
// certificate key usages.
func CertificateTemplateValidateAndOverrideKeyUsages(keyUsage x509.KeyUsage, extKeyUsage []x509.ExtKeyUsage) CertificateTemplateValidatorMutator {
return func(req *x509.CertificateRequest, cert *x509.Certificate) error {
if hasExtension(req, OIDExtensionKeyUsage) || hasExtension(req, OIDExtensionExtendedKeyUsage) {
if cert.KeyUsage != keyUsage {
return fmt.Errorf("encoded CSR error: the KeyUsages %s do not match the expected KeyUsages %s",
printKeyUsage(apiutil.KeyUsageStrings(cert.KeyUsage)),
printKeyUsage(apiutil.KeyUsageStrings(keyUsage)),
)
}
if !slices.Equal(cert.ExtKeyUsage, extKeyUsage) {
return fmt.Errorf("encoded CSR error: the ExtKeyUsages %s do not match the expected ExtKeyUsages %s",
printKeyUsage(apiutil.ExtKeyUsageStrings(cert.ExtKeyUsage)),
printKeyUsage(apiutil.ExtKeyUsageStrings(extKeyUsage)),
)
}
}
cert.KeyUsage = keyUsage
cert.ExtKeyUsage = extKeyUsage
return nil
}
}
type printKeyUsage []v1.KeyUsage
func (k printKeyUsage) String() string {
var sb strings.Builder
sb.WriteString("[")
for i, u := range k {
sb.WriteString(" '")
sb.WriteString(string(u))
sb.WriteString("'")
if i < len(k)-1 {
sb.WriteString(",")
}
}
if len(k) > 0 {
sb.WriteString(" ")
}
sb.WriteString("]")
return sb.String()
}
// CertificateTemplateFromCSR will create a x509.Certificate for the
// given *x509.CertificateRequest.
func CertificateTemplateFromCSR(csr *x509.CertificateRequest, validatorMutators ...CertificateTemplateValidatorMutator) (*x509.Certificate, error) {
cert := &x509.Certificate{
PublicKeyAlgorithm: csr.PublicKeyAlgorithm,
PublicKey: csr.PublicKey,
Subject: csr.Subject,
RawSubject: csr.RawSubject,
DNSNames: csr.DNSNames,
IPAddresses: csr.IPAddresses,
EmailAddresses: csr.EmailAddresses,
URIs: csr.URIs,
}
// Start by copying all extensions from the CSR
extractExtensions := func(template *x509.Certificate, val pkix.Extension) error {
// Check the CSR for the X.509 BasicConstraints (RFC 5280, 4.2.1.9)
// extension and append to template if necessary
if val.Id.Equal(OIDExtensionBasicConstraints) {
unmarshalIsCA, unmarshalMaxPathLen, err := UnmarshalBasicConstraints(val.Value)
if err != nil {
return err
}
template.BasicConstraintsValid = true
template.IsCA = unmarshalIsCA
if unmarshalMaxPathLen != nil {
template.MaxPathLen = *unmarshalMaxPathLen
template.MaxPathLenZero = *unmarshalMaxPathLen == 0
} else {
template.MaxPathLen = 0
template.MaxPathLenZero = false
}
}
if val.Id.Equal(OIDExtensionNameConstraints) {
nameConstraints, err := UnmarshalNameConstraints(val.Value)
if err != nil {
return err
}
template.PermittedDNSDomainsCritical = val.Critical
template.PermittedDNSDomains = nameConstraints.PermittedDNSDomains
template.PermittedIPRanges = nameConstraints.PermittedIPRanges
template.PermittedEmailAddresses = nameConstraints.PermittedEmailAddresses
template.PermittedURIDomains = nameConstraints.PermittedURIDomains
template.ExcludedDNSDomains = nameConstraints.ExcludedDNSDomains
template.ExcludedIPRanges = nameConstraints.ExcludedIPRanges
template.ExcludedEmailAddresses = nameConstraints.ExcludedEmailAddresses
template.ExcludedURIDomains = nameConstraints.ExcludedURIDomains
}
// RFC 5280, 4.2.1.3
if val.Id.Equal(OIDExtensionKeyUsage) {
usage, err := UnmarshalKeyUsage(val.Value)
if err != nil {
return err
}
template.KeyUsage = usage
}
if val.Id.Equal(OIDExtensionExtendedKeyUsage) {
extUsages, unknownUsages, err := UnmarshalExtKeyUsage(val.Value)
if err != nil {
return err
}
template.ExtKeyUsage = extUsages
template.UnknownExtKeyUsage = unknownUsages
}
// The SANs fields in the Certificate resource are not enough to
// represent the full set of SANs that can be encoded in a CSR.
// Therefore, we need to copy the SANs from the CSR into the
// ExtraExtensions field of the certificate template.
if val.Id.Equal(oidExtensionSubjectAltName) {
template.ExtraExtensions = append(template.ExtraExtensions, val)
}
return nil
}
for _, val := range csr.Extensions {
if err := extractExtensions(cert, val); err != nil {
return nil, err
}
}
for _, val := range csr.ExtraExtensions {
if err := extractExtensions(cert, val); err != nil {
return nil, err
}
}
cert.Extensions = csr.Extensions
for _, validatorMutator := range validatorMutators {
if err := validatorMutator(csr, cert); err != nil {
return nil, err
}
}
// Finally, we fix up the certificate template to ensure that it is valid
{
// If the certificate has an empty Subject, we set any SAN extensions to be critical
var asn1Subject []byte
var err error
if cert.RawSubject != nil {
asn1Subject = cert.RawSubject
} else {
asn1Subject, err = asn1.Marshal(cert.Subject.ToRDNSequence())
if err != nil {
return nil, fmt.Errorf("failed to marshal subject to ASN.1 DER: %s", err.Error())
}
}
for i := range cert.ExtraExtensions {
if cert.ExtraExtensions[i].Id.Equal(oidExtensionSubjectAltName) {
cert.ExtraExtensions[i].Critical = IsASN1SubjectEmpty(asn1Subject)
}
}
}
return cert, nil
}
// CertificateTemplateFromCSRPEM will create a x509.Certificate for the
// given csrPEM.
func CertificateTemplateFromCSRPEM(csrPEM []byte, validatorMutators ...CertificateTemplateValidatorMutator) (*x509.Certificate, error) {
csr, err := DecodeX509CertificateRequestBytes(csrPEM)
if err != nil {
return nil, err
}
if err := csr.CheckSignature(); err != nil {
return nil, err
}
return CertificateTemplateFromCSR(csr, validatorMutators...)
}
// CertificateTemplateFromCertificate will create a x509.Certificate for the given
// Certificate resource
func CertificateTemplateFromCertificate(crt *v1.Certificate) (*x509.Certificate, error) {
csr, err := GenerateCSR(crt)
if err != nil {
return nil, err
}
certDuration := apiutil.DefaultCertDuration(crt.Spec.Duration)
keyUsage, extKeyUsage, err := KeyUsagesForCertificateOrCertificateRequest(crt.Spec.Usages, crt.Spec.IsCA)
if err != nil {
return nil, err
}
return CertificateTemplateFromCSR(
csr,
CertificateTemplateOverrideDuration(certDuration),
CertificateTemplateValidateAndOverrideBasicConstraints(crt.Spec.IsCA, nil),
CertificateTemplateValidateAndOverrideKeyUsages(keyUsage, extKeyUsage),
)
}
// CertificateTemplateFromCertificateRequest will create a x509.Certificate for the given
// CertificateRequest resource
func CertificateTemplateFromCertificateRequest(cr *v1.CertificateRequest) (*x509.Certificate, error) {
certDuration := apiutil.DefaultCertDuration(cr.Spec.Duration)
keyUsage, extKeyUsage, err := KeyUsagesForCertificateOrCertificateRequest(cr.Spec.Usages, cr.Spec.IsCA)
if err != nil {
return nil, err
}
return CertificateTemplateFromCSRPEM(
cr.Spec.Request,
CertificateTemplateOverrideDuration(certDuration),
CertificateTemplateValidateAndOverrideBasicConstraints(cr.Spec.IsCA, nil), // Override the basic constraints, but make sure they match the constraints in the CSR if present
CertificateTemplateValidateAndOverrideKeyUsages(keyUsage, extKeyUsage), // Override the key usages, but make sure they match the usages in the CSR if present
)
}
// CertificateTemplateFromCertificateSigningRequest will create a x509.Certificate for the given
// CertificateSigningRequest resource
func CertificateTemplateFromCertificateSigningRequest(csr *certificatesv1.CertificateSigningRequest) (*x509.Certificate, error) {
duration, err := DurationFromCertificateSigningRequest(csr)
if err != nil {
return nil, err
}
ku, eku, err := BuildKeyUsagesKube(csr.Spec.Usages)
if err != nil {
return nil, err
}
isCA := csr.Annotations[experimentalapi.CertificateSigningRequestIsCAAnnotationKey] == "true"
return CertificateTemplateFromCSRPEM(
csr.Spec.Request,
CertificateTemplateOverrideDuration(duration),
CertificateTemplateValidateAndOverrideBasicConstraints(isCA, nil), // Override the basic constraints, but make sure they match the constraints in the CSR if present
CertificateTemplateValidateAndOverrideKeyUsages(ku, eku), // Override the key usages, but make sure they match the usages in the CSR if present
)
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package pki
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
"errors"
"fmt"
"net"
"net/netip"
"net/url"
apiutil "github.com/cert-manager/cert-manager/pkg/api/util"
v1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
)
// IPAddressesToString converts a slice of IP addresses to strings, which can be useful for
// printing a list of addresses but MUST NOT be used for comparing two slices of IP addresses.
func IPAddressesToString(ipAddresses []net.IP) []string {
var ipNames []string
for _, ip := range ipAddresses {
ipNames = append(ipNames, ip.String())
}
return ipNames
}
func IPAddressesFromStrings(ipStrings []string) ([]net.IP, error) {
var ipAddresses []net.IP
for _, ipString := range ipStrings {
ip, err := netip.ParseAddr(ipString)
if err != nil || ip.Zone() != "" {
return nil, err
}
addr := ip.AsSlice()
if len(addr) == 0 {
return nil, fmt.Errorf("failed to parse IP address %q", ipString)
}
ipAddresses = append(ipAddresses, net.IP(addr))
}
return ipAddresses, nil
}
func URLsToString(uris []*url.URL) []string {
var uriStrs []string
for _, uri := range uris {
if uri == nil {
panic("provided uri to string is nil")
}
uriStrs = append(uriStrs, uri.String())
}
return uriStrs
}
// SubjectForCertificate will return the Subject from the Certificate resource or an empty one if it is not set
func SubjectForCertificate(crt *v1.Certificate) v1.X509Subject {
if crt.Spec.Subject == nil {
return v1.X509Subject{}
}
return *crt.Spec.Subject
}
func KeyUsagesForCertificateOrCertificateRequest(usages []v1.KeyUsage, isCA bool) (ku x509.KeyUsage, eku []x509.ExtKeyUsage, err error) {
var unk []v1.KeyUsage
if isCA {
ku |= x509.KeyUsageCertSign
}
// If no usages are specified, default to the ones specified in the
// Kubernetes API.
if len(usages) == 0 {
usages = v1.DefaultKeyUsages()
}
for _, u := range usages {
if kuse, ok := apiutil.KeyUsageType(u); ok {
ku |= kuse
} else if ekuse, ok := apiutil.ExtKeyUsageType(u); ok {
eku = append(eku, ekuse)
} else {
unk = append(unk, u)
}
}
if len(unk) > 0 {
err = fmt.Errorf("unknown key usages: %v", unk)
}
return
}
type generateCSROptions struct {
EncodeBasicConstraintsInRequest bool
EncodeNameConstraints bool
EncodeOtherNames bool
UseLiteralSubject bool
}
type GenerateCSROption func(*generateCSROptions)
// WithEncodeBasicConstraintsInRequest determines whether the BasicConstraints
// extension should be encoded in the CSR.
// NOTE: this is a temporary option that will be removed in a future release.
func WithEncodeBasicConstraintsInRequest(encode bool) GenerateCSROption {
return func(o *generateCSROptions) {
o.EncodeBasicConstraintsInRequest = encode
}
}
func WithNameConstraints(enabled bool) GenerateCSROption {
return func(o *generateCSROptions) {
o.EncodeNameConstraints = enabled
}
}
func WithOtherNames(enabled bool) GenerateCSROption {
return func(o *generateCSROptions) {
o.EncodeOtherNames = enabled
}
}
func WithUseLiteralSubject(useLiteralSubject bool) GenerateCSROption {
return func(o *generateCSROptions) {
o.UseLiteralSubject = useLiteralSubject
}
}
// GenerateCSR will generate a new *x509.CertificateRequest template to be used
// by issuers that utilise CSRs to obtain Certificates.
// The CSR will not be signed, and should be passed to either EncodeCSR or
// to the x509.CreateCertificateRequest function.
func GenerateCSR(crt *v1.Certificate, optFuncs ...GenerateCSROption) (*x509.CertificateRequest, error) {
opts := &generateCSROptions{
EncodeBasicConstraintsInRequest: false,
EncodeNameConstraints: false,
EncodeOtherNames: false,
UseLiteralSubject: false,
}
for _, opt := range optFuncs {
opt(opts)
}
// Generate the Subject field for the CSR.
var commonName string
var rdnSubject pkix.RDNSequence
if opts.UseLiteralSubject && len(crt.Spec.LiteralSubject) > 0 {
subjectRDNSequence, err := UnmarshalSubjectStringToRDNSequence(crt.Spec.LiteralSubject)
if err != nil {
return nil, err
}
commonName = ExtractCommonNameFromRDNSequence(subjectRDNSequence)
rdnSubject = subjectRDNSequence
} else {
subject := SubjectForCertificate(crt)
commonName = crt.Spec.CommonName
rdnSubject = pkix.Name{
Country: subject.Countries,
Organization: subject.Organizations,
OrganizationalUnit: subject.OrganizationalUnits,
Locality: subject.Localities,
Province: subject.Provinces,
StreetAddress: subject.StreetAddresses,
PostalCode: subject.PostalCodes,
SerialNumber: subject.SerialNumber,
CommonName: commonName,
}.ToRDNSequence()
}
// Generate the SANs for the CSR.
ipAddresses, err := IPAddressesFromStrings(crt.Spec.IPAddresses)
if err != nil {
return nil, err
}
sans := GeneralNames{
RFC822Names: crt.Spec.EmailAddresses,
DNSNames: crt.Spec.DNSNames,
UniformResourceIdentifiers: crt.Spec.URIs,
IPAddresses: ipAddresses,
}
if opts.EncodeOtherNames {
for _, otherName := range crt.Spec.OtherNames {
oid, err := ParseObjectIdentifier(otherName.OID)
if err != nil {
return nil, err
}
value, err := MarshalUniversalValue(UniversalValue{
UTF8String: otherName.UTF8Value,
})
if err != nil {
return nil, err
}
sans.OtherNames = append(sans.OtherNames, OtherName{
TypeID: oid,
Value: asn1.RawValue{
Tag: 0,
Class: asn1.ClassContextSpecific,
IsCompound: true,
Bytes: value,
},
})
}
}
if len(commonName) == 0 && sans.Empty() {
return nil, fmt.Errorf("no common name (from the commonName field or from a literalSubject), DNS name, URI SAN, Email SAN, IP or OtherName SAN specified on certificate")
}
pubKeyAlgo, sigAlgo, err := SignatureAlgorithm(crt)
if err != nil {
return nil, err
}
asn1Subject, err := MarshalRDNSequenceToRawDERBytes(rdnSubject)
if err != nil {
return nil, err
}
var extraExtensions []pkix.Extension
if !sans.Empty() {
sanExtension, err := MarshalSANs(sans, !IsASN1SubjectEmpty(asn1Subject))
if err != nil {
return nil, err
}
extraExtensions = append(extraExtensions, sanExtension)
}
if crt.Spec.EncodeUsagesInRequest == nil || *crt.Spec.EncodeUsagesInRequest {
ku, ekus, err := KeyUsagesForCertificateOrCertificateRequest(crt.Spec.Usages, crt.Spec.IsCA)
if err != nil {
return nil, fmt.Errorf("failed to build key usages: %w", err)
}
if ku != 0 {
usage, err := MarshalKeyUsage(ku)
if err != nil {
return nil, fmt.Errorf("failed to asn1 encode usages: %w", err)
}
extraExtensions = append(extraExtensions, usage)
}
// Only add extended usages if they are specified.
if len(ekus) > 0 {
extendedUsages, err := MarshalExtKeyUsage(ekus, nil)
if err != nil {
return nil, fmt.Errorf("failed to asn1 encode extended usages: %w", err)
}
extraExtensions = append(extraExtensions, extendedUsages)
}
}
// NOTE(@inteon): opts.EncodeBasicConstraintsInRequest is a temporary solution and will
// be removed/ replaced in a future release.
if opts.EncodeBasicConstraintsInRequest {
basicExtension, err := MarshalBasicConstraints(crt.Spec.IsCA, nil)
if err != nil {
return nil, err
}
extraExtensions = append(extraExtensions, basicExtension)
}
if opts.EncodeNameConstraints && crt.Spec.NameConstraints != nil {
nameConstraints := &NameConstraints{}
if crt.Spec.NameConstraints.Permitted != nil {
nameConstraints.PermittedDNSDomains = crt.Spec.NameConstraints.Permitted.DNSDomains
nameConstraints.PermittedIPRanges, err = parseCIDRs(crt.Spec.NameConstraints.Permitted.IPRanges)
if err != nil {
return nil, err
}
nameConstraints.PermittedEmailAddresses = crt.Spec.NameConstraints.Permitted.EmailAddresses
nameConstraints.PermittedURIDomains = crt.Spec.NameConstraints.Permitted.URIDomains
}
if crt.Spec.NameConstraints.Excluded != nil {
nameConstraints.ExcludedDNSDomains = crt.Spec.NameConstraints.Excluded.DNSDomains
nameConstraints.ExcludedIPRanges, err = parseCIDRs(crt.Spec.NameConstraints.Excluded.IPRanges)
if err != nil {
return nil, err
}
nameConstraints.ExcludedEmailAddresses = crt.Spec.NameConstraints.Excluded.EmailAddresses
nameConstraints.ExcludedURIDomains = crt.Spec.NameConstraints.Excluded.URIDomains
}
if !nameConstraints.IsEmpty() {
extension, err := MarshalNameConstraints(nameConstraints, crt.Spec.NameConstraints.Critical)
if err != nil {
return nil, err
}
extraExtensions = append(extraExtensions, extension)
}
}
cr := &x509.CertificateRequest{
// Version 0 is the only one defined in the PKCS#10 standard, RFC2986.
// This value isn't used by Go at the time of writing.
// https://datatracker.ietf.org/doc/html/rfc2986#section-4
Version: 0,
SignatureAlgorithm: sigAlgo,
PublicKeyAlgorithm: pubKeyAlgo,
RawSubject: asn1Subject,
ExtraExtensions: extraExtensions,
}
return cr, nil
}
// SignCertificate returns a signed *x509.Certificate given a template
// *x509.Certificate crt and an issuer.
// publicKey is the public key of the signee, and signerKey is the private
// key of the signer.
// It returns a PEM encoded copy of the Certificate as well as a *x509.Certificate
// which can be used for reading the encoded values.
func SignCertificate(template *x509.Certificate, issuerCert *x509.Certificate, publicKey crypto.PublicKey, signerKey any) ([]byte, *x509.Certificate, error) {
typedSigner, ok := signerKey.(crypto.Signer)
if !ok {
return nil, nil, fmt.Errorf("didn't get an expected Signer in call to SignCertificate")
}
var pubKeyAlgo x509.PublicKeyAlgorithm
var sigAlgoArg any
// NB: can't rely on issuerCert.Public or issuercert.PublicKeyAlgorithm being set reliably;
// but we know that signerKey.Public() will work!
switch pubKey := typedSigner.Public().(type) {
case *rsa.PublicKey:
pubKeyAlgo = x509.RSA
// Size is in bytes so multiply by 8 to get bits because they're more familiar
// This is technically not portable but if you're using cert-manager on a platform
// with bytes that don't have 8 bits, you've got bigger problems than this!
sigAlgoArg = pubKey.Size() * 8
case *ecdsa.PublicKey:
pubKeyAlgo = x509.ECDSA
sigAlgoArg = pubKey.Curve
case ed25519.PublicKey:
pubKeyAlgo = x509.Ed25519
sigAlgoArg = nil // ignored by signatureAlgorithmFromPublicKey
default:
return nil, nil, fmt.Errorf("unknown public key type on signing certificate: %T", issuerCert.PublicKey)
}
var err error
template.SignatureAlgorithm, err = signatureAlgorithmFromPublicKey(pubKeyAlgo, sigAlgoArg)
if err != nil {
return nil, nil, err
}
derBytes, err := x509.CreateCertificate(rand.Reader, template, issuerCert, publicKey, signerKey)
if err != nil {
return nil, nil, fmt.Errorf("error creating x509 certificate: %s", err.Error())
}
cert, err := x509.ParseCertificate(derBytes)
if err != nil {
return nil, nil, fmt.Errorf("error decoding DER certificate bytes: %s", err.Error())
}
pemBytes := bytes.NewBuffer([]byte{})
err = pem.Encode(pemBytes, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
if err != nil {
return nil, nil, fmt.Errorf("error encoding certificate PEM: %s", err.Error())
}
return pemBytes.Bytes(), cert, err
}
// SignCSRTemplate signs a certificate template usually based upon a CSR. This
// function expects all fields to be present in the certificate template,
// including its public key.
// It returns the PEM bundle containing certificate data and the CA data, encoded in PEM format.
func SignCSRTemplate(caCerts []*x509.Certificate, caPrivateKey crypto.Signer, template *x509.Certificate) (PEMBundle, error) {
if len(caCerts) == 0 {
return PEMBundle{}, errors.New("no CA certificates given to sign CSR template")
}
issuingCACert := caCerts[0]
_, cert, err := SignCertificate(template, issuingCACert, template.PublicKey, caPrivateKey)
if err != nil {
return PEMBundle{}, err
}
bundle, err := ParseSingleCertificateChain(append(caCerts, cert))
if err != nil {
return PEMBundle{}, err
}
return bundle, nil
}
// EncodeCSR calls x509.CreateCertificateRequest to sign the given CSR template.
// It returns a DER encoded signed CSR.
func EncodeCSR(template *x509.CertificateRequest, key crypto.Signer) ([]byte, error) {
derBytes, err := x509.CreateCertificateRequest(rand.Reader, template, key)
if err != nil {
return nil, fmt.Errorf("error creating x509 certificate: %s", err.Error())
}
return derBytes, nil
}
// EncodeX509 will encode a single *x509.Certificate into PEM format.
func EncodeX509(cert *x509.Certificate) ([]byte, error) {
caPem := bytes.NewBuffer([]byte{})
err := pem.Encode(caPem, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw})
if err != nil {
return nil, err
}
return caPem.Bytes(), nil
}
// EncodeX509Chain will encode a list of *x509.Certificates into a PEM format chain.
// Self-signed certificates are not included as per
// https://datatracker.ietf.org/doc/html/rfc5246#section-7.4.2
// Certificates are output in the order they're given; if the input is not ordered
// as specified in RFC5246 section 7.4.2, the resulting chain might not be valid
// for use in TLS.
func EncodeX509Chain(certs []*x509.Certificate) ([]byte, error) {
caPem := bytes.NewBuffer([]byte{})
for _, cert := range certs {
if cert == nil {
continue
}
if cert.CheckSignatureFrom(cert) == nil {
// Don't include self-signed certificate
continue
}
err := pem.Encode(caPem, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw})
if err != nil {
return nil, err
}
}
return caPem.Bytes(), nil
}
var keyAlgorithms = map[v1.PrivateKeyAlgorithm]x509.PublicKeyAlgorithm{
v1.RSAKeyAlgorithm: x509.RSA,
v1.ECDSAKeyAlgorithm: x509.ECDSA,
v1.Ed25519KeyAlgorithm: x509.Ed25519,
}
var sigAlgorithms = map[v1.SignatureAlgorithm]x509.SignatureAlgorithm{
v1.SHA256WithRSA: x509.SHA256WithRSA,
v1.SHA384WithRSA: x509.SHA384WithRSA,
v1.SHA512WithRSA: x509.SHA512WithRSA,
v1.ECDSAWithSHA256: x509.ECDSAWithSHA256,
v1.ECDSAWithSHA384: x509.ECDSAWithSHA384,
v1.ECDSAWithSHA512: x509.ECDSAWithSHA512,
v1.PureEd25519: x509.PureEd25519,
}
// SignatureAlgorithm will determine the appropriate signature algorithm for
// the given certificate.
// Adapted from https://github.com/cloudflare/cfssl/blob/master/csr/csr.go#L102
func SignatureAlgorithm(crt *v1.Certificate) (x509.PublicKeyAlgorithm, x509.SignatureAlgorithm, error) {
var pubKeyAlgo x509.PublicKeyAlgorithm
var specAlgorithm v1.PrivateKeyAlgorithm
var specKeySize int
if crt.Spec.PrivateKey != nil {
specAlgorithm = crt.Spec.PrivateKey.Algorithm
specKeySize = crt.Spec.PrivateKey.Size
}
var sigAlgoArg any
var ok bool
if specAlgorithm == "" {
pubKeyAlgo = x509.RSA
} else {
pubKeyAlgo, ok = keyAlgorithms[specAlgorithm]
if !ok {
return x509.UnknownPublicKeyAlgorithm, x509.UnknownSignatureAlgorithm, fmt.Errorf("unsupported algorithm specified: %s. should be either 'ecdsa', 'ed25519' or 'rsa", crt.Spec.PrivateKey.Algorithm)
}
}
var sigAlgo x509.SignatureAlgorithm
if crt.Spec.SignatureAlgorithm != "" {
sigAlgo, ok = sigAlgorithms[crt.Spec.SignatureAlgorithm]
if !ok {
return x509.UnknownPublicKeyAlgorithm, x509.UnknownSignatureAlgorithm, fmt.Errorf("unsupported signature algorithm: %s", crt.Spec.SignatureAlgorithm)
}
return pubKeyAlgo, sigAlgo, nil
}
switch pubKeyAlgo {
case x509.RSA:
if specKeySize == 0 {
sigAlgoArg = MinRSAKeySize
} else {
sigAlgoArg = specKeySize
}
case x509.ECDSA:
switch specKeySize {
case 521:
sigAlgoArg = elliptic.P521()
case 384:
sigAlgoArg = elliptic.P384()
case 256, 0:
sigAlgoArg = elliptic.P256()
default:
return x509.UnknownPublicKeyAlgorithm, x509.UnknownSignatureAlgorithm, fmt.Errorf("unsupported ecdsa keysize specified: %d", crt.Spec.PrivateKey.Size)
}
}
sigAlgo, err := signatureAlgorithmFromPublicKey(pubKeyAlgo, sigAlgoArg)
if err != nil {
return x509.UnknownPublicKeyAlgorithm, x509.UnknownSignatureAlgorithm, err
}
return pubKeyAlgo, sigAlgo, nil
}
// signatureAlgorithmFromPublicKey takes a public key type and an argument specific to that public
// key, and returns an appropriate signature algorithm for that key.
// If alg is x509.RSA, arg must be an integer key size in bits
// If alg is x509.ECDSA, arg must be an elliptic.Curve
// If alg is x509.Ed25519, arg is ignored
// All other algorithms and args cause an error
// The signature algorithms returned by this function are to some degree a matter of preference. The
// choices here are motivated by what is common and what is required by bodies such as the US DoD.
func signatureAlgorithmFromPublicKey(alg x509.PublicKeyAlgorithm, arg any) (x509.SignatureAlgorithm, error) {
var signatureAlgorithm x509.SignatureAlgorithm
switch alg {
case x509.RSA:
size, ok := arg.(int)
if !ok {
return x509.UnknownSignatureAlgorithm, fmt.Errorf("expected to get an integer key size for RSA key but got %T", arg)
}
switch {
case size >= 4096:
signatureAlgorithm = x509.SHA512WithRSA
case size >= 3072:
signatureAlgorithm = x509.SHA384WithRSA
case size >= 2048:
signatureAlgorithm = x509.SHA256WithRSA
default:
return x509.UnknownSignatureAlgorithm, fmt.Errorf("invalid size %d for RSA key on signing certificate", size)
}
case x509.ECDSA:
curve, ok := arg.(elliptic.Curve)
if !ok {
return x509.UnknownSignatureAlgorithm, fmt.Errorf("expected to get an ECDSA curve for ECDSA key but got %T", arg)
}
switch curve {
case elliptic.P521():
signatureAlgorithm = x509.ECDSAWithSHA512
case elliptic.P384():
signatureAlgorithm = x509.ECDSAWithSHA384
case elliptic.P256():
signatureAlgorithm = x509.ECDSAWithSHA256
default:
return x509.UnknownSignatureAlgorithm, fmt.Errorf("unknown / unsupported curve attached to ECDSA signing certificate")
}
case x509.Ed25519:
signatureAlgorithm = x509.PureEd25519
default:
return x509.UnknownSignatureAlgorithm, fmt.Errorf("got unsupported public key type when trying to calculate signature algorithm")
}
return signatureAlgorithm, nil
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package pki
import (
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
v1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
)
const (
// MinRSAKeySize is the minimum RSA keysize allowed to be generated by the
// generator functions in this package.
MinRSAKeySize = 2048
// MaxRSAKeySize is the maximum RSA keysize allowed to be generated by the
// generator functions in this package.
MaxRSAKeySize = 8192
// ECCurve256 represents a secp256r1 / prime256v1 / NIST P-256 ECDSA key.
ECCurve256 = 256
// ECCurve384 represents a secp384r1 / NIST P-384 ECDSA key.
ECCurve384 = 384
// ECCurve521 represents a secp521r1 / NIST P-521 ECDSA key.
ECCurve521 = 521
)
// GeneratePrivateKeyForCertificate will generate a private key suitable for
// the provided cert-manager Certificate resource, taking into account the
// parameters on the provided resource.
// The returned key will either be RSA or ECDSA.
func GeneratePrivateKeyForCertificate(crt *v1.Certificate) (crypto.Signer, error) {
crt = crt.DeepCopy()
if crt.Spec.PrivateKey == nil {
crt.Spec.PrivateKey = &v1.CertificatePrivateKey{}
}
switch crt.Spec.PrivateKey.Algorithm {
case v1.PrivateKeyAlgorithm(""), v1.RSAKeyAlgorithm:
keySize := MinRSAKeySize
if crt.Spec.PrivateKey.Size > 0 {
keySize = crt.Spec.PrivateKey.Size
}
return GenerateRSAPrivateKey(keySize)
case v1.ECDSAKeyAlgorithm:
keySize := ECCurve256
if crt.Spec.PrivateKey.Size > 0 {
keySize = crt.Spec.PrivateKey.Size
}
return GenerateECPrivateKey(keySize)
case v1.Ed25519KeyAlgorithm:
return GenerateEd25519PrivateKey()
default:
return nil, fmt.Errorf("unsupported private key algorithm specified: %s", crt.Spec.PrivateKey.Algorithm)
}
}
// GenerateRSAPrivateKey will generate a RSA private key of the given size.
// It places restrictions on the minimum and maximum RSA keysize.
func GenerateRSAPrivateKey(keySize int) (*rsa.PrivateKey, error) {
// Do not allow keySize < 2048
// https://en.wikipedia.org/wiki/Key_size#cite_note-twirl-14
if keySize < MinRSAKeySize {
return nil, fmt.Errorf("weak rsa key size specified: %d. minimum key size: %d", keySize, MinRSAKeySize)
}
if keySize > MaxRSAKeySize {
return nil, fmt.Errorf("rsa key size specified too big: %d. maximum key size: %d", keySize, MaxRSAKeySize)
}
return rsa.GenerateKey(rand.Reader, keySize)
}
// GenerateECPrivateKey will generate an ECDSA private key of the given size.
// It can be used to generate 256, 384 and 521 sized keys.
func GenerateECPrivateKey(keySize int) (*ecdsa.PrivateKey, error) {
var ecCurve elliptic.Curve
switch keySize {
case ECCurve256:
ecCurve = elliptic.P256()
case ECCurve384:
ecCurve = elliptic.P384()
case ECCurve521:
ecCurve = elliptic.P521()
default:
return nil, fmt.Errorf("unsupported ecdsa key size specified: %d", keySize)
}
return ecdsa.GenerateKey(ecCurve, rand.Reader)
}
// GenerateEd25519PrivateKey will generate an Ed25519 private key
func GenerateEd25519PrivateKey() (ed25519.PrivateKey, error) {
_, prvkey, err := ed25519.GenerateKey(rand.Reader)
return prvkey, err
}
// EncodePrivateKey will encode a given crypto.PrivateKey by first inspecting
// the type of key encoding and then inspecting the type of key provided.
// It only supports encoding RSA or ECDSA keys.
func EncodePrivateKey(pk crypto.PrivateKey, keyEncoding v1.PrivateKeyEncoding) ([]byte, error) {
switch keyEncoding {
case v1.PrivateKeyEncoding(""), v1.PKCS1:
switch k := pk.(type) {
case *rsa.PrivateKey:
return EncodePKCS1PrivateKey(k), nil
case *ecdsa.PrivateKey:
return EncodeECPrivateKey(k)
case ed25519.PrivateKey:
return EncodePKCS8PrivateKey(k)
default:
return nil, fmt.Errorf("error encoding private key: unknown key type: %T", pk)
}
case v1.PKCS8:
return EncodePKCS8PrivateKey(pk)
default:
return nil, fmt.Errorf("error encoding private key: unknown key encoding: %s", keyEncoding)
}
}
// EncodePKCS1PrivateKey will marshal a RSA private key into x509 PEM format.
func EncodePKCS1PrivateKey(pk *rsa.PrivateKey) []byte {
block := &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(pk)}
return pem.EncodeToMemory(block)
}
// EncodePKCS8PrivateKey will marshal a private key into x509 PEM format.
func EncodePKCS8PrivateKey(pk interface{}) ([]byte, error) {
keyBytes, err := x509.MarshalPKCS8PrivateKey(pk)
if err != nil {
return nil, err
}
block := &pem.Block{Type: "PRIVATE KEY", Bytes: keyBytes}
return pem.EncodeToMemory(block), nil
}
// EncodeECPrivateKey will marshal an ECDSA private key into x509 PEM format.
func EncodeECPrivateKey(pk *ecdsa.PrivateKey) ([]byte, error) {
asnBytes, err := x509.MarshalECPrivateKey(pk)
if err != nil {
return nil, fmt.Errorf("error encoding private key: %s", err.Error())
}
block := &pem.Block{Type: "EC PRIVATE KEY", Bytes: asnBytes}
return pem.EncodeToMemory(block), nil
}
// PublicKeyForPrivateKey will return the crypto.PublicKey for the given
// crypto.PrivateKey. It only supports RSA and ECDSA keys.
func PublicKeyForPrivateKey(pk crypto.PrivateKey) (crypto.PublicKey, error) {
switch k := pk.(type) {
case *rsa.PrivateKey:
return k.Public(), nil
case *ecdsa.PrivateKey:
return k.Public(), nil
case ed25519.PrivateKey:
return k.Public(), nil
default:
return nil, fmt.Errorf("unknown private key type: %T", pk)
}
}
// PublicKeyMatchesCertificate checks whether the given public key matches the
// public key in the given x509.Certificate.
// Returns false and no error if the public key is *not* the same as the certificate's key
// Returns true and no error if the public key *is* the same as the certificate's key
// Returns an error if the certificate's key type cannot be determined (i.e. non RSA/ECDSA keys)
func PublicKeyMatchesCertificate(check crypto.PublicKey, crt *x509.Certificate) (bool, error) {
return PublicKeysEqual(crt.PublicKey, check)
}
// PublicKeyMatchesCSR can be used to verify the given public key matches the
// public key in the given x509.CertificateRequest.
// Returns false and no error if the given public key is *not* the same as the CSR's key
// Returns true and no error if the given public key *is* the same as the CSR's key
// Returns an error if the CSR's key type cannot be determined (i.e. non RSA/ECDSA keys)
func PublicKeyMatchesCSR(check crypto.PublicKey, csr *x509.CertificateRequest) (bool, error) {
return PublicKeysEqual(csr.PublicKey, check)
}
// PublicKeysEqual compares two given public keys for equality.
// The definition of "equality" depends on the type of the public keys.
// Returns true if the keys are the same, false if they differ or an error if
// the key type of `a` cannot be determined.
func PublicKeysEqual(a, b crypto.PublicKey) (bool, error) {
switch pub := a.(type) {
case *rsa.PublicKey:
return pub.Equal(b), nil
case *ecdsa.PublicKey:
return pub.Equal(b), nil
case ed25519.PublicKey:
return pub.Equal(b), nil
default:
return false, fmt.Errorf("unrecognised public key type: %T", a)
}
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package pki
import (
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"errors"
)
// Copied from x509.go
var (
OIDExtensionKeyUsage = []int{2, 5, 29, 15}
OIDExtensionExtendedKeyUsage = []int{2, 5, 29, 37}
)
// RFC 5280, 4.2.1.12 Extended Key Usage
//
// anyExtendedKeyUsage OBJECT IDENTIFIER ::= { id-ce-extKeyUsage 0 }
//
// id-kp OBJECT IDENTIFIER ::= { id-pkix 3 }
//
// id-kp-serverAuth OBJECT IDENTIFIER ::= { id-kp 1 }
// id-kp-clientAuth OBJECT IDENTIFIER ::= { id-kp 2 }
// id-kp-codeSigning OBJECT IDENTIFIER ::= { id-kp 3 }
// id-kp-emailProtection OBJECT IDENTIFIER ::= { id-kp 4 }
// id-kp-timeStamping OBJECT IDENTIFIER ::= { id-kp 8 }
// id-kp-OCSPSigning OBJECT IDENTIFIER ::= { id-kp 9 }
var (
oidExtKeyUsageAny = asn1.ObjectIdentifier{2, 5, 29, 37, 0}
oidExtKeyUsageServerAuth = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 1}
oidExtKeyUsageClientAuth = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 2}
oidExtKeyUsageCodeSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 3}
oidExtKeyUsageEmailProtection = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 4}
oidExtKeyUsageIPSECEndSystem = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 5}
oidExtKeyUsageIPSECTunnel = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 6}
oidExtKeyUsageIPSECUser = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 7}
oidExtKeyUsageTimeStamping = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 8}
oidExtKeyUsageOCSPSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 9}
oidExtKeyUsageMicrosoftServerGatedCrypto = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 10, 3, 3}
oidExtKeyUsageNetscapeServerGatedCrypto = asn1.ObjectIdentifier{2, 16, 840, 1, 113730, 4, 1}
oidExtKeyUsageMicrosoftCommercialCodeSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 2, 1, 22}
oidExtKeyUsageMicrosoftKernelCodeSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 61, 1, 1}
)
// extKeyUsageOIDs contains the mapping between an ExtKeyUsage and its OID.
var extKeyUsageOIDs = []struct {
extKeyUsage x509.ExtKeyUsage
oid asn1.ObjectIdentifier
}{
{x509.ExtKeyUsageAny, oidExtKeyUsageAny},
{x509.ExtKeyUsageServerAuth, oidExtKeyUsageServerAuth},
{x509.ExtKeyUsageClientAuth, oidExtKeyUsageClientAuth},
{x509.ExtKeyUsageCodeSigning, oidExtKeyUsageCodeSigning},
{x509.ExtKeyUsageEmailProtection, oidExtKeyUsageEmailProtection},
{x509.ExtKeyUsageIPSECEndSystem, oidExtKeyUsageIPSECEndSystem},
{x509.ExtKeyUsageIPSECTunnel, oidExtKeyUsageIPSECTunnel},
{x509.ExtKeyUsageIPSECUser, oidExtKeyUsageIPSECUser},
{x509.ExtKeyUsageTimeStamping, oidExtKeyUsageTimeStamping},
{x509.ExtKeyUsageOCSPSigning, oidExtKeyUsageOCSPSigning},
{x509.ExtKeyUsageMicrosoftServerGatedCrypto, oidExtKeyUsageMicrosoftServerGatedCrypto},
{x509.ExtKeyUsageNetscapeServerGatedCrypto, oidExtKeyUsageNetscapeServerGatedCrypto},
{x509.ExtKeyUsageMicrosoftCommercialCodeSigning, oidExtKeyUsageMicrosoftCommercialCodeSigning},
{x509.ExtKeyUsageMicrosoftKernelCodeSigning, oidExtKeyUsageMicrosoftKernelCodeSigning},
}
// OIDFromExtKeyUsage returns the ASN1 Identifier for a x509.ExtKeyUsage
func OIDFromExtKeyUsage(eku x509.ExtKeyUsage) (oid asn1.ObjectIdentifier, ok bool) {
for _, pair := range extKeyUsageOIDs {
if eku == pair.extKeyUsage {
return pair.oid, true
}
}
return
}
func ExtKeyUsageFromOID(oid asn1.ObjectIdentifier) (eku x509.ExtKeyUsage, ok bool) {
for _, pair := range extKeyUsageOIDs {
if oid.Equal(pair.oid) {
return pair.extKeyUsage, true
}
}
return
}
// asn1BitLength returns the bit-length of bitString by considering the
// most-significant bit in a byte to be the "first" bit. This convention
// matches ASN.1, but differs from almost everything else.
func asn1BitLength(bitString []byte) int {
bitLen := len(bitString) * 8
for i := range bitString {
b := bitString[len(bitString)-i-1]
for bit := range uint(8) {
if (b>>bit)&1 == 1 {
return bitLen
}
bitLen--
}
}
return 0
}
// Copied from x509.go
func reverseBitsInAByte(in byte) byte {
b1 := in>>4 | in<<4
b2 := b1>>2&0x33 | b1<<2&0xcc
b3 := b2>>1&0x55 | b2<<1&0xaa
return b3
}
// Adapted from x509.go
func MarshalKeyUsage(usage x509.KeyUsage) (pkix.Extension, error) {
ext := pkix.Extension{Id: OIDExtensionKeyUsage, Critical: true}
var a [2]byte
a[0] = reverseBitsInAByte(byte(usage))
a[1] = reverseBitsInAByte(byte(usage >> 8))
l := 1
if a[1] != 0 {
l = 2
}
bitString := a[:l]
var err error
ext.Value, err = asn1.Marshal(asn1.BitString{Bytes: bitString, BitLength: asn1BitLength(bitString)})
return ext, err
}
func UnmarshalKeyUsage(value []byte) (usage x509.KeyUsage, err error) {
var asn1bits asn1.BitString
var rest []byte
if rest, err = asn1.Unmarshal(value, &asn1bits); err != nil {
return usage, err
} else if len(rest) != 0 {
return usage, errors.New("x509: trailing data after X.509 KeyUsage")
}
var usageInt int
for i := range 9 {
if asn1bits.At(i) != 0 {
usageInt |= 1 << uint(i) // #nosec G115 -- gosec can somehow not detect that this is safe
}
}
return x509.KeyUsage(usageInt), nil
}
// Adapted from x509.go
func MarshalExtKeyUsage(extUsages []x509.ExtKeyUsage, unknownUsages []asn1.ObjectIdentifier) (pkix.Extension, error) {
ext := pkix.Extension{Id: OIDExtensionExtendedKeyUsage}
oids := make([]asn1.ObjectIdentifier, len(extUsages)+len(unknownUsages))
for i, u := range extUsages {
if oid, ok := OIDFromExtKeyUsage(u); ok {
oids[i] = oid
} else {
return ext, errors.New("x509: unknown extended key usage")
}
}
copy(oids[len(extUsages):], unknownUsages)
var err error
ext.Value, err = asn1.Marshal(oids)
return ext, err
}
func UnmarshalExtKeyUsage(value []byte) (extUsages []x509.ExtKeyUsage, unknownUsages []asn1.ObjectIdentifier, err error) {
var asn1ExtendedUsages []asn1.ObjectIdentifier
var rest []byte
if rest, err = asn1.Unmarshal(value, &asn1ExtendedUsages); err != nil {
return extUsages, unknownUsages, err
} else if len(rest) != 0 {
return extUsages, unknownUsages, errors.New("x509: trailing data after X.509 ExtendedKeyUsage")
}
for _, asnExtUsage := range asn1ExtendedUsages {
if eku, ok := ExtKeyUsageFromOID(asnExtUsage); ok {
extUsages = append(extUsages, eku)
} else {
unknownUsages = append(unknownUsages, asnExtUsage)
}
}
return extUsages, unknownUsages, nil
}
/*
Copyright 2021 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package pki
import (
"crypto/x509"
"fmt"
"time"
certificatesv1 "k8s.io/api/certificates/v1"
apiutil "github.com/cert-manager/cert-manager/pkg/api/util"
cmapi "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
experimentalapi "github.com/cert-manager/cert-manager/pkg/apis/experimental/v1alpha1"
)
// DurationFromCertificateSigningRequest returns the duration that the user may
// have requested using the annotation
// "experimental.cert-manager.io/request-duration" or via the CSR
// spec.expirationSeconds field (the annotation is preferred since it predates
// the field which is only available in Kubernetes v1.22+).
// Returns the cert-manager default certificate duration when the user hasn't
// provided the annotation or spec.expirationSeconds.
func DurationFromCertificateSigningRequest(csr *certificatesv1.CertificateSigningRequest) (time.Duration, error) {
requestedDuration, ok := csr.Annotations[experimentalapi.CertificateSigningRequestDurationAnnotationKey]
if !ok {
if csr.Spec.ExpirationSeconds != nil {
return time.Duration(*csr.Spec.ExpirationSeconds) * time.Second, nil
}
// The user may not have set a duration annotation. Use the default
// duration in this case.
return cmapi.DefaultCertificateDuration, nil
}
duration, err := time.ParseDuration(requestedDuration)
if err != nil {
return -1, fmt.Errorf("failed to parse requested duration on annotation %q: %w",
experimentalapi.CertificateSigningRequestDurationAnnotationKey, err)
}
return duration, nil
}
// BuildKeyUsagesKube returns a key usage and extended key usage of the x509 certificate
func BuildKeyUsagesKube(usages []certificatesv1.KeyUsage) (x509.KeyUsage, []x509.ExtKeyUsage, error) {
var unk []certificatesv1.KeyUsage
if len(usages) == 0 {
usages = []certificatesv1.KeyUsage{certificatesv1.UsageDigitalSignature, certificatesv1.UsageKeyEncipherment}
}
var (
ku x509.KeyUsage
eku []x509.ExtKeyUsage
)
for _, u := range usages {
if kuse, ok := apiutil.KeyUsageTypeKube(u); ok {
ku |= kuse
} else if ekuse, ok := apiutil.ExtKeyUsageTypeKube(u); ok {
eku = append(eku, ekuse)
} else {
unk = append(unk, u)
}
}
if len(unk) > 0 {
return -1, nil, fmt.Errorf("unknown key usages: %v", unk)
}
return ku, eku, nil
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package pki
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"fmt"
"net"
"reflect"
"k8s.io/apimachinery/pkg/util/sets"
cmapi "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
"github.com/cert-manager/cert-manager/pkg/util"
)
// PrivateKeyMatchesSpec returns a list of violations for the provided private
// key against the provided CertificateSpec. It will return an empty list/ nil
// if there are no violations found. RSA, Ed25519 and ECDSA private keys are
// supported.
// The function panics if the CertificateSpec contains an unknown key algorithm,
// since this should have been caught by the CertificateSpec validation already.
func PrivateKeyMatchesSpec(pk crypto.PrivateKey, spec cmapi.CertificateSpec) []string {
spec = *spec.DeepCopy()
if spec.PrivateKey == nil {
spec.PrivateKey = &cmapi.CertificatePrivateKey{}
}
switch spec.PrivateKey.Algorithm {
case "", cmapi.RSAKeyAlgorithm:
return rsaPrivateKeyMatchesSpec(pk, spec)
case cmapi.Ed25519KeyAlgorithm:
return ed25519PrivateKeyMatchesSpec(pk)
case cmapi.ECDSAKeyAlgorithm:
return ecdsaPrivateKeyMatchesSpec(pk, spec)
default:
// This should never happen as the CertificateSpec validation should
// catch this before it reaches this point.
panic(fmt.Sprintf("[PROGRAMMING ERROR] unrecognised key algorithm type %q", spec.PrivateKey.Algorithm))
}
}
func rsaPrivateKeyMatchesSpec(pk crypto.PrivateKey, spec cmapi.CertificateSpec) []string {
rsaPk, ok := pk.(*rsa.PrivateKey)
if !ok {
return []string{"spec.privateKey.algorithm"}
}
var violations []string
// TODO: we should not use implicit defaulting here, and instead rely on
// defaulting performed within the Kubernetes apiserver here.
// This requires careful handling in order to not interrupt users upgrading
// from older versions.
// The default RSA keySize is set to 2048.
keySize := MinRSAKeySize
if spec.PrivateKey.Size > 0 {
keySize = spec.PrivateKey.Size
}
if rsaPk.N.BitLen() != keySize {
violations = append(violations, "spec.privateKey.size")
}
return violations
}
func ecdsaPrivateKeyMatchesSpec(pk crypto.PrivateKey, spec cmapi.CertificateSpec) []string {
ecdsaPk, ok := pk.(*ecdsa.PrivateKey)
if !ok {
return []string{"spec.privateKey.algorithm"}
}
var violations []string
// TODO: we should not use implicit defaulting here, and instead rely on
// defaulting performed within the Kubernetes apiserver here.
// This requires careful handling in order to not interrupt users upgrading
// from older versions.
// The default EC curve type is EC256
expectedKeySize := ECCurve256
if spec.PrivateKey.Size > 0 {
expectedKeySize = spec.PrivateKey.Size
}
if expectedKeySize != ecdsaPk.Curve.Params().BitSize {
violations = append(violations, "spec.privateKey.size")
}
return violations
}
func ed25519PrivateKeyMatchesSpec(pk crypto.PrivateKey) []string {
_, ok := pk.(ed25519.PrivateKey)
if !ok {
return []string{"spec.privateKey.algorithm"}
}
return nil
}
func ipSlicesMatch(parsedIPs []net.IP, stringIPs []string) bool {
parsedStringIPs := make([]net.IP, len(stringIPs))
for i, s := range stringIPs {
parsedStringIPs[i] = net.ParseIP(s)
}
return util.EqualIPsUnsorted(parsedStringIPs, parsedIPs)
}
// RequestMatchesSpec compares a CertificateRequest with a CertificateSpec
// and returns a list of field names on the Certificate that do not match their
// counterpart fields on the CertificateRequest.
// If decoding the x509 certificate request fails, an error will be returned.
func RequestMatchesSpec(req *cmapi.CertificateRequest, spec cmapi.CertificateSpec) ([]string, error) {
x509req, err := DecodeX509CertificateRequestBytes(req.Spec.Request)
if err != nil {
return nil, err
}
// It is safe to mutate top-level fields in `spec` as it is not a pointer
// meaning changes will not affect the caller.
if spec.Subject == nil {
spec.Subject = &cmapi.X509Subject{}
}
var violations []string
if !ipSlicesMatch(x509req.IPAddresses, spec.IPAddresses) {
violations = append(violations, "spec.ipAddresses")
}
if !util.EqualUnsorted(URLsToString(x509req.URIs), spec.URIs) {
violations = append(violations, "spec.uris")
}
if !util.EqualUnsorted(x509req.EmailAddresses, spec.EmailAddresses) {
violations = append(violations, "spec.emailAddresses")
}
if !util.EqualUnsorted(x509req.DNSNames, spec.DNSNames) {
violations = append(violations, "spec.dnsNames")
}
if spec.OtherNames != nil {
matched, err := matchOtherNames(x509req.Extensions, spec.OtherNames)
if err != nil {
return nil, err
}
if !matched {
violations = append(violations, "spec.otherNames")
}
}
if spec.LiteralSubject == "" {
// Comparing Subject fields
if x509req.Subject.CommonName != spec.CommonName {
violations = append(violations, "spec.commonName")
}
if x509req.Subject.SerialNumber != spec.Subject.SerialNumber {
violations = append(violations, "spec.subject.serialNumber")
}
if !util.EqualUnsorted(x509req.Subject.Organization, spec.Subject.Organizations) {
violations = append(violations, "spec.subject.organizations")
}
if !util.EqualUnsorted(x509req.Subject.Country, spec.Subject.Countries) {
violations = append(violations, "spec.subject.countries")
}
if !util.EqualUnsorted(x509req.Subject.Locality, spec.Subject.Localities) {
violations = append(violations, "spec.subject.localities")
}
if !util.EqualUnsorted(x509req.Subject.OrganizationalUnit, spec.Subject.OrganizationalUnits) {
violations = append(violations, "spec.subject.organizationalUnits")
}
if !util.EqualUnsorted(x509req.Subject.PostalCode, spec.Subject.PostalCodes) {
violations = append(violations, "spec.subject.postCodes")
}
if !util.EqualUnsorted(x509req.Subject.Province, spec.Subject.Provinces) {
violations = append(violations, "spec.subject.postCodes")
}
if !util.EqualUnsorted(x509req.Subject.StreetAddress, spec.Subject.StreetAddresses) {
violations = append(violations, "spec.subject.streetAddresses")
}
} else {
// we have a LiteralSubject, generate the RDNSequence and encode it to compare
// with the request's subject
rdnSequenceFromCertificate, err := UnmarshalSubjectStringToRDNSequence(spec.LiteralSubject)
if err != nil {
return nil, err
}
asn1Sequence, err := asn1.Marshal(rdnSequenceFromCertificate)
if err != nil {
return nil, err
}
if !bytes.Equal(x509req.RawSubject, asn1Sequence) {
violations = append(violations, "spec.literalSubject")
}
}
if req.Spec.IsCA != spec.IsCA {
violations = append(violations, "spec.isCA")
}
if !util.EqualKeyUsagesUnsorted(req.Spec.Usages, spec.Usages) {
violations = append(violations, "spec.usages")
}
if req.Spec.Duration != nil && spec.Duration != nil &&
req.Spec.Duration.Duration != spec.Duration.Duration {
violations = append(violations, "spec.duration")
}
if !reflect.DeepEqual(req.Spec.IssuerRef, spec.IssuerRef) {
violations = append(violations, "spec.issuerRef")
}
// TODO: check spec.EncodeBasicConstraintsInRequest and spec.EncodeUsagesInRequest
return violations, nil
}
func matchOtherNames(extension []pkix.Extension, specOtherNames []cmapi.OtherName) (bool, error) {
x509SANExtension, err := extractSANExtension(extension)
if err != nil {
return false, nil
}
x509GeneralNames, err := UnmarshalSANs(x509SANExtension.Value)
if err != nil {
return false, err
}
x509OtherNames := make([]cmapi.OtherName, 0, len(x509GeneralNames.OtherNames))
for _, otherName := range x509GeneralNames.OtherNames {
var otherNameInnerValue asn1.RawValue
// We have to perform one more level of unwrapping because value is still context specific class
// tagged 0
_, err := asn1.Unmarshal(otherName.Value.Bytes, &otherNameInnerValue)
if err != nil {
return false, err
}
uv, err := UnmarshalUniversalValue(otherNameInnerValue)
if err != nil {
return false, err
}
if uv.Type() != UniversalValueTypeUTF8String {
// This means the CertificateRequest's otherName was not an utf8 value
return false, fmt.Errorf("otherName is not an utf8 value, got: %v", uv.Type())
}
x509OtherNames = append(x509OtherNames, cmapi.OtherName{
OID: otherName.TypeID.String(),
UTF8Value: uv.UTF8String,
})
}
if !util.EqualOtherNamesUnsorted(x509OtherNames, specOtherNames) {
return false, nil
}
return true, nil
}
// FuzzyX509AltNamesMatchSpec will compare a X509 Certificate to a CertificateSpec
// and return a list of 'violations' for any fields that do not match their counterparts.
//
// This is a purposely less comprehensive check than RequestMatchesSpec as some
// issuers override/force certain fields.
//
// Deprecated: This function is very fuzzy and makes too many assumptions about
// how the issuer maps a CSR to a certificate. We only keep it for backward compatibility
// reasons, but use other comparison functions when possible.
func FuzzyX509AltNamesMatchSpec(x509cert *x509.Certificate, spec cmapi.CertificateSpec) []string {
var violations []string
// Perform a 'loose' check on the x509 certificate to determine if the
// commonName and dnsNames fields are up to date.
// This check allows names to move between the DNSNames and CommonName
// field freely in order to account for CAs behaviour of promoting DNSNames
// to be CommonNames or vice-versa.
expectedDNSNames := sets.New(spec.DNSNames...)
if spec.CommonName != "" {
expectedDNSNames.Insert(spec.CommonName)
}
allDNSNames := sets.New(x509cert.DNSNames...)
if x509cert.Subject.CommonName != "" {
allDNSNames.Insert(x509cert.Subject.CommonName)
}
if !allDNSNames.Equal(expectedDNSNames) {
// We know a mismatch occurred, so now determine which fields mismatched.
if (spec.CommonName != "" && !allDNSNames.Has(spec.CommonName)) || (x509cert.Subject.CommonName != "" && !expectedDNSNames.Has(x509cert.Subject.CommonName)) {
violations = append(violations, "spec.commonName")
}
if !allDNSNames.HasAll(spec.DNSNames...) || !expectedDNSNames.HasAll(x509cert.DNSNames...) {
violations = append(violations, "spec.dnsNames")
}
}
if !ipSlicesMatch(x509cert.IPAddresses, spec.IPAddresses) {
violations = append(violations, "spec.ipAddresses")
}
if !util.EqualUnsorted(URLsToString(x509cert.URIs), spec.URIs) {
violations = append(violations, "spec.uris")
}
if !util.EqualUnsorted(x509cert.EmailAddresses, spec.EmailAddresses) {
violations = append(violations, "spec.emailAddresses")
}
return violations
}
func extractSANExtension(extensions []pkix.Extension) (pkix.Extension, error) {
oidExtensionSubjectAltName := []int{2, 5, 29, 17}
for _, extension := range extensions {
if extension.Id.Equal(oidExtensionSubjectAltName) {
return extension, nil
}
}
return pkix.Extension{}, fmt.Errorf("SAN extension not present!")
}
/*
Copyright 2023 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package pki
import (
"crypto/x509/pkix"
"errors"
"fmt"
"net"
"golang.org/x/crypto/cryptobyte"
cryptobyte_asn1 "golang.org/x/crypto/cryptobyte/asn1"
)
// Copied from x509.go
var (
OIDExtensionNameConstraints = []int{2, 5, 29, 30}
)
// NameConstraints represents the NameConstraints extension.
type NameConstraints struct {
PermittedDNSDomains []string
ExcludedDNSDomains []string
PermittedIPRanges []*net.IPNet
ExcludedIPRanges []*net.IPNet
PermittedEmailAddresses []string
ExcludedEmailAddresses []string
PermittedURIDomains []string
ExcludedURIDomains []string
}
func (nc NameConstraints) IsEmpty() bool {
return len(nc.PermittedDNSDomains) == 0 &&
len(nc.PermittedIPRanges) == 0 &&
len(nc.PermittedEmailAddresses) == 0 &&
len(nc.PermittedURIDomains) == 0 &&
len(nc.ExcludedDNSDomains) == 0 &&
len(nc.ExcludedIPRanges) == 0 &&
len(nc.ExcludedEmailAddresses) == 0 &&
len(nc.ExcludedURIDomains) == 0
}
// Adapted from x509.go
func MarshalNameConstraints(nameConstraints *NameConstraints, critical bool) (pkix.Extension, error) {
ipAndMask := func(ipNet *net.IPNet) []byte {
maskedIP := ipNet.IP.Mask(ipNet.Mask)
ipAndMask := make([]byte, 0, len(maskedIP)+len(ipNet.Mask))
ipAndMask = append(ipAndMask, maskedIP...)
ipAndMask = append(ipAndMask, ipNet.Mask...)
return ipAndMask
}
serialiseConstraints := func(dns []string, ips []*net.IPNet, emails []string, uriDomains []string) (der []byte, err error) {
var b cryptobyte.Builder
for _, name := range dns {
if err = isIA5String(name); err != nil {
return nil, err
}
b.AddASN1(cryptobyte_asn1.SEQUENCE, func(b *cryptobyte.Builder) {
b.AddASN1(cryptobyte_asn1.Tag(2).ContextSpecific(), func(b *cryptobyte.Builder) {
b.AddBytes([]byte(name))
})
})
}
for _, ipNet := range ips {
b.AddASN1(cryptobyte_asn1.SEQUENCE, func(b *cryptobyte.Builder) {
b.AddASN1(cryptobyte_asn1.Tag(7).ContextSpecific(), func(b *cryptobyte.Builder) {
b.AddBytes(ipAndMask(ipNet))
})
})
}
for _, email := range emails {
if err = isIA5String(email); err != nil {
return nil, err
}
b.AddASN1(cryptobyte_asn1.SEQUENCE, func(b *cryptobyte.Builder) {
b.AddASN1(cryptobyte_asn1.Tag(1).ContextSpecific(), func(b *cryptobyte.Builder) {
b.AddBytes([]byte(email))
})
})
}
for _, uriDomain := range uriDomains {
if err = isIA5String(uriDomain); err != nil {
return nil, err
}
b.AddASN1(cryptobyte_asn1.SEQUENCE, func(b *cryptobyte.Builder) {
b.AddASN1(cryptobyte_asn1.Tag(6).ContextSpecific(), func(b *cryptobyte.Builder) {
b.AddBytes([]byte(uriDomain))
})
})
}
return b.Bytes()
}
var permitted []byte
var err error
permitted, err = serialiseConstraints(nameConstraints.PermittedDNSDomains, nameConstraints.PermittedIPRanges, nameConstraints.PermittedEmailAddresses, nameConstraints.PermittedURIDomains)
if err != nil {
return pkix.Extension{}, err
}
var excluded []byte
excluded, err = serialiseConstraints(nameConstraints.ExcludedDNSDomains, nameConstraints.ExcludedIPRanges, nameConstraints.ExcludedEmailAddresses, nameConstraints.ExcludedURIDomains)
if err != nil {
return pkix.Extension{}, err
}
var b cryptobyte.Builder
b.AddASN1(cryptobyte_asn1.SEQUENCE, func(b *cryptobyte.Builder) {
if len(permitted) > 0 {
b.AddASN1(cryptobyte_asn1.Tag(0).ContextSpecific().Constructed(), func(b *cryptobyte.Builder) {
b.AddBytes(permitted)
})
}
if len(excluded) > 0 {
b.AddASN1(cryptobyte_asn1.Tag(1).ContextSpecific().Constructed(), func(b *cryptobyte.Builder) {
b.AddBytes(excluded)
})
}
})
bytes, err := b.Bytes()
if err != nil {
return pkix.Extension{}, err
}
return pkix.Extension{
Id: OIDExtensionNameConstraints,
Critical: critical,
Value: bytes,
}, nil
}
func parseCIDRs(cidrs []string) ([]*net.IPNet, error) {
ipRanges := []*net.IPNet{}
for _, cidr := range cidrs {
_, ipNet, err := net.ParseCIDR(cidr)
if err != nil {
return nil, err
}
ipRanges = append(ipRanges, &net.IPNet{
IP: ipNet.IP,
Mask: ipNet.Mask,
})
}
return ipRanges, nil
}
// Adapted from crypto/x509/parser.go
func UnmarshalNameConstraints(value []byte) (*NameConstraints, error) {
// RFC 5280, 4.2.1.10
// NameConstraints ::= SEQUENCE {
// permittedSubtrees [0] GeneralSubtrees OPTIONAL,
// excludedSubtrees [1] GeneralSubtrees OPTIONAL }
//
// GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
//
// GeneralSubtree ::= SEQUENCE {
// base GeneralName,
// minimum [0] BaseDistance DEFAULT 0,
// maximum [1] BaseDistance OPTIONAL }
//
// BaseDistance ::= INTEGER (0..MAX)
outer := cryptobyte.String(value)
var toplevel, permitted, excluded cryptobyte.String
var havePermitted, haveExcluded bool
if !outer.ReadASN1(&toplevel, cryptobyte_asn1.SEQUENCE) ||
!outer.Empty() ||
!toplevel.ReadOptionalASN1(&permitted, &havePermitted, cryptobyte_asn1.Tag(0).ContextSpecific().Constructed()) ||
!toplevel.ReadOptionalASN1(&excluded, &haveExcluded, cryptobyte_asn1.Tag(1).ContextSpecific().Constructed()) ||
!toplevel.Empty() {
return nil, errors.New("x509: invalid NameConstraints extension")
}
if !havePermitted && !haveExcluded || len(permitted) == 0 && len(excluded) == 0 {
// From RFC 5280, Section 4.2.1.10:
// “either the permittedSubtrees field
// or the excludedSubtrees MUST be
// present”
return nil, errors.New("x509: empty name constraints extension")
}
getValues := func(subtrees cryptobyte.String) (dnsNames []string, ips []*net.IPNet, emails, uriDomains []string, err error) {
for !subtrees.Empty() {
var seq, value cryptobyte.String
var tag cryptobyte_asn1.Tag
if !subtrees.ReadASN1(&seq, cryptobyte_asn1.SEQUENCE) ||
!seq.ReadAnyASN1(&value, &tag) {
return nil, nil, nil, nil, fmt.Errorf("x509: invalid NameConstraints extension")
}
var (
dnsTag = cryptobyte_asn1.Tag(2).ContextSpecific()
emailTag = cryptobyte_asn1.Tag(1).ContextSpecific()
ipTag = cryptobyte_asn1.Tag(7).ContextSpecific()
uriTag = cryptobyte_asn1.Tag(6).ContextSpecific()
)
switch tag {
case dnsTag:
domain := string(value)
if err := isIA5String(domain); err != nil {
return nil, nil, nil, nil, errors.New("x509: invalid constraint value: " + err.Error())
}
dnsNames = append(dnsNames, domain)
case ipTag:
l := len(value)
var ip, mask []byte
switch l {
case 2 * net.IPv4len:
ip = value[:net.IPv4len]
mask = value[net.IPv4len:]
case 2 * net.IPv6len:
ip = value[:net.IPv6len]
mask = value[net.IPv6len:]
default:
return nil, nil, nil, nil, fmt.Errorf("x509: IP constraint contained value of length %d", l)
}
if !isValidIPMask(mask) {
return nil, nil, nil, nil, fmt.Errorf("x509: IP constraint contained invalid mask %x", mask)
}
ips = append(ips, &net.IPNet{IP: net.IP(ip), Mask: net.IPMask(mask)})
case emailTag:
constraint := string(value)
if err := isIA5String(constraint); err != nil {
return nil, nil, nil, nil, errors.New("x509: invalid constraint value: " + err.Error())
}
emails = append(emails, constraint)
case uriTag:
domain := string(value)
if err := isIA5String(domain); err != nil {
return nil, nil, nil, nil, errors.New("x509: invalid constraint value: " + err.Error())
}
uriDomains = append(uriDomains, domain)
}
}
return dnsNames, ips, emails, uriDomains, nil
}
out := &NameConstraints{}
var err error
if out.PermittedDNSDomains, out.PermittedIPRanges, out.PermittedEmailAddresses, out.PermittedURIDomains, err = getValues(permitted); err != nil {
return nil, err
}
if out.ExcludedDNSDomains, out.ExcludedIPRanges, out.ExcludedEmailAddresses, out.ExcludedURIDomains, err = getValues(excluded); err != nil {
return nil, err
}
return out, nil
}
// isValidIPMask reports whether mask consists of zero or more 1 bits, followed by zero bits.
func isValidIPMask(mask []byte) bool {
seenZero := false
for _, b := range mask {
if seenZero {
if b != 0 {
return false
}
continue
}
switch b {
case 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe:
seenZero = true
case 0xff:
default:
return false
}
}
return true
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package pki
import (
"crypto"
"crypto/x509"
stdpem "encoding/pem"
"github.com/cert-manager/cert-manager/internal/pem"
"github.com/cert-manager/cert-manager/pkg/util/errors"
)
// DecodePrivateKeyBytes will decode a PEM encoded private key into a crypto.Signer.
// It supports ECDSA, RSA and EdDSA private keys only. All other types will return err.
func DecodePrivateKeyBytes(keyBytes []byte) (crypto.Signer, error) {
// decode the private key pem
block, _, err := pem.SafeDecodePrivateKey(keyBytes)
if err != nil {
return nil, errors.NewInvalidData("error decoding private key PEM block")
}
switch block.Type {
case "PRIVATE KEY":
key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return nil, errors.NewInvalidData("error parsing pkcs#8 private key: %s", err.Error())
}
signer, ok := key.(crypto.Signer)
if !ok {
return nil, errors.NewInvalidData("error parsing pkcs#8 private key: invalid key type")
}
return signer, nil
case "EC PRIVATE KEY":
key, err := x509.ParseECPrivateKey(block.Bytes)
if err != nil {
return nil, errors.NewInvalidData("error parsing ecdsa private key: %s", err.Error())
}
return key, nil
case "RSA PRIVATE KEY":
key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, errors.NewInvalidData("error parsing rsa private key: %s", err.Error())
}
err = key.Validate()
if err != nil {
return nil, errors.NewInvalidData("rsa private key failed validation: %s", err.Error())
}
return key, nil
default:
return nil, errors.NewInvalidData("unknown private key type: %s", block.Type)
}
}
func decodeMultipleCerts(certBytes []byte, decodeFn func([]byte) (*stdpem.Block, []byte, error)) ([]*x509.Certificate, error) {
certs := []*x509.Certificate{}
var block *stdpem.Block
for {
var err error
// decode the tls certificate pem
block, certBytes, err = decodeFn(certBytes)
if err != nil {
if err == pem.ErrNoPEMData {
break
}
return nil, err
}
// parse the tls certificate
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, errors.NewInvalidData("error parsing TLS certificate: %s", err.Error())
}
certs = append(certs, cert)
}
if len(certs) == 0 {
return nil, errors.NewInvalidData("error decoding certificate PEM block")
}
return certs, nil
}
// DecodeX509CertificateChainBytes will decode a PEM encoded x509 Certificate chain with a tight
// size limit to reduce the risk of DoS attacks. If you need to decode many certificates, use
// DecodeX509CertificateSetBytes instead.
func DecodeX509CertificateChainBytes(certBytes []byte) ([]*x509.Certificate, error) {
return decodeMultipleCerts(certBytes, pem.SafeDecodeCertificateChain)
}
// DecodeX509CertificateSetBytes will decode a concatenated set of PEM encoded x509 Certificates,
// with generous size limits to enable parsing of TLS trust bundles.
// If you need to decode a single certificate chain, use DecodeX509CertificateChainBytes instead.
func DecodeX509CertificateSetBytes(certBytes []byte) ([]*x509.Certificate, error) {
return decodeMultipleCerts(certBytes, pem.SafeDecodeCertificateBundle)
}
// DecodeX509CertificateBytes will decode a PEM encoded x509 Certificate.
func DecodeX509CertificateBytes(certBytes []byte) (*x509.Certificate, error) {
certs, err := DecodeX509CertificateSetBytes(certBytes)
if err != nil {
return nil, err
}
return certs[0], nil
}
// DecodeX509CertificateRequestBytes will decode a PEM encoded x509 Certificate Request.
func DecodeX509CertificateRequestBytes(csrBytes []byte) (*x509.CertificateRequest, error) {
block, _, err := pem.SafeDecodeCSR(csrBytes)
if err != nil {
return nil, errors.NewInvalidData("error decoding certificate request PEM block")
}
csr, err := x509.ParseCertificateRequest(block.Bytes)
if err != nil {
return nil, err
}
return csr, nil
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package pki
import (
"bytes"
"crypto/x509"
"slices"
"github.com/cert-manager/cert-manager/pkg/util/errors"
)
// PEMBundle includes the PEM encoded X.509 certificate chain and CA. CAPEM
// contains either 1 CA certificate, or is empty if only a single certificate
// exists in the chain.
type PEMBundle struct {
CAPEM []byte
ChainPEM []byte
}
type chainNode struct {
cert *x509.Certificate
issuer *chainNode
}
// ParseSingleCertificateChainPEM decodes a PEM encoded certificate chain before
// calling ParseSingleCertificateChainPEM
func ParseSingleCertificateChainPEM(pembundle []byte) (PEMBundle, error) {
certs, err := DecodeX509CertificateChainBytes(pembundle)
if err != nil {
return PEMBundle{}, err
}
return ParseSingleCertificateChain(certs)
}
// ParseSingleCertificateChain returns the PEM-encoded chain of certificates as
// well as the PEM-encoded CA certificate.
//
// The CA (CAPEM) may not be a true root, but the highest intermediate certificate.
// The certificate is chosen as follows:
// - If the chain has a self-signed root, the root certificate.
// - If the chain has no self-signed root and has > 1 certificates, the highest certificate in the chain.
// - If the chain has no self-signed root and has == 1 certificate, nil.
//
// The certificate chain (ChainPEM) starts with the leaf certificate and ends with the
// highest certificate in the chain which is not self-signed. Self-signed certificates
// are not included in the chain because we are certain they are known and trusted by the
// client already.
//
// This function removes duplicate certificate entries as well as comments and
// unnecessary white space.
//
// An error is returned if the passed bundle is not a valid single chain,
// the bundle is malformed, or the chain is broken.
func ParseSingleCertificateChain(certs []*x509.Certificate) (PEMBundle, error) {
for _, cert := range certs {
if cert == nil {
return PEMBundle{}, errors.NewInvalidData("certificate chain contains nil certificate")
}
if len(cert.Raw) == 0 {
return PEMBundle{}, errors.NewInvalidData("certificate chain contains certificate without Raw set")
}
}
{
// De-duplicate certificates. This moves "complicated" logic away from
// consumers and into a shared function, who would otherwise have to do this
// anyway.
// For lots of certificates, the time complexity is O(n log n).
uniqueCerts := append([]*x509.Certificate{}, certs...)
slices.SortFunc(uniqueCerts, func(a, b *x509.Certificate) int {
return bytes.Compare(a.Raw, b.Raw)
})
uniqueCerts = slices.CompactFunc(uniqueCerts, func(a, b *x509.Certificate) bool {
return bytes.Equal(a.Raw, b.Raw)
})
certs = uniqueCerts
}
// To prevent a malicious input from causing a DoS, we limit the number of unique
// certificates. This helps us avoid issues with O(n^2) time complexity in the algorithm below.
if len(certs) > 1000 {
return PEMBundle{}, errors.NewInvalidData("certificate chain is too long, must be less than 1000 certificates")
}
// A certificate chain can be well described as a linked list. Here we build
// multiple lists that contain a single node, each being a single certificate
// that was passed.
var chains []*chainNode
for i := range certs {
chains = append(chains, &chainNode{cert: certs[i]})
}
// The task is to build a single list which represents a single certificate
// chain. The strategy is to iteratively attempt to join items in the list to
// build this single chain. Once we have a single list, we have built the
// chain. If no match is found after a pass, then the list can never be reduced
// to a single chain and we error.
// For lots of certificates, the time complexity is O(n^2).
//
// If a single list is left, then we have built the entire chain. Stop
// iterating.
for len(chains) > 1 {
// If we were not able to merge two chains in this pass, then the chain is
// broken and cannot be built. Error.
mergedTwoChains := false
// Pop the last chain off the list and attempt to find a chain it can be
// merged with.
lastChain := chains[len(chains)-1]
chains = chains[:len(chains)-1]
for i, chain := range chains {
// attempt to add both chains together
chain, ok := lastChain.tryMergeChain(chain)
if ok {
// If adding the chains together was successful, replace the chain at
// index i with the new chain.
chains[i] = chain
mergedTwoChains = true
break
}
}
// If no chains were merged in this pass, the chain can never be built as a
// single list. Error.
if !mergedTwoChains {
return PEMBundle{}, errors.NewInvalidData("certificate chain is malformed or broken")
}
}
// There is only a single chain left at index 0. Return chain as PEM.
return chains[0].toBundleAndCA()
}
// toBundleAndCA will return the PEM bundle of this chain.
func (c *chainNode) toBundleAndCA() (PEMBundle, error) {
var (
certs []*x509.Certificate
ca *x509.Certificate
)
for {
// If the issuer is nil, we have hit the root of the chain. Assign the CA
// to this certificate and stop traversing. If the certificate at the root
// of the chain is not self-signed (i.e. is not a root CA), then also append
// that certificate to the chain.
// Root certificates are omitted from the chain as per
// https://datatracker.ietf.org/doc/html/rfc5246#section-7.4.2
// > [T]he self-signed certificate that specifies the root certificate authority
// > MAY be omitted from the chain, under the assumption that the remote end must
// > already possess it in order to validate it in any case.
if c.issuer == nil {
if len(certs) > 0 && !isSelfSignedCertificate(c.cert) {
certs = append(certs, c.cert)
}
ca = c.cert
break
}
// Add this node's certificate to the list at the end. Ready to check
// next node up.
certs = append(certs, c.cert)
c = c.issuer
}
caPEM, err := EncodeX509(ca)
if err != nil {
return PEMBundle{}, err
}
// If no certificates parsed, then CA is the only certificate and should be
// the chain. If the CA is also self-signed, then by definition it's also the
// issuer and so can be placed in CAPEM too.
if len(certs) == 0 {
if isSelfSignedCertificate(ca) {
return PEMBundle{ChainPEM: caPEM, CAPEM: caPEM}, nil
}
return PEMBundle{ChainPEM: caPEM}, nil
}
// Encode full certificate chain
chainPEM, err := EncodeX509Chain(certs)
if err != nil {
return PEMBundle{}, err
}
// Return chain and ca
return PEMBundle{CAPEM: caPEM, ChainPEM: chainPEM}, nil
}
// tryMergeChain glues two chains A and B together by adding one on top of
// the other. The function tries both gluing A on top of B and B on top of
// A, which is why the argument order for the two input chains does not
// matter.
//
// Glueability: We say that the chains A and B are glueable when either the
// leaf certificate of A can be verified using the root certificate of B,
// or that the leaf certificate of B can be verified using the root certificate
// of A.
//
// A leaf certificate C (as in "child") is verified by a certificate P
// (as in "parent"), when they satisfy C.CheckSignatureFrom(P). In the
// following diagram, C.CheckSignatureFrom(P) is satisfied, i.e., the
// signature ("sig") on the certificate C can be verified using the parent P:
//
// head tail
// +------+-------+ +------+-------+ +------+-------+
// | | | | | | | | |
// | | sig ------->| C | sig ------->| P | |
// | | | | | | | | |
// +------+-------+ +------+-------+ +------+-------+
// leaf certificate root certificate
//
// The function returns false if the chains A and B are not glueable.
func (a *chainNode) tryMergeChain(b *chainNode) (*chainNode, bool) {
bRoot := b.root()
// b's root has been signed by a. Add a as parent of b's root.
if bytes.Equal(bRoot.cert.RawIssuer, a.cert.RawSubject) &&
bRoot.cert.CheckSignatureFrom(a.cert) == nil {
bRoot.issuer = a
return b, true
}
aRoot := a.root()
// a's root has been signed by b. Add b as parent of a's root.
if bytes.Equal(aRoot.cert.RawIssuer, b.cert.RawSubject) &&
aRoot.cert.CheckSignatureFrom(b.cert) == nil {
aRoot.issuer = b
return a, true
}
// Chains cannot be added together.
return a, false
}
// Return the root most node of this chain.
func (c *chainNode) root() *chainNode {
for c.issuer != nil {
c = c.issuer
}
return c
}
// isSelfSignedCertificate returns true if the given X.509 certificate has been
// signed by itself, which would make it a "root" certificate.
func isSelfSignedCertificate(cert *x509.Certificate) bool {
return cert.CheckSignatureFrom(cert) == nil
}
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package pki
func FuzzUnmarshalSubjectStringToRDNSequence(data []byte) int {
UnmarshalSubjectStringToRDNSequence(string(data))
return 1
}
func FuzzDecodePrivateKeyBytes(data []byte) int {
DecodePrivateKeyBytes(data)
return 1
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package pki
import (
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// RenewalTimeFunc is a custom function type for calculating renewal time of a certificate.
type RenewalTimeFunc func(time.Time, time.Time, *metav1.Duration, *int32) *metav1.Time
// RenewalTime calculates renewal time for a certificate.
// If renewBefore is non-nil and less than the certificate's lifetime, renewal
// time will be the computed renewBefore period before expiry.
// If renewBeforePercentage is non-nil and in the range (0,100), renewal time
// will be the computed period before expiry based on the renewBeforePercentage
// value and certificate lifetime.
// Default renewal time is 2/3 through certificate's lifetime.
func RenewalTime(notBefore, notAfter time.Time, renewBefore *metav1.Duration, renewBeforePercentage *int32) *metav1.Time {
// 1. Calculate how long before expiry a cert should be renewed
actualDuration := notAfter.Sub(notBefore)
actualRenewBefore := RenewBefore(actualDuration, renewBefore, renewBeforePercentage)
// 2. Calculate when a cert should be renewed
// Truncate the renewal time to nearest second. This is important
// because the renewal time also gets stored on Certificate's status
// where it is truncated to the nearest second. We use the renewal time
// from Certificate's status to determine when the Certificate will be
// added to the queue to be renewed, but then re-calculate whether it
// needs to be renewed _now_ using this function, so returning a
// non-truncated value here would potentially cause Certificates to be
// re-queued for renewal earlier than the calculated renewal time thus
// causing Certificates to not be automatically renewed. See
// https://github.com/cert-manager/cert-manager/pull/4399.
rt := metav1.NewTime(notAfter.Add(-1 * actualRenewBefore).Truncate(time.Second))
return &rt
}
// RenewBefore calculates how far before expiry a certificate should be renewed.
// If renewBefore is non-nil and less than the certificate's lifetime, renewal
// time will be the computed renewBefore period before expiry.
// If renewBeforePercentage is non-nil and in the range (0,100), renewal time
// will be the computed period before expiry based on the renewBeforePercentage
// and actualDuration values.
// Default is 2/3 through certificate's lifetime.
func RenewBefore(actualDuration time.Duration, renewBefore *metav1.Duration, renewBeforePercentage *int32) time.Duration {
// If spec.renewBefore or spec.renewBeforePercentage was set (and is
// valid) respect that. We don't want to prevent users from renewing
// longer lived certs more frequently.
if renewBefore != nil && renewBefore.Duration > 0 && renewBefore.Duration < actualDuration {
return renewBefore.Duration
} else if renewBeforePercentage != nil && *renewBeforePercentage > 0 && *renewBeforePercentage < 100 {
return actualDuration * time.Duration(*renewBeforePercentage) / 100
}
// Otherwise, default to renewing 2/3 through certificate's lifetime.
return actualDuration / 3
}
/*
Copyright 2023 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package pki
import (
"crypto/x509/pkix"
"encoding/asn1"
"errors"
"fmt"
"net"
"strconv"
)
// Copied from x509.go
var (
oidExtensionSubjectAltName = []int{2, 5, 29, 17}
)
// Based on RFC 5280, section 4.2.1.6
// see https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.6
/*
OtherName ::= SEQUENCE {
type-id OBJECT IDENTIFIER,
value [0] EXPLICIT ANY DEFINED BY type-id }
*/
type OtherName struct {
TypeID asn1.ObjectIdentifier
Value asn1.RawValue `asn1:"tag:0,explicit"`
}
// Based on RFC 5280, section 4.2.1.6
// see https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.6
/*
EDIPartyName ::= SEQUENCE {
nameAssigner [0] DirectoryString OPTIONAL,
partyName [1] DirectoryString }
*/
type EDIPartyName struct {
NameAssigner string `asn1:"tag:0,optional"`
PartyName string `asn1:"tag:1"`
}
// Based on RFC 5280, section 4.2.1.6
// see https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.6
/*
GeneralName ::= CHOICE {
otherName [0] OtherName,
rfc822Name [1] IA5String,
dnsName [2] IA5String,
x400Address [3] ORAddress,
directoryName [4] Name,
ediPartyName [5] EDIPartyName,
uniformResourceIdentifier [6] IA5String,
ipAddress [7] OCTET STRING,
registeredID [8] OBJECT IDENTIFIER }
*/
const (
nameTypeOtherName = 0
nameTypeRFC822Name = 1
nameTypeDNSName = 2
nameTypeX400Address = 3
nameTypeDirectoryName = 4
nameTypeEDIPartyName = 5
nameTypeUniformResourceIdentifier = 6
nameTypeIPAddress = 7
nameTypeRegisteredID = 8
)
type GeneralNames struct {
OtherNames []OtherName
RFC822Names []string
DNSNames []string
X400Addresses []asn1.RawValue
DirectoryNames []pkix.RDNSequence
EDIPartyNames []EDIPartyName
UniformResourceIdentifiers []string
IPAddresses []net.IP
RegisteredIDs []asn1.ObjectIdentifier
}
func (gns GeneralNames) Empty() bool {
return len(gns.OtherNames) == 0 &&
len(gns.RFC822Names) == 0 &&
len(gns.DNSNames) == 0 &&
len(gns.X400Addresses) == 0 &&
len(gns.DirectoryNames) == 0 &&
len(gns.EDIPartyNames) == 0 &&
len(gns.UniformResourceIdentifiers) == 0 &&
len(gns.IPAddresses) == 0 &&
len(gns.RegisteredIDs) == 0
}
// adapted from https://cs.opensource.google/go/go/+/master:src/crypto/x509/parser.go;l=373-416;drc=16d3040a84be821d801b75bd1a3d8ab4cc89ee36
func UnmarshalSANs(value []byte) (GeneralNames, error) {
var gns GeneralNames
err := forEachSAN(value, func(v asn1.RawValue) error {
switch v.Tag {
case nameTypeOtherName:
var otherName OtherName
if _, err := asn1.UnmarshalWithParams(v.FullBytes, &otherName, fmt.Sprintf("tag:%d", nameTypeOtherName)); err != nil {
return err
}
gns.OtherNames = append(gns.OtherNames, otherName)
case nameTypeRFC822Name:
email := string(v.Bytes)
if err := isIA5String(email); err != nil {
return errors.New("x509: SAN rfc822Name is malformed")
}
gns.RFC822Names = append(gns.RFC822Names, email)
case nameTypeDNSName:
name := string(v.Bytes)
if err := isIA5String(name); err != nil {
return errors.New("x509: SAN dNSName is malformed")
}
gns.DNSNames = append(gns.DNSNames, name)
case nameTypeX400Address:
gns.X400Addresses = append(gns.X400Addresses, v)
case nameTypeDirectoryName:
var rdn pkix.RDNSequence
if _, err := asn1.UnmarshalWithParams(v.FullBytes, &rdn, fmt.Sprintf("tag:%d", nameTypeDirectoryName)); err != nil {
return err
}
gns.DirectoryNames = append(gns.DirectoryNames, rdn)
case nameTypeEDIPartyName:
var edipn EDIPartyName
if _, err := asn1.UnmarshalWithParams(v.FullBytes, &edipn, fmt.Sprintf("tag:%d", nameTypeEDIPartyName)); err != nil {
return err
}
gns.EDIPartyNames = append(gns.EDIPartyNames, edipn)
case nameTypeUniformResourceIdentifier:
uriStr := string(v.Bytes)
if err := isIA5String(uriStr); err != nil {
return errors.New("x509: SAN uniformResourceIdentifier is malformed")
}
gns.UniformResourceIdentifiers = append(gns.UniformResourceIdentifiers, uriStr)
case nameTypeIPAddress:
switch len(v.Bytes) {
case net.IPv4len, net.IPv6len:
gns.IPAddresses = append(gns.IPAddresses, v.Bytes)
default:
return errors.New("x509: cannot parse IP address of length " + strconv.Itoa(len(v.Bytes)))
}
case nameTypeRegisteredID:
var oid asn1.ObjectIdentifier
if _, err := asn1.UnmarshalWithParams(v.FullBytes, &oid, fmt.Sprintf("tag:%d", nameTypeRegisteredID)); err != nil {
return err
}
gns.RegisteredIDs = append(gns.RegisteredIDs, oid)
default:
return asn1.StructuralError{Msg: "bad SAN choice"}
}
return nil
})
return gns, err
}
func forEachSAN(extension []byte, callback func(v asn1.RawValue) error) error {
var seq asn1.RawValue
rest, err := asn1.Unmarshal(extension, &seq)
if err != nil {
return err
} else if len(rest) != 0 {
return fmt.Errorf("x509: trailing data after X.509 extension")
}
if !seq.IsCompound || seq.Tag != asn1.TagSequence || seq.Class != asn1.ClassUniversal {
return asn1.StructuralError{Msg: "bad SAN sequence"}
}
rest = seq.Bytes
for len(rest) > 0 {
var v asn1.RawValue
rest, err = asn1.Unmarshal(rest, &v)
if err != nil {
return err
}
if err := callback(v); err != nil {
return err
}
}
return nil
}
// adapted from https://cs.opensource.google/go/go/+/master:src/crypto/x509/x509.go;l=1059-1103;drc=e2d9574b14b3db044331da0c6fadeb62315c644a
// MarshalSANs marshals a list of addresses into the contents of an X.509
// SubjectAlternativeName extension.
func MarshalSANs(gns GeneralNames, hasSubject bool) (pkix.Extension, error) {
var rawValues []asn1.RawValue
addMarshalable := func(tag int, val interface{}) error {
fullBytes, err := asn1.MarshalWithParams(val, fmt.Sprint("tag:", tag))
if err != nil {
return err
}
rawValues = append(rawValues, asn1.RawValue{FullBytes: fullBytes})
return nil
}
addIA5String := func(tag int, val string) error {
if err := isIA5String(val); err != nil {
return fmt.Errorf("x509: %q cannot be encoded as an IA5String", val)
}
rawValues = append(rawValues, asn1.RawValue{Tag: tag, Class: asn1.ClassContextSpecific, Bytes: []byte(val)})
return nil
}
// Maintain the order of the SANs as produced by the Go x509 library.
for _, val := range gns.DNSNames {
if err := addIA5String(nameTypeDNSName, val); err != nil {
return pkix.Extension{}, err
}
}
for _, val := range gns.RFC822Names {
if err := addIA5String(nameTypeRFC822Name, val); err != nil {
return pkix.Extension{}, err
}
}
for _, rawIP := range gns.IPAddresses {
// If possible, we always want to encode IPv4 addresses in 4 bytes.
ip := rawIP.To4()
if ip == nil {
ip = rawIP
}
rawValues = append(rawValues, asn1.RawValue{Tag: nameTypeIPAddress, Class: asn1.ClassContextSpecific, Bytes: ip})
}
for _, val := range gns.UniformResourceIdentifiers {
if err := addIA5String(nameTypeUniformResourceIdentifier, val); err != nil {
return pkix.Extension{}, err
}
}
// Add support for the remaining SAN types.
for _, val := range gns.OtherNames {
if err := addMarshalable(nameTypeOtherName, val); err != nil {
return pkix.Extension{}, err
}
}
for _, val := range gns.X400Addresses {
if err := addMarshalable(nameTypeX400Address, val); err != nil {
return pkix.Extension{}, err
}
}
for _, val := range gns.DirectoryNames {
if err := addMarshalable(nameTypeDirectoryName, val); err != nil {
return pkix.Extension{}, err
}
}
for _, val := range gns.EDIPartyNames {
if err := addMarshalable(nameTypeEDIPartyName, val); err != nil {
return pkix.Extension{}, err
}
}
for _, val := range gns.RegisteredIDs {
if err := addMarshalable(nameTypeRegisteredID, val); err != nil {
return pkix.Extension{}, err
}
}
byteValue, err := asn1.Marshal(rawValues)
if err != nil {
return pkix.Extension{}, err
}
return pkix.Extension{
Id: oidExtensionSubjectAltName,
Critical: !hasSubject,
Value: byteValue,
}, nil
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package pki
import (
"bytes"
"crypto/x509/pkix"
"encoding/asn1"
"errors"
"github.com/go-ldap/ldap/v3"
)
var OIDConstants = struct {
Country []int
Organization []int
OrganizationalUnit []int
CommonName []int
SerialNumber []int
Locality []int
Province []int
StreetAddress []int
DomainComponent []int
UniqueIdentifier []int
}{
Country: []int{2, 5, 4, 6},
Organization: []int{2, 5, 4, 10},
OrganizationalUnit: []int{2, 5, 4, 11},
CommonName: []int{2, 5, 4, 3},
SerialNumber: []int{2, 5, 4, 5},
Locality: []int{2, 5, 4, 7},
Province: []int{2, 5, 4, 8},
StreetAddress: []int{2, 5, 4, 9},
DomainComponent: []int{0, 9, 2342, 19200300, 100, 1, 25},
UniqueIdentifier: []int{0, 9, 2342, 19200300, 100, 1, 1},
}
// Copied from pkix.attributeTypeNames and inverted. (Sadly it is private.)
// Source: https://cs.opensource.google/go/go/+/refs/tags/go1.18.2:src/crypto/x509/pkix/pkix.go;l=26
// Added RDNs identifier to support rfc4514 LDAP certificates, cf https://github.com/cert-manager/cert-manager/issues/5582
var attributeTypeNames = map[string][]int{
"C": OIDConstants.Country,
"O": OIDConstants.Organization,
"OU": OIDConstants.OrganizationalUnit,
"CN": OIDConstants.CommonName,
"SERIALNUMBER": OIDConstants.SerialNumber,
"L": OIDConstants.Locality,
"ST": OIDConstants.Province,
"STREET": OIDConstants.StreetAddress,
"DC": OIDConstants.DomainComponent,
"UID": OIDConstants.UniqueIdentifier,
}
func UnmarshalSubjectStringToRDNSequence(subject string) (pkix.RDNSequence, error) {
dn, err := ldap.ParseDN(subject)
if err != nil {
return nil, err
}
// Traverse the parsed RDNSequence in REVERSE order as RDNs in String format are expected to be written in reverse order.
// Meaning, a string of "CN=Foo,OU=Bar,O=Baz" actually should have "O=Baz" as the first element in the RDNSequence.
rdns := make(pkix.RDNSequence, 0, len(dn.RDNs))
for i := range dn.RDNs {
ldapRelativeDN := dn.RDNs[len(dn.RDNs)-i-1]
atvs := make([]pkix.AttributeTypeAndValue, 0, len(ldapRelativeDN.Attributes))
for _, ldapATV := range ldapRelativeDN.Attributes {
oid, ok := attributeTypeNames[ldapATV.Type]
if !ok {
// If the attribute type is not known, we try to parse it as an OID.
// If it is not an OID, we set Type=nil
oid, err = ParseObjectIdentifier(ldapATV.Type)
if err != nil {
oid = nil
}
}
atvs = append(atvs, pkix.AttributeTypeAndValue{
Type: oid,
Value: ldapATV.Value,
})
}
rdns = append(rdns, atvs)
}
return rdns, nil
}
func IsASN1SubjectEmpty(asn1Subject []byte) bool {
// emptyASN1Subject is the ASN.1 DER encoding of an empty Subject, which is
// just an empty SEQUENCE.
var emptyASN1Subject = []byte{0x30, 0}
return bytes.Equal(asn1Subject, emptyASN1Subject)
}
func MarshalRDNSequenceToRawDERBytes(rdnSequence pkix.RDNSequence) ([]byte, error) {
return asn1.Marshal(rdnSequence)
}
func UnmarshalRawDerBytesToRDNSequence(der []byte) (rdnSequence pkix.RDNSequence, err error) {
var rest []byte
if rest, err = asn1.Unmarshal(der, &rdnSequence); err != nil {
return rdnSequence, err
} else if len(rest) != 0 {
return rdnSequence, errors.New("RDNSequence: trailing data after Subject")
} else {
return rdnSequence, nil
}
}
func ExtractCommonNameFromRDNSequence(rdns pkix.RDNSequence) string {
for _, rdn := range rdns {
for _, atv := range rdn {
if atv.Type.Equal(OIDConstants.CommonName) {
if str, ok := atv.Value.(string); ok {
return str
}
}
}
}
return ""
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package pki
import cmapi "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
// staticTemporarySerialNumber is a fixed serial number we use for temporary certificates
const staticTemporarySerialNumber = "1234567890"
// GenerateLocallySignedTemporaryCertificate signs a temporary certificate for
// the given certificate resource using a one-use temporary CA that is then
// discarded afterwards.
// This is to mitigate a potential attack against x509 certificates that use a
// predictable serial number and weak MD5 hashing algorithms.
// In practice, this shouldn't really be a concern anyway.
func GenerateLocallySignedTemporaryCertificate(crt *cmapi.Certificate, pkData []byte) ([]byte, error) {
// generate a throwaway self-signed root CA
caPk, err := GenerateECPrivateKey(ECCurve521)
if err != nil {
return nil, err
}
caCertTemplate, err := CertificateTemplateFromCertificate(&cmapi.Certificate{
Spec: cmapi.CertificateSpec{
CommonName: "cert-manager.local",
IsCA: true,
},
})
if err != nil {
return nil, err
}
_, caCert, err := SignCertificate(caCertTemplate, caCertTemplate, caPk.Public(), caPk)
if err != nil {
return nil, err
}
// sign a temporary certificate using the root CA
template, err := CertificateTemplateFromCertificate(crt)
if err != nil {
return nil, err
}
template.Subject.SerialNumber = staticTemporarySerialNumber
signeeKey, err := DecodePrivateKeyBytes(pkData)
if err != nil {
return nil, err
}
b, _, err := SignCertificate(template, caCert, signeeKey.Public(), caPk)
if err != nil {
return nil, err
}
return b, nil
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"bytes"
"fmt"
"net/http"
"strings"
"unicode"
"unicode/utf8"
"k8s.io/apimachinery/pkg/apis/meta/v1/validation"
"k8s.io/client-go/rest"
)
// RestConfigWithUserAgent returns a copy of the Kubernetes REST config with
// the User Agent set which includes the optional component strings given.
func RestConfigWithUserAgent(restConfig *rest.Config, component ...string) *rest.Config {
restConfig = rest.CopyConfig(restConfig)
restConfig.UserAgent = fmt.Sprintf("%s/%s (%s) cert-manager/%s",
strings.Join(append([]string{"cert-manager"}, component...), "-"),
version(), VersionInfo().Platform, VersionInfo().GitCommit)
return restConfig
}
// PrefixFromUserAgent takes the characters preceding the first /, quote
// unprintable character and then trim what's beyond the FieldManagerMaxLength
// limit.
// Taken from
// https://github.com/kubernetes/kubernetes/blob/9a75e7b0fd1b567f774a3373be640e19b33e7ef1/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/create.go#L252
func PrefixFromUserAgent(u string) string {
m := strings.Split(u, "/")[0]
buf := bytes.NewBuffer(nil)
for _, r := range m {
// Ignore non-printable characters
if !unicode.IsPrint(r) {
continue
}
// Only append if we have room for it
if buf.Len()+utf8.RuneLen(r) > validation.FieldManagerMaxLength {
break
}
buf.WriteRune(r)
}
return buf.String()
}
// UserAgentRoundTripper implements the http.RoundTripper interface and adds a User-Agent
// header.
type userAgentRoundTripper struct {
inner http.RoundTripper
userAgent string
}
// UserAgentRoundTripper returns a RoundTripper that functions identically to
// the provided 'inner' round tripper, other than also setting a user agent.
func UserAgentRoundTripper(inner http.RoundTripper, userAgent string) http.RoundTripper {
return userAgentRoundTripper{
inner: inner,
userAgent: userAgent,
}
}
// RoundTrip implements http.RoundTripper
func (u userAgentRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
req.Header.Set("User-Agent", u.userAgent)
return u.inner.RoundTrip(req)
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"bytes"
"encoding/csv"
"fmt"
"net"
"net/url"
"slices"
"strings"
cmapi "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
)
// genericEqualUnsorted reports whether two slices are identical up to reordering
// using a comparison function.
// If the lengths are different, genericEqualUnsorted returns false. Otherwise, the
// elements are sorted using the comparison function, and the sorted slices are
// compared element by element using the same comparison function. If all elements
// are equal, genericEqualUnsorted returns true. Otherwise it returns false.
func genericEqualUnsorted[S ~[]E, E any](
s1 S, s2 S,
cmp func(a, b E) int,
) bool {
if len(s1) != len(s2) {
return false
}
s1, s2 = slices.Clone(s1), slices.Clone(s2)
slices.SortStableFunc(s1, cmp)
slices.SortStableFunc(s2, cmp)
return slices.EqualFunc(s1, s2, func(a, b E) bool {
return cmp(a, b) == 0
})
}
func EqualUnsorted(s1 []string, s2 []string) bool {
return genericEqualUnsorted(s1, s2, strings.Compare)
}
// Test for equal URL slices even if unsorted. Panics if any element is nil
func EqualURLsUnsorted(s1, s2 []*url.URL) bool {
return genericEqualUnsorted(s1, s2, func(a, b *url.URL) int {
return strings.Compare(a.String(), b.String())
})
}
// Test for equal cmapi.OtherName slices even if unsorted. Panics if any element is nil
func EqualOtherNamesUnsorted(s1, s2 []cmapi.OtherName) bool {
return genericEqualUnsorted(s1, s2, func(a cmapi.OtherName, b cmapi.OtherName) int {
if a.OID == b.OID {
return strings.Compare(a.UTF8Value, b.UTF8Value)
}
return strings.Compare(a.OID, b.OID)
})
}
// EqualIPsUnsorted checks if the given slices of IP addresses contain the same elements, even if in a different order
func EqualIPsUnsorted(s1, s2 []net.IP) bool {
// Two IPv4 addresses can compare unequal with bytes.Equal which is why net.IP.Equal exists.
// We still want to sort the lists, though, and we don't want different representations of IPv4 addresses
// to be sorted differently. That can happen if one is stored as a 4-byte address while
// the other is stored as a 16-byte representation
// To avoid ambiguity, we ensure that only the 16-byte form is used for all addresses we work with.
return genericEqualUnsorted(s1, s2, func(a, b net.IP) int {
return bytes.Compare(a.To16(), b.To16())
})
}
// Test for equal KeyUsage slices even if unsorted
func EqualKeyUsagesUnsorted(s1, s2 []cmapi.KeyUsage) bool {
return genericEqualUnsorted(s1, s2, func(a, b cmapi.KeyUsage) int {
return strings.Compare(string(a), string(b))
})
}
// JoinWithEscapeCSV returns the given list as a single line of CSV that
// is escaped with quotes if necessary
func JoinWithEscapeCSV(in []string) (string, error) {
b := new(bytes.Buffer)
writer := csv.NewWriter(b)
if err := writer.Write(in); err != nil {
return "", fmt.Errorf("failed to write %q as CSV: %w", in, err)
}
writer.Flush()
if err := writer.Error(); err != nil {
return "", fmt.Errorf("failed to write %q as CSV: %w", in, err)
}
s := b.String()
// CSV writer adds a trailing new line, we need to clean it up
s = strings.TrimSuffix(s, "\n")
return s, nil
}
// SplitWithEscapeCSV parses the given input as a single line of CSV, which allows
// a comma-separated list of strings to be parsed while allowing commas to be present
// in each field. For example, a user can specify:
// "10 Downing Street, Westminster",Manchester
// to produce []string{"10 Downing Street, Westminster", "Manchester"}, keeping the comma
// in the first address. Empty lines or multiple CSV records are both rejected.
func SplitWithEscapeCSV(in string) ([]string, error) {
reader := csv.NewReader(strings.NewReader(in))
records, err := reader.ReadAll()
if err != nil {
return nil, fmt.Errorf("failed to parse %q as CSV: %w", in, err)
}
if len(records) == 0 {
return nil, fmt.Errorf("no values found after parsing %q", in)
} else if len(records) > 1 {
return nil, fmt.Errorf("refusing to use %q as input as it parses as multiple lines of CSV", in)
}
return records[0], nil
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"fmt"
"runtime"
)
type Version struct {
GitVersion string `json:"gitVersion"`
GitCommit string `json:"gitCommit"`
GitTreeState string `json:"gitTreeState"`
GoVersion string `json:"goVersion"`
Compiler string `json:"compiler"`
Platform string `json:"platform"`
}
// This variable block holds information used to build up the version string
var (
AppGitState = ""
AppGitCommit = ""
AppVersion = "canary"
)
func VersionInfo() Version {
return Version{
GitVersion: AppVersion,
GitCommit: AppGitCommit,
GitTreeState: AppGitState,
GoVersion: runtime.Version(),
Compiler: runtime.Compiler,
Platform: fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH),
}
}
func version() string {
v := AppVersion
if AppVersion == "canary" && AppGitCommit != "" {
v += "-" + AppGitCommit
}
if AppGitState != "" {
v += fmt.Sprintf(" (%v)", AppGitState)
}
return v
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package gen
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
v1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
)
type CertificateModifier func(*v1.Certificate)
func Certificate(name string, mods ...CertificateModifier) *v1.Certificate {
c := &v1.Certificate{
ObjectMeta: ObjectMeta(name),
Spec: v1.CertificateSpec{
PrivateKey: &v1.CertificatePrivateKey{},
},
}
for _, mod := range mods {
mod(c)
}
return c
}
func CertificateFrom(crt *v1.Certificate, mods ...CertificateModifier) *v1.Certificate {
crt = crt.DeepCopy()
for _, mod := range mods {
mod(crt)
}
return crt
}
// SetCertificateIssuer sets the Certificate.spec.issuerRef field
func SetCertificateIssuer(o cmmeta.ObjectReference) CertificateModifier {
return func(c *v1.Certificate) {
c.Spec.IssuerRef = o
}
}
func SetCertificateDNSNames(dnsNames ...string) CertificateModifier {
return func(crt *v1.Certificate) {
crt.Spec.DNSNames = dnsNames
}
}
func SetCertificateCommonName(commonName string) CertificateModifier {
return func(crt *v1.Certificate) {
crt.Spec.CommonName = commonName
}
}
func SetCertificateIPs(ips ...string) CertificateModifier {
return func(crt *v1.Certificate) {
crt.Spec.IPAddresses = ips
}
}
func SetCertificateOtherNames(otherNames ...v1.OtherName) CertificateModifier {
return func(crt *v1.Certificate) {
crt.Spec.OtherNames = otherNames
}
}
func SetCertificateEmails(emails ...string) CertificateModifier {
return func(crt *v1.Certificate) {
crt.Spec.EmailAddresses = emails
}
}
func SetCertificateURIs(uris ...string) CertificateModifier {
return func(crt *v1.Certificate) {
crt.Spec.URIs = uris
}
}
func SetCertificateIsCA(isCA bool) CertificateModifier {
return func(crt *v1.Certificate) {
crt.Spec.IsCA = isCA
}
}
func SetCertificateKeyAlgorithm(keyAlgorithm v1.PrivateKeyAlgorithm) CertificateModifier {
return func(crt *v1.Certificate) {
crt.Spec.PrivateKey.Algorithm = keyAlgorithm
}
}
func SetCertificateKeySize(keySize int) CertificateModifier {
return func(crt *v1.Certificate) {
crt.Spec.PrivateKey.Size = keySize
}
}
func SetCertificateKeyEncoding(keyEncoding v1.PrivateKeyEncoding) CertificateModifier {
return func(crt *v1.Certificate) {
crt.Spec.PrivateKey.Encoding = keyEncoding
}
}
func SetCertificateSecretName(secretName string) CertificateModifier {
return func(crt *v1.Certificate) {
crt.Spec.SecretName = secretName
}
}
// SetCertificateSecretTemplate sets annotations and labels to be attached to the secret metadata.
func SetCertificateSecretTemplate(annotations, labels map[string]string) CertificateModifier {
return func(crt *v1.Certificate) {
crt.Spec.SecretTemplate = &v1.CertificateSecretTemplate{
Annotations: annotations,
Labels: labels,
}
}
}
func SetCertificateDuration(duration *metav1.Duration) CertificateModifier {
return func(crt *v1.Certificate) {
crt.Spec.Duration = duration
}
}
func SetCertificateRenewBefore(renewBefore *metav1.Duration) CertificateModifier {
return func(crt *v1.Certificate) {
crt.Spec.RenewBefore = renewBefore
}
}
func SetCertificateNextPrivateKeySecretName(name string) CertificateModifier {
return func(crt *v1.Certificate) {
crt.Status.NextPrivateKeySecretName = &name
}
}
func SetCertificateStatusCondition(c v1.CertificateCondition) CertificateModifier {
return func(crt *v1.Certificate) {
if len(crt.Status.Conditions) == 0 {
crt.Status.Conditions = []v1.CertificateCondition{c}
return
}
for i, existingC := range crt.Status.Conditions {
if existingC.Type == c.Type {
crt.Status.Conditions[i] = c
return
}
}
crt.Status.Conditions = append(crt.Status.Conditions, c)
}
}
func SetCertificateLastFailureTime(p metav1.Time) CertificateModifier {
return func(crt *v1.Certificate) {
crt.Status.LastFailureTime = &p
}
}
func SetCertificateIssuanceAttempts(ia *int) CertificateModifier {
return func(crt *v1.Certificate) {
crt.Status.FailedIssuanceAttempts = ia
}
}
func SetCertificateNotAfter(p metav1.Time) CertificateModifier {
return func(crt *v1.Certificate) {
crt.Status.NotAfter = &p
}
}
func SetCertificateNotBefore(p metav1.Time) CertificateModifier {
return func(crt *v1.Certificate) {
crt.Status.NotBefore = &p
}
}
func SetCertificateRenewalTime(p metav1.Time) CertificateModifier {
return func(crt *v1.Certificate) {
crt.Status.RenewalTime = &p
}
}
func SetCertificateOrganization(orgs ...string) CertificateModifier {
return func(ch *v1.Certificate) {
if ch.Spec.Subject == nil {
ch.Spec.Subject = &v1.X509Subject{}
}
ch.Spec.Subject.Organizations = orgs
}
}
func SetCertificateNamespace(namespace string) CertificateModifier {
return func(crt *v1.Certificate) {
crt.ObjectMeta.Namespace = namespace
}
}
func SetCertificateKeyUsages(usages ...v1.KeyUsage) CertificateModifier {
return func(crt *v1.Certificate) {
crt.Spec.Usages = usages
}
}
func SetCertificateRevision(revision int) CertificateModifier {
return func(crt *v1.Certificate) {
crt.Status.Revision = &revision
}
}
func SetCertificateUID(uid types.UID) CertificateModifier {
return func(crt *v1.Certificate) {
crt.UID = uid
}
}
func SetCertificateGeneration(gen int64) CertificateModifier {
return func(crt *v1.Certificate) {
crt.Generation = gen
}
}
func SetCertificateCreationTimestamp(creationTimestamp metav1.Time) CertificateModifier {
return func(crt *v1.Certificate) {
crt.ObjectMeta.CreationTimestamp = creationTimestamp
}
}
func AddCertificateAnnotations(annotations map[string]string) CertificateModifier {
return func(crt *v1.Certificate) {
if crt.Annotations == nil {
crt.Annotations = make(map[string]string)
}
for k, v := range annotations {
crt.Annotations[k] = v
}
}
}
func AddCertificateLabels(labels map[string]string) CertificateModifier {
return func(crt *v1.Certificate) {
if crt.Labels == nil {
crt.Labels = make(map[string]string)
}
for k, v := range labels {
crt.Labels[k] = v
}
}
}
// CertificateRef creates an owner reference for a certificate without having to
// give the full certificate. Only use this function for testing purposes.
//
// Note that the only "important" field that must be filled in ownerReference
// [1] is the UID. Most notably, the IsControlledBy function [2] only cares
// about the UID. The apiVersion, kind and name are only used for information
// purposes.
//
// [1]: https://github.com/kubernetes/apimachinery/blob/10b3882/pkg/apis/meta/v1/types.go#L273-L275
// [2]: https://github.com/kubernetes/apimachinery/blob/10b3882/pkg/apis/meta/v1/controller_ref.go#L29
func CertificateRef(certName, certUID string) metav1.OwnerReference {
return *metav1.NewControllerRef(
Certificate(certName,
SetCertificateUID(types.UID(certUID)),
),
v1.SchemeGroupVersion.WithKind("Certificate"),
)
}
func SetCertificateRevisionHistoryLimit(limit int32) CertificateModifier {
return func(crt *v1.Certificate) {
crt.Spec.RevisionHistoryLimit = &limit
}
}
func SetCertificateAdditionalOutputFormats(additionalOutputFormats ...v1.CertificateAdditionalOutputFormat) CertificateModifier {
return func(crt *v1.Certificate) {
crt.Spec.AdditionalOutputFormats = additionalOutputFormats
}
}
func SetCertificateKeystore(keystores *v1.CertificateKeystores) CertificateModifier {
return func(crt *v1.Certificate) {
crt.Spec.Keystores = keystores
}
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package gen
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
)
type CertificateRequestModifier func(*v1.CertificateRequest)
func CertificateRequest(name string, mods ...CertificateRequestModifier) *v1.CertificateRequest {
c := &v1.CertificateRequest{
ObjectMeta: ObjectMeta(name),
}
for _, mod := range mods {
mod(c)
}
return c
}
func CertificateRequestFrom(cr *v1.CertificateRequest, mods ...CertificateRequestModifier) *v1.CertificateRequest {
cr = cr.DeepCopy()
for _, mod := range mods {
mod(cr)
}
return cr
}
// SetCertificateRequestIssuer sets the CertificateRequest.spec.issuerRef field
func SetCertificateRequestIssuer(o cmmeta.ObjectReference) CertificateRequestModifier {
return func(c *v1.CertificateRequest) {
c.Spec.IssuerRef = o
}
}
func SetCertificateRequestCSR(csr []byte) CertificateRequestModifier {
return func(cr *v1.CertificateRequest) {
cr.Spec.Request = csr
}
}
func SetCertificateRequestIsCA(isCA bool) CertificateRequestModifier {
return func(cr *v1.CertificateRequest) {
cr.Spec.IsCA = isCA
}
}
func SetCertificateRequestDuration(duration *metav1.Duration) CertificateRequestModifier {
return func(cr *v1.CertificateRequest) {
cr.Spec.Duration = duration
}
}
func SetCertificateRequestCA(ca []byte) CertificateRequestModifier {
return func(cr *v1.CertificateRequest) {
cr.Status.CA = ca
}
}
func SetCertificateRequestCertificate(cert []byte) CertificateRequestModifier {
return func(cr *v1.CertificateRequest) {
cr.Status.Certificate = cert
}
}
func SetCertificateRequestStatusCondition(c v1.CertificateRequestCondition) CertificateRequestModifier {
return func(cr *v1.CertificateRequest) {
if len(cr.Status.Conditions) == 0 {
cr.Status.Conditions = []v1.CertificateRequestCondition{c}
return
}
for i, existingC := range cr.Status.Conditions {
if existingC.Type == c.Type {
cr.Status.Conditions[i] = c
return
}
}
cr.Status.Conditions = append(cr.Status.Conditions, c)
}
}
func AddCertificateRequestStatusCondition(c v1.CertificateRequestCondition) CertificateRequestModifier {
return func(cr *v1.CertificateRequest) {
cr.Status.Conditions = append(cr.Status.Conditions, c)
}
}
func SetCertificateRequestNamespace(namespace string) CertificateRequestModifier {
return func(cr *v1.CertificateRequest) {
cr.ObjectMeta.Namespace = namespace
}
}
func SetCertificateRequestName(name string) CertificateRequestModifier {
return func(cr *v1.CertificateRequest) {
cr.ObjectMeta.Name = name
}
}
func SetCertificateRequestGenerateName(generateName string) CertificateRequestModifier {
return func(cr *v1.CertificateRequest) {
cr.ObjectMeta.GenerateName = generateName
}
}
func SetCertificateRequestKeyUsages(usages ...v1.KeyUsage) CertificateRequestModifier {
return func(cr *v1.CertificateRequest) {
cr.Spec.Usages = usages
}
}
func AddCertificateRequestAnnotations(annotations map[string]string) CertificateRequestModifier {
return func(cr *v1.CertificateRequest) {
// Make sure to do a merge here with new annotations overriding.
annotationsNew := cr.GetAnnotations()
if annotationsNew == nil {
annotationsNew = make(map[string]string)
}
for k, v := range annotations {
annotationsNew[k] = v
}
cr.SetAnnotations(annotationsNew)
}
}
func AddCertificateRequestOwnerReferences(owners ...metav1.OwnerReference) CertificateRequestModifier {
return func(cr *v1.CertificateRequest) {
cr.OwnerReferences = append(cr.OwnerReferences, owners...)
}
}
func SetCertificateRequestAnnotations(annotations map[string]string) CertificateRequestModifier {
return func(cr *v1.CertificateRequest) {
if cr.Annotations == nil {
cr.Annotations = make(map[string]string)
}
for k, v := range annotations {
cr.Annotations[k] = v
}
}
}
func DeleteCertificateRequestAnnotation(key string) CertificateRequestModifier {
return func(cr *v1.CertificateRequest) {
if cr.Annotations == nil {
return
}
delete(cr.Annotations, key)
}
}
func SetCertificateRequestFailureTime(p metav1.Time) CertificateRequestModifier {
return func(cr *v1.CertificateRequest) {
cr.Status.FailureTime = &p
}
}
func SetCertificateRequestTypeMeta(tm metav1.TypeMeta) CertificateRequestModifier {
return func(cr *v1.CertificateRequest) {
cr.TypeMeta = tm
}
}
func SetCertificateRequestUsername(username string) CertificateRequestModifier {
return func(cr *v1.CertificateRequest) {
cr.Spec.Username = username
}
}
func SetCertificateRequestGroups(groups []string) CertificateRequestModifier {
return func(cr *v1.CertificateRequest) {
cr.Spec.Groups = groups
}
}
func SetCertificateRequestRevision(rev string) CertificateRequestModifier {
return func(cr *v1.CertificateRequest) {
if cr.Annotations == nil {
cr.Annotations = make(map[string]string)
}
cr.Annotations[v1.CertificateRequestRevisionAnnotationKey] = rev
}
}
/*
Copyright 2021 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package gen
import (
"strconv"
certificatesv1 "k8s.io/api/certificates/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
experimentalapi "github.com/cert-manager/cert-manager/pkg/apis/experimental/v1alpha1"
)
type CertificateSigningRequestModifier func(*certificatesv1.CertificateSigningRequest)
func CertificateSigningRequest(name string, mods ...CertificateSigningRequestModifier) *certificatesv1.CertificateSigningRequest {
c := &certificatesv1.CertificateSigningRequest{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Annotations: make(map[string]string),
Labels: make(map[string]string),
},
}
for _, mod := range mods {
mod(c)
}
return c
}
func CertificateSigningRequestWithRandomName(prefix string, mods ...CertificateSigningRequestModifier) *certificatesv1.CertificateSigningRequest {
c := &certificatesv1.CertificateSigningRequest{
ObjectMeta: metav1.ObjectMeta{
GenerateName: prefix,
Annotations: make(map[string]string),
Labels: make(map[string]string),
},
}
for _, mod := range mods {
mod(c)
}
return c
}
func CertificateSigningRequestFrom(cr *certificatesv1.CertificateSigningRequest, mods ...CertificateSigningRequestModifier) *certificatesv1.CertificateSigningRequest {
cr = cr.DeepCopy()
for _, mod := range mods {
mod(cr)
}
return cr
}
func SetCertificateSigningRequestIsCA(isCA bool) CertificateSigningRequestModifier {
return AddCertificateSigningRequestAnnotations(map[string]string{
experimentalapi.CertificateSigningRequestIsCAAnnotationKey: strconv.FormatBool(isCA),
})
}
func SetCertificateSigningRequestRequest(request []byte) CertificateSigningRequestModifier {
return func(csr *certificatesv1.CertificateSigningRequest) {
csr.Spec.Request = request
}
}
func AddCertificateSigningRequestAnnotations(annotations map[string]string) CertificateSigningRequestModifier {
return func(csr *certificatesv1.CertificateSigningRequest) {
// Make sure to do a merge here with new annotations overriding.
annotationsNew := csr.GetAnnotations()
if annotationsNew == nil {
annotationsNew = make(map[string]string)
}
for k, v := range annotations {
annotationsNew[k] = v
}
csr.SetAnnotations(annotationsNew)
}
}
func SetCertificateSigningRequestSignerName(signerName string) CertificateSigningRequestModifier {
return func(csr *certificatesv1.CertificateSigningRequest) {
csr.Spec.SignerName = signerName
}
}
func SetCertificateSigningRequestExpirationSeconds(seconds int32) CertificateSigningRequestModifier {
return func(csr *certificatesv1.CertificateSigningRequest) {
csr.Spec.ExpirationSeconds = &seconds
}
}
func SetCertificateSigningRequestDuration(duration string) CertificateSigningRequestModifier {
return AddCertificateSigningRequestAnnotations(map[string]string{
experimentalapi.CertificateSigningRequestDurationAnnotationKey: duration,
})
}
func SetCertificateSigningRequestCertificate(cert []byte) CertificateSigningRequestModifier {
return func(csr *certificatesv1.CertificateSigningRequest) {
csr.Status.Certificate = cert
}
}
func SetCertificateSigningRequestStatusCondition(c certificatesv1.CertificateSigningRequestCondition) CertificateSigningRequestModifier {
return func(csr *certificatesv1.CertificateSigningRequest) {
if len(csr.Status.Conditions) == 0 {
csr.Status.Conditions = []certificatesv1.CertificateSigningRequestCondition{c}
return
}
for i, existingC := range csr.Status.Conditions {
if existingC.Type == c.Type {
csr.Status.Conditions[i] = c
return
}
}
csr.Status.Conditions = append(csr.Status.Conditions, c)
}
}
func SetCertificateSigningRequestUsername(username string) CertificateSigningRequestModifier {
return func(csr *certificatesv1.CertificateSigningRequest) {
csr.Spec.Username = username
}
}
func SetCertificateSigningRequestGroups(groups []string) CertificateSigningRequestModifier {
return func(csr *certificatesv1.CertificateSigningRequest) {
csr.Spec.Groups = groups
}
}
func SetCertificateSigningRequestUID(uid string) CertificateSigningRequestModifier {
return func(csr *certificatesv1.CertificateSigningRequest) {
csr.Spec.UID = uid
}
}
func SetCertificateSigningRequestExtra(extra map[string]certificatesv1.ExtraValue) CertificateSigningRequestModifier {
return func(csr *certificatesv1.CertificateSigningRequest) {
csr.Spec.Extra = extra
}
}
func SetCertificateSigningRequestUsages(usages []certificatesv1.KeyUsage) CertificateSigningRequestModifier {
return func(csr *certificatesv1.CertificateSigningRequest) {
csr.Spec.Usages = usages
}
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package gen
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
cmacme "github.com/cert-manager/cert-manager/pkg/apis/acme/v1"
cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
)
type ChallengeModifier func(*cmacme.Challenge)
func Challenge(name string, mods ...ChallengeModifier) *cmacme.Challenge {
c := &cmacme.Challenge{
ObjectMeta: ObjectMeta(name),
}
for _, mod := range mods {
mod(c)
}
return c
}
func ChallengeFrom(ch *cmacme.Challenge, mods ...ChallengeModifier) *cmacme.Challenge {
ch = ch.DeepCopy()
for _, mod := range mods {
mod(ch)
}
return ch
}
func SetChallengeNamespace(ns string) ChallengeModifier {
return func(ch *cmacme.Challenge) {
ch.Namespace = ns
}
}
func SetChallengeType(t cmacme.ACMEChallengeType) ChallengeModifier {
return func(ch *cmacme.Challenge) {
ch.Spec.Type = t
}
}
func SetChallengeToken(t string) ChallengeModifier {
return func(ch *cmacme.Challenge) {
ch.Spec.Token = t
}
}
func SetChallengeKey(k string) ChallengeModifier {
return func(ch *cmacme.Challenge) {
ch.Spec.Key = k
}
}
// SetChallengeIssuer sets the challenge.spec.issuerRef field
func SetChallengeIssuer(o cmmeta.ObjectReference) ChallengeModifier {
return func(c *cmacme.Challenge) {
c.Spec.IssuerRef = o
}
}
func SetChallengeDNSName(dnsName string) ChallengeModifier {
return func(ch *cmacme.Challenge) {
ch.Spec.DNSName = dnsName
}
}
func SetChallengePresented(p bool) ChallengeModifier {
return func(ch *cmacme.Challenge) {
ch.Status.Presented = p
}
}
func SetChallengeWildcard(p bool) ChallengeModifier {
return func(ch *cmacme.Challenge) {
ch.Spec.Wildcard = p
}
}
func SetChallengeState(s cmacme.State) ChallengeModifier {
return func(ch *cmacme.Challenge) {
ch.Status.State = s
}
}
func SetChallengeReason(s string) ChallengeModifier {
return func(ch *cmacme.Challenge) {
ch.Status.Reason = s
}
}
func SetChallengeURL(s string) ChallengeModifier {
return func(ch *cmacme.Challenge) {
ch.Spec.URL = s
}
}
func SetChallengeProcessing(b bool) ChallengeModifier {
return func(ch *cmacme.Challenge) {
ch.Status.Processing = b
}
}
func SetChallengeFinalizers(finalizers []string) ChallengeModifier {
return func(ch *cmacme.Challenge) {
ch.Finalizers = finalizers
}
}
func SetChallengeDeletionTimestamp(ts metav1.Time) ChallengeModifier {
return func(ch *cmacme.Challenge) {
ch.DeletionTimestamp = &ts
}
}
func ResetChallengeStatus() ChallengeModifier {
return func(ch *cmacme.Challenge) {
ch.Status = cmacme.ChallengeStatus{}
}
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package gen
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
)
type IssuerConditionModifier func(*v1.IssuerCondition)
func IssuerCondition(t v1.IssuerConditionType, mods ...IssuerConditionModifier) *v1.IssuerCondition {
c := &v1.IssuerCondition{
Type: t,
}
for _, m := range mods {
m(c)
}
return c
}
func IssuerConditionFrom(c *v1.IssuerCondition, mods ...IssuerConditionModifier) *v1.IssuerCondition {
c = c.DeepCopy()
for _, m := range mods {
m(c)
}
return c
}
func SetIssuerConditionStatus(s cmmeta.ConditionStatus) IssuerConditionModifier {
return func(c *v1.IssuerCondition) {
c.Status = s
}
}
func SetIssuerConditionLastTransitionTime(t *metav1.Time) IssuerConditionModifier {
return func(c *v1.IssuerCondition) {
c.LastTransitionTime = t
}
}
func SetIssuerConditionReason(s string) IssuerConditionModifier {
return func(c *v1.IssuerCondition) {
c.Reason = s
}
}
func SetIssuerConditionMessage(s string) IssuerConditionModifier {
return func(c *v1.IssuerCondition) {
c.Message = s
}
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package gen
import (
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"net"
"net/url"
v1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
"github.com/cert-manager/cert-manager/pkg/util/pki"
)
type CSRModifier func(*x509.CertificateRequest) error
var defaultGenerateCSROptions = []pki.GenerateCSROption{
pki.WithEncodeBasicConstraintsInRequest(true),
pki.WithNameConstraints(true),
pki.WithOtherNames(true),
pki.WithUseLiteralSubject(true),
}
func CSRForCertificate(crt *v1.Certificate, mods ...CSRModifier) (csr []byte, sk crypto.Signer, err error) {
cr, err := pki.GenerateCSR(crt, defaultGenerateCSROptions...)
if err != nil {
return nil, nil, err
}
modifiers := []CSRModifier{}
modifiers = append(modifiers, func(c *x509.CertificateRequest) error {
*c = *cr
return nil
})
modifiers = append(modifiers, mods...)
return CSR(cr.PublicKeyAlgorithm, modifiers...)
}
func CSRWithSignerForCertificate(crt *v1.Certificate, sk crypto.Signer, mods ...CSRModifier) (csr []byte, err error) {
cr, err := pki.GenerateCSR(crt, defaultGenerateCSROptions...)
if err != nil {
return nil, err
}
modifiers := []CSRModifier{}
modifiers = append(modifiers, func(c *x509.CertificateRequest) error {
if c.PublicKeyAlgorithm != cr.PublicKeyAlgorithm {
return fmt.Errorf("public key algorithm mismatch: %s != %s", c.PublicKeyAlgorithm, cr.PublicKeyAlgorithm)
}
if c.SignatureAlgorithm != cr.SignatureAlgorithm {
return fmt.Errorf("signature algorithm mismatch: %s != %s", c.SignatureAlgorithm, cr.SignatureAlgorithm)
}
*c = *cr
return nil
})
modifiers = append(modifiers, mods...)
return CSRWithSigner(sk, modifiers...)
}
func CSR(keyAlgorithm x509.PublicKeyAlgorithm, mods ...CSRModifier) (csr []byte, sk crypto.Signer, err error) {
switch keyAlgorithm {
case x509.RSA:
sk, err = pki.GenerateRSAPrivateKey(pki.MinRSAKeySize)
if err != nil {
return nil, nil, err
}
case x509.ECDSA:
sk, err = pki.GenerateECPrivateKey(pki.ECCurve256)
if err != nil {
return nil, nil, err
}
case x509.Ed25519:
sk, err = pki.GenerateEd25519PrivateKey()
if err != nil {
return nil, nil, err
}
default:
return nil, nil, fmt.Errorf("unrecognised key algorithm: %s", keyAlgorithm)
}
csr, err = CSRWithSigner(sk, mods...)
return
}
func CSRWithSigner(sk crypto.Signer, mods ...CSRModifier) (csr []byte, err error) {
var keyAlgorithm x509.PublicKeyAlgorithm
var signatureAlgorithm x509.SignatureAlgorithm
switch pub := sk.Public().(type) {
case *rsa.PublicKey:
keyAlgorithm = x509.RSA
keySize := pub.N.BitLen()
switch {
case keySize >= 4096:
signatureAlgorithm = x509.SHA512WithRSA
case keySize >= 3072:
signatureAlgorithm = x509.SHA384WithRSA
case keySize >= 2048:
signatureAlgorithm = x509.SHA256WithRSA
default:
signatureAlgorithm = x509.SHA1WithRSA
}
case *ecdsa.PublicKey:
keyAlgorithm = x509.ECDSA
switch pub.Curve {
case elliptic.P256():
signatureAlgorithm = x509.ECDSAWithSHA256
case elliptic.P384():
signatureAlgorithm = x509.ECDSAWithSHA384
case elliptic.P521():
signatureAlgorithm = x509.ECDSAWithSHA512
default:
signatureAlgorithm = x509.ECDSAWithSHA1
}
case ed25519.PublicKey:
keyAlgorithm = x509.Ed25519
signatureAlgorithm = x509.PureEd25519
default:
return nil, fmt.Errorf("unrecognised public key type: %T", sk)
}
cr := &x509.CertificateRequest{
Version: 0,
SignatureAlgorithm: signatureAlgorithm,
PublicKeyAlgorithm: keyAlgorithm,
PublicKey: sk.Public(),
}
for _, mod := range mods {
err = mod(cr)
if err != nil {
return
}
}
csrBytes, err := pki.EncodeCSR(cr, sk)
if err != nil {
return nil, err
}
csr = pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE REQUEST", Bytes: csrBytes,
})
return
}
func SetCSRDNSNames(dnsNames ...string) CSRModifier {
return func(c *x509.CertificateRequest) error {
c.DNSNames = dnsNames
return nil
}
}
func SetCSRIPAddresses(ips ...net.IP) CSRModifier {
return func(c *x509.CertificateRequest) error {
c.IPAddresses = ips
return nil
}
}
func SetCSRIPAddressesFromStrings(ips ...string) CSRModifier {
return func(c *x509.CertificateRequest) error {
var certIPs []net.IP
for _, ip := range ips {
if certIP := net.ParseIP(ip); certIP == nil {
return fmt.Errorf("invalid ip: %s", ip)
} else {
certIPs = append(certIPs, certIP)
}
}
c.IPAddresses = certIPs
return nil
}
}
func SetCSRURIs(uris ...*url.URL) CSRModifier {
return func(c *x509.CertificateRequest) error {
c.URIs = uris
return nil
}
}
func SetCSRURIsFromStrings(uris ...string) CSRModifier {
return func(c *x509.CertificateRequest) error {
var certUris []*url.URL
for _, uri := range uris {
parsed, err := url.Parse(uri)
if err != nil {
return err
}
certUris = append(certUris, parsed)
}
c.URIs = certUris
return nil
}
}
func SetCSRCommonName(commonName string) CSRModifier {
return func(c *x509.CertificateRequest) error {
c.Subject.CommonName = commonName
return nil
}
}
func SetCSREmails(emails []string) CSRModifier {
return func(c *x509.CertificateRequest) error {
c.EmailAddresses = emails
return nil
}
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package gen
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
cmacme "github.com/cert-manager/cert-manager/pkg/apis/acme/v1"
v1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
)
type IssuerModifier func(v1.GenericIssuer)
func ClusterIssuer(name string, mods ...IssuerModifier) *v1.ClusterIssuer {
c := &v1.ClusterIssuer{
ObjectMeta: ObjectMeta(name),
}
c.ObjectMeta.Namespace = ""
for _, mod := range mods {
mod(c)
}
return c
}
func ClusterIssuerFrom(iss *v1.ClusterIssuer, mods ...IssuerModifier) *v1.ClusterIssuer {
for _, mod := range mods {
mod(iss)
}
return iss
}
// ClusterIssuerWithRandomName returns a ClusterIssuer named 'prefix<random-string>'
// with the specified modifications.
func ClusterIssuerWithRandomName(prefix string, mods ...IssuerModifier) *v1.ClusterIssuer {
iss := &v1.ClusterIssuer{
ObjectMeta: metav1.ObjectMeta{
GenerateName: prefix,
},
}
for _, mod := range mods {
mod(iss)
}
return iss
}
func Issuer(name string, mods ...IssuerModifier) *v1.Issuer {
c := &v1.Issuer{
ObjectMeta: ObjectMeta(name),
}
for _, mod := range mods {
mod(c)
}
return c
}
func IssuerFrom(iss *v1.Issuer, mods ...IssuerModifier) *v1.Issuer {
iss = iss.DeepCopy()
for _, mod := range mods {
mod(iss)
}
return iss
}
// IssuerWithRandomName returns a new Issuer named prefix<random-string>
// with the provided modifications.
func IssuerWithRandomName(prefix string, mods ...IssuerModifier) *v1.Issuer {
iss := &v1.Issuer{
ObjectMeta: metav1.ObjectMeta{
GenerateName: prefix,
},
}
for _, mod := range mods {
mod(iss)
}
return iss
}
func SetIssuerACME(a cmacme.ACMEIssuer) IssuerModifier {
return func(iss v1.GenericIssuer) {
iss.GetSpec().ACME = &a
}
}
func SetIssuerACMEPreferredChain(chain string) IssuerModifier {
return func(iss v1.GenericIssuer) {
spec := iss.GetSpec()
if spec.ACME == nil {
spec.ACME = &cmacme.ACMEIssuer{}
}
spec.ACME.PreferredChain = chain
}
}
func SetIssuerACMEURL(url string) IssuerModifier {
return func(iss v1.GenericIssuer) {
spec := iss.GetSpec()
if spec.ACME == nil {
spec.ACME = &cmacme.ACMEIssuer{}
}
spec.ACME.Server = url
}
}
func SetIssuerACMEEmail(email string) IssuerModifier {
return func(iss v1.GenericIssuer) {
spec := iss.GetSpec()
if spec.ACME == nil {
spec.ACME = &cmacme.ACMEIssuer{}
}
spec.ACME.Email = email
}
}
func SetIssuerACMEProfile(profile string) IssuerModifier {
return func(iss v1.GenericIssuer) {
spec := iss.GetSpec()
if spec.ACME == nil {
spec.ACME = &cmacme.ACMEIssuer{}
}
spec.ACME.Profile = profile
}
}
func SetIssuerACMEPrivKeyRef(privateKeyName string) IssuerModifier {
return func(iss v1.GenericIssuer) {
spec := iss.GetSpec()
if spec.ACME == nil {
spec.ACME = &cmacme.ACMEIssuer{}
}
spec.ACME.PrivateKey = cmmeta.SecretKeySelector{
LocalObjectReference: cmmeta.LocalObjectReference{
Name: privateKeyName,
},
}
}
}
func SetIssuerACMESolvers(solvers []cmacme.ACMEChallengeSolver) IssuerModifier {
return func(iss v1.GenericIssuer) {
spec := iss.GetSpec()
if spec.ACME == nil {
spec.ACME = &cmacme.ACMEIssuer{}
}
spec.ACME.Solvers = solvers
}
}
func SetIssuerACMEDuration(enabled bool) IssuerModifier {
return func(iss v1.GenericIssuer) {
spec := iss.GetSpec()
if spec.ACME == nil {
spec.ACME = &cmacme.ACMEIssuer{}
}
spec.ACME.EnableDurationFeature = enabled
}
}
func SetIssuerACMESkipTLSVerify(shouldSkip bool) IssuerModifier {
return func(iss v1.GenericIssuer) {
spec := iss.GetSpec()
if spec.ACME == nil {
spec.ACME = &cmacme.ACMEIssuer{}
}
spec.ACME.SkipTLSVerify = shouldSkip
}
}
func SetIssuerACMEDisableAccountKeyGeneration(disabled bool) IssuerModifier {
return func(iss v1.GenericIssuer) {
spec := iss.GetSpec()
if spec.ACME == nil {
spec.ACME = &cmacme.ACMEIssuer{}
}
spec.ACME.DisableAccountKeyGeneration = disabled
}
}
func SetIssuerACMEEAB(keyID, secretName string) IssuerModifier {
return func(iss v1.GenericIssuer) {
spec := iss.GetSpec()
if spec.ACME == nil {
spec.ACME = &cmacme.ACMEIssuer{}
}
spec.ACME.ExternalAccountBinding = &cmacme.ACMEExternalAccountBinding{
KeyID: keyID,
Key: cmmeta.SecretKeySelector{
Key: "key",
LocalObjectReference: cmmeta.LocalObjectReference{
Name: secretName,
},
},
}
}
}
// SetIssuerACMEEABWithKeyAlgorithm returns an ACME Issuer modifier that sets
// ACME External Account Binding with the legacy keyAlgorithm field set.
func SetIssuerACMEEABWithKeyAlgorithm(keyID, secretName string, keyAlgorithm cmacme.HMACKeyAlgorithm) IssuerModifier {
return func(iss v1.GenericIssuer) {
spec := iss.GetSpec()
if spec.ACME == nil {
spec.ACME = &cmacme.ACMEIssuer{}
}
spec.ACME.ExternalAccountBinding = &cmacme.ACMEExternalAccountBinding{
KeyID: keyID,
KeyAlgorithm: keyAlgorithm,
Key: cmmeta.SecretKeySelector{
Key: "key",
LocalObjectReference: cmmeta.LocalObjectReference{
Name: secretName,
},
},
}
}
}
func SetIssuerACMEAccountURL(url string) IssuerModifier {
return func(iss v1.GenericIssuer) {
status := iss.GetStatus()
if status.ACME == nil {
status.ACME = &cmacme.ACMEIssuerStatus{}
}
status.ACME.URI = url
}
}
func SetIssuerACMELastRegisteredEmail(email string) IssuerModifier {
return func(iss v1.GenericIssuer) {
status := iss.GetStatus()
if status.ACME == nil {
status.ACME = &cmacme.ACMEIssuerStatus{}
}
status.ACME.LastRegisteredEmail = email
}
}
func SetIssuerACMELastPrivateKeyHash(privateKeyHash string) IssuerModifier {
return func(iss v1.GenericIssuer) {
status := iss.GetStatus()
if status.ACME == nil {
status.ACME = &cmacme.ACMEIssuerStatus{}
}
status.ACME.LastPrivateKeyHash = privateKeyHash
}
}
func SetIssuerCA(a v1.CAIssuer) IssuerModifier {
return func(iss v1.GenericIssuer) {
iss.GetSpec().CA = &a
}
}
func SetIssuerCASecretName(secretName string) IssuerModifier {
return func(iss v1.GenericIssuer) {
spec := iss.GetSpec()
if spec.CA == nil {
spec.CA = &v1.CAIssuer{}
}
spec.CA.SecretName = secretName
}
}
func SetIssuerVault(v v1.VaultIssuer) IssuerModifier {
return func(iss v1.GenericIssuer) {
iss.GetSpec().Vault = &v
}
}
func SetIssuerVaultURL(url string) IssuerModifier {
return func(iss v1.GenericIssuer) {
spec := iss.GetSpec()
if spec.Vault == nil {
spec.Vault = &v1.VaultIssuer{}
}
spec.Vault.Server = url
}
}
func SetIssuerVaultPath(path string) IssuerModifier {
return func(iss v1.GenericIssuer) {
spec := iss.GetSpec()
if spec.Vault == nil {
spec.Vault = &v1.VaultIssuer{}
}
spec.Vault.Path = path
}
}
func SetIssuerVaultCABundle(caBundle []byte) IssuerModifier {
return func(iss v1.GenericIssuer) {
spec := iss.GetSpec()
if spec.Vault == nil {
spec.Vault = &v1.VaultIssuer{}
}
spec.Vault.CABundle = caBundle
}
}
func SetIssuerVaultCABundleSecretRef(name, namespace, key string) IssuerModifier {
return func(iss v1.GenericIssuer) {
spec := iss.GetSpec()
if spec.Vault == nil {
spec.Vault = &v1.VaultIssuer{}
}
spec.Vault.CABundleSecretRef = &cmmeta.SecretKeySelector{
LocalObjectReference: cmmeta.LocalObjectReference{
Name: name,
},
Key: key,
}
}
}
func SetIssuerVaultClientCertSecretRef(vaultClientCertificateSecretName, key string) IssuerModifier {
return func(iss v1.GenericIssuer) {
spec := iss.GetSpec()
if spec.Vault == nil {
spec.Vault = &v1.VaultIssuer{}
}
spec.Vault.ClientCertSecretRef = &cmmeta.SecretKeySelector{
LocalObjectReference: cmmeta.LocalObjectReference{
Name: vaultClientCertificateSecretName,
},
Key: key,
}
}
}
func SetIssuerVaultClientKeySecretRef(vaultClientCertificateSecretName, key string) IssuerModifier {
return func(iss v1.GenericIssuer) {
spec := iss.GetSpec()
if spec.Vault == nil {
spec.Vault = &v1.VaultIssuer{}
}
spec.Vault.ClientKeySecretRef = &cmmeta.SecretKeySelector{
LocalObjectReference: cmmeta.LocalObjectReference{
Name: vaultClientCertificateSecretName,
},
Key: key,
}
}
}
func SetIssuerVaultTokenAuth(keyName, tokenName string) IssuerModifier {
return func(iss v1.GenericIssuer) {
spec := iss.GetSpec()
if spec.Vault == nil {
spec.Vault = &v1.VaultIssuer{}
}
spec.Vault.Auth.TokenSecretRef = &cmmeta.SecretKeySelector{
Key: keyName,
LocalObjectReference: cmmeta.LocalObjectReference{
Name: tokenName,
},
}
}
}
func SetIssuerVaultAppRoleAuth(keyName, approleName, roleId, path string) IssuerModifier {
return func(iss v1.GenericIssuer) {
spec := iss.GetSpec()
if spec.Vault == nil {
spec.Vault = &v1.VaultIssuer{}
}
spec.Vault.Auth.AppRole = &v1.VaultAppRole{
Path: path,
RoleId: roleId,
SecretRef: cmmeta.SecretKeySelector{
Key: keyName,
LocalObjectReference: cmmeta.LocalObjectReference{
Name: approleName,
},
},
}
}
}
func SetIssuerVaultClientCertificateAuth(path, secretName string) IssuerModifier {
return func(iss v1.GenericIssuer) {
spec := iss.GetSpec()
if spec.Vault == nil {
spec.Vault = &v1.VaultIssuer{}
}
spec.Vault.Auth.ClientCertificate = &v1.VaultClientCertificateAuth{
Path: path,
SecretName: secretName,
}
}
}
func SetIssuerVaultKubernetesAuthSecret(secretKey, secretName, vaultRole, vaultPath string) IssuerModifier {
return func(iss v1.GenericIssuer) {
spec := iss.GetSpec()
if spec.Vault == nil {
spec.Vault = &v1.VaultIssuer{}
}
spec.Vault.Auth.Kubernetes = &v1.VaultKubernetesAuth{
Path: vaultPath,
SecretRef: cmmeta.SecretKeySelector{
Key: secretKey,
LocalObjectReference: cmmeta.LocalObjectReference{
Name: secretName,
},
},
Role: vaultRole,
}
}
}
func SetIssuerVaultKubernetesAuthServiceAccount(serviceAccount, role, path string) IssuerModifier {
return func(iss v1.GenericIssuer) {
spec := iss.GetSpec()
if spec.Vault == nil {
spec.Vault = &v1.VaultIssuer{}
}
spec.Vault.Auth.Kubernetes = &v1.VaultKubernetesAuth{
Path: path,
Role: role,
ServiceAccountRef: &v1.ServiceAccountRef{
Name: serviceAccount,
},
}
}
}
func SetIssuerSelfSigned(a v1.SelfSignedIssuer) IssuerModifier {
return func(iss v1.GenericIssuer) {
iss.GetSpec().SelfSigned = &a
}
}
func SetIssuerVenafi(a v1.VenafiIssuer) IssuerModifier {
return func(iss v1.GenericIssuer) {
iss.GetSpec().Venafi = &a
}
}
func AddIssuerCondition(c v1.IssuerCondition) IssuerModifier {
return func(iss v1.GenericIssuer) {
iss.GetStatus().Conditions = append(iss.GetStatus().Conditions, c)
}
}
func SetIssuerNamespace(namespace string) IssuerModifier {
return func(iss v1.GenericIssuer) {
iss.GetObjectMeta().Namespace = namespace
}
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package gen
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
const (
// DefaultTestNamespace is the default namespace set on resources that
// are namespaced.
DefaultTestNamespace = "default-unit-test-ns"
)
// ObjectMetaModifier applies a transformation to the provider ObjectMeta
type ObjectMetaModifier func(*metav1.ObjectMeta)
// ObjectMeta creates a new metav1.ObjectMeta with the given name, optionally
// applying the provided ObjectMetaModifiers.
// It applies a DefaultTestNamespace by default.
// Cluster-scoped resource generators should explicitly add `SetNamespace("")`
// to their constructors.
func ObjectMeta(name string, mods ...ObjectMetaModifier) metav1.ObjectMeta {
m := &metav1.ObjectMeta{
Name: name,
Namespace: DefaultTestNamespace,
}
for _, mod := range mods {
mod(m)
}
return *m
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package gen
import (
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
cmacme "github.com/cert-manager/cert-manager/pkg/apis/acme/v1"
cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
)
type OrderModifier func(*cmacme.Order)
func Order(name string, mods ...OrderModifier) *cmacme.Order {
order := &cmacme.Order{
ObjectMeta: ObjectMeta(name),
}
for _, mod := range mods {
mod(order)
}
return order
}
func OrderFrom(order *cmacme.Order, mods ...OrderModifier) *cmacme.Order {
order = order.DeepCopy()
for _, mod := range mods {
mod(order)
}
return order
}
// SetOrderIssuer sets the Order.spec.issuerRef field
func SetOrderIssuer(o cmmeta.ObjectReference) OrderModifier {
return func(order *cmacme.Order) {
order.Spec.IssuerRef = o
}
}
func SetOrderDNSNames(dnsNames ...string) OrderModifier {
return func(order *cmacme.Order) {
order.Spec.DNSNames = dnsNames
}
}
func SetOrderIPAddresses(ips ...string) OrderModifier {
return func(order *cmacme.Order) {
order.Spec.IPAddresses = ips
}
}
func SetOrderURL(url string) OrderModifier {
return func(order *cmacme.Order) {
order.Status.URL = url
}
}
func SetOrderState(s cmacme.State) OrderModifier {
return func(order *cmacme.Order) {
order.Status.State = s
}
}
func SetOrderReason(reason string) OrderModifier {
return func(order *cmacme.Order) {
order.Status.Reason = reason
}
}
func SetOrderStatus(s cmacme.OrderStatus) OrderModifier {
return func(order *cmacme.Order) {
order.Status = s
}
}
func SetOrderCertificate(d []byte) OrderModifier {
return func(order *cmacme.Order) {
order.Status.Certificate = d
}
}
func SetOrderCommonName(commonName string) OrderModifier {
return func(order *cmacme.Order) {
order.Spec.CommonName = commonName
}
}
func SetOrderNamespace(namespace string) OrderModifier {
return func(order *cmacme.Order) {
order.ObjectMeta.Namespace = namespace
}
}
func SetOrderCsr(csr []byte) OrderModifier {
return func(order *cmacme.Order) {
order.Spec.Request = csr
}
}
func SetOrderDuration(duration time.Duration) OrderModifier {
return func(order *cmacme.Order) {
order.Spec.Duration = &metav1.Duration{Duration: duration}
}
}
func SetOrderAnnotations(annotations map[string]string) OrderModifier {
return func(order *cmacme.Order) {
order.Annotations = annotations
}
}
func SetOrderOwnerReference(ref metav1.OwnerReference) OrderModifier {
return func(order *cmacme.Order) {
order.OwnerReferences = []metav1.OwnerReference{ref}
}
}
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package gen
import (
corev1 "k8s.io/api/core/v1"
)
type SecretModifier func(*corev1.Secret)
func Secret(name string, mods ...SecretModifier) *corev1.Secret {
c := &corev1.Secret{
ObjectMeta: ObjectMeta(name),
}
for _, mod := range mods {
mod(c)
}
return c
}
func SecretFrom(sec *corev1.Secret, mods ...SecretModifier) *corev1.Secret {
sec = sec.DeepCopy()
for _, mod := range mods {
mod(sec)
}
return sec
}
func SetSecretNamespace(namespace string) SecretModifier {
return func(sec *corev1.Secret) {
sec.ObjectMeta.Namespace = namespace
}
}
func SetSecretAnnotations(an map[string]string) SecretModifier {
return func(sec *corev1.Secret) {
sec.Annotations = make(map[string]string)
for k, v := range an {
sec.Annotations[k] = v
}
}
}
func SetSecretData(data map[string][]byte) SecretModifier {
return func(sec *corev1.Secret) {
sec.Data = make(map[string][]byte)
for k, v := range data {
sec.Data[k] = v
}
}
}