Skip to content
Merged
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
6 changes: 0 additions & 6 deletions .bingo/Variables.mk
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,6 @@ $(CRD_REF_DOCS): $(BINGO_DIR)/crd-ref-docs.mod
@echo "(re)installing $(GOBIN)/crd-ref-docs-v0.3.0"
@cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=crd-ref-docs.mod -o=$(GOBIN)/crd-ref-docs-v0.3.0 "github.com/elastic/crd-ref-docs"

GOJQ := $(GOBIN)/gojq-v0.12.17
$(GOJQ): $(BINGO_DIR)/gojq.mod
@# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies.
@echo "(re)installing $(GOBIN)/gojq-v0.12.17"
@cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=gojq.mod -o=$(GOBIN)/gojq-v0.12.17 "github.com/itchyny/gojq/cmd/gojq"

GOLANGCI_LINT := $(GOBIN)/golangci-lint-v2.8.0
$(GOLANGCI_LINT): $(BINGO_DIR)/golangci-lint.mod
@# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies.
Expand Down
5 changes: 0 additions & 5 deletions .bingo/gojq.mod

This file was deleted.

17 changes: 0 additions & 17 deletions .bingo/gojq.sum

This file was deleted.

2 changes: 0 additions & 2 deletions .bingo/variables.env
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ CRD_DIFF="${GOBIN}/crd-diff-v0.5.1-0.20260309184313-54162f2e3097"

CRD_REF_DOCS="${GOBIN}/crd-ref-docs-v0.3.0"

GOJQ="${GOBIN}/gojq-v0.12.17"

GOLANGCI_LINT="${GOBIN}/golangci-lint-v2.8.0"

GORELEASER="${GOBIN}/goreleaser-v2.11.2"
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,8 @@ fmt: $(YAMLFMT) #EXHELP Formats code
$(YAMLFMT) -gitignore_excludes testdata

.PHONY: update-tls-profiles
update-tls-profiles: $(GOJQ) #EXHELP Update TLS profiles from the Mozilla wiki
env JQ=$(GOJQ) hack/tools/update-tls-profiles.sh
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems to me here we drop the only usage of gojq, but we keep it still in bingo files (.bingo/gojq.mod, .bingo/Variables.mk, ...).
Should we drop it also from bingo?
Should be something like:

bingo-v0.9.0 get gojq@none

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, if not used anywhere else, we should remove it from bingo files as well.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, and rebased, please re-review @pedjak

update-tls-profiles: #EXHELP Update TLS profiles from the Mozilla wiki
hack/tools/update-tls-profiles.sh

.PHONY: update-registryv1-bundle-schema
update-registryv1-bundle-schema: #EXHELP Update registry+v1 bundle configuration JSON schema
Expand Down
125 changes: 2 additions & 123 deletions hack/tools/update-tls-profiles.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,131 +2,10 @@

set -e

if [ -z "${JQ}" ]; then
echo "JQ not defined"
exit 1
fi

OUTPUT=internal/shared/util/tlsprofiles/mozilla_data.go
OUTPUT=internal/shared/util/tlsprofiles/mozilla_data.json
INPUT=https://ssl-config.mozilla.org/guidelines/latest.json

TMPFILE="$(mktemp)"
trap 'rm -rf "$TMPFILE"' EXIT

if ! curl -L -s -f "${INPUT}" > "${TMPFILE}"; then
if ! curl -L -s -f "${INPUT}" -o "${OUTPUT}"; then
echo "ERROR: Failed to download ${INPUT} (HTTP error or connection failure)" >&2
exit 1
fi

if ! ${JQ} empty "${TMPFILE}" 2>/dev/null; then
echo "ERROR: Downloaded data from ${INPUT} is not valid JSON" >&2
exit 1
fi

# Extract stored version from current output file (may be empty on first run)
STORED_VERSION=$(grep '^// DATA VERSION:' "${OUTPUT}" 2>/dev/null | awk '{print $4}' || true)

# Extract version from downloaded JSON and fail early if missing
NEW_VERSION=$(${JQ} -r '.version' "${TMPFILE}")
if [ -z "${NEW_VERSION}" ] || [ "${NEW_VERSION}" = "null" ]; then
echo "ERROR: Could not read .version from ${INPUT}" >&2
exit 1
fi

if [ "${NEW_VERSION}" = "${STORED_VERSION}" ]; then
echo "Mozilla TLS data is already at version ${NEW_VERSION}, skipping regeneration."
exit 0
fi
echo "Updating Mozilla TLS data from version ${STORED_VERSION:-unknown} to ${NEW_VERSION}"

cat > "${OUTPUT}" <<EOF
package tlsprofiles

// DO NOT EDIT, GENERATED BY ${0}
// DATA SOURCE: ${INPUT}
// DATA VERSION: ${NEW_VERSION}

import (
"crypto/tls"
)
EOF

function generate_profile {
local profile="${1}"

# Validate the profile key exists before writing any output
local exists
exists=$(${JQ} -r ".configurations | has(\"${profile}\")" "${TMPFILE}")
if [ "${exists}" != "true" ]; then
echo "ERROR: Profile '${profile}' not found in ${INPUT} (version ${NEW_VERSION})" >&2
echo "Available profiles: $(${JQ} -r '.configurations | keys | join(", ")' "${TMPFILE}")" >&2
exit 1
fi

# Validate tls_versions is a non-empty array with a non-null first entry
if ! ${JQ} -e ".configurations.${profile}.tls_versions | type == \"array\" and length > 0 and .[0] != null" "${TMPFILE}" >/dev/null; then
echo "ERROR: Missing or empty .configurations.${profile}.tls_versions[0] in ${INPUT}" >&2
exit 1
fi

# Validate that at least one cipher is present across ciphersuites and ciphers.iana
# (modern has only ciphersuites; intermediate has both; either alone is valid)
local cipher_count
cipher_count=$(${JQ} -r "
[
(.configurations.${profile}.ciphersuites // []),
(.configurations.${profile}.ciphers.iana // [])
] | add | length" "${TMPFILE}")
if [ "${cipher_count}" -eq 0 ] 2>/dev/null; then
echo "ERROR: Profile '${profile}' has no ciphers in ciphersuites or ciphers.iana" >&2
exit 1
fi

# Validate tls_curves is non-empty
local curve_count
curve_count=$(${JQ} -r ".configurations.${profile}.tls_curves | length" "${TMPFILE}")
if [ "${curve_count}" -eq 0 ] 2>/dev/null; then
echo "ERROR: Profile '${profile}' has no entries in tls_curves" >&2
exit 1
fi

cat >> "${OUTPUT}" <<EOF

var ${profile}TLSProfile = tlsProfile{
ciphers: cipherSlice{
cipherNums: []uint16{
EOF

${JQ} -r "(.configurations.${profile}.ciphersuites // [])[] | . |= \"tls.\" + . + \",\"" "${TMPFILE}" >> "${OUTPUT}"
${JQ} -r "(.configurations.${profile}.ciphers.iana // [])[] | . |= \"tls.\" + . + \",\"" "${TMPFILE}" >> "${OUTPUT}"

cat >> "${OUTPUT}" <<EOF
},
},
curves: curveSlice{
curveNums: []tls.CurveID{
EOF

${JQ} -r ".configurations.${profile}.tls_curves[] | . |= . + \",\"" "${TMPFILE}" >> "${OUTPUT}"

version=$(${JQ} -r ".configurations.${profile}.tls_versions[0]" "${TMPFILE}")
version=${version/TLSv1./tls.VersionTLS1}
version=${version/TLSv1/tls.VersionTLS10}

cat >> "${OUTPUT}" <<EOF
},
},
minTLSVersion: ${version},
}
EOF
}

generate_profile "modern"
generate_profile "intermediate"

# Remove unsupported ciphers from Go's crypto/tls package
sed -i.bak '/TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384/d; /TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384/d; /TLS_RSA_WITH_AES_256_CBC_SHA256/d' "${OUTPUT}"
rm -f "${OUTPUT}.bak"

# Make go happy
go fmt "${OUTPUT}"
137 changes: 94 additions & 43 deletions internal/shared/util/tlsprofiles/mozilla_data.go
Original file line number Diff line number Diff line change
@@ -1,53 +1,104 @@
package tlsprofiles

// DO NOT EDIT, GENERATED BY hack/tools/update-tls-profiles.sh
// DATA SOURCE: https://ssl-config.mozilla.org/guidelines/latest.json
// DATA VERSION: 6
// This file embeds the Mozilla SSL/TLS Configuration Guidelines JSON and parses
// it at init() time to populate the modern and intermediate TLS profiles.
// Run `make update-tls-profiles` to refresh mozilla_data.json from the upstream spec.

import (
"crypto/tls"
_ "embed"
"encoding/json"
"fmt"
)

var modernTLSProfile = tlsProfile{
ciphers: cipherSlice{
cipherNums: []uint16{
tls.TLS_AES_128_GCM_SHA256,
tls.TLS_AES_256_GCM_SHA384,
tls.TLS_CHACHA20_POLY1305_SHA256,
},
},
curves: curveSlice{
curveNums: []tls.CurveID{
X25519MLKEM768,
X25519,
prime256v1,
secp384r1,
},
},
minTLSVersion: tls.VersionTLS13,
//go:embed mozilla_data.json
var mozillaDataJSON []byte

// skippedCiphers records cipher names from mozilla_data.json that are not
// supported by Go's crypto/tls and were omitted from the profiles.
var skippedCiphers []string

// skippedCurves records curve names from mozilla_data.json that are not
// supported by Go's crypto/tls and were omitted from the profiles.
var skippedCurves []string

var (
modernTLSProfile tlsProfile
intermediateTLSProfile tlsProfile
)

type mozillaConfiguration struct {
Ciphersuites []string `json:"ciphersuites"`
Ciphers struct {
IANA []string `json:"iana"`
} `json:"ciphers"`
TLSCurves []string `json:"tls_curves"`
TLSVersions []string `json:"tls_versions"`
}

type mozillaSpec struct {
Configurations map[string]mozillaConfiguration `json:"configurations"`
}

func init() {
var spec mozillaSpec
if err := json.Unmarshal(mozillaDataJSON, &spec); err != nil {
panic(fmt.Sprintf("tlsprofiles: failed to parse embedded mozilla_data.json: %v", err))
}

for _, name := range []string{"modern", "intermediate"} {
cfg, ok := spec.Configurations[name]
if !ok {
panic(fmt.Sprintf("tlsprofiles: profile %q not found in embedded mozilla_data.json", name))
}

p, ciphers, curves := parseProfile(name, cfg)
skippedCiphers = append(skippedCiphers, ciphers...)
skippedCurves = append(skippedCurves, curves...)

switch name {
case "modern":
modernTLSProfile = p
case "intermediate":
intermediateTLSProfile = p
}
}
}

var intermediateTLSProfile = tlsProfile{
ciphers: cipherSlice{
cipherNums: []uint16{
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,
},
},
curves: curveSlice{
curveNums: []tls.CurveID{
X25519MLKEM768,
X25519,
prime256v1,
secp384r1,
},
},
minTLSVersion: tls.VersionTLS12,
func parseProfile(name string, cfg mozillaConfiguration) (tlsProfile, []string, []string) {
var skippedC, skippedK []string
var cipherNums []uint16
for _, c := range append(cfg.Ciphersuites, cfg.Ciphers.IANA...) {
id := cipherSuiteId(c)
if id == 0 {
skippedC = append(skippedC, c)
continue
}
cipherNums = append(cipherNums, id)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unsupported ciphers are tracked in skippedCiphers and validated by TestNoSkippedCiphers — nice. But unsupported curves (where curveId() returns 0) are silently dropped here with no tracking or test.

If Mozilla adds a new curve that Go does not yet support, this would silently weaken the profile. Consider adding a skippedCurves slice with a corresponding test, mirroring the cipher handling:

id := curveId(c)
if id == 0 {
    skipped = append(skipped, "curve:"+c)
    continue
}

(Or a separate skippedCurves list if you prefer to keep them distinct.)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I noticed that too... it may not even be a curve that isn't support, but simply not mapped in our own functions.

var curveNums []tls.CurveID
for _, c := range cfg.TLSCurves {
id := curveId(c)
if id == 0 {
skippedK = append(skippedK, c)
continue
}
curveNums = append(curveNums, id)
}

if len(cfg.TLSVersions) == 0 {
panic(fmt.Sprintf("tlsprofiles: profile %q has no tls_versions in embedded mozilla_data.json", name))
}

var version tlsVersion
if err := version.Set(cfg.TLSVersions[0]); err != nil {
panic(fmt.Sprintf("tlsprofiles: profile %q has unrecognized tls_versions[0] %q: %v", name, cfg.TLSVersions[0], err))
}

return tlsProfile{
ciphers: cipherSlice{cipherNums: cipherNums},
curves: curveSlice{curveNums: curveNums},
minTLSVersion: version,
}, skippedC, skippedK
}
Loading
Loading