Merge pull request #2643 from munnerz/keystore-mvp
Add --experimental-issue-pkcs12 flag to enable PKCS12 bundle generation
This commit is contained in:
commit
02da8fbd6f
36
LICENSES
36
LICENSES
@ -17898,3 +17898,39 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
= vendor/sigs.k8s.io/yaml/LICENSE 0ceb9ff3b27d3a8cf451ca3785d73c71
|
||||
================================================================================
|
||||
|
||||
|
||||
================================================================================
|
||||
= vendor/software.sslmate.com/src/go-pkcs12 licensed under: =
|
||||
|
||||
Copyright (c) 2015, 2018 Opsmate, Inc.
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
= vendor/software.sslmate.com/src/go-pkcs12/LICENSE 59ab963efbc183656060b23893ea5745
|
||||
================================================================================
|
||||
|
||||
|
||||
@ -94,6 +94,18 @@ type ControllerOptions struct {
|
||||
|
||||
// DNSNames are the dns names that should be set on the serving certificate
|
||||
WebhookDNSNames []string
|
||||
|
||||
// ExperimentalIssuePKCS12, if true, will make the certificates controller
|
||||
// create a `keystore.p12` in the Secret resource for each Certificate.
|
||||
// This can only be toggled globally, and the keystore will be encrypted
|
||||
// with the supplied ExperimentalPKCS12KeystorePassword.
|
||||
// This flag is likely to be removed in future in favour of native PKCS12
|
||||
// keystore bundle support.
|
||||
ExperimentalIssuePKCS12 bool
|
||||
// ExperimentalPKCS12KeystorePassword is the password used to encrypt and
|
||||
// decrypt PKCS#12 bundles stored in Secret resources.
|
||||
// This option only has any affect is ExperimentalIssuePKCS12 is true.
|
||||
ExperimentalPKCS12KeystorePassword string
|
||||
}
|
||||
|
||||
const (
|
||||
@ -289,6 +301,12 @@ func (s *ControllerOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
"serving certificate.")
|
||||
fs.StringSliceVar(&s.WebhookDNSNames, "webhook-dns-names", defaultWebhookDNSNames, "Comma-separated list of DNS names that should be present on "+
|
||||
"the webhook's serving certificate.")
|
||||
|
||||
fs.BoolVar(&s.ExperimentalIssuePKCS12, "experimental-issue-pkcs12", false, "If true, the certificate controller will create 'keystore.p12' files in Secret resources it "+
|
||||
"manages, containing a copy of the certificate data encrypted using the provided --experimental-pkcs12-keystore-password. "+
|
||||
"If true, --experimental-pkcs12-keystore-password must be provided.")
|
||||
fs.StringVar(&s.ExperimentalPKCS12KeystorePassword, "experimental-pkcs12-keystore-password", "", "The password used to encrypt and decrypt PKCS#12 "+
|
||||
"bundles stored in Secret resources. This field is required if --experimental-issue-pkcs12 is enabled.")
|
||||
}
|
||||
|
||||
func (o *ControllerOptions) Validate() error {
|
||||
@ -306,5 +324,10 @@ func (o *ControllerOptions) Validate() error {
|
||||
return fmt.Errorf("invalid DNS server (%v): %v", err, server)
|
||||
}
|
||||
}
|
||||
|
||||
if o.ExperimentalIssuePKCS12 && len(o.ExperimentalPKCS12KeystorePassword) == 0 {
|
||||
return fmt.Errorf("--experimental-pkcs12-keystore-password must be specified if --experimental-issue-pkcs12 is enabled")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
1
go.mod
1
go.mod
@ -55,4 +55,5 @@ require (
|
||||
sigs.k8s.io/controller-runtime v0.4.0
|
||||
sigs.k8s.io/controller-tools v0.2.5
|
||||
sigs.k8s.io/testing_frameworks v0.1.2
|
||||
software.sslmate.com/src/go-pkcs12 v0.0.0-20180114231543-2291e8f0f237
|
||||
)
|
||||
|
||||
1
go.sum
1
go.sum
@ -744,4 +744,5 @@ sigs.k8s.io/testing_frameworks v0.1.2 h1:vK0+tvjF0BZ/RYFeZ1E6BYBwHJJXhjuZ3TdsEKH
|
||||
sigs.k8s.io/testing_frameworks v0.1.2/go.mod h1:ToQrwSC3s8Xf/lADdZp3Mktcql9CG0UAmdJG9th5i0w=
|
||||
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
software.sslmate.com/src/go-pkcs12 v0.0.0-20180114231543-2291e8f0f237 h1:iAEkCBPbRaflBgZ7o9gjVUuWuvWeV4sytFWg9o+Pj2k=
|
||||
software.sslmate.com/src/go-pkcs12 v0.0.0-20180114231543-2291e8f0f237/go.mod h1:/xvNRWUqm0+/ZMiF4EX00vrSCMsE4/NHb+Pt3freEeQ=
|
||||
|
||||
@ -5,6 +5,7 @@ go_library(
|
||||
srcs = [
|
||||
"checks.go",
|
||||
"controller.go",
|
||||
"keystore.go",
|
||||
"sync.go",
|
||||
"util.go",
|
||||
],
|
||||
@ -26,6 +27,7 @@ go_library(
|
||||
"//pkg/util/pki:go_default_library",
|
||||
"@com_github_go_logr_logr//:go_default_library",
|
||||
"@com_github_kr_pretty//:go_default_library",
|
||||
"@com_sslmate_software_src_go_pkcs12//:go_default_library",
|
||||
"@io_k8s_api//core/v1:go_default_library",
|
||||
"@io_k8s_apimachinery//pkg/api/errors:go_default_library",
|
||||
"@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library",
|
||||
|
||||
@ -19,6 +19,7 @@ package certificates
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"k8s.io/client-go/kubernetes"
|
||||
@ -82,6 +83,18 @@ type certificateRequestManager struct {
|
||||
// Secret resource will be automatically deleted.
|
||||
// This option is disabled by default.
|
||||
enableSecretOwnerReferences bool
|
||||
|
||||
// experimentalIssuePKCS12, if true, will make the certificates controller
|
||||
// create a `keystore.p12` in the Secret resource for each Certificate.
|
||||
// This can only be toggled globally, and the keystore will be encrypted
|
||||
// with the supplied ExperimentalPKCS12KeystorePassword.
|
||||
// This flag is likely to be removed in future in favour of native PKCS12
|
||||
// keystore bundle support.
|
||||
experimentalIssuePKCS12 bool
|
||||
// ExperimentalPKCS12KeystorePassword is the password used to encrypt and
|
||||
// decrypt PKCS#12 bundles stored in Secret resources.
|
||||
// This option only has any affect is ExperimentalIssuePKCS12 is true.
|
||||
experimentalPKCS12KeystorePassword string
|
||||
}
|
||||
|
||||
type localTemporarySignerFn func(crt *cmapi.Certificate, pk []byte) ([]byte, error)
|
||||
@ -139,6 +152,13 @@ func (c *certificateRequestManager) Register(ctx *controllerpkg.Context) (workqu
|
||||
c.localTemporarySigner = generateLocallySignedTemporaryCertificate
|
||||
c.enableSecretOwnerReferences = ctx.CertificateOptions.EnableOwnerRef
|
||||
|
||||
// Experimental PKCS12 handling options
|
||||
c.experimentalIssuePKCS12 = ctx.CertificateOptions.ExperimentalIssuePKCS12
|
||||
c.experimentalPKCS12KeystorePassword = ctx.CertificateOptions.ExperimentalPKCS12KeystorePassword
|
||||
if c.experimentalIssuePKCS12 && len(c.experimentalPKCS12KeystorePassword) == 0 {
|
||||
return nil, nil, nil, fmt.Errorf("if experimental pkcs12 issuance is enabled, a keystore password must be provided")
|
||||
}
|
||||
|
||||
c.cmClient = ctx.CMClient
|
||||
c.kubeClient = ctx.Client
|
||||
|
||||
|
||||
71
pkg/controller/certificates/keystore.go
Normal file
71
pkg/controller/certificates/keystore.go
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
Copyright 2020 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.
|
||||
*/
|
||||
|
||||
// This file defines methods used for PKCS#12 support.
|
||||
// This is an experimental feature and the contents of this file are intended
|
||||
// to be absorbed into a more fully fledged implementing ahead of the v0.15
|
||||
// release.
|
||||
// This should hopefully not exist by the next time you come to read this :)
|
||||
|
||||
package certificates
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
|
||||
"software.sslmate.com/src/go-pkcs12"
|
||||
|
||||
"github.com/jetstack/cert-manager/pkg/util/pki"
|
||||
)
|
||||
|
||||
const (
|
||||
// pkcs12SecretKey is the name of the data entry in the Secret resource
|
||||
// used to store the p12 file.
|
||||
pkcs12SecretKey = "keystore.p12"
|
||||
)
|
||||
|
||||
// encodePKCS12Keystore will encode a PKCS12 keystore using the password provided.
|
||||
// The key, certificate and CA data must be provided in PKCS1 or PKCS8 PEM format.
|
||||
// If the certificate data contains multiple certificates, the first will be used
|
||||
// as the keystores 'certificate' and the remaining certificates will be prepended
|
||||
// to the list of CAs in the resulting keystore.
|
||||
func encodePKCS12Keystore(password string, rawKey []byte, certPem []byte, caPem []byte) ([]byte, error) {
|
||||
key, err := pki.DecodePrivateKeyBytes(rawKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
certs, err := pki.DecodeX509CertificateChainBytes(certPem)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var cas []*x509.Certificate
|
||||
if len(caPem) > 0 {
|
||||
cas, err = pki.DecodeX509CertificateChainBytes(caPem)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// prepend the certificate chain to the list of certificates as the PKCS12
|
||||
// library only allows setting a single certificate.
|
||||
if len(certs) > 1 {
|
||||
cas = append(certs[1:], cas...)
|
||||
}
|
||||
}
|
||||
keystoreData, err := pkcs12.Encode(rand.Reader, key, certs[0], cas, password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return keystoreData, nil
|
||||
}
|
||||
@ -17,6 +17,7 @@ limitations under the License.
|
||||
package certificates
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
@ -536,7 +537,7 @@ func (c *certificateRequestManager) updateSecretData(ctx context.Context, crt *c
|
||||
}
|
||||
|
||||
newSecret := s.DeepCopy()
|
||||
err := setSecretValues(ctx, crt, newSecret, secretData{pk: data.pk, cert: data.cert, ca: data.ca})
|
||||
err := c.setSecretValues(ctx, crt, newSecret, secretData{pk: data.pk, cert: data.cert, ca: data.ca})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@ -582,7 +583,7 @@ func (c *certificateRequestManager) issueTemporaryCertificate(ctx context.Contex
|
||||
}
|
||||
|
||||
newSecret := secret.DeepCopy()
|
||||
err = setSecretValues(ctx, crt, newSecret, secretData{pk: key, cert: tempCertData})
|
||||
err = c.setSecretValues(ctx, crt, newSecret, secretData{pk: key, cert: tempCertData})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -808,12 +809,28 @@ type secretData struct {
|
||||
// If updating an existing Secret resource returned by an api client 'lister',
|
||||
// make sure to DeepCopy the object first to avoid modifying data in-cache.
|
||||
// It will also update depreciated issuer name and kind annotations if they exist.
|
||||
func setSecretValues(ctx context.Context, crt *cmapi.Certificate, s *corev1.Secret, data secretData) error {
|
||||
func (c *certificateRequestManager) setSecretValues(ctx context.Context, crt *cmapi.Certificate, s *corev1.Secret, data secretData) error {
|
||||
// initialize the `Data` field if it is nil
|
||||
if s.Data == nil {
|
||||
s.Data = make(map[string][]byte)
|
||||
}
|
||||
|
||||
// Handle the experimental PKCS12 support
|
||||
if c.experimentalIssuePKCS12 {
|
||||
// Only write a new PKCS12 file if any of the private key/certificate/CA data has
|
||||
// actually changed.
|
||||
if data.pk != nil && data.cert != nil &&
|
||||
(!bytes.Equal(s.Data[corev1.TLSPrivateKeyKey], data.pk) ||
|
||||
!bytes.Equal(s.Data[corev1.TLSCertKey], data.cert) ||
|
||||
!bytes.Equal(s.Data[cmmeta.TLSCAKey], data.ca)) {
|
||||
keystoreData, err := encodePKCS12Keystore(c.experimentalPKCS12KeystorePassword, data.pk, data.cert, data.ca)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error encoding PKCS12 bundle: %w", err)
|
||||
}
|
||||
// always overwrite the keystore entry for now
|
||||
s.Data[pkcs12SecretKey] = keystoreData
|
||||
}
|
||||
}
|
||||
s.Data[corev1.TLSPrivateKeyKey] = data.pk
|
||||
s.Data[corev1.TLSCertKey] = data.cert
|
||||
s.Data[cmmeta.TLSCAKey] = data.ca
|
||||
|
||||
@ -132,6 +132,18 @@ type CertificateOptions struct {
|
||||
// EnableOwnerRef controls whether the certificate is configured as an owner of
|
||||
// secret where the effective TLS certificate is stored.
|
||||
EnableOwnerRef bool
|
||||
|
||||
// ExperimentalIssuePKCS12, if true, will make the certificates controller
|
||||
// create a `keystore.p12` in the Secret resource for each Certificate.
|
||||
// This can only be toggled globally, and the keystore will be encrypted
|
||||
// with the supplied ExperimentalPKCS12KeystorePassword.
|
||||
// This flag is likely to be removed in future in favour of native PKCS12
|
||||
// keystore bundle support.
|
||||
ExperimentalIssuePKCS12 bool
|
||||
// ExperimentalPKCS12KeystorePassword is the password used to encrypt and
|
||||
// decrypt PKCS#12 bundles stored in Secret resources.
|
||||
// This option only has any affect is ExperimentalIssuePKCS12 is true.
|
||||
ExperimentalPKCS12KeystorePassword string
|
||||
}
|
||||
|
||||
type SchedulerOptions struct {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user