mirror of
https://github.com/vmware-tanzu/pinniped.git
synced 2026-01-04 20:24:26 +00:00
Add integration test for allowed ciphers
This commit is contained in:
committed by
Ryan Richard
parent
53031ad8d4
commit
4ab2ed10f5
@@ -1523,7 +1523,7 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl
|
||||
stdout, stderr := testlib.RunNmapSSLEnum(t, "127.0.0.1", 10445)
|
||||
|
||||
require.Empty(t, stderr)
|
||||
require.Contains(t, stdout, testlib.GetExpectedCiphers(ptls.Default(nil)), "stdout:\n%s", stdout)
|
||||
require.Contains(t, stdout, testlib.GetExpectedCiphers(ptls.Default(nil), testlib.DefaultCipherSuitePreference), "stdout:\n%s", stdout)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
29
test/integration/limited_ciphers_fips_test.go
Normal file
29
test/integration/limited_ciphers_fips_test.go
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright 2024 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//go:build fips_strict
|
||||
|
||||
package integration
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestLimitedCiphers_Disruptive will confirm that the Pinniped Supervisor exposes only those ciphers listed in
|
||||
// configuration.
|
||||
// This does not test the Concierge (which has the same feature) since the Concierge does not have exposed API
|
||||
// endpoints with the Default profile.
|
||||
// This does not test the CLI, since it does not have a feature to limit cipher suites.
|
||||
func TestLimitedCiphersFIPS_Disruptive(t *testing.T) {
|
||||
performLimitedCiphersTest(t,
|
||||
[]string{
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_RSA_WITH_AES_256_GCM_SHA384", // this is an insecure cipher but allowed for FIPS
|
||||
},
|
||||
[]uint16{
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||
})
|
||||
}
|
||||
30
test/integration/limited_ciphers_test.go
Normal file
30
test/integration/limited_ciphers_test.go
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright 2024 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//go:build !fips_strict
|
||||
|
||||
package integration
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestLimitedCiphersNotFIPS_Disruptive will confirm that the Pinniped Supervisor exposes only those ciphers listed in
|
||||
// configuration.
|
||||
// This does not test the Concierge (which has the same feature) since the Concierge does not have exposed API
|
||||
// endpoints with the Default profile.
|
||||
// This does not test the CLI, since it does not have a feature to limit cipher suites.
|
||||
func TestLimitedCiphersNotFIPS_Disruptive(t *testing.T) {
|
||||
performLimitedCiphersTest(t,
|
||||
[]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",
|
||||
},
|
||||
[]uint16{
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
})
|
||||
}
|
||||
156
test/integration/limited_ciphers_utils_test.go
Normal file
156
test/integration/limited_ciphers_utils_test.go
Normal file
@@ -0,0 +1,156 @@
|
||||
// Copyright 2024 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package integration
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"go.pinniped.dev/internal/config/supervisor"
|
||||
"go.pinniped.dev/test/testlib"
|
||||
)
|
||||
|
||||
func performLimitedCiphersTest(t *testing.T, allowedCiphers []string, expectedCiphers []uint16) {
|
||||
env := testOnKindWithPodShutdown(t)
|
||||
|
||||
client := testlib.NewKubernetesClientset(t)
|
||||
configMapClient := client.CoreV1().ConfigMaps(env.SupervisorNamespace)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||
t.Cleanup(cancel)
|
||||
|
||||
staticConfigMapName := env.SupervisorAppName + "-static-config"
|
||||
supervisorStaticConfigMap, err := configMapClient.Get(ctx, staticConfigMapName, metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
originalSupervisorConfig := supervisorStaticConfigMap.Data["pinniped.yaml"]
|
||||
require.NotEmpty(t, originalSupervisorConfig)
|
||||
|
||||
t.Cleanup(func() {
|
||||
supervisorStaticConfigMapCleanup, err := configMapClient.Get(ctx, staticConfigMapName, metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
supervisorStaticConfigMapCleanup.Data = make(map[string]string)
|
||||
supervisorStaticConfigMapCleanup.Data["pinniped.yaml"] = originalSupervisorConfig
|
||||
|
||||
_, err = configMapClient.Update(ctx, supervisorStaticConfigMapCleanup, metav1.UpdateOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
// this will cycle all the pods
|
||||
restartAllPodsOfApp(t, env.SupervisorNamespace, env.SupervisorAppName, false)
|
||||
})
|
||||
|
||||
var config supervisor.Config
|
||||
err = yaml.Unmarshal([]byte(originalSupervisorConfig), &config)
|
||||
require.NoError(t, err)
|
||||
|
||||
// As a precondition of this test, ensure that the list of allowedCiphers is empty
|
||||
require.Empty(t, config.TLS.OneDotTwo.AllowedCiphers)
|
||||
|
||||
config.TLS.OneDotTwo.AllowedCiphers = allowedCiphers
|
||||
|
||||
updatedSupervisorConfig, err := yaml.Marshal(config)
|
||||
require.NoError(t, err)
|
||||
|
||||
supervisorStaticConfigMap.Data = make(map[string]string)
|
||||
supervisorStaticConfigMap.Data["pinniped.yaml"] = string(updatedSupervisorConfig)
|
||||
|
||||
_, err = configMapClient.Update(ctx, supervisorStaticConfigMap, metav1.UpdateOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
// this will cycle all the pods
|
||||
restartAllPodsOfApp(t, env.SupervisorNamespace, env.SupervisorAppName, false)
|
||||
|
||||
startKubectlPortForward(ctx, t, "10509", "443", env.SupervisorAppName+"-nodeport", env.SupervisorNamespace)
|
||||
stdout, stderr := testlib.RunNmapSSLEnum(t, "127.0.0.1", 10509)
|
||||
require.Empty(t, stderr)
|
||||
|
||||
expectedCiphersConfig := &tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
MaxVersion: testlib.MaxTLSVersion,
|
||||
CipherSuites: expectedCiphers,
|
||||
}
|
||||
|
||||
require.Contains(t, stdout, testlib.GetExpectedCiphers(expectedCiphersConfig, "server"), "stdout:\n%s", stdout)
|
||||
|
||||
}
|
||||
|
||||
// restartAllPodsOfApp will immediately scale to 0 and then scale back.
|
||||
// There are no uses of t.Cleanup since these actions need to happen immediately.
|
||||
func restartAllPodsOfApp(
|
||||
t *testing.T,
|
||||
namespace string,
|
||||
appName string,
|
||||
isConcierge bool,
|
||||
) {
|
||||
t.Helper()
|
||||
|
||||
ignorePodsWithNameSubstring := ""
|
||||
if isConcierge {
|
||||
ignorePodsWithNameSubstring = "-kube-cert-agent-"
|
||||
}
|
||||
|
||||
// Precondition: the app should have some pods running initially.
|
||||
initialPods := getRunningPodsByNamePrefix(t, namespace, appName+"-", ignorePodsWithNameSubstring)
|
||||
require.Greater(t, len(initialPods), 0)
|
||||
|
||||
// Scale down the deployment's number of replicas to 0, which will shut down all the pods.
|
||||
originalScale := updateDeploymentScale(t, namespace, appName, 0)
|
||||
|
||||
testlib.RequireEventually(t, func(requireEventually *require.Assertions) {
|
||||
newPods := getRunningPodsByNamePrefix(t, namespace, appName+"-", ignorePodsWithNameSubstring)
|
||||
requireEventually.Len(newPods, 0, "wanted zero pods")
|
||||
}, 2*time.Minute, 200*time.Millisecond)
|
||||
|
||||
// Reset the application to its original scale.
|
||||
updateDeploymentScale(t, namespace, appName, originalScale)
|
||||
|
||||
testlib.RequireEventually(t, func(requireEventually *require.Assertions) {
|
||||
newPods := getRunningPodsByNamePrefix(t, namespace, appName+"-", ignorePodsWithNameSubstring)
|
||||
requireEventually.Len(newPods, originalScale, "wanted %d pods", originalScale)
|
||||
}, 2*time.Minute, 200*time.Millisecond)
|
||||
}
|
||||
|
||||
// TestRemoveAllowedCiphersFromStaticConfig_Disruptive updates the Supervisor's static configuration to make sure that the allowed ciphers list is empty.
|
||||
// It will restart the Supervisor pods. Skipped because it's only here for local testing purposes.
|
||||
func TestRemoveAllowedCiphersFromStaticConfig_Disruptive(t *testing.T) {
|
||||
t.Skip()
|
||||
|
||||
env := testOnKindWithPodShutdown(t)
|
||||
|
||||
client := testlib.NewKubernetesClientset(t)
|
||||
configMapClient := client.CoreV1().ConfigMaps(env.SupervisorNamespace)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
staticConfigMapName := env.SupervisorAppName + "-static-config"
|
||||
supervisorStaticConfigMap, err := configMapClient.Get(ctx, staticConfigMapName, metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
originalSupervisorConfig := supervisorStaticConfigMap.Data["pinniped.yaml"]
|
||||
require.NotEmpty(t, originalSupervisorConfig)
|
||||
|
||||
var config supervisor.Config
|
||||
err = yaml.Unmarshal([]byte(originalSupervisorConfig), &config)
|
||||
require.NoError(t, err)
|
||||
|
||||
config.TLS.OneDotTwo.AllowedCiphers = nil
|
||||
|
||||
updatedConfigBytes, err := yaml.Marshal(config)
|
||||
require.NoError(t, err)
|
||||
|
||||
supervisorStaticConfigMap.Data = make(map[string]string)
|
||||
supervisorStaticConfigMap.Data["pinniped.yaml"] = string(updatedConfigBytes)
|
||||
|
||||
_, err = configMapClient.Update(ctx, supervisorStaticConfigMap, metav1.UpdateOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
// this will cycle all the pods
|
||||
restartAllPodsOfApp(t, env.SupervisorNamespace, env.SupervisorAppName, false)
|
||||
}
|
||||
@@ -94,7 +94,7 @@ func TestSecureTLSConciergeAggregatedAPI_Parallel(t *testing.T) {
|
||||
stdout, stderr := testlib.RunNmapSSLEnum(t, "127.0.0.1", 10446)
|
||||
|
||||
require.Empty(t, stderr)
|
||||
require.Contains(t, stdout, testlib.GetExpectedCiphers(ptls.Secure(nil)), "stdout:\n%s", stdout)
|
||||
require.Contains(t, stdout, testlib.GetExpectedCiphers(ptls.Secure(nil), testlib.DefaultCipherSuitePreference), "stdout:\n%s", stdout)
|
||||
}
|
||||
|
||||
// TLS checks safe to run in parallel with serial tests, see main_test.go.
|
||||
@@ -109,7 +109,7 @@ func TestSecureTLSSupervisorAggregatedAPI_Parallel(t *testing.T) {
|
||||
stdout, stderr := testlib.RunNmapSSLEnum(t, "127.0.0.1", 10447)
|
||||
|
||||
require.Empty(t, stderr)
|
||||
require.Contains(t, stdout, testlib.GetExpectedCiphers(ptls.Secure(nil)), "stdout:\n%s", stdout)
|
||||
require.Contains(t, stdout, testlib.GetExpectedCiphers(ptls.Secure(nil), testlib.DefaultCipherSuitePreference), "stdout:\n%s", stdout)
|
||||
}
|
||||
|
||||
func TestSecureTLSSupervisor(t *testing.T) {
|
||||
@@ -136,7 +136,7 @@ func TestSecureTLSSupervisor(t *testing.T) {
|
||||
defaultECDSAOnly.CipherSuites = ciphers
|
||||
|
||||
require.Empty(t, stderr)
|
||||
require.Contains(t, stdout, testlib.GetExpectedCiphers(defaultECDSAOnly), "stdout:\n%s", stdout)
|
||||
require.Contains(t, stdout, testlib.GetExpectedCiphers(defaultECDSAOnly, testlib.DefaultCipherSuitePreference), "stdout:\n%s", stdout)
|
||||
}
|
||||
|
||||
type fakeT struct {
|
||||
|
||||
@@ -50,7 +50,7 @@ func RunNmapSSLEnum(t *testing.T, host string, port uint16) (string, string) {
|
||||
return stdout.String(), stderr.String()
|
||||
}
|
||||
|
||||
func GetExpectedCiphers(config *tls.Config) string {
|
||||
func GetExpectedCiphers(config *tls.Config, preference string) string {
|
||||
skip12 := config.MinVersion == tls.VersionTLS13
|
||||
skip13 := config.MaxVersion == tls.VersionTLS12
|
||||
|
||||
@@ -86,7 +86,7 @@ func GetExpectedCiphers(config *tls.Config) string {
|
||||
}
|
||||
s.WriteString("\n")
|
||||
}
|
||||
tls12Bit = fmt.Sprintf(tls12Base, s.String(), cipherSuitePreference)
|
||||
tls12Bit = fmt.Sprintf(tls12Base, s.String(), preference)
|
||||
}
|
||||
|
||||
if !skip13 {
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
// Copyright 2022-2023 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2022-2024 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//go:build fips_strict
|
||||
|
||||
package testlib
|
||||
|
||||
import "crypto/tls"
|
||||
|
||||
// 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'.
|
||||
const cipherSuitePreference = "server"
|
||||
const DefaultCipherSuitePreference = "server"
|
||||
|
||||
const MaxTLSVersion = tls.VersionTLS12
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
// Copyright 2022-2023 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2022-2024 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//go:build !fips_strict
|
||||
|
||||
package testlib
|
||||
|
||||
import "crypto/tls"
|
||||
|
||||
// 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'.
|
||||
const cipherSuitePreference = "client"
|
||||
const DefaultCipherSuitePreference = "client"
|
||||
|
||||
const MaxTLSVersion = tls.VersionTLS13
|
||||
|
||||
Reference in New Issue
Block a user