Merge pull request #99494 from enj/enj/i/not_after_ttl_hint

csr: add expirationSeconds field to control cert lifetime

Kubernetes-commit: 659c7e709f3b7f5f2a25e456525cd8747f2e68cc
This commit is contained in:
Kubernetes Publisher 2021-07-01 23:02:12 -07:00
commit ca3a47f0b4
7 changed files with 83 additions and 39 deletions

View File

@ -27,6 +27,7 @@ import (
type CertificateSigningRequestSpecApplyConfiguration struct { type CertificateSigningRequestSpecApplyConfiguration struct {
Request []byte `json:"request,omitempty"` Request []byte `json:"request,omitempty"`
SignerName *string `json:"signerName,omitempty"` SignerName *string `json:"signerName,omitempty"`
ExpirationSeconds *int32 `json:"expirationSeconds,omitempty"`
Usages []v1.KeyUsage `json:"usages,omitempty"` Usages []v1.KeyUsage `json:"usages,omitempty"`
Username *string `json:"username,omitempty"` Username *string `json:"username,omitempty"`
UID *string `json:"uid,omitempty"` UID *string `json:"uid,omitempty"`
@ -58,6 +59,14 @@ func (b *CertificateSigningRequestSpecApplyConfiguration) WithSignerName(value s
return b return b
} }
// WithExpirationSeconds sets the ExpirationSeconds field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the ExpirationSeconds field is set to the value of the last call.
func (b *CertificateSigningRequestSpecApplyConfiguration) WithExpirationSeconds(value int32) *CertificateSigningRequestSpecApplyConfiguration {
b.ExpirationSeconds = &value
return b
}
// WithUsages adds the given value to the Usages field in the declarative configuration // WithUsages adds the given value to the Usages field in the declarative configuration
// and returns the receiver, so that objects can be build by chaining "With" function invocations. // and returns the receiver, so that objects can be build by chaining "With" function invocations.
// If called multiple times, values provided by each call will be appended to the Usages field. // If called multiple times, values provided by each call will be appended to the Usages field.

View File

@ -27,6 +27,7 @@ import (
type CertificateSigningRequestSpecApplyConfiguration struct { type CertificateSigningRequestSpecApplyConfiguration struct {
Request []byte `json:"request,omitempty"` Request []byte `json:"request,omitempty"`
SignerName *string `json:"signerName,omitempty"` SignerName *string `json:"signerName,omitempty"`
ExpirationSeconds *int32 `json:"expirationSeconds,omitempty"`
Usages []v1beta1.KeyUsage `json:"usages,omitempty"` Usages []v1beta1.KeyUsage `json:"usages,omitempty"`
Username *string `json:"username,omitempty"` Username *string `json:"username,omitempty"`
UID *string `json:"uid,omitempty"` UID *string `json:"uid,omitempty"`
@ -58,6 +59,14 @@ func (b *CertificateSigningRequestSpecApplyConfiguration) WithSignerName(value s
return b return b
} }
// WithExpirationSeconds sets the ExpirationSeconds field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the ExpirationSeconds field is set to the value of the last call.
func (b *CertificateSigningRequestSpecApplyConfiguration) WithExpirationSeconds(value int32) *CertificateSigningRequestSpecApplyConfiguration {
b.ExpirationSeconds = &value
return b
}
// WithUsages adds the given value to the Usages field in the declarative configuration // WithUsages adds the given value to the Usages field in the declarative configuration
// and returns the receiver, so that objects can be build by chaining "With" function invocations. // and returns the receiver, so that objects can be build by chaining "With" function invocations.
// If called multiple times, values provided by each call will be appended to the Usages field. // If called multiple times, values provided by each call will be appended to the Usages field.

View File

@ -2843,6 +2843,9 @@ var schemaYAML = typed.YAMLObject(`types:
- name: io.k8s.api.certificates.v1.CertificateSigningRequestSpec - name: io.k8s.api.certificates.v1.CertificateSigningRequestSpec
map: map:
fields: fields:
- name: expirationSeconds
type:
scalar: numeric
- name: extra - name: extra
type: type:
map: map:
@ -2939,6 +2942,9 @@ var schemaYAML = typed.YAMLObject(`types:
- name: io.k8s.api.certificates.v1beta1.CertificateSigningRequestSpec - name: io.k8s.api.certificates.v1beta1.CertificateSigningRequestSpec
map: map:
fields: fields:
- name: expirationSeconds
type:
scalar: numeric
- name: extra - name: extra
type: type:
map: map:

4
go.mod
View File

@ -30,7 +30,7 @@ require (
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba
google.golang.org/protobuf v1.26.0 google.golang.org/protobuf v1.26.0
k8s.io/api v0.0.0-20210701054328-f75dde501d56 k8s.io/api v0.0.0-20210702094336-49e8721f8489
k8s.io/apimachinery v0.0.0-20210701054147-830375057167 k8s.io/apimachinery v0.0.0-20210701054147-830375057167
k8s.io/klog/v2 v2.9.0 k8s.io/klog/v2 v2.9.0
k8s.io/utils v0.0.0-20210521133846-da695404a2bc k8s.io/utils v0.0.0-20210521133846-da695404a2bc
@ -39,6 +39,6 @@ require (
) )
replace ( replace (
k8s.io/api => k8s.io/api v0.0.0-20210701054328-f75dde501d56 k8s.io/api => k8s.io/api v0.0.0-20210702094336-49e8721f8489
k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20210701054147-830375057167 k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20210701054147-830375057167
) )

4
go.sum
View File

@ -444,8 +444,8 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
k8s.io/api v0.0.0-20210701054328-f75dde501d56 h1:D7+RjL9qHD5SvrWwSnT0NugysPfJAopsJphgGxpp+kU= k8s.io/api v0.0.0-20210702094336-49e8721f8489 h1:R+giJyPdlHfUvmzHBQ6tm2tlmxcZ2NWgkGSxLbQZ/ZM=
k8s.io/api v0.0.0-20210701054328-f75dde501d56/go.mod h1:zoURDvOPW5UMFZr2YUU/sStjYnWSPt+x+MM4R94ATgQ= k8s.io/api v0.0.0-20210702094336-49e8721f8489/go.mod h1:zoURDvOPW5UMFZr2YUU/sStjYnWSPt+x+MM4R94ATgQ=
k8s.io/apimachinery v0.0.0-20210701054147-830375057167 h1:fob/j8+uMBIVvyo+9bG7GvjFSj0LX3RNuSXW+RcUrwo= k8s.io/apimachinery v0.0.0-20210701054147-830375057167 h1:fob/j8+uMBIVvyo+9bG7GvjFSj0LX3RNuSXW+RcUrwo=
k8s.io/apimachinery v0.0.0-20210701054147-830375057167/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= k8s.io/apimachinery v0.0.0-20210701054147-830375057167/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0=
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=

View File

@ -88,6 +88,11 @@ type Config struct {
// SignerName is the name of the certificate signer that should sign certificates // SignerName is the name of the certificate signer that should sign certificates
// generated by the manager. // generated by the manager.
SignerName string SignerName string
// RequestedCertificateLifetime is the requested lifetime length for certificates generated by the manager.
// Optional.
// This will set the spec.expirationSeconds field on the CSR. Controlling the lifetime of
// the issued certificate is not guaranteed as the signer may choose to ignore the request.
RequestedCertificateLifetime *time.Duration
// Usages is the types of usages that certificates generated by the manager // Usages is the types of usages that certificates generated by the manager
// can be used for. // can be used for.
Usages []certificates.KeyUsage Usages []certificates.KeyUsage
@ -186,6 +191,7 @@ type manager struct {
dynamicTemplate bool dynamicTemplate bool
signerName string signerName string
requestedCertificateLifetime *time.Duration
usages []certificates.KeyUsage usages []certificates.KeyUsage
forceRotation bool forceRotation bool
@ -235,6 +241,7 @@ func NewManager(config *Config) (Manager, error) {
getTemplate: getTemplate, getTemplate: getTemplate,
dynamicTemplate: config.GetTemplate != nil, dynamicTemplate: config.GetTemplate != nil,
signerName: config.SignerName, signerName: config.SignerName,
requestedCertificateLifetime: config.RequestedCertificateLifetime,
usages: config.Usages, usages: config.Usages,
certStore: config.CertificateStore, certStore: config.CertificateStore,
cert: cert, cert: cert,
@ -459,7 +466,7 @@ func (m *manager) rotateCerts() (bool, error) {
// Call the Certificate Signing Request API to get a certificate for the // Call the Certificate Signing Request API to get a certificate for the
// new private key. // new private key.
reqName, reqUID, err := csr.RequestCertificate(clientSet, csrPEM, "", m.signerName, m.usages, privateKey) reqName, reqUID, err := csr.RequestCertificate(clientSet, csrPEM, "", m.signerName, m.requestedCertificateLifetime, m.usages, privateKey)
if err != nil { if err != nil {
utilruntime.HandleError(fmt.Errorf("%s: Failed while requesting a signed certificate from the control plane: %v", m.name, err)) utilruntime.HandleError(fmt.Errorf("%s: Failed while requesting a signed certificate from the control plane: %v", m.name, err))
if m.certificateRenewFailure != nil { if m.certificateRenewFailure != nil {

View File

@ -25,8 +25,6 @@ import (
"reflect" "reflect"
"time" "time"
"k8s.io/klog/v2"
certificatesv1 "k8s.io/api/certificates/v1" certificatesv1 "k8s.io/api/certificates/v1"
certificatesv1beta1 "k8s.io/api/certificates/v1beta1" certificatesv1beta1 "k8s.io/api/certificates/v1beta1"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
@ -41,12 +39,16 @@ import (
"k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/cache"
watchtools "k8s.io/client-go/tools/watch" watchtools "k8s.io/client-go/tools/watch"
certutil "k8s.io/client-go/util/cert" certutil "k8s.io/client-go/util/cert"
"k8s.io/klog/v2"
"k8s.io/utils/pointer"
) )
// RequestCertificate will either use an existing (if this process has run // RequestCertificate will either use an existing (if this process has run
// before but not to completion) or create a certificate signing request using the // before but not to completion) or create a certificate signing request using the
// PEM encoded CSR and send it to API server. // PEM encoded CSR and send it to API server. An optional requestedDuration may be passed
func RequestCertificate(client clientset.Interface, csrData []byte, name string, signerName string, usages []certificatesv1.KeyUsage, privateKey interface{}) (reqName string, reqUID types.UID, err error) { // to set the spec.expirationSeconds field on the CSR to control the lifetime of the issued
// certificate. This is not guaranteed as the signer may choose to ignore the request.
func RequestCertificate(client clientset.Interface, csrData []byte, name, signerName string, requestedDuration *time.Duration, usages []certificatesv1.KeyUsage, privateKey interface{}) (reqName string, reqUID types.UID, err error) {
csr := &certificatesv1.CertificateSigningRequest{ csr := &certificatesv1.CertificateSigningRequest{
// Username, UID, Groups will be injected by API server. // Username, UID, Groups will be injected by API server.
TypeMeta: metav1.TypeMeta{Kind: "CertificateSigningRequest"}, TypeMeta: metav1.TypeMeta{Kind: "CertificateSigningRequest"},
@ -62,6 +64,9 @@ func RequestCertificate(client clientset.Interface, csrData []byte, name string,
if len(csr.Name) == 0 { if len(csr.Name) == 0 {
csr.GenerateName = "csr-" csr.GenerateName = "csr-"
} }
if requestedDuration != nil {
csr.Spec.ExpirationSeconds = DurationToExpirationSeconds(*requestedDuration)
}
reqName, reqUID, err = create(client, csr) reqName, reqUID, err = create(client, csr)
switch { switch {
@ -85,6 +90,14 @@ func RequestCertificate(client clientset.Interface, csrData []byte, name string,
} }
} }
func DurationToExpirationSeconds(duration time.Duration) *int32 {
return pointer.Int32(int32(duration / time.Second))
}
func ExpirationSecondsToDuration(expirationSeconds int32) time.Duration {
return time.Duration(expirationSeconds) * time.Second
}
func get(client clientset.Interface, name string) (*certificatesv1.CertificateSigningRequest, error) { func get(client clientset.Interface, name string) (*certificatesv1.CertificateSigningRequest, error) {
v1req, v1err := client.CertificatesV1().CertificateSigningRequests().Get(context.TODO(), name, metav1.GetOptions{}) v1req, v1err := client.CertificatesV1().CertificateSigningRequests().Get(context.TODO(), name, metav1.GetOptions{})
if v1err == nil || !apierrors.IsNotFound(v1err) { if v1err == nil || !apierrors.IsNotFound(v1err) {