Update dynamiclistener

Second round of fixes for #1621

Signed-off-by: Brad Davidson <brad.davidson@rancher.com>
This commit is contained in:
Brad Davidson 2020-08-06 16:26:55 -07:00
parent 7fb1797fd3
commit b1d017f892
14 changed files with 207 additions and 80 deletions

2
go.mod
View File

@ -94,7 +94,7 @@ require (
github.com/opencontainers/runc v1.0.0-rc10 github.com/opencontainers/runc v1.0.0-rc10
github.com/opencontainers/selinux v1.5.3-0.20200613095409-bb88c45a3863 github.com/opencontainers/selinux v1.5.3-0.20200613095409-bb88c45a3863
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/rancher/dynamiclistener v0.2.1-0.20200418023342-52ede5ec9234 github.com/rancher/dynamiclistener v0.2.1
github.com/rancher/helm-controller v0.6.5 github.com/rancher/helm-controller v0.6.5
github.com/rancher/kine v0.4.0 github.com/rancher/kine v0.4.0
github.com/rancher/remotedialer v0.2.0 github.com/rancher/remotedialer v0.2.0

4
go.sum
View File

@ -630,8 +630,8 @@ github.com/rancher/cri v1.3.0-k3s.7 h1:5V/AVFLCpYIsJMavJKjKpwrQU92e87dhWSX43N4q8
github.com/rancher/cri v1.3.0-k3s.7/go.mod h1:Ht5T1dIKzm+4NExmb7wDVG6qR+j0xeXIjjhCv1d9geY= github.com/rancher/cri v1.3.0-k3s.7/go.mod h1:Ht5T1dIKzm+4NExmb7wDVG6qR+j0xeXIjjhCv1d9geY=
github.com/rancher/cri-tools v1.18.0-k3s1 h1:pLYthxpSu6k3Up9tNAMA0MK2ERqB6FC1sZQPRSW1qSg= github.com/rancher/cri-tools v1.18.0-k3s1 h1:pLYthxpSu6k3Up9tNAMA0MK2ERqB6FC1sZQPRSW1qSg=
github.com/rancher/cri-tools v1.18.0-k3s1/go.mod h1:Ij/GWNRcEDP6zVN6eQpvN/s0nhuJVtPQFy7RAdl+Wu8= github.com/rancher/cri-tools v1.18.0-k3s1/go.mod h1:Ij/GWNRcEDP6zVN6eQpvN/s0nhuJVtPQFy7RAdl+Wu8=
github.com/rancher/dynamiclistener v0.2.1-0.20200418023342-52ede5ec9234 h1:wZ1Zh7fI7B9hfZw9Ouhz7171CZKu6XffM3ysUhhO6i0= github.com/rancher/dynamiclistener v0.2.1 h1:QiY1jxs2TOLrKB04G36vE2ehEvPMPGiWp8zEHLKB1nE=
github.com/rancher/dynamiclistener v0.2.1-0.20200418023342-52ede5ec9234/go.mod h1:9WusTANoiRr8cDWCTtf5txieulezHbpv4vhLADPp0zU= github.com/rancher/dynamiclistener v0.2.1/go.mod h1:9WusTANoiRr8cDWCTtf5txieulezHbpv4vhLADPp0zU=
github.com/rancher/flannel v0.12.0-k3s1 h1:P23dWSk/9mGT1x2rDWW9JXNrF/0kjftiHwMau/+ZLGM= github.com/rancher/flannel v0.12.0-k3s1 h1:P23dWSk/9mGT1x2rDWW9JXNrF/0kjftiHwMau/+ZLGM=
github.com/rancher/flannel v0.12.0-k3s1/go.mod h1:zQ/9Uhaw0yV4Wh6ljVwHVT1x5KuhenZA+6L8lRzOJEY= github.com/rancher/flannel v0.12.0-k3s1/go.mod h1:zQ/9Uhaw0yV4Wh6ljVwHVT1x5KuhenZA+6L8lRzOJEY=
github.com/rancher/go-powershell v0.0.0-20200701182037-6845e6fcfa79 h1:UeC0rjrIel8hHz92cdVN09Cm4Hz+BhsPP/ZvQnPOr58= github.com/rancher/go-powershell v0.0.0-20200701182037-6845e6fcfa79 h1:UeC0rjrIel8hHz92cdVN09Cm4Hz+BhsPP/ZvQnPOr58=

View File

@ -39,7 +39,7 @@ func (c *Cluster) newListener(ctx context.Context) (net.Listener, http.Handler,
CipherSuites: c.config.TLSCipherSuites, CipherSuites: c.config.TLSCipherSuites,
}, },
SANs: append(c.config.SANs, "localhost", "kubernetes", "kubernetes.default", "kubernetes.default.svc."+c.config.ClusterDomain), SANs: append(c.config.SANs, "localhost", "kubernetes", "kubernetes.default", "kubernetes.default.svc."+c.config.ClusterDomain),
ExpirationDaysCheck: 90, ExpirationDaysCheck: config.CertificateRenewDays,
}) })
} }

View File

@ -20,6 +20,7 @@ const (
FlannelBackendHostGW = "host-gw" FlannelBackendHostGW = "host-gw"
FlannelBackendIPSEC = "ipsec" FlannelBackendIPSEC = "ipsec"
FlannelBackendWireguard = "wireguard" FlannelBackendWireguard = "wireguard"
CertificateRenewDays = 90
) )
type Node struct { type Node struct {

View File

@ -898,7 +898,7 @@ func expired(certFile string, pool *x509.CertPool) bool {
if err != nil { if err != nil {
return true return true
} }
return certutil.IsCertExpired(certificates[0]) return certutil.IsCertExpired(certificates[0], config.CertificateRenewDays)
} }
func cloudControllerManager(ctx context.Context, cfg *config.Control, runtime *config.ControlRuntime) { func cloudControllerManager(ctx context.Context, cfg *config.Control, runtime *config.ControlRuntime) {

View File

@ -33,7 +33,7 @@ import (
"math" "math"
"math/big" "math/big"
"net" "net"
"path" "path/filepath"
"strings" "strings"
"time" "time"
@ -45,6 +45,10 @@ const (
duration365d = time.Hour * 24 * 365 duration365d = time.Hour * 24 * 365
) )
var (
ErrStaticCert = errors.New("cannot renew static certificate")
)
// Config contains the basic fields required for creating a certificate // Config contains the basic fields required for creating a certificate
type Config struct { type Config struct {
CommonName string CommonName string
@ -119,7 +123,13 @@ func NewSignedCert(cfg Config, key crypto.Signer, caCert *x509.Certificate, caKe
if err != nil { if err != nil {
return nil, err return nil, err
} }
return x509.ParseCertificate(certDERBytes)
parsedCert, err := x509.ParseCertificate(certDERBytes)
if err == nil {
logrus.Infof("certificate %s signed by %s: notBefore=%s notAfter=%s",
parsedCert.Subject, caCert.Subject, parsedCert.NotBefore, parsedCert.NotAfter)
}
return parsedCert, err
} }
// MakeEllipticPrivateKeyPEM creates an ECDSA private key // MakeEllipticPrivateKeyPEM creates an ECDSA private key
@ -161,8 +171,8 @@ func GenerateSelfSignedCertKeyWithFixtures(host string, alternateIPs []net.IP, a
maxAge := time.Hour * 24 * 365 // one year self-signed certs maxAge := time.Hour * 24 * 365 // one year self-signed certs
baseName := fmt.Sprintf("%s_%s_%s", host, strings.Join(ipsToStrings(alternateIPs), "-"), strings.Join(alternateDNS, "-")) baseName := fmt.Sprintf("%s_%s_%s", host, strings.Join(ipsToStrings(alternateIPs), "-"), strings.Join(alternateDNS, "-"))
certFixturePath := path.Join(fixtureDirectory, baseName+".crt") certFixturePath := filepath.Join(fixtureDirectory, baseName+".crt")
keyFixturePath := path.Join(fixtureDirectory, baseName+".key") keyFixturePath := filepath.Join(fixtureDirectory, baseName+".key")
if len(fixtureDirectory) > 0 { if len(fixtureDirectory) > 0 {
cert, err := ioutil.ReadFile(certFixturePath) cert, err := ioutil.ReadFile(certFixturePath)
if err == nil { if err == nil {
@ -271,11 +281,11 @@ func ipsToStrings(ips []net.IP) []string {
} }
// IsCertExpired checks if the certificate about to expire // IsCertExpired checks if the certificate about to expire
func IsCertExpired(cert *x509.Certificate) bool { func IsCertExpired(cert *x509.Certificate, days int) bool {
expirationDate := cert.NotAfter expirationDate := cert.NotAfter
diffDays := expirationDate.Sub(time.Now()).Hours() / 24.0 diffDays := time.Until(expirationDate).Hours() / 24.0
if diffDays <= 90 { if diffDays <= float64(days) {
logrus.Infof("certificate will expire in %f days", diffDays) logrus.Infof("certificate %s will expire in %f days at %s", cert.Subject, diffDays, cert.NotAfter)
return true return true
} }
return false return false

View File

@ -34,15 +34,15 @@ func CanReadCertAndKey(certPath, keyPath string) (bool, error) {
certReadable := canReadFile(certPath) certReadable := canReadFile(certPath)
keyReadable := canReadFile(keyPath) keyReadable := canReadFile(keyPath)
if certReadable == false && keyReadable == false { if !certReadable && !keyReadable {
return false, nil return false, nil
} }
if certReadable == false { if !certReadable {
return false, fmt.Errorf("error reading %s, certificate and key must be supplied as a pair", certPath) return false, fmt.Errorf("error reading %s, certificate and key must be supplied as a pair", certPath)
} }
if keyReadable == false { if !keyReadable {
return false, fmt.Errorf("error reading %s, certificate and key must be supplied as a pair", keyPath) return false, fmt.Errorf("error reading %s, certificate and key must be supplied as a pair", keyPath)
} }

View File

@ -11,6 +11,8 @@ import (
"math/big" "math/big"
"net" "net"
"time" "time"
"github.com/sirupsen/logrus"
) )
const ( const (
@ -40,6 +42,31 @@ func NewSelfSignedCACert(key crypto.Signer, cn string, org ...string) (*x509.Cer
return x509.ParseCertificate(certDERBytes) 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, func NewSignedCert(signer crypto.Signer, caCert *x509.Certificate, caKey crypto.Signer, cn string, orgs []string,
domains []string, ips []net.IP) (*x509.Certificate, error) { domains []string, ips []net.IP) (*x509.Certificate, error) {
@ -67,7 +94,12 @@ func NewSignedCert(signer crypto.Signer, caCert *x509.Certificate, caKey crypto.
return nil, err return nil, err
} }
return x509.ParseCertificate(cert) parsedCert, err := x509.ParseCertificate(cert)
if err == nil {
logrus.Infof("certificate %s signed by %s: notBefore=%s notAfter=%s",
parsedCert.Subject, caCert.Subject, parsedCert.NotBefore, parsedCert.NotAfter)
}
return parsedCert, err
} }
func ParseCertPEM(pemCerts []byte) (*x509.Certificate, error) { func ParseCertPEM(pemCerts []byte) (*x509.Certificate, error) {

View File

@ -5,10 +5,10 @@ import (
"crypto/ecdsa" "crypto/ecdsa"
"crypto/elliptic" "crypto/elliptic"
"crypto/rand" "crypto/rand"
"crypto/sha256" "crypto/sha1"
"crypto/x509" "crypto/x509"
"encoding/hex"
"encoding/pem" "encoding/pem"
"fmt"
"net" "net"
"regexp" "regexp"
"sort" "sort"
@ -20,9 +20,9 @@ import (
) )
const ( const (
cnPrefix = "listener.cattle.io/cn-" cnPrefix = "listener.cattle.io/cn-"
Static = "listener.cattle.io/static" Static = "listener.cattle.io/static"
hashKey = "listener.cattle.io/hash" fingerprint = "listener.cattle.io/fingerprint"
) )
var ( var (
@ -49,16 +49,14 @@ func cns(secret *v1.Secret) (cns []string) {
return return
} }
func collectCNs(secret *v1.Secret) (domains []string, ips []net.IP, hash string, err error) { func collectCNs(secret *v1.Secret) (domains []string, ips []net.IP, err error) {
var ( var (
cns = cns(secret) cns = cns(secret)
digest = sha256.New()
) )
sort.Strings(cns) sort.Strings(cns)
for _, v := range cns { for _, v := range cns {
digest.Write([]byte(v))
ip := net.ParseIP(v) ip := net.ParseIP(v)
if ip == nil { if ip == nil {
domains = append(domains, v) domains = append(domains, v)
@ -67,40 +65,61 @@ func collectCNs(secret *v1.Secret) (domains []string, ips []net.IP, hash string,
} }
} }
hash = hex.EncodeToString(digest.Sum(nil))
return return
} }
// Merge combines the SAN lists from the target and additional Secrets, and returns a potentially modified Secret,
// along with a bool indicating if the returned Secret has been updated or not. If the two SAN lists alread matched
// and no merging was necessary, but the Secrets' certificate fingerprints differed, the second secret is returned
// and the updated bool is set to true despite neither certificate having actually been modified. This is required
// to support handling certificate renewal within the kubernetes storage provider.
func (t *TLS) Merge(target, additional *v1.Secret) (*v1.Secret, bool, error) { func (t *TLS) Merge(target, additional *v1.Secret) (*v1.Secret, bool, error) {
return t.AddCN(target, cns(additional)...) secret, updated, err := t.AddCN(target, cns(additional)...)
if !updated {
if target.Annotations[fingerprint] != additional.Annotations[fingerprint] {
secret = additional
updated = true
}
}
return secret, updated, err
} }
func (t *TLS) Refresh(secret *v1.Secret) (*v1.Secret, error) { // Renew returns a copy of the given certificate that has been re-signed
// to extend the NotAfter date. It is an error to attempt to renew
// a static (user-provided) certificate.
func (t *TLS) Renew(secret *v1.Secret) (*v1.Secret, error) {
if IsStatic(secret) {
return secret, cert.ErrStaticCert
}
cns := cns(secret) cns := cns(secret)
secret = secret.DeepCopy() secret = secret.DeepCopy()
secret.Annotations = map[string]string{} secret.Annotations = map[string]string{}
secret, _, err := t.AddCN(secret, cns...) secret, _, err := t.generateCert(secret, cns...)
return secret, err return secret, err
} }
// Filter ensures that the CNs are all valid accorting to both internal logic, and any filter callbacks.
// The returned list will contain only approved CN entries.
func (t *TLS) Filter(cn ...string) []string { func (t *TLS) Filter(cn ...string) []string {
if t.FilterCN == nil { if len(cn) == 0 || t.FilterCN == nil {
return cn return cn
} }
return t.FilterCN(cn...) return t.FilterCN(cn...)
} }
// AddCN attempts to add a list of CN strings to a given Secret, returning the potentially-modified
// Secret along with a bool indicating whether or not it has been updated. The Secret will not be changed
// if it has an attribute indicating that it is static (aka user-provided), or if no new CNs were added.
func (t *TLS) AddCN(secret *v1.Secret, cn ...string) (*v1.Secret, bool, error) { func (t *TLS) AddCN(secret *v1.Secret, cn ...string) (*v1.Secret, bool, error) {
var (
err error
)
cn = t.Filter(cn...) cn = t.Filter(cn...)
if !NeedsUpdate(0, secret, cn...) { if IsStatic(secret) || !NeedsUpdate(0, secret, cn...) {
return secret, false, nil return secret, false, nil
} }
return t.generateCert(secret, cn...)
}
func (t *TLS) generateCert(secret *v1.Secret, cn ...string) (*v1.Secret, bool, error) {
secret = secret.DeepCopy() secret = secret.DeepCopy()
if secret == nil { if secret == nil {
secret = &v1.Secret{} secret = &v1.Secret{}
@ -113,7 +132,7 @@ func (t *TLS) AddCN(secret *v1.Secret, cn ...string) (*v1.Secret, bool, error) {
return nil, false, err return nil, false, err
} }
domains, ips, hash, err := collectCNs(secret) domains, ips, err := collectCNs(secret)
if err != nil { if err != nil {
return nil, false, err return nil, false, err
} }
@ -133,7 +152,7 @@ func (t *TLS) AddCN(secret *v1.Secret, cn ...string) (*v1.Secret, bool, error) {
} }
secret.Data[v1.TLSCertKey] = certBytes secret.Data[v1.TLSCertKey] = certBytes
secret.Data[v1.TLSPrivateKeyKey] = keyBytes secret.Data[v1.TLSPrivateKeyKey] = keyBytes
secret.Annotations[hashKey] = hash secret.Annotations[fingerprint] = fmt.Sprintf("SHA1=%X", sha1.Sum(newCert.Raw))
return secret, true, nil return secret, true, nil
} }
@ -157,15 +176,21 @@ func populateCN(secret *v1.Secret, cn ...string) *v1.Secret {
return secret return secret
} }
// IsStatic returns true if the Secret has an attribute indicating that it contains
// a static (aka user-provided) certificate, which should not be modified.
func IsStatic(secret *v1.Secret) bool {
return secret.Annotations[Static] == "true"
}
// NeedsUpdate returns true if any of the CNs are not currently present on the
// secret's Certificate, as recorded in the cnPrefix annotations. It will return
// false if all requested CNs are already present, or if maxSANs is non-zero and has
// been exceeded.
func NeedsUpdate(maxSANs int, secret *v1.Secret, cn ...string) bool { func NeedsUpdate(maxSANs int, secret *v1.Secret, cn ...string) bool {
if secret == nil { if secret == nil {
return true return true
} }
if secret.Annotations[Static] == "true" {
return false
}
for _, cn := range cn { for _, cn := range cn {
if secret.Annotations[cnPrefix+cn] == "" { if secret.Annotations[cnPrefix+cn] == "" {
if maxSANs > 0 && len(cns(secret)) >= maxSANs { if maxSANs > 0 && len(cns(secret)) >= maxSANs {
@ -192,6 +217,7 @@ func getPrivateKey(secret *v1.Secret) (crypto.Signer, error) {
return NewPrivateKey() return NewPrivateKey()
} }
// Marshal returns the given cert and key as byte slices.
func Marshal(x509Cert *x509.Certificate, privateKey crypto.Signer) ([]byte, []byte, error) { func Marshal(x509Cert *x509.Certificate, privateKey crypto.Signer) ([]byte, []byte, error) {
certBlock := pem.Block{ certBlock := pem.Block{
Type: CertificateBlockType, Type: CertificateBlockType,
@ -206,6 +232,7 @@ func Marshal(x509Cert *x509.Certificate, privateKey crypto.Signer) ([]byte, []by
return pem.EncodeToMemory(&certBlock), keyBytes, nil return pem.EncodeToMemory(&certBlock), keyBytes, nil
} }
// NewPrivateKey returnes a new ECDSA key
func NewPrivateKey() (crypto.Signer, error) { func NewPrivateKey() (crypto.Signer, error) {
return ecdsa.GenerateKey(elliptic.P256(), rand.Reader) return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
} }

View File

@ -11,6 +11,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/rancher/dynamiclistener/cert"
"github.com/rancher/dynamiclistener/factory" "github.com/rancher/dynamiclistener/factory"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
@ -22,7 +23,7 @@ type TLSStorage interface {
} }
type TLSFactory interface { type TLSFactory interface {
Refresh(secret *v1.Secret) (*v1.Secret, error) Renew(secret *v1.Secret) (*v1.Secret, error)
AddCN(secret *v1.Secret, cn ...string) (*v1.Secret, bool, error) AddCN(secret *v1.Secret, cn ...string) (*v1.Secret, bool, error)
Merge(target *v1.Secret, additional *v1.Secret) (*v1.Secret, bool, error) Merge(target *v1.Secret, additional *v1.Secret) (*v1.Secret, bool, error)
Filter(cn ...string) []string Filter(cn ...string) []string
@ -152,13 +153,18 @@ type listener struct {
func (l *listener) WrapExpiration(days int) net.Listener { func (l *listener) WrapExpiration(days int) net.Listener {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
go func() { go func() {
time.Sleep(5 * time.Minute) time.Sleep(30 * time.Second)
for { for {
wait := 6 * time.Hour wait := 6 * time.Hour
if err := l.checkExpiration(days); err != nil { if err := l.checkExpiration(days); err != nil {
logrus.Errorf("failed to check and refresh dynamic cert: %v", err) logrus.Errorf("failed to check and renew dynamic cert: %v", err)
wait = 5 + time.Minute // Don't go into short retry loop if we're using a static (user-provided) cert.
// We will still check and print an error every six hours until the user updates the secret with
// a cert that is not about to expire. Hopefully this will prompt them to take action.
if err != cert.ErrStaticCert {
wait = 5 * time.Minute
}
} }
select { select {
case <-ctx.Done(): case <-ctx.Done():
@ -191,22 +197,26 @@ func (l *listener) checkExpiration(days int) error {
return err return err
} }
cert, err := tls.X509KeyPair(secret.Data[v1.TLSCertKey], secret.Data[v1.TLSPrivateKeyKey]) keyPair, err := tls.X509KeyPair(secret.Data[v1.TLSCertKey], secret.Data[v1.TLSPrivateKeyKey])
if err != nil { if err != nil {
return err return err
} }
certParsed, err := x509.ParseCertificate(cert.Certificate[0]) certParsed, err := x509.ParseCertificate(keyPair.Certificate[0])
if err != nil { if err != nil {
return err return err
} }
if time.Now().UTC().Add(time.Hour * 24 * time.Duration(days)).After(certParsed.NotAfter) { if cert.IsCertExpired(certParsed, days) {
secret, err := l.factory.Refresh(secret) secret, err := l.factory.Renew(secret)
if err != nil { if err != nil {
return err return err
} }
return l.storage.Update(secret) if err := l.storage.Update(secret); err != nil {
return err
}
// clear version to force cert reload
l.version = ""
} }
return nil return nil
@ -304,7 +314,7 @@ func (l *listener) updateCert(cn ...string) error {
return err return err
} }
if !factory.NeedsUpdate(l.maxSANs, secret, cn...) { if !factory.IsStatic(secret) && !factory.NeedsUpdate(l.maxSANs, secret, cn...) {
return nil return nil
} }
@ -324,13 +334,6 @@ func (l *listener) updateCert(cn ...string) error {
} }
// clear version to force cert reload // clear version to force cert reload
l.version = "" l.version = ""
if l.conns != nil {
l.connLock.Lock()
for _, conn := range l.conns {
_ = conn.close()
}
l.connLock.Unlock()
}
} }
return nil return nil
@ -366,6 +369,15 @@ func (l *listener) loadCert() (*tls.Certificate, error) {
return nil, err return nil, err
} }
// cert has changed, close closeWrapper wrapped connections
if l.conns != nil {
l.connLock.Lock()
for _, conn := range l.conns {
_ = conn.close()
}
l.connLock.Unlock()
}
l.cert = &cert l.cert = &cert
l.version = secret.ResourceVersion l.version = secret.ResourceVersion
return l.cert, nil return l.cert, nil

View File

@ -19,27 +19,46 @@ func LoadOrGenCA(secrets v1controller.SecretClient, namespace, name string) (*x5
return factory.LoadCA(secret.Data[v1.TLSCertKey], secret.Data[v1.TLSPrivateKeyKey]) 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) { func getSecret(secrets v1controller.SecretClient, namespace, name string) (*v1.Secret, error) {
s, err := secrets.Get(namespace, name, metav1.GetOptions{}) s, err := secrets.Get(namespace, name, metav1.GetOptions{})
if !errors.IsNotFound(err) { if !errors.IsNotFound(err) {
return s, err return s, err
} }
if err := createAndStore(secrets, namespace, name); err != nil { return createAndStore(secrets, namespace, name)
return nil, err
}
return secrets.Get(namespace, name, metav1.GetOptions{})
} }
func createAndStore(secrets v1controller.SecretClient, namespace string, name string) error { func createAndStoreClientCert(secrets v1controller.SecretClient, namespace string, name, cn string, caCert *x509.Certificate, caKey crypto.Signer) (*v1.Secret, error) {
ca, cert, err := factory.GenCA() key, err := factory.NewPrivateKey()
if err != nil { if err != nil {
return err return nil, err
} }
certPem, keyPem, err := factory.Marshal(ca, cert) cert, err := factory.NewSignedClientCert(key, caCert, caKey, cn)
if err != nil { if err != nil {
return err return nil, err
}
certPem, keyPem, err := factory.Marshal(cert, key)
if err != nil {
return nil, err
} }
secret := &v1.Secret{ secret := &v1.Secret{
@ -54,6 +73,31 @@ func createAndStore(secrets v1controller.SecretClient, namespace string, name st
Type: v1.SecretTypeTLS, Type: v1.SecretTypeTLS,
} }
secrets.Create(secret) return secrets.Create(secret)
return nil }
func createAndStore(secrets v1controller.SecretClient, namespace string, name string) (*v1.Secret, error) {
ca, cert, err := factory.GenCA()
if err != nil {
return nil, err
}
certPem, keyPem, err := factory.Marshal(ca, cert)
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)
} }

View File

@ -156,10 +156,9 @@ func (s *storage) saveInK8s(secret *v1.Secret) (*v1.Secret, error) {
if targetSecret.UID == "" { if targetSecret.UID == "" {
logrus.Infof("Creating new TLS secret for %v (count: %d): %v", targetSecret.Name, len(targetSecret.Annotations)-1, targetSecret.Annotations) logrus.Infof("Creating new TLS secret for %v (count: %d): %v", targetSecret.Name, len(targetSecret.Annotations)-1, targetSecret.Annotations)
return s.secrets.Create(targetSecret) return s.secrets.Create(targetSecret)
} else {
logrus.Infof("Updating TLS secret for %v (count: %d): %v", targetSecret.Name, len(targetSecret.Annotations)-1, targetSecret.Annotations)
return s.secrets.Update(targetSecret)
} }
logrus.Infof("Updating TLS secret for %v (count: %d): %v", targetSecret.Name, len(targetSecret.Annotations)-1, targetSecret.Annotations)
return s.secrets.Update(targetSecret)
} }
func (s *storage) Update(secret *v1.Secret) (err error) { func (s *storage) Update(secret *v1.Secret) (err error) {

View File

@ -32,13 +32,15 @@ func (m *memory) Get() (*v1.Secret, error) {
} }
func (m *memory) Update(secret *v1.Secret) error { func (m *memory) Update(secret *v1.Secret) error {
if m.storage != nil { if m.secret == nil || m.secret.ResourceVersion != secret.ResourceVersion {
if err := m.storage.Update(secret); err != nil { if m.storage != nil {
return err if err := m.storage.Update(secret); err != nil {
return err
}
} }
}
logrus.Infof("Active TLS secret %s (ver=%s) (count %d): %v", secret.Name, secret.ResourceVersion, len(secret.Annotations)-1, secret.Annotations) logrus.Infof("Active TLS secret %s (ver=%s) (count %d): %v", secret.Name, secret.ResourceVersion, len(secret.Annotations)-1, secret.Annotations)
m.secret = secret m.secret = secret
}
return nil return nil
} }

2
vendor/modules.txt vendored
View File

@ -726,7 +726,7 @@ github.com/prometheus/procfs/xfs
# github.com/rakelkar/gonetsh v0.0.0-20190930180311-e5c5ffe4bdf0 # github.com/rakelkar/gonetsh v0.0.0-20190930180311-e5c5ffe4bdf0
github.com/rakelkar/gonetsh/netroute github.com/rakelkar/gonetsh/netroute
github.com/rakelkar/gonetsh/netsh github.com/rakelkar/gonetsh/netsh
# github.com/rancher/dynamiclistener v0.2.1-0.20200418023342-52ede5ec9234 # github.com/rancher/dynamiclistener v0.2.1
github.com/rancher/dynamiclistener github.com/rancher/dynamiclistener
github.com/rancher/dynamiclistener/cert github.com/rancher/dynamiclistener/cert
github.com/rancher/dynamiclistener/factory github.com/rancher/dynamiclistener/factory