diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 851381ff..b67bf4f5 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -12,3 +12,8 @@ updates: directory: "/" schedule: interval: "daily" + + - package-ecosystem: "docker" + directory: "/hack" # this should keep the FIPS dockerfile updated per https://github.com/dependabot/feedback/issues/145#issuecomment-414738498 + schedule: + interval: "daily" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 97269f7a..09779a71 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,6 +11,7 @@ repos: - id: check-json - id: end-of-file-fixer - id: trailing-whitespace + exclude: 'securetls*' # prevent the linter from running in this file because it's not smart enough not to trim the nmap test output. - id: check-merge-conflict - id: check-added-large-files - id: check-byte-order-marker diff --git a/cmd/pinniped-concierge-kube-cert-agent/main.go b/cmd/pinniped-concierge-kube-cert-agent/main.go index 0947fe84..66de9af6 100644 --- a/cmd/pinniped-concierge-kube-cert-agent/main.go +++ b/cmd/pinniped-concierge-kube-cert-agent/main.go @@ -1,4 +1,4 @@ -// Copyright 2021 the Pinniped contributors. All Rights Reserved. +// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 // Package main is the combined entrypoint for the Pinniped "kube-cert-agent" component. @@ -13,6 +13,9 @@ import ( "math" "os" "time" + + // this side effect import ensures that we use fipsonly crypto in fips_strict mode. + _ "go.pinniped.dev/internal/crypto/ptls" ) //nolint: gochecknoglobals // these are swapped during unit tests. diff --git a/cmd/pinniped-server/main.go b/cmd/pinniped-server/main.go index dfb4b4ca..fdce4f9d 100644 --- a/cmd/pinniped-server/main.go +++ b/cmd/pinniped-server/main.go @@ -1,4 +1,4 @@ -// Copyright 2021 the Pinniped contributors. All Rights Reserved. +// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 // Package main is the combined entrypoint for all Pinniped server components. @@ -15,6 +15,8 @@ import ( "k8s.io/klog/v2" concierge "go.pinniped.dev/internal/concierge/server" + // this side effect import ensures that we use fipsonly crypto in fips_strict mode. + _ "go.pinniped.dev/internal/crypto/ptls" lua "go.pinniped.dev/internal/localuserauthenticator" supervisor "go.pinniped.dev/internal/supervisor/server" ) diff --git a/cmd/pinniped/main.go b/cmd/pinniped/main.go index 6b5a4611..d4776f24 100644 --- a/cmd/pinniped/main.go +++ b/cmd/pinniped/main.go @@ -1,4 +1,4 @@ -// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package main @@ -9,6 +9,8 @@ import ( "github.com/pkg/browser" "go.pinniped.dev/cmd/pinniped/cmd" + // this side effect import ensures that we use fipsonly crypto in fips_strict mode. + _ "go.pinniped.dev/internal/crypto/ptls" ) //nolint: gochecknoinits diff --git a/hack/Dockerfile_fips b/hack/Dockerfile_fips new file mode 100644 index 00000000..1b1bf858 --- /dev/null +++ b/hack/Dockerfile_fips @@ -0,0 +1,45 @@ +# syntax = docker/dockerfile:1.0-experimental + +# Copyright 2020-2022 the Pinniped contributors. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +# this dockerfile is used to produce a binary of Pinniped that uses +# only fips-allowable ciphers. + +# use go-boringcrypto rather than main go +FROM us-docker.pkg.dev/google.com/api-project-999119582588/go-boringcrypto/golang:1.17.8b7 as build-env + +WORKDIR /work +COPY . . +ARG GOPROXY + +# Build the executable binary (CGO_ENABLED=1 is required for go boring) +# Pass in GOCACHE (build cache) and GOMODCACHE (module cache) so they +# can be re-used between image builds. +RUN \ + --mount=type=cache,target=/cache/gocache \ + --mount=type=cache,target=/cache/gomodcache \ + mkdir out && \ + export CGO_ENABLED=1 GOOS=linux GOARCH=amd64 && \ + go build -tags fips_strict,osusergo,netgo -v -trimpath -ldflags "$(hack/get-ldflags.sh) -w -linkmode=external -extldflags -static" -o /usr/local/bin/pinniped-concierge-kube-cert-agent ./cmd/pinniped-concierge-kube-cert-agent/... && \ + go build -tags fips_strict,osusergo,netgo -v -trimpath -ldflags "$(hack/get-ldflags.sh) -w -linkmode=external -extldflags -static" -o /usr/local/bin/pinniped-server ./cmd/pinniped-server/... && \ + ln -s /usr/local/bin/pinniped-server /usr/local/bin/pinniped-concierge && \ + ln -s /usr/local/bin/pinniped-server /usr/local/bin/pinniped-supervisor && \ + ln -s /usr/local/bin/pinniped-server /usr/local/bin/local-user-authenticator + +# Use a distroless runtime image with CA certificates, timezone data, and not much else. +FROM gcr.io/distroless/static:nonroot@sha256:80c956fb0836a17a565c43a4026c9c80b2013c83bea09f74fa4da195a59b7a99 + +# Copy the server binary from the build-env stage. +COPY --from=build-env /usr/local/bin /usr/local/bin + +# Document the default server ports for the various server apps +EXPOSE 8443 8444 10250 + +# Run as non-root for security posture +# Use the same non-root user as https://github.com/GoogleContainerTools/distroless/blob/fc3c4eaceb0518900f886aae90407c43be0a42d9/base/base.bzl#L9 +# This is a workaround for https://github.com/GoogleContainerTools/distroless/issues/718 +USER 65532:65532 + +# Set the entrypoint +ENTRYPOINT ["/usr/local/bin/pinniped-server"] diff --git a/hack/prepare-for-integration-tests.sh b/hack/prepare-for-integration-tests.sh index e9116cd9..db1cc8db 100755 --- a/hack/prepare-for-integration-tests.sh +++ b/hack/prepare-for-integration-tests.sh @@ -49,6 +49,7 @@ help=no skip_build=no clean_kind=no api_group_suffix="pinniped.dev" # same default as in the values.yaml ytt file +dockerfile_path="" skip_chromedriver_check=no get_active_directory_vars="" # specify a filename for a script to get AD related env variables alternate_deploy="undefined" @@ -91,6 +92,16 @@ while (("$#")); do get_active_directory_vars=$1 shift ;; + --dockerfile-path) + shift + # If there are no more command line arguments, or there is another command line argument but it starts with a dash, then error + if [[ "$#" == "0" || "$1" == -* ]]; then + log_error "--dockerfile-path requires a script name to be specified" + exit 1 + fi + dockerfile_path=$1 + shift + ;; --alternate-deploy) shift if [[ "$#" == "0" || "$1" == -* ]]; then @@ -219,9 +230,14 @@ registry_repo_tag="${registry_repo}:${tag}" if [[ "$do_build" == "yes" ]]; then # Rebuild the code - log_note "Docker building the app..." - # DOCKER_BUILDKIT=1 is optional on MacOS but required on linux. - DOCKER_BUILDKIT=1 docker build . --tag "$registry_repo_tag" + if [[ "$dockerfile_path" != "" ]]; then + log_note "Docker building the app with dockerfile $dockerfile_path..." + DOCKER_BUILDKIT=1 docker build . --tag "$registry_repo_tag" --file "$dockerfile_path" + else + log_note "Docker building the app..." + # DOCKER_BUILDKIT=1 is optional on MacOS but required on linux. + DOCKER_BUILDKIT=1 docker build . --tag "$registry_repo_tag" + fi fi # Load it into the cluster diff --git a/internal/controller/kubecertagent/kubecertagent.go b/internal/controller/kubecertagent/kubecertagent.go index 8408005b..230cf0ba 100644 --- a/internal/controller/kubecertagent/kubecertagent.go +++ b/internal/controller/kubecertagent/kubecertagent.go @@ -145,12 +145,12 @@ type agentController struct { var ( // controllerManagerLabels are the Kubernetes labels we expect on the kube-controller-manager Pod. - controllerManagerLabels = labels.SelectorFromSet(map[string]string{ //nolint: gochecknoglobals + controllerManagerLabels = labels.SelectorFromSet(map[string]string{ // nolint: gochecknoglobals "component": "kube-controller-manager", }) // agentLabels are the Kubernetes labels we always expect on the kube-controller-manager Pod. - agentLabels = labels.SelectorFromSet(map[string]string{ //nolint: gochecknoglobals + agentLabels = labels.SelectorFromSet(map[string]string{ // nolint: gochecknoglobals agentPodLabelKey: agentPodLabelValue, }) ) @@ -543,12 +543,12 @@ func (c *agentController) newAgentDeployment(controllerManagerPod *corev1.Pod) * }, Resources: corev1.ResourceRequirements{ Limits: corev1.ResourceList{ - corev1.ResourceMemory: resource.MustParse("16Mi"), - corev1.ResourceCPU: resource.MustParse("10m"), + corev1.ResourceMemory: resource.MustParse("32Mi"), + corev1.ResourceCPU: resource.MustParse("20m"), }, Requests: corev1.ResourceList{ - corev1.ResourceMemory: resource.MustParse("16Mi"), - corev1.ResourceCPU: resource.MustParse("10m"), + corev1.ResourceMemory: resource.MustParse("32Mi"), + corev1.ResourceCPU: resource.MustParse("20m"), }, }, }, diff --git a/internal/controller/kubecertagent/kubecertagent_test.go b/internal/controller/kubecertagent/kubecertagent_test.go index ee382458..38736213 100644 --- a/internal/controller/kubecertagent/kubecertagent_test.go +++ b/internal/controller/kubecertagent/kubecertagent_test.go @@ -120,12 +120,12 @@ func TestAgentController(t *testing.T) { }}, Resources: corev1.ResourceRequirements{ Limits: corev1.ResourceList{ - corev1.ResourceMemory: resource.MustParse("16Mi"), - corev1.ResourceCPU: resource.MustParse("10m"), + corev1.ResourceMemory: resource.MustParse("32Mi"), + corev1.ResourceCPU: resource.MustParse("20m"), }, Requests: corev1.ResourceList{ - corev1.ResourceMemory: resource.MustParse("16Mi"), - corev1.ResourceCPU: resource.MustParse("10m"), + corev1.ResourceMemory: resource.MustParse("32Mi"), + corev1.ResourceCPU: resource.MustParse("20m"), }, }, ImagePullPolicy: corev1.PullIfNotPresent, @@ -1028,7 +1028,7 @@ func TestAgentController(t *testing.T) { } kubeInformers := informers.NewSharedInformerFactory(kubeClientset, 0) - log := testlogger.NewLegacy(t) //nolint: staticcheck // old test with lots of log statements + log := testlogger.NewLegacy(t) // nolint: staticcheck // old test with lots of log statements ctrl := gomock.NewController(t) defer ctrl.Finish() @@ -1106,7 +1106,7 @@ func TestAgentController(t *testing.T) { require.NoError(t, err) if tt.wantAgentDeployment == nil { assert.Empty(t, deployments.Items, "did not expect an agent deployment") - } else { //nolint: gocritic + } else { // nolint: gocritic if assert.Len(t, deployments.Items, 1, "expected a single agent deployment") { assert.Equal(t, tt.wantAgentDeployment, &deployments.Items[0]) } diff --git a/internal/crypto/ptls/default.go b/internal/crypto/ptls/default.go new file mode 100644 index 00000000..d929a4eb --- /dev/null +++ b/internal/crypto/ptls/default.go @@ -0,0 +1,73 @@ +// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +//go:build !fips_strict +// +build !fips_strict + +package ptls + +import ( + "crypto/tls" + "crypto/x509" +) + +func Default(rootCAs *x509.CertPool) *tls.Config { + return &tls.Config{ + // 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 + // + // The Kubernetes API Server must use TLS 1.2, at a minimum, + // to protect the confidentiality of sensitive data during electronic dissemination. + // https://stigviewer.com/stig/kubernetes/2021-06-17/finding/V-242378 + MinVersion: tls.VersionTLS12, + + // the order does not matter in go 1.17+ https://go.dev/blog/tls-cipher-suites + // we match crypto/tls.cipherSuitesPreferenceOrder because it makes unit tests easier to write + // this list is ignored when TLS 1.3 is used + // + // as of 2021-10-19, Mozilla Guideline v5.6, Go 1.17.2, intermediate configuration, supports: + // - Firefox 27 + // - Android 4.4.2 + // - Chrome 31 + // - Edge + // - IE 11 on Windows 7 + // - Java 8u31 + // - OpenSSL 1.0.1 + // - Opera 20 + // - Safari 9 + // https://ssl-config.mozilla.org/#server=go&version=1.17.2&config=intermediate&guideline=5.6 + // + // The Kubernetes API server must use approved cipher suites. + // https://stigviewer.com/stig/kubernetes/2021-06-17/finding/V-242418 + CipherSuites: []uint16{ + // these are all AEADs with ECDHE, some use ChaCha20Poly1305 while others use AES-GCM + // this provides forward secrecy, confidentiality and authenticity of data + 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, tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, + }, + + // enable HTTP2 for go's 1.7 HTTP Server + // setting this explicitly is only required in very specific circumstances + // it is simpler to just set it here than to try and determine if we need to + NextProtos: []string{"h2", "http/1.1"}, + + // optional root CAs, nil means use the host's root CA set + RootCAs: rootCAs, + } +} + +func DefaultLDAP(rootCAs *x509.CertPool) *tls.Config { + c := Default(rootCAs) + // add less secure ciphers to support the default AWS Active Directory config + c.CipherSuites = append(c.CipherSuites, + // CBC with ECDHE + // this provides forward secrecy and confidentiality of data but not authenticity + // MAC-then-Encrypt CBC ciphers are susceptible to padding oracle attacks + // See https://crypto.stackexchange.com/a/205 and https://crypto.stackexchange.com/a/224 + tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + ) + return c +} diff --git a/internal/crypto/ptls/fips_strict.go b/internal/crypto/ptls/fips_strict.go new file mode 100644 index 00000000..e35a30a5 --- /dev/null +++ b/internal/crypto/ptls/fips_strict.go @@ -0,0 +1,66 @@ +// Copyright 2022 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// The configurations here override the usual ptls.Secure, ptls.Default, and ptls.DefaultLDAP +// configs when Pinniped is built in fips-only mode. +// All of these are the same because FIPs is already so limited. +//go:build fips_strict +// +build fips_strict + +package ptls + +import ( + "C" + "crypto/tls" + _ "crypto/tls/fipsonly" // restricts all TLS configuration to FIPS-approved settings. + "crypto/x509" + "runtime" + + "go.pinniped.dev/internal/plog" +) + +// Always use TLS 1.2 for FIPs +const secureServingOptionsMinTLSVersion = "VersionTLS12" +const SecureTLSConfigMinTLSVersion = tls.VersionTLS12 + +func init() { + go func() { + version := runtime.Version() + plog.Debug("using boringcrypto in fips only mode.", "go version", version) + }() +} + +func Default(rootCAs *x509.CertPool) *tls.Config { + return &tls.Config{ + // goboring requires TLS 1.2 and only TLS 1.2 + MinVersion: SecureTLSConfigMinTLSVersion, + MaxVersion: SecureTLSConfigMinTLSVersion, + + // enable HTTP2 for go's 1.7 HTTP Server + // setting this explicitly is only required in very specific circumstances + // it is simpler to just set it here than to try and determine if we need to + NextProtos: []string{"h2", "http/1.1"}, + + // optional root CAs, nil means use the host's root CA set + RootCAs: rootCAs, + + // This is all of the fips-approved ciphers. + // The list is hard-coded for convenience of testing. + CipherSuites: []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_RSA_WITH_AES_128_GCM_SHA256, + tls.TLS_RSA_WITH_AES_256_GCM_SHA384, + }, + } +} + +func Secure(rootCAs *x509.CertPool) *tls.Config { + return Default(rootCAs) +} + +func DefaultLDAP(rootCAs *x509.CertPool) *tls.Config { + return Default(rootCAs) +} diff --git a/internal/crypto/ptls/ptls.go b/internal/crypto/ptls/ptls.go index 5c64978c..3eb45d41 100644 --- a/internal/crypto/ptls/ptls.go +++ b/internal/crypto/ptls/ptls.go @@ -1,4 +1,4 @@ -// Copyright 2021 the Pinniped contributors. All Rights Reserved. +// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package ptls @@ -21,92 +21,13 @@ import ( // TODO decide if we need to expose the four TLS levels (secure, default, default-ldap, legacy) as config. +// defaultServingOptionsMinTLSVersion is the minimum tls version in the format +// expected by SecureServingOptions.MinTLSVersion from +// k8s.io/apiserver/pkg/server/options. +const defaultServingOptionsMinTLSVersion = "VersionTLS12" + type ConfigFunc func(*x509.CertPool) *tls.Config -func Default(rootCAs *x509.CertPool) *tls.Config { - return &tls.Config{ - // 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 - // - // The Kubernetes API Server must use TLS 1.2, at a minimum, - // to protect the confidentiality of sensitive data during electronic dissemination. - // https://stigviewer.com/stig/kubernetes/2021-06-17/finding/V-242378 - MinVersion: tls.VersionTLS12, - - // the order does not matter in go 1.17+ https://go.dev/blog/tls-cipher-suites - // we match crypto/tls.cipherSuitesPreferenceOrder because it makes unit tests easier to write - // this list is ignored when TLS 1.3 is used - // - // as of 2021-10-19, Mozilla Guideline v5.6, Go 1.17.2, intermediate configuration, supports: - // - Firefox 27 - // - Android 4.4.2 - // - Chrome 31 - // - Edge - // - IE 11 on Windows 7 - // - Java 8u31 - // - OpenSSL 1.0.1 - // - Opera 20 - // - Safari 9 - // https://ssl-config.mozilla.org/#server=go&version=1.17.2&config=intermediate&guideline=5.6 - // - // The Kubernetes API server must use approved cipher suites. - // https://stigviewer.com/stig/kubernetes/2021-06-17/finding/V-242418 - CipherSuites: []uint16{ - // these are all AEADs with ECDHE, some use ChaCha20Poly1305 while others use AES-GCM - // this provides forward secrecy, confidentiality and authenticity of data - 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, tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, - }, - - // enable HTTP2 for go's 1.7 HTTP Server - // setting this explicitly is only required in very specific circumstances - // it is simpler to just set it here than to try and determine if we need to - NextProtos: []string{"h2", "http/1.1"}, - - // optional root CAs, nil means use the host's root CA set - RootCAs: rootCAs, - } -} - -func Secure(rootCAs *x509.CertPool) *tls.Config { - // as of 2021-10-19, Mozilla Guideline v5.6, Go 1.17.2, modern configuration, supports: - // - Firefox 63 - // - Android 10.0 - // - Chrome 70 - // - Edge 75 - // - Java 11 - // - OpenSSL 1.1.1 - // - Opera 57 - // - Safari 12.1 - // https://ssl-config.mozilla.org/#server=go&version=1.17.2&config=modern&guideline=5.6 - c := Default(rootCAs) - c.MinVersion = tls.VersionTLS13 // max out the security - c.CipherSuites = []uint16{ - // TLS 1.3 ciphers are not configurable, but we need to explicitly set them here to make our client hello behave correctly - // See https://github.com/golang/go/pull/49293 - tls.TLS_AES_128_GCM_SHA256, - tls.TLS_AES_256_GCM_SHA384, - tls.TLS_CHACHA20_POLY1305_SHA256, - } - return c -} - -func DefaultLDAP(rootCAs *x509.CertPool) *tls.Config { - c := Default(rootCAs) - // add less secure ciphers to support the default AWS Active Directory config - c.CipherSuites = append(c.CipherSuites, - // CBC with ECDHE - // this provides forward secrecy and confidentiality of data but not authenticity - // MAC-then-Encrypt CBC ciphers are susceptible to padding oracle attacks - // See https://crypto.stackexchange.com/a/205 and https://crypto.stackexchange.com/a/224 - tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, - tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, - ) - return c -} - func Legacy(rootCAs *x509.CertPool) *tls.Config { c := Default(rootCAs) // add all the ciphers (even the crappy ones) except the ones that Go considers to be outright broken like 3DES @@ -158,11 +79,11 @@ func defaultServing(opts *options.SecureServingOptionsWithLoopback) { } opts.CipherSuites = cipherSuites - opts.MinTLSVersion = "VersionTLS12" + opts.MinTLSVersion = defaultServingOptionsMinTLSVersion } func secureServing(opts *options.SecureServingOptionsWithLoopback) { - opts.MinTLSVersion = "VersionTLS13" + opts.MinTLSVersion = secureServingOptionsMinTLSVersion opts.CipherSuites = nil } diff --git a/internal/crypto/ptls/secure.go b/internal/crypto/ptls/secure.go new file mode 100644 index 00000000..9f07b633 --- /dev/null +++ b/internal/crypto/ptls/secure.go @@ -0,0 +1,44 @@ +// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +//go:build !fips_strict +// +build !fips_strict + +package ptls + +import ( + "crypto/tls" + "crypto/x509" +) + +// secureServingOptionsMinTLSVersion is the minimum tls version in the format +// expected by SecureServingOptions.MinTLSVersion from +// k8s.io/apiserver/pkg/server/options. +const secureServingOptionsMinTLSVersion = "VersionTLS13" + +// SecureTLSConfigMinTLSVersion is the minimum tls version in the format expected +// by tls.Config. +const SecureTLSConfigMinTLSVersion = tls.VersionTLS13 + +func Secure(rootCAs *x509.CertPool) *tls.Config { + // as of 2021-10-19, Mozilla Guideline v5.6, Go 1.17.2, modern configuration, supports: + // - Firefox 63 + // - Android 10.0 + // - Chrome 70 + // - Edge 75 + // - Java 11 + // - OpenSSL 1.1.1 + // - Opera 57 + // - Safari 12.1 + // https://ssl-config.mozilla.org/#server=go&version=1.17.2&config=modern&guideline=5.6 + c := Default(rootCAs) + c.MinVersion = SecureTLSConfigMinTLSVersion // max out the security + c.CipherSuites = []uint16{ + // TLS 1.3 ciphers are not configurable, but we need to explicitly set them here to make our client hello behave correctly + // See https://github.com/golang/go/pull/49293 + tls.TLS_AES_128_GCM_SHA256, + tls.TLS_AES_256_GCM_SHA384, + tls.TLS_CHACHA20_POLY1305_SHA256, + } + return c +} diff --git a/internal/testutil/tlsserver/tlsserver.go b/internal/testutil/tlsserver/tlsserver.go index 425c43c9..21238993 100644 --- a/internal/testutil/tlsserver/tlsserver.go +++ b/internal/testutil/tlsserver/tlsserver.go @@ -1,4 +1,4 @@ -// Copyright 2021 the Pinniped contributors. All Rights Reserved. +// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package tlsserver @@ -69,6 +69,14 @@ func RecordTLSHello(server *httptest.Server) { func AssertTLS(t *testing.T, r *http.Request, tlsConfigFunc ptls.ConfigFunc) { t.Helper() + tlsConfig := tlsConfigFunc(nil) + + AssertTLSConfig(t, r, tlsConfig) +} + +func AssertTLSConfig(t *testing.T, r *http.Request, tlsConfig *tls.Config) { + t.Helper() + m, ok := getCtxMap(r.Context()) require.True(t, ok) @@ -78,8 +86,6 @@ func AssertTLS(t *testing.T, r *http.Request, tlsConfigFunc ptls.ConfigFunc) { info, ok := h.(*tls.ClientHelloInfo) require.True(t, ok) - tlsConfig := tlsConfigFunc(nil) - supportedVersions := []uint16{tlsConfig.MinVersion} ciphers := tlsConfig.CipherSuites @@ -95,7 +101,7 @@ func AssertTLS(t *testing.T, r *http.Request, tlsConfigFunc ptls.ConfigFunc) { // use assert instead of require to not break the http.Handler with a panic ok1 := assert.Equal(t, supportedVersions, info.SupportedVersions) - ok2 := assert.Equal(t, ciphers, info.CipherSuites) + ok2 := assert.Equal(t, cipherSuiteIDsToStrings(ciphers), cipherSuiteIDsToStrings(info.CipherSuites)) ok3 := assert.Equal(t, protos, info.SupportedProtos) if all := ok1 && ok2 && ok3; !all { @@ -104,6 +110,14 @@ func AssertTLS(t *testing.T, r *http.Request, tlsConfigFunc ptls.ConfigFunc) { } } +func cipherSuiteIDsToStrings(ids []uint16) []string { + cipherSuites := make([]string, 0, len(ids)) + for _, id := range ids { + cipherSuites = append(cipherSuites, tls.CipherSuiteName(id)) + } + return cipherSuites +} + func getCtxMap(ctx context.Context) (*sync.Map, bool) { m, ok := ctx.Value(mapKey).(*sync.Map) return m, ok diff --git a/test/integration/concierge_impersonation_proxy_test.go b/test/integration/concierge_impersonation_proxy_test.go index 352a473a..c4831909 100644 --- a/test/integration/concierge_impersonation_proxy_test.go +++ b/test/integration/concierge_impersonation_proxy_test.go @@ -1503,10 +1503,10 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl startKubectlPortForward(cancelCtx, t, "10445", "443", env.ConciergeAppName+"-proxy", env.ConciergeNamespace) - stdout, stderr := runNmapSSLEnum(t, "127.0.0.1", 10445) + stdout, stderr := testlib.RunNmapSSLEnum(t, "127.0.0.1", 10445) require.Empty(t, stderr) - require.Contains(t, stdout, getExpectedCiphers(ptls.Default), "stdout:\n%s", stdout) + require.Contains(t, stdout, testlib.GetExpectedCiphers(ptls.Default(nil)), "stdout:\n%s", stdout) }) }) diff --git a/test/integration/securetls_fips_test.go b/test/integration/securetls_fips_test.go new file mode 100644 index 00000000..aed0c399 --- /dev/null +++ b/test/integration/securetls_fips_test.go @@ -0,0 +1,191 @@ +// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +//go:build fips_strict +// +build fips_strict + +package integration + +import ( + "context" + "crypto/tls" + "encoding/base64" + "fmt" + "net/http" + "strings" + "testing" + + "github.com/stretchr/testify/require" + "k8s.io/client-go/util/cert" + + "go.pinniped.dev/internal/crypto/ptls" + "go.pinniped.dev/internal/testutil/tlsserver" + "go.pinniped.dev/test/testlib" +) + +// This test mirrors securetls_test.go, but adapted for fips mode. +// e.g. checks for only TLS 1.2 ciphers and checks for the +// list of fips-approved ciphers above. +// TLS checks safe to run in parallel with serial tests, see main_test.go. +func TestSecureTLSPinnipedCLIToKAS_Parallel(t *testing.T) { + _ = testlib.IntegrationEnv(t) + t.Log("testing FIPs tls config") + + server := tlsserver.TLSTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // pinniped CLI uses ptls.Secure when talking to KAS, + // although the distinction doesn't matter much in FIPs mode because + // each of the configs is a wrapper for the same base FIPs config. + secure := ptls.Secure(nil) + tlsserver.AssertTLSConfig(t, r, secure) + w.Header().Set("content-type", "application/json") + fmt.Fprint(w, `{"kind":"TokenCredentialRequest","apiVersion":"login.concierge.pinniped.dev/v1alpha1",`+ + `"status":{"credential":{"token":"some-fancy-token"}}}`) + }), tlsserver.RecordTLSHello) + + ca := tlsserver.TLSTestServerCA(server) + + pinnipedExe := testlib.PinnipedCLIPath(t) + + stdout, stderr := runPinnipedCLI(t, nil, pinnipedExe, "login", "static", + "--token", "does-not-matter", + "--concierge-authenticator-type", "webhook", + "--concierge-authenticator-name", "does-not-matter", + "--concierge-ca-bundle-data", base64.StdEncoding.EncodeToString(ca), + "--concierge-endpoint", server.URL, + "--enable-concierge", + "--credential-cache", "", + ) + + require.Empty(t, stderr) + require.Equal(t, `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1",`+ + `"spec":{"interactive":false},"status":{"expirationTimestamp":null,"token":"some-fancy-token"}} +`, stdout) +} + +// TLS checks safe to run in parallel with serial tests, see main_test.go. +func TestSecureTLSPinnipedCLIToSupervisor_Parallel(t *testing.T) { + _ = testlib.IntegrationEnv(t) + + server := tlsserver.TLSTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // pinniped CLI uses ptls.Default when talking to supervisor, + // although the distinction doesn't matter much in FIPs mode because + // each of the configs is a wrapper for the same base FIPs config. + defaultTLS := ptls.Default(nil) + tlsserver.AssertTLSConfig(t, r, defaultTLS) + w.Header().Set("content-type", "application/json") + fmt.Fprint(w, `{"issuer":"https://not-a-good-issuer"}`) + }), tlsserver.RecordTLSHello) + + ca := tlsserver.TLSTestServerCA(server) + + pinnipedExe := testlib.PinnipedCLIPath(t) + + stdout, stderr := runPinnipedCLI(&fakeT{T: t}, nil, pinnipedExe, "login", "oidc", + "--ca-bundle-data", base64.StdEncoding.EncodeToString(ca), + "--issuer", server.URL, + "--credential-cache", "", + "--upstream-identity-provider-flow", "cli_password", + "--upstream-identity-provider-name", "does-not-matter", + "--upstream-identity-provider-type", "oidc", + ) + + require.Equal(t, `Error: could not complete Pinniped login: could not perform OIDC discovery for "`+ + server.URL+`": oidc: issuer did not match the issuer returned by provider, expected "`+ + server.URL+`" got "https://not-a-good-issuer" +`, stderr) + require.Empty(t, stdout) +} + +// TLS checks safe to run in parallel with serial tests, see main_test.go. +func TestSecureTLSConciergeAggregatedAPI_Parallel(t *testing.T) { + env := testlib.IntegrationEnv(t) + + cancelCtx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + + startKubectlPortForward(cancelCtx, t, "10446", "443", env.ConciergeAppName+"-api", env.ConciergeNamespace) + + stdout, stderr := testlib.RunNmapSSLEnum(t, "127.0.0.1", 10446) + + require.Empty(t, stderr) + secure := ptls.Secure(nil) + require.Contains(t, stdout, testlib.GetExpectedCiphers(secure), "stdout:\n%s", stdout) +} + +func TestSecureTLSSupervisor(t *testing.T) { // does not run in parallel because of the createSupervisorDefaultTLSCertificateSecretIfNeeded call + env := testlib.IntegrationEnv(t) + + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + + startKubectlPortForward(ctx, t, "10447", "443", env.SupervisorAppName+"-nodeport", env.SupervisorNamespace) + + stdout, stderr := testlib.RunNmapSSLEnum(t, "127.0.0.1", 10447) + + // supervisor's cert is ECDSA + defaultECDSAOnly := ptls.Default(nil) + ciphers := make([]uint16, 0, len(defaultECDSAOnly.CipherSuites)/3) + for _, id := range defaultECDSAOnly.CipherSuites { + id := id + if !strings.Contains(tls.CipherSuiteName(id), "_ECDSA_") { + continue + } + ciphers = append(ciphers, id) + } + defaultECDSAOnly.CipherSuites = ciphers + + require.Empty(t, stderr) + require.Contains(t, stdout, testlib.GetExpectedCiphers(defaultECDSAOnly), "stdout:\n%s", stdout) +} + +// this test ensures that if the list of default fips cipher +// suites changes, we will know. +func TestFIPSCipherSuites_Parallel(t *testing.T) { + _ = testlib.IntegrationEnv(t) + server := tlsserver.TLSTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // use the default fips config which contains a hard coded list of cipher suites + // that should be equal to the default list of fips cipher suites. + defaultTLS := ptls.Default(nil) + // assert that the client hello response has the same tls config as this test server. + tlsserver.AssertTLSConfig(t, r, defaultTLS) + }), tlsserver.RecordTLSHello) + + ca := tlsserver.TLSTestServerCA(server) + pool, err := cert.NewPoolFromBytes(ca) + require.NoError(t, err) + // create a tls config that does not explicitly set cipher suites, + // and therefore uses goboring's default fips ciphers. + defaultConfig := &tls.Config{ + RootCAs: pool, + NextProtos: ptls.Default(nil).NextProtos, + } + transport := http.Transport{ + TLSClientConfig: defaultConfig, + ForceAttemptHTTP2: true, + } + // make a request against the test server, which will validate that the + // tls config of the client without explicitly set ciphers + // is the same as the tls config of the test server with explicitly + // set ciphers from ptls. + request, _ := http.NewRequest("GET", server.URL, nil) + response, err := transport.RoundTrip(request) + require.NoError(t, err) + require.Equal(t, http.StatusOK, response.StatusCode) +} + +type fakeT struct { + *testing.T +} + +func (t *fakeT) FailNow() { + t.Errorf("fakeT ignored FailNow") +} + +func (t *fakeT) Errorf(format string, args ...interface{}) { + t.Cleanup(func() { + if !t.Failed() { + return + } + t.Logf("reporting previously ignored errors since main test failed:\n"+format, args...) + }) +} diff --git a/test/integration/securetls_test.go b/test/integration/securetls_test.go index 29d281ff..05ef2586 100644 --- a/test/integration/securetls_test.go +++ b/test/integration/securetls_test.go @@ -1,23 +1,19 @@ -// Copyright 2021 the Pinniped contributors. All Rights Reserved. +// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +//go:build !fips_strict +// +build !fips_strict + package integration import ( - "bytes" "context" "crypto/tls" - "crypto/x509" "encoding/base64" "fmt" "net/http" - "os/exec" - "regexp" - "sort" - "strconv" "strings" "testing" - "time" "github.com/stretchr/testify/require" @@ -96,10 +92,10 @@ func TestSecureTLSConciergeAggregatedAPI_Parallel(t *testing.T) { startKubectlPortForward(cancelCtx, t, "10446", "443", env.ConciergeAppName+"-api", env.ConciergeNamespace) - stdout, stderr := runNmapSSLEnum(t, "127.0.0.1", 10446) + stdout, stderr := testlib.RunNmapSSLEnum(t, "127.0.0.1", 10446) require.Empty(t, stderr) - require.Contains(t, stdout, getExpectedCiphers(ptls.Secure), "stdout:\n%s", stdout) + require.Contains(t, stdout, testlib.GetExpectedCiphers(ptls.Secure(nil)), "stdout:\n%s", stdout) } func TestSecureTLSSupervisor(t *testing.T) { // does not run in parallel because of the createSupervisorDefaultTLSCertificateSecretIfNeeded call @@ -110,25 +106,22 @@ func TestSecureTLSSupervisor(t *testing.T) { // does not run in parallel because startKubectlPortForward(ctx, t, "10447", "443", env.SupervisorAppName+"-nodeport", env.SupervisorNamespace) - stdout, stderr := runNmapSSLEnum(t, "127.0.0.1", 10447) + stdout, stderr := testlib.RunNmapSSLEnum(t, "127.0.0.1", 10447) // supervisor's cert is ECDSA - defaultECDSAOnly := func(rootCAs *x509.CertPool) *tls.Config { - c := ptls.Default(rootCAs) - ciphers := make([]uint16, 0, len(c.CipherSuites)/2) - for _, id := range c.CipherSuites { - id := id - if !strings.Contains(tls.CipherSuiteName(id), "_ECDSA_") { - continue - } - ciphers = append(ciphers, id) + defaultECDSAOnly := ptls.Default(nil) + ciphers := make([]uint16, 0, len(defaultECDSAOnly.CipherSuites)/2) + for _, id := range defaultECDSAOnly.CipherSuites { + id := id + if !strings.Contains(tls.CipherSuiteName(id), "_ECDSA_") { + continue } - c.CipherSuites = ciphers - return c + ciphers = append(ciphers, id) } + defaultECDSAOnly.CipherSuites = ciphers require.Empty(t, stderr) - require.Contains(t, stdout, getExpectedCiphers(defaultECDSAOnly), "stdout:\n%s", stdout) + require.Contains(t, stdout, testlib.GetExpectedCiphers(defaultECDSAOnly), "stdout:\n%s", stdout) } type fakeT struct { @@ -147,107 +140,3 @@ func (t *fakeT) Errorf(format string, args ...interface{}) { t.Logf("reporting previously ignored errors since main test failed:\n"+format, args...) }) } - -func runNmapSSLEnum(t *testing.T, host string, port uint16) (string, string) { - t.Helper() - - ctx, cancel := context.WithTimeout(context.Background(), time.Minute) - defer cancel() - - version, err := exec.CommandContext(ctx, "nmap", "-V").CombinedOutput() - require.NoError(t, err) - - versionMatches := regexp.MustCompile(`Nmap version 7\.(?P\d+)`).FindStringSubmatch(string(version)) - require.Len(t, versionMatches, 2) - minorVersion, err := strconv.Atoi(versionMatches[1]) - require.NoError(t, err) - require.GreaterOrEqual(t, minorVersion, 92, "nmap >= 7.92.x is required") - - var stdout, stderr bytes.Buffer - //nolint:gosec // we are not performing malicious argument injection against ourselves - cmd := exec.CommandContext(ctx, "nmap", "--script", "ssl-enum-ciphers", - "-p", strconv.FormatUint(uint64(port), 10), - host, - ) - cmd.Stdout = &stdout - cmd.Stderr = &stderr - - require.NoErrorf(t, cmd.Run(), "stderr:\n%s\n\nstdout:\n%s\n\n", stderr.String(), stdout.String()) - - return stdout.String(), stderr.String() -} - -func getExpectedCiphers(configFunc ptls.ConfigFunc) string { - config := configFunc(nil) - secureConfig := ptls.Secure(nil) - - skip12 := config.MinVersion == secureConfig.MinVersion - - var tls12Bit, tls13Bit string - - if !skip12 { - sort.SliceStable(config.CipherSuites, func(i, j int) bool { - a := tls.CipherSuiteName(config.CipherSuites[i]) - b := tls.CipherSuiteName(config.CipherSuites[j]) - - ok1 := strings.Contains(a, "_ECDSA_") - ok2 := strings.Contains(b, "_ECDSA_") - - if ok1 && ok2 { - return false - } - - return ok1 - }) - - var s strings.Builder - for i, id := range config.CipherSuites { - s.WriteString(fmt.Sprintf(tls12Item, tls.CipherSuiteName(id))) - if i == len(config.CipherSuites)-1 { - break - } - s.WriteString("\n") - } - tls12Bit = fmt.Sprintf(tls12Base, s.String()) - } - - var s strings.Builder - for i, id := range secureConfig.CipherSuites { - s.WriteString(fmt.Sprintf(tls13Item, strings.Replace(tls.CipherSuiteName(id), "TLS_", "TLS_AKE_WITH_", 1))) - if i == len(secureConfig.CipherSuites)-1 { - break - } - s.WriteString("\n") - } - tls13Bit = fmt.Sprintf(tls13Base, s.String()) - - return fmt.Sprintf(baseItem, tls12Bit, tls13Bit) -} - -const ( - // this surrounds the tls 1.2 and 1.3 text in a way that guarantees that other TLS versions are not supported. - baseItem = `/tcp open unknown -| ssl-enum-ciphers: %s%s -|_ least strength: A - -Nmap done: 1 IP address (1 host up) scanned in` - - // the "cipher preference: client" bit a bug in nmap. - // https://github.com/nmap/nmap/issues/1691#issuecomment-536919978 - tls12Base = ` -| TLSv1.2: -| ciphers: -%s -| compressors: -| NULL -| cipher preference: client` - - tls13Base = ` -| TLSv1.3: -| ciphers: -%s -| cipher preference: server` - - tls12Item = `| %s (secp256r1) - A` - tls13Item = `| %s (ecdh_x25519) - A` -) diff --git a/test/integration/supervisor_discovery_test.go b/test/integration/supervisor_discovery_test.go index 4274d880..9991fa01 100644 --- a/test/integration/supervisor_discovery_test.go +++ b/test/integration/supervisor_discovery_test.go @@ -28,6 +28,7 @@ import ( "go.pinniped.dev/generated/latest/apis/supervisor/config/v1alpha1" pinnipedclientset "go.pinniped.dev/generated/latest/client/supervisor/clientset/versioned" "go.pinniped.dev/internal/certauthority" + "go.pinniped.dev/internal/crypto/ptls" "go.pinniped.dev/internal/here" "go.pinniped.dev/test/testlib" ) @@ -660,7 +661,7 @@ func newHTTPClient(t *testing.T, caBundle string, dnsOverrides map[string]string caCertPool.AppendCertsFromPEM([]byte(caBundle)) c.Transport = &http.Transport{ DialContext: overrideDialContext, - TLSClientConfig: &tls.Config{MinVersion: tls.VersionTLS13, RootCAs: caCertPool}, + TLSClientConfig: &tls.Config{MinVersion: ptls.SecureTLSConfigMinTLSVersion, RootCAs: caCertPool}, //nolint: gosec // this seems to be a false flag, min tls version is 1.3 in normal mode or 1.2 in fips mode } } else { c.Transport = &http.Transport{ diff --git a/test/testlib/securetls.go b/test/testlib/securetls.go new file mode 100644 index 00000000..3777c1aa --- /dev/null +++ b/test/testlib/securetls.go @@ -0,0 +1,142 @@ +// Copyright 2022 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package testlib + +import ( + "bytes" + "context" + "crypto/tls" + "fmt" + "os/exec" + "regexp" + "sort" + "strconv" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "go.pinniped.dev/internal/crypto/ptls" +) + +func RunNmapSSLEnum(t *testing.T, host string, port uint16) (string, string) { + t.Helper() + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + version, err := exec.CommandContext(ctx, "nmap", "-V").CombinedOutput() + require.NoError(t, err) + + versionMatches := regexp.MustCompile(`Nmap version 7\.(?P\d+)`).FindStringSubmatch(string(version)) + require.Len(t, versionMatches, 2) + minorVersion, err := strconv.Atoi(versionMatches[1]) + require.NoError(t, err) + require.GreaterOrEqual(t, minorVersion, 92, "nmap >= 7.92.x is required") + + var stdout, stderr bytes.Buffer + //nolint:gosec // we are not performing malicious argument injection against ourselves + cmd := exec.CommandContext(ctx, "nmap", "--script", "ssl-enum-ciphers", + "-p", strconv.FormatUint(uint64(port), 10), + host, + ) + cmd.Stdout = &stdout + cmd.Stderr = &stderr + + require.NoErrorf(t, cmd.Run(), "stderr:\n%s\n\nstdout:\n%s\n\n", stderr.String(), stdout.String()) + + return stdout.String(), stderr.String() +} + +func GetExpectedCiphers(config *tls.Config) string { + secureConfig := ptls.Secure(nil) + + skip12 := config.MinVersion == tls.VersionTLS13 + + var tls12Bit, tls13Bit string + + if !skip12 { + sort.SliceStable(config.CipherSuites, func(i, j int) bool { + a := tls.CipherSuiteName(config.CipherSuites[i]) + b := tls.CipherSuiteName(config.CipherSuites[j]) + + ok1 := strings.Contains(a, "_ECDSA_") + ok2 := strings.Contains(b, "_ECDSA_") + + if ok1 && ok2 { + return false + } + + return ok1 + }) + + var s strings.Builder + for i, id := range config.CipherSuites { + name := tls.CipherSuiteName(id) + group := "" + if strings.Contains(name, "_ECDHE_") { + group = secp256r1 + } else { + group = rsa2048 + } + s.WriteString(fmt.Sprintf(tls12Item, name, group)) + if i == len(config.CipherSuites)-1 { + break + } + s.WriteString("\n") + } + tls12Bit = fmt.Sprintf(tls12Base, s.String(), getCipherSuitePreference()) + } + + skip13 := config.MaxVersion == tls.VersionTLS12 + if !skip13 { + var s strings.Builder + for i, id := range secureConfig.CipherSuites { + s.WriteString(fmt.Sprintf(tls13Item, strings.Replace(tls.CipherSuiteName(id), "TLS_", "TLS_AKE_WITH_", 1))) + if i == len(secureConfig.CipherSuites)-1 { + break + } + s.WriteString("\n") + } + tls13Bit = fmt.Sprintf(tls13Base, s.String()) + } + + return fmt.Sprintf(baseItem, tls12Bit, tls13Bit) +} + +const ( + // this surrounds the tls 1.2 and 1.3 text in a way that guarantees that other TLS versions are not supported. + baseItem = `/tcp open unknown +| ssl-enum-ciphers: %s%s +|_ least strength: A + +Nmap done: 1 IP address (1 host up) scanned in` + + // cipher preference is a variable because in FIPs mode it is server + // but in normal mode it is client. + tls12Base = ` +| TLSv1.2: +| ciphers: +%s +| compressors: +| NULL +| cipher preference: %s` + + tls12Item = `| %s (%s) - A` + + tls13Base = ` +| TLSv1.3: +| ciphers: +%s +| cipher preference: server` + + // This curve name is part of the output for each of our elliptic curve ciphers. + // secp256r1 is also known as P-256. + secp256r1 = "secp256r1" + // For the RSA ciphers, we expect this output to be RSA 2048. + rsa2048 = "rsa 2048" + + tls13Item = `| %s (ecdh_x25519) - A` +) diff --git a/test/testlib/securetls_preference_fips.go b/test/testlib/securetls_preference_fips.go new file mode 100644 index 00000000..3da1f5df --- /dev/null +++ b/test/testlib/securetls_preference_fips.go @@ -0,0 +1,15 @@ +// Copyright 2022 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +//go:build fips_strict +// +build fips_strict + +package testlib + +// Because of a bug in nmap, the cipher suite preference is +// incorrectly shown as 'client' in some cases. +// in fips-only mode, it correctly shows the cipher preference +// as 'server', while in non-fips mode it shows as 'client'. +func getCipherSuitePreference() string { + return "server" +} diff --git a/test/testlib/securetls_preference_nonfips.go b/test/testlib/securetls_preference_nonfips.go new file mode 100644 index 00000000..86bf0edc --- /dev/null +++ b/test/testlib/securetls_preference_nonfips.go @@ -0,0 +1,15 @@ +// Copyright 2022 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +//go:build !fips_strict +// +build !fips_strict + +package testlib + +// Because of a bug in nmap, the cipher suite preference is +// incorrectly shown as 'client' in some cases. +// in fips-only mode, it correctly shows the cipher preference +// as 'server', while in non-fips mode it shows as 'client'. +func getCipherSuitePreference() string { + return "client" +}