Compare commits

..

14 Commits

Author SHA1 Message Date
Max Sokolovsky
a73b7d7f4c Merge pull request #55 from genexpr/cherry-pick-filter
Add filter helper method
2022-01-07 13:23:23 -05:00
Darren Shepherd
b0dbb8fd60 Add filter helper method
(cherry picked from commit 9b1b7d3132)
2022-01-06 14:23:11 -05:00
Max Sokolovsky
7d99790dba Merge pull request #54 from genexpr/merge-cert-updates-from-master
Cherry-pick commits that allow setting certificate expiration date
2022-01-04 12:55:30 -05:00
Max Sokolovsky
2c1c2032dc Use Go 1.16 2022-01-04 11:54:07 -05:00
Max Sokolovsky
acdc51060f update config to allow for specifying experiation in days (#53)
(cherry picked from commit 148d38076d)
2022-01-04 11:53:36 -05:00
Max Sokolovsky
3bf34c8ff9 Fix listenAndServe certificate expiration by preloading certs
Signed-off-by: Brad Davidson <brad.davidson@rancher.com>
(cherry picked from commit 284cc004e8)
2022-01-04 11:53:23 -05:00
Max Sokolovsky
097ec29ed8 Add README
(cherry picked from commit bbac29e0fa)
2022-01-04 11:52:40 -05:00
Max Sokolovsky
500cf6baf3 Fix defaultNewSignedCertExpirationDays const
This a quick fix for 2644a6ed16

(cherry picked from commit f147aa4166)
2022-01-04 11:47:37 -05:00
Max Sokolovsky
ada93274e5 Allow for default expiration days to be loaded from env
(cherry picked from commit 2644a6ed16)
2022-01-04 11:42:50 -05:00
Brian Downs
2df892b5d7 Add ability to force cert regeneration (#43) (#48)
* add ability to force cert regeneration
2021-11-15 14:05:41 -07:00
Brad Davidson
cec44b5e30 Update wrangler to v0.8.3
Signed-off-by: Brad Davidson <brad.davidson@rancher.com>
2021-07-13 15:16:59 -07:00
Sjoerd Simons
8056fb92e8 Accept IPv6 address as CN names
Expand the cnRegexp to also accept ipv6 addresses such as:
  * ::1
  * 2a00:1450:400e:80e::
  * 2a00:1450:400e:80e::200e

Fixes: #37

Signed-off-by: Sjoerd Simons <sjoerd@collabora.com>
(cherry picked from commit dc7452dbb8)
2021-06-14 14:43:06 -07:00
Dan Ramich
51bda41d9c Merge pull request #34 from dramich/wrangler
Update wrangler and drop wrangler-api
2021-04-23 08:46:33 -06:00
Dan Ramich
624606ae5a Update wrangler and drop wrangler-api 2021-04-22 15:44:19 -06:00
7 changed files with 30 additions and 87 deletions

View File

@@ -1,16 +0,0 @@
package cert
import v1 "k8s.io/api/core/v1"
func IsValidTLSSecret(secret *v1.Secret) bool {
if secret == nil {
return false
}
if _, ok := secret.Data[v1.TLSCertKey]; !ok {
return false
}
if _, ok := secret.Data[v1.TLSPrivateKeyKey]; !ok {
return false
}
return true
}

View File

@@ -179,7 +179,6 @@ func (t *TLS) generateCert(secret *v1.Secret, cn ...string) (*v1.Secret, bool, e
if secret.Data == nil {
secret.Data = map[string][]byte{}
}
secret.Type = v1.SecretTypeTLS
secret.Data[v1.TLSCertKey] = certBytes
secret.Data[v1.TLSPrivateKeyKey] = keyBytes
secret.Annotations[fingerprint] = fmt.Sprintf("SHA1=%X", sha1.Sum(newCert.Raw))
@@ -209,10 +208,7 @@ func populateCN(secret *v1.Secret, cn ...string) *v1.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 {
if secret != nil && secret.Annotations != nil {
return secret.Annotations[Static] == "true"
}
return false
return secret.Annotations[Static] == "true"
}
// NeedsUpdate returns true if any of the CNs are not currently present on the

3
go.mod
View File

@@ -1,6 +1,6 @@
module github.com/rancher/dynamiclistener
go 1.12
go 1.16
require (
github.com/rancher/wrangler v0.8.9
@@ -8,5 +8,4 @@ require (
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
k8s.io/api v0.18.8
k8s.io/apimachinery v0.18.8
k8s.io/client-go v0.18.8
)

View File

@@ -408,9 +408,6 @@ func (l *listener) loadCert(currentConn net.Conn) (*tls.Certificate, error) {
if err != nil {
return nil, err
}
if !cert.IsValidTLSSecret(secret) {
return l.cert, nil
}
if l.cert != nil && l.version == secret.ResourceVersion && secret.ResourceVersion != "" {
return l.cert, nil
}

View File

@@ -64,10 +64,7 @@ func ListenAndServe(ctx context.Context, httpsPort, httpPort int, handler http.H
}
tlsServer := http.Server{
Handler: handler,
BaseContext: func(listener net.Listener) context.Context {
return ctx
},
Handler: handler,
ErrorLog: errorLog,
}
@@ -89,9 +86,6 @@ func ListenAndServe(ctx context.Context, httpsPort, httpPort int, handler http.H
Addr: fmt.Sprintf("%s:%d", opts.BindHost, httpPort),
Handler: handler,
ErrorLog: errorLog,
BaseContext: func(listener net.Listener) context.Context {
return ctx
},
}
go func() {
logrus.Infof("Listening on %s:%d", opts.BindHost, httpPort)

View File

@@ -6,7 +6,6 @@ import (
"time"
"github.com/rancher/dynamiclistener"
"github.com/rancher/dynamiclistener/cert"
"github.com/rancher/wrangler/pkg/generated/controllers/core"
v1controller "github.com/rancher/wrangler/pkg/generated/controllers/core/v1"
"github.com/rancher/wrangler/pkg/start"
@@ -14,7 +13,6 @@ import (
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/util/retry"
)
type CoreGetter func() *core.Factory
@@ -41,9 +39,10 @@ func New(ctx context.Context, core CoreGetter, namespace, name string, backing d
// lazy init
go func() {
for {
if coreFactory := core(); coreFactory != nil {
storage.init(coreFactory.Core().V1().Secret())
_ = start.All(ctx, 5, coreFactory)
core := core()
if core != nil {
storage.init(core.Core().V1().Secret())
_ = start.All(ctx, 5, core)
return
}
@@ -59,11 +58,11 @@ func New(ctx context.Context, core CoreGetter, namespace, name string, backing d
}
type storage struct {
sync.RWMutex
sync.Mutex
namespace, name string
storage dynamiclistener.TLSStorage
secrets v1controller.SecretController
secrets v1controller.SecretClient
ctx context.Context
tls dynamiclistener.TLSFactory
}
@@ -91,7 +90,7 @@ func (s *storage) init(secrets v1controller.SecretController) {
s.secrets = secrets
secret, err := s.storage.Get()
if err == nil && cert.IsValidTLSSecret(secret) {
if err == nil && secret != nil && len(secret.Data) > 0 {
// local storage had a cached secret, ensure that it exists in Kubernetes
_, err := s.secrets.Create(&v1.Secret{
ObjectMeta: metav1.ObjectMeta{
@@ -120,16 +119,13 @@ func (s *storage) init(secrets v1controller.SecretController) {
}
func (s *storage) Get() (*v1.Secret, error) {
s.RLock()
defer s.RUnlock()
s.Lock()
defer s.Unlock()
return s.storage.Get()
}
func (s *storage) targetSecret() (*v1.Secret, error) {
s.RLock()
defer s.RUnlock()
existingSecret, err := s.secrets.Get(s.namespace, s.name, metav1.GetOptions{})
if errors.IsNotFound(err) {
return &v1.Secret{
@@ -137,14 +133,13 @@ func (s *storage) targetSecret() (*v1.Secret, error) {
Name: s.name,
Namespace: s.namespace,
},
Type: v1.SecretTypeTLS,
}, nil
}
return existingSecret, err
}
func (s *storage) saveInK8s(secret *v1.Secret) (*v1.Secret, error) {
if !s.controllerHasSynced() {
if s.secrets == nil {
return secret, nil
}
@@ -157,14 +152,14 @@ func (s *storage) saveInK8s(secret *v1.Secret) (*v1.Secret, error) {
// in favor of just blindly replacing the fields on the Kubernetes secret.
if s.tls != nil {
// merge new secret with secret from backing storage, if one exists
if existing, err := s.Get(); err == nil && cert.IsValidTLSSecret(existing) {
if existing, err := s.storage.Get(); err == nil && existing != nil && len(existing.Data) > 0 {
if newSecret, updated, err := s.tls.Merge(existing, secret); err == nil && updated {
secret = newSecret
}
}
// merge new secret with existing secret from Kubernetes, if one exists
if cert.IsValidTLSSecret(targetSecret) {
if len(targetSecret.Data) > 0 {
if newSecret, updated, err := s.tls.Merge(targetSecret, secret); err != nil {
return nil, err
} else if !updated {
@@ -175,58 +170,36 @@ func (s *storage) saveInK8s(secret *v1.Secret) (*v1.Secret, error) {
}
}
// ensure that the merged secret actually contains data before overwriting the existing fields
if !cert.IsValidTLSSecret(secret) {
logrus.Warnf("Skipping save of TLS secret for %s/%s due to missing certificate data", secret.Namespace, secret.Name)
return targetSecret, nil
}
targetSecret.Annotations = secret.Annotations
targetSecret.Type = v1.SecretTypeTLS
targetSecret.Data = secret.Data
if targetSecret.UID == "" {
logrus.Infof("Creating new TLS secret for %s/%s (count: %d): %v", targetSecret.Namespace, 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)
}
logrus.Infof("Updating TLS secret for %s/%s (count: %d): %v", targetSecret.Namespace, targetSecret.Name, len(targetSecret.Annotations)-1, targetSecret.Annotations)
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) error {
// Asynchronously update the Kubernetes secret, as doing so inline may block the listener from
// accepting new connections if the apiserver becomes unavailable after the Secrets controller
// has been initialized. We're not passing around any contexts here, nor does the controller
// accept any, so there's no good way to soft-fail with a reasonable timeout.
go func() {
if err := s.update(secret); err != nil {
logrus.Errorf("Failed to save TLS secret for %s/%s: %v", secret.Namespace, secret.Name, err)
}
}()
return nil
}
func (s *storage) Update(secret *v1.Secret) (err error) {
s.Lock()
defer s.Unlock()
func (s *storage) update(secret *v1.Secret) (err error) {
err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
for i := 0; i < 3; i++ {
secret, err = s.saveInK8s(secret)
return err
})
if errors.IsConflict(err) {
continue
} else if err != nil {
return err
}
break
}
if err != nil {
return err
}
// Only hold the lock while updating underlying storage
s.Lock()
defer s.Unlock()
// update underlying storage
return s.storage.Update(secret)
}
func (s *storage) controllerHasSynced() bool {
s.RLock()
defer s.RUnlock()
if s.secrets == nil {
return false
}
return s.secrets.Informer().HasSynced()
}

View File

@@ -39,7 +39,7 @@ func (m *memory) Update(secret *v1.Secret) error {
}
}
logrus.Infof("Active TLS secret %s/%s (ver=%s) (count %d): %v", secret.Namespace, 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
}
return nil