Skip to content

Commit ebea1e0

Browse files
tmshortclaude
andauthored
Replace generated mozilla_data.go with go:embed + runtime parsing (#2634)
Embeds mozilla_data.json at compile time and parses it in init() to populate modern and intermediate TLS profiles, removing the jq/gojq dependency. Tracks skipped ciphers and curves (names not supported by Go's crypto/tls) and asserts both are empty via TestNoSkippedCiphers and TestNoSkippedCurves. Signed-off-by: Todd Short <tshort@redhat.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent b6dfd40 commit ebea1e0

9 files changed

Lines changed: 186 additions & 198 deletions

File tree

.bingo/Variables.mk

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,6 @@ $(CRD_REF_DOCS): $(BINGO_DIR)/crd-ref-docs.mod
4747
@echo "(re)installing $(GOBIN)/crd-ref-docs-v0.3.0"
4848
@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"
4949

50-
GOJQ := $(GOBIN)/gojq-v0.12.17
51-
$(GOJQ): $(BINGO_DIR)/gojq.mod
52-
@# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies.
53-
@echo "(re)installing $(GOBIN)/gojq-v0.12.17"
54-
@cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=gojq.mod -o=$(GOBIN)/gojq-v0.12.17 "github.com/itchyny/gojq/cmd/gojq"
55-
5650
GOLANGCI_LINT := $(GOBIN)/golangci-lint-v2.8.0
5751
$(GOLANGCI_LINT): $(BINGO_DIR)/golangci-lint.mod
5852
@# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies.

.bingo/gojq.mod

Lines changed: 0 additions & 5 deletions
This file was deleted.

.bingo/gojq.sum

Lines changed: 0 additions & 17 deletions
This file was deleted.

.bingo/variables.env

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@ CRD_DIFF="${GOBIN}/crd-diff-v0.5.1-0.20260309184313-54162f2e3097"
1818

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

21-
GOJQ="${GOBIN}/gojq-v0.12.17"
22-
2321
GOLANGCI_LINT="${GOBIN}/golangci-lint-v2.8.0"
2422

2523
GORELEASER="${GOBIN}/goreleaser-v2.11.2"

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,8 +221,8 @@ fmt: $(YAMLFMT) #EXHELP Formats code
221221
$(YAMLFMT) -gitignore_excludes testdata
222222

223223
.PHONY: update-tls-profiles
224-
update-tls-profiles: $(GOJQ) #EXHELP Update TLS profiles from the Mozilla wiki
225-
env JQ=$(GOJQ) hack/tools/update-tls-profiles.sh
224+
update-tls-profiles: #EXHELP Update TLS profiles from the Mozilla wiki
225+
hack/tools/update-tls-profiles.sh
226226

227227
.PHONY: update-registryv1-bundle-schema
228228
update-registryv1-bundle-schema: #EXHELP Update registry+v1 bundle configuration JSON schema

hack/tools/update-tls-profiles.sh

Lines changed: 2 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -2,131 +2,10 @@
22

33
set -e
44

5-
if [ -z "${JQ}" ]; then
6-
echo "JQ not defined"
7-
exit 1
8-
fi
9-
10-
OUTPUT=internal/shared/util/tlsprofiles/mozilla_data.go
5+
OUTPUT=internal/shared/util/tlsprofiles/mozilla_data.json
116
INPUT=https://ssl-config.mozilla.org/guidelines/latest.json
127

13-
TMPFILE="$(mktemp)"
14-
trap 'rm -rf "$TMPFILE"' EXIT
15-
16-
if ! curl -L -s -f "${INPUT}" > "${TMPFILE}"; then
8+
if ! curl -L -s -f "${INPUT}" -o "${OUTPUT}"; then
179
echo "ERROR: Failed to download ${INPUT} (HTTP error or connection failure)" >&2
1810
exit 1
1911
fi
20-
21-
if ! ${JQ} empty "${TMPFILE}" 2>/dev/null; then
22-
echo "ERROR: Downloaded data from ${INPUT} is not valid JSON" >&2
23-
exit 1
24-
fi
25-
26-
# Extract stored version from current output file (may be empty on first run)
27-
STORED_VERSION=$(grep '^// DATA VERSION:' "${OUTPUT}" 2>/dev/null | awk '{print $4}' || true)
28-
29-
# Extract version from downloaded JSON and fail early if missing
30-
NEW_VERSION=$(${JQ} -r '.version' "${TMPFILE}")
31-
if [ -z "${NEW_VERSION}" ] || [ "${NEW_VERSION}" = "null" ]; then
32-
echo "ERROR: Could not read .version from ${INPUT}" >&2
33-
exit 1
34-
fi
35-
36-
if [ "${NEW_VERSION}" = "${STORED_VERSION}" ]; then
37-
echo "Mozilla TLS data is already at version ${NEW_VERSION}, skipping regeneration."
38-
exit 0
39-
fi
40-
echo "Updating Mozilla TLS data from version ${STORED_VERSION:-unknown} to ${NEW_VERSION}"
41-
42-
cat > "${OUTPUT}" <<EOF
43-
package tlsprofiles
44-
45-
// DO NOT EDIT, GENERATED BY ${0}
46-
// DATA SOURCE: ${INPUT}
47-
// DATA VERSION: ${NEW_VERSION}
48-
49-
import (
50-
"crypto/tls"
51-
)
52-
EOF
53-
54-
function generate_profile {
55-
local profile="${1}"
56-
57-
# Validate the profile key exists before writing any output
58-
local exists
59-
exists=$(${JQ} -r ".configurations | has(\"${profile}\")" "${TMPFILE}")
60-
if [ "${exists}" != "true" ]; then
61-
echo "ERROR: Profile '${profile}' not found in ${INPUT} (version ${NEW_VERSION})" >&2
62-
echo "Available profiles: $(${JQ} -r '.configurations | keys | join(", ")' "${TMPFILE}")" >&2
63-
exit 1
64-
fi
65-
66-
# Validate tls_versions is a non-empty array with a non-null first entry
67-
if ! ${JQ} -e ".configurations.${profile}.tls_versions | type == \"array\" and length > 0 and .[0] != null" "${TMPFILE}" >/dev/null; then
68-
echo "ERROR: Missing or empty .configurations.${profile}.tls_versions[0] in ${INPUT}" >&2
69-
exit 1
70-
fi
71-
72-
# Validate that at least one cipher is present across ciphersuites and ciphers.iana
73-
# (modern has only ciphersuites; intermediate has both; either alone is valid)
74-
local cipher_count
75-
cipher_count=$(${JQ} -r "
76-
[
77-
(.configurations.${profile}.ciphersuites // []),
78-
(.configurations.${profile}.ciphers.iana // [])
79-
] | add | length" "${TMPFILE}")
80-
if [ "${cipher_count}" -eq 0 ] 2>/dev/null; then
81-
echo "ERROR: Profile '${profile}' has no ciphers in ciphersuites or ciphers.iana" >&2
82-
exit 1
83-
fi
84-
85-
# Validate tls_curves is non-empty
86-
local curve_count
87-
curve_count=$(${JQ} -r ".configurations.${profile}.tls_curves | length" "${TMPFILE}")
88-
if [ "${curve_count}" -eq 0 ] 2>/dev/null; then
89-
echo "ERROR: Profile '${profile}' has no entries in tls_curves" >&2
90-
exit 1
91-
fi
92-
93-
cat >> "${OUTPUT}" <<EOF
94-
95-
var ${profile}TLSProfile = tlsProfile{
96-
ciphers: cipherSlice{
97-
cipherNums: []uint16{
98-
EOF
99-
100-
${JQ} -r "(.configurations.${profile}.ciphersuites // [])[] | . |= \"tls.\" + . + \",\"" "${TMPFILE}" >> "${OUTPUT}"
101-
${JQ} -r "(.configurations.${profile}.ciphers.iana // [])[] | . |= \"tls.\" + . + \",\"" "${TMPFILE}" >> "${OUTPUT}"
102-
103-
cat >> "${OUTPUT}" <<EOF
104-
},
105-
},
106-
curves: curveSlice{
107-
curveNums: []tls.CurveID{
108-
EOF
109-
110-
${JQ} -r ".configurations.${profile}.tls_curves[] | . |= . + \",\"" "${TMPFILE}" >> "${OUTPUT}"
111-
112-
version=$(${JQ} -r ".configurations.${profile}.tls_versions[0]" "${TMPFILE}")
113-
version=${version/TLSv1./tls.VersionTLS1}
114-
version=${version/TLSv1/tls.VersionTLS10}
115-
116-
cat >> "${OUTPUT}" <<EOF
117-
},
118-
},
119-
minTLSVersion: ${version},
120-
}
121-
EOF
122-
}
123-
124-
generate_profile "modern"
125-
generate_profile "intermediate"
126-
127-
# Remove unsupported ciphers from Go's crypto/tls package
128-
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}"
129-
rm -f "${OUTPUT}.bak"
130-
131-
# Make go happy
132-
go fmt "${OUTPUT}"
Lines changed: 94 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,104 @@
11
package tlsprofiles
22

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

77
import (
88
"crypto/tls"
9+
_ "embed"
10+
"encoding/json"
11+
"fmt"
912
)
1013

11-
var modernTLSProfile = tlsProfile{
12-
ciphers: cipherSlice{
13-
cipherNums: []uint16{
14-
tls.TLS_AES_128_GCM_SHA256,
15-
tls.TLS_AES_256_GCM_SHA384,
16-
tls.TLS_CHACHA20_POLY1305_SHA256,
17-
},
18-
},
19-
curves: curveSlice{
20-
curveNums: []tls.CurveID{
21-
X25519MLKEM768,
22-
X25519,
23-
prime256v1,
24-
secp384r1,
25-
},
26-
},
27-
minTLSVersion: tls.VersionTLS13,
14+
//go:embed mozilla_data.json
15+
var mozillaDataJSON []byte
16+
17+
// skippedCiphers records cipher names from mozilla_data.json that are not
18+
// supported by Go's crypto/tls and were omitted from the profiles.
19+
var skippedCiphers []string
20+
21+
// skippedCurves records curve names from mozilla_data.json that are not
22+
// supported by Go's crypto/tls and were omitted from the profiles.
23+
var skippedCurves []string
24+
25+
var (
26+
modernTLSProfile tlsProfile
27+
intermediateTLSProfile tlsProfile
28+
)
29+
30+
type mozillaConfiguration struct {
31+
Ciphersuites []string `json:"ciphersuites"`
32+
Ciphers struct {
33+
IANA []string `json:"iana"`
34+
} `json:"ciphers"`
35+
TLSCurves []string `json:"tls_curves"`
36+
TLSVersions []string `json:"tls_versions"`
37+
}
38+
39+
type mozillaSpec struct {
40+
Configurations map[string]mozillaConfiguration `json:"configurations"`
41+
}
42+
43+
func init() {
44+
var spec mozillaSpec
45+
if err := json.Unmarshal(mozillaDataJSON, &spec); err != nil {
46+
panic(fmt.Sprintf("tlsprofiles: failed to parse embedded mozilla_data.json: %v", err))
47+
}
48+
49+
for _, name := range []string{"modern", "intermediate"} {
50+
cfg, ok := spec.Configurations[name]
51+
if !ok {
52+
panic(fmt.Sprintf("tlsprofiles: profile %q not found in embedded mozilla_data.json", name))
53+
}
54+
55+
p, ciphers, curves := parseProfile(name, cfg)
56+
skippedCiphers = append(skippedCiphers, ciphers...)
57+
skippedCurves = append(skippedCurves, curves...)
58+
59+
switch name {
60+
case "modern":
61+
modernTLSProfile = p
62+
case "intermediate":
63+
intermediateTLSProfile = p
64+
}
65+
}
2866
}
2967

30-
var intermediateTLSProfile = tlsProfile{
31-
ciphers: cipherSlice{
32-
cipherNums: []uint16{
33-
tls.TLS_AES_128_GCM_SHA256,
34-
tls.TLS_AES_256_GCM_SHA384,
35-
tls.TLS_CHACHA20_POLY1305_SHA256,
36-
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
37-
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
38-
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
39-
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
40-
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
41-
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
42-
},
43-
},
44-
curves: curveSlice{
45-
curveNums: []tls.CurveID{
46-
X25519MLKEM768,
47-
X25519,
48-
prime256v1,
49-
secp384r1,
50-
},
51-
},
52-
minTLSVersion: tls.VersionTLS12,
68+
func parseProfile(name string, cfg mozillaConfiguration) (tlsProfile, []string, []string) {
69+
var skippedC, skippedK []string
70+
var cipherNums []uint16
71+
for _, c := range append(cfg.Ciphersuites, cfg.Ciphers.IANA...) {
72+
id := cipherSuiteId(c)
73+
if id == 0 {
74+
skippedC = append(skippedC, c)
75+
continue
76+
}
77+
cipherNums = append(cipherNums, id)
78+
}
79+
80+
var curveNums []tls.CurveID
81+
for _, c := range cfg.TLSCurves {
82+
id := curveId(c)
83+
if id == 0 {
84+
skippedK = append(skippedK, c)
85+
continue
86+
}
87+
curveNums = append(curveNums, id)
88+
}
89+
90+
if len(cfg.TLSVersions) == 0 {
91+
panic(fmt.Sprintf("tlsprofiles: profile %q has no tls_versions in embedded mozilla_data.json", name))
92+
}
93+
94+
var version tlsVersion
95+
if err := version.Set(cfg.TLSVersions[0]); err != nil {
96+
panic(fmt.Sprintf("tlsprofiles: profile %q has unrecognized tls_versions[0] %q: %v", name, cfg.TLSVersions[0], err))
97+
}
98+
99+
return tlsProfile{
100+
ciphers: cipherSlice{cipherNums: cipherNums},
101+
curves: curveSlice{curveNums: curveNums},
102+
minTLSVersion: version,
103+
}, skippedC, skippedK
53104
}

0 commit comments

Comments
 (0)