Stratum TLS cert: add subjectAltName, auto-regen on upgrade

Old cert was /CN=kamado-pool-stratum with no SAN extension. Strict
TLS clients (Go, Rust, mbedtls, most modern miner firmwares) treat
missing SAN as a hard validation failure and send TLS alert 42
(bad_certificate), which is exactly what we saw in the stunnel
logs: "SSL_accept: ... error:0A000412: ... sslv3 alert bad
certificate".

Generate the cert with subjectAltName covering kamado-pool.embassy,
localhost, and 127.0.0.1, and an explicit
extendedKeyUsage=serverAuth. Swap the combined PEM so the cert
comes before the key (convention). Upgrades auto-heal: if an
existing cert lacks SAN, the entrypoint regenerates it on next
start — the fingerprint changes and the user needs to re-pin on
their miner, but the previous cert couldn't complete a handshake
anyway so there's nothing to lose.

Also make stunnel's intent explicit with verify=0 so anyone reading
the config knows we're doing opportunistic TLS, not client-cert
auth; miner auth is the stratum layer's job.
This commit is contained in:
satoshi
2026-04-23 02:12:44 +03:00
parent dc33bb7694
commit 1fccebef01
+33 -7
View File
@@ -139,19 +139,42 @@ API_PID=$!
STUNNEL_PID=""
if [[ "${TLS_ENABLED}" == "enabled" ]]; then
TLS_DIR=/root/.kamado/tls
CRT="${TLS_DIR}/stratum.crt"
KEY="${TLS_DIR}/stratum.key"
CERT="${TLS_DIR}/stratum.pem"
mkdir -p "${TLS_DIR}"
if [[ ! -f "${CERT}" ]]; then
# Regenerate when the cert is missing, OR when an older cert lacks
# a subjectAltName extension. Strict TLS clients (Go, Rust,
# mbedtls, most modern miner firmwares) reject CN-only certs with
# a "bad certificate" alert, which is what we saw in the logs on
# older Kamado installs.
NEEDS_REGEN=false
if [[ ! -f "${CERT}" || ! -f "${CRT}" || ! -f "${KEY}" ]]; then
NEEDS_REGEN=true
elif ! openssl x509 -in "${CRT}" -noout -ext subjectAltName 2>/dev/null \
| grep -qE "DNS:|IP:"; then
echo "kamado-entrypoint: existing TLS cert lacks subjectAltName; regenerating"
NEEDS_REGEN=true
fi
if [[ "${NEEDS_REGEN}" == "true" ]]; then
echo "kamado-entrypoint: generating self-signed stratum TLS cert"
openssl req -x509 -newkey rsa:2048 -sha256 -nodes \
-keyout "${TLS_DIR}/stratum.key" \
-out "${TLS_DIR}/stratum.crt" \
-keyout "${KEY}" \
-out "${CRT}" \
-days 3650 \
-subj "/CN=kamado-pool-stratum" >/dev/null 2>&1
cat "${TLS_DIR}/stratum.key" "${TLS_DIR}/stratum.crt" > "${CERT}"
chmod 600 "${TLS_DIR}/stratum.key" "${CERT}"
-subj "/CN=kamado-pool-stratum" \
-addext "subjectAltName = DNS:kamado-pool.embassy, DNS:localhost, IP:127.0.0.1" \
-addext "extendedKeyUsage = serverAuth" \
>/dev/null 2>&1
# stunnel happily reads cert+key in either order, but cert-first
# is the convention openssl and most tooling expect.
cat "${CRT}" "${KEY}" > "${CERT}"
chmod 600 "${KEY}" "${CERT}"
fi
FINGERPRINT=$(openssl x509 -in "${TLS_DIR}/stratum.crt" -noout -fingerprint -sha256 | cut -d= -f2)
FINGERPRINT=$(openssl x509 -in "${CRT}" -noout -fingerprint -sha256 | cut -d= -f2)
printf '%s\n' "${FINGERPRINT}" > "${TLS_DIR}/fingerprint.txt"
echo "kamado-entrypoint: stratum TLS SHA256 fingerprint: ${FINGERPRINT}"
@@ -167,6 +190,9 @@ debug = 4
accept = 0.0.0.0:${TLS_PORT}
connect = 127.0.0.1:${STRATUM_PORT}
cert = ${CERT}
# No client-cert auth — stratum over TLS is opportunistic encryption,
# the stratum layer handles miner auth via username.
verify = 0
EOF
echo "kamado-entrypoint: starting stunnel on :${TLS_PORT} -> :${STRATUM_PORT}"