Configurable issuer duration and renewBefore Take 2 (#893)
* Configurable issuer duration and renewBefore [1/3] This is part one of (probably) three parts manually moving the changes from commit 723015174a167d746323f506ab3575cfb243d8bd to the new master. This commit moves the basic functionality of configurable duration while skipping e2e tests and docs. It does not include new work. Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Configurable issuer duration and renewBefore [2/3] This commit moves over most of the e2e testing updates, some things are intentionally left out as they may be obsolete Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Configurable issuer duration and renewBefore [3/3] This commit moves the documentation changes, completely the migration of the original code to the latest master Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Rerunning all hack scripts with since the massive bazel update Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Add missing boilerplate headers Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Rerun codegen hack Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Rerunning update-docs hack Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Fix failing unit tests Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Fix build errors in e2e tests Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Rerun update-deps Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Don't recreate the CA issuer, it already exists Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Need to create new issuers for the duration and renew time tests because those fields are set in the issuer, so make sure they are named uniquely Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Add duration e2e tests for self-signed issuer Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Add duration e2e tests for vault w/ custom mount path Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Add validation to disallow acme certificates with duration and renewBefore set and update unit tests to verify Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Update docs to mention duration/renew for self-signed issuer and fix potential parsing errors with rst formatting Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Self-signed issuer was missing duration validation Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Fix a bug causing certificates with a short enough renew-before w.r.t their duration to be renewed instantly and forever Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Print the exact time until renewal Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Move duration and renwal validation to the issuer validation Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Update e2e tests to work with new validation Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Add e2e test for the self-signed issuer Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Redo cert duration and renew before to appear as part of the CSR and not the issuer Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Updating tests to match new duration/renewbefore format Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Update e2e tests to match new format Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Update docs to reflect changing the field from issuers to certificates Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Remove event firing and replace with a TODO as of discussion on PR Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Run hack scripts Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Remove the sync unit test since without events there is no way to catch the warnings that it was testing Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Use IssuerOptions RenewBeforeExpiryDuration if certificates dont set a renewBefore value for immediate renewal checks Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Delete check on certificate data length in e2e test for certificate duration as there is no reason it should be there Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Update e2e tests since certificate creation will never generate an event Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Rerunning hack scripts after big rebase Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Fix a few problems that slipped through during the rebase Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Fix an e2e error that resulted from the rebase Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Add unit test for the calculateTimeBeforeExpiry function Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Adding back in a bunch of missing error checks Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Remove unused function Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Add missing boilerplate Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Remove unused constant Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Move log constants to function body Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Rerun hack scripts Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Remove mistakenly commited file Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Remove double-import of util package Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Fix bad function call in e2e vault issuer Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Change duration and renewBefore to be pointer fields as they are optional Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Remove wrong vault issuer test that got passed the rebase somehow Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Change e2e to use pointer format Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Move e2e cert tests out of issuer test file Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Move e2e self-signed issuer test to new location Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Make sure to check for nil in GenerateTemplate Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Add more empty checks to be safe Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Rerunning hacks after rebase Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Fix bad function call in new e2e test Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Try not setting duration and renewbefore on acme e2e tests Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Zero checks should really just be replaced by nil tests, zero should be caught as any other too-small value Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Fixed a missing nil check that got away Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Change e2e duration test format to use pointer times to better simulate API calls Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Fix sync unit test to match e2e test format Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Fix vault e2e test Signed-off-by: Max Ehrlich <max.ehr@gmail.com> * Revert changes to Certificate sync function Signed-off-by: James Munnelly <james@munnelly.eu> * Remove selfsigned e2e issuer.go Signed-off-by: James Munnelly <james@munnelly.eu> * Don't use ACME issuer in duration example and tidy up line endings Signed-off-by: James Munnelly <james@munnelly.eu> * Allow renewBefore to be set on ACME certificates Signed-off-by: James Munnelly <james@munnelly.eu> * Update renewBefore ACME docs. Remove unused fields. Signed-off-by: James Munnelly <james@munnelly.eu> * Rename calculateTimeBeforeExpiry to calculateDurationUntilRenew Signed-off-by: James Munnelly <james@munnelly.eu>
This commit is contained in:
parent
c3b4bc88ee
commit
bbd9249198
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
||||
(function(){navData = {"toc":[{"section":"-strong-field-definitions-strong-","subsections":[{"section":"vaultissuer-v1alpha1"},{"section":"vaultauth-v1alpha1"},{"section":"vaultapprole-v1alpha1"},{"section":"time-v1"},{"section":"statusdetails-v1"},{"section":"statuscause-v1"},{"section":"status-v1"},{"section":"solverconfig-v1alpha1"},{"section":"selfsignedissuer-v1alpha1"},{"section":"secretkeyselector-v1alpha1"},{"section":"ownerreference-v1"},{"section":"objectreference-v1alpha1"},{"section":"objectmeta-v1"},{"section":"listmeta-v1"},{"section":"issuercondition-v1alpha1"},{"section":"initializers-v1"},{"section":"initializer-v1"},{"section":"http01solverconfig-v1alpha1"},{"section":"domainsolverconfig-v1alpha1"},{"section":"dns01solverconfig-v1alpha1"},{"section":"certificatecondition-v1alpha1"},{"section":"caissuer-v1alpha1"},{"section":"acmeissuerhttp01config-v1alpha1"},{"section":"acmeissuerdns01providerroute53-v1alpha1"},{"section":"acmeissuerdns01providerrfc2136-v1alpha1"},{"section":"acmeissuerdns01providerdigitalocean-v1alpha1"},{"section":"acmeissuerdns01providercloudflare-v1alpha1"},{"section":"acmeissuerdns01providerclouddns-v1alpha1"},{"section":"acmeissuerdns01providerazuredns-v1alpha1"},{"section":"acmeissuerdns01providerakamai-v1alpha1"},{"section":"acmeissuerdns01provideracmedns-v1alpha1"},{"section":"acmeissuerdns01provider-v1alpha1"},{"section":"acmeissuerdns01config-v1alpha1"},{"section":"acmeissuer-v1alpha1"},{"section":"acmecertificateconfig-v1alpha1"}]},{"section":"-strong-old-api-versions-strong-","subsections":[]},{"section":"challenge-v1alpha1","subsections":[]},{"section":"order-v1alpha1","subsections":[]},{"section":"issuer-v1alpha1","subsections":[]},{"section":"clusterissuer-v1alpha1","subsections":[]},{"section":"certificate-v1alpha1","subsections":[]},{"section":"-strong-cert-manager-strong-","subsections":[]}],"flatToc":["vaultissuer-v1alpha1","vaultauth-v1alpha1","vaultapprole-v1alpha1","time-v1","statusdetails-v1","statuscause-v1","status-v1","solverconfig-v1alpha1","selfsignedissuer-v1alpha1","secretkeyselector-v1alpha1","ownerreference-v1","objectreference-v1alpha1","objectmeta-v1","listmeta-v1","issuercondition-v1alpha1","initializers-v1","initializer-v1","http01solverconfig-v1alpha1","domainsolverconfig-v1alpha1","dns01solverconfig-v1alpha1","certificatecondition-v1alpha1","caissuer-v1alpha1","acmeissuerhttp01config-v1alpha1","acmeissuerdns01providerroute53-v1alpha1","acmeissuerdns01providerrfc2136-v1alpha1","acmeissuerdns01providerdigitalocean-v1alpha1","acmeissuerdns01providercloudflare-v1alpha1","acmeissuerdns01providerclouddns-v1alpha1","acmeissuerdns01providerazuredns-v1alpha1","acmeissuerdns01providerakamai-v1alpha1","acmeissuerdns01provideracmedns-v1alpha1","acmeissuerdns01provider-v1alpha1","acmeissuerdns01config-v1alpha1","acmeissuer-v1alpha1","acmecertificateconfig-v1alpha1","-strong-field-definitions-strong-","-strong-old-api-versions-strong-","challenge-v1alpha1","order-v1alpha1","issuer-v1alpha1","clusterissuer-v1alpha1","certificate-v1alpha1","-strong-cert-manager-strong-"]};})();
|
||||
(function(){navData = {"toc":[{"section":"-strong-field-definitions-strong-","subsections":[{"section":"vaultissuer-v1alpha1"},{"section":"vaultauth-v1alpha1"},{"section":"vaultapprole-v1alpha1"},{"section":"time-v1"},{"section":"statusdetails-v1"},{"section":"statuscause-v1"},{"section":"status-v1"},{"section":"solverconfig-v1alpha1"},{"section":"selfsignedissuer-v1alpha1"},{"section":"secretkeyselector-v1alpha1"},{"section":"ownerreference-v1"},{"section":"objectreference-v1alpha1"},{"section":"objectmeta-v1"},{"section":"listmeta-v1"},{"section":"issuercondition-v1alpha1"},{"section":"initializers-v1"},{"section":"initializer-v1"},{"section":"http01solverconfig-v1alpha1"},{"section":"duration-v1"},{"section":"domainsolverconfig-v1alpha1"},{"section":"dns01solverconfig-v1alpha1"},{"section":"certificatecondition-v1alpha1"},{"section":"caissuer-v1alpha1"},{"section":"acmeissuerhttp01config-v1alpha1"},{"section":"acmeissuerdns01providerroute53-v1alpha1"},{"section":"acmeissuerdns01providerrfc2136-v1alpha1"},{"section":"acmeissuerdns01providerdigitalocean-v1alpha1"},{"section":"acmeissuerdns01providercloudflare-v1alpha1"},{"section":"acmeissuerdns01providerclouddns-v1alpha1"},{"section":"acmeissuerdns01providerazuredns-v1alpha1"},{"section":"acmeissuerdns01providerakamai-v1alpha1"},{"section":"acmeissuerdns01provideracmedns-v1alpha1"},{"section":"acmeissuerdns01provider-v1alpha1"},{"section":"acmeissuerdns01config-v1alpha1"},{"section":"acmeissuer-v1alpha1"},{"section":"acmecertificateconfig-v1alpha1"}]},{"section":"-strong-old-api-versions-strong-","subsections":[]},{"section":"challenge-v1alpha1","subsections":[]},{"section":"order-v1alpha1","subsections":[]},{"section":"issuer-v1alpha1","subsections":[]},{"section":"clusterissuer-v1alpha1","subsections":[]},{"section":"certificate-v1alpha1","subsections":[]},{"section":"-strong-cert-manager-strong-","subsections":[]}],"flatToc":["vaultissuer-v1alpha1","vaultauth-v1alpha1","vaultapprole-v1alpha1","time-v1","statusdetails-v1","statuscause-v1","status-v1","solverconfig-v1alpha1","selfsignedissuer-v1alpha1","secretkeyselector-v1alpha1","ownerreference-v1","objectreference-v1alpha1","objectmeta-v1","listmeta-v1","issuercondition-v1alpha1","initializers-v1","initializer-v1","http01solverconfig-v1alpha1","duration-v1","domainsolverconfig-v1alpha1","dns01solverconfig-v1alpha1","certificatecondition-v1alpha1","caissuer-v1alpha1","acmeissuerhttp01config-v1alpha1","acmeissuerdns01providerroute53-v1alpha1","acmeissuerdns01providerrfc2136-v1alpha1","acmeissuerdns01providerdigitalocean-v1alpha1","acmeissuerdns01providercloudflare-v1alpha1","acmeissuerdns01providerclouddns-v1alpha1","acmeissuerdns01providerazuredns-v1alpha1","acmeissuerdns01providerakamai-v1alpha1","acmeissuerdns01provideracmedns-v1alpha1","acmeissuerdns01provider-v1alpha1","acmeissuerdns01config-v1alpha1","acmeissuer-v1alpha1","acmecertificateconfig-v1alpha1","-strong-field-definitions-strong-","-strong-old-api-versions-strong-","challenge-v1alpha1","order-v1alpha1","issuer-v1alpha1","clusterissuer-v1alpha1","certificate-v1alpha1","-strong-cert-manager-strong-"]};})();
|
||||
@ -35,17 +35,18 @@ A simple Certificate could be defined as:
|
||||
|
||||
This Certificate will tell cert-manager to attempt to use the Issuer
|
||||
named ``letsencrypt-prod`` to obtain a certificate key pair for the
|
||||
``foo.example.com`` and ``bar.example.com`` domains. If successful, the resulting
|
||||
key and certificate will be stored in a secret named ``acme-crt-secret`` with
|
||||
keys of ``tls.key`` and ``tls.crt`` respectively. This secret will live in the
|
||||
same namespace as the ``Certificate`` resource.
|
||||
``foo.example.com`` and ``bar.example.com`` domains. If successful, the
|
||||
resulting key and certificate will be stored in a secret named
|
||||
``acme-crt-secret`` with keys of ``tls.key`` and ``tls.crt`` respectively.
|
||||
This secret will live in the same namespace as the ``Certificate`` resource.
|
||||
|
||||
The ``dnsNames`` field specifies a list of `Subject Alternative Names`_ to be
|
||||
associated with the certificate. If the ``commonName`` field is omitted, the
|
||||
first element in the list will be the common name.
|
||||
|
||||
The referenced Issuer must exist in the same namespace as the Certificate. A
|
||||
Certificate can alternatively reference a ClusterIssuer which is non-namespaced.
|
||||
The referenced Issuer must exist in the same namespace as the Certificate.
|
||||
A Certificate can alternatively reference a ClusterIssuer which is
|
||||
non-namespaced.
|
||||
|
||||
.. _`Subject Alternative Names`: https://en.wikipedia.org/wiki/Subject_Alternative_Name
|
||||
|
||||
@ -54,3 +55,63 @@ Certificate can alternatively reference a ClusterIssuer which is non-namespaced.
|
||||
:hidden:
|
||||
|
||||
certificates/issuer-specific-config/acme
|
||||
|
||||
***************************************
|
||||
Certificate Duration and Renewal Window
|
||||
***************************************
|
||||
|
||||
cert-manager Certificate resources also support custom validity durations and
|
||||
renewal windows.
|
||||
|
||||
**Important**: The backend service implementation can choose to generate a
|
||||
certificate with a different validity period than what is requested in the
|
||||
issuer.
|
||||
|
||||
Although the duration and renewal periods are specified on the Certificate
|
||||
resources, the corresponding Issuer or ClusterIssuer must support this.
|
||||
|
||||
The table below shows the support state of the different backend services used
|
||||
by issuer types:
|
||||
|
||||
=========== ============================================================
|
||||
Issuer Description
|
||||
=========== ============================================================
|
||||
ACME Only 'renewBefore' supported
|
||||
CA Fully supported
|
||||
Vault Fully supported (although the requested duration must be lower
|
||||
than the configured Vault role's TTL)
|
||||
Self Signed Fully supported
|
||||
=========== ============================================================
|
||||
|
||||
The default duration for all certificates is 90 days and the default renewal
|
||||
windows is 30 days. This means that certificates are considered valid for 3
|
||||
months and renewal will be attempted within 1 month of expiration.
|
||||
|
||||
The *duration* and *renewBefore* parameters must be given in the golang `parseDuration string format <https://golang.org/pkg/time/#ParseDuration>`__.
|
||||
|
||||
Example Usage
|
||||
=============
|
||||
Here an example of an issuer specifying the duration and renewal window.
|
||||
|
||||
The certificate from the previous section is extended with a validity period of
|
||||
24 hours and to begin trying to renew 12 hours before the certificate
|
||||
expiration.
|
||||
|
||||
.. code-block:: yaml
|
||||
:linenos:
|
||||
:emphasize-lines: 7,8
|
||||
|
||||
apiVersion: certmanager.k8s.io/v1alpha1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
name: example
|
||||
spec:
|
||||
secretName: example-tls
|
||||
duration: 24h
|
||||
renewBefore: 12h
|
||||
dnsNames:
|
||||
- foo.example.com
|
||||
- bar.example.com
|
||||
issuerRef:
|
||||
name: my-internal-ca
|
||||
kind: Issuer
|
||||
|
||||
@ -3,6 +3,7 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"const.go",
|
||||
"conversion.go",
|
||||
"defaults.go",
|
||||
"doc.go",
|
||||
|
||||
33
pkg/apis/certmanager/v1alpha1/const.go
Normal file
33
pkg/apis/certmanager/v1alpha1/const.go
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
Copyright 2018 The Jetstack cert-manager contributors.
|
||||
|
||||
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 "time"
|
||||
|
||||
const (
|
||||
// minimum permitted certificate duration by cert-manager
|
||||
MinimumCertificateDuration = time.Hour
|
||||
|
||||
// default certificate duration if Issuer.spec.duration is not set
|
||||
DefaultCertificateDuration = time.Hour * 24 * 90
|
||||
|
||||
// minimum certificate duration before certificate expiration
|
||||
MinimumRenewBefore = time.Minute * 5
|
||||
|
||||
// Default duration before certificate expiration if Issuer.spec.renewBefore is not set
|
||||
DefaultRenewBefore = time.Hour * 24 * 30
|
||||
)
|
||||
@ -57,6 +57,12 @@ type CertificateSpec struct {
|
||||
// Organization is the organization to be used on the Certificate
|
||||
Organization []string `json:"organization,omitempty"`
|
||||
|
||||
// Certificate default Duration
|
||||
Duration *metav1.Duration `json:"duration,omitempty"`
|
||||
|
||||
// Certificate renew before expiration duration
|
||||
RenewBefore *metav1.Duration `json:"renewBefore,omitempty"`
|
||||
|
||||
// DNSNames is a list of subject alt names to be used on the Certificate
|
||||
DNSNames []string `json:"dnsNames,omitempty"`
|
||||
|
||||
|
||||
@ -468,6 +468,24 @@ func (in *CertificateSpec) DeepCopyInto(out *CertificateSpec) {
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Duration != nil {
|
||||
in, out := &in.Duration, &out.Duration
|
||||
if *in == nil {
|
||||
*out = nil
|
||||
} else {
|
||||
*out = new(v1.Duration)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
if in.RenewBefore != nil {
|
||||
in, out := &in.RenewBefore, &out.RenewBefore
|
||||
if *in == nil {
|
||||
*out = nil
|
||||
} else {
|
||||
*out = new(v1.Duration)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
if in.DNSNames != nil {
|
||||
in, out := &in.DNSNames, &out.DNSNames
|
||||
*out = make([]string, len(*in))
|
||||
|
||||
@ -32,6 +32,7 @@ go_test(
|
||||
"//pkg/issuer/acme/dns/rfc2136:go_default_library",
|
||||
"//test/util/generate:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@ -70,6 +70,10 @@ func ValidateCertificateSpec(crt *v1alpha1.CertificateSpec, fldPath *field.Path)
|
||||
el = append(el, field.Invalid(fldPath.Child("keyAlgorithm"), crt.KeyAlgorithm, "must be either empty or one of rsa or ecdsa"))
|
||||
}
|
||||
|
||||
if crt.Duration != nil || crt.RenewBefore != nil {
|
||||
el = append(el, ValidateDuration(crt, fldPath)...)
|
||||
}
|
||||
|
||||
return el
|
||||
}
|
||||
|
||||
@ -148,3 +152,26 @@ func ValidateHTTP01SolverConfig(a *v1alpha1.HTTP01SolverConfig, fldPath *field.P
|
||||
// TODO: ensure 'ingress' is a valid resource name (i.e. DNS name)
|
||||
return el
|
||||
}
|
||||
|
||||
func ValidateDuration(crt *v1alpha1.CertificateSpec, fldPath *field.Path) field.ErrorList {
|
||||
el := field.ErrorList{}
|
||||
|
||||
duration := v1alpha1.DefaultCertificateDuration
|
||||
if crt.Duration != nil {
|
||||
duration = crt.Duration.Duration
|
||||
}
|
||||
renewBefore := v1alpha1.DefaultRenewBefore
|
||||
if crt.RenewBefore != nil {
|
||||
renewBefore = crt.RenewBefore.Duration
|
||||
}
|
||||
if duration < v1alpha1.MinimumCertificateDuration {
|
||||
el = append(el, field.Invalid(fldPath.Child("duration"), duration, fmt.Sprintf("certificate duration must be greater than %s", v1alpha1.MinimumCertificateDuration)))
|
||||
}
|
||||
if renewBefore < v1alpha1.MinimumRenewBefore {
|
||||
el = append(el, field.Invalid(fldPath.Child("renewBefore"), renewBefore, fmt.Sprintf("certificate renewBefore must be greater than %s", v1alpha1.MinimumRenewBefore)))
|
||||
}
|
||||
if duration <= renewBefore {
|
||||
el = append(el, field.Invalid(fldPath.Child("renewBefore"), renewBefore, fmt.Sprintf("certificate duration %s must be greater than renewBefore %s", duration, renewBefore)))
|
||||
}
|
||||
return el
|
||||
}
|
||||
|
||||
@ -59,6 +59,10 @@ func ValidateCertificateForACMEIssuer(crt *v1alpha1.CertificateSpec, issuer *v1a
|
||||
el = append(el, field.Invalid(specPath.Child("organization"), crt.Organization, "ACME does not support setting the organization name"))
|
||||
}
|
||||
|
||||
if crt.Duration != nil {
|
||||
el = append(el, field.Invalid(specPath.Child("duration"), crt.Duration, "ACME does not support certificate durations"))
|
||||
}
|
||||
|
||||
return el
|
||||
}
|
||||
|
||||
|
||||
@ -19,10 +19,13 @@ package validation
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1"
|
||||
"github.com/jetstack/cert-manager/test/util/generate"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -130,6 +133,54 @@ func TestValidateCertificateForIssuer(t *testing.T) {
|
||||
field.Invalid(fldPath.Child("organization"), []string{"shouldfailorg"}, "ACME does not support setting the organization name"),
|
||||
},
|
||||
},
|
||||
"acme certificate with duration set": {
|
||||
crt: &v1alpha1.Certificate{
|
||||
Spec: v1alpha1.CertificateSpec{
|
||||
Duration: &metav1.Duration{Duration: time.Minute * 60},
|
||||
IssuerRef: validIssuerRef,
|
||||
ACME: &v1alpha1.ACMECertificateConfig{
|
||||
Config: []v1alpha1.DomainSolverConfig{
|
||||
{
|
||||
Domains: []string{"example.com"},
|
||||
SolverConfig: v1alpha1.SolverConfig{
|
||||
HTTP01: &v1alpha1.HTTP01SolverConfig{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
issuer: generate.Issuer(generate.IssuerConfig{
|
||||
Name: defaultTestIssuerName,
|
||||
Namespace: defaultTestNamespace,
|
||||
}),
|
||||
errs: []*field.Error{
|
||||
field.Invalid(fldPath.Child("duration"), &metav1.Duration{Duration: time.Minute * 60}, "ACME does not support certificate durations"),
|
||||
},
|
||||
},
|
||||
"acme certificate with renewBefore set": {
|
||||
crt: &v1alpha1.Certificate{
|
||||
Spec: v1alpha1.CertificateSpec{
|
||||
RenewBefore: &metav1.Duration{Duration: time.Minute * 60},
|
||||
IssuerRef: validIssuerRef,
|
||||
ACME: &v1alpha1.ACMECertificateConfig{
|
||||
Config: []v1alpha1.DomainSolverConfig{
|
||||
{
|
||||
Domains: []string{"example.com"},
|
||||
SolverConfig: v1alpha1.SolverConfig{
|
||||
HTTP01: &v1alpha1.HTTP01SolverConfig{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
issuer: generate.Issuer(generate.IssuerConfig{
|
||||
Name: defaultTestIssuerName,
|
||||
Namespace: defaultTestNamespace,
|
||||
}),
|
||||
errs: []*field.Error{},
|
||||
},
|
||||
"certificate with unspecified issuer type": {
|
||||
crt: &v1alpha1.Certificate{
|
||||
Spec: v1alpha1.CertificateSpec{
|
||||
|
||||
@ -17,11 +17,15 @@ limitations under the License.
|
||||
package validation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -545,3 +549,135 @@ func TestValidateHTTP01SolverConfig(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateDuration(t *testing.T) {
|
||||
usefulDurations := map[string]*metav1.Duration{
|
||||
"one second": {Duration: time.Second},
|
||||
"ten minutes": {Duration: time.Minute * 10},
|
||||
"half hour": {Duration: time.Minute * 30},
|
||||
"one hour": {Duration: time.Hour},
|
||||
"one month": {Duration: time.Hour * 24 * 30},
|
||||
"half year": {Duration: time.Hour * 24 * 180},
|
||||
"one year": {Duration: time.Hour * 24 * 365},
|
||||
"ten years": {Duration: time.Hour * 24 * 365 * 10},
|
||||
}
|
||||
|
||||
fldPath := field.NewPath("spec")
|
||||
scenarios := map[string]struct {
|
||||
cfg *v1alpha1.Certificate
|
||||
errs []*field.Error
|
||||
}{
|
||||
"default duration and renewBefore": {
|
||||
cfg: &v1alpha1.Certificate{
|
||||
Spec: v1alpha1.CertificateSpec{
|
||||
CommonName: "testcn",
|
||||
SecretName: "abc",
|
||||
IssuerRef: validIssuerRef,
|
||||
},
|
||||
},
|
||||
},
|
||||
"valid duration and renewBefore": {
|
||||
cfg: &v1alpha1.Certificate{
|
||||
Spec: v1alpha1.CertificateSpec{
|
||||
Duration: usefulDurations["one year"],
|
||||
RenewBefore: usefulDurations["half year"],
|
||||
CommonName: "testcn",
|
||||
SecretName: "abc",
|
||||
IssuerRef: validIssuerRef,
|
||||
},
|
||||
},
|
||||
},
|
||||
"unset duration, valid renewBefore for default": {
|
||||
cfg: &v1alpha1.Certificate{
|
||||
Spec: v1alpha1.CertificateSpec{
|
||||
RenewBefore: usefulDurations["one month"],
|
||||
CommonName: "testcn",
|
||||
SecretName: "abc",
|
||||
IssuerRef: validIssuerRef,
|
||||
},
|
||||
},
|
||||
},
|
||||
"unset renewBefore, valid duration for default": {
|
||||
cfg: &v1alpha1.Certificate{
|
||||
Spec: v1alpha1.CertificateSpec{
|
||||
Duration: usefulDurations["one year"],
|
||||
CommonName: "testcn",
|
||||
SecretName: "abc",
|
||||
IssuerRef: validIssuerRef,
|
||||
},
|
||||
},
|
||||
},
|
||||
"renewBefore is bigger than the default duration": {
|
||||
cfg: &v1alpha1.Certificate{
|
||||
Spec: v1alpha1.CertificateSpec{
|
||||
RenewBefore: usefulDurations["ten years"],
|
||||
CommonName: "testcn",
|
||||
SecretName: "abc",
|
||||
IssuerRef: validIssuerRef,
|
||||
},
|
||||
},
|
||||
errs: []*field.Error{field.Invalid(fldPath.Child("renewBefore"), usefulDurations["ten years"].Duration, fmt.Sprintf("certificate duration %s must be greater than renewBefore %s", v1alpha1.DefaultCertificateDuration, usefulDurations["ten years"].Duration))},
|
||||
},
|
||||
"default renewBefore is bigger than the set duration": {
|
||||
cfg: &v1alpha1.Certificate{
|
||||
Spec: v1alpha1.CertificateSpec{
|
||||
Duration: usefulDurations["one hour"],
|
||||
CommonName: "testcn",
|
||||
SecretName: "abc",
|
||||
IssuerRef: validIssuerRef,
|
||||
},
|
||||
},
|
||||
errs: []*field.Error{field.Invalid(fldPath.Child("renewBefore"), v1alpha1.DefaultRenewBefore, fmt.Sprintf("certificate duration %s must be greater than renewBefore %s", usefulDurations["one hour"].Duration, v1alpha1.DefaultRenewBefore))},
|
||||
},
|
||||
"renewBefore is bigger than the duration": {
|
||||
cfg: &v1alpha1.Certificate{
|
||||
Spec: v1alpha1.CertificateSpec{
|
||||
Duration: usefulDurations["one month"],
|
||||
RenewBefore: usefulDurations["one year"],
|
||||
CommonName: "testcn",
|
||||
SecretName: "abc",
|
||||
IssuerRef: validIssuerRef,
|
||||
},
|
||||
},
|
||||
errs: []*field.Error{field.Invalid(fldPath.Child("renewBefore"), usefulDurations["one year"].Duration, fmt.Sprintf("certificate duration %s must be greater than renewBefore %s", usefulDurations["one month"].Duration, usefulDurations["one year"].Duration))},
|
||||
},
|
||||
"renewBefore is less than the minimum permitted value": {
|
||||
cfg: &v1alpha1.Certificate{
|
||||
Spec: v1alpha1.CertificateSpec{
|
||||
RenewBefore: usefulDurations["one second"],
|
||||
CommonName: "testcn",
|
||||
SecretName: "abc",
|
||||
IssuerRef: validIssuerRef,
|
||||
},
|
||||
},
|
||||
errs: []*field.Error{field.Invalid(fldPath.Child("renewBefore"), usefulDurations["one second"].Duration, fmt.Sprintf("certificate renewBefore must be greater than %s", v1alpha1.MinimumRenewBefore))},
|
||||
},
|
||||
"duration is less than the minimum permitted value": {
|
||||
cfg: &v1alpha1.Certificate{
|
||||
Spec: v1alpha1.CertificateSpec{
|
||||
Duration: usefulDurations["half hour"],
|
||||
RenewBefore: usefulDurations["ten minutes"],
|
||||
CommonName: "testcn",
|
||||
SecretName: "abc",
|
||||
IssuerRef: validIssuerRef,
|
||||
},
|
||||
},
|
||||
errs: []*field.Error{field.Invalid(fldPath.Child("duration"), usefulDurations["half hour"].Duration, fmt.Sprintf("certificate duration must be greater than %s", v1alpha1.MinimumCertificateDuration))},
|
||||
},
|
||||
}
|
||||
for n, s := range scenarios {
|
||||
t.Run(n, func(t *testing.T) {
|
||||
errs := ValidateDuration(&s.cfg.Spec, fldPath)
|
||||
if len(errs) != len(s.errs) {
|
||||
t.Errorf("Expected %v but got %v", s.errs, errs)
|
||||
return
|
||||
}
|
||||
for i, e := range errs {
|
||||
expectedErr := s.errs[i]
|
||||
if !reflect.DeepEqual(e, expectedErr) {
|
||||
t.Errorf("Expected %v but got %v", expectedErr, e)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,6 +80,7 @@ func ValidateIssuerConfig(iss *v1alpha1.IssuerConfig, fldPath *field.Path) field
|
||||
if numConfigs == 0 {
|
||||
el = append(el, field.Required(fldPath, "at least one issuer must be configured"))
|
||||
}
|
||||
|
||||
return el
|
||||
}
|
||||
|
||||
|
||||
@ -18,6 +18,7 @@ go_library(
|
||||
"//pkg/client/listers/certmanager/v1alpha1:go_default_library",
|
||||
"//pkg/issuer:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
|
||||
"//vendor/k8s.io/client-go/informers:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
@ -48,3 +48,13 @@ filegroup(
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["sync_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//pkg/apis/certmanager/v1alpha1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@ -18,6 +18,7 @@ package certificates
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
@ -69,6 +70,9 @@ var (
|
||||
certificateGvk = v1alpha1.SchemeGroupVersion.WithKind("Certificate")
|
||||
)
|
||||
|
||||
// to help testing
|
||||
var now = time.Now
|
||||
|
||||
func (c *Controller) Sync(ctx context.Context, crt *v1alpha1.Certificate) (requeue bool, err error) {
|
||||
crtCopy := crt.DeepCopy()
|
||||
defer func() {
|
||||
@ -180,7 +184,7 @@ func (c *Controller) Sync(ctx context.Context, crt *v1alpha1.Certificate) (reque
|
||||
}
|
||||
|
||||
// check if the certificate needs renewal
|
||||
needsRenew := c.Context.IssuerOptions.CertificateNeedsRenew(cert)
|
||||
needsRenew := c.Context.IssuerOptions.CertificateNeedsRenew(cert, crt.Spec.RenewBefore)
|
||||
if needsRenew {
|
||||
return c.issue(ctx, i, crtCopy)
|
||||
}
|
||||
@ -226,12 +230,11 @@ func (c *Controller) scheduleRenewal(crt *v1alpha1.Certificate) {
|
||||
return
|
||||
}
|
||||
|
||||
durationUntilExpiry := cert.NotAfter.Sub(time.Now())
|
||||
renewIn := durationUntilExpiry - c.Context.IssuerOptions.RenewBeforeExpiryDuration
|
||||
renewIn := c.calculateDurationUntilRenew(cert, crt)
|
||||
|
||||
c.scheduledWorkQueue.Add(key, renewIn)
|
||||
|
||||
glog.Infof("Certificate %s/%s scheduled for renewal in %d hours", crt.Namespace, crt.Name, renewIn/time.Hour)
|
||||
glog.Infof("Certificate %s/%s scheduled for renewal in %s", crt.Namespace, crt.Name, renewIn.String())
|
||||
}
|
||||
|
||||
// issuerKind returns the kind of issuer for a certificate
|
||||
@ -341,3 +344,43 @@ func (c *Controller) updateCertificateStatus(old, new *v1alpha1.Certificate) (*v
|
||||
// for CRDs (https://github.com/kubernetes/kubernetes/issues/38113)
|
||||
return c.CMClient.CertmanagerV1alpha1().Certificates(new.Namespace).Update(new)
|
||||
}
|
||||
|
||||
// calculateDurationUntilRenew calculates how long cert-manager should wait to
|
||||
// until attempting to renew this certificate resource.
|
||||
func (c *Controller) calculateDurationUntilRenew(cert *x509.Certificate, crt *v1alpha1.Certificate) time.Duration {
|
||||
messageCertificateDuration := "Certificate received from server has a validity duration of %s. The requested certificate validity duration was %s"
|
||||
messageScheduleModified := "Certificate renewal duration was changed to fit inside the received certificate validity duration from issuer."
|
||||
|
||||
// validate if the certificate received was with the issuer configured
|
||||
// duration. If not we generate an event to warn the user of that fact.
|
||||
certDuration := cert.NotAfter.Sub(cert.NotBefore)
|
||||
if crt.Spec.Duration != nil && certDuration < crt.Spec.Duration.Duration {
|
||||
s := fmt.Sprintf(messageCertificateDuration, certDuration, crt.Spec.Duration.Duration)
|
||||
glog.Info(s)
|
||||
// TODO Use the message as the reason in a 'renewal status' condition
|
||||
}
|
||||
|
||||
// renew is the duration before the certificate expiration that cert-manager
|
||||
// will start to try renewing the certificate.
|
||||
renewBefore := v1alpha1.DefaultRenewBefore
|
||||
if crt.Spec.RenewBefore != nil {
|
||||
renewBefore = crt.Spec.RenewBefore.Duration
|
||||
}
|
||||
|
||||
// Verify that the renewBefore duration is inside the certificate validity duration.
|
||||
// If not we notify with an event that we will renew the certificate
|
||||
// before (certificate duration / 3) of its expiration duration.
|
||||
if renewBefore > certDuration {
|
||||
glog.Info(messageScheduleModified)
|
||||
// TODO Use the message as the reason in a 'renewal status' condition
|
||||
// We will renew 1/3 before the expiration date.
|
||||
renewBefore = certDuration / 3
|
||||
}
|
||||
|
||||
// calculate the amount of time until expiry
|
||||
durationUntilExpiry := cert.NotAfter.Sub(now())
|
||||
// calculate how long until we should start attempting to renew the certificate
|
||||
renewIn := durationUntilExpiry - renewBefore
|
||||
|
||||
return renewIn
|
||||
}
|
||||
|
||||
103
pkg/controller/certificates/sync_test.go
Normal file
103
pkg/controller/certificates/sync_test.go
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
Copyright 2018 The Jetstack cert-manager contributors.
|
||||
|
||||
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 certificates
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestCalculateDurationUntilRenew(t *testing.T) {
|
||||
c := &Controller{}
|
||||
currentTime := time.Now()
|
||||
now = func() time.Time { return currentTime }
|
||||
defer func() { now = time.Now }()
|
||||
tests := []struct {
|
||||
desc string
|
||||
notBefore time.Time
|
||||
notAfter time.Time
|
||||
duration *metav1.Duration
|
||||
renewBefore *metav1.Duration
|
||||
expectedExpiry time.Duration
|
||||
}{
|
||||
{
|
||||
desc: "generate an event if certificate duration is lower than requested duration",
|
||||
notBefore: now(),
|
||||
notAfter: now().Add(time.Hour * 24 * 90),
|
||||
duration: &metav1.Duration{time.Hour * 24 * 120},
|
||||
renewBefore: nil,
|
||||
expectedExpiry: time.Hour * 24 * 60,
|
||||
},
|
||||
{
|
||||
desc: "default expiry to 30 days",
|
||||
notBefore: now(),
|
||||
notAfter: now().Add(time.Hour * 24 * 120),
|
||||
duration: nil,
|
||||
renewBefore: nil,
|
||||
expectedExpiry: (time.Hour * 24 * 120) - (time.Hour * 24 * 30),
|
||||
},
|
||||
{
|
||||
desc: "default expiry to 2/3 of total duration if duration < 30 days",
|
||||
notBefore: now(),
|
||||
notAfter: now().Add(time.Hour * 24 * 20),
|
||||
duration: nil,
|
||||
renewBefore: nil,
|
||||
expectedExpiry: time.Hour * 24 * 20 * 2 / 3,
|
||||
},
|
||||
{
|
||||
desc: "expiry of 2/3 of certificate duration when duration < 30 minutes",
|
||||
notBefore: now(),
|
||||
notAfter: now().Add(time.Hour),
|
||||
duration: &metav1.Duration{time.Hour},
|
||||
renewBefore: &metav1.Duration{time.Hour / 3},
|
||||
expectedExpiry: time.Hour * 2 / 3,
|
||||
},
|
||||
{
|
||||
desc: "expiry of 60 days of certificate duration",
|
||||
notBefore: now(),
|
||||
notAfter: now().Add(time.Hour * 24 * 365),
|
||||
duration: &metav1.Duration{time.Hour * 24 * 365},
|
||||
renewBefore: &metav1.Duration{time.Hour * 24 * 60},
|
||||
expectedExpiry: (time.Hour * 24 * 365) - (time.Hour * 24 * 60),
|
||||
},
|
||||
{
|
||||
desc: "expiry of 2/3 of certificate duration when renewBefore greater than certificate duration",
|
||||
notBefore: now(),
|
||||
notAfter: now().Add(time.Hour * 24 * 35),
|
||||
duration: &metav1.Duration{time.Hour * 24 * 35},
|
||||
renewBefore: &metav1.Duration{time.Hour * 24 * 40},
|
||||
expectedExpiry: time.Hour * 24 * 35 * 2 / 3,
|
||||
},
|
||||
}
|
||||
for k, v := range tests {
|
||||
cert := &v1alpha1.Certificate{
|
||||
Spec: v1alpha1.CertificateSpec{
|
||||
Duration: v.duration,
|
||||
RenewBefore: v.renewBefore,
|
||||
},
|
||||
}
|
||||
x509Cert := &x509.Certificate{NotBefore: v.notBefore, NotAfter: v.notAfter}
|
||||
duration := c.calculateDurationUntilRenew(x509Cert, cert)
|
||||
if duration != v.expectedExpiry {
|
||||
t.Errorf("test # %d - %s: got %v, expected %v", k, v.desc, duration, v.expectedExpiry)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -23,6 +23,7 @@ import (
|
||||
|
||||
cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1"
|
||||
cmlisters "github.com/jetstack/cert-manager/pkg/client/listers/certmanager/v1alpha1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type Helper interface {
|
||||
@ -90,12 +91,17 @@ func (o IssuerOptions) CanUseAmbientCredentials(iss cmapi.GenericIssuer) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (o IssuerOptions) CertificateNeedsRenew(cert *x509.Certificate) bool {
|
||||
func (o IssuerOptions) CertificateNeedsRenew(cert *x509.Certificate, renewBefore *metav1.Duration) bool {
|
||||
renewBeforeDuration := o.RenewBeforeExpiryDuration
|
||||
if renewBefore != nil {
|
||||
renewBeforeDuration = renewBefore.Duration
|
||||
}
|
||||
|
||||
// calculate the amount of time until expiry
|
||||
durationUntilExpiry := cert.NotAfter.Sub(time.Now())
|
||||
// calculate how long until we should start attempting to renew the
|
||||
// certificate
|
||||
renewIn := durationUntilExpiry - o.RenewBeforeExpiryDuration
|
||||
renewIn := durationUntilExpiry - renewBeforeDuration
|
||||
// if we should being attempting to renew now, then trigger a renewal
|
||||
if renewIn <= 0 {
|
||||
return true
|
||||
|
||||
@ -182,7 +182,7 @@ func (a *Acme) Issue(ctx context.Context, crt *v1alpha1.Certificate) (issuer.Iss
|
||||
return a.retryOrder(crt, existingOrder)
|
||||
}
|
||||
|
||||
if a.Context.IssuerOptions.CertificateNeedsRenew(x509Cert) {
|
||||
if a.Context.IssuerOptions.CertificateNeedsRenew(x509Cert, crt.Spec.RenewBefore) {
|
||||
// existing order's certificate is near expiry
|
||||
return a.retryOrder(crt, existingOrder)
|
||||
}
|
||||
|
||||
@ -47,8 +47,6 @@ const (
|
||||
messageErrorIssueCert = "Error issuing TLS certificate: "
|
||||
|
||||
messageCertIssued = "Certificate issued successfully"
|
||||
|
||||
defaultCertificateDuration = time.Hour * 24 * 90
|
||||
)
|
||||
|
||||
func (v *Vault) Issue(ctx context.Context, crt *v1alpha1.Certificate) (issuer.IssueResponse, error) {
|
||||
@ -98,7 +96,13 @@ func (v *Vault) obtainCertificate(ctx context.Context, crt *v1alpha1.Certificate
|
||||
return nil, nil, nil, fmt.Errorf("error encoding certificate request: %s", err.Error())
|
||||
}
|
||||
|
||||
crtBytes, caBytes, err := v.requestVaultCert(template.Subject.CommonName, template.DNSNames, pemRequestBuf.Bytes())
|
||||
certDuration := v1alpha1.DefaultCertificateDuration
|
||||
if crt.Spec.Duration != nil {
|
||||
certDuration = crt.Spec.Duration.Duration
|
||||
}
|
||||
|
||||
crtBytes, caBytes, err := v.requestVaultCert(template.Subject.CommonName, certDuration, template.DNSNames, pemRequestBuf.Bytes())
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
@ -212,7 +216,7 @@ func (v *Vault) requestTokenWithAppRoleRef(client *vault.Client, appRole *v1alph
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func (v *Vault) requestVaultCert(commonName string, altNames []string, csr []byte) ([]byte, []byte, error) {
|
||||
func (v *Vault) requestVaultCert(commonName string, certDuration time.Duration, altNames []string, csr []byte) ([]byte, []byte, error) {
|
||||
client, err := v.initVaultClient()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@ -223,7 +227,7 @@ func (v *Vault) requestVaultCert(commonName string, altNames []string, csr []byt
|
||||
parameters := map[string]string{
|
||||
"common_name": commonName,
|
||||
"alt_names": strings.Join(altNames, ","),
|
||||
"ttl": defaultCertificateDuration.String(),
|
||||
"ttl": certDuration.String(),
|
||||
"csr": string(csr),
|
||||
"exclude_cn_from_sans": "true",
|
||||
}
|
||||
|
||||
@ -86,9 +86,6 @@ func OrganizationForCertificate(crt *v1alpha1.Certificate) []string {
|
||||
|
||||
var serialNumberLimit = new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
|
||||
// default certification duration is 1 year
|
||||
const defaultNotAfter = time.Hour * 24 * 365
|
||||
|
||||
// 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
|
||||
@ -139,7 +136,13 @@ func GenerateTemplate(issuer v1alpha1.GenericIssuer, crt *v1alpha1.Certificate)
|
||||
return nil, fmt.Errorf("failed to generate serial number: %s", err.Error())
|
||||
}
|
||||
|
||||
certDuration := v1alpha1.DefaultCertificateDuration
|
||||
if crt.Spec.Duration != nil {
|
||||
certDuration = crt.Spec.Duration.Duration
|
||||
}
|
||||
|
||||
pubKeyAlgo, _, err := SignatureAlgorithm(crt)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -160,7 +163,7 @@ func GenerateTemplate(issuer v1alpha1.GenericIssuer, crt *v1alpha1.Certificate)
|
||||
CommonName: commonName,
|
||||
},
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: time.Now().Add(defaultNotAfter),
|
||||
NotAfter: time.Now().Add(certDuration),
|
||||
// see http://golang.org/pkg/crypto/x509/#KeyUsage
|
||||
KeyUsage: keyUsages,
|
||||
DNSNames: dnsNames,
|
||||
|
||||
@ -240,7 +240,7 @@ func signTestCert(key crypto.Signer) *x509.Certificate {
|
||||
CommonName: commonName,
|
||||
},
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: time.Now().Add(defaultNotAfter),
|
||||
NotAfter: time.Now().Add(v1alpha1.DefaultCertificateDuration),
|
||||
// see http://golang.org/pkg/crypto/x509/#KeyUsage
|
||||
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
|
||||
}
|
||||
|
||||
@ -12,7 +12,9 @@ go_library(
|
||||
tags = ["manual"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/apis/certmanager/v1alpha1:go_default_library",
|
||||
"//pkg/client/clientset/versioned:go_default_library",
|
||||
"//pkg/util/pki:go_default_library",
|
||||
"//test/e2e/framework/addon:go_default_library",
|
||||
"//test/e2e/framework/config:go_default_library",
|
||||
"//test/e2e/framework/helper:go_default_library",
|
||||
|
||||
@ -17,15 +17,23 @@ limitations under the License.
|
||||
package framework
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/jetstack/cert-manager/test/e2e/framework/helper"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1"
|
||||
"github.com/jetstack/cert-manager/pkg/util/pki"
|
||||
"k8s.io/api/core/v1"
|
||||
api "k8s.io/api/core/v1"
|
||||
apiextcs "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
|
||||
clientset "github.com/jetstack/cert-manager/pkg/client/clientset/versioned"
|
||||
|
||||
"github.com/jetstack/cert-manager/test/e2e/framework/addon"
|
||||
"github.com/jetstack/cert-manager/test/e2e/framework/config"
|
||||
"github.com/jetstack/cert-manager/test/e2e/framework/util"
|
||||
@ -168,6 +176,22 @@ func (f *Framework) Helper() *helper.Helper {
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Framework) CertificateDurationValid(c *v1alpha1.Certificate, duration time.Duration) {
|
||||
By("Verifying TLS certificate exists")
|
||||
secret, err := f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Get(c.Spec.SecretName, metav1.GetOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
certBytes, ok := secret.Data[api.TLSCertKey]
|
||||
if !ok {
|
||||
Failf("No certificate data found for Certificate %q", c.Name)
|
||||
}
|
||||
cert, err := pki.DecodeX509CertificateBytes(certBytes)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
By("Verifying that the duration is valid")
|
||||
if cert.NotAfter.Sub(cert.NotBefore) != duration {
|
||||
Failf("Expected duration of %s, got %s [NotBefore: %s, NotAfter: %s]", duration, cert.NotAfter.Sub(cert.NotBefore), cert.NotBefore.Format(time.RFC3339), cert.NotAfter.Format(time.RFC3339))
|
||||
}
|
||||
}
|
||||
|
||||
// CertManagerDescribe is a wrapper function for ginkgo describe. Adds namespacing.
|
||||
func CertManagerDescribe(text string, body func()) bool {
|
||||
return Describe("[cert-manager] "+text, body)
|
||||
|
||||
@ -116,7 +116,7 @@ var _ = framework.CertManagerDescribe("ACME Certificate (HTTP01)", func() {
|
||||
secretClient := f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name)
|
||||
|
||||
By("Creating a Certificate")
|
||||
_, err := certClient.Create(util.NewCertManagerACMECertificate(certificateName, certificateSecretName, issuerName, v1alpha1.IssuerKind, acmeIngressClass, acmeIngressDomain))
|
||||
_, err := certClient.Create(util.NewCertManagerACMECertificate(certificateName, certificateSecretName, issuerName, v1alpha1.IssuerKind, nil, nil, acmeIngressClass, acmeIngressDomain))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
By("Verifying the Certificate is valid")
|
||||
err = util.WaitCertificateIssuedValid(certClient, secretClient, certificateName, time.Minute*5)
|
||||
@ -130,7 +130,7 @@ var _ = framework.CertManagerDescribe("ACME Certificate (HTTP01)", func() {
|
||||
// the maximum length of a single segment of the domain being requested
|
||||
const maxLengthOfDomainSegment = 63
|
||||
By("Creating a Certificate")
|
||||
_, err := certClient.Create(util.NewCertManagerACMECertificate(certificateName, certificateSecretName, issuerName, v1alpha1.IssuerKind, acmeIngressClass, fmt.Sprintf("%s.%s", cmutil.RandStringRunes(maxLengthOfDomainSegment), acmeIngressDomain)))
|
||||
_, err := certClient.Create(util.NewCertManagerACMECertificate(certificateName, certificateSecretName, issuerName, v1alpha1.IssuerKind, nil, nil, acmeIngressClass, fmt.Sprintf("%s.%s", cmutil.RandStringRunes(maxLengthOfDomainSegment), acmeIngressDomain)))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = util.WaitCertificateIssuedValid(certClient, secretClient, certificateName, time.Minute*5)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
@ -141,7 +141,7 @@ var _ = framework.CertManagerDescribe("ACME Certificate (HTTP01)", func() {
|
||||
secretClient := f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name)
|
||||
|
||||
By("Creating a Certificate")
|
||||
_, err := certClient.Create(util.NewCertManagerACMECertificate(certificateName, certificateSecretName, issuerName, v1alpha1.IssuerKind, acmeIngressClass, acmeIngressDomain, fmt.Sprintf("%s.%s", cmutil.RandStringRunes(5), acmeIngressDomain)))
|
||||
_, err := certClient.Create(util.NewCertManagerACMECertificate(certificateName, certificateSecretName, issuerName, v1alpha1.IssuerKind, nil, nil, acmeIngressClass, acmeIngressDomain, fmt.Sprintf("%s.%s", cmutil.RandStringRunes(5), acmeIngressDomain)))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
By("Verifying the Certificate is valid")
|
||||
err = util.WaitCertificateIssuedValid(certClient, secretClient, certificateName, time.Minute*5)
|
||||
@ -153,7 +153,7 @@ var _ = framework.CertManagerDescribe("ACME Certificate (HTTP01)", func() {
|
||||
secretClient := f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name)
|
||||
|
||||
By("Creating a Certificate")
|
||||
cert, err := certClient.Create(util.NewCertManagerACMECertificate(certificateName, certificateSecretName, issuerName, v1alpha1.IssuerKind, acmeIngressClass, acmeIngressDomain, fmt.Sprintf("%s.%s", cmutil.RandStringRunes(5), acmeIngressDomain)))
|
||||
cert, err := certClient.Create(util.NewCertManagerACMECertificate(certificateName, certificateSecretName, issuerName, v1alpha1.IssuerKind, nil, nil, acmeIngressClass, acmeIngressDomain, fmt.Sprintf("%s.%s", cmutil.RandStringRunes(5), acmeIngressDomain)))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
By("Verifying the Certificate is valid")
|
||||
err = util.WaitCertificateIssuedValid(certClient, secretClient, certificateName, time.Minute*5)
|
||||
@ -180,7 +180,7 @@ var _ = framework.CertManagerDescribe("ACME Certificate (HTTP01)", func() {
|
||||
Skip("Poorly designed test skipped until it can be rewritten")
|
||||
|
||||
By("Creating a Certificate")
|
||||
_, err := f.CertManagerClientSet.CertmanagerV1alpha1().Certificates(f.Namespace.Name).Create(util.NewCertManagerACMECertificate(certificateName, certificateSecretName, issuerName, v1alpha1.IssuerKind, acmeIngressClass, "google.com"))
|
||||
_, err := f.CertManagerClientSet.CertmanagerV1alpha1().Certificates(f.Namespace.Name).Create(util.NewCertManagerACMECertificate(certificateName, certificateSecretName, issuerName, v1alpha1.IssuerKind, nil, nil, acmeIngressClass, "google.com"))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
By("Waiting for the Certificate to not have a ready condition")
|
||||
err = util.WaitForCertificateCondition(f.CertManagerClientSet.CertmanagerV1alpha1().Certificates(f.Namespace.Name),
|
||||
|
||||
@ -18,6 +18,7 @@ go_library(
|
||||
"//test/util:go_default_library",
|
||||
"//vendor/github.com/onsi/ginkgo:go_default_library",
|
||||
"//vendor/github.com/onsi/gomega:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@ -25,6 +25,7 @@ import (
|
||||
"github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1"
|
||||
"github.com/jetstack/cert-manager/test/e2e/framework"
|
||||
"github.com/jetstack/cert-manager/test/util"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
var _ = framework.CertManagerDescribe("CA Certificate", func() {
|
||||
@ -64,7 +65,7 @@ var _ = framework.CertManagerDescribe("CA Certificate", func() {
|
||||
secretClient := f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name)
|
||||
|
||||
By("Creating a Certificate")
|
||||
_, err := certClient.Create(util.NewCertManagerBasicCertificate(certificateName, certificateSecretName, issuerName, v1alpha1.IssuerKind))
|
||||
_, err := certClient.Create(util.NewCertManagerBasicCertificate(certificateName, certificateSecretName, issuerName, v1alpha1.IssuerKind, nil, nil))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
By("Verifying the Certificate is valid")
|
||||
err = util.WaitCertificateIssuedValid(certClient, secretClient, certificateName, time.Second*30)
|
||||
@ -75,7 +76,7 @@ var _ = framework.CertManagerDescribe("CA Certificate", func() {
|
||||
certClient := f.CertManagerClientSet.CertmanagerV1alpha1().Certificates(f.Namespace.Name)
|
||||
secretClient := f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name)
|
||||
|
||||
crt := util.NewCertManagerBasicCertificate(certificateName, certificateSecretName, issuerName, v1alpha1.IssuerKind)
|
||||
crt := util.NewCertManagerBasicCertificate(certificateName, certificateSecretName, issuerName, v1alpha1.IssuerKind, nil, nil)
|
||||
crt.Spec.KeyAlgorithm = v1alpha1.ECDSAKeyAlgorithm
|
||||
crt.Spec.KeySize = 521
|
||||
|
||||
@ -88,4 +89,39 @@ var _ = framework.CertManagerDescribe("CA Certificate", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
cases := []struct {
|
||||
inputDuration *metav1.Duration
|
||||
inputRenewBefore *metav1.Duration
|
||||
expectedDuration time.Duration
|
||||
label string
|
||||
}{
|
||||
{
|
||||
inputDuration: &metav1.Duration{time.Hour * 24 * 35},
|
||||
inputRenewBefore: nil,
|
||||
expectedDuration: time.Hour * 24 * 35,
|
||||
label: "35 days",
|
||||
},
|
||||
{
|
||||
inputDuration: nil,
|
||||
inputRenewBefore: nil,
|
||||
expectedDuration: time.Hour * 24 * 90,
|
||||
label: "the default duration (90 days)",
|
||||
},
|
||||
}
|
||||
for _, v := range cases {
|
||||
v := v
|
||||
It("should generate a signed keypair valid for "+v.label, func() {
|
||||
certClient := f.CertManagerClientSet.CertmanagerV1alpha1().Certificates(f.Namespace.Name)
|
||||
secretClient := f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name)
|
||||
|
||||
By("Creating a Certificate")
|
||||
cert, err := certClient.Create(util.NewCertManagerBasicCertificate(certificateName, certificateSecretName, issuerName, v1alpha1.IssuerKind, v.inputDuration, v.inputRenewBefore))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
By("Verifying the Certificate is valid")
|
||||
err = util.WaitCertificateIssuedValid(certClient, secretClient, certificateName, time.Second*30)
|
||||
f.CertificateDurationValid(cert, v.expectedDuration)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
@ -12,6 +12,7 @@ go_library(
|
||||
"//test/util:go_default_library",
|
||||
"//vendor/github.com/onsi/ginkgo:go_default_library",
|
||||
"//vendor/github.com/onsi/gomega:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@ -17,6 +17,7 @@ limitations under the License.
|
||||
package certificate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
@ -25,6 +26,7 @@ import (
|
||||
"github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1"
|
||||
"github.com/jetstack/cert-manager/test/e2e/framework"
|
||||
"github.com/jetstack/cert-manager/test/util"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
var _ = framework.CertManagerDescribe("Self Signed Certificate", func() {
|
||||
@ -51,9 +53,56 @@ var _ = framework.CertManagerDescribe("Self Signed Certificate", func() {
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
By("Creating a Certificate")
|
||||
_, err = certClient.Create(util.NewCertManagerBasicCertificate(certificateName, certificateSecretName, issuerName, v1alpha1.IssuerKind))
|
||||
_, err = certClient.Create(util.NewCertManagerBasicCertificate(certificateName, certificateSecretName, issuerName, v1alpha1.IssuerKind, nil, nil))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = util.WaitCertificateIssuedValid(certClient, secretClient, certificateName, time.Minute*5)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
cases := []struct {
|
||||
inputDuration *metav1.Duration
|
||||
inputRenewBefore *metav1.Duration
|
||||
expectedDuration time.Duration
|
||||
label string
|
||||
}{
|
||||
{
|
||||
inputDuration: &metav1.Duration{time.Hour * 24 * 35},
|
||||
inputRenewBefore: nil,
|
||||
expectedDuration: time.Hour * 24 * 35,
|
||||
label: "35 days",
|
||||
},
|
||||
{
|
||||
inputDuration: nil,
|
||||
inputRenewBefore: nil,
|
||||
expectedDuration: time.Hour * 24 * 90,
|
||||
label: "the default duration (90 days)",
|
||||
},
|
||||
}
|
||||
for _, v := range cases {
|
||||
v := v
|
||||
It("should generate a signed keypair valid for "+v.label, func() {
|
||||
certClient := f.CertManagerClientSet.CertmanagerV1alpha1().Certificates(f.Namespace.Name)
|
||||
secretClient := f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name)
|
||||
|
||||
By("Creating an Issuer")
|
||||
issuerDurationName := fmt.Sprintf("%s-%d", issuerName, v.expectedDuration)
|
||||
_, err := f.CertManagerClientSet.CertmanagerV1alpha1().Issuers(f.Namespace.Name).Create(util.NewCertManagerSelfSignedIssuer(issuerDurationName))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
By("Waiting for Issuer to become Ready")
|
||||
err = util.WaitForIssuerCondition(f.CertManagerClientSet.CertmanagerV1alpha1().Issuers(f.Namespace.Name),
|
||||
issuerDurationName,
|
||||
v1alpha1.IssuerCondition{
|
||||
Type: v1alpha1.IssuerConditionReady,
|
||||
Status: v1alpha1.ConditionTrue,
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Creating a Certificate")
|
||||
cert, err := certClient.Create(util.NewCertManagerBasicCertificate(certificateName, certificateSecretName, issuerDurationName, v1alpha1.IssuerKind, v.inputDuration, v.inputRenewBefore))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = util.WaitCertificateIssuedValid(certClient, secretClient, certificateName, time.Second*30)
|
||||
f.CertificateDurationValid(cert, v.expectedDuration)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@ -17,6 +17,7 @@ go_library(
|
||||
"//test/util:go_default_library",
|
||||
"//vendor/github.com/onsi/ginkgo:go_default_library",
|
||||
"//vendor/github.com/onsi/gomega:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@ -28,6 +28,7 @@ import (
|
||||
"github.com/jetstack/cert-manager/test/e2e/framework/addon/tiller"
|
||||
vaultaddon "github.com/jetstack/cert-manager/test/e2e/framework/addon/vault"
|
||||
"github.com/jetstack/cert-manager/test/util"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
var _ = framework.CertManagerDescribe("Vault Certificate (AppRole)", func() {
|
||||
@ -98,6 +99,7 @@ var _ = framework.CertManagerDescribe("Vault Certificate (AppRole)", func() {
|
||||
secretClient := f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name)
|
||||
|
||||
_, err := f.CertManagerClientSet.CertmanagerV1alpha1().Issuers(f.Namespace.Name).Create(util.NewCertManagerVaultIssuerAppRole(issuerName, vaultURL, vaultPath, roleId, vaultSecretAppRoleName, authPath, vault.Details().VaultCA))
|
||||
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Issuer to become Ready")
|
||||
@ -110,11 +112,75 @@ var _ = framework.CertManagerDescribe("Vault Certificate (AppRole)", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Creating a Certificate")
|
||||
_, err = certClient.Create(util.NewCertManagerVaultCertificate(certificateName, certificateSecretName, issuerName, v1alpha1.IssuerKind))
|
||||
_, err = certClient.Create(util.NewCertManagerVaultCertificate(certificateName, certificateSecretName, issuerName, v1alpha1.IssuerKind, nil, nil))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = util.WaitCertificateIssuedValid(certClient, secretClient, certificateName, time.Minute*5)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
})
|
||||
|
||||
cases := []struct {
|
||||
inputDuration *metav1.Duration
|
||||
inputRenewBefore *metav1.Duration
|
||||
expectedDuration time.Duration
|
||||
label string
|
||||
event string
|
||||
}{
|
||||
{
|
||||
inputDuration: &metav1.Duration{time.Hour * 24 * 35},
|
||||
inputRenewBefore: nil,
|
||||
expectedDuration: time.Hour * 24 * 35,
|
||||
label: "valid for 35 days",
|
||||
},
|
||||
{
|
||||
inputDuration: nil,
|
||||
inputRenewBefore: nil,
|
||||
expectedDuration: time.Hour * 24 * 90,
|
||||
label: "valid for the default value (90 days)",
|
||||
},
|
||||
{
|
||||
inputDuration: &metav1.Duration{time.Hour * 24 * 365},
|
||||
inputRenewBefore: nil,
|
||||
expectedDuration: time.Hour * 24 * 90,
|
||||
label: "with Vault configured maximum TTL duration (90 days) when requested duration is greater than TTL",
|
||||
},
|
||||
{
|
||||
inputDuration: &metav1.Duration{time.Hour * 24 * 240},
|
||||
inputRenewBefore: &metav1.Duration{time.Hour * 24 * 120},
|
||||
expectedDuration: time.Hour * 24 * 90,
|
||||
label: "with a warning event when renewBefore is bigger than the duration",
|
||||
},
|
||||
}
|
||||
|
||||
for _, v := range cases {
|
||||
v := v
|
||||
It("should generate a new certificate "+v.label, func() {
|
||||
By("Creating an Issuer")
|
||||
certClient := f.CertManagerClientSet.CertmanagerV1alpha1().Certificates(f.Namespace.Name)
|
||||
secretClient := f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name)
|
||||
|
||||
_, err := f.CertManagerClientSet.CertmanagerV1alpha1().Issuers(f.Namespace.Name).Create(util.NewCertManagerVaultIssuerAppRole(issuerName, vault.Details().Host, vaultPath, roleId, vaultSecretAppRoleName, authPath, vault.Details().VaultCA))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for Issuer to become Ready")
|
||||
err = util.WaitForIssuerCondition(f.CertManagerClientSet.CertmanagerV1alpha1().Issuers(f.Namespace.Name),
|
||||
issuerName,
|
||||
v1alpha1.IssuerCondition{
|
||||
Type: v1alpha1.IssuerConditionReady,
|
||||
Status: v1alpha1.ConditionTrue,
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Creating a Certificate")
|
||||
cert, err := f.CertManagerClientSet.CertmanagerV1alpha1().Certificates(f.Namespace.Name).Create(util.NewCertManagerVaultCertificate(certificateName, certificateSecretName, issuerName, v1alpha1.IssuerKind, v.inputDuration, v.inputRenewBefore))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = util.WaitCertificateIssuedValid(certClient, secretClient, certificateName, time.Minute*5)
|
||||
|
||||
// Vault substract 30 seconds to the NotBefore date.
|
||||
f.CertificateDurationValid(cert, v.expectedDuration+(30*time.Second))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@ -111,7 +111,7 @@ var _ = framework.CertManagerDescribe("Vault Certificate (AppRole with a custom
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Creating a Certificate")
|
||||
_, err = certClient.Create(util.NewCertManagerVaultCertificate(certificateName, certificateSecretName, issuerName, v1alpha1.IssuerKind))
|
||||
_, err = certClient.Create(util.NewCertManagerVaultCertificate(certificateName, certificateSecretName, issuerName, v1alpha1.IssuerKind, nil, nil))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = util.WaitCertificateIssuedValid(certClient, secretClient, certificateName, time.Minute*5)
|
||||
|
||||
@ -94,7 +94,6 @@ var _ = framework.CertManagerDescribe("Vault Issuer", func() {
|
||||
_, err := f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Create(vaultaddon.NewVaultAppRoleSecret(vaultSecretAppRoleName, secretId))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Creating an Issuer")
|
||||
_, err = f.CertManagerClientSet.CertmanagerV1alpha1().Issuers(f.Namespace.Name).Create(util.NewCertManagerVaultIssuerAppRole(issuerName, vault.Details().Host, vaultPath, roleId, vaultSecretAppRoleName, authPath, vault.Details().VaultCA))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
|
||||
@ -31,6 +31,8 @@ type CertificateConfig struct {
|
||||
SecretName string
|
||||
CommonName string
|
||||
DNSNames []string
|
||||
Duration *metav1.Duration
|
||||
RenewBefore *metav1.Duration
|
||||
|
||||
// ACME parameters
|
||||
SolverConfig v1alpha1.SolverConfig
|
||||
@ -43,7 +45,9 @@ func Certificate(cfg CertificateConfig) *v1alpha1.Certificate {
|
||||
Namespace: cfg.Namespace,
|
||||
},
|
||||
Spec: v1alpha1.CertificateSpec{
|
||||
SecretName: cfg.SecretName,
|
||||
Duration: cfg.Duration,
|
||||
RenewBefore: cfg.RenewBefore,
|
||||
SecretName: cfg.SecretName,
|
||||
IssuerRef: v1alpha1.ObjectReference{
|
||||
Name: cfg.IssuerName,
|
||||
Kind: cfg.IssuerKind,
|
||||
|
||||
@ -350,7 +350,7 @@ func NewCertManagerCAClusterIssuer(name, secretName string) *v1alpha1.ClusterIss
|
||||
}
|
||||
}
|
||||
|
||||
func NewCertManagerBasicCertificate(name, secretName, issuerName string, issuerKind string) *v1alpha1.Certificate {
|
||||
func NewCertManagerBasicCertificate(name, secretName, issuerName string, issuerKind string, duration, renewBefore *metav1.Duration) *v1alpha1.Certificate {
|
||||
return &v1alpha1.Certificate{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
@ -359,6 +359,8 @@ func NewCertManagerBasicCertificate(name, secretName, issuerName string, issuerK
|
||||
CommonName: "test.domain.com",
|
||||
Organization: []string{"test-org"},
|
||||
SecretName: secretName,
|
||||
Duration: duration,
|
||||
RenewBefore: renewBefore,
|
||||
IssuerRef: v1alpha1.ObjectReference{
|
||||
Name: issuerName,
|
||||
Kind: issuerKind,
|
||||
@ -367,15 +369,17 @@ func NewCertManagerBasicCertificate(name, secretName, issuerName string, issuerK
|
||||
}
|
||||
}
|
||||
|
||||
func NewCertManagerACMECertificate(name, secretName, issuerName string, issuerKind string, ingressClass string, cn string, dnsNames ...string) *v1alpha1.Certificate {
|
||||
func NewCertManagerACMECertificate(name, secretName, issuerName string, issuerKind string, duration, renewBefore *metav1.Duration, ingressClass string, cn string, dnsNames ...string) *v1alpha1.Certificate {
|
||||
return &v1alpha1.Certificate{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Spec: v1alpha1.CertificateSpec{
|
||||
CommonName: cn,
|
||||
DNSNames: dnsNames,
|
||||
SecretName: secretName,
|
||||
CommonName: cn,
|
||||
DNSNames: dnsNames,
|
||||
SecretName: secretName,
|
||||
Duration: duration,
|
||||
RenewBefore: renewBefore,
|
||||
IssuerRef: v1alpha1.ObjectReference{
|
||||
Name: issuerName,
|
||||
Kind: issuerKind,
|
||||
@ -396,14 +400,16 @@ func NewCertManagerACMECertificate(name, secretName, issuerName string, issuerKi
|
||||
}
|
||||
}
|
||||
|
||||
func NewCertManagerVaultCertificate(name, secretName, issuerName string, issuerKind string) *v1alpha1.Certificate {
|
||||
func NewCertManagerVaultCertificate(name, secretName, issuerName string, issuerKind string, duration, renewBefore *metav1.Duration) *v1alpha1.Certificate {
|
||||
return &v1alpha1.Certificate{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Spec: v1alpha1.CertificateSpec{
|
||||
CommonName: "test.domain.com",
|
||||
SecretName: secretName,
|
||||
CommonName: "test.domain.com",
|
||||
SecretName: secretName,
|
||||
Duration: duration,
|
||||
RenewBefore: renewBefore,
|
||||
IssuerRef: v1alpha1.ObjectReference{
|
||||
Name: issuerName,
|
||||
Kind: issuerKind,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user