Add --experimental-issue-jks flag to enable JKS bundle generation
Signed-off-by: James Munnelly <james@munnelly.eu>
This commit is contained in:
parent
f2c462d29f
commit
98bc0d52f9
29
LICENSES
29
LICENSES
@ -10550,6 +10550,35 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
================================================================================
|
||||
|
||||
|
||||
================================================================================
|
||||
= vendor/github.com/pavel-v-chernykh/keystore-go licensed under: =
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Pavel Chernykh
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
= vendor/github.com/pavel-v-chernykh/keystore-go/LICENSE 1ef2fe9f6c2064290158c50854f690dc
|
||||
================================================================================
|
||||
|
||||
|
||||
================================================================================
|
||||
= vendor/github.com/pierrec/lz4 licensed under: =
|
||||
|
||||
|
||||
@ -106,6 +106,18 @@ type ControllerOptions struct {
|
||||
// decrypt PKCS#12 bundles stored in Secret resources.
|
||||
// This option only has any affect is ExperimentalIssuePKCS12 is true.
|
||||
ExperimentalPKCS12KeystorePassword string
|
||||
|
||||
// ExperimentalIssueJKS, if true, will make the certificates controller
|
||||
// create a `keystore.jks` in the Secret resource for each Certificate.
|
||||
// This can only be toggled globally, and the keystore will be encrypted
|
||||
// with the supplied ExperimentalJKSPassword.
|
||||
// This flag is likely to be removed in future in favour of native JKS
|
||||
// keystore bundle support.
|
||||
ExperimentalIssueJKS bool
|
||||
// ExperimentalJKSPassword is the password used to encrypt and
|
||||
// decrypt JKS bundles stored in Secret resources.
|
||||
// This option only has any affect is ExperimentalIssueJKS is true.
|
||||
ExperimentalJKSPassword string
|
||||
}
|
||||
|
||||
const (
|
||||
@ -307,6 +319,11 @@ func (s *ControllerOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
"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.")
|
||||
fs.BoolVar(&s.ExperimentalIssueJKS, "experimental-issue-jks", false, "If true, the certificate controller will create 'keystore.jks' files in Secret resources it "+
|
||||
"manages, containing a copy of the certificate data encrypted using the provided --experimental-jks-password. "+
|
||||
"If true, --experimental-jks-password must be provided.")
|
||||
fs.StringVar(&s.ExperimentalJKSPassword, "experimental-jks-password", "", "The password used to encrypt and decrypt JKS "+
|
||||
"bundles stored in Secret resources. This field is required if --experimental-issue-jks is enabled.")
|
||||
}
|
||||
|
||||
func (o *ControllerOptions) Validate() error {
|
||||
|
||||
1
go.mod
1
go.mod
@ -30,6 +30,7 @@ require (
|
||||
github.com/munnerz/crd-schema-fuzz v0.0.0-20191114184610-fbd148d44a0a
|
||||
github.com/onsi/ginkgo v1.10.1
|
||||
github.com/onsi/gomega v1.7.0
|
||||
github.com/pavel-v-chernykh/keystore-go v2.1.0+incompatible
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/prometheus/client_golang v1.0.0
|
||||
github.com/spf13/cobra v0.0.5
|
||||
|
||||
2
go.sum
2
go.sum
@ -401,6 +401,8 @@ github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pavel-v-chernykh/keystore-go v2.1.0+incompatible h1:Jd6xfriVlJ6hWPvYOE0Ni0QWcNTLRehfGPFxr3eSL80=
|
||||
github.com/pavel-v-chernykh/keystore-go v2.1.0+incompatible/go.mod h1:xlUlxe/2ItGlQyMTstqeDv9r3U4obH7xYd26TbDQutY=
|
||||
github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g=
|
||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
|
||||
@ -2451,3 +2451,11 @@ def go_repositories():
|
||||
sum = "h1:TxyelI5cVkbREznMhfzycHdkp5cLA7DpE+GKjSslYhM=",
|
||||
version = "v1.0.0-20181015200546-f715ec2f112d",
|
||||
)
|
||||
go_repository(
|
||||
name = "com_github_pavel_v_chernykh_keystore_go",
|
||||
build_file_generation = "on",
|
||||
build_file_proto_mode = "disable",
|
||||
importpath = "github.com/pavel-v-chernykh/keystore-go",
|
||||
sum = "h1:Jd6xfriVlJ6hWPvYOE0Ni0QWcNTLRehfGPFxr3eSL80=",
|
||||
version = "v2.1.0+incompatible",
|
||||
)
|
||||
|
||||
@ -27,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_github_pavel_v_chernykh_keystore_go//: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",
|
||||
|
||||
@ -95,6 +95,17 @@ type certificateRequestManager struct {
|
||||
// decrypt PKCS#12 bundles stored in Secret resources.
|
||||
// This option only has any affect is ExperimentalIssuePKCS12 is true.
|
||||
experimentalPKCS12KeystorePassword string
|
||||
// experimentalIssueJKS, if true, will make the certificates controller
|
||||
// create a `keystore.jks` in the Secret resource for each Certificate.
|
||||
// This can only be toggled globally, and the keystore will be encrypted
|
||||
// with the supplied ExperimentalJKSPassword.
|
||||
// This flag is likely to be removed in future in favour of native JKS
|
||||
// keystore bundle support.
|
||||
experimentalIssueJKS bool
|
||||
// experimentalJKSPassword is the password used to encrypt and
|
||||
// decrypt JKS files stored in Secret resources.
|
||||
// This option only has any affect is experimentalIssueJKS is true.
|
||||
experimentalJKSPassword string
|
||||
}
|
||||
|
||||
type localTemporarySignerFn func(crt *cmapi.Certificate, pk []byte) ([]byte, error)
|
||||
@ -158,6 +169,12 @@ func (c *certificateRequestManager) Register(ctx *controllerpkg.Context) (workqu
|
||||
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")
|
||||
}
|
||||
// Experimental JKS handling options
|
||||
c.experimentalIssueJKS = ctx.CertificateOptions.ExperimentalIssueJKS
|
||||
c.experimentalJKSPassword = ctx.CertificateOptions.ExperimentalJKSPassword
|
||||
if c.experimentalIssueJKS && len(c.experimentalJKSPassword) == 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
|
||||
|
||||
@ -23,9 +23,12 @@ limitations under the License.
|
||||
package certificates
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"time"
|
||||
|
||||
jks "github.com/pavel-v-chernykh/keystore-go"
|
||||
"software.sslmate.com/src/go-pkcs12"
|
||||
|
||||
"github.com/jetstack/cert-manager/pkg/util/pki"
|
||||
@ -35,6 +38,10 @@ const (
|
||||
// pkcs12SecretKey is the name of the data entry in the Secret resource
|
||||
// used to store the p12 file.
|
||||
pkcs12SecretKey = "keystore.p12"
|
||||
|
||||
// jksSecretKey is the name of the data entry in the Secret resource
|
||||
// used to store the jks file.
|
||||
jksSecretKey = "keystore.jks"
|
||||
)
|
||||
|
||||
// encodePKCS12Keystore will encode a PKCS12 keystore using the password provided.
|
||||
@ -69,3 +76,61 @@ func encodePKCS12Keystore(password string, rawKey []byte, certPem []byte, caPem
|
||||
}
|
||||
return keystoreData, nil
|
||||
}
|
||||
|
||||
func encodeJKSKeystore(password string, rawKey []byte, certPem []byte, caPem []byte) ([]byte, error) {
|
||||
// encode the private key to PKCS8
|
||||
key, err := pki.DecodePrivateKeyBytes(rawKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keyDER, err := x509.MarshalPKCS8PrivateKey(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// encode the certificate chain
|
||||
chain, err := pki.DecodeX509CertificateChainBytes(certPem)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
certs := make([]jks.Certificate, len(chain))
|
||||
for i, cert := range chain {
|
||||
certs[i] = jks.Certificate{
|
||||
Type: "X509",
|
||||
Content: cert.Raw,
|
||||
}
|
||||
}
|
||||
|
||||
ks := jks.KeyStore{
|
||||
"certificate": jks.PrivateKeyEntry{
|
||||
Entry: jks.Entry{
|
||||
CreationDate: time.Now(),
|
||||
},
|
||||
PrivKey: keyDER,
|
||||
CertChain: certs,
|
||||
},
|
||||
}
|
||||
// add the CA certificate, if set
|
||||
if len(caPem) > 0 {
|
||||
ca, err := pki.DecodeX509CertificateBytes(caPem)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ks["ca"] = jks.TrustedCertificateEntry{
|
||||
Entry: jks.Entry{
|
||||
CreationDate: time.Now(),
|
||||
},
|
||||
Certificate: jks.Certificate{
|
||||
Type: "X509",
|
||||
Content: ca.Raw,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
if err := jks.Encode(buf, ks, []byte(password)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
@ -831,6 +831,23 @@ func (c *certificateRequestManager) setSecretValues(ctx context.Context, crt *cm
|
||||
s.Data[pkcs12SecretKey] = keystoreData
|
||||
}
|
||||
}
|
||||
// Handle the experimental JKS support
|
||||
if c.experimentalIssueJKS {
|
||||
// Only write a new JKS 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 := encodeJKSKeystore(c.experimentalJKSPassword, data.pk, data.cert, data.ca)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error encoding JKS bundle: %w", err)
|
||||
}
|
||||
// always overwrite the keystore entry for now
|
||||
s.Data[jksSecretKey] = keystoreData
|
||||
}
|
||||
}
|
||||
|
||||
s.Data[corev1.TLSPrivateKeyKey] = data.pk
|
||||
s.Data[corev1.TLSCertKey] = data.cert
|
||||
s.Data[cmmeta.TLSCAKey] = data.ca
|
||||
|
||||
@ -144,6 +144,18 @@ type CertificateOptions struct {
|
||||
// decrypt PKCS#12 bundles stored in Secret resources.
|
||||
// This option only has any affect is ExperimentalIssuePKCS12 is true.
|
||||
ExperimentalPKCS12KeystorePassword string
|
||||
|
||||
// ExperimentalIssueJKS, if true, will make the certificates controller
|
||||
// create a `keystore.jks` in the Secret resource for each Certificate.
|
||||
// This can only be toggled globally, and the keystore will be encrypted
|
||||
// with the supplied ExperimentalJKSPassword.
|
||||
// This flag is likely to be removed in future in favour of native JKS
|
||||
// bundle support.
|
||||
ExperimentalIssueJKS bool
|
||||
// ExperimentalJKSPassword is the password used to encrypt and
|
||||
// decrypt JKS bundles stored in Secret resources.
|
||||
// This option only has any affect is ExperimentalIssueJKS is true.
|
||||
ExperimentalJKSPassword string
|
||||
}
|
||||
|
||||
type SchedulerOptions struct {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user