...

Source file src/sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane/auth_test.go

Documentation: sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane

     1  /*
     2  Copyright 2021 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package controlplane_test
    18  
    19  import (
    20  	"crypto/tls"
    21  	"crypto/x509"
    22  	"os"
    23  	"path/filepath"
    24  
    25  	. "github.com/onsi/ginkgo/v2"
    26  	. "github.com/onsi/gomega"
    27  	"k8s.io/client-go/rest"
    28  	kcert "k8s.io/client-go/util/cert"
    29  
    30  	cp "sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane"
    31  	"sigs.k8s.io/controller-runtime/pkg/internal/testing/process"
    32  )
    33  
    34  var _ = Describe("Cert Authentication", func() {
    35  	var authn *cp.CertAuthn
    36  	BeforeEach(func() {
    37  		var err error
    38  		authn, err = cp.NewCertAuthn()
    39  		Expect(err).NotTo(HaveOccurred(), "should be able to create the cert authn")
    40  	})
    41  	Context("when starting", func() {
    42  		It("should write the verifying CA to the configured directory", func() {
    43  			By("setting up a temp dir")
    44  			dir, err := os.MkdirTemp("", "envtest_controlplane_*")
    45  			Expect(err).NotTo(HaveOccurred(), "should be able to provision a temp dir")
    46  			if dir != "" {
    47  				defer os.RemoveAll(dir)
    48  			}
    49  
    50  			By("configuring to use that dir")
    51  			Expect(authn.Configure(dir, process.EmptyArguments())).To(Succeed())
    52  
    53  			By("starting and checking the dir")
    54  			Expect(authn.Start()).To(Succeed())
    55  			defer func() { Expect(authn.Stop()).To(Succeed()) }() // not strictly necessary, but future-proof
    56  
    57  			_, err = os.Stat(filepath.Join(dir, "client-cert-auth-ca.crt"))
    58  			Expect(err).NotTo(HaveOccurred())
    59  		})
    60  
    61  		It("should error out if we haven't been configured yet", func() {
    62  			// NB(directxman12): no configure here intentionally
    63  			Expect(authn.Start()).NotTo(Succeed())
    64  		})
    65  	})
    66  	Context("when configuring", func() {
    67  		It("should have set up the API server to use the written file for client cert auth", func() {
    68  			args := process.EmptyArguments()
    69  			Expect(authn.Configure("/tmp/____doesnotexist", args)).To(Succeed())
    70  			Expect(args.Get("client-ca-file").Get(nil)).To(ConsistOf("/tmp/____doesnotexist/client-cert-auth-ca.crt"))
    71  		})
    72  	})
    73  
    74  	Describe("creating users", func() {
    75  		user := cp.User{Name: "someuser", Groups: []string{"group1", "group2"}}
    76  
    77  		Context("before starting", func() {
    78  			It("should yield a REST config that contains certs valid for the to-be-written CA", func() {
    79  				cfg, err := authn.AddUser(user, &rest.Config{})
    80  				Expect(err).NotTo(HaveOccurred())
    81  				Expect(cfg).NotTo(BeNil())
    82  
    83  				Expect(cfg.CertData).NotTo(BeEmpty())
    84  				Expect(cfg.KeyData).NotTo(BeEmpty())
    85  
    86  				// double-check the cert (assume the key is fine if it's present
    87  				// and the cert is also present, cause it's more annoying to verify
    88  				// and we have separate tinyca & integration tests.
    89  				By("parsing the config's cert & key data")
    90  				certs, err := tls.X509KeyPair(cfg.CertData, cfg.KeyData)
    91  				Expect(err).NotTo(HaveOccurred(), "config cert/key data should be valid key pair")
    92  				cert, err := x509.ParseCertificate(certs.Certificate[0]) // re-parse cause .Leaf isn't saved
    93  				Expect(err).NotTo(HaveOccurred())
    94  
    95  				By("starting and loading the CA cert")
    96  				dir, err := os.MkdirTemp("", "envtest_controlplane_*")
    97  				Expect(err).NotTo(HaveOccurred(), "should be able to provision a temp dir")
    98  				if dir != "" {
    99  					defer os.RemoveAll(dir)
   100  				}
   101  				Expect(authn.Configure(dir, process.EmptyArguments())).To(Succeed())
   102  				Expect(authn.Start()).To(Succeed())
   103  				caCerts, err := kcert.CertsFromFile(filepath.Join(dir, "client-cert-auth-ca.crt"))
   104  				Expect(err).NotTo(HaveOccurred(), "should be able to read the CA cert file))))")
   105  				Expect(cert.CheckSignatureFrom(caCerts[0])).To(Succeed(), "the config's cert should be signed by the written CA")
   106  			})
   107  
   108  			It("should copy the configuration from the base CA without modifying it", func() {
   109  				By("creating a user and checking the output config")
   110  				base := &rest.Config{Burst: 30}
   111  				cfg, err := authn.AddUser(user, base)
   112  				Expect(err).NotTo(HaveOccurred())
   113  				Expect(cfg).NotTo(BeNil())
   114  				Expect(cfg.Burst).To(Equal(30))
   115  
   116  				By("mutating the base and verifying the cfg doesn't change")
   117  				base.Burst = 8675
   118  				Expect(cfg.Burst).To(Equal(30))
   119  			})
   120  		})
   121  
   122  		Context("after starting", func() {
   123  			var dir string
   124  			BeforeEach(func() {
   125  				By("setting up a temp dir & starting with it")
   126  				var err error
   127  				dir, err = os.MkdirTemp("", "envtest_controlplane_*")
   128  				Expect(err).NotTo(HaveOccurred(), "should be able to provision a temp dir")
   129  				Expect(authn.Configure(dir, process.EmptyArguments())).To(Succeed())
   130  				Expect(authn.Start()).To(Succeed())
   131  			})
   132  			AfterEach(func() {
   133  				if dir != "" {
   134  					defer os.RemoveAll(dir)
   135  				}
   136  			})
   137  
   138  			It("should yield a REST config that contains certs valid for the written CA", func() {
   139  				cfg, err := authn.AddUser(user, &rest.Config{})
   140  				Expect(err).NotTo(HaveOccurred())
   141  				Expect(cfg).NotTo(BeNil())
   142  
   143  				Expect(cfg.CertData).NotTo(BeEmpty())
   144  				Expect(cfg.KeyData).NotTo(BeEmpty())
   145  
   146  				// double-check the cert (assume the key is fine if it's present
   147  				// and the cert is also present, cause it's more annoying to verify
   148  				// and we have separate tinyca & integration tests.
   149  				By("parsing the config's cert & key data")
   150  				certs, err := tls.X509KeyPair(cfg.CertData, cfg.KeyData)
   151  				Expect(err).NotTo(HaveOccurred(), "config cert/key data should be valid key pair")
   152  				cert, err := x509.ParseCertificate(certs.Certificate[0]) // re-parse cause .Leaf isn't saved
   153  				Expect(err).NotTo(HaveOccurred())
   154  
   155  				By("loading the CA cert")
   156  				caCerts, err := kcert.CertsFromFile(filepath.Join(dir, "client-cert-auth-ca.crt"))
   157  				Expect(err).NotTo(HaveOccurred(), "should be able to read the CA cert file))))")
   158  				Expect(cert.CheckSignatureFrom(caCerts[0])).To(Succeed(), "the config's cert should be signed by the written CA")
   159  			})
   160  
   161  			It("should copy the configuration from the base CA without modifying it", func() {
   162  				By("creating a user and checking the output config")
   163  				base := &rest.Config{Burst: 30}
   164  				cfg, err := authn.AddUser(user, base)
   165  				Expect(err).NotTo(HaveOccurred())
   166  				Expect(cfg).NotTo(BeNil())
   167  				Expect(cfg.Burst).To(Equal(30))
   168  
   169  				By("mutating the base and verifying the cfg doesn't change")
   170  				base.Burst = 8675
   171  				Expect(cfg.Burst).To(Equal(30))
   172  			})
   173  		})
   174  	})
   175  })
   176  

View as plain text