Add --experimental-issue-jks flag to enable JKS bundle generation

Signed-off-by: James Munnelly <james@munnelly.eu>
This commit is contained in:
James Munnelly 2020-03-04 13:02:25 +00:00
parent f2c462d29f
commit 98bc0d52f9
10 changed files with 169 additions and 0 deletions

View File

@ -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: =

View File

@ -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
View File

@ -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
View File

@ -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=

View File

@ -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",
)

View File

@ -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",

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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 {