From 962c04084bb2ca8e9f480bfd7effd43094c1e281 Mon Sep 17 00:00:00 2001 From: "Aaron D. Lee" Date: Mon, 5 Jan 2026 22:16:12 -0500 Subject: [PATCH] Fix SSL certificate generation for HTTPS mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - wizard/setup now generate certs when HTTPS enabled - app.py has proper error handling for cert failures - Add custom SSL certificate documentation to INSTALL.md - Include SANs for hostname, localhost, and local IP Previously HTTPS could be enabled but certs weren't generated, causing SSL_ERROR_RX_RECORD_TOO_LONG browser errors. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- INSTALL.md | 79 ++++++++++++++++++++++++++++++++++++++++ frontends/web/app.py | 18 +++++++-- rpi/first-boot-wizard.sh | 26 +++++++++++++ rpi/setup.sh | 25 +++++++++++++ 4 files changed, 145 insertions(+), 3 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index 77cb079..fbec218 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -553,6 +553,85 @@ print(f'jpegio: {has_jpegio_support()}') --- +## Custom SSL Certificates + +By default, Stegasoo generates a self-signed certificate for HTTPS. To use your own certificate (e.g., from Let's Encrypt or your organization's CA): + +### Replace Self-Signed Certificates + +```bash +# Stop the service +sudo systemctl stop stegasoo + +# Backup existing certs (optional) +mv /opt/stegasoo/frontends/web/certs /opt/stegasoo/frontends/web/certs.bak + +# Create new certs directory +mkdir -p /opt/stegasoo/frontends/web/certs + +# Copy your certificates (adjust paths as needed) +cp /path/to/your/certificate.crt /opt/stegasoo/frontends/web/certs/server.crt +cp /path/to/your/private.key /opt/stegasoo/frontends/web/certs/server.key + +# Set permissions (key must be readable by service user) +chmod 600 /opt/stegasoo/frontends/web/certs/server.key +chown -R $(whoami):$(whoami) /opt/stegasoo/frontends/web/certs + +# Start the service +sudo systemctl start stegasoo +``` + +### Generate New Self-Signed Certificate + +If your certificate expires or you need to regenerate: + +```bash +# Stop service +sudo systemctl stop stegasoo + +# Generate new cert with SANs +CERT_DIR="/opt/stegasoo/frontends/web/certs" +LOCAL_IP=$(hostname -I | awk '{print $1}') +HOSTNAME=$(hostname) + +openssl req -x509 -newkey rsa:2048 \ + -keyout "$CERT_DIR/server.key" \ + -out "$CERT_DIR/server.crt" \ + -days 365 -nodes \ + -subj "/O=Stegasoo/CN=$HOSTNAME" \ + -addext "subjectAltName=DNS:$HOSTNAME,DNS:$HOSTNAME.local,DNS:localhost,IP:$LOCAL_IP,IP:127.0.0.1" + +chmod 600 "$CERT_DIR/server.key" + +# Start service +sudo systemctl start stegasoo +``` + +### Let's Encrypt with Certbot + +For publicly accessible servers: + +```bash +# Install certbot +sudo apt install certbot + +# Get certificate (standalone mode) +sudo certbot certonly --standalone -d yourdomain.com + +# Copy to Stegasoo +sudo cp /etc/letsencrypt/live/yourdomain.com/fullchain.pem /opt/stegasoo/frontends/web/certs/server.crt +sudo cp /etc/letsencrypt/live/yourdomain.com/privkey.pem /opt/stegasoo/frontends/web/certs/server.key +sudo chown $(whoami):$(whoami) /opt/stegasoo/frontends/web/certs/* +sudo chmod 600 /opt/stegasoo/frontends/web/certs/server.key + +# Restart +sudo systemctl restart stegasoo +``` + +**Note:** Set up a cron job or systemd timer to copy renewed certificates and restart Stegasoo. + +--- + ## Verification ### Check Installation diff --git a/frontends/web/app.py b/frontends/web/app.py index b8cf89b..842fada 100644 --- a/frontends/web/app.py +++ b/frontends/web/app.py @@ -2324,9 +2324,21 @@ if __name__ == "__main__": ssl_context = None if app.config.get("HTTPS_ENABLED", False): hostname = os.environ.get("STEGASOO_HOSTNAME", "localhost") - cert_path, key_path = ensure_certs(base_dir, hostname) - ssl_context = (str(cert_path), str(key_path)) - print(f"HTTPS enabled with self-signed certificate for {hostname}") + try: + cert_path, key_path = ensure_certs(base_dir, hostname) + if cert_path.exists() and key_path.exists(): + ssl_context = (str(cert_path), str(key_path)) + print(f"HTTPS enabled with self-signed certificate for {hostname}") + else: + print("ERROR: SSL certificates not found after generation attempt") + print(f" Expected: {cert_path}, {key_path}") + print(" Falling back to HTTP (INSECURE)") + except Exception as e: + print(f"ERROR: Failed to generate SSL certificates: {e}") + print(" Falling back to HTTP (INSECURE)") + print(" To fix: mkdir -p certs && openssl req -x509 -newkey rsa:2048 \\") + print(" -keyout certs/server.key -out certs/server.crt -days 365 -nodes \\") + print(" -subj '/CN=localhost'") # Auth status if app.config.get("AUTH_ENABLED", True): diff --git a/rpi/first-boot-wizard.sh b/rpi/first-boot-wizard.sh index df37b1b..edb9bda 100755 --- a/rpi/first-boot-wizard.sh +++ b/rpi/first-boot-wizard.sh @@ -279,6 +279,32 @@ EOF " gum style --foreground 82 "✓ Service configured" +# Generate SSL certificates if HTTPS enabled +if [ "$ENABLE_HTTPS" = "true" ]; then + gum spin --spinner dot --title "Generating SSL certificates..." -- bash -c " + CERT_DIR='$INSTALL_DIR/frontends/web/certs' + mkdir -p \"\$CERT_DIR\" + + # Get local IP for SAN + LOCAL_IP=\$(hostname -I | awk '{print \$1}') + HOSTNAME=\$(hostname) + + # Generate cert with SANs for IP, hostname, and localhost + openssl req -x509 -newkey rsa:2048 \ + -keyout \"\$CERT_DIR/server.key\" \ + -out \"\$CERT_DIR/server.crt\" \ + -days 365 -nodes \ + -subj \"/O=Stegasoo/CN=\$HOSTNAME\" \ + -addext \"subjectAltName=DNS:\$HOSTNAME,DNS:\$HOSTNAME.local,DNS:localhost,IP:\$LOCAL_IP,IP:127.0.0.1\" \ + 2>/dev/null + + # Fix permissions + chmod 600 \"\$CERT_DIR/server.key\" + chown -R $STEGASOO_USER:\$(id -gn $STEGASOO_USER) \"\$CERT_DIR\" + " + gum style --foreground 82 "✓ SSL certificates generated" +fi + # Setup port 443 if requested if [ "$USE_PORT_443" = "true" ]; then gum spin --spinner dot --title "Setting up port 443 redirect..." -- bash -c " diff --git a/rpi/setup.sh b/rpi/setup.sh index 895da0e..454def7 100755 --- a/rpi/setup.sh +++ b/rpi/setup.sh @@ -465,6 +465,31 @@ RestartSec=5 WantedBy=multi-user.target EOF +# Generate SSL certificates if HTTPS enabled +if [ "$ENABLE_HTTPS" = "true" ]; then + echo " Generating SSL certificates..." + CERT_DIR="$INSTALL_DIR/frontends/web/certs" + mkdir -p "$CERT_DIR" + + # Get local IP for SAN + LOCAL_IP=$(hostname -I | awk '{print $1}') + PI_HOSTNAME=$(hostname) + + # Generate cert with SANs for IP, hostname, and localhost + openssl req -x509 -newkey rsa:2048 \ + -keyout "$CERT_DIR/server.key" \ + -out "$CERT_DIR/server.crt" \ + -days 365 -nodes \ + -subj "/O=Stegasoo/CN=$PI_HOSTNAME" \ + -addext "subjectAltName=DNS:$PI_HOSTNAME,DNS:$PI_HOSTNAME.local,DNS:localhost,IP:$LOCAL_IP,IP:127.0.0.1" \ + 2>/dev/null + + # Fix permissions + chmod 600 "$CERT_DIR/server.key" + chown -R "$USER:$USER" "$CERT_DIR" + echo -e " ${GREEN}✓${NC} SSL certificates generated" +fi + # Setup port 443 redirect if requested if [ "$USE_PORT_443" = "true" ]; then echo " Setting up port 443 redirect..."