mirror of
https://github.com/rancher/dynamiclistener.git
synced 2025-09-02 13:44:26 +00:00
Compare commits
26 Commits
v0.3.5
...
renovate/r
Author | SHA1 | Date | |
---|---|---|---|
|
ae1545795b | ||
|
35b8be0eb9 | ||
|
d5cd913a6f | ||
|
6cb7335776 | ||
|
7d5f0cbf5e | ||
|
64779f3d67 | ||
|
d941584b20 | ||
|
f3fd177670 | ||
|
9e2c8269e9 | ||
|
64bcbbed34 | ||
|
ea3b7aa886 | ||
|
0a2d8dff62 | ||
|
0132d96ec2 | ||
|
06acb83e5d | ||
|
10456ff2e6 | ||
|
1c60bf414f | ||
|
3e7612c2c9 | ||
|
e6585da47a | ||
|
6cc9a670e1 | ||
|
8f13b193a1 | ||
|
02304047cf | ||
|
4c1ac9bd4b | ||
|
2b62d5cc69 | ||
|
2ac221e5d6 | ||
|
b7a028fe3f | ||
|
a150115362 |
15
.drone.yml
15
.drone.yml
@@ -1,15 +0,0 @@
|
||||
---
|
||||
|
||||
kind: pipeline
|
||||
name: fossa
|
||||
|
||||
steps:
|
||||
- name: fossa
|
||||
image: rancher/drone-fossa:latest
|
||||
settings:
|
||||
api_key:
|
||||
from_secret: FOSSA_API_KEY
|
||||
when:
|
||||
instance:
|
||||
- drone-publish.rancher.io
|
||||
|
9
.github/renovate.json
vendored
Normal file
9
.github/renovate.json
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": [
|
||||
"github>rancher/renovate-config#release"
|
||||
],
|
||||
"baseBranches": [
|
||||
"master"
|
||||
],
|
||||
"prHourlyLimit": 2
|
||||
}
|
20
.github/workflows/ci.yaml
vendored
Normal file
20
.github/workflows/ci.yaml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
name: CI
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- 'release/*'
|
||||
jobs:
|
||||
ci:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
# https://github.com/actions/checkout/releases/tag/v4.1.1
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- name: Install Go
|
||||
# https://github.com/actions/setup-go/releases/tag/v5.0.0
|
||||
uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
- run: go test -race -cover ./...
|
25
.github/workflows/release.yaml
vendored
Normal file
25
.github/workflows/release.yaml
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name : Checkout repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Create release on Github
|
||||
run: |
|
||||
if [[ "${{ github.ref_name }}" == *-rc* ]]; then
|
||||
gh --repo "${{ github.repository }}" release create ${{ github.ref_name }} --verify-tag --generate-notes --prerelease
|
||||
else
|
||||
gh --repo "${{ github.repository }}" release create ${{ github.ref_name }} --verify-tag --generate-notes
|
||||
fi
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
25
.github/workflows/renovate.yml
vendored
Normal file
25
.github/workflows/renovate.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
name: Renovate
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
logLevel:
|
||||
description: "Override default log level"
|
||||
required: false
|
||||
default: "info"
|
||||
type: string
|
||||
overrideSchedule:
|
||||
description: "Override all schedules"
|
||||
required: false
|
||||
default: "false"
|
||||
type: string
|
||||
# Run twice in the early morning (UTC) for initial and follow up steps (create pull request and merge)
|
||||
schedule:
|
||||
- cron: '30 4,6 * * *'
|
||||
|
||||
jobs:
|
||||
call-workflow:
|
||||
uses: rancher/renovate-config/.github/workflows/renovate.yml@release
|
||||
with:
|
||||
logLevel: ${{ inputs.logLevel || 'info' }}
|
||||
overrideSchedule: ${{ github.event.inputs.overrideSchedule == 'true' && '{''schedule'':null}' || '' }}
|
||||
secrets: inherit
|
@@ -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 {
|
||||
|
72
go.mod
72
go.mod
@@ -1,51 +1,65 @@
|
||||
module github.com/rancher/dynamiclistener
|
||||
|
||||
go 1.19
|
||||
go 1.23.0
|
||||
|
||||
toolchain go1.24.2
|
||||
|
||||
require (
|
||||
github.com/rancher/wrangler v0.8.9
|
||||
github.com/sirupsen/logrus v1.4.2
|
||||
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
|
||||
github.com/rancher/wrangler v1.1.2
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/stretchr/testify v1.10.0
|
||||
golang.org/x/crypto v0.41.0
|
||||
k8s.io/api v0.27.4
|
||||
k8s.io/apimachinery v0.27.4
|
||||
k8s.io/client-go v0.27.4
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.9.0 // indirect
|
||||
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
|
||||
github.com/go-logr/logr v1.2.3 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.1 // indirect
|
||||
github.com/go-openapi/swag v0.22.3 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/gnostic v0.5.7-v3refs // indirect
|
||||
github.com/google/go-cmp v0.5.8 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/googleapis/gnostic v0.3.1 // indirect
|
||||
github.com/hashicorp/golang-lru v0.5.3 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/rancher/lasso v0.0.0-20210616224652-fc3ebd901c08 // indirect
|
||||
github.com/stretchr/testify v1.8.0 // indirect
|
||||
golang.org/x/net v0.0.0-20221004154528-8021a29435af // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 // indirect
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 // indirect
|
||||
golang.org/x/sys v0.0.0-20221010170243-090e33056c14 // indirect
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
||||
golang.org/x/text v0.3.8 // indirect
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect
|
||||
google.golang.org/appengine v1.6.1 // indirect
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
github.com/prometheus/client_golang v1.14.0 // indirect
|
||||
github.com/prometheus/client_model v0.3.0 // indirect
|
||||
github.com/prometheus/common v0.37.0 // indirect
|
||||
github.com/prometheus/procfs v0.8.0 // indirect
|
||||
github.com/rancher/lasso v0.0.0-20230830164424-d684fdeb6f29 // indirect
|
||||
golang.org/x/net v0.42.0 // indirect
|
||||
golang.org/x/oauth2 v0.10.0 // indirect
|
||||
golang.org/x/sync v0.16.0 // indirect
|
||||
golang.org/x/sys v0.35.0 // indirect
|
||||
golang.org/x/term v0.34.0 // indirect
|
||||
golang.org/x/text v0.28.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/protobuf v1.31.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/klog v1.0.0 // indirect
|
||||
k8s.io/klog/v2 v2.80.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect
|
||||
k8s.io/utils v0.0.0-20221011040102-427025108f67 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v3 v3.0.1 // indirect
|
||||
sigs.k8s.io/yaml v1.2.0 // indirect
|
||||
k8s.io/klog/v2 v2.90.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect
|
||||
k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
|
||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||
)
|
||||
|
21
listener.go
21
listener.go
@@ -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"
|
||||
}
|
||||
@@ -58,6 +63,7 @@ func NewListener(l net.Listener, storage TLSStorage, caCert *x509.Certificate, c
|
||||
ExpirationDaysCheck: config.ExpirationDaysCheck,
|
||||
},
|
||||
Listener: l,
|
||||
certReady: make(chan struct{}),
|
||||
storage: &nonNil{storage: storage},
|
||||
sans: config.SANs,
|
||||
maxSANs: config.MaxSANs,
|
||||
@@ -154,6 +160,7 @@ type listener struct {
|
||||
version string
|
||||
tlsConfig *tls.Config
|
||||
cert *tls.Certificate
|
||||
certReady chan struct{}
|
||||
sans []string
|
||||
maxSANs int
|
||||
init sync.Once
|
||||
@@ -162,9 +169,12 @@ type listener struct {
|
||||
func (l *listener) WrapExpiration(days int) net.Listener {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
go func() {
|
||||
// loop on short sleeps until certificate preload completes
|
||||
for l.cert == nil {
|
||||
time.Sleep(time.Millisecond)
|
||||
|
||||
// wait for cert to be set, this will unblock when the channel is closed
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-l.certReady:
|
||||
}
|
||||
|
||||
for {
|
||||
@@ -448,7 +458,12 @@ func (l *listener) loadCert(currentConn net.Conn) (*tls.Certificate, error) {
|
||||
l.connLock.Unlock()
|
||||
}
|
||||
|
||||
// we can only close the ready channel once when the cert is first assigned
|
||||
canClose := l.cert == nil
|
||||
l.cert = &cert
|
||||
if canClose {
|
||||
close(l.certReady)
|
||||
}
|
||||
l.version = secret.ResourceVersion
|
||||
return l.cert, nil
|
||||
}
|
||||
|
@@ -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.NewListenerWithChain(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"
|
||||
)
|
||||
|
||||
@@ -25,6 +26,7 @@ func Load(ctx context.Context, secrets v1controller.SecretController, namespace,
|
||||
namespace: namespace,
|
||||
storage: backing,
|
||||
ctx: ctx,
|
||||
initSync: &sync.Once{},
|
||||
}
|
||||
storage.init(secrets)
|
||||
return storage
|
||||
@@ -36,23 +38,18 @@ func New(ctx context.Context, core CoreGetter, namespace, name string, backing d
|
||||
namespace: namespace,
|
||||
storage: backing,
|
||||
ctx: ctx,
|
||||
initSync: &sync.Once{},
|
||||
}
|
||||
|
||||
// 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 +63,8 @@ type storage struct {
|
||||
secrets v1controller.SecretController
|
||||
ctx context.Context
|
||||
tls dynamiclistener.TLSFactory
|
||||
initialized bool
|
||||
initSync *sync.Once
|
||||
}
|
||||
|
||||
func (s *storage) SetFactory(tls dynamiclistener.TLSFactory) {
|
||||
@@ -92,7 +91,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 +118,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)
|
||||
}
|
||||
@@ -149,6 +164,20 @@ func (s *storage) targetSecret() (*v1.Secret, error) {
|
||||
|
||||
func (s *storage) saveInK8s(secret *v1.Secret) (*v1.Secret, error) {
|
||||
if !s.initComplete() {
|
||||
// Start a goroutine to attempt to save the secret later, once init is complete.
|
||||
// If this was already handled by initComplete, it should be a no-op, or at worst get
|
||||
// merged with the Kubernetes secret.
|
||||
go s.initSync.Do(func() {
|
||||
if err := wait.Poll(100*time.Millisecond, 15*time.Minute, func() (bool, error) {
|
||||
if !s.initComplete() {
|
||||
return false, nil
|
||||
}
|
||||
_, err := s.saveInK8s(secret)
|
||||
return true, err
|
||||
}); err != nil {
|
||||
logrus.Errorf("Failed to save TLS secret after controller init: %v", err)
|
||||
}
|
||||
})
|
||||
return secret, nil
|
||||
}
|
||||
|
||||
@@ -234,5 +263,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