From e3a043333e223b1b79efe7bfc40f9059959f2762 Mon Sep 17 00:00:00 2001 From: Andrej Krejcir Date: Thu, 25 Jul 2024 14:27:09 +0200 Subject: [PATCH 1/4] api: Return token expiration timestamp Added "expirationTimestamp" to the response of the /token endpoint. This is for convenience, and to be similar to the response from ServiceAccount TokenRequest API. Signed-off-by: Andrej Krejcir --- api/v1alpha1/types.go | 4 +++- pkg/console/service/service.go | 11 ++++++----- pkg/console/service/service_test.go | 8 ++++++++ tests/proxy_test.go | 16 ++++++++++++++++ 4 files changed, 33 insertions(+), 6 deletions(-) diff --git a/api/v1alpha1/types.go b/api/v1alpha1/types.go index ae61127c..95cb268d 100644 --- a/api/v1alpha1/types.go +++ b/api/v1alpha1/types.go @@ -2,11 +2,13 @@ package v1alpha1 import ( ocpv1 "github.com/openshift/api/config/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // TokenResponse is the response object from /token endpoint. type TokenResponse struct { - Token string `json:"token"` + Token string `json:"token"` + ExpirationTimestamp metav1.Time `json:"expirationTimestamp"` } // TlsSecurityProfile is the TLS configuration for the proxy. diff --git a/pkg/console/service/service.go b/pkg/console/service/service.go index 4457ea73..66c2f6e2 100644 --- a/pkg/console/service/service.go +++ b/pkg/console/service/service.go @@ -79,14 +79,15 @@ func (s *service) TokenHandler(request *restful.Request, response *restful.Respo return } - token, err := s.requestToken(request.Request.Context(), vm.Namespace, resourceName, params.duration) + tokenRequestStatus, err := s.requestToken(request.Request.Context(), vm.Namespace, resourceName, params.duration) if err != nil { _ = response.WriteError(http.StatusInternalServerError, fmt.Errorf("failed to request token: %w", err)) return } _ = response.WriteAsJson(&v1alpha1.TokenResponse{ - Token: token, + Token: tokenRequestStatus.Token, + ExpirationTimestamp: tokenRequestStatus.ExpirationTimestamp, }) } @@ -272,7 +273,7 @@ func (s *service) createResources(ctx context.Context, name string, vm *kubevirt return nil } -func (s *service) requestToken(ctx context.Context, serviceAccountNamespace string, serviceAccountName string, duration time.Duration) (string, error) { +func (s *service) requestToken(ctx context.Context, serviceAccountNamespace string, serviceAccountName string, duration time.Duration) (*authnv1.TokenRequestStatus, error) { durationSeconds := int64(duration.Seconds()) tokenRequest := &authnv1.TokenRequest{ Spec: authnv1.TokenRequestSpec{ @@ -289,9 +290,9 @@ func (s *service) requestToken(ctx context.Context, serviceAccountNamespace stri metav1.CreateOptions{}, ) if err != nil { - return "", err + return nil, err } - return tokenRequest.Status.Token, nil + return &tokenRequest.Status, nil } type tokenRequestParams struct { diff --git a/pkg/console/service/service_test.go b/pkg/console/service/service_test.go index 01b59432..d4523c96 100644 --- a/pkg/console/service/service_test.go +++ b/pkg/console/service/service_test.go @@ -8,6 +8,7 @@ import ( "net/http/httptest" "net/url" "testing" + "time" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -51,6 +52,8 @@ var _ = Describe("Service", func() { request *restful.Request response *restful.Response recorder *httptest.ResponseRecorder + + testExpirationTimestamp metav1.Time ) BeforeEach(func() { @@ -103,10 +106,14 @@ var _ = Describe("Service", func() { return true, sar, nil }) + testExpirationTimestamp = metav1.Date(2024, 7, 1, 0, 0, 0, 0, time.UTC) + apiClient.Fake.PrependReactor("create", "serviceaccounts/token", func(action k8stesting.Action) (bool, runtime.Object, error) { createAction := action.(k8stesting.CreateAction) tokenRequest := createAction.GetObject().(*authnv1.TokenRequest) tokenRequest.Status.Token = testToken + tokenRequest.Status.ExpirationTimestamp = testExpirationTimestamp + return true, tokenRequest, nil }) @@ -281,6 +288,7 @@ var _ = Describe("Service", func() { Expect(json.NewDecoder(recorder.Body).Decode(tokenResponse)).To(Succeed()) Expect(tokenResponse.Token).To(Equal(testToken)) + Expect(&tokenResponse.ExpirationTimestamp).To(Satisfy(testExpirationTimestamp.Equal)) }) It("should fail if duration parameter fails to parse", func() { diff --git a/tests/proxy_test.go b/tests/proxy_test.go index b247ce5c..76b4029b 100644 --- a/tests/proxy_test.go +++ b/tests/proxy_test.go @@ -3,6 +3,7 @@ package tests import ( "context" "encoding/json" + "fmt" "net/http" "path" "time" @@ -117,6 +118,11 @@ var _ = Describe("Kubevirt proxy", func() { It("should get token with default duration", func() { tokenUrl := getTokenUrl(vmName) + + // Default duration is 10 minutes. + // Removing smaller intervals than seconds, because the timestamp in JSON is only accurate to seconds. + expectedExpirationTimestamp := time.Now().Add(10 * time.Minute).Truncate(time.Second) + code, body, err := httpGet(tokenUrl, saToken, TestHttpClient) Expect(err).ToNot(HaveOccurred()) Expect(code).To(Equal(http.StatusOK)) @@ -124,11 +130,18 @@ var _ = Describe("Kubevirt proxy", func() { tokenResponse := &api.TokenResponse{} Expect(json.Unmarshal(body, tokenResponse)).To(Succeed()) Expect(tokenResponse.Token).ToNot(BeEmpty()) + + Expect(tokenResponse.ExpirationTimestamp.Time.Before(expectedExpirationTimestamp)).To(BeFalse(), + fmt.Sprintf("expiration timestamp %v, should be equal or later than %v", tokenResponse.ExpirationTimestamp.Time, expectedExpirationTimestamp), + ) }) It("should get token with specified duration", func() { tokenUrl := getTokenUrl(vmName) + // Removing smaller intervals than seconds, because the timestamp in JSON is only accurate to seconds. + expectedExpirationTimestamp := time.Now().Add(24 * time.Hour).Truncate(time.Second) + code, body, err := httpGet(tokenUrl+"?duration=24h", saToken, TestHttpClient) Expect(err).ToNot(HaveOccurred()) Expect(code).To(Equal(http.StatusOK)) @@ -136,6 +149,9 @@ var _ = Describe("Kubevirt proxy", func() { tokenResponse := &api.TokenResponse{} Expect(json.Unmarshal(body, tokenResponse)).To(Succeed()) Expect(tokenResponse.Token).ToNot(BeEmpty()) + Expect(tokenResponse.ExpirationTimestamp.Time.Before(expectedExpirationTimestamp)).To(BeFalse(), + fmt.Sprintf("expiration timestamp %v, should be equal or later than %v", tokenResponse.ExpirationTimestamp.Time, expectedExpirationTimestamp), + ) claims := &jwt.RegisteredClaims{} _, _, err = jwt.NewParser().ParseUnverified(tokenResponse.Token, claims) From 1e05da73b3af4de532de09250ef91d999260402e Mon Sep 17 00:00:00 2001 From: Andrej Krejcir Date: Thu, 25 Jul 2024 16:13:35 +0200 Subject: [PATCH 2/4] chore: Simplify tlsconfig unit tests Signed-off-by: Andrej Krejcir --- pkg/console/tlsconfig/tlsconfig_test.go | 56 +++++-------------------- 1 file changed, 11 insertions(+), 45 deletions(-) diff --git a/pkg/console/tlsconfig/tlsconfig_test.go b/pkg/console/tlsconfig/tlsconfig_test.go index fb697210..987e75b3 100644 --- a/pkg/console/tlsconfig/tlsconfig_test.go +++ b/pkg/console/tlsconfig/tlsconfig_test.go @@ -149,21 +149,13 @@ var _ = Describe("TlsConfig", func() { originalConfig, err := configWatch.GetConfig() Expect(err).ToNot(HaveOccurred()) - func() { - configFile, err := os.Create(tlsConfigPath) - Expect(err).ToNot(HaveOccurred()) - defer configFile.Close() - - tlsProfile := &v1alpha1.TlsSecurityProfile{ - Type: ocpconfigv1.TLSProfileModernType, - Modern: &ocpconfigv1.ModernTLSProfile{}, - } - tlsProfileYaml, err := yaml.Marshal(tlsProfile) - Expect(err).ToNot(HaveOccurred()) - - _, err = configFile.Write(tlsProfileYaml) - Expect(err).ToNot(HaveOccurred()) - }() + tlsProfile := &v1alpha1.TlsSecurityProfile{ + Type: ocpconfigv1.TLSProfileModernType, + Modern: &ocpconfigv1.ModernTLSProfile{}, + } + tlsProfileYaml, err := yaml.Marshal(tlsProfile) + Expect(err).ToNot(HaveOccurred()) + Expect(os.WriteFile(tlsConfigPath, tlsProfileYaml, 0666)).To(Succeed()) mockWatch.Trigger(configDir) @@ -178,13 +170,7 @@ var _ = Describe("TlsConfig", func() { }) It("should fail if config is invalid", func() { - func() { - configFile, err := os.Create(tlsConfigPath) - Expect(err).ToNot(HaveOccurred()) - defer configFile.Close() - _, err = configFile.WriteString("This is definitely not a valid YAML") - Expect(err).ToNot(HaveOccurred()) - }() + Expect(os.WriteFile(tlsConfigPath, []byte("This is definitely not a valid YAML"), 0666)).To(Succeed()) mockWatch.Trigger(configDir) @@ -197,21 +183,8 @@ var _ = Describe("TlsConfig", func() { certBytes, keyBytes, err := cert.GenerateSelfSignedCertKey(newDnsName, nil, nil) Expect(err).ToNot(HaveOccurred()) - func() { - certFile, err := os.Create(certPath) - Expect(err).ToNot(HaveOccurred()) - defer certFile.Close() - - _, err = certFile.Write(certBytes) - Expect(err).ToNot(HaveOccurred()) - - keyFile, err := os.Create(keyPath) - Expect(err).ToNot(HaveOccurred()) - defer keyFile.Close() - - _, err = keyFile.Write(keyBytes) - Expect(err).ToNot(HaveOccurred()) - }() + Expect(os.WriteFile(certPath, certBytes, 0666)).To(Succeed()) + Expect(os.WriteFile(keyPath, keyBytes, 0666)).To(Succeed()) mockWatch.Trigger(certAndKeyDir) @@ -222,14 +195,7 @@ var _ = Describe("TlsConfig", func() { }) It("should fail if certificate is invalid", func() { - func() { - certFile, err := os.Create(certPath) - Expect(err).ToNot(HaveOccurred()) - defer certFile.Close() - - _, err = certFile.WriteString("This is invalid certificate file") - Expect(err).ToNot(HaveOccurred()) - }() + Expect(os.WriteFile(certPath, []byte("This is invalid certificate file"), 0666)).To(Succeed()) mockWatch.Trigger(certAndKeyDir) From 856175c6e163edf5c6e3023243492562c1a8ab97 Mon Sep 17 00:00:00 2001 From: Andrej Krejcir Date: Thu, 25 Jul 2024 16:04:46 +0200 Subject: [PATCH 3/4] api: Do not use OCP struct in the config file API The struct that is expected in the TLS config file does not use OpenShift type. This is useful to remove OpenShift dependency from the API. Signed-off-by: Andrej Krejcir --- api/v1alpha1/types.go | 22 +- go.mod | 4 +- go.sum | 4 - manifests/config_map.yaml | 4 +- pkg/console/tlsconfig/tlsconfig.go | 95 +- pkg/console/tlsconfig/tlsconfig_test.go | 91 +- tests/tlsconfig_test.go | 48 +- .../github.com/openshift/library-go/LICENSE | 201 --- .../openshift/library-go/pkg/crypto/OWNERS | 4 - .../openshift/library-go/pkg/crypto/crypto.go | 1221 ----------------- .../library-go/pkg/crypto/rotation.go | 20 - vendor/k8s.io/apiserver/LICENSE | 202 --- .../apiserver/pkg/authentication/user/doc.go | 19 - .../apiserver/pkg/authentication/user/user.go | 84 -- vendor/modules.txt | 6 - 15 files changed, 162 insertions(+), 1863 deletions(-) delete mode 100644 vendor/github.com/openshift/library-go/LICENSE delete mode 100644 vendor/github.com/openshift/library-go/pkg/crypto/OWNERS delete mode 100644 vendor/github.com/openshift/library-go/pkg/crypto/crypto.go delete mode 100644 vendor/github.com/openshift/library-go/pkg/crypto/rotation.go delete mode 100644 vendor/k8s.io/apiserver/LICENSE delete mode 100644 vendor/k8s.io/apiserver/pkg/authentication/user/doc.go delete mode 100644 vendor/k8s.io/apiserver/pkg/authentication/user/user.go diff --git a/api/v1alpha1/types.go b/api/v1alpha1/types.go index 95cb268d..37c7633b 100644 --- a/api/v1alpha1/types.go +++ b/api/v1alpha1/types.go @@ -1,7 +1,6 @@ package v1alpha1 import ( - ocpv1 "github.com/openshift/api/config/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -11,5 +10,22 @@ type TokenResponse struct { ExpirationTimestamp metav1.Time `json:"expirationTimestamp"` } -// TlsSecurityProfile is the TLS configuration for the proxy. -type TlsSecurityProfile = ocpv1.TLSSecurityProfile +// TlsProfile is the TLS configuration for the proxy. +type TlsProfile struct { + Ciphers []string `json:"ciphers,omitempty"` + MinTLSVersion TLSProtocolVersion `json:"minTLSVersion,omitempty"` +} + +// TLSProtocolVersion is a way to specify the protocol version used for TLS connections. +type TLSProtocolVersion string + +const ( + // VersionTLS10 is version 1.0 of the TLS security protocol. + VersionTLS10 TLSProtocolVersion = "VersionTLS10" + // VersionTLS11 is version 1.1 of the TLS security protocol. + VersionTLS11 TLSProtocolVersion = "VersionTLS11" + // VersionTLS12 is version 1.2 of the TLS security protocol. + VersionTLS12 TLSProtocolVersion = "VersionTLS12" + // VersionTLS13 is version 1.3 of the TLS security protocol. + VersionTLS13 TLSProtocolVersion = "VersionTLS13" +) diff --git a/go.mod b/go.mod index 03eda228..c5151870 100644 --- a/go.mod +++ b/go.mod @@ -9,8 +9,6 @@ require ( github.com/golang/mock v1.6.0 github.com/onsi/ginkgo/v2 v2.19.0 github.com/onsi/gomega v1.33.1 - github.com/openshift/api v0.0.0-20240717221938-8da8de571496 // release-4.17 - github.com/openshift/library-go v0.0.0-20240715191351-e0aa70d55678 // release-4.17 k8s.io/api v0.30.3 k8s.io/apimachinery v0.30.3 k8s.io/client-go v0.30.3 @@ -51,6 +49,7 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect + github.com/openshift/api v0.0.0-20240717221938-8da8de571496 // indirect github.com/openshift/client-go v0.0.0-20240528061634-b054aa794d87 // indirect github.com/openshift/custom-resource-status v1.1.2 // indirect github.com/pkg/errors v0.9.1 // indirect @@ -68,7 +67,6 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.30.3 // indirect - k8s.io/apiserver v0.30.3 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.30.1 // indirect kubevirt.io/containerized-data-importer-api v1.59.0 // indirect diff --git a/go.sum b/go.sum index 990b32d5..8339e305 100644 --- a/go.sum +++ b/go.sum @@ -290,8 +290,6 @@ github.com/openshift/client-go v0.0.0-20240528061634-b054aa794d87 h1:JtLhaGpSEco github.com/openshift/client-go v0.0.0-20240528061634-b054aa794d87/go.mod h1:3IPD4U0qyovZS4EFady2kqY32m8lGcbs/Wx+yprg9z8= github.com/openshift/custom-resource-status v1.1.2 h1:C3DL44LEbvlbItfd8mT5jWrqPfHnSOQoQf/sypqA6A4= github.com/openshift/custom-resource-status v1.1.2/go.mod h1:DB/Mf2oTeiAmVVX1gN+NEqweonAPY0TKUwADizj8+ZA= -github.com/openshift/library-go v0.0.0-20240715191351-e0aa70d55678 h1:H08EzrqjY63m1jlZ+D4sTy9fSGlHsPwViyxFrWTIh4A= -github.com/openshift/library-go v0.0.0-20240715191351-e0aa70d55678/go.mod h1:PdASVamWinll2BPxiUpXajTwZxV8A1pQbWEsCN1od7I= github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -683,8 +681,6 @@ k8s.io/apimachinery v0.19.0/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlm k8s.io/apimachinery v0.23.3/go.mod h1:BEuFMMBaIbcOqVIJqNZJXGFTP4W6AycEpb5+m/97hrM= k8s.io/apimachinery v0.30.3 h1:q1laaWCmrszyQuSQCfNB8cFgCuDAoPszKY4ucAjDwHc= k8s.io/apimachinery v0.30.3/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc= -k8s.io/apiserver v0.30.3 h1:QZJndA9k2MjFqpnyYv/PH+9PE0SHhx3hBho4X0vE65g= -k8s.io/apiserver v0.30.3/go.mod h1:6Oa88y1CZqnzetd2JdepO0UXzQX4ZnOekx2/PtEjrOg= k8s.io/client-go v0.19.0/go.mod h1:H9E/VT95blcFQnlyShFgnFT9ZnJOAceiUHM3MlRC+mU= k8s.io/client-go v0.30.3 h1:bHrJu3xQZNXIi8/MoxYtZBBWQQXwy16zqJwloXXfD3k= k8s.io/client-go v0.30.3/go.mod h1:8d4pf8vYu665/kUbsxWAQ/JDBNWqfFeZnvFiVdmx89U= diff --git a/manifests/config_map.yaml b/manifests/config_map.yaml index 83dde3ba..6015e97f 100644 --- a/manifests/config_map.yaml +++ b/manifests/config_map.yaml @@ -3,6 +3,4 @@ kind: ConfigMap metadata: name: vm-console-proxy data: - tls-profile-v1alpha1.yaml: | - type: Intermediate - intermediate: {} + tls-profile-v1alpha1.yaml: "{}" diff --git a/pkg/console/tlsconfig/tlsconfig.go b/pkg/console/tlsconfig/tlsconfig.go index 2f48ee18..9caffaac 100644 --- a/pkg/console/tlsconfig/tlsconfig.go +++ b/pkg/console/tlsconfig/tlsconfig.go @@ -9,8 +9,6 @@ import ( "strings" "sync" - ocpconfigv1 "github.com/openshift/api/config/v1" - "github.com/openshift/library-go/pkg/crypto" "k8s.io/apimachinery/pkg/util/yaml" "k8s.io/client-go/util/cert" "kubevirt.io/client-go/log" @@ -120,10 +118,21 @@ func (w *watch) reloadTlsProfile() { { // TODO: only compute human readable strings on debug level. // For now, there is no easy way to test for logging level. - cipherNames := crypto.CipherSuitesToNamesOrDie(ciphers) - minVersionName := crypto.TLSVersionToNameOrDie(minVersion) - log.Log.V(1).Infof("Set min TLS version: %s", minVersionName) - log.Log.V(1).Infof("Set ciphers: %s", strings.Join(cipherNames, ", ")) + if minVersion == 0 { + log.Log.V(1).Infof("Min TLS version was not set in the config file. Using default.") + } else { + log.Log.V(1).Infof("Set min TLS version: %s", tls.VersionName(minVersion)) + } + + if ciphers == nil { + log.Log.V(1).Infof("Ciphers were not set in the config file. Using default.") + } else { + cipherNames := make([]string, 0, len(ciphers)) + for _, cipher := range ciphers { + cipherNames = append(cipherNames, tls.CipherSuiteName(cipher)) + } + log.Log.V(1).Infof("Set ciphers: %s", strings.Join(cipherNames, ", ")) + } } w.ciphers = ciphers @@ -154,15 +163,20 @@ func loadCipherSuitesAndMinVersion(configPath string) ([]uint16, uint16, error) return nil, 0, fmt.Errorf("could not load tls config: %w", err) } - ciphers, minVersion, err := getTlsCiphersAndMinVersion(tlsProfile) + ciphers, err := getCipherSuites(tlsProfile.Ciphers) if err != nil { - return nil, 0, err + return nil, 0, fmt.Errorf("could not get cipher suite numbers: %w", err) + } + + minVersion, err := getMinTlsVersion(tlsProfile.MinTLSVersion) + if err != nil { + return nil, 0, fmt.Errorf("could not get minimum TLS version: %w", err) } return ciphers, minVersion, nil } -func loadTlsProfile(profilePath string) (*v1alpha1.TlsSecurityProfile, error) { +func loadTlsProfile(profilePath string) (*v1alpha1.TlsProfile, error) { file, err := os.Open(profilePath) if err != nil { return nil, fmt.Errorf("error opening file: %w", err) @@ -170,7 +184,7 @@ func loadTlsProfile(profilePath string) (*v1alpha1.TlsSecurityProfile, error) { // It's ok to ignore error on close, because the file is opened of reading defer func() { _ = file.Close() }() - result := &v1alpha1.TlsSecurityProfile{} + result := &v1alpha1.TlsProfile{} err = yaml.NewYAMLToJSONDecoder(file).Decode(result) if err != nil { return nil, fmt.Errorf("error decoding tls config: %w", err) @@ -178,44 +192,43 @@ func loadTlsProfile(profilePath string) (*v1alpha1.TlsSecurityProfile, error) { return result, nil } -func getTlsCiphersAndMinVersion(tlsProfile *v1alpha1.TlsSecurityProfile) ([]uint16, uint16, error) { - var profile *ocpconfigv1.TLSProfileSpec - if tlsProfile.Type == ocpconfigv1.TLSProfileCustomType { - if tlsProfile.Custom == nil { - return nil, 0, fmt.Errorf("tls profile \"custom\" field is nil") - } - profile = &tlsProfile.Custom.TLSProfileSpec - } else { - var exists bool - profile, exists = ocpconfigv1.TLSProfiles[tlsProfile.Type] - if !exists { - return nil, 0, fmt.Errorf("unknown profile type: %s", tlsProfile.Type) - } +func getCipherSuites(cipherNames []string) ([]uint16, error) { + if len(cipherNames) == 0 { + // nil value has means default cipher suites will be used + return nil, nil } - ciphers := getCipherSuites(profile) - minVersion, err := crypto.TLSVersion(string(profile.MinTLSVersion)) - if err != nil { - return nil, 0, err + result := make([]uint16, 0, len(cipherNames)) + +outerLoop: + for _, cipherName := range cipherNames { + for _, cipherSuite := range tls.CipherSuites() { + if cipherName == cipherSuite.Name { + result = append(result, cipherSuite.ID) + continue outerLoop + } + } + return nil, fmt.Errorf("unknown cipher suite: %v", cipherName) } - return ciphers, minVersion, nil + return result, nil } -func getCipherSuites(profileSpec *ocpconfigv1.TLSProfileSpec) []uint16 { - tlsCiphers := make(map[string]*tls.CipherSuite, len(tls.CipherSuites())) - for _, suite := range tls.CipherSuites() { - tlsCiphers[suite.Name] = suite +func getMinTlsVersion(version v1alpha1.TLSProtocolVersion) (uint16, error) { + switch version { + case "": + return 0, nil + case v1alpha1.VersionTLS10: + return tls.VersionTLS10, nil + case v1alpha1.VersionTLS11: + return tls.VersionTLS11, nil + case v1alpha1.VersionTLS12: + return tls.VersionTLS12, nil + case v1alpha1.VersionTLS13: + return tls.VersionTLS13, nil + default: + return 0, fmt.Errorf("unsupported TLS version: %s", version) } - - cipherIds := make([]uint16, 0, len(profileSpec.Ciphers)) - for _, ianaCipher := range crypto.OpenSSLToIANACipherSuites(profileSpec.Ciphers) { - if cipher, found := tlsCiphers[ianaCipher]; found { - cipherIds = append(cipherIds, cipher.ID) - } - } - - return cipherIds } func LoadCertificates(certPath, keyPath string) (*tls.Certificate, error) { diff --git a/pkg/console/tlsconfig/tlsconfig_test.go b/pkg/console/tlsconfig/tlsconfig_test.go index 987e75b3..8424fbdc 100644 --- a/pkg/console/tlsconfig/tlsconfig_test.go +++ b/pkg/console/tlsconfig/tlsconfig_test.go @@ -13,7 +13,6 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - ocpconfigv1 "github.com/openshift/api/config/v1" "k8s.io/client-go/util/cert" "sigs.k8s.io/yaml" @@ -28,6 +27,9 @@ var _ = Describe("TlsConfig", func() { ) var ( + testCiphersNames []string + testCipherIds []uint16 + configDir string tlsConfigPath string @@ -42,9 +44,27 @@ var _ = Describe("TlsConfig", func() { ) BeforeEach(func() { - tlsProfile := &v1alpha1.TlsSecurityProfile{ - Type: ocpconfigv1.TLSProfileIntermediateType, - Intermediate: &ocpconfigv1.IntermediateTLSProfile{}, + testCiphersNames = []string{ + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", + } + + testCipherIds = []uint16{ + tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + } + + tlsProfile := &v1alpha1.TlsProfile{ + Ciphers: testCiphersNames, + MinTLSVersion: v1alpha1.VersionTLS12, } tlsProfileYaml, err := yaml.Marshal(tlsProfile) Expect(err).ToNot(HaveOccurred()) @@ -101,17 +121,45 @@ var _ = Describe("TlsConfig", func() { config, err := configWatch.GetConfig() Expect(err).ToNot(HaveOccurred()) - Expect(config.CipherSuites).To(ConsistOf( - tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, - tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - )) + Expect(config.CipherSuites).To(ConsistOf(testCipherIds)) + Expect(config.MinVersion).To(Equal(uint16(tls.VersionTLS12))) + }) + + It("should use default ciphers, if ciphers are not sepcified", func() { + tlsConfig := &v1alpha1.TlsProfile{ + MinTLSVersion: v1alpha1.VersionTLS12, + } + tlsConfigYaml, err := yaml.Marshal(tlsConfig) + Expect(err).ToNot(HaveOccurred()) + Expect(os.WriteFile(tlsConfigPath, tlsConfigYaml, 0666)).To(Succeed()) + + configWatch.Reload() + + config, err := configWatch.GetConfig() + Expect(err).ToNot(HaveOccurred()) + + // Testing for nil specifically, because nil means default configuration. + Expect(config.CipherSuites).To(BeNil()) Expect(config.MinVersion).To(Equal(uint16(tls.VersionTLS12))) }) + It("should use default tls version, if MinTLSVersion is not specified", func() { + tlsConfig := &v1alpha1.TlsProfile{ + Ciphers: testCiphersNames, + } + tlsConfigYaml, err := yaml.Marshal(tlsConfig) + Expect(err).ToNot(HaveOccurred()) + Expect(os.WriteFile(tlsConfigPath, tlsConfigYaml, 0666)).To(Succeed()) + + configWatch.Reload() + + config, err := configWatch.GetConfig() + Expect(err).ToNot(HaveOccurred()) + + Expect(config.CipherSuites).To(ConsistOf(testCipherIds)) + Expect(config.MinVersion).To(BeZero()) + }) + It("should use default config if file does not exist", func() { Expect(os.Remove(tlsConfigPath)).ToNot(HaveOccurred()) configWatch.Reload() @@ -119,7 +167,8 @@ var _ = Describe("TlsConfig", func() { config, err := configWatch.GetConfig() Expect(err).ToNot(HaveOccurred()) - Expect(config.CipherSuites).To(BeEmpty()) + // Testing for nil specifically, because nil means default configuration. + Expect(config.CipherSuites).To(BeNil()) Expect(config.MinVersion).To(BeZero()) }) @@ -149,9 +198,13 @@ var _ = Describe("TlsConfig", func() { originalConfig, err := configWatch.GetConfig() Expect(err).ToNot(HaveOccurred()) - tlsProfile := &v1alpha1.TlsSecurityProfile{ - Type: ocpconfigv1.TLSProfileModernType, - Modern: &ocpconfigv1.ModernTLSProfile{}, + tlsProfile := &v1alpha1.TlsProfile{ + Ciphers: []string{ + "TLS_AES_128_GCM_SHA256", + "TLS_AES_256_GCM_SHA384", + "TLS_CHACHA20_POLY1305_SHA256", + }, + MinTLSVersion: v1alpha1.VersionTLS13, } tlsProfileYaml, err := yaml.Marshal(tlsProfile) Expect(err).ToNot(HaveOccurred()) @@ -165,7 +218,11 @@ var _ = Describe("TlsConfig", func() { Expect(config.CipherSuites).ToNot(Equal(originalConfig.CipherSuites)) Expect(config.MinVersion).ToNot(Equal(originalConfig.MinVersion)) - Expect(config.CipherSuites).To(BeEmpty()) + Expect(config.CipherSuites).To(ConsistOf( + tls.TLS_AES_128_GCM_SHA256, + tls.TLS_AES_256_GCM_SHA384, + tls.TLS_CHACHA20_POLY1305_SHA256, + )) Expect(config.MinVersion).To(Equal(uint16(tls.VersionTLS13))) }) diff --git a/tests/tlsconfig_test.go b/tests/tlsconfig_test.go index 6bb3e2e5..31e98755 100644 --- a/tests/tlsconfig_test.go +++ b/tests/tlsconfig_test.go @@ -11,7 +11,6 @@ import ( . "github.com/onsi/gomega" api "github.com/kubevirt/vm-console-proxy/api/v1alpha1" - ocpconfigv1 "github.com/openshift/api/config/v1" core "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -20,35 +19,19 @@ import ( var _ = Describe("TLS config", func() { - It("should read config from ConfigMap", func() { - Eventually(func(g Gomega) { - connState, err := getTlsConnectionState() - Expect(err).ToNot(HaveOccurred()) - - Expect(connState.CipherSuite).To(BeElementOf( - tls.TLS_AES_128_GCM_SHA256, - tls.TLS_AES_256_GCM_SHA384, - tls.TLS_CHACHA20_POLY1305_SHA256, - tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, - tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - )) - Expect(connState.Version).To(BeNumerically(">=", tls.VersionTLS12)) - }, 1*time.Minute, time.Second).Should(Succeed()) - }) - Context("with changed ConfigMap", func() { AfterEach(func() { RevertToOriginalConfigMap() }) It("should reload config at runtime", func() { - tlsProfile := &api.TlsSecurityProfile{ - Type: ocpconfigv1.TLSProfileModernType, - Modern: &ocpconfigv1.ModernTLSProfile{}, + tlsProfile := &api.TlsProfile{ + Ciphers: []string{ + "TLS_AES_128_GCM_SHA256", + "TLS_AES_256_GCM_SHA384", + "TLS_CHACHA20_POLY1305_SHA256", + }, + MinTLSVersion: api.VersionTLS13, } tlsProfileYaml, err := yaml.Marshal(tlsProfile) @@ -103,18 +86,13 @@ var _ = Describe("TLS config", func() { }, 1*time.Minute, time.Second).Should(Succeed()) // Recreate file - tlsProfile := &api.TlsSecurityProfile{ - Type: ocpconfigv1.TLSProfileCustomType, - Custom: &ocpconfigv1.CustomTLSProfile{ - TLSProfileSpec: ocpconfigv1.TLSProfileSpec{ - Ciphers: []string{ - "TLS_AES_128_GCM_SHA256", - "TLS_AES_256_GCM_SHA384", - "TLS_CHACHA20_POLY1305_SHA256", - }, - MinTLSVersion: ocpconfigv1.VersionTLS13, - }, + tlsProfile := &api.TlsProfile{ + Ciphers: []string{ + "TLS_AES_128_GCM_SHA256", + "TLS_AES_256_GCM_SHA384", + "TLS_CHACHA20_POLY1305_SHA256", }, + MinTLSVersion: api.VersionTLS13, } tlsProfileYaml, err := yaml.Marshal(tlsProfile) diff --git a/vendor/github.com/openshift/library-go/LICENSE b/vendor/github.com/openshift/library-go/LICENSE deleted file mode 100644 index 261eeb9e..00000000 --- a/vendor/github.com/openshift/library-go/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/vendor/github.com/openshift/library-go/pkg/crypto/OWNERS b/vendor/github.com/openshift/library-go/pkg/crypto/OWNERS deleted file mode 100644 index 4d4ce5ab..00000000 --- a/vendor/github.com/openshift/library-go/pkg/crypto/OWNERS +++ /dev/null @@ -1,4 +0,0 @@ -reviewers: - - stlaz -approvers: - - stlaz diff --git a/vendor/github.com/openshift/library-go/pkg/crypto/crypto.go b/vendor/github.com/openshift/library-go/pkg/crypto/crypto.go deleted file mode 100644 index 63184d2e..00000000 --- a/vendor/github.com/openshift/library-go/pkg/crypto/crypto.go +++ /dev/null @@ -1,1221 +0,0 @@ -package crypto - -import ( - "bytes" - "crypto" - "crypto/ecdsa" - "crypto/rand" - "crypto/rsa" - "crypto/sha1" - "crypto/tls" - "crypto/x509" - "crypto/x509/pkix" - "encoding/pem" - "errors" - "fmt" - "io" - "math/big" - mathrand "math/rand" - "net" - "os" - "path/filepath" - "reflect" - "sort" - "strconv" - "sync" - "time" - - "k8s.io/klog/v2" - - "k8s.io/apimachinery/pkg/util/sets" - "k8s.io/apiserver/pkg/authentication/user" - "k8s.io/client-go/util/cert" -) - -// TLS versions that are known to golang. Go 1.13 adds support for -// TLS 1.3 that's opt-out with a build flag. -var versions = map[string]uint16{ - "VersionTLS10": tls.VersionTLS10, - "VersionTLS11": tls.VersionTLS11, - "VersionTLS12": tls.VersionTLS12, - "VersionTLS13": tls.VersionTLS13, -} - -// TLS versions that are enabled. -var supportedVersions = map[string]uint16{ - "VersionTLS10": tls.VersionTLS10, - "VersionTLS11": tls.VersionTLS11, - "VersionTLS12": tls.VersionTLS12, - "VersionTLS13": tls.VersionTLS13, -} - -// TLSVersionToNameOrDie given a tls version as an int, return its readable name -func TLSVersionToNameOrDie(intVal uint16) string { - matches := []string{} - for key, version := range versions { - if version == intVal { - matches = append(matches, key) - } - } - - if len(matches) == 0 { - panic(fmt.Sprintf("no name found for %d", intVal)) - } - if len(matches) > 1 { - panic(fmt.Sprintf("multiple names found for %d: %v", intVal, matches)) - } - return matches[0] -} - -func TLSVersion(versionName string) (uint16, error) { - if len(versionName) == 0 { - return DefaultTLSVersion(), nil - } - if version, ok := versions[versionName]; ok { - return version, nil - } - return 0, fmt.Errorf("unknown tls version %q", versionName) -} -func TLSVersionOrDie(versionName string) uint16 { - version, err := TLSVersion(versionName) - if err != nil { - panic(err) - } - return version -} - -// TLS versions that are known to golang, but may not necessarily be enabled. -func GolangTLSVersions() []string { - supported := []string{} - for k := range versions { - supported = append(supported, k) - } - sort.Strings(supported) - return supported -} - -// Returns the build enabled TLS versions. -func ValidTLSVersions() []string { - validVersions := []string{} - for k := range supportedVersions { - validVersions = append(validVersions, k) - } - sort.Strings(validVersions) - return validVersions -} -func DefaultTLSVersion() uint16 { - // Can't use SSLv3 because of POODLE and BEAST - // Can't use TLSv1.0 because of POODLE and BEAST using CBC cipher - // Can't use TLSv1.1 because of RC4 cipher usage - return tls.VersionTLS12 -} - -// ciphersTLS13 copies golang 1.13 implementation, where TLS1.3 suites are not -// configurable (cipherSuites field is ignored for TLS1.3 flows and all of the -// below three - and none other - are used) -var ciphersTLS13 = map[string]uint16{ - "TLS_AES_128_GCM_SHA256": tls.TLS_AES_128_GCM_SHA256, - "TLS_AES_256_GCM_SHA384": tls.TLS_AES_256_GCM_SHA384, - "TLS_CHACHA20_POLY1305_SHA256": tls.TLS_CHACHA20_POLY1305_SHA256, -} - -var ciphers = map[string]uint16{ - "TLS_RSA_WITH_RC4_128_SHA": tls.TLS_RSA_WITH_RC4_128_SHA, - "TLS_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA, - "TLS_RSA_WITH_AES_128_CBC_SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA, - "TLS_RSA_WITH_AES_256_CBC_SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA, - "TLS_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_RSA_WITH_AES_128_CBC_SHA256, - "TLS_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256, - "TLS_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384, - "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, - "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, - "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, - "TLS_ECDHE_RSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA, - "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, - "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, - "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, - "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, - "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, - "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, -} - -// openSSLToIANACiphersMap maps OpenSSL cipher suite names to IANA names -// ref: https://www.iana.org/assignments/tls-parameters/tls-parameters.xml -var openSSLToIANACiphersMap = map[string]string{ - // TLS 1.3 ciphers - not configurable in go 1.13, all of them are used in TLSv1.3 flows - // "TLS_AES_128_GCM_SHA256": "TLS_AES_128_GCM_SHA256", // 0x13,0x01 - // "TLS_AES_256_GCM_SHA384": "TLS_AES_256_GCM_SHA384", // 0x13,0x02 - // "TLS_CHACHA20_POLY1305_SHA256": "TLS_CHACHA20_POLY1305_SHA256", // 0x13,0x03 - - // TLS 1.2 - "ECDHE-ECDSA-AES128-GCM-SHA256": "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", // 0xC0,0x2B - "ECDHE-RSA-AES128-GCM-SHA256": "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", // 0xC0,0x2F - "ECDHE-ECDSA-AES256-GCM-SHA384": "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", // 0xC0,0x2C - "ECDHE-RSA-AES256-GCM-SHA384": "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", // 0xC0,0x30 - "ECDHE-ECDSA-CHACHA20-POLY1305": "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", // 0xCC,0xA9 - "ECDHE-RSA-CHACHA20-POLY1305": "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", // 0xCC,0xA8 - "ECDHE-ECDSA-AES128-SHA256": "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", // 0xC0,0x23 - "ECDHE-RSA-AES128-SHA256": "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", // 0xC0,0x27 - "AES128-GCM-SHA256": "TLS_RSA_WITH_AES_128_GCM_SHA256", // 0x00,0x9C - "AES256-GCM-SHA384": "TLS_RSA_WITH_AES_256_GCM_SHA384", // 0x00,0x9D - "AES128-SHA256": "TLS_RSA_WITH_AES_128_CBC_SHA256", // 0x00,0x3C - - // TLS 1 - "ECDHE-ECDSA-AES128-SHA": "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", // 0xC0,0x09 - "ECDHE-RSA-AES128-SHA": "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", // 0xC0,0x13 - "ECDHE-ECDSA-AES256-SHA": "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", // 0xC0,0x0A - "ECDHE-RSA-AES256-SHA": "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", // 0xC0,0x14 - - // SSL 3 - "AES128-SHA": "TLS_RSA_WITH_AES_128_CBC_SHA", // 0x00,0x2F - "AES256-SHA": "TLS_RSA_WITH_AES_256_CBC_SHA", // 0x00,0x35 - "DES-CBC3-SHA": "TLS_RSA_WITH_3DES_EDE_CBC_SHA", // 0x00,0x0A -} - -// CipherSuitesToNamesOrDie given a list of cipher suites as ints, return their readable names -func CipherSuitesToNamesOrDie(intVals []uint16) []string { - ret := []string{} - for _, intVal := range intVals { - ret = append(ret, CipherSuiteToNameOrDie(intVal)) - } - - return ret -} - -// CipherSuiteToNameOrDie given a cipher suite as an int, return its readable name -func CipherSuiteToNameOrDie(intVal uint16) string { - // The following suite ids appear twice in the cipher map (with - // and without the _SHA256 suffix) for the purposes of backwards - // compatibility. Always return the current rather than the legacy - // name. - switch intVal { - case tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: - return "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256" - case tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: - return "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256" - } - - matches := []string{} - for key, version := range ciphers { - if version == intVal { - matches = append(matches, key) - } - } - - if len(matches) == 0 { - panic(fmt.Sprintf("no name found for %d", intVal)) - } - if len(matches) > 1 { - panic(fmt.Sprintf("multiple names found for %d: %v", intVal, matches)) - } - return matches[0] -} - -func CipherSuite(cipherName string) (uint16, error) { - if cipher, ok := ciphers[cipherName]; ok { - return cipher, nil - } - - if _, ok := ciphersTLS13[cipherName]; ok { - return 0, fmt.Errorf("all golang TLSv1.3 ciphers are always used for TLSv1.3 flows") - } - - return 0, fmt.Errorf("unknown cipher name %q", cipherName) -} - -func CipherSuitesOrDie(cipherNames []string) []uint16 { - if len(cipherNames) == 0 { - return DefaultCiphers() - } - cipherValues := []uint16{} - for _, cipherName := range cipherNames { - cipher, err := CipherSuite(cipherName) - if err != nil { - panic(err) - } - cipherValues = append(cipherValues, cipher) - } - return cipherValues -} -func ValidCipherSuites() []string { - validCipherSuites := []string{} - for k := range ciphers { - validCipherSuites = append(validCipherSuites, k) - } - sort.Strings(validCipherSuites) - return validCipherSuites -} -func DefaultCiphers() []uint16 { - // HTTP/2 mandates TLS 1.2 or higher with an AEAD cipher - // suite (GCM, Poly1305) and ephemeral key exchange (ECDHE, DHE) for - // perfect forward secrecy. Servers may provide additional cipher - // suites for backwards compatibility with HTTP/1.1 clients. - // See RFC7540, section 9.2 (Use of TLS Features) and Appendix A - // (TLS 1.2 Cipher Suite Black List). - return []uint16{ - tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, - tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, // required by http/2 - tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, // forbidden by http/2, not flagged by http2isBadCipher() in go1.8 - tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, // forbidden by http/2, not flagged by http2isBadCipher() in go1.8 - tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, // forbidden by http/2 - tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, // forbidden by http/2 - tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, // forbidden by http/2 - tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, // forbidden by http/2 - tls.TLS_RSA_WITH_AES_128_GCM_SHA256, // forbidden by http/2 - tls.TLS_RSA_WITH_AES_256_GCM_SHA384, // forbidden by http/2 - // the next one is in the intermediate suite, but go1.8 http2isBadCipher() complains when it is included at the recommended index - // because it comes after ciphers forbidden by the http/2 spec - // tls.TLS_RSA_WITH_AES_128_CBC_SHA256, - // tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, // forbidden by http/2, disabled to mitigate SWEET32 attack - // tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA, // forbidden by http/2, disabled to mitigate SWEET32 attack - tls.TLS_RSA_WITH_AES_128_CBC_SHA, // forbidden by http/2 - tls.TLS_RSA_WITH_AES_256_CBC_SHA, // forbidden by http/2 - } -} - -// SecureTLSConfig enforces the default minimum security settings for the cluster. -func SecureTLSConfig(config *tls.Config) *tls.Config { - if config.MinVersion == 0 { - config.MinVersion = DefaultTLSVersion() - } - - config.PreferServerCipherSuites = true - if len(config.CipherSuites) == 0 { - config.CipherSuites = DefaultCiphers() - } - return config -} - -// OpenSSLToIANACipherSuites maps input OpenSSL Cipher Suite names to their -// IANA counterparts. -// Unknown ciphers are left out. -func OpenSSLToIANACipherSuites(ciphers []string) []string { - ianaCiphers := make([]string, 0, len(ciphers)) - - for _, c := range ciphers { - ianaCipher, found := openSSLToIANACiphersMap[c] - if found { - ianaCiphers = append(ianaCiphers, ianaCipher) - } - } - - return ianaCiphers -} - -type TLSCertificateConfig struct { - Certs []*x509.Certificate - Key crypto.PrivateKey -} - -type TLSCARoots struct { - Roots []*x509.Certificate -} - -func (c *TLSCertificateConfig) WriteCertConfigFile(certFile, keyFile string) error { - // ensure parent dir - if err := os.MkdirAll(filepath.Dir(certFile), os.FileMode(0755)); err != nil { - return err - } - certFileWriter, err := os.OpenFile(certFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) - if err != nil { - return err - } - if err := os.MkdirAll(filepath.Dir(keyFile), os.FileMode(0755)); err != nil { - return err - } - keyFileWriter, err := os.OpenFile(keyFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) - if err != nil { - return err - } - - if err := writeCertificates(certFileWriter, c.Certs...); err != nil { - return err - } - if err := writeKeyFile(keyFileWriter, c.Key); err != nil { - return err - } - - if err := certFileWriter.Close(); err != nil { - return err - } - if err := keyFileWriter.Close(); err != nil { - return err - } - - return nil -} - -func (c *TLSCertificateConfig) WriteCertConfig(certFile, keyFile io.Writer) error { - if err := writeCertificates(certFile, c.Certs...); err != nil { - return err - } - if err := writeKeyFile(keyFile, c.Key); err != nil { - return err - } - return nil -} - -func (c *TLSCertificateConfig) GetPEMBytes() ([]byte, []byte, error) { - certBytes, err := EncodeCertificates(c.Certs...) - if err != nil { - return nil, nil, err - } - keyBytes, err := EncodeKey(c.Key) - if err != nil { - return nil, nil, err - } - - return certBytes, keyBytes, nil -} - -func GetTLSCertificateConfig(certFile, keyFile string) (*TLSCertificateConfig, error) { - if len(certFile) == 0 { - return nil, errors.New("certFile missing") - } - if len(keyFile) == 0 { - return nil, errors.New("keyFile missing") - } - - certPEMBlock, err := os.ReadFile(certFile) - if err != nil { - return nil, err - } - certs, err := cert.ParseCertsPEM(certPEMBlock) - if err != nil { - return nil, fmt.Errorf("Error reading %s: %s", certFile, err) - } - - keyPEMBlock, err := os.ReadFile(keyFile) - if err != nil { - return nil, err - } - keyPairCert, err := tls.X509KeyPair(certPEMBlock, keyPEMBlock) - if err != nil { - return nil, err - } - key := keyPairCert.PrivateKey - - return &TLSCertificateConfig{certs, key}, nil -} - -func GetTLSCertificateConfigFromBytes(certBytes, keyBytes []byte) (*TLSCertificateConfig, error) { - if len(certBytes) == 0 { - return nil, errors.New("certFile missing") - } - if len(keyBytes) == 0 { - return nil, errors.New("keyFile missing") - } - - certs, err := cert.ParseCertsPEM(certBytes) - if err != nil { - return nil, fmt.Errorf("Error reading cert: %s", err) - } - - keyPairCert, err := tls.X509KeyPair(certBytes, keyBytes) - if err != nil { - return nil, err - } - key := keyPairCert.PrivateKey - - return &TLSCertificateConfig{certs, key}, nil -} - -const ( - DefaultCertificateLifetimeInDays = 365 * 2 // 2 years - DefaultCACertificateLifetimeInDays = 365 * 5 // 5 years - - // Default keys are 2048 bits - keyBits = 2048 -) - -type CA struct { - Config *TLSCertificateConfig - - SerialGenerator SerialGenerator -} - -// SerialGenerator is an interface for getting a serial number for the cert. It MUST be thread-safe. -type SerialGenerator interface { - Next(template *x509.Certificate) (int64, error) -} - -// SerialFileGenerator returns a unique, monotonically increasing serial number and ensures the CA on disk records that value. -type SerialFileGenerator struct { - SerialFile string - - // lock guards access to the Serial field - lock sync.Mutex - Serial int64 -} - -func NewSerialFileGenerator(serialFile string) (*SerialFileGenerator, error) { - // read serial file, it must already exist - serial, err := fileToSerial(serialFile) - if err != nil { - return nil, err - } - - generator := &SerialFileGenerator{ - Serial: serial, - SerialFile: serialFile, - } - - // 0 is unused and 1 is reserved for the CA itself - // Thus we need to guarantee that the first external call to SerialFileGenerator.Next returns 2+ - // meaning that SerialFileGenerator.Serial must not be less than 1 (it is guaranteed to be non-negative) - if generator.Serial < 1 { - // fake a call to Next so the file stays in sync and Serial is incremented - if _, err := generator.Next(&x509.Certificate{}); err != nil { - return nil, err - } - } - - return generator, nil -} - -// Next returns a unique, monotonically increasing serial number and ensures the CA on disk records that value. -func (s *SerialFileGenerator) Next(template *x509.Certificate) (int64, error) { - s.lock.Lock() - defer s.lock.Unlock() - - // do a best effort check to make sure concurrent external writes are not occurring to the underlying serial file - serial, err := fileToSerial(s.SerialFile) - if err != nil { - return 0, err - } - if serial != s.Serial { - return 0, fmt.Errorf("serial file %s out of sync ram=%d disk=%d", s.SerialFile, s.Serial, serial) - } - - next := s.Serial + 1 - s.Serial = next - - // Output in hex, padded to multiples of two characters for OpenSSL's sake - serialText := fmt.Sprintf("%X", next) - if len(serialText)%2 == 1 { - serialText = "0" + serialText - } - // always add a newline at the end to have a valid file - serialText += "\n" - - if err := os.WriteFile(s.SerialFile, []byte(serialText), os.FileMode(0640)); err != nil { - return 0, err - } - return next, nil -} - -func fileToSerial(serialFile string) (int64, error) { - serialData, err := os.ReadFile(serialFile) - if err != nil { - return 0, err - } - - // read the file as a single hex number after stripping any whitespace - serial, err := strconv.ParseInt(string(bytes.TrimSpace(serialData)), 16, 64) - if err != nil { - return 0, err - } - - if serial < 0 { - return 0, fmt.Errorf("invalid negative serial %d in serial file %s", serial, serialFile) - } - - return serial, nil -} - -// RandomSerialGenerator returns a serial based on time.Now and the subject -type RandomSerialGenerator struct { -} - -func (s *RandomSerialGenerator) Next(template *x509.Certificate) (int64, error) { - return randomSerialNumber(), nil -} - -// randomSerialNumber returns a random int64 serial number based on -// time.Now. It is defined separately from the generator interface so -// that the caller doesn't have to worry about an input template or -// error - these are unnecessary when creating a random serial. -func randomSerialNumber() int64 { - r := mathrand.New(mathrand.NewSource(time.Now().UTC().UnixNano())) - return r.Int63() -} - -// EnsureCA returns a CA, whether it was created (as opposed to pre-existing), and any error -// if serialFile is empty, a RandomSerialGenerator will be used -func EnsureCA(certFile, keyFile, serialFile, name string, expireDays int) (*CA, bool, error) { - if ca, err := GetCA(certFile, keyFile, serialFile); err == nil { - return ca, false, err - } - ca, err := MakeSelfSignedCA(certFile, keyFile, serialFile, name, expireDays) - return ca, true, err -} - -// if serialFile is empty, a RandomSerialGenerator will be used -func GetCA(certFile, keyFile, serialFile string) (*CA, error) { - caConfig, err := GetTLSCertificateConfig(certFile, keyFile) - if err != nil { - return nil, err - } - - var serialGenerator SerialGenerator - if len(serialFile) > 0 { - serialGenerator, err = NewSerialFileGenerator(serialFile) - if err != nil { - return nil, err - } - } else { - serialGenerator = &RandomSerialGenerator{} - } - - return &CA{ - SerialGenerator: serialGenerator, - Config: caConfig, - }, nil -} - -func GetCAFromBytes(certBytes, keyBytes []byte) (*CA, error) { - caConfig, err := GetTLSCertificateConfigFromBytes(certBytes, keyBytes) - if err != nil { - return nil, err - } - - return &CA{ - SerialGenerator: &RandomSerialGenerator{}, - Config: caConfig, - }, nil -} - -// if serialFile is empty, a RandomSerialGenerator will be used -func MakeSelfSignedCA(certFile, keyFile, serialFile, name string, expireDays int) (*CA, error) { - klog.V(2).Infof("Generating new CA for %s cert, and key in %s, %s", name, certFile, keyFile) - - caConfig, err := MakeSelfSignedCAConfig(name, expireDays) - if err != nil { - return nil, err - } - if err := caConfig.WriteCertConfigFile(certFile, keyFile); err != nil { - return nil, err - } - - var serialGenerator SerialGenerator - if len(serialFile) > 0 { - // create / overwrite the serial file with a zero padded hex value (ending in a newline to have a valid file) - if err := os.WriteFile(serialFile, []byte("00\n"), 0644); err != nil { - return nil, err - } - serialGenerator, err = NewSerialFileGenerator(serialFile) - if err != nil { - return nil, err - } - } else { - serialGenerator = &RandomSerialGenerator{} - } - - return &CA{ - SerialGenerator: serialGenerator, - Config: caConfig, - }, nil -} - -func MakeSelfSignedCAConfig(name string, expireDays int) (*TLSCertificateConfig, error) { - subject := pkix.Name{CommonName: name} - return MakeSelfSignedCAConfigForSubject(subject, expireDays) -} - -func MakeSelfSignedCAConfigForSubject(subject pkix.Name, expireDays int) (*TLSCertificateConfig, error) { - var caLifetimeInDays = DefaultCACertificateLifetimeInDays - if expireDays > 0 { - caLifetimeInDays = expireDays - } - - if caLifetimeInDays > DefaultCACertificateLifetimeInDays { - warnAboutCertificateLifeTime(subject.CommonName, DefaultCACertificateLifetimeInDays) - } - - caLifetime := time.Duration(caLifetimeInDays) * 24 * time.Hour - return makeSelfSignedCAConfigForSubjectAndDuration(subject, time.Now, caLifetime) -} - -func MakeSelfSignedCAConfigForDuration(name string, caLifetime time.Duration) (*TLSCertificateConfig, error) { - subject := pkix.Name{CommonName: name} - return makeSelfSignedCAConfigForSubjectAndDuration(subject, time.Now, caLifetime) -} - -func UnsafeMakeSelfSignedCAConfigForDurationAtTime(name string, currentTime func() time.Time, caLifetime time.Duration) (*TLSCertificateConfig, error) { - subject := pkix.Name{CommonName: name} - return makeSelfSignedCAConfigForSubjectAndDuration(subject, currentTime, caLifetime) -} - -func makeSelfSignedCAConfigForSubjectAndDuration(subject pkix.Name, currentTime func() time.Time, caLifetime time.Duration) (*TLSCertificateConfig, error) { - // Create CA cert - rootcaPublicKey, rootcaPrivateKey, publicKeyHash, err := newKeyPairWithHash() - if err != nil { - return nil, err - } - // AuthorityKeyId and SubjectKeyId should match for a self-signed CA - authorityKeyId := publicKeyHash - subjectKeyId := publicKeyHash - rootcaTemplate := newSigningCertificateTemplateForDuration(subject, caLifetime, currentTime, authorityKeyId, subjectKeyId) - rootcaCert, err := signCertificate(rootcaTemplate, rootcaPublicKey, rootcaTemplate, rootcaPrivateKey) - if err != nil { - return nil, err - } - caConfig := &TLSCertificateConfig{ - Certs: []*x509.Certificate{rootcaCert}, - Key: rootcaPrivateKey, - } - return caConfig, nil -} - -func MakeCAConfigForDuration(name string, caLifetime time.Duration, issuer *CA) (*TLSCertificateConfig, error) { - // Create CA cert - signerPublicKey, signerPrivateKey, publicKeyHash, err := newKeyPairWithHash() - if err != nil { - return nil, err - } - authorityKeyId := issuer.Config.Certs[0].SubjectKeyId - subjectKeyId := publicKeyHash - signerTemplate := newSigningCertificateTemplateForDuration(pkix.Name{CommonName: name}, caLifetime, time.Now, authorityKeyId, subjectKeyId) - signerCert, err := issuer.SignCertificate(signerTemplate, signerPublicKey) - if err != nil { - return nil, err - } - signerConfig := &TLSCertificateConfig{ - Certs: append([]*x509.Certificate{signerCert}, issuer.Config.Certs...), - Key: signerPrivateKey, - } - return signerConfig, nil -} - -// EnsureSubCA returns a subCA signed by the `ca`, whether it was created -// (as opposed to pre-existing), and any error that might occur during the subCA -// creation. -// If serialFile is an empty string, a RandomSerialGenerator will be used. -func (ca *CA) EnsureSubCA(certFile, keyFile, serialFile, name string, expireDays int) (*CA, bool, error) { - if subCA, err := GetCA(certFile, keyFile, serialFile); err == nil { - return subCA, false, err - } - subCA, err := ca.MakeAndWriteSubCA(certFile, keyFile, serialFile, name, expireDays) - return subCA, true, err -} - -// MakeAndWriteSubCA returns a new sub-CA configuration. New cert/key pair is generated -// while using this function. -// If serialFile is an empty string, a RandomSerialGenerator will be used. -func (ca *CA) MakeAndWriteSubCA(certFile, keyFile, serialFile, name string, expireDays int) (*CA, error) { - klog.V(4).Infof("Generating sub-CA certificate in %s, key in %s, serial in %s", certFile, keyFile, serialFile) - - subCAConfig, err := MakeCAConfigForDuration(name, time.Duration(expireDays)*time.Hour*24, ca) - if err != nil { - return nil, err - } - - if err := subCAConfig.WriteCertConfigFile(certFile, keyFile); err != nil { - return nil, err - } - - var serialGenerator SerialGenerator - if len(serialFile) > 0 { - // create / overwrite the serial file with a zero padded hex value (ending in a newline to have a valid file) - if err := os.WriteFile(serialFile, []byte("00\n"), 0644); err != nil { - return nil, err - } - - serialGenerator, err = NewSerialFileGenerator(serialFile) - if err != nil { - return nil, err - } - } else { - serialGenerator = &RandomSerialGenerator{} - } - - return &CA{ - Config: subCAConfig, - SerialGenerator: serialGenerator, - }, nil -} - -func (ca *CA) EnsureServerCert(certFile, keyFile string, hostnames sets.Set[string], expireDays int) (*TLSCertificateConfig, bool, error) { - certConfig, err := GetServerCert(certFile, keyFile, hostnames) - if err != nil { - certConfig, err = ca.MakeAndWriteServerCert(certFile, keyFile, hostnames, expireDays) - return certConfig, true, err - } - - return certConfig, false, nil -} - -func GetServerCert(certFile, keyFile string, hostnames sets.Set[string]) (*TLSCertificateConfig, error) { - server, err := GetTLSCertificateConfig(certFile, keyFile) - if err != nil { - return nil, err - } - - cert := server.Certs[0] - certNames := sets.New[string]() - for _, ip := range cert.IPAddresses { - certNames.Insert(ip.String()) - } - certNames.Insert(cert.DNSNames...) - if hostnames.Equal(certNames) { - klog.V(4).Infof("Found existing server certificate in %s", certFile) - return server, nil - } - - return nil, fmt.Errorf("Existing server certificate in %s does not match required hostnames.", certFile) -} - -func (ca *CA) MakeAndWriteServerCert(certFile, keyFile string, hostnames sets.Set[string], expireDays int) (*TLSCertificateConfig, error) { - klog.V(4).Infof("Generating server certificate in %s, key in %s", certFile, keyFile) - - server, err := ca.MakeServerCert(hostnames, expireDays) - if err != nil { - return nil, err - } - if err := server.WriteCertConfigFile(certFile, keyFile); err != nil { - return server, err - } - return server, nil -} - -// CertificateExtensionFunc is passed a certificate that it may extend, or return an error -// if the extension attempt failed. -type CertificateExtensionFunc func(*x509.Certificate) error - -func (ca *CA) MakeServerCert(hostnames sets.Set[string], expireDays int, fns ...CertificateExtensionFunc) (*TLSCertificateConfig, error) { - serverPublicKey, serverPrivateKey, publicKeyHash, _ := newKeyPairWithHash() - authorityKeyId := ca.Config.Certs[0].SubjectKeyId - subjectKeyId := publicKeyHash - serverTemplate := newServerCertificateTemplate(pkix.Name{CommonName: sets.List(hostnames)[0]}, sets.List(hostnames), expireDays, time.Now, authorityKeyId, subjectKeyId) - for _, fn := range fns { - if err := fn(serverTemplate); err != nil { - return nil, err - } - } - serverCrt, err := ca.SignCertificate(serverTemplate, serverPublicKey) - if err != nil { - return nil, err - } - server := &TLSCertificateConfig{ - Certs: append([]*x509.Certificate{serverCrt}, ca.Config.Certs...), - Key: serverPrivateKey, - } - return server, nil -} - -func (ca *CA) MakeServerCertForDuration(hostnames sets.Set[string], lifetime time.Duration, fns ...CertificateExtensionFunc) (*TLSCertificateConfig, error) { - serverPublicKey, serverPrivateKey, publicKeyHash, _ := newKeyPairWithHash() - authorityKeyId := ca.Config.Certs[0].SubjectKeyId - subjectKeyId := publicKeyHash - serverTemplate := newServerCertificateTemplateForDuration(pkix.Name{CommonName: sets.List(hostnames)[0]}, sets.List(hostnames), lifetime, time.Now, authorityKeyId, subjectKeyId) - for _, fn := range fns { - if err := fn(serverTemplate); err != nil { - return nil, err - } - } - serverCrt, err := ca.SignCertificate(serverTemplate, serverPublicKey) - if err != nil { - return nil, err - } - server := &TLSCertificateConfig{ - Certs: append([]*x509.Certificate{serverCrt}, ca.Config.Certs...), - Key: serverPrivateKey, - } - return server, nil -} - -func (ca *CA) EnsureClientCertificate(certFile, keyFile string, u user.Info, expireDays int) (*TLSCertificateConfig, bool, error) { - certConfig, err := GetClientCertificate(certFile, keyFile, u) - if err != nil { - certConfig, err = ca.MakeClientCertificate(certFile, keyFile, u, expireDays) - return certConfig, true, err // true indicates we wrote the files. - } - return certConfig, false, nil -} - -func GetClientCertificate(certFile, keyFile string, u user.Info) (*TLSCertificateConfig, error) { - certConfig, err := GetTLSCertificateConfig(certFile, keyFile) - if err != nil { - return nil, err - } - - if subject := certConfig.Certs[0].Subject; subjectChanged(subject, UserToSubject(u)) { - return nil, fmt.Errorf("existing client certificate in %s was issued for a different Subject (%s)", - certFile, subject) - } - - return certConfig, nil -} - -func subjectChanged(existing, expected pkix.Name) bool { - sort.Strings(existing.Organization) - sort.Strings(expected.Organization) - - return existing.CommonName != expected.CommonName || - existing.SerialNumber != expected.SerialNumber || - !reflect.DeepEqual(existing.Organization, expected.Organization) -} - -func (ca *CA) MakeClientCertificate(certFile, keyFile string, u user.Info, expireDays int) (*TLSCertificateConfig, error) { - klog.V(4).Infof("Generating client cert in %s and key in %s", certFile, keyFile) - // ensure parent dirs - if err := os.MkdirAll(filepath.Dir(certFile), os.FileMode(0755)); err != nil { - return nil, err - } - if err := os.MkdirAll(filepath.Dir(keyFile), os.FileMode(0755)); err != nil { - return nil, err - } - - clientPublicKey, clientPrivateKey, _ := NewKeyPair() - clientTemplate := NewClientCertificateTemplate(UserToSubject(u), expireDays, time.Now) - clientCrt, err := ca.SignCertificate(clientTemplate, clientPublicKey) - if err != nil { - return nil, err - } - - certData, err := EncodeCertificates(clientCrt) - if err != nil { - return nil, err - } - keyData, err := EncodeKey(clientPrivateKey) - if err != nil { - return nil, err - } - - if err = os.WriteFile(certFile, certData, os.FileMode(0644)); err != nil { - return nil, err - } - if err = os.WriteFile(keyFile, keyData, os.FileMode(0600)); err != nil { - return nil, err - } - - return GetTLSCertificateConfig(certFile, keyFile) -} - -func (ca *CA) MakeClientCertificateForDuration(u user.Info, lifetime time.Duration) (*TLSCertificateConfig, error) { - clientPublicKey, clientPrivateKey, _ := NewKeyPair() - clientTemplate := NewClientCertificateTemplateForDuration(UserToSubject(u), lifetime, time.Now) - clientCrt, err := ca.SignCertificate(clientTemplate, clientPublicKey) - if err != nil { - return nil, err - } - - certData, err := EncodeCertificates(clientCrt) - if err != nil { - return nil, err - } - keyData, err := EncodeKey(clientPrivateKey) - if err != nil { - return nil, err - } - - return GetTLSCertificateConfigFromBytes(certData, keyData) -} - -type sortedForDER []string - -func (s sortedForDER) Len() int { - return len(s) -} -func (s sortedForDER) Swap(i, j int) { - s[i], s[j] = s[j], s[i] -} -func (s sortedForDER) Less(i, j int) bool { - l1 := len(s[i]) - l2 := len(s[j]) - if l1 == l2 { - return s[i] < s[j] - } - return l1 < l2 -} - -func UserToSubject(u user.Info) pkix.Name { - // Ok we are going to order groups in a peculiar way here to workaround a - // 2 bugs, 1 in golang (https://github.com/golang/go/issues/24254) which - // incorrectly encodes Multivalued RDNs and another in GNUTLS clients - // which are too picky (https://gitlab.com/gnutls/gnutls/issues/403) - // and try to "correct" this issue when reading client certs. - // - // This workaround should be killed once Golang's pkix module is fixed to - // generate a correct DER encoding. - // - // The workaround relies on the fact that the first octect that differs - // between the encoding of two group RDNs will end up being the encoded - // length which is directly related to the group name's length. So we'll - // sort such that shortest names come first. - ugroups := u.GetGroups() - groups := make([]string, len(ugroups)) - copy(groups, ugroups) - sort.Sort(sortedForDER(groups)) - - return pkix.Name{ - CommonName: u.GetName(), - SerialNumber: u.GetUID(), - Organization: groups, - } -} - -func (ca *CA) SignCertificate(template *x509.Certificate, requestKey crypto.PublicKey) (*x509.Certificate, error) { - // Increment and persist serial - serial, err := ca.SerialGenerator.Next(template) - if err != nil { - return nil, err - } - template.SerialNumber = big.NewInt(serial) - return signCertificate(template, requestKey, ca.Config.Certs[0], ca.Config.Key) -} - -func NewKeyPair() (crypto.PublicKey, crypto.PrivateKey, error) { - return newRSAKeyPair() -} - -func newKeyPairWithHash() (crypto.PublicKey, crypto.PrivateKey, []byte, error) { - publicKey, privateKey, err := newRSAKeyPair() - var publicKeyHash []byte - if err == nil { - hash := sha1.New() - hash.Write(publicKey.N.Bytes()) - publicKeyHash = hash.Sum(nil) - } - return publicKey, privateKey, publicKeyHash, err -} - -func newRSAKeyPair() (*rsa.PublicKey, *rsa.PrivateKey, error) { - privateKey, err := rsa.GenerateKey(rand.Reader, keyBits) - if err != nil { - return nil, nil, err - } - return &privateKey.PublicKey, privateKey, nil -} - -// Can be used for CA or intermediate signing certs -func newSigningCertificateTemplateForDuration(subject pkix.Name, caLifetime time.Duration, currentTime func() time.Time, authorityKeyId, subjectKeyId []byte) *x509.Certificate { - return &x509.Certificate{ - Subject: subject, - - SignatureAlgorithm: x509.SHA256WithRSA, - - NotBefore: currentTime().Add(-1 * time.Second), - NotAfter: currentTime().Add(caLifetime), - - // Specify a random serial number to avoid the same issuer+serial - // number referring to different certs in a chain of trust if the - // signing certificate is ever rotated. - SerialNumber: big.NewInt(randomSerialNumber()), - - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, - BasicConstraintsValid: true, - IsCA: true, - - AuthorityKeyId: authorityKeyId, - SubjectKeyId: subjectKeyId, - } -} - -// Can be used for ListenAndServeTLS -func newServerCertificateTemplate(subject pkix.Name, hosts []string, expireDays int, currentTime func() time.Time, authorityKeyId, subjectKeyId []byte) *x509.Certificate { - var lifetimeInDays = DefaultCertificateLifetimeInDays - if expireDays > 0 { - lifetimeInDays = expireDays - } - - if lifetimeInDays > DefaultCertificateLifetimeInDays { - warnAboutCertificateLifeTime(subject.CommonName, DefaultCertificateLifetimeInDays) - } - - lifetime := time.Duration(lifetimeInDays) * 24 * time.Hour - - return newServerCertificateTemplateForDuration(subject, hosts, lifetime, currentTime, authorityKeyId, subjectKeyId) -} - -// Can be used for ListenAndServeTLS -func newServerCertificateTemplateForDuration(subject pkix.Name, hosts []string, lifetime time.Duration, currentTime func() time.Time, authorityKeyId, subjectKeyId []byte) *x509.Certificate { - template := &x509.Certificate{ - Subject: subject, - - SignatureAlgorithm: x509.SHA256WithRSA, - - NotBefore: currentTime().Add(-1 * time.Second), - NotAfter: currentTime().Add(lifetime), - SerialNumber: big.NewInt(1), - - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, - BasicConstraintsValid: true, - - AuthorityKeyId: authorityKeyId, - SubjectKeyId: subjectKeyId, - } - - template.IPAddresses, template.DNSNames = IPAddressesDNSNames(hosts) - - return template -} - -func IPAddressesDNSNames(hosts []string) ([]net.IP, []string) { - ips := []net.IP{} - dns := []string{} - for _, host := range hosts { - if ip := net.ParseIP(host); ip != nil { - ips = append(ips, ip) - } else { - dns = append(dns, host) - } - } - - // Include IP addresses as DNS subjectAltNames in the cert as well, for the sake of Python, Windows (< 10), and unnamed other libraries - // Ensure these technically invalid DNS subjectAltNames occur after the valid ones, to avoid triggering cert errors in Firefox - // See https://bugzilla.mozilla.org/show_bug.cgi?id=1148766 - for _, ip := range ips { - dns = append(dns, ip.String()) - } - - return ips, dns -} - -func CertsFromPEM(pemCerts []byte) ([]*x509.Certificate, error) { - ok := false - certs := []*x509.Certificate{} - for len(pemCerts) > 0 { - var block *pem.Block - block, pemCerts = pem.Decode(pemCerts) - if block == nil { - break - } - if block.Type != "CERTIFICATE" || len(block.Headers) != 0 { - continue - } - - cert, err := x509.ParseCertificate(block.Bytes) - if err != nil { - return certs, err - } - - certs = append(certs, cert) - ok = true - } - - if !ok { - return certs, errors.New("Could not read any certificates") - } - return certs, nil -} - -// Can be used as a certificate in http.Transport TLSClientConfig -func NewClientCertificateTemplate(subject pkix.Name, expireDays int, currentTime func() time.Time) *x509.Certificate { - var lifetimeInDays = DefaultCertificateLifetimeInDays - if expireDays > 0 { - lifetimeInDays = expireDays - } - - if lifetimeInDays > DefaultCertificateLifetimeInDays { - warnAboutCertificateLifeTime(subject.CommonName, DefaultCertificateLifetimeInDays) - } - - lifetime := time.Duration(lifetimeInDays) * 24 * time.Hour - - return NewClientCertificateTemplateForDuration(subject, lifetime, currentTime) -} - -// Can be used as a certificate in http.Transport TLSClientConfig -func NewClientCertificateTemplateForDuration(subject pkix.Name, lifetime time.Duration, currentTime func() time.Time) *x509.Certificate { - return &x509.Certificate{ - Subject: subject, - - SignatureAlgorithm: x509.SHA256WithRSA, - - NotBefore: currentTime().Add(-1 * time.Second), - NotAfter: currentTime().Add(lifetime), - SerialNumber: big.NewInt(1), - - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, - BasicConstraintsValid: true, - } -} - -func warnAboutCertificateLifeTime(name string, defaultLifetimeInDays int) { - defaultLifetimeInYears := defaultLifetimeInDays / 365 - fmt.Fprintf(os.Stderr, "WARNING: Validity period of the certificate for %q is greater than %d years!\n", name, defaultLifetimeInYears) - fmt.Fprintln(os.Stderr, "WARNING: By security reasons it is strongly recommended to change this period and make it smaller!") -} - -func signCertificate(template *x509.Certificate, requestKey crypto.PublicKey, issuer *x509.Certificate, issuerKey crypto.PrivateKey) (*x509.Certificate, error) { - derBytes, err := x509.CreateCertificate(rand.Reader, template, issuer, requestKey, issuerKey) - if err != nil { - return nil, err - } - certs, err := x509.ParseCertificates(derBytes) - if err != nil { - return nil, err - } - if len(certs) != 1 { - return nil, errors.New("Expected a single certificate") - } - return certs[0], nil -} - -func EncodeCertificates(certs ...*x509.Certificate) ([]byte, error) { - b := bytes.Buffer{} - for _, cert := range certs { - if err := pem.Encode(&b, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}); err != nil { - return []byte{}, err - } - } - return b.Bytes(), nil -} -func EncodeKey(key crypto.PrivateKey) ([]byte, error) { - b := bytes.Buffer{} - switch key := key.(type) { - case *ecdsa.PrivateKey: - keyBytes, err := x509.MarshalECPrivateKey(key) - if err != nil { - return []byte{}, err - } - if err := pem.Encode(&b, &pem.Block{Type: "EC PRIVATE KEY", Bytes: keyBytes}); err != nil { - return b.Bytes(), err - } - case *rsa.PrivateKey: - if err := pem.Encode(&b, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)}); err != nil { - return []byte{}, err - } - default: - return []byte{}, errors.New("Unrecognized key type") - - } - return b.Bytes(), nil -} - -func writeCertificates(f io.Writer, certs ...*x509.Certificate) error { - bytes, err := EncodeCertificates(certs...) - if err != nil { - return err - } - if _, err := f.Write(bytes); err != nil { - return err - } - - return nil -} -func writeKeyFile(f io.Writer, key crypto.PrivateKey) error { - bytes, err := EncodeKey(key) - if err != nil { - return err - } - if _, err := f.Write(bytes); err != nil { - return err - } - - return nil -} diff --git a/vendor/github.com/openshift/library-go/pkg/crypto/rotation.go b/vendor/github.com/openshift/library-go/pkg/crypto/rotation.go deleted file mode 100644 index 0aa12703..00000000 --- a/vendor/github.com/openshift/library-go/pkg/crypto/rotation.go +++ /dev/null @@ -1,20 +0,0 @@ -package crypto - -import ( - "crypto/x509" - "time" -) - -// FilterExpiredCerts checks are all certificates in the bundle valid, i.e. they have not expired. -// The function returns new bundle with only valid certificates or error if no valid certificate is found. -func FilterExpiredCerts(certs ...*x509.Certificate) []*x509.Certificate { - currentTime := time.Now() - var validCerts []*x509.Certificate - for _, c := range certs { - if c.NotAfter.After(currentTime) { - validCerts = append(validCerts, c) - } - } - - return validCerts -} diff --git a/vendor/k8s.io/apiserver/LICENSE b/vendor/k8s.io/apiserver/LICENSE deleted file mode 100644 index d6456956..00000000 --- a/vendor/k8s.io/apiserver/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/vendor/k8s.io/apiserver/pkg/authentication/user/doc.go b/vendor/k8s.io/apiserver/pkg/authentication/user/doc.go deleted file mode 100644 index 3d87fd72..00000000 --- a/vendor/k8s.io/apiserver/pkg/authentication/user/doc.go +++ /dev/null @@ -1,19 +0,0 @@ -/* -Copyright 2014 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 user contains utilities for dealing with simple user exchange in the auth -// packages. The user.Info interface defines an interface for exchanging that info. -package user // import "k8s.io/apiserver/pkg/authentication/user" diff --git a/vendor/k8s.io/apiserver/pkg/authentication/user/user.go b/vendor/k8s.io/apiserver/pkg/authentication/user/user.go deleted file mode 100644 index 4d6ec098..00000000 --- a/vendor/k8s.io/apiserver/pkg/authentication/user/user.go +++ /dev/null @@ -1,84 +0,0 @@ -/* -Copyright 2014 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 user - -// Info describes a user that has been authenticated to the system. -type Info interface { - // GetName returns the name that uniquely identifies this user among all - // other active users. - GetName() string - // GetUID returns a unique value for a particular user that will change - // if the user is removed from the system and another user is added with - // the same name. - GetUID() string - // GetGroups returns the names of the groups the user is a member of - GetGroups() []string - - // GetExtra can contain any additional information that the authenticator - // thought was interesting. One example would be scopes on a token. - // Keys in this map should be namespaced to the authenticator or - // authenticator/authorizer pair making use of them. - // For instance: "example.org/foo" instead of "foo" - // This is a map[string][]string because it needs to be serializeable into - // a SubjectAccessReviewSpec.authorization.k8s.io for proper authorization - // delegation flows - // In order to faithfully round-trip through an impersonation flow, these keys - // MUST be lowercase. - GetExtra() map[string][]string -} - -// DefaultInfo provides a simple user information exchange object -// for components that implement the UserInfo interface. -type DefaultInfo struct { - Name string - UID string - Groups []string - Extra map[string][]string -} - -func (i *DefaultInfo) GetName() string { - return i.Name -} - -func (i *DefaultInfo) GetUID() string { - return i.UID -} - -func (i *DefaultInfo) GetGroups() []string { - return i.Groups -} - -func (i *DefaultInfo) GetExtra() map[string][]string { - return i.Extra -} - -// well-known user and group names -const ( - SystemPrivilegedGroup = "system:masters" - NodesGroup = "system:nodes" - MonitoringGroup = "system:monitoring" - AllUnauthenticated = "system:unauthenticated" - AllAuthenticated = "system:authenticated" - - Anonymous = "system:anonymous" - APIServerUser = "system:apiserver" - - // core kubernetes process identities - KubeProxy = "system:kube-proxy" - KubeControllerManager = "system:kube-controller-manager" - KubeScheduler = "system:kube-scheduler" -) diff --git a/vendor/modules.txt b/vendor/modules.txt index f99ba7df..0260a1ad 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -171,9 +171,6 @@ github.com/openshift/client-go/security/clientset/versioned/typed/security/v1 # github.com/openshift/custom-resource-status v1.1.2 ## explicit; go 1.12 github.com/openshift/custom-resource-status/conditions/v1 -# github.com/openshift/library-go v0.0.0-20240715191351-e0aa70d55678 -## explicit; go 1.22.0 -github.com/openshift/library-go/pkg/crypto # github.com/pkg/errors v0.9.1 ## explicit github.com/pkg/errors @@ -403,9 +400,6 @@ k8s.io/apimachinery/pkg/watch k8s.io/apimachinery/third_party/forked/golang/json k8s.io/apimachinery/third_party/forked/golang/netutil k8s.io/apimachinery/third_party/forked/golang/reflect -# k8s.io/apiserver v0.30.3 -## explicit; go 1.22.0 -k8s.io/apiserver/pkg/authentication/user # k8s.io/client-go v0.30.3 ## explicit; go 1.22.0 k8s.io/client-go/applyconfigurations/admissionregistration/v1 From 490ed97e4aaf17f7e65b31ee5d5cbb25368ec56e Mon Sep 17 00:00:00 2001 From: Andrej Krejcir Date: Fri, 26 Jul 2024 11:09:15 +0200 Subject: [PATCH 4/4] api: Increase API version to v1 The current API is good enough to be v1. Signed-off-by: Andrej Krejcir --- api/{v1alpha1 => v1}/constants.go | 4 ++-- api/{v1alpha1 => v1}/types.go | 2 +- docs/api.md | 4 ++-- manifests/api_service.yaml | 4 ++-- manifests/config_map.yaml | 2 +- pkg/console/console.go | 4 ++-- pkg/console/service/service.go | 4 ++-- pkg/console/service/service_test.go | 2 +- pkg/console/tlsconfig/tlsconfig.go | 16 ++++++++-------- pkg/console/tlsconfig/tlsconfig_test.go | 16 ++++++++-------- tests/proxy_test.go | 2 +- tests/tests_suite_test.go | 2 +- tests/tlsconfig_test.go | 2 +- tests/user-role_test.go | 2 +- 14 files changed, 33 insertions(+), 33 deletions(-) rename api/{v1alpha1 => v1}/constants.go (51%) rename api/{v1alpha1 => v1}/types.go (98%) diff --git a/api/v1alpha1/constants.go b/api/v1/constants.go similarity index 51% rename from api/v1alpha1/constants.go rename to api/v1/constants.go index 1f3a6724..ab6958e0 100644 --- a/api/v1alpha1/constants.go +++ b/api/v1/constants.go @@ -1,6 +1,6 @@ -package v1alpha1 +package v1 const ( Group = "token.kubevirt.io" - Version = "v1alpha1" + Version = "v1" ) diff --git a/api/v1alpha1/types.go b/api/v1/types.go similarity index 98% rename from api/v1alpha1/types.go rename to api/v1/types.go index 37c7633b..c5d33fae 100644 --- a/api/v1alpha1/types.go +++ b/api/v1/types.go @@ -1,4 +1,4 @@ -package v1alpha1 +package v1 import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" diff --git a/docs/api.md b/docs/api.md index 2ab077a8..5a55ac96 100644 --- a/docs/api.md +++ b/docs/api.md @@ -4,7 +4,7 @@ A temporary token can be generated using: ``` -GET /apis/token.kubevirt.io/v1alpha1/namespaces/${VMI_NAMESPACE}/virtualmachines/${VMI_NAME}/vnc +GET /apis/token.kubevirt.io/v1/namespaces/${VMI_NAMESPACE}/virtualmachines/${VMI_NAME}/vnc ``` Where `${VMI_NAMESPACE}` and `${VMI_NAME}` are the namespace and name of a running VMI. @@ -28,7 +28,7 @@ Result is a JSON object containing the token: ### Example ```bash curl --header "Authorization: Bearer ${KUBERNETES_USER_TOKEN}" \ - "https://${K8S_API_URL}/apis/token.kubevirt.io/v1alpha1/namespaces/${VMI_NAMESPACE}/virtualmachines/${VMI_NAME}/vnc?duration=${DURATION}" + "https://${K8S_API_URL}/apis/token.kubevirt.io/v1/namespaces/${VMI_NAMESPACE}/virtualmachines/${VMI_NAME}/vnc?duration=${DURATION}" ``` In this example, we use a bearer token to authenticate the user with the Kubernetes API server. diff --git a/manifests/api_service.yaml b/manifests/api_service.yaml index 4e9916cd..44c7af74 100644 --- a/manifests/api_service.yaml +++ b/manifests/api_service.yaml @@ -1,12 +1,12 @@ apiVersion: apiregistration.k8s.io/v1 kind: APIService metadata: - name: v1alpha1.token.kubevirt.io + name: v1.token.kubevirt.io annotations: service.beta.openshift.io/inject-cabundle: "true" spec: group: token.kubevirt.io - version: v1alpha1 + version: v1 groupPriorityMinimum: 2000 versionPriority: 10 service: diff --git a/manifests/config_map.yaml b/manifests/config_map.yaml index 6015e97f..693c05ae 100644 --- a/manifests/config_map.yaml +++ b/manifests/config_map.yaml @@ -3,4 +3,4 @@ kind: ConfigMap metadata: name: vm-console-proxy data: - tls-profile-v1alpha1.yaml: "{}" + tls-profile-v1.yaml: "{}" diff --git a/pkg/console/console.go b/pkg/console/console.go index 33e3d598..30c43aae 100644 --- a/pkg/console/console.go +++ b/pkg/console/console.go @@ -6,7 +6,7 @@ import ( "net/http" "github.com/emicklei/go-restful/v3" - api "github.com/kubevirt/vm-console-proxy/api/v1alpha1" + api "github.com/kubevirt/vm-console-proxy/api/v1" "github.com/kubevirt/vm-console-proxy/pkg/console/authConfig" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "kubevirt.io/client-go/kubecli" @@ -26,7 +26,7 @@ const ( keyName = "tls.key" configDir = "/config" - TlsProfileFile = "tls-profile-v1alpha1.yaml" + TlsProfileFile = "tls-profile-v1.yaml" ) func Run() error { diff --git a/pkg/console/service/service.go b/pkg/console/service/service.go index 66c2f6e2..6e5363ae 100644 --- a/pkg/console/service/service.go +++ b/pkg/console/service/service.go @@ -21,7 +21,7 @@ import ( kubevirtv1 "kubevirt.io/api/core/v1" "kubevirt.io/client-go/kubecli" - "github.com/kubevirt/vm-console-proxy/api/v1alpha1" + "github.com/kubevirt/vm-console-proxy/api/v1" "github.com/kubevirt/vm-console-proxy/pkg/console/authConfig" ) @@ -85,7 +85,7 @@ func (s *service) TokenHandler(request *restful.Request, response *restful.Respo return } - _ = response.WriteAsJson(&v1alpha1.TokenResponse{ + _ = response.WriteAsJson(&v1.TokenResponse{ Token: tokenRequestStatus.Token, ExpirationTimestamp: tokenRequestStatus.ExpirationTimestamp, }) diff --git a/pkg/console/service/service_test.go b/pkg/console/service/service_test.go index d4523c96..6d3e3f6f 100644 --- a/pkg/console/service/service_test.go +++ b/pkg/console/service/service_test.go @@ -25,7 +25,7 @@ import ( v1 "kubevirt.io/api/core/v1" "kubevirt.io/client-go/kubecli" - api "github.com/kubevirt/vm-console-proxy/api/v1alpha1" + api "github.com/kubevirt/vm-console-proxy/api/v1" "github.com/kubevirt/vm-console-proxy/pkg/console/authConfig" fakeAuth "github.com/kubevirt/vm-console-proxy/pkg/console/authConfig/fake" ) diff --git a/pkg/console/tlsconfig/tlsconfig.go b/pkg/console/tlsconfig/tlsconfig.go index 9caffaac..71854338 100644 --- a/pkg/console/tlsconfig/tlsconfig.go +++ b/pkg/console/tlsconfig/tlsconfig.go @@ -13,7 +13,7 @@ import ( "k8s.io/client-go/util/cert" "kubevirt.io/client-go/log" - "github.com/kubevirt/vm-console-proxy/api/v1alpha1" + "github.com/kubevirt/vm-console-proxy/api/v1" "github.com/kubevirt/vm-console-proxy/pkg/console/authConfig" "github.com/kubevirt/vm-console-proxy/pkg/filewatch" ) @@ -176,7 +176,7 @@ func loadCipherSuitesAndMinVersion(configPath string) ([]uint16, uint16, error) return ciphers, minVersion, nil } -func loadTlsProfile(profilePath string) (*v1alpha1.TlsProfile, error) { +func loadTlsProfile(profilePath string) (*v1.TlsProfile, error) { file, err := os.Open(profilePath) if err != nil { return nil, fmt.Errorf("error opening file: %w", err) @@ -184,7 +184,7 @@ func loadTlsProfile(profilePath string) (*v1alpha1.TlsProfile, error) { // It's ok to ignore error on close, because the file is opened of reading defer func() { _ = file.Close() }() - result := &v1alpha1.TlsProfile{} + result := &v1.TlsProfile{} err = yaml.NewYAMLToJSONDecoder(file).Decode(result) if err != nil { return nil, fmt.Errorf("error decoding tls config: %w", err) @@ -214,17 +214,17 @@ outerLoop: return result, nil } -func getMinTlsVersion(version v1alpha1.TLSProtocolVersion) (uint16, error) { +func getMinTlsVersion(version v1.TLSProtocolVersion) (uint16, error) { switch version { case "": return 0, nil - case v1alpha1.VersionTLS10: + case v1.VersionTLS10: return tls.VersionTLS10, nil - case v1alpha1.VersionTLS11: + case v1.VersionTLS11: return tls.VersionTLS11, nil - case v1alpha1.VersionTLS12: + case v1.VersionTLS12: return tls.VersionTLS12, nil - case v1alpha1.VersionTLS13: + case v1.VersionTLS13: return tls.VersionTLS13, nil default: return 0, fmt.Errorf("unsupported TLS version: %s", version) diff --git a/pkg/console/tlsconfig/tlsconfig_test.go b/pkg/console/tlsconfig/tlsconfig_test.go index 8424fbdc..632f508a 100644 --- a/pkg/console/tlsconfig/tlsconfig_test.go +++ b/pkg/console/tlsconfig/tlsconfig_test.go @@ -16,7 +16,7 @@ import ( "k8s.io/client-go/util/cert" "sigs.k8s.io/yaml" - "github.com/kubevirt/vm-console-proxy/api/v1alpha1" + "github.com/kubevirt/vm-console-proxy/api/v1" fakeAuth "github.com/kubevirt/vm-console-proxy/pkg/console/authConfig/fake" "github.com/kubevirt/vm-console-proxy/pkg/filewatch" ) @@ -62,9 +62,9 @@ var _ = Describe("TlsConfig", func() { tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, } - tlsProfile := &v1alpha1.TlsProfile{ + tlsProfile := &v1.TlsProfile{ Ciphers: testCiphersNames, - MinTLSVersion: v1alpha1.VersionTLS12, + MinTLSVersion: v1.VersionTLS12, } tlsProfileYaml, err := yaml.Marshal(tlsProfile) Expect(err).ToNot(HaveOccurred()) @@ -126,8 +126,8 @@ var _ = Describe("TlsConfig", func() { }) It("should use default ciphers, if ciphers are not sepcified", func() { - tlsConfig := &v1alpha1.TlsProfile{ - MinTLSVersion: v1alpha1.VersionTLS12, + tlsConfig := &v1.TlsProfile{ + MinTLSVersion: v1.VersionTLS12, } tlsConfigYaml, err := yaml.Marshal(tlsConfig) Expect(err).ToNot(HaveOccurred()) @@ -144,7 +144,7 @@ var _ = Describe("TlsConfig", func() { }) It("should use default tls version, if MinTLSVersion is not specified", func() { - tlsConfig := &v1alpha1.TlsProfile{ + tlsConfig := &v1.TlsProfile{ Ciphers: testCiphersNames, } tlsConfigYaml, err := yaml.Marshal(tlsConfig) @@ -198,13 +198,13 @@ var _ = Describe("TlsConfig", func() { originalConfig, err := configWatch.GetConfig() Expect(err).ToNot(HaveOccurred()) - tlsProfile := &v1alpha1.TlsProfile{ + tlsProfile := &v1.TlsProfile{ Ciphers: []string{ "TLS_AES_128_GCM_SHA256", "TLS_AES_256_GCM_SHA384", "TLS_CHACHA20_POLY1305_SHA256", }, - MinTLSVersion: v1alpha1.VersionTLS13, + MinTLSVersion: v1.VersionTLS13, } tlsProfileYaml, err := yaml.Marshal(tlsProfile) Expect(err).ToNot(HaveOccurred()) diff --git a/tests/proxy_test.go b/tests/proxy_test.go index 76b4029b..a79cdea6 100644 --- a/tests/proxy_test.go +++ b/tests/proxy_test.go @@ -23,7 +23,7 @@ import ( "k8s.io/utils/pointer" kubevirtcorev1 "kubevirt.io/api/core/v1" - api "github.com/kubevirt/vm-console-proxy/api/v1alpha1" + api "github.com/kubevirt/vm-console-proxy/api/v1" ) var _ = Describe("Kubevirt proxy", func() { diff --git a/tests/tests_suite_test.go b/tests/tests_suite_test.go index 69e233af..7e155d0e 100644 --- a/tests/tests_suite_test.go +++ b/tests/tests_suite_test.go @@ -11,7 +11,7 @@ import ( "testing" "time" - api "github.com/kubevirt/vm-console-proxy/api/v1alpha1" + api "github.com/kubevirt/vm-console-proxy/api/v1" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" core "k8s.io/api/core/v1" diff --git a/tests/tlsconfig_test.go b/tests/tlsconfig_test.go index 31e98755..28b6e011 100644 --- a/tests/tlsconfig_test.go +++ b/tests/tlsconfig_test.go @@ -10,7 +10,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - api "github.com/kubevirt/vm-console-proxy/api/v1alpha1" + api "github.com/kubevirt/vm-console-proxy/api/v1" core "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" diff --git a/tests/user-role_test.go b/tests/user-role_test.go index 83e7e683..96e3046c 100644 --- a/tests/user-role_test.go +++ b/tests/user-role_test.go @@ -13,7 +13,7 @@ import ( rbac "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - proxy "github.com/kubevirt/vm-console-proxy/api/v1alpha1" + proxy "github.com/kubevirt/vm-console-proxy/api/v1" ) var _ = Describe("Role for token generation", func() {