mirror of
https://github.com/lrnselfreliance/wrolpi.git
synced 2026-10-25 18:40:06 -04: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.
This commit is contained in:
parent
dd270b37dc
commit
fbb71e012d
43 changed files with 881 additions and 583 deletions
|
|
@ -1,19 +1,94 @@
|
|||
#!/usr/bin/env bash
|
||||
# Generate self-signed SSL certificates for nginx if they don't exist.
|
||||
# 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.
|
||||
|
||||
set -e
|
||||
|
||||
CERT_FILE="/etc/nginx/cert.crt"
|
||||
KEY_FILE="/etc/nginx/cert.key"
|
||||
MEDIA_DIRECTORY="${MEDIA_DIRECTORY:-/media/wrolpi}"
|
||||
CERT_DIR="${CERT_DIR:-/etc/ssl/wrolpi}"
|
||||
|
||||
if [[ -f "${CERT_FILE}" && -f "${KEY_FILE}" ]]; then
|
||||
echo "Certificates already exist at ${CERT_FILE} and ${KEY_FILE}"
|
||||
exit 0
|
||||
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
|
||||
|
||||
echo "Generating nginx certificates..."
|
||||
openssl genrsa -out "${KEY_FILE}" 2048
|
||||
openssl req -new -x509 -nodes -key "${KEY_FILE}" -out "${CERT_FILE}" -days 3650 \
|
||||
-subj "/C=US/ST=State/L=City/O=Org/OU=WROLPi/CN=$(hostname).local"
|
||||
chmod 640 "${KEY_FILE}" "${CERT_FILE}"
|
||||
echo "Certificates generated successfully."
|
||||
# --- Leaf certificate (always regenerated) ---
|
||||
echo "Generating WROLPi leaf certificate..."
|
||||
|
||||
# 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
|
||||
SAN="${SAN},IP:${ip}"
|
||||
done
|
||||
|
||||
# Append any extra SANs (e.g. host IP when running in Docker).
|
||||
if [[ -n "${EXTRA_SANS:-}" ]]; then
|
||||
SAN="${SAN},${EXTRA_SANS}"
|
||||
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}"
|
||||
fi
|
||||
|
||||
# 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
|
||||
|
|
|
|||
|
|
@ -39,6 +39,14 @@ if [[ ! -f /opt/wrolpi-blobs/map-db-gis.dump || ! -s /opt/wrolpi-blobs/map-db-gi
|
|||
wget https://wrolpi.nyc3.cdn.digitaloceanspaces.com/map-db-gis.dump -O /opt/wrolpi-blobs/map-db-gis.dump
|
||||
fi
|
||||
|
||||
# Install Caddy from official apt repo if not already installed.
|
||||
if ! command -v caddy &>/dev/null; then
|
||||
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
|
||||
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list
|
||||
apt-get update
|
||||
apt-get install -y caddy
|
||||
fi
|
||||
|
||||
# Install WROLPi Help.
|
||||
/opt/wrolpi/scripts/install_help_service.sh
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,14 @@ if [[ ! -f /opt/wrolpi-blobs/map-db-gis.dump || ! -s /opt/wrolpi-blobs/map-db-gi
|
|||
wget https://wrolpi.nyc3.cdn.digitaloceanspaces.com/map-db-gis.dump -O /opt/wrolpi-blobs/map-db-gis.dump
|
||||
fi
|
||||
|
||||
# Install Caddy from official apt repo if not already installed.
|
||||
if ! command -v caddy &>/dev/null; then
|
||||
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
|
||||
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list
|
||||
apt-get update
|
||||
apt-get install -y caddy
|
||||
fi
|
||||
|
||||
# Install WROLPi Help.
|
||||
/opt/wrolpi/scripts/install_help_service.sh
|
||||
|
||||
|
|
|
|||
28
scripts/renew_certificates.sh
Normal file
28
scripts/renew_certificates.sh
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/env bash
|
||||
# Check if the WROLPi leaf certificate is expired or expiring within 30 days.
|
||||
# If so, regenerate it and reload Caddy.
|
||||
|
||||
set -e
|
||||
|
||||
CERT_DIR="${CERT_DIR:-/etc/ssl/wrolpi}"
|
||||
LEAF_CERT="${CERT_DIR}/cert.crt"
|
||||
|
||||
if [ ! -f "${LEAF_CERT}" ]; then
|
||||
echo "Leaf certificate not found at ${LEAF_CERT}, generating..."
|
||||
/opt/wrolpi/scripts/generate_certificates.sh
|
||||
systemctl reload caddy || systemctl restart caddy || echo "Warning: failed to reload Caddy"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Check if cert expires within 30 days.
|
||||
if openssl x509 -checkend 2592000 -noout -in "${LEAF_CERT}" 2>/dev/null; then
|
||||
echo "Certificate is still valid for more than 30 days, no renewal needed."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Certificate is expired or expiring within 30 days, renewing..."
|
||||
/opt/wrolpi/scripts/generate_certificates.sh
|
||||
|
||||
# Reload Caddy to pick up new cert.
|
||||
systemctl reload caddy || systemctl restart caddy || echo "Warning: failed to reload Caddy"
|
||||
echo "Certificate renewed and Caddy reloaded."
|
||||
|
|
@ -23,5 +23,5 @@ find /media/wrolpi/zims -iname '*.zim' -exec kiwix-manage ${LIBRARY} add {} \;
|
|||
|
||||
[ ! -f ${LIBRARY} ] && echo "Could not find any Zim files to import" && exit 2
|
||||
|
||||
# Serve HTTP kiwix on 9085. nginx will serve HTTPS on 8085.
|
||||
# Serve HTTP kiwix on 9085. Caddy will serve HTTPS on 8085.
|
||||
kiwix-serve --library ${LIBRARY} --address 0.0.0.0 --port 9085
|
||||
|
|
|
|||
|
|
@ -104,5 +104,18 @@ fi
|
|||
# Migrate map DB if necessary. Do this before repair because it will reset map if map db is empty.
|
||||
/opt/wrolpi/wrolpi/scripts/migrate_map_db.sh || echo "Map DB migration failed."
|
||||
|
||||
# Migrate from nginx to Caddy if necessary.
|
||||
if ! command -v caddy &>/dev/null; then
|
||||
echo "Installing Caddy (replacing nginx)..."
|
||||
# Disable and stop nginx if it was previously used.
|
||||
systemctl stop nginx 2>/dev/null || :
|
||||
systemctl disable nginx 2>/dev/null || :
|
||||
# Install Caddy from official apt repo.
|
||||
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
|
||||
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list
|
||||
apt-get update
|
||||
apt-get install -y caddy
|
||||
fi
|
||||
|
||||
# Install any configs, restart services.
|
||||
/opt/wrolpi/repair.sh
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue