2026-02-24 17:14:56 -07:00
|
|
|
#!/usr/bin/env bash
|
Replace nginx with Caddy for TLS termination and reverse proxying.
- Replace nginx with Caddy as the web server for both Docker and native
(RPi/Debian) deployments. Caddy handles HTTPS termination, reverse
proxying to all services (API, React app, Controller, Kiwix, Help, Map),
and serves the media/download file browsers.
- Generate a Root CA and signed leaf certificates for HTTPS. The Root CA
uses Name Constraints to restrict signing to local addresses only
(.local, localhost, and RFC 1918 private IP ranges), mitigating MITM
risk if the CA key is compromised.
- Add an HTTP landing page that guides users through one-time CA
certificate installation on iOS, Android, macOS, Windows, Linux, and
Firefox, with auto-redirect when the cert is already trusted.
- Add trailing-slash redirects for /controller, /docs, /media, and
/download so bare paths don't fall through to the React catch-all.
- Add a Root CA download link on the Settings page under Configs.
- Update help.sh diagnostics for IPv6 listener detection and HTTP/2
status codes.
- Remove all nginx config files and the https_proxy Docker container.
- Bump wrolpi-help submodule with certificate documentation.
2026-03-07 12:08:27 -07:00
|
|
|
# Generate a root CA and signed leaf certificate for WROLPi.
|
|
|
|
|
#
|
|
|
|
|
# Root CA: stored in ${MEDIA_DIRECTORY}/config/ssl/ — persists forever.
|
|
|
|
|
# Leaf cert: stored in ${CERT_DIR}/ — regenerated each run to pick up hostname/IP changes.
|
|
|
|
|
#
|
|
|
|
|
# Users trust the root CA once; leaf certs can be regenerated without re-trusting.
|
2026-02-24 17:14:56 -07:00
|
|
|
|
|
|
|
|
set -e
|
|
|
|
|
|
Replace nginx with Caddy for TLS termination and reverse proxying.
- Replace nginx with Caddy as the web server for both Docker and native
(RPi/Debian) deployments. Caddy handles HTTPS termination, reverse
proxying to all services (API, React app, Controller, Kiwix, Help, Map),
and serves the media/download file browsers.
- Generate a Root CA and signed leaf certificates for HTTPS. The Root CA
uses Name Constraints to restrict signing to local addresses only
(.local, localhost, and RFC 1918 private IP ranges), mitigating MITM
risk if the CA key is compromised.
- Add an HTTP landing page that guides users through one-time CA
certificate installation on iOS, Android, macOS, Windows, Linux, and
Firefox, with auto-redirect when the cert is already trusted.
- Add trailing-slash redirects for /controller, /docs, /media, and
/download so bare paths don't fall through to the React catch-all.
- Add a Root CA download link on the Settings page under Configs.
- Update help.sh diagnostics for IPv6 listener detection and HTTP/2
status codes.
- Remove all nginx config files and the https_proxy Docker container.
- Bump wrolpi-help submodule with certificate documentation.
2026-03-07 12:08:27 -07:00
|
|
|
MEDIA_DIRECTORY="${MEDIA_DIRECTORY:-/media/wrolpi}"
|
|
|
|
|
CERT_DIR="${CERT_DIR:-/etc/ssl/wrolpi}"
|
2026-02-24 17:14:56 -07:00
|
|
|
|
Replace nginx with Caddy for TLS termination and reverse proxying.
- Replace nginx with Caddy as the web server for both Docker and native
(RPi/Debian) deployments. Caddy handles HTTPS termination, reverse
proxying to all services (API, React app, Controller, Kiwix, Help, Map),
and serves the media/download file browsers.
- Generate a Root CA and signed leaf certificates for HTTPS. The Root CA
uses Name Constraints to restrict signing to local addresses only
(.local, localhost, and RFC 1918 private IP ranges), mitigating MITM
risk if the CA key is compromised.
- Add an HTTP landing page that guides users through one-time CA
certificate installation on iOS, Android, macOS, Windows, Linux, and
Firefox, with auto-redirect when the cert is already trusted.
- Add trailing-slash redirects for /controller, /docs, /media, and
/download so bare paths don't fall through to the React catch-all.
- Add a Root CA download link on the Settings page under Configs.
- Update help.sh diagnostics for IPv6 listener detection and HTTP/2
status codes.
- Remove all nginx config files and the https_proxy Docker container.
- Bump wrolpi-help submodule with certificate documentation.
2026-03-07 12:08:27 -07:00
|
|
|
CA_DIR="${MEDIA_DIRECTORY}/config/ssl"
|
|
|
|
|
CA_KEY="${CA_DIR}/ca.key"
|
|
|
|
|
CA_CERT="${CA_DIR}/ca.crt"
|
|
|
|
|
|
|
|
|
|
LEAF_KEY="${CERT_DIR}/cert.key"
|
|
|
|
|
LEAF_CERT="${CERT_DIR}/cert.crt"
|
|
|
|
|
|
|
|
|
|
mkdir -p "${CA_DIR}" "${CERT_DIR}"
|
|
|
|
|
|
|
|
|
|
# --- Root CA (only created once) ---
|
|
|
|
|
if [[ -f "${CA_CERT}" && -f "${CA_KEY}" ]]; then
|
|
|
|
|
echo "Root CA already exists at ${CA_CERT}"
|
|
|
|
|
else
|
|
|
|
|
echo "Generating WROLPi Root CA..."
|
|
|
|
|
openssl genrsa -out "${CA_KEY}" 4096
|
|
|
|
|
openssl req -new -x509 -nodes -key "${CA_KEY}" -out "${CA_CERT}" -days 36500 \
|
|
|
|
|
-subj "/C=US/ST=State/L=City/O=WROLPi/CN=WROLPi Root CA" \
|
|
|
|
|
-addext "basicConstraints = critical, CA:TRUE" \
|
|
|
|
|
-addext "keyUsage = critical, keyCertSign, cRLSign" \
|
|
|
|
|
-addext "nameConstraints = critical, permitted;DNS:.local, permitted;DNS:localhost, permitted;IP:127.0.0.0/255.0.0.0, permitted;IP:10.0.0.0/255.0.0.0, permitted;IP:172.16.0.0/255.240.0.0, permitted;IP:192.168.0.0/255.255.0.0, permitted;IP:::1/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff, permitted;IP:fc00::/fe00::, permitted;IP:fe80::/ffc0::"
|
|
|
|
|
chmod 600 "${CA_KEY}"
|
|
|
|
|
chmod 644 "${CA_CERT}"
|
|
|
|
|
echo "Root CA generated."
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# --- Leaf certificate (always regenerated) ---
|
|
|
|
|
echo "Generating WROLPi leaf certificate..."
|
|
|
|
|
|
2026-07-05 12:37:59 -06:00
|
|
|
# Only SANs inside the Root CA's name constraints may be included; any other
|
|
|
|
|
# (e.g. a global IPv6 address from the router) fails verification with
|
|
|
|
|
# "permitted subtree violation". Takes one SAN like "IP:192.168.0.1" or "DNS:wrolpi.local".
|
|
|
|
|
san_permitted() {
|
|
|
|
|
local san
|
|
|
|
|
san="$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]')"
|
|
|
|
|
case "${san}" in
|
|
|
|
|
dns:localhost|dns:*.local) return 0 ;;
|
|
|
|
|
ip:127.*|ip:10.*|ip:192.168.*) return 0 ;;
|
|
|
|
|
ip:172.1[6-9].*|ip:172.2[0-9].*|ip:172.3[01].*) return 0 ;;
|
|
|
|
|
ip:::1) return 0 ;;
|
|
|
|
|
ip:f[cd]*) return 0 ;; # fc00::/7 unique-local
|
|
|
|
|
ip:fe[89ab]*) return 0 ;; # fe80::/10 link-local
|
|
|
|
|
*) return 1 ;;
|
|
|
|
|
esac
|
|
|
|
|
}
|
|
|
|
|
|
Replace nginx with Caddy for TLS termination and reverse proxying.
- Replace nginx with Caddy as the web server for both Docker and native
(RPi/Debian) deployments. Caddy handles HTTPS termination, reverse
proxying to all services (API, React app, Controller, Kiwix, Help, Map),
and serves the media/download file browsers.
- Generate a Root CA and signed leaf certificates for HTTPS. The Root CA
uses Name Constraints to restrict signing to local addresses only
(.local, localhost, and RFC 1918 private IP ranges), mitigating MITM
risk if the CA key is compromised.
- Add an HTTP landing page that guides users through one-time CA
certificate installation on iOS, Android, macOS, Windows, Linux, and
Firefox, with auto-redirect when the cert is already trusted.
- Add trailing-slash redirects for /controller, /docs, /media, and
/download so bare paths don't fall through to the React catch-all.
- Add a Root CA download link on the Settings page under Configs.
- Update help.sh diagnostics for IPv6 listener detection and HTTP/2
status codes.
- Remove all nginx config files and the https_proxy Docker container.
- Bump wrolpi-help submodule with certificate documentation.
2026-03-07 12:08:27 -07:00
|
|
|
# Build SAN list dynamically.
|
|
|
|
|
SAN="DNS:localhost,DNS:wrolpi.local"
|
|
|
|
|
|
|
|
|
|
HOSTNAME_SHORT="$(hostname)"
|
|
|
|
|
if [[ -n "${HOSTNAME_SHORT}" ]]; then
|
|
|
|
|
SAN="${SAN},DNS:${HOSTNAME_SHORT}.local"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
SAN="${SAN},IP:127.0.0.1"
|
|
|
|
|
|
|
|
|
|
# Add all local IPs.
|
|
|
|
|
for ip in $(hostname -I 2>/dev/null || true); do
|
2026-07-05 12:37:59 -06:00
|
|
|
if san_permitted "IP:${ip}"; then
|
|
|
|
|
SAN="${SAN},IP:${ip}"
|
|
|
|
|
else
|
|
|
|
|
echo "Skipping IP:${ip}: not permitted by Root CA name constraints"
|
|
|
|
|
fi
|
Replace nginx with Caddy for TLS termination and reverse proxying.
- Replace nginx with Caddy as the web server for both Docker and native
(RPi/Debian) deployments. Caddy handles HTTPS termination, reverse
proxying to all services (API, React app, Controller, Kiwix, Help, Map),
and serves the media/download file browsers.
- Generate a Root CA and signed leaf certificates for HTTPS. The Root CA
uses Name Constraints to restrict signing to local addresses only
(.local, localhost, and RFC 1918 private IP ranges), mitigating MITM
risk if the CA key is compromised.
- Add an HTTP landing page that guides users through one-time CA
certificate installation on iOS, Android, macOS, Windows, Linux, and
Firefox, with auto-redirect when the cert is already trusted.
- Add trailing-slash redirects for /controller, /docs, /media, and
/download so bare paths don't fall through to the React catch-all.
- Add a Root CA download link on the Settings page under Configs.
- Update help.sh diagnostics for IPv6 listener detection and HTTP/2
status codes.
- Remove all nginx config files and the https_proxy Docker container.
- Bump wrolpi-help submodule with certificate documentation.
2026-03-07 12:08:27 -07:00
|
|
|
done
|
|
|
|
|
|
|
|
|
|
# Append any extra SANs (e.g. host IP when running in Docker).
|
|
|
|
|
if [[ -n "${EXTRA_SANS:-}" ]]; then
|
2026-07-05 12:37:59 -06:00
|
|
|
for extra in ${EXTRA_SANS//,/ }; do
|
|
|
|
|
# openssl requires the canonical prefix case (IP:/DNS:).
|
|
|
|
|
case "$(printf '%s' "${extra%%:*}" | tr '[:lower:]' '[:upper:]')" in
|
|
|
|
|
IP) extra="IP:${extra#*:}" ;;
|
|
|
|
|
DNS) extra="DNS:${extra#*:}" ;;
|
|
|
|
|
esac
|
|
|
|
|
if san_permitted "${extra}"; then
|
|
|
|
|
SAN="${SAN},${extra}"
|
|
|
|
|
else
|
|
|
|
|
echo "Skipping ${extra}: not permitted by Root CA name constraints"
|
|
|
|
|
fi
|
|
|
|
|
done
|
Replace nginx with Caddy for TLS termination and reverse proxying.
- Replace nginx with Caddy as the web server for both Docker and native
(RPi/Debian) deployments. Caddy handles HTTPS termination, reverse
proxying to all services (API, React app, Controller, Kiwix, Help, Map),
and serves the media/download file browsers.
- Generate a Root CA and signed leaf certificates for HTTPS. The Root CA
uses Name Constraints to restrict signing to local addresses only
(.local, localhost, and RFC 1918 private IP ranges), mitigating MITM
risk if the CA key is compromised.
- Add an HTTP landing page that guides users through one-time CA
certificate installation on iOS, Android, macOS, Windows, Linux, and
Firefox, with auto-redirect when the cert is already trusted.
- Add trailing-slash redirects for /controller, /docs, /media, and
/download so bare paths don't fall through to the React catch-all.
- Add a Root CA download link on the Settings page under Configs.
- Update help.sh diagnostics for IPv6 listener detection and HTTP/2
status codes.
- Remove all nginx config files and the https_proxy Docker container.
- Bump wrolpi-help submodule with certificate documentation.
2026-03-07 12:08:27 -07:00
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
echo "SANs: ${SAN}"
|
|
|
|
|
|
|
|
|
|
# Create CSR and sign with root CA.
|
|
|
|
|
openssl genrsa -out "${LEAF_KEY}" 2048
|
|
|
|
|
|
|
|
|
|
openssl req -new -key "${LEAF_KEY}" \
|
|
|
|
|
-subj "/C=US/ST=State/L=City/O=WROLPi/CN=${HOSTNAME_SHORT:-wrolpi}.local" \
|
|
|
|
|
-out "${CERT_DIR}/cert.csr"
|
|
|
|
|
|
|
|
|
|
openssl x509 -req -in "${CERT_DIR}/cert.csr" \
|
|
|
|
|
-CA "${CA_CERT}" -CAkey "${CA_KEY}" -CAcreateserial \
|
|
|
|
|
-days 825 \
|
|
|
|
|
-extfile <(printf "subjectAltName=%s\nbasicConstraints=CA:FALSE\nkeyUsage=digitalSignature,keyEncipherment\nextendedKeyUsage=serverAuth" "${SAN}") \
|
|
|
|
|
-out "${LEAF_CERT}"
|
|
|
|
|
|
|
|
|
|
rm -f "${CERT_DIR}/cert.csr"
|
|
|
|
|
chmod 640 "${LEAF_KEY}" "${LEAF_CERT}"
|
|
|
|
|
|
|
|
|
|
# Allow the caddy user to read the leaf cert/key (native installs).
|
|
|
|
|
if id caddy >/dev/null 2>&1; then
|
|
|
|
|
chown root:caddy "${LEAF_KEY}" "${LEAF_CERT}"
|
2026-02-24 17:14:56 -07:00
|
|
|
fi
|
|
|
|
|
|
Replace nginx with Caddy for TLS termination and reverse proxying.
- Replace nginx with Caddy as the web server for both Docker and native
(RPi/Debian) deployments. Caddy handles HTTPS termination, reverse
proxying to all services (API, React app, Controller, Kiwix, Help, Map),
and serves the media/download file browsers.
- Generate a Root CA and signed leaf certificates for HTTPS. The Root CA
uses Name Constraints to restrict signing to local addresses only
(.local, localhost, and RFC 1918 private IP ranges), mitigating MITM
risk if the CA key is compromised.
- Add an HTTP landing page that guides users through one-time CA
certificate installation on iOS, Android, macOS, Windows, Linux, and
Firefox, with auto-redirect when the cert is already trusted.
- Add trailing-slash redirects for /controller, /docs, /media, and
/download so bare paths don't fall through to the React catch-all.
- Add a Root CA download link on the Settings page under Configs.
- Update help.sh diagnostics for IPv6 listener detection and HTTP/2
status codes.
- Remove all nginx config files and the https_proxy Docker container.
- Bump wrolpi-help submodule with certificate documentation.
2026-03-07 12:08:27 -07:00
|
|
|
# Validate the chain.
|
|
|
|
|
openssl verify -CAfile "${CA_CERT}" "${LEAF_CERT}"
|
|
|
|
|
echo "Leaf certificate generated and verified."
|
|
|
|
|
|
|
|
|
|
# Install root CA into system trust store so the device trusts its own certs.
|
|
|
|
|
if command -v update-ca-certificates >/dev/null 2>&1; then
|
|
|
|
|
cp "${CA_CERT}" /usr/local/share/ca-certificates/wrolpi-ca.crt
|
|
|
|
|
update-ca-certificates
|
|
|
|
|
echo "Root CA installed in system trust store."
|
|
|
|
|
fi
|