mirror of
https://github.com/rancher/dynamiclistener.git
synced 2025-09-03 14:14:47 +00:00
Compare commits
6 Commits
v0.3.6-rc1
...
v0.3.6-rc2
Author | SHA1 | Date | |
---|---|---|---|
|
1c60bf414f | ||
|
3e7612c2c9 | ||
|
e6585da47a | ||
|
6cc9a670e1 | ||
|
8f13b193a1 | ||
|
02304047cf |
@@ -25,18 +25,28 @@ func GenCA() (*x509.Certificate, crypto.Signer, error) {
|
||||
return caCert, caKey, nil
|
||||
}
|
||||
|
||||
// Deprecated: Use LoadOrGenCAChain instead as it supports intermediate CAs
|
||||
func LoadOrGenCA() (*x509.Certificate, crypto.Signer, error) {
|
||||
cert, key, err := loadCA()
|
||||
if err == nil {
|
||||
return cert, key, nil
|
||||
}
|
||||
|
||||
cert, key, err = GenCA()
|
||||
chain, signer, err := LoadOrGenCAChain()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return chain[0], signer, err
|
||||
}
|
||||
|
||||
certBytes, keyBytes, err := Marshal(cert, key)
|
||||
func LoadOrGenCAChain() ([]*x509.Certificate, crypto.Signer, error) {
|
||||
certs, key, err := loadCA()
|
||||
if err == nil {
|
||||
return certs, key, nil
|
||||
}
|
||||
|
||||
cert, key, err := GenCA()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
certs = []*x509.Certificate{cert}
|
||||
|
||||
certBytes, keyBytes, err := MarshalChain(key, certs...)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -53,14 +63,22 @@ func LoadOrGenCA() (*x509.Certificate, crypto.Signer, error) {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return cert, key, nil
|
||||
return certs, key, nil
|
||||
}
|
||||
|
||||
func loadCA() (*x509.Certificate, crypto.Signer, error) {
|
||||
return LoadCerts("./certs/ca.pem", "./certs/ca.key")
|
||||
func loadCA() ([]*x509.Certificate, crypto.Signer, error) {
|
||||
return LoadCertsChain("./certs/ca.pem", "./certs/ca.key")
|
||||
}
|
||||
|
||||
func LoadCA(caPem, caKey []byte) (*x509.Certificate, crypto.Signer, error) {
|
||||
chain, signer, err := LoadCAChain(caPem, caKey)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return chain[0], signer, nil
|
||||
}
|
||||
|
||||
func LoadCAChain(caPem, caKey []byte) ([]*x509.Certificate, crypto.Signer, error) {
|
||||
key, err := cert.ParsePrivateKeyPEM(caKey)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -70,15 +88,24 @@ func LoadCA(caPem, caKey []byte) (*x509.Certificate, crypto.Signer, error) {
|
||||
return nil, nil, fmt.Errorf("key is not a crypto.Signer")
|
||||
}
|
||||
|
||||
cert, err := ParseCertPEM(caPem)
|
||||
certs, err := cert.ParseCertsPEM(caPem)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return cert, signer, nil
|
||||
return certs, signer, nil
|
||||
}
|
||||
|
||||
// Deprecated: Use LoadCertsChain instead as it supports intermediate CAs
|
||||
func LoadCerts(certFile, keyFile string) (*x509.Certificate, crypto.Signer, error) {
|
||||
chain, signer, err := LoadCertsChain(certFile, keyFile)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return chain[0], signer, err
|
||||
}
|
||||
|
||||
func LoadCertsChain(certFile, keyFile string) ([]*x509.Certificate, crypto.Signer, error) {
|
||||
caPem, err := ioutil.ReadFile(certFile)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -88,5 +115,5 @@ func LoadCerts(certFile, keyFile string) (*x509.Certificate, crypto.Signer, erro
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return LoadCA(caPem, caKey)
|
||||
return LoadCAChain(caPem, caKey)
|
||||
}
|
||||
|
@@ -33,7 +33,7 @@ var (
|
||||
)
|
||||
|
||||
type TLS struct {
|
||||
CACert *x509.Certificate
|
||||
CACert []*x509.Certificate
|
||||
CAKey crypto.Signer
|
||||
CN string
|
||||
Organization []string
|
||||
@@ -178,7 +178,7 @@ func (t *TLS) generateCert(secret *v1.Secret, cn ...string) (*v1.Secret, bool, e
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
keyBytes, certBytes, err := MarshalChain(privateKey, newCert, t.CACert)
|
||||
keyBytes, certBytes, err := MarshalChain(privateKey, append([]*x509.Certificate{newCert}, t.CACert...)...)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
@@ -226,14 +226,16 @@ func (t *TLS) Verify(secret *v1.Secret) error {
|
||||
x509.ExtKeyUsageAny,
|
||||
},
|
||||
}
|
||||
verifyOpts.Roots.AddCert(t.CACert)
|
||||
for _, c := range t.CACert {
|
||||
verifyOpts.Roots.AddCert(c)
|
||||
}
|
||||
|
||||
_, err = certificates[0].Verify(verifyOpts)
|
||||
return err
|
||||
}
|
||||
|
||||
func (t *TLS) newCert(domains []string, ips []net.IP, privateKey crypto.Signer) (*x509.Certificate, error) {
|
||||
return NewSignedCert(privateKey, t.CACert, t.CAKey, t.CN, t.Organization, domains, ips)
|
||||
return NewSignedCert(privateKey, t.CACert[0], t.CAKey, t.CN, t.Organization, domains, ips)
|
||||
}
|
||||
|
||||
func populateCN(secret *v1.Secret, cn ...string) *v1.Secret {
|
||||
|
2
go.mod
2
go.mod
@@ -4,7 +4,7 @@ go 1.19
|
||||
|
||||
require (
|
||||
github.com/rancher/wrangler v1.1.0
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/stretchr/testify v1.8.1
|
||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd
|
||||
k8s.io/api v0.25.4
|
||||
|
7
go.sum
7
go.sum
@@ -236,8 +236,8 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@@ -249,6 +249,7 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
@@ -366,7 +367,6 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -391,6 +391,7 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20221010170243-090e33056c14 h1:k5II8e6QD8mITdi+okbbmR/cIyEbeXLBhy5Ha4nevyc=
|
||||
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
|
@@ -34,7 +34,12 @@ type SetFactory interface {
|
||||
SetFactory(tls TLSFactory)
|
||||
}
|
||||
|
||||
// Deprecated: Use NewListenerWithChain instead as it supports intermediate CAs
|
||||
func NewListener(l net.Listener, storage TLSStorage, caCert *x509.Certificate, caKey crypto.Signer, config Config) (net.Listener, http.Handler, error) {
|
||||
return NewListenerWithChain(l, storage, []*x509.Certificate{caCert}, caKey, config)
|
||||
}
|
||||
|
||||
func NewListenerWithChain(l net.Listener, storage TLSStorage, caCert []*x509.Certificate, caKey crypto.Signer, config Config) (net.Listener, http.Handler, error) {
|
||||
if config.CN == "" {
|
||||
config.CN = "dynamic"
|
||||
}
|
||||
|
@@ -21,6 +21,8 @@ import (
|
||||
)
|
||||
|
||||
type ListenOpts struct {
|
||||
CAChain []*x509.Certificate
|
||||
// Deprecated: Use CAChain instead
|
||||
CA *x509.Certificate
|
||||
CAKey crypto.Signer
|
||||
Storage dynamiclistener.TLSStorage
|
||||
@@ -132,7 +134,7 @@ func getTLSListener(ctx context.Context, tcp net.Listener, handler http.Handler,
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
listener, dynHandler, err := dynamiclistener.NewListener(tcp, storage, caCert, caKey, opts.TLSListenerConfig)
|
||||
listener, dynHandler, err := dynamiclistener.NewListener2(tcp, storage, caCert, caKey, opts.TLSListenerConfig)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -140,13 +142,17 @@ func getTLSListener(ctx context.Context, tcp net.Listener, handler http.Handler,
|
||||
return listener, wrapHandler(dynHandler, handler), nil
|
||||
}
|
||||
|
||||
func getCA(opts ListenOpts) (*x509.Certificate, crypto.Signer, error) {
|
||||
if opts.CA != nil && opts.CAKey != nil {
|
||||
return opts.CA, opts.CAKey, nil
|
||||
func getCA(opts ListenOpts) ([]*x509.Certificate, crypto.Signer, error) {
|
||||
if opts.CAKey != nil {
|
||||
if opts.CAChain != nil {
|
||||
return opts.CAChain, opts.CAKey, nil
|
||||
} else if opts.CA != nil {
|
||||
return []*x509.Certificate{opts.CA}, opts.CAKey, nil
|
||||
}
|
||||
}
|
||||
|
||||
if opts.Secrets == nil {
|
||||
return factory.LoadOrGenCA()
|
||||
return factory.LoadOrGenCAChain()
|
||||
}
|
||||
|
||||
if opts.CAName == "" {
|
||||
@@ -161,7 +167,7 @@ func getCA(opts ListenOpts) (*x509.Certificate, crypto.Signer, error) {
|
||||
opts.CANamespace = "kube-system"
|
||||
}
|
||||
|
||||
return kubernetes.LoadOrGenCA(opts.Secrets, opts.CANamespace, opts.CAName)
|
||||
return kubernetes.LoadOrGenCAChain(opts.Secrets, opts.CANamespace, opts.CAName)
|
||||
}
|
||||
|
||||
func newStorage(ctx context.Context, opts ListenOpts) dynamiclistener.TLSStorage {
|
||||
|
@@ -11,12 +11,21 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// Deprecated: Use LoadOrGenCAChain instead as it supports intermediate CAs
|
||||
func LoadOrGenCA(secrets v1controller.SecretClient, namespace, name string) (*x509.Certificate, crypto.Signer, error) {
|
||||
chain, signer, err := LoadOrGenCAChain(secrets, namespace, name)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return chain[0], signer, err
|
||||
}
|
||||
|
||||
func LoadOrGenCAChain(secrets v1controller.SecretClient, namespace, name string) ([]*x509.Certificate, crypto.Signer, error) {
|
||||
secret, err := getSecret(secrets, namespace, name)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return factory.LoadCA(secret.Data[v1.TLSCertKey], secret.Data[v1.TLSPrivateKeyKey])
|
||||
return factory.LoadCAChain(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) {
|
||||
|
@@ -14,6 +14,7 @@ import (
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/util/retry"
|
||||
)
|
||||
|
||||
@@ -40,19 +41,13 @@ func New(ctx context.Context, core CoreGetter, namespace, name string, backing d
|
||||
|
||||
// lazy init
|
||||
go func() {
|
||||
for {
|
||||
wait.PollImmediateUntilWithContext(ctx, time.Second, func(cxt context.Context) (bool, error) {
|
||||
if coreFactory := core(); coreFactory != nil {
|
||||
storage.init(coreFactory.Core().V1().Secret())
|
||||
_ = start.All(ctx, 5, coreFactory)
|
||||
return
|
||||
return true, start.All(ctx, 5, coreFactory)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-time.After(time.Second):
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
}()
|
||||
|
||||
return storage
|
||||
@@ -66,6 +61,7 @@ type storage struct {
|
||||
secrets v1controller.SecretController
|
||||
ctx context.Context
|
||||
tls dynamiclistener.TLSFactory
|
||||
initialized bool
|
||||
}
|
||||
|
||||
func (s *storage) SetFactory(tls dynamiclistener.TLSFactory) {
|
||||
@@ -92,7 +88,17 @@ func (s *storage) init(secrets v1controller.SecretController) {
|
||||
})
|
||||
s.secrets = secrets
|
||||
|
||||
secret, err := s.storage.Get()
|
||||
// Asynchronously sync the backing storage to 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 s.syncStorage()
|
||||
}
|
||||
|
||||
func (s *storage) syncStorage() {
|
||||
var updateStorage bool
|
||||
secret, err := s.Get()
|
||||
if err == nil && cert.IsValidTLSSecret(secret) {
|
||||
// local storage had a cached secret, ensure that it exists in Kubernetes
|
||||
_, err := s.secrets.Create(&v1.Secret{
|
||||
@@ -109,14 +115,20 @@ func (s *storage) init(secrets v1controller.SecretController) {
|
||||
}
|
||||
} else {
|
||||
// local storage was empty, try to populate it
|
||||
secret, err := s.secrets.Get(s.namespace, s.name, metav1.GetOptions{})
|
||||
secret, err = s.secrets.Get(s.namespace, s.name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
if !errors.IsNotFound(err) {
|
||||
logrus.Warnf("Failed to init Kubernetes secret: %v", err)
|
||||
}
|
||||
return
|
||||
} else {
|
||||
updateStorage = true
|
||||
}
|
||||
}
|
||||
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
s.initialized = true
|
||||
if updateStorage {
|
||||
if err := s.storage.Update(secret); err != nil {
|
||||
logrus.Warnf("Failed to init backing storage secret: %v", err)
|
||||
}
|
||||
@@ -234,5 +246,5 @@ func (s *storage) update(secret *v1.Secret) (err error) {
|
||||
func (s *storage) initComplete() bool {
|
||||
s.RLock()
|
||||
defer s.RUnlock()
|
||||
return s.secrets != nil
|
||||
return s.initialized
|
||||
}
|
||||
|
Reference in New Issue
Block a user