mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-13 18:43:34 +00:00
build: move e2e dependencies into e2e/go.mod
Several packages are only used while running the e2e suite. These packages are less important to update, as the they can not influence the final executable that is part of the Ceph-CSI container-image. By moving these dependencies out of the main Ceph-CSI go.mod, it is easier to identify if a reported CVE affects Ceph-CSI, or only the testing (like most of the Kubernetes CVEs). Signed-off-by: Niels de Vos <ndevos@ibm.com>
This commit is contained in:
committed by
mergify[bot]
parent
15da101b1b
commit
bec6090996
59
e2e/vendor/k8s.io/apiserver/pkg/server/dynamiccertificates/cert_key.go
generated
vendored
Normal file
59
e2e/vendor/k8s.io/apiserver/pkg/server/dynamiccertificates/cert_key.go
generated
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package dynamiccertificates
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
)
|
||||
|
||||
// certKeyContent holds the content for the cert and key
|
||||
type certKeyContent struct {
|
||||
cert []byte
|
||||
key []byte
|
||||
}
|
||||
|
||||
func (c *certKeyContent) Equal(rhs *certKeyContent) bool {
|
||||
if c == nil || rhs == nil {
|
||||
return c == rhs
|
||||
}
|
||||
|
||||
return bytes.Equal(c.key, rhs.key) && bytes.Equal(c.cert, rhs.cert)
|
||||
}
|
||||
|
||||
// sniCertKeyContent holds the content for the cert and key as well as any explicit names
|
||||
type sniCertKeyContent struct {
|
||||
certKeyContent
|
||||
sniNames []string
|
||||
}
|
||||
|
||||
func (c *sniCertKeyContent) Equal(rhs *sniCertKeyContent) bool {
|
||||
if c == nil || rhs == nil {
|
||||
return c == rhs
|
||||
}
|
||||
|
||||
if len(c.sniNames) != len(rhs.sniNames) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := range c.sniNames {
|
||||
if c.sniNames[i] != rhs.sniNames[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return c.certKeyContent.Equal(&rhs.certKeyContent)
|
||||
}
|
69
e2e/vendor/k8s.io/apiserver/pkg/server/dynamiccertificates/client_ca.go
generated
vendored
Normal file
69
e2e/vendor/k8s.io/apiserver/pkg/server/dynamiccertificates/client_ca.go
generated
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package dynamiccertificates
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
)
|
||||
|
||||
// dynamicCertificateContent holds the content that overrides the baseTLSConfig
|
||||
type dynamicCertificateContent struct {
|
||||
// clientCA holds the content for the clientCA bundle
|
||||
clientCA caBundleContent
|
||||
servingCert certKeyContent
|
||||
sniCerts []sniCertKeyContent
|
||||
}
|
||||
|
||||
// caBundleContent holds the content for the clientCA bundle. Wrapping the bytes makes the Equals work nicely with the
|
||||
// method receiver.
|
||||
type caBundleContent struct {
|
||||
caBundle []byte
|
||||
}
|
||||
|
||||
func (c *dynamicCertificateContent) Equal(rhs *dynamicCertificateContent) bool {
|
||||
if c == nil || rhs == nil {
|
||||
return c == rhs
|
||||
}
|
||||
|
||||
if !c.clientCA.Equal(&rhs.clientCA) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !c.servingCert.Equal(&rhs.servingCert) {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(c.sniCerts) != len(rhs.sniCerts) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := range c.sniCerts {
|
||||
if !c.sniCerts[i].Equal(&rhs.sniCerts[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *caBundleContent) Equal(rhs *caBundleContent) bool {
|
||||
if c == nil || rhs == nil {
|
||||
return c == rhs
|
||||
}
|
||||
|
||||
return bytes.Equal(c.caBundle, rhs.caBundle)
|
||||
}
|
277
e2e/vendor/k8s.io/apiserver/pkg/server/dynamiccertificates/configmap_cafile_content.go
generated
vendored
Normal file
277
e2e/vendor/k8s.io/apiserver/pkg/server/dynamiccertificates/configmap_cafile_content.go
generated
vendored
Normal file
@ -0,0 +1,277 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package dynamiccertificates
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
corev1informers "k8s.io/client-go/informers/core/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
corev1listers "k8s.io/client-go/listers/core/v1"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
// ConfigMapCAController provies a CAContentProvider that can dynamically react to configmap changes
|
||||
// It also fulfills the authenticator interface to provide verifyoptions
|
||||
type ConfigMapCAController struct {
|
||||
name string
|
||||
|
||||
configmapLister corev1listers.ConfigMapLister
|
||||
configmapNamespace string
|
||||
configmapName string
|
||||
configmapKey string
|
||||
// configMapInformer is tracked so that we can start these on Run
|
||||
configMapInformer cache.SharedIndexInformer
|
||||
|
||||
// caBundle is a caBundleAndVerifier that contains the last read, non-zero length content of the file
|
||||
caBundle atomic.Value
|
||||
|
||||
listeners []Listener
|
||||
|
||||
queue workqueue.TypedRateLimitingInterface[string]
|
||||
// preRunCaches are the caches to sync before starting the work of this control loop
|
||||
preRunCaches []cache.InformerSynced
|
||||
}
|
||||
|
||||
var _ CAContentProvider = &ConfigMapCAController{}
|
||||
var _ ControllerRunner = &ConfigMapCAController{}
|
||||
|
||||
// NewDynamicCAFromConfigMapController returns a CAContentProvider based on a configmap that automatically reloads content.
|
||||
// It is near-realtime via an informer.
|
||||
func NewDynamicCAFromConfigMapController(purpose, namespace, name, key string, kubeClient kubernetes.Interface) (*ConfigMapCAController, error) {
|
||||
if len(purpose) == 0 {
|
||||
return nil, fmt.Errorf("missing purpose for ca bundle")
|
||||
}
|
||||
if len(namespace) == 0 {
|
||||
return nil, fmt.Errorf("missing namespace for ca bundle")
|
||||
}
|
||||
if len(name) == 0 {
|
||||
return nil, fmt.Errorf("missing name for ca bundle")
|
||||
}
|
||||
if len(key) == 0 {
|
||||
return nil, fmt.Errorf("missing key for ca bundle")
|
||||
}
|
||||
caContentName := fmt.Sprintf("%s::%s::%s::%s", purpose, namespace, name, key)
|
||||
|
||||
// we construct our own informer because we need such a small subset of the information available. Just one namespace.
|
||||
uncastConfigmapInformer := corev1informers.NewFilteredConfigMapInformer(kubeClient, namespace, 12*time.Hour, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, func(listOptions *v1.ListOptions) {
|
||||
listOptions.FieldSelector = fields.OneTermEqualSelector("metadata.name", name).String()
|
||||
})
|
||||
|
||||
configmapLister := corev1listers.NewConfigMapLister(uncastConfigmapInformer.GetIndexer())
|
||||
|
||||
c := &ConfigMapCAController{
|
||||
name: caContentName,
|
||||
configmapNamespace: namespace,
|
||||
configmapName: name,
|
||||
configmapKey: key,
|
||||
configmapLister: configmapLister,
|
||||
configMapInformer: uncastConfigmapInformer,
|
||||
|
||||
queue: workqueue.NewTypedRateLimitingQueueWithConfig(
|
||||
workqueue.DefaultTypedControllerRateLimiter[string](),
|
||||
workqueue.TypedRateLimitingQueueConfig[string]{Name: fmt.Sprintf("DynamicConfigMapCABundle-%s", purpose)},
|
||||
),
|
||||
preRunCaches: []cache.InformerSynced{uncastConfigmapInformer.HasSynced},
|
||||
}
|
||||
|
||||
uncastConfigmapInformer.AddEventHandler(cache.FilteringResourceEventHandler{
|
||||
FilterFunc: func(obj interface{}) bool {
|
||||
if cast, ok := obj.(*corev1.ConfigMap); ok {
|
||||
return cast.Name == c.configmapName && cast.Namespace == c.configmapNamespace
|
||||
}
|
||||
if tombstone, ok := obj.(cache.DeletedFinalStateUnknown); ok {
|
||||
if cast, ok := tombstone.Obj.(*corev1.ConfigMap); ok {
|
||||
return cast.Name == c.configmapName && cast.Namespace == c.configmapNamespace
|
||||
}
|
||||
}
|
||||
return true // always return true just in case. The checks are fairly cheap
|
||||
},
|
||||
Handler: cache.ResourceEventHandlerFuncs{
|
||||
// we have a filter, so any time we're called, we may as well queue. We only ever check one configmap
|
||||
// so we don't have to be choosy about our key.
|
||||
AddFunc: func(obj interface{}) {
|
||||
c.queue.Add(c.keyFn())
|
||||
},
|
||||
UpdateFunc: func(oldObj, newObj interface{}) {
|
||||
c.queue.Add(c.keyFn())
|
||||
},
|
||||
DeleteFunc: func(obj interface{}) {
|
||||
c.queue.Add(c.keyFn())
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *ConfigMapCAController) keyFn() string {
|
||||
// this format matches DeletionHandlingMetaNamespaceKeyFunc for our single key
|
||||
return c.configmapNamespace + "/" + c.configmapName
|
||||
}
|
||||
|
||||
// AddListener adds a listener to be notified when the CA content changes.
|
||||
func (c *ConfigMapCAController) AddListener(listener Listener) {
|
||||
c.listeners = append(c.listeners, listener)
|
||||
}
|
||||
|
||||
// loadCABundle determines the next set of content for the file.
|
||||
func (c *ConfigMapCAController) loadCABundle() error {
|
||||
configMap, err := c.configmapLister.ConfigMaps(c.configmapNamespace).Get(c.configmapName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
caBundle := configMap.Data[c.configmapKey]
|
||||
if len(caBundle) == 0 {
|
||||
return fmt.Errorf("missing content for CA bundle %q", c.Name())
|
||||
}
|
||||
|
||||
// check to see if we have a change. If the values are the same, do nothing.
|
||||
if !c.hasCAChanged([]byte(caBundle)) {
|
||||
return nil
|
||||
}
|
||||
|
||||
caBundleAndVerifier, err := newCABundleAndVerifier(c.Name(), []byte(caBundle))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.caBundle.Store(caBundleAndVerifier)
|
||||
|
||||
for _, listener := range c.listeners {
|
||||
listener.Enqueue()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// hasCAChanged returns true if the caBundle is different than the current.
|
||||
func (c *ConfigMapCAController) hasCAChanged(caBundle []byte) bool {
|
||||
uncastExisting := c.caBundle.Load()
|
||||
if uncastExisting == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
// check to see if we have a change. If the values are the same, do nothing.
|
||||
existing, ok := uncastExisting.(*caBundleAndVerifier)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
if !bytes.Equal(existing.caBundle, caBundle) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// RunOnce runs a single sync loop
|
||||
func (c *ConfigMapCAController) RunOnce(ctx context.Context) error {
|
||||
// Ignore the error when running once because when using a dynamically loaded ca file, because we think it's better to have nothing for
|
||||
// a brief time than completely crash. If crashing is necessary, higher order logic like a healthcheck and cause failures.
|
||||
_ = c.loadCABundle()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run starts the kube-apiserver and blocks until stopCh is closed.
|
||||
func (c *ConfigMapCAController) Run(ctx context.Context, workers int) {
|
||||
defer utilruntime.HandleCrash()
|
||||
defer c.queue.ShutDown()
|
||||
|
||||
klog.InfoS("Starting controller", "name", c.name)
|
||||
defer klog.InfoS("Shutting down controller", "name", c.name)
|
||||
|
||||
// we have a personal informer that is narrowly scoped, start it.
|
||||
go c.configMapInformer.Run(ctx.Done())
|
||||
|
||||
// wait for your secondary caches to fill before starting your work
|
||||
if !cache.WaitForNamedCacheSync(c.name, ctx.Done(), c.preRunCaches...) {
|
||||
return
|
||||
}
|
||||
|
||||
// doesn't matter what workers say, only start one.
|
||||
go wait.Until(c.runWorker, time.Second, ctx.Done())
|
||||
|
||||
// start timer that rechecks every minute, just in case. this also serves to prime the controller quickly.
|
||||
go wait.PollImmediateUntil(FileRefreshDuration, func() (bool, error) {
|
||||
c.queue.Add(workItemKey)
|
||||
return false, nil
|
||||
}, ctx.Done())
|
||||
|
||||
<-ctx.Done()
|
||||
}
|
||||
|
||||
func (c *ConfigMapCAController) runWorker() {
|
||||
for c.processNextWorkItem() {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ConfigMapCAController) processNextWorkItem() bool {
|
||||
dsKey, quit := c.queue.Get()
|
||||
if quit {
|
||||
return false
|
||||
}
|
||||
defer c.queue.Done(dsKey)
|
||||
|
||||
err := c.loadCABundle()
|
||||
if err == nil {
|
||||
c.queue.Forget(dsKey)
|
||||
return true
|
||||
}
|
||||
|
||||
utilruntime.HandleError(fmt.Errorf("%v failed with : %v", dsKey, err))
|
||||
c.queue.AddRateLimited(dsKey)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Name is just an identifier
|
||||
func (c *ConfigMapCAController) Name() string {
|
||||
return c.name
|
||||
}
|
||||
|
||||
// CurrentCABundleContent provides ca bundle byte content
|
||||
func (c *ConfigMapCAController) CurrentCABundleContent() []byte {
|
||||
uncastObj := c.caBundle.Load()
|
||||
if uncastObj == nil {
|
||||
return nil // this can happen if we've been unable load data from the apiserver for some reason
|
||||
}
|
||||
|
||||
return c.caBundle.Load().(*caBundleAndVerifier).caBundle
|
||||
}
|
||||
|
||||
// VerifyOptions provides verifyoptions compatible with authenticators
|
||||
func (c *ConfigMapCAController) VerifyOptions() (x509.VerifyOptions, bool) {
|
||||
uncastObj := c.caBundle.Load()
|
||||
if uncastObj == nil {
|
||||
// This can happen if we've been unable load data from the apiserver for some reason.
|
||||
// In this case, we should not accept any connections on the basis of this ca bundle.
|
||||
return x509.VerifyOptions{}, false
|
||||
}
|
||||
|
||||
return uncastObj.(*caBundleAndVerifier).verifyOptions, true
|
||||
}
|
294
e2e/vendor/k8s.io/apiserver/pkg/server/dynamiccertificates/dynamic_cafile_content.go
generated
vendored
Normal file
294
e2e/vendor/k8s.io/apiserver/pkg/server/dynamiccertificates/dynamic_cafile_content.go
generated
vendored
Normal file
@ -0,0 +1,294 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package dynamiccertificates
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"k8s.io/client-go/util/cert"
|
||||
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
// FileRefreshDuration is exposed so that integration tests can crank up the reload speed.
|
||||
var FileRefreshDuration = 1 * time.Minute
|
||||
|
||||
// ControllerRunner is a generic interface for starting a controller
|
||||
type ControllerRunner interface {
|
||||
// RunOnce runs the sync loop a single time. This useful for synchronous priming
|
||||
RunOnce(ctx context.Context) error
|
||||
|
||||
// Run should be called a go .Run
|
||||
Run(ctx context.Context, workers int)
|
||||
}
|
||||
|
||||
// DynamicFileCAContent provides a CAContentProvider that can dynamically react to new file content
|
||||
// It also fulfills the authenticator interface to provide verifyoptions
|
||||
type DynamicFileCAContent struct {
|
||||
name string
|
||||
|
||||
// filename is the name the file to read.
|
||||
filename string
|
||||
|
||||
// caBundle is a caBundleAndVerifier that contains the last read, non-zero length content of the file
|
||||
caBundle atomic.Value
|
||||
|
||||
listeners []Listener
|
||||
|
||||
// queue only ever has one item, but it has nice error handling backoff/retry semantics
|
||||
queue workqueue.TypedRateLimitingInterface[string]
|
||||
}
|
||||
|
||||
var _ Notifier = &DynamicFileCAContent{}
|
||||
var _ CAContentProvider = &DynamicFileCAContent{}
|
||||
var _ ControllerRunner = &DynamicFileCAContent{}
|
||||
|
||||
type caBundleAndVerifier struct {
|
||||
caBundle []byte
|
||||
verifyOptions x509.VerifyOptions
|
||||
}
|
||||
|
||||
// NewDynamicCAContentFromFile returns a CAContentProvider based on a filename that automatically reloads content
|
||||
func NewDynamicCAContentFromFile(purpose, filename string) (*DynamicFileCAContent, error) {
|
||||
if len(filename) == 0 {
|
||||
return nil, fmt.Errorf("missing filename for ca bundle")
|
||||
}
|
||||
name := fmt.Sprintf("%s::%s", purpose, filename)
|
||||
|
||||
ret := &DynamicFileCAContent{
|
||||
name: name,
|
||||
filename: filename,
|
||||
queue: workqueue.NewTypedRateLimitingQueueWithConfig(
|
||||
workqueue.DefaultTypedControllerRateLimiter[string](),
|
||||
workqueue.TypedRateLimitingQueueConfig[string]{Name: fmt.Sprintf("DynamicCABundle-%s", purpose)},
|
||||
),
|
||||
}
|
||||
if err := ret.loadCABundle(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// AddListener adds a listener to be notified when the CA content changes.
|
||||
func (c *DynamicFileCAContent) AddListener(listener Listener) {
|
||||
c.listeners = append(c.listeners, listener)
|
||||
}
|
||||
|
||||
// loadCABundle determines the next set of content for the file.
|
||||
func (c *DynamicFileCAContent) loadCABundle() error {
|
||||
caBundle, err := os.ReadFile(c.filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(caBundle) == 0 {
|
||||
return fmt.Errorf("missing content for CA bundle %q", c.Name())
|
||||
}
|
||||
|
||||
// check to see if we have a change. If the values are the same, do nothing.
|
||||
if !c.hasCAChanged(caBundle) {
|
||||
return nil
|
||||
}
|
||||
|
||||
caBundleAndVerifier, err := newCABundleAndVerifier(c.Name(), caBundle)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.caBundle.Store(caBundleAndVerifier)
|
||||
klog.V(2).InfoS("Loaded a new CA Bundle and Verifier", "name", c.Name())
|
||||
|
||||
for _, listener := range c.listeners {
|
||||
listener.Enqueue()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// hasCAChanged returns true if the caBundle is different than the current.
|
||||
func (c *DynamicFileCAContent) hasCAChanged(caBundle []byte) bool {
|
||||
uncastExisting := c.caBundle.Load()
|
||||
if uncastExisting == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
// check to see if we have a change. If the values are the same, do nothing.
|
||||
existing, ok := uncastExisting.(*caBundleAndVerifier)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
if !bytes.Equal(existing.caBundle, caBundle) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// RunOnce runs a single sync loop
|
||||
func (c *DynamicFileCAContent) RunOnce(ctx context.Context) error {
|
||||
return c.loadCABundle()
|
||||
}
|
||||
|
||||
// Run starts the controller and blocks until stopCh is closed.
|
||||
func (c *DynamicFileCAContent) Run(ctx context.Context, workers int) {
|
||||
defer utilruntime.HandleCrash()
|
||||
defer c.queue.ShutDown()
|
||||
|
||||
klog.InfoS("Starting controller", "name", c.name)
|
||||
defer klog.InfoS("Shutting down controller", "name", c.name)
|
||||
|
||||
// doesn't matter what workers say, only start one.
|
||||
go wait.Until(c.runWorker, time.Second, ctx.Done())
|
||||
|
||||
// start the loop that watches the CA file until stopCh is closed.
|
||||
go wait.Until(func() {
|
||||
if err := c.watchCAFile(ctx.Done()); err != nil {
|
||||
klog.ErrorS(err, "Failed to watch CA file, will retry later")
|
||||
}
|
||||
}, time.Minute, ctx.Done())
|
||||
|
||||
<-ctx.Done()
|
||||
}
|
||||
|
||||
func (c *DynamicFileCAContent) watchCAFile(stopCh <-chan struct{}) error {
|
||||
// Trigger a check here to ensure the content will be checked periodically even if the following watch fails.
|
||||
c.queue.Add(workItemKey)
|
||||
|
||||
w, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating fsnotify watcher: %v", err)
|
||||
}
|
||||
defer w.Close()
|
||||
|
||||
if err = w.Add(c.filename); err != nil {
|
||||
return fmt.Errorf("error adding watch for file %s: %v", c.filename, err)
|
||||
}
|
||||
// Trigger a check in case the file is updated before the watch starts.
|
||||
c.queue.Add(workItemKey)
|
||||
|
||||
for {
|
||||
select {
|
||||
case e := <-w.Events:
|
||||
if err := c.handleWatchEvent(e, w); err != nil {
|
||||
return err
|
||||
}
|
||||
case err := <-w.Errors:
|
||||
return fmt.Errorf("received fsnotify error: %v", err)
|
||||
case <-stopCh:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handleWatchEvent triggers reloading the CA file, and restarts a new watch if it's a Remove or Rename event.
|
||||
func (c *DynamicFileCAContent) handleWatchEvent(e fsnotify.Event, w *fsnotify.Watcher) error {
|
||||
// This should be executed after restarting the watch (if applicable) to ensure no file event will be missing.
|
||||
defer c.queue.Add(workItemKey)
|
||||
if !e.Has(fsnotify.Remove) && !e.Has(fsnotify.Rename) {
|
||||
return nil
|
||||
}
|
||||
if err := w.Remove(c.filename); err != nil && !errors.Is(err, fsnotify.ErrNonExistentWatch) {
|
||||
klog.InfoS("Failed to remove file watch, it may have been deleted", "file", c.filename, "err", err)
|
||||
}
|
||||
if err := w.Add(c.filename); err != nil {
|
||||
return fmt.Errorf("error adding watch for file %s: %v", c.filename, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *DynamicFileCAContent) runWorker() {
|
||||
for c.processNextWorkItem() {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *DynamicFileCAContent) processNextWorkItem() bool {
|
||||
dsKey, quit := c.queue.Get()
|
||||
if quit {
|
||||
return false
|
||||
}
|
||||
defer c.queue.Done(dsKey)
|
||||
|
||||
err := c.loadCABundle()
|
||||
if err == nil {
|
||||
c.queue.Forget(dsKey)
|
||||
return true
|
||||
}
|
||||
|
||||
utilruntime.HandleError(fmt.Errorf("%v failed with : %v", dsKey, err))
|
||||
c.queue.AddRateLimited(dsKey)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Name is just an identifier
|
||||
func (c *DynamicFileCAContent) Name() string {
|
||||
return c.name
|
||||
}
|
||||
|
||||
// CurrentCABundleContent provides ca bundle byte content
|
||||
func (c *DynamicFileCAContent) CurrentCABundleContent() (cabundle []byte) {
|
||||
return c.caBundle.Load().(*caBundleAndVerifier).caBundle
|
||||
}
|
||||
|
||||
// VerifyOptions provides verifyoptions compatible with authenticators
|
||||
func (c *DynamicFileCAContent) VerifyOptions() (x509.VerifyOptions, bool) {
|
||||
uncastObj := c.caBundle.Load()
|
||||
if uncastObj == nil {
|
||||
return x509.VerifyOptions{}, false
|
||||
}
|
||||
|
||||
return uncastObj.(*caBundleAndVerifier).verifyOptions, true
|
||||
}
|
||||
|
||||
// newVerifyOptions creates a new verification func from a file. It reads the content and then fails.
|
||||
// It will return a nil function if you pass an empty CA file.
|
||||
func newCABundleAndVerifier(name string, caBundle []byte) (*caBundleAndVerifier, error) {
|
||||
if len(caBundle) == 0 {
|
||||
return nil, fmt.Errorf("missing content for CA bundle %q", name)
|
||||
}
|
||||
|
||||
// Wrap with an x509 verifier
|
||||
var err error
|
||||
verifyOptions := defaultVerifyOptions()
|
||||
verifyOptions.Roots, err = cert.NewPoolFromBytes(caBundle)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error loading CA bundle for %q: %v", name, err)
|
||||
}
|
||||
|
||||
return &caBundleAndVerifier{
|
||||
caBundle: caBundle,
|
||||
verifyOptions: verifyOptions,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// defaultVerifyOptions returns VerifyOptions that use the system root certificates, current time,
|
||||
// and requires certificates to be valid for client auth (x509.ExtKeyUsageClientAuth)
|
||||
func defaultVerifyOptions() x509.VerifyOptions {
|
||||
return x509.VerifyOptions{
|
||||
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
||||
}
|
||||
}
|
236
e2e/vendor/k8s.io/apiserver/pkg/server/dynamiccertificates/dynamic_serving_content.go
generated
vendored
Normal file
236
e2e/vendor/k8s.io/apiserver/pkg/server/dynamiccertificates/dynamic_serving_content.go
generated
vendored
Normal file
@ -0,0 +1,236 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package dynamiccertificates
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"os"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
// DynamicCertKeyPairContent provides a CertKeyContentProvider that can dynamically react to new file content
|
||||
type DynamicCertKeyPairContent struct {
|
||||
name string
|
||||
|
||||
// certFile is the name of the certificate file to read.
|
||||
certFile string
|
||||
// keyFile is the name of the key file to read.
|
||||
keyFile string
|
||||
|
||||
// certKeyPair is a certKeyContent that contains the last read, non-zero length content of the key and cert
|
||||
certKeyPair atomic.Value
|
||||
|
||||
listeners []Listener
|
||||
|
||||
// queue only ever has one item, but it has nice error handling backoff/retry semantics
|
||||
queue workqueue.TypedRateLimitingInterface[string]
|
||||
}
|
||||
|
||||
var _ CertKeyContentProvider = &DynamicCertKeyPairContent{}
|
||||
var _ ControllerRunner = &DynamicCertKeyPairContent{}
|
||||
|
||||
// NewDynamicServingContentFromFiles returns a dynamic CertKeyContentProvider based on a cert and key filename
|
||||
func NewDynamicServingContentFromFiles(purpose, certFile, keyFile string) (*DynamicCertKeyPairContent, error) {
|
||||
if len(certFile) == 0 || len(keyFile) == 0 {
|
||||
return nil, fmt.Errorf("missing filename for serving cert")
|
||||
}
|
||||
name := fmt.Sprintf("%s::%s::%s", purpose, certFile, keyFile)
|
||||
|
||||
ret := &DynamicCertKeyPairContent{
|
||||
name: name,
|
||||
certFile: certFile,
|
||||
keyFile: keyFile,
|
||||
queue: workqueue.NewTypedRateLimitingQueueWithConfig(
|
||||
workqueue.DefaultTypedControllerRateLimiter[string](),
|
||||
workqueue.TypedRateLimitingQueueConfig[string]{Name: fmt.Sprintf("DynamicCABundle-%s", purpose)},
|
||||
),
|
||||
}
|
||||
if err := ret.loadCertKeyPair(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// AddListener adds a listener to be notified when the serving cert content changes.
|
||||
func (c *DynamicCertKeyPairContent) AddListener(listener Listener) {
|
||||
c.listeners = append(c.listeners, listener)
|
||||
}
|
||||
|
||||
// loadCertKeyPair determines the next set of content for the file.
|
||||
func (c *DynamicCertKeyPairContent) loadCertKeyPair() error {
|
||||
cert, err := os.ReadFile(c.certFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key, err := os.ReadFile(c.keyFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(cert) == 0 || len(key) == 0 {
|
||||
return fmt.Errorf("missing content for serving cert %q", c.Name())
|
||||
}
|
||||
|
||||
// Ensure that the key matches the cert and both are valid
|
||||
_, err = tls.X509KeyPair(cert, key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newCertKey := &certKeyContent{
|
||||
cert: cert,
|
||||
key: key,
|
||||
}
|
||||
|
||||
// check to see if we have a change. If the values are the same, do nothing.
|
||||
existing, ok := c.certKeyPair.Load().(*certKeyContent)
|
||||
if ok && existing != nil && existing.Equal(newCertKey) {
|
||||
return nil
|
||||
}
|
||||
|
||||
c.certKeyPair.Store(newCertKey)
|
||||
klog.V(2).InfoS("Loaded a new cert/key pair", "name", c.Name())
|
||||
|
||||
for _, listener := range c.listeners {
|
||||
listener.Enqueue()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RunOnce runs a single sync loop
|
||||
func (c *DynamicCertKeyPairContent) RunOnce(ctx context.Context) error {
|
||||
return c.loadCertKeyPair()
|
||||
}
|
||||
|
||||
// Run starts the controller and blocks until context is killed.
|
||||
func (c *DynamicCertKeyPairContent) Run(ctx context.Context, workers int) {
|
||||
defer utilruntime.HandleCrash()
|
||||
defer c.queue.ShutDown()
|
||||
|
||||
klog.InfoS("Starting controller", "name", c.name)
|
||||
defer klog.InfoS("Shutting down controller", "name", c.name)
|
||||
|
||||
// doesn't matter what workers say, only start one.
|
||||
go wait.Until(c.runWorker, time.Second, ctx.Done())
|
||||
|
||||
// start the loop that watches the cert and key files until stopCh is closed.
|
||||
go wait.Until(func() {
|
||||
if err := c.watchCertKeyFile(ctx.Done()); err != nil {
|
||||
klog.ErrorS(err, "Failed to watch cert and key file, will retry later")
|
||||
}
|
||||
}, time.Minute, ctx.Done())
|
||||
|
||||
<-ctx.Done()
|
||||
}
|
||||
|
||||
func (c *DynamicCertKeyPairContent) watchCertKeyFile(stopCh <-chan struct{}) error {
|
||||
// Trigger a check here to ensure the content will be checked periodically even if the following watch fails.
|
||||
c.queue.Add(workItemKey)
|
||||
|
||||
w, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating fsnotify watcher: %v", err)
|
||||
}
|
||||
defer w.Close()
|
||||
|
||||
if err := w.Add(c.certFile); err != nil {
|
||||
return fmt.Errorf("error adding watch for file %s: %v", c.certFile, err)
|
||||
}
|
||||
if err := w.Add(c.keyFile); err != nil {
|
||||
return fmt.Errorf("error adding watch for file %s: %v", c.keyFile, err)
|
||||
}
|
||||
// Trigger a check in case the file is updated before the watch starts.
|
||||
c.queue.Add(workItemKey)
|
||||
|
||||
for {
|
||||
select {
|
||||
case e := <-w.Events:
|
||||
if err := c.handleWatchEvent(e, w); err != nil {
|
||||
return err
|
||||
}
|
||||
case err := <-w.Errors:
|
||||
return fmt.Errorf("received fsnotify error: %v", err)
|
||||
case <-stopCh:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handleWatchEvent triggers reloading the cert and key file, and restarts a new watch if it's a Remove or Rename event.
|
||||
// If one file is updated before the other, the loadCertKeyPair method will catch the mismatch and will not apply the
|
||||
// change. When an event of the other file is received, it will trigger reloading the files again and the new content
|
||||
// will be loaded and used.
|
||||
func (c *DynamicCertKeyPairContent) handleWatchEvent(e fsnotify.Event, w *fsnotify.Watcher) error {
|
||||
// This should be executed after restarting the watch (if applicable) to ensure no file event will be missing.
|
||||
defer c.queue.Add(workItemKey)
|
||||
if !e.Has(fsnotify.Remove) && !e.Has(fsnotify.Rename) {
|
||||
return nil
|
||||
}
|
||||
if err := w.Remove(e.Name); err != nil {
|
||||
klog.InfoS("Failed to remove file watch, it may have been deleted", "file", e.Name, "err", err)
|
||||
}
|
||||
if err := w.Add(e.Name); err != nil {
|
||||
return fmt.Errorf("error adding watch for file %s: %v", e.Name, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *DynamicCertKeyPairContent) runWorker() {
|
||||
for c.processNextWorkItem() {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *DynamicCertKeyPairContent) processNextWorkItem() bool {
|
||||
dsKey, quit := c.queue.Get()
|
||||
if quit {
|
||||
return false
|
||||
}
|
||||
defer c.queue.Done(dsKey)
|
||||
|
||||
err := c.loadCertKeyPair()
|
||||
if err == nil {
|
||||
c.queue.Forget(dsKey)
|
||||
return true
|
||||
}
|
||||
|
||||
utilruntime.HandleError(fmt.Errorf("%v failed with : %v", dsKey, err))
|
||||
c.queue.AddRateLimited(dsKey)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Name is just an identifier
|
||||
func (c *DynamicCertKeyPairContent) Name() string {
|
||||
return c.name
|
||||
}
|
||||
|
||||
// CurrentCertKeyContent provides cert and key byte content
|
||||
func (c *DynamicCertKeyPairContent) CurrentCertKeyContent() ([]byte, []byte) {
|
||||
certKeyContent := c.certKeyPair.Load().(*certKeyContent)
|
||||
return certKeyContent.cert, certKeyContent.key
|
||||
}
|
49
e2e/vendor/k8s.io/apiserver/pkg/server/dynamiccertificates/dynamic_sni_content.go
generated
vendored
Normal file
49
e2e/vendor/k8s.io/apiserver/pkg/server/dynamiccertificates/dynamic_sni_content.go
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package dynamiccertificates
|
||||
|
||||
// DynamicFileSNIContent provides a SNICertKeyContentProvider that can dynamically react to new file content
|
||||
type DynamicFileSNIContent struct {
|
||||
*DynamicCertKeyPairContent
|
||||
sniNames []string
|
||||
}
|
||||
|
||||
var _ SNICertKeyContentProvider = &DynamicFileSNIContent{}
|
||||
var _ ControllerRunner = &DynamicFileSNIContent{}
|
||||
|
||||
// NewDynamicSNIContentFromFiles returns a dynamic SNICertKeyContentProvider based on a cert and key filename and explicit names
|
||||
func NewDynamicSNIContentFromFiles(purpose, certFile, keyFile string, sniNames ...string) (*DynamicFileSNIContent, error) {
|
||||
servingContent, err := NewDynamicServingContentFromFiles(purpose, certFile, keyFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := &DynamicFileSNIContent{
|
||||
DynamicCertKeyPairContent: servingContent,
|
||||
sniNames: sniNames,
|
||||
}
|
||||
if err := ret.loadCertKeyPair(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// SNINames returns explicitly set SNI names for the certificate. These are not dynamic.
|
||||
func (c *DynamicFileSNIContent) SNINames() []string {
|
||||
return c.sniNames
|
||||
}
|
68
e2e/vendor/k8s.io/apiserver/pkg/server/dynamiccertificates/interfaces.go
generated
vendored
Normal file
68
e2e/vendor/k8s.io/apiserver/pkg/server/dynamiccertificates/interfaces.go
generated
vendored
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
Copyright 2021 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package dynamiccertificates
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
)
|
||||
|
||||
// Listener is an interface to use to notify interested parties of a change.
|
||||
type Listener interface {
|
||||
// Enqueue should be called when an input may have changed
|
||||
Enqueue()
|
||||
}
|
||||
|
||||
// Notifier is a way to add listeners
|
||||
type Notifier interface {
|
||||
// AddListener is adds a listener to be notified of potential input changes.
|
||||
// This is a noop on static providers.
|
||||
AddListener(listener Listener)
|
||||
}
|
||||
|
||||
// CAContentProvider provides ca bundle byte content
|
||||
type CAContentProvider interface {
|
||||
Notifier
|
||||
|
||||
// Name is just an identifier.
|
||||
Name() string
|
||||
// CurrentCABundleContent provides ca bundle byte content. Errors can be
|
||||
// contained to the controllers initializing the value. By the time you get
|
||||
// here, you should always be returning a value that won't fail.
|
||||
CurrentCABundleContent() []byte
|
||||
// VerifyOptions provides VerifyOptions for authenticators.
|
||||
VerifyOptions() (x509.VerifyOptions, bool)
|
||||
}
|
||||
|
||||
// CertKeyContentProvider provides a certificate and matching private key.
|
||||
type CertKeyContentProvider interface {
|
||||
Notifier
|
||||
|
||||
// Name is just an identifier.
|
||||
Name() string
|
||||
// CurrentCertKeyContent provides cert and key byte content.
|
||||
CurrentCertKeyContent() ([]byte, []byte)
|
||||
}
|
||||
|
||||
// SNICertKeyContentProvider provides a certificate and matching private key as
|
||||
// well as optional explicit names.
|
||||
type SNICertKeyContentProvider interface {
|
||||
Notifier
|
||||
|
||||
CertKeyContentProvider
|
||||
// SNINames provides names used for SNI. May return nil.
|
||||
SNINames() []string
|
||||
}
|
91
e2e/vendor/k8s.io/apiserver/pkg/server/dynamiccertificates/named_certificates.go
generated
vendored
Normal file
91
e2e/vendor/k8s.io/apiserver/pkg/server/dynamiccertificates/named_certificates.go
generated
vendored
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package dynamiccertificates
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/util/validation"
|
||||
"k8s.io/klog/v2"
|
||||
netutils "k8s.io/utils/net"
|
||||
)
|
||||
|
||||
// BuildNamedCertificates returns a map of *tls.Certificate by name. It's
|
||||
// suitable for use in tls.Config#NamedCertificates. Returns an error if any of the certs
|
||||
// is invalid. Returns nil if len(certs) == 0
|
||||
func (c *DynamicServingCertificateController) BuildNamedCertificates(sniCerts []sniCertKeyContent) (map[string]*tls.Certificate, error) {
|
||||
nameToCertificate := map[string]*tls.Certificate{}
|
||||
byNameExplicit := map[string]*tls.Certificate{}
|
||||
|
||||
// Iterate backwards so that earlier certs take precedence in the names map
|
||||
for i := len(sniCerts) - 1; i >= 0; i-- {
|
||||
cert, err := tls.X509KeyPair(sniCerts[i].cert, sniCerts[i].key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid SNI cert keypair [%d/%q]: %v", i, c.sniCerts[i].Name(), err)
|
||||
}
|
||||
|
||||
// error is not possible given above call to X509KeyPair
|
||||
x509Cert, _ := x509.ParseCertificate(cert.Certificate[0])
|
||||
|
||||
names := sniCerts[i].sniNames
|
||||
for _, name := range names {
|
||||
byNameExplicit[name] = &cert
|
||||
}
|
||||
|
||||
klog.V(2).InfoS("Loaded SNI cert", "index", i, "certName", c.sniCerts[i].Name(), "certDetail", GetHumanCertDetail(x509Cert))
|
||||
if c.eventRecorder != nil {
|
||||
c.eventRecorder.Eventf(&corev1.ObjectReference{Name: c.sniCerts[i].Name()}, nil, corev1.EventTypeWarning, "TLSConfigChanged", "SNICertificateReload", "loaded SNI cert [%d/%q]: %s with explicit names %v", i, c.sniCerts[i].Name(), GetHumanCertDetail(x509Cert), names)
|
||||
}
|
||||
|
||||
if len(names) == 0 {
|
||||
names = getCertificateNames(x509Cert)
|
||||
for _, name := range names {
|
||||
nameToCertificate[name] = &cert
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Explicitly set names must override
|
||||
for k, v := range byNameExplicit {
|
||||
nameToCertificate[k] = v
|
||||
}
|
||||
|
||||
return nameToCertificate, nil
|
||||
}
|
||||
|
||||
// getCertificateNames returns names for an x509.Certificate. The names are
|
||||
// suitable for use in tls.Config#NamedCertificates.
|
||||
func getCertificateNames(cert *x509.Certificate) []string {
|
||||
var names []string
|
||||
|
||||
cn := cert.Subject.CommonName
|
||||
cnIsIP := netutils.ParseIPSloppy(cn) != nil
|
||||
cnIsValidDomain := cn == "*" || len(validation.IsDNS1123Subdomain(strings.TrimPrefix(cn, "*."))) == 0
|
||||
// don't use the CN if it is a valid IP because our IP serving detection may unexpectedly use it to terminate the connection.
|
||||
if !cnIsIP && cnIsValidDomain {
|
||||
names = append(names, cn)
|
||||
}
|
||||
names = append(names, cert.DNSNames...)
|
||||
// intentionally all IPs in the cert are ignored as SNI forbids passing IPs
|
||||
// to select a cert. Before go 1.6 the tls happily passed IPs as SNI values.
|
||||
|
||||
return names
|
||||
}
|
120
e2e/vendor/k8s.io/apiserver/pkg/server/dynamiccertificates/static_content.go
generated
vendored
Normal file
120
e2e/vendor/k8s.io/apiserver/pkg/server/dynamiccertificates/static_content.go
generated
vendored
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package dynamiccertificates
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
)
|
||||
|
||||
type staticCAContent struct {
|
||||
name string
|
||||
caBundle *caBundleAndVerifier
|
||||
}
|
||||
|
||||
var _ CAContentProvider = &staticCAContent{}
|
||||
|
||||
// NewStaticCAContent returns a CAContentProvider that always returns the same value
|
||||
func NewStaticCAContent(name string, caBundle []byte) (CAContentProvider, error) {
|
||||
caBundleAndVerifier, err := newCABundleAndVerifier(name, caBundle)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &staticCAContent{
|
||||
name: name,
|
||||
caBundle: caBundleAndVerifier,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Name is just an identifier
|
||||
func (c *staticCAContent) Name() string {
|
||||
return c.name
|
||||
}
|
||||
|
||||
func (c *staticCAContent) AddListener(Listener) {}
|
||||
|
||||
// CurrentCABundleContent provides ca bundle byte content
|
||||
func (c *staticCAContent) CurrentCABundleContent() (cabundle []byte) {
|
||||
return c.caBundle.caBundle
|
||||
}
|
||||
|
||||
func (c *staticCAContent) VerifyOptions() (x509.VerifyOptions, bool) {
|
||||
return c.caBundle.verifyOptions, true
|
||||
}
|
||||
|
||||
type staticCertKeyContent struct {
|
||||
name string
|
||||
cert []byte
|
||||
key []byte
|
||||
}
|
||||
|
||||
// NewStaticCertKeyContent returns a CertKeyContentProvider that always returns the same value
|
||||
func NewStaticCertKeyContent(name string, cert, key []byte) (CertKeyContentProvider, error) {
|
||||
// Ensure that the key matches the cert and both are valid
|
||||
_, err := tls.X509KeyPair(cert, key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &staticCertKeyContent{
|
||||
name: name,
|
||||
cert: cert,
|
||||
key: key,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Name is just an identifier
|
||||
func (c *staticCertKeyContent) Name() string {
|
||||
return c.name
|
||||
}
|
||||
|
||||
func (c *staticCertKeyContent) AddListener(Listener) {}
|
||||
|
||||
// CurrentCertKeyContent provides cert and key content
|
||||
func (c *staticCertKeyContent) CurrentCertKeyContent() ([]byte, []byte) {
|
||||
return c.cert, c.key
|
||||
}
|
||||
|
||||
type staticSNICertKeyContent struct {
|
||||
staticCertKeyContent
|
||||
sniNames []string
|
||||
}
|
||||
|
||||
// NewStaticSNICertKeyContent returns a SNICertKeyContentProvider that always returns the same value
|
||||
func NewStaticSNICertKeyContent(name string, cert, key []byte, sniNames ...string) (SNICertKeyContentProvider, error) {
|
||||
// Ensure that the key matches the cert and both are valid
|
||||
_, err := tls.X509KeyPair(cert, key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &staticSNICertKeyContent{
|
||||
staticCertKeyContent: staticCertKeyContent{
|
||||
name: name,
|
||||
cert: cert,
|
||||
key: key,
|
||||
},
|
||||
sniNames: sniNames,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *staticSNICertKeyContent) SNINames() []string {
|
||||
return c.sniNames
|
||||
}
|
||||
|
||||
func (c *staticSNICertKeyContent) AddListener(Listener) {}
|
287
e2e/vendor/k8s.io/apiserver/pkg/server/dynamiccertificates/tlsconfig.go
generated
vendored
Normal file
287
e2e/vendor/k8s.io/apiserver/pkg/server/dynamiccertificates/tlsconfig.go
generated
vendored
Normal file
@ -0,0 +1,287 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package dynamiccertificates
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/tools/events"
|
||||
"k8s.io/client-go/util/cert"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
const workItemKey = "key"
|
||||
|
||||
// DynamicServingCertificateController dynamically loads certificates and provides a golang tls compatible dynamic GetCertificate func.
|
||||
type DynamicServingCertificateController struct {
|
||||
// baseTLSConfig is the static portion of the tlsConfig for serving to clients. It is copied and the copy is mutated
|
||||
// based on the dynamic cert state.
|
||||
baseTLSConfig *tls.Config
|
||||
|
||||
// clientCA provides the very latest content of the ca bundle
|
||||
clientCA CAContentProvider
|
||||
// servingCert provides the very latest content of the default serving certificate
|
||||
servingCert CertKeyContentProvider
|
||||
// sniCerts are a list of CertKeyContentProvider with associated names used for SNI
|
||||
sniCerts []SNICertKeyContentProvider
|
||||
|
||||
// currentlyServedContent holds the original bytes that we are serving. This is used to decide if we need to set a
|
||||
// new atomic value. The types used for efficient TLSConfig preclude using the processed value.
|
||||
currentlyServedContent *dynamicCertificateContent
|
||||
// currentServingTLSConfig holds a *tls.Config that will be used to serve requests
|
||||
currentServingTLSConfig atomic.Value
|
||||
|
||||
// queue only ever has one item, but it has nice error handling backoff/retry semantics
|
||||
queue workqueue.TypedRateLimitingInterface[string]
|
||||
eventRecorder events.EventRecorder
|
||||
}
|
||||
|
||||
var _ Listener = &DynamicServingCertificateController{}
|
||||
|
||||
// NewDynamicServingCertificateController returns a controller that can be used to keep a TLSConfig up to date.
|
||||
func NewDynamicServingCertificateController(
|
||||
baseTLSConfig *tls.Config,
|
||||
clientCA CAContentProvider,
|
||||
servingCert CertKeyContentProvider,
|
||||
sniCerts []SNICertKeyContentProvider,
|
||||
eventRecorder events.EventRecorder,
|
||||
) *DynamicServingCertificateController {
|
||||
c := &DynamicServingCertificateController{
|
||||
baseTLSConfig: baseTLSConfig,
|
||||
clientCA: clientCA,
|
||||
servingCert: servingCert,
|
||||
sniCerts: sniCerts,
|
||||
|
||||
queue: workqueue.NewTypedRateLimitingQueueWithConfig(
|
||||
workqueue.DefaultTypedControllerRateLimiter[string](),
|
||||
workqueue.TypedRateLimitingQueueConfig[string]{Name: "DynamicServingCertificateController"},
|
||||
),
|
||||
eventRecorder: eventRecorder,
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// GetConfigForClient is an implementation of tls.Config.GetConfigForClient
|
||||
func (c *DynamicServingCertificateController) GetConfigForClient(clientHello *tls.ClientHelloInfo) (*tls.Config, error) {
|
||||
uncastObj := c.currentServingTLSConfig.Load()
|
||||
if uncastObj == nil {
|
||||
return nil, errors.New("dynamiccertificates: configuration not ready")
|
||||
}
|
||||
tlsConfig, ok := uncastObj.(*tls.Config)
|
||||
if !ok {
|
||||
return nil, errors.New("dynamiccertificates: unexpected config type")
|
||||
}
|
||||
|
||||
tlsConfigCopy := tlsConfig.Clone()
|
||||
|
||||
// if the client set SNI information, just use our "normal" SNI flow
|
||||
if len(clientHello.ServerName) > 0 {
|
||||
return tlsConfigCopy, nil
|
||||
}
|
||||
|
||||
// if the client didn't set SNI, then we need to inspect the requested IP so that we can choose
|
||||
// a certificate from our list if we specifically handle that IP. This can happen when an IP is specifically mapped by name.
|
||||
host, _, err := net.SplitHostPort(clientHello.Conn.LocalAddr().String())
|
||||
if err != nil {
|
||||
return tlsConfigCopy, nil
|
||||
}
|
||||
|
||||
ipCert, ok := tlsConfigCopy.NameToCertificate[host]
|
||||
if !ok {
|
||||
return tlsConfigCopy, nil
|
||||
}
|
||||
tlsConfigCopy.Certificates = []tls.Certificate{*ipCert}
|
||||
tlsConfigCopy.NameToCertificate = nil
|
||||
|
||||
return tlsConfigCopy, nil
|
||||
}
|
||||
|
||||
// newTLSContent determines the next set of content for overriding the baseTLSConfig.
|
||||
func (c *DynamicServingCertificateController) newTLSContent() (*dynamicCertificateContent, error) {
|
||||
newContent := &dynamicCertificateContent{}
|
||||
|
||||
if c.clientCA != nil {
|
||||
currClientCABundle := c.clientCA.CurrentCABundleContent()
|
||||
// we allow removing all client ca bundles because the server is still secure when this happens. it just means
|
||||
// that there isn't a hint to clients about which client-cert to used. this happens when there is no client-ca
|
||||
// yet known for authentication, which can happen in aggregated apiservers and some kube-apiserver deployment modes.
|
||||
newContent.clientCA = caBundleContent{caBundle: currClientCABundle}
|
||||
}
|
||||
|
||||
if c.servingCert != nil {
|
||||
currServingCert, currServingKey := c.servingCert.CurrentCertKeyContent()
|
||||
if len(currServingCert) == 0 || len(currServingKey) == 0 {
|
||||
return nil, fmt.Errorf("not loading an empty serving certificate from %q", c.servingCert.Name())
|
||||
}
|
||||
|
||||
newContent.servingCert = certKeyContent{cert: currServingCert, key: currServingKey}
|
||||
}
|
||||
|
||||
for i, sniCert := range c.sniCerts {
|
||||
currCert, currKey := sniCert.CurrentCertKeyContent()
|
||||
if len(currCert) == 0 || len(currKey) == 0 {
|
||||
return nil, fmt.Errorf("not loading an empty SNI certificate from %d/%q", i, sniCert.Name())
|
||||
}
|
||||
|
||||
newContent.sniCerts = append(newContent.sniCerts, sniCertKeyContent{certKeyContent: certKeyContent{cert: currCert, key: currKey}, sniNames: sniCert.SNINames()})
|
||||
}
|
||||
|
||||
return newContent, nil
|
||||
}
|
||||
|
||||
// syncCerts gets newTLSContent, if it has changed from the existing, the content is parsed and stored for usage in
|
||||
// GetConfigForClient.
|
||||
func (c *DynamicServingCertificateController) syncCerts() error {
|
||||
newContent, err := c.newTLSContent()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// if the content is the same as what we currently have, we can simply skip it. This works because we are single
|
||||
// threaded. If you ever make this multi-threaded, add a lock.
|
||||
if newContent.Equal(c.currentlyServedContent) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// make a shallow copy and override the dynamic pieces which have changed.
|
||||
newTLSConfigCopy := c.baseTLSConfig.Clone()
|
||||
|
||||
// parse new content to add to TLSConfig
|
||||
if len(newContent.clientCA.caBundle) > 0 {
|
||||
newClientCAPool := x509.NewCertPool()
|
||||
newClientCAs, err := cert.ParseCertsPEM(newContent.clientCA.caBundle)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to load client CA file %q: %v", string(newContent.clientCA.caBundle), err)
|
||||
}
|
||||
for i, cert := range newClientCAs {
|
||||
klog.V(2).InfoS("Loaded client CA", "index", i, "certName", c.clientCA.Name(), "certDetail", GetHumanCertDetail(cert))
|
||||
if c.eventRecorder != nil {
|
||||
c.eventRecorder.Eventf(&corev1.ObjectReference{Name: c.clientCA.Name()}, nil, corev1.EventTypeWarning, "TLSConfigChanged", "CACertificateReload", "loaded client CA [%d/%q]: %s", i, c.clientCA.Name(), GetHumanCertDetail(cert))
|
||||
}
|
||||
|
||||
newClientCAPool.AddCert(cert)
|
||||
}
|
||||
|
||||
newTLSConfigCopy.ClientCAs = newClientCAPool
|
||||
}
|
||||
|
||||
if len(newContent.servingCert.cert) > 0 && len(newContent.servingCert.key) > 0 {
|
||||
cert, err := tls.X509KeyPair(newContent.servingCert.cert, newContent.servingCert.key)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid serving cert keypair: %v", err)
|
||||
}
|
||||
|
||||
x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid serving cert: %v", err)
|
||||
}
|
||||
|
||||
klog.V(2).InfoS("Loaded serving cert", "certName", c.servingCert.Name(), "certDetail", GetHumanCertDetail(x509Cert))
|
||||
if c.eventRecorder != nil {
|
||||
c.eventRecorder.Eventf(&corev1.ObjectReference{Name: c.servingCert.Name()}, nil, corev1.EventTypeWarning, "TLSConfigChanged", "ServingCertificateReload", "loaded serving cert [%q]: %s", c.servingCert.Name(), GetHumanCertDetail(x509Cert))
|
||||
}
|
||||
|
||||
newTLSConfigCopy.Certificates = []tls.Certificate{cert}
|
||||
}
|
||||
|
||||
if len(newContent.sniCerts) > 0 {
|
||||
newTLSConfigCopy.NameToCertificate, err = c.BuildNamedCertificates(newContent.sniCerts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to build named certificate map: %v", err)
|
||||
}
|
||||
|
||||
// append all named certs. Otherwise, the go tls stack will think no SNI processing
|
||||
// is necessary because there is only one cert anyway.
|
||||
// Moreover, if servingCert is not set, the first SNI
|
||||
// cert will become the default cert. That's what we expect anyway.
|
||||
for _, sniCert := range newTLSConfigCopy.NameToCertificate {
|
||||
newTLSConfigCopy.Certificates = append(newTLSConfigCopy.Certificates, *sniCert)
|
||||
}
|
||||
}
|
||||
|
||||
// store new values of content for serving.
|
||||
c.currentServingTLSConfig.Store(newTLSConfigCopy)
|
||||
c.currentlyServedContent = newContent // this is single threaded, so we have no locking issue
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RunOnce runs a single sync step to ensure that we have a valid starting configuration.
|
||||
func (c *DynamicServingCertificateController) RunOnce() error {
|
||||
return c.syncCerts()
|
||||
}
|
||||
|
||||
// Run starts the kube-apiserver and blocks until stopCh is closed.
|
||||
func (c *DynamicServingCertificateController) Run(workers int, stopCh <-chan struct{}) {
|
||||
defer utilruntime.HandleCrash()
|
||||
defer c.queue.ShutDown()
|
||||
|
||||
klog.InfoS("Starting DynamicServingCertificateController")
|
||||
defer klog.InfoS("Shutting down DynamicServingCertificateController")
|
||||
|
||||
// synchronously load once. We will trigger again, so ignoring any error is fine
|
||||
_ = c.RunOnce()
|
||||
|
||||
// doesn't matter what workers say, only start one.
|
||||
go wait.Until(c.runWorker, time.Second, stopCh)
|
||||
|
||||
// start timer that rechecks every minute, just in case. this also serves to prime the controller quickly.
|
||||
go wait.Until(func() {
|
||||
c.Enqueue()
|
||||
}, 1*time.Minute, stopCh)
|
||||
|
||||
<-stopCh
|
||||
}
|
||||
|
||||
func (c *DynamicServingCertificateController) runWorker() {
|
||||
for c.processNextWorkItem() {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *DynamicServingCertificateController) processNextWorkItem() bool {
|
||||
dsKey, quit := c.queue.Get()
|
||||
if quit {
|
||||
return false
|
||||
}
|
||||
defer c.queue.Done(dsKey)
|
||||
|
||||
err := c.syncCerts()
|
||||
if err == nil {
|
||||
c.queue.Forget(dsKey)
|
||||
return true
|
||||
}
|
||||
|
||||
utilruntime.HandleError(fmt.Errorf("%v failed with : %v", dsKey, err))
|
||||
c.queue.AddRateLimited(dsKey)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Enqueue a method to allow separate control loops to cause the certificate controller to trigger and read content.
|
||||
func (c *DynamicServingCertificateController) Enqueue() {
|
||||
c.queue.Add(workItemKey)
|
||||
}
|
105
e2e/vendor/k8s.io/apiserver/pkg/server/dynamiccertificates/union_content.go
generated
vendored
Normal file
105
e2e/vendor/k8s.io/apiserver/pkg/server/dynamiccertificates/union_content.go
generated
vendored
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package dynamiccertificates
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"strings"
|
||||
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
)
|
||||
|
||||
type unionCAContent []CAContentProvider
|
||||
|
||||
var _ CAContentProvider = &unionCAContent{}
|
||||
var _ ControllerRunner = &unionCAContent{}
|
||||
|
||||
// NewUnionCAContentProvider returns a CAContentProvider that is a union of other CAContentProviders
|
||||
func NewUnionCAContentProvider(caContentProviders ...CAContentProvider) CAContentProvider {
|
||||
return unionCAContent(caContentProviders)
|
||||
}
|
||||
|
||||
// Name is just an identifier
|
||||
func (c unionCAContent) Name() string {
|
||||
names := []string{}
|
||||
for _, curr := range c {
|
||||
names = append(names, curr.Name())
|
||||
}
|
||||
return strings.Join(names, ",")
|
||||
}
|
||||
|
||||
// CurrentCABundleContent provides ca bundle byte content
|
||||
func (c unionCAContent) CurrentCABundleContent() []byte {
|
||||
caBundles := [][]byte{}
|
||||
for _, curr := range c {
|
||||
if currCABytes := curr.CurrentCABundleContent(); len(currCABytes) > 0 {
|
||||
caBundles = append(caBundles, []byte(strings.TrimSpace(string(currCABytes))))
|
||||
}
|
||||
}
|
||||
|
||||
return bytes.Join(caBundles, []byte("\n"))
|
||||
}
|
||||
|
||||
// CurrentCABundleContent provides ca bundle byte content
|
||||
func (c unionCAContent) VerifyOptions() (x509.VerifyOptions, bool) {
|
||||
currCABundle := c.CurrentCABundleContent()
|
||||
if len(currCABundle) == 0 {
|
||||
return x509.VerifyOptions{}, false
|
||||
}
|
||||
|
||||
// TODO make more efficient. This isn't actually used in any of our mainline paths. It's called to build the TLSConfig
|
||||
// TODO on file changes, but the actual authentication runs against the individual items, not the union.
|
||||
ret, err := newCABundleAndVerifier(c.Name(), c.CurrentCABundleContent())
|
||||
if err != nil {
|
||||
// because we're made up of already vetted values, this indicates some kind of coding error
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return ret.verifyOptions, true
|
||||
}
|
||||
|
||||
// AddListener adds a listener to be notified when the CA content changes.
|
||||
func (c unionCAContent) AddListener(listener Listener) {
|
||||
for _, curr := range c {
|
||||
curr.AddListener(listener)
|
||||
}
|
||||
}
|
||||
|
||||
// AddListener adds a listener to be notified when the CA content changes.
|
||||
func (c unionCAContent) RunOnce(ctx context.Context) error {
|
||||
errors := []error{}
|
||||
for _, curr := range c {
|
||||
if controller, ok := curr.(ControllerRunner); ok {
|
||||
if err := controller.RunOnce(ctx); err != nil {
|
||||
errors = append(errors, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return utilerrors.NewAggregate(errors)
|
||||
}
|
||||
|
||||
// Run runs the controller
|
||||
func (c unionCAContent) Run(ctx context.Context, workers int) {
|
||||
for _, curr := range c {
|
||||
if controller, ok := curr.(ControllerRunner); ok {
|
||||
go controller.Run(ctx, workers)
|
||||
}
|
||||
}
|
||||
}
|
66
e2e/vendor/k8s.io/apiserver/pkg/server/dynamiccertificates/util.go
generated
vendored
Normal file
66
e2e/vendor/k8s.io/apiserver/pkg/server/dynamiccertificates/util.go
generated
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package dynamiccertificates
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// GetHumanCertDetail is a convenient method for printing compact details of certificate that helps when debugging
|
||||
// kube-apiserver usage of certs.
|
||||
func GetHumanCertDetail(certificate *x509.Certificate) string {
|
||||
humanName := certificate.Subject.CommonName
|
||||
signerHumanName := certificate.Issuer.CommonName
|
||||
if certificate.Subject.CommonName == certificate.Issuer.CommonName {
|
||||
signerHumanName = "<self>"
|
||||
}
|
||||
|
||||
usages := []string{}
|
||||
for _, curr := range certificate.ExtKeyUsage {
|
||||
if curr == x509.ExtKeyUsageClientAuth {
|
||||
usages = append(usages, "client")
|
||||
continue
|
||||
}
|
||||
if curr == x509.ExtKeyUsageServerAuth {
|
||||
usages = append(usages, "serving")
|
||||
continue
|
||||
}
|
||||
|
||||
usages = append(usages, fmt.Sprintf("%d", curr))
|
||||
}
|
||||
|
||||
validServingNames := []string{}
|
||||
for _, ip := range certificate.IPAddresses {
|
||||
validServingNames = append(validServingNames, ip.String())
|
||||
}
|
||||
validServingNames = append(validServingNames, certificate.DNSNames...)
|
||||
servingString := ""
|
||||
if len(validServingNames) > 0 {
|
||||
servingString = fmt.Sprintf(" validServingFor=[%s]", strings.Join(validServingNames, ","))
|
||||
}
|
||||
|
||||
groupString := ""
|
||||
if len(certificate.Subject.Organization) > 0 {
|
||||
groupString = fmt.Sprintf(" groups=[%s]", strings.Join(certificate.Subject.Organization, ","))
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%q [%s]%s%s issuer=%q (%v to %v (now=%v))", humanName, strings.Join(usages, ","), groupString, servingString, signerHumanName, certificate.NotBefore.UTC(), certificate.NotAfter.UTC(),
|
||||
time.Now().UTC())
|
||||
}
|
Reference in New Issue
Block a user