Skip to content

Commit

Permalink
Add tests for CA manager
Browse files Browse the repository at this point in the history
  • Loading branch information
rmohr committed May 2, 2019
1 parent 0c9f8ef commit 87afa4f
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 8 deletions.
2 changes: 2 additions & 0 deletions pkg/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (

const ServiceAccountNamespaceFile = "/var/run/secrets/kubernetes.io/serviceaccount/namespace"
const namespaceKubevirt = "kubevirt"
const ExtensionAPIServerAuthenticationConfigMap = "extension-apiserver-authentication"
const RequestHeaderClientCAFileKey = "requestheader-client-ca-file"

func GetNamespace() (string, error) {
if data, err := ioutil.ReadFile(ServiceAccountNamespaceFile); err == nil {
Expand Down
3 changes: 3 additions & 0 deletions pkg/virt-api/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,15 @@ go_test(
name = "go_default_test",
srcs = [
"api_test.go",
"ca-manager_test.go",
"virt-api_suite_test.go",
],
embed = [":go_default_library"],
deps = [
"//pkg/api/v1:go_default_library",
"//pkg/kubecli:go_default_library",
"//pkg/log:go_default_library",
"//pkg/util:go_default_library",
"//pkg/virt-api/rest:go_default_library",
"//vendor/github.com/emicklei/go-restful:go_default_library",
"//vendor/github.com/golang/mock/gomock:go_default_library",
Expand All @@ -62,6 +64,7 @@ go_test(
"//vendor/k8s.io/api/admissionregistration/v1beta1:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/client-go/tools/cache:go_default_library",
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
"//vendor/k8s.io/client-go/util/cert:go_default_library",
"//vendor/k8s.io/client-go/util/cert/triple:go_default_library",
Expand Down
4 changes: 2 additions & 2 deletions pkg/virt-api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -401,15 +401,15 @@ func deserializeStrings(in string) ([]string, error) {
}

func (app *virtAPIApp) readRequestHeader() error {
authConfigMap, err := app.virtCli.CoreV1().ConfigMaps(metav1.NamespaceSystem).Get("extension-apiserver-authentication", metav1.GetOptions{})
authConfigMap, err := app.virtCli.CoreV1().ConfigMaps(metav1.NamespaceSystem).Get(util.ExtensionAPIServerAuthenticationConfigMap, metav1.GetOptions{})
if err != nil {
return err
}

// The request-header CA is mandatory. It can be retrieved from the configmap as we do here, or it must be provided
// via flag on start of this apiserver. Since we don't do the latter, the former is mandatory for us
// see https://github.com/kubernetes-incubator/apiserver-builder-alpha/blob/master/docs/concepts/auth.md#requestheader-authentication
_, ok := authConfigMap.Data["requestheader-client-ca-file"]
_, ok := authConfigMap.Data[util.RequestHeaderClientCAFileKey]
if !ok {
return fmt.Errorf("requestheader-client-ca-file not found in extension-apiserver-authentication ConfigMap")
}
Expand Down
4 changes: 3 additions & 1 deletion pkg/virt-api/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ import (
"k8s.io/client-go/util/cert/triple"
aggregatorclient "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset"

"kubevirt.io/kubevirt/pkg/util"

v1 "kubevirt.io/kubevirt/pkg/api/v1"
"kubevirt.io/kubevirt/pkg/kubecli"
"kubevirt.io/kubevirt/pkg/log"
Expand Down Expand Up @@ -206,7 +208,7 @@ var _ = Describe("Virt-api", func() {
It("should auto detect correct request headers from cert configmap", func() {
configMap := &k8sv1.ConfigMap{}
configMap.Data = make(map[string]string)
configMap.Data["requestheader-client-ca-file"] = "morefakedata"
configMap.Data[util.RequestHeaderClientCAFileKey] = "morefakedata"
configMap.Data["requestheader-username-headers"] = "[\"fakeheader1\"]"
configMap.Data["requestheader-group-headers"] = "[\"fakeheader2\"]"
configMap.Data["requestheader-extra-headers-prefix"] = "[\"fakeheader3-\"]"
Expand Down
10 changes: 5 additions & 5 deletions pkg/virt-api/ca-manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/cert"

"kubevirt.io/kubevirt/pkg/util"
)

type ClientCAManager interface {
Expand All @@ -53,9 +55,7 @@ func (m *manager) GetCurrent() (*x509.CertPool, error) {
m.lock.Lock()
defer m.lock.Unlock()

configMapName := "extension-apiserver-authentication"

obj, exists, err := m.store.GetByKey(metav1.NamespaceSystem + "/" + configMapName)
obj, exists, err := m.store.GetByKey(metav1.NamespaceSystem + "/" + util.ExtensionAPIServerAuthenticationConfigMap)

if err != nil {
return nil, err
Expand All @@ -64,7 +64,7 @@ func (m *manager) GetCurrent() (*x509.CertPool, error) {
return m.lastPool, nil
}

return nil, fmt.Errorf("configmap %s not found. Unable to detect request header CA", configMapName)
return nil, fmt.Errorf("configmap %s not found. Unable to detect request header CA", util.ExtensionAPIServerAuthenticationConfigMap)
}

configMap := obj.(*k8sv1.ConfigMap)
Expand All @@ -74,7 +74,7 @@ func (m *manager) GetCurrent() (*x509.CertPool, error) {
return m.lastPool, nil
}

requestHeaderClientCA, ok := configMap.Data["requestheader-client-ca-file"]
requestHeaderClientCA, ok := configMap.Data[util.RequestHeaderClientCAFileKey]
if !ok {
return nil, fmt.Errorf("requestheader-client-ca-file not found in extension-apiserver-authentication ConfigMap")
}
Expand Down
93 changes: 93 additions & 0 deletions pkg/virt-api/ca-manager_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package virt_api

import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
v1 "k8s.io/api/core/v1"
v12 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/cert"
"k8s.io/client-go/util/cert/triple"

"kubevirt.io/kubevirt/pkg/util"
)

var _ = Describe("CaManager", func() {

var configMap *v1.ConfigMap
var manager ClientCAManager
var store cache.Store

BeforeEach(func() {
ca, err := triple.NewCA("first")
Expect(err).ToNot(HaveOccurred())
configMap = &v1.ConfigMap{
ObjectMeta: v12.ObjectMeta{
Name: util.ExtensionAPIServerAuthenticationConfigMap,
Namespace: v12.NamespaceSystem,
ResourceVersion: "1",
},
Data: map[string]string{
util.RequestHeaderClientCAFileKey: string(cert.EncodeCertPEM(ca.Cert)),
},
}
store = cache.NewStore(cache.DeletionHandlingMetaNamespaceKeyFunc)
Expect(store.Add(configMap)).To(Succeed())
manager = NewClientCAManager(store)
})

It("should load an initial CA", func() {
cert, err := manager.GetCurrent()
Expect(err).ToNot(HaveOccurred())
Expect(cert.Subjects()[0]).To(ContainSubstring("first"))
})

It("should detect updates on the informer and update the CA", func() {
newCA, err := triple.NewCA("new")
Expect(err).ToNot(HaveOccurred())
configMap.Data[util.RequestHeaderClientCAFileKey] = string(cert.EncodeCertPEM(newCA.Cert))
configMap.ObjectMeta.ResourceVersion = "2"
cert, err := manager.GetCurrent()
Expect(err).ToNot(HaveOccurred())
Expect(cert.Subjects()[0]).To(ContainSubstring("new"))
})

It("should detect invalid CAs and recover later", func() {
By("injecting an invalid CA")
configMap.Data[util.RequestHeaderClientCAFileKey] = string("garbage")
configMap.ObjectMeta.ResourceVersion = "2"
_, err := manager.GetCurrent()
Expect(err).To(HaveOccurred())
By("repairing the CA")
configMap.ObjectMeta.ResourceVersion = "3"
newCA, err := triple.NewCA("new")
Expect(err).ToNot(HaveOccurred())
configMap.Data[util.RequestHeaderClientCAFileKey] = string(cert.EncodeCertPEM(newCA.Cert))
cert, err := manager.GetCurrent()
Expect(err).ToNot(HaveOccurred())
Expect(cert.Subjects()[0]).To(ContainSubstring("new"))
})

It("should detect if the is no CA provided", func() {
delete(configMap.Data, util.RequestHeaderClientCAFileKey)
_, err := manager.GetCurrent()
Expect(err).To(HaveOccurred())
})

It("should detect if the config map is missing", func() {
Expect(store.Delete(configMap)).To(Succeed())
_, err := manager.GetCurrent()
Expect(err).To(HaveOccurred())
})

It("should return the last result if the resource version of the map did not change", func() {
By("first loading the valid CA")
_, err := manager.GetCurrent()
Expect(err).ToNot(HaveOccurred())
By("changing the content but not increasing the resource version")
configMap.Data[util.RequestHeaderClientCAFileKey] = string("garbage")
cert, err := manager.GetCurrent()
Expect(err).ToNot(HaveOccurred())
Expect(cert.Subjects()[0]).To(ContainSubstring("first"))
})
})

0 comments on commit 87afa4f

Please sign in to comment.