diff --git a/factory/cert_utils.go b/factory/cert_utils.go index cb62678..6b28abe 100644 --- a/factory/cert_utils.go +++ b/factory/cert_utils.go @@ -40,6 +40,31 @@ func NewSelfSignedCACert(key crypto.Signer, cn string, org ...string) (*x509.Cer return x509.ParseCertificate(certDERBytes) } +func NewSignedClientCert(signer crypto.Signer, caCert *x509.Certificate, caKey crypto.Signer, cn string) (*x509.Certificate, error) { + serialNumber, err := rand.Int(rand.Reader, new(big.Int).SetInt64(math.MaxInt64)) + if err != nil { + return nil, err + } + + parent := x509.Certificate{ + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + NotAfter: time.Now().Add(time.Hour * 24 * 365).UTC(), + NotBefore: caCert.NotBefore, + SerialNumber: serialNumber, + Subject: pkix.Name{ + CommonName: cn, + }, + } + + cert, err := x509.CreateCertificate(rand.Reader, &parent, caCert, signer.Public(), caKey) + if err != nil { + return nil, err + } + + return x509.ParseCertificate(cert) +} + func NewSignedCert(signer crypto.Signer, caCert *x509.Certificate, caKey crypto.Signer, cn string, orgs []string, domains []string, ips []net.IP) (*x509.Certificate, error) { diff --git a/storage/kubernetes/ca.go b/storage/kubernetes/ca.go index 5297418..5d41845 100644 --- a/storage/kubernetes/ca.go +++ b/storage/kubernetes/ca.go @@ -19,6 +19,23 @@ func LoadOrGenCA(secrets v1controller.SecretClient, namespace, name string) (*x5 return factory.LoadCA(secret.Data[v1.TLSCertKey], secret.Data[v1.TLSPrivateKeyKey]) } +func LoadOrGenClient(secrets v1controller.SecretClient, namespace, name, cn string, ca *x509.Certificate, key crypto.Signer) (*x509.Certificate, crypto.Signer, error) { + secret, err := getClientSecret(secrets, namespace, name, cn, ca, key) + if err != nil { + return nil, nil, err + } + return factory.LoadCA(secret.Data[v1.TLSCertKey], secret.Data[v1.TLSPrivateKeyKey]) +} + +func getClientSecret(secrets v1controller.SecretClient, namespace, name, cn string, caCert *x509.Certificate, caKey crypto.Signer) (*v1.Secret, error) { + s, err := secrets.Get(namespace, name, metav1.GetOptions{}) + if !errors.IsNotFound(err) { + return s, err + } + + return createAndStoreClientCert(secrets, namespace, name, cn, caCert, caKey) +} + func getSecret(secrets v1controller.SecretClient, namespace, name string) (*v1.Secret, error) { s, err := secrets.Get(namespace, name, metav1.GetOptions{}) if !errors.IsNotFound(err) { @@ -28,6 +45,37 @@ func getSecret(secrets v1controller.SecretClient, namespace, name string) (*v1.S return createAndStore(secrets, namespace, name) } +func createAndStoreClientCert(secrets v1controller.SecretClient, namespace string, name, cn string, caCert *x509.Certificate, caKey crypto.Signer) (*v1.Secret, error) { + key, err := factory.NewPrivateKey() + if err != nil { + return nil, err + } + + cert, err := factory.NewSignedClientCert(key, caCert, caKey, cn) + if err != nil { + return nil, err + } + + certPem, keyPem, err := factory.Marshal(cert, key) + if err != nil { + return nil, err + } + + secret := &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Data: map[string][]byte{ + v1.TLSCertKey: certPem, + v1.TLSPrivateKeyKey: keyPem, + }, + Type: v1.SecretTypeTLS, + } + + return secrets.Create(secret) +} + func createAndStore(secrets v1controller.SecretClient, namespace string, name string) (*v1.Secret, error) { ca, cert, err := factory.GenCA() if err != nil {