Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ verify: verify-podnetworkconnectivitychecks
verify-podnetworkconnectivitychecks:
$(MAKE) -C pkg/operator/connectivitycheckcontroller verify

.PHONY: update-mozilla-guidelines
update-mozilla-guidelines:
curl -sS -o pkg/crypto/testfiles/mozilla-guidelines.json https://ssl-config.mozilla.org/guidelines/latest.json
@echo "Mozilla TLS configuration guidelines updated to latest version"

test-e2e-encryption: GO_TEST_PACKAGES :=./test/e2e-encryption/...
.PHONY: test-e2e-encryption

Expand Down
130 changes: 130 additions & 0 deletions pkg/crypto/crypto_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ package crypto

import (
"crypto"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
_ "embed"
"encoding/json"
"fmt"
"go/importer"
"os"
Expand Down Expand Up @@ -549,3 +552,130 @@ func TestServerCertRegeneration(t *testing.T) {
require.NotNil(t, serverCert)
require.True(t, created)
}

//go:embed testfiles/mozilla-guidelines.json
var guidelinesJSON []byte

// TestDefaultCiphersAgainstMozillaGuidelines compares our DefaultCiphers() with the Mozilla TLS
// configuration guidelines stored in testfiles/mozilla-guidelines.json.
func TestDefaultCiphersAgainstMozillaGuidelines(t *testing.T) {

// Parse the JSON
var guidelines struct {
Version float64 `json:"version"`
Configurations map[string]struct {
TLSVersions []string `json:"tls_versions"`
Ciphers map[string][]string `json:"ciphers"`
Ciphersuites []string `json:"ciphersuites"` // TLS 1.3 ciphers
} `json:"configurations"`
}
if err := json.Unmarshal(guidelinesJSON, &guidelines); err != nil {
t.Fatalf("Failed to parse Mozilla guidelines JSON: %v", err)
}

// Get Intermediate profile
intermediate, ok := guidelines.Configurations["intermediate"]
if !ok {
t.Fatalf("Mozilla guidelines missing 'intermediate' configuration")
}

// Get Go cipher names from Mozilla (they use IANA names)
mozillaCipherNames := sets.New[string]()
mozillaCipherIDs := sets.New[uint16]()
unknownCiphers := []string{}

// Process TLS 1.2 ciphers (from "go" field)
goCiphers := intermediate.Ciphers["go"]
for _, cipherName := range goCiphers {
mozillaCipherNames.Insert(cipherName)

// Convert IANA name to cipher suite ID
cipherID, ok := ciphers[cipherName]
if ok {
mozillaCipherIDs.Insert(cipherID)
} else {
unknownCiphers = append(unknownCiphers, cipherName)
}
}

// Process TLS 1.3 ciphersuites
for _, cipherName := range intermediate.Ciphersuites {
mozillaCipherNames.Insert(cipherName)

// Convert IANA name to cipher suite ID
cipherID, ok := ciphers[cipherName]
if ok {
mozillaCipherIDs.Insert(cipherID)
} else {
unknownCiphers = append(unknownCiphers, cipherName)
}
}

// Get our default ciphers
ourCiphers := DefaultCiphers()
ourCipherIDs := sets.New[uint16](ourCiphers...)
ourCipherNames := sets.New[string]()
for _, id := range ourCiphers {
ourCipherNames.Insert(CipherSuiteToNameOrDie(id))
}

// Find discrepancies
deprecated := ourCipherIDs.Difference(mozillaCipherIDs)
missing := mozillaCipherIDs.Difference(ourCipherIDs)

// Report findings
t.Logf("Mozilla SSL Configuration Guidelines Version: %.1f", guidelines.Version)
t.Logf("Mozilla Intermediate Profile: TLS Versions %v", intermediate.TLSVersions)
t.Logf("Mozilla Intermediate Profile: %d cipher suites", len(intermediate.Ciphers["go"])+len(intermediate.Ciphersuites))
t.Logf("DefaultCiphers(): %d cipher suites", len(ourCiphers))

if len(unknownCiphers) > 0 {
t.Errorf("Mozilla recommends %d cipher(s) that are not in our cipher mapping", len(unknownCiphers))
for _, cipher := range unknownCiphers {
t.Errorf(" - %s (OpenSSL name)", cipher)
}
t.Errorf("To fix: Add these ciphers to DefaultCiphers() in crypto.go, or update the cached guidelines file with 'make update-mozilla-guidelines'")
}

if len(deprecated) > 0 {
t.Errorf("DefaultCiphers() includes %d deprecated cipher(s) not in Mozilla Intermediate recommendations", len(deprecated))
var deprecatedNames []string
for id := range deprecated {
name := CipherSuiteToNameOrDie(id)
deprecatedNames = append(deprecatedNames, name)
}
sort.Strings(deprecatedNames)
for _, name := range deprecatedNames {
t.Errorf(" - %s", name)
}
t.Errorf("To fix: Remove these ciphers from DefaultCiphers() in crypto.go, or update the cached guidelines file with 'make update-mozilla-guidelines'")
}

if len(missing) > 0 {
t.Errorf("DefaultCiphers() is missing %d cipher(s) recommended by Mozilla Intermediate profile", len(missing))
var missingNames []string
for id := range missing {
// Use tls.CipherSuiteName if available, otherwise lookup in our map
name := tls.CipherSuiteName(id)
if name == "" || !strings.HasPrefix(name, "TLS_") {
// Fallback to our mapping
for k, v := range ciphers {
if v == id {
name = k
break
}
}
}
missingNames = append(missingNames, name)
}
sort.Strings(missingNames)
for _, name := range missingNames {
t.Errorf(" - %s", name)
}
t.Errorf("To fix: Add these ciphers to the ciphers map in crypto.go, or update the cached guidelines file with 'make update-mozilla-guidelines'")
}

if len(deprecated) == 0 && len(missing) == 0 {
t.Logf("DefaultCiphers() is perfectly aligned with Mozilla Intermediate profile")
}
}
209 changes: 209 additions & 0 deletions pkg/crypto/testfiles/mozilla-guidelines.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
{
"version": 5.7,
"href": "https://ssl-config.mozilla.org/guidelines/5.7.json",
"configurations": {
"modern": {
"certificate_curves": ["prime256v1", "secp384r1"],
"certificate_signatures": ["ecdsa-with-SHA256", "ecdsa-with-SHA384", "ecdsa-with-SHA512"],
"certificate_types": ["ecdsa"],
"ciphers": {
"caddy": [],
"go": [],
"iana": [],
"openssl": []
},
"ciphersuites": [
"TLS_AES_128_GCM_SHA256",
"TLS_AES_256_GCM_SHA384",
"TLS_CHACHA20_POLY1305_SHA256"
],
"dh_param_size": null,
"ecdh_param_size": 256,
"hsts_min_age": 63072000,
"maximum_certificate_lifespan": 90,
"ocsp_staple": true,
"oldest_clients": ["Firefox 63", "Android 10.0", "Chrome 70", "Edge 75", "Java 11", "OpenSSL 1.1.1", "Opera 57", "Safari 12.1"],
"recommended_certificate_lifespan": 90,
"rsa_key_size": null,
"server_preferred_order": false,
"tls_curves": ["X25519", "prime256v1", "secp384r1"],
"tls_versions": ["TLSv1.3"]
},
"intermediate": {
"certificate_curves": ["prime256v1", "secp384r1"],
"certificate_signatures": ["sha256WithRSAEncryption", "ecdsa-with-SHA256", "ecdsa-with-SHA384", "ecdsa-with-SHA512"],
"certificate_types": ["ecdsa", "rsa"],
"ciphers": {
"caddy": [
"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"
],
"go": [
"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",
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305"
],
"iana": [
"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",
"TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256"
],
"openssl": [
"ECDHE-ECDSA-AES128-GCM-SHA256",
"ECDHE-RSA-AES128-GCM-SHA256",
"ECDHE-ECDSA-AES256-GCM-SHA384",
"ECDHE-RSA-AES256-GCM-SHA384",
"ECDHE-ECDSA-CHACHA20-POLY1305",
"ECDHE-RSA-CHACHA20-POLY1305",
"DHE-RSA-AES128-GCM-SHA256",
"DHE-RSA-AES256-GCM-SHA384",
"DHE-RSA-CHACHA20-POLY1305"
]
},
"ciphersuites": [
"TLS_AES_128_GCM_SHA256",
"TLS_AES_256_GCM_SHA384",
"TLS_CHACHA20_POLY1305_SHA256"
],
"dh_param_size": 2048,
"ecdh_param_size": 256,
"hsts_min_age": 63072000,
"maximum_certificate_lifespan": 366,
"ocsp_staple": true,
"oldest_clients": ["Firefox 27", "Android 4.4.2", "Chrome 31", "Edge", "IE 11 on Windows 7", "Java 8u31", "OpenSSL 1.0.1", "Opera 20", "Safari 9"],
"recommended_certificate_lifespan": 90,
"rsa_key_size": 2048,
"server_preferred_order": false,
"tls_curves": ["X25519", "prime256v1", "secp384r1"],
"tls_versions": ["TLSv1.2", "TLSv1.3"]
},
"old": {
"certificate_curves": ["prime256v1", "secp384r1"],
"certificate_signatures": ["sha256WithRSAEncryption"],
"certificate_types": ["rsa"],
"ciphers": {
"caddy": [
"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_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
"TLS_RSA_WITH_AES_128_GCM_SHA256",
"TLS_RSA_WITH_AES_256_GCM_SHA384",
"TLS_RSA_WITH_AES_128_CBC_SHA",
"TLS_RSA_WITH_AES_256_CBC_SHA",
"TLS_RSA_WITH_3DES_EDE_CBC_SHA"
],
"go": [
"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",
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
"TLS_RSA_WITH_AES_128_GCM_SHA256",
"TLS_RSA_WITH_AES_256_GCM_SHA384",
"TLS_RSA_WITH_AES_128_CBC_SHA256",
"TLS_RSA_WITH_AES_128_CBC_SHA",
"TLS_RSA_WITH_AES_256_CBC_SHA",
"TLS_RSA_WITH_3DES_EDE_CBC_SHA"
],
"iana": [
"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",
"TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
"TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
"TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
"TLS_RSA_WITH_AES_128_GCM_SHA256",
"TLS_RSA_WITH_AES_256_GCM_SHA384",
"TLS_RSA_WITH_AES_128_CBC_SHA256",
"TLS_RSA_WITH_AES_256_CBC_SHA256",
"TLS_RSA_WITH_AES_128_CBC_SHA",
"TLS_RSA_WITH_AES_256_CBC_SHA",
"TLS_RSA_WITH_3DES_EDE_CBC_SHA"
],
"openssl": [
"ECDHE-ECDSA-AES128-GCM-SHA256",
"ECDHE-RSA-AES128-GCM-SHA256",
"ECDHE-ECDSA-AES256-GCM-SHA384",
"ECDHE-RSA-AES256-GCM-SHA384",
"ECDHE-ECDSA-CHACHA20-POLY1305",
"ECDHE-RSA-CHACHA20-POLY1305",
"DHE-RSA-AES128-GCM-SHA256",
"DHE-RSA-AES256-GCM-SHA384",
"DHE-RSA-CHACHA20-POLY1305",
"ECDHE-ECDSA-AES128-SHA256",
"ECDHE-RSA-AES128-SHA256",
"ECDHE-ECDSA-AES128-SHA",
"ECDHE-RSA-AES128-SHA",
"ECDHE-ECDSA-AES256-SHA384",
"ECDHE-RSA-AES256-SHA384",
"ECDHE-ECDSA-AES256-SHA",
"ECDHE-RSA-AES256-SHA",
"DHE-RSA-AES128-SHA256",
"DHE-RSA-AES256-SHA256",
"AES128-GCM-SHA256",
"AES256-GCM-SHA384",
"AES128-SHA256",
"AES256-SHA256",
"AES128-SHA",
"AES256-SHA",
"DES-CBC3-SHA"
]
},
"ciphersuites": [
"TLS_AES_128_GCM_SHA256",
"TLS_AES_256_GCM_SHA384",
"TLS_CHACHA20_POLY1305_SHA256"
],
"dh_param_size": 1024,
"ecdh_param_size": 256,
"hsts_min_age": 63072000,
"maximum_certificate_lifespan": 366,
"ocsp_staple": true,
"oldest_clients": ["Firefox 1", "Android 2.3", "Chrome 1", "Edge 12", "IE8 on Windows XP", "Java 6", "OpenSSL 0.9.8", "Opera 5", "Safari 1"],
"recommended_certificate_lifespan": 90,
"rsa_key_size": 2048,
"server_preferred_order": true,
"tls_curves": ["X25519", "prime256v1", "secp384r1"],
"tls_versions": ["TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"]
}
}
}