Add mkcert support for browser-trusted HTTPS certificates
No more browser warnings! mkcert creates locally-trusted certs. Pi Setup: - Auto-install mkcert during setup - Generate trusted certs when HTTPS enabled - Copy CA to /static/ca/rootCA.pem for easy device setup - New devices can download CA via HTTP and install it Docker: - docker-entrypoint.sh checks for mkcert, falls back to openssl - Shows instructions for CA distribution to other devices Scripts: - Added setup-trusted-certs.sh helper for local dev - Installs mkcert, generates certs, shows device setup instructions To trust on new devices: 1. Download: http://stegasoo.local/static/ca/rootCA.pem 2. Install as trusted CA in browser/OS 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -68,6 +68,7 @@ test_data/*.png
|
|||||||
scripts/*
|
scripts/*
|
||||||
!scripts/validate-release.sh
|
!scripts/validate-release.sh
|
||||||
!scripts/smoke-test.sh
|
!scripts/smoke-test.sh
|
||||||
|
!scripts/setup-trusted-certs.sh
|
||||||
|
|
||||||
# Web UI auth database and SSL certs
|
# Web UI auth database and SSL certs
|
||||||
instance/
|
instance/
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
# Docker entrypoint for Stegasoo Web UI
|
# Docker entrypoint for Stegasoo Web UI
|
||||||
# Handles SSL certificate generation and gunicorn startup
|
# Handles SSL certificate generation and gunicorn startup
|
||||||
#
|
#
|
||||||
|
# Supports mkcert for browser-trusted certificates (no warning screen)
|
||||||
|
#
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
@@ -11,24 +13,42 @@ CERT_FILE="$CERT_DIR/cert.pem"
|
|||||||
KEY_FILE="$CERT_DIR/key.pem"
|
KEY_FILE="$CERT_DIR/key.pem"
|
||||||
HOSTNAME="${STEGASOO_HOSTNAME:-localhost}"
|
HOSTNAME="${STEGASOO_HOSTNAME:-localhost}"
|
||||||
|
|
||||||
# Generate self-signed SSL certificate if HTTPS enabled and certs don't exist
|
# Generate SSL certificates
|
||||||
|
# Priority: 1) Existing certs, 2) mkcert (trusted), 3) openssl (self-signed)
|
||||||
generate_certs() {
|
generate_certs() {
|
||||||
if [ ! -f "$CERT_FILE" ] || [ ! -f "$KEY_FILE" ]; then
|
if [ -f "$CERT_FILE" ] && [ -f "$KEY_FILE" ]; then
|
||||||
echo "Generating self-signed SSL certificate for $HOSTNAME..."
|
|
||||||
mkdir -p "$CERT_DIR"
|
|
||||||
|
|
||||||
openssl req -x509 -newkey rsa:2048 \
|
|
||||||
-keyout "$KEY_FILE" \
|
|
||||||
-out "$CERT_FILE" \
|
|
||||||
-sha256 -days 365 -nodes \
|
|
||||||
-subj "/CN=$HOSTNAME" \
|
|
||||||
-addext "subjectAltName=DNS:$HOSTNAME,DNS:localhost,IP:127.0.0.1" \
|
|
||||||
2>/dev/null
|
|
||||||
|
|
||||||
echo "SSL certificate generated."
|
|
||||||
else
|
|
||||||
echo "Using existing SSL certificates."
|
echo "Using existing SSL certificates."
|
||||||
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$CERT_DIR"
|
||||||
|
|
||||||
|
# Try mkcert first (creates browser-trusted certs)
|
||||||
|
if command -v mkcert &> /dev/null; then
|
||||||
|
echo "Generating trusted certificate with mkcert for $HOSTNAME..."
|
||||||
|
cd "$CERT_DIR"
|
||||||
|
mkcert -key-file key.pem -cert-file cert.pem "$HOSTNAME" localhost 127.0.0.1 ::1
|
||||||
|
echo "Trusted certificate generated."
|
||||||
|
echo ""
|
||||||
|
echo " To trust on other devices, install the CA cert from:"
|
||||||
|
echo " $(mkcert -CAROOT)/rootCA.pem"
|
||||||
|
echo ""
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Fallback to self-signed (shows browser warning)
|
||||||
|
echo "Generating self-signed SSL certificate for $HOSTNAME..."
|
||||||
|
echo "(Install mkcert for browser-trusted certs without warnings)"
|
||||||
|
|
||||||
|
openssl req -x509 -newkey rsa:2048 \
|
||||||
|
-keyout "$KEY_FILE" \
|
||||||
|
-out "$CERT_FILE" \
|
||||||
|
-sha256 -days 365 -nodes \
|
||||||
|
-subj "/CN=$HOSTNAME" \
|
||||||
|
-addext "subjectAltName=DNS:$HOSTNAME,DNS:localhost,IP:127.0.0.1" \
|
||||||
|
2>/dev/null
|
||||||
|
|
||||||
|
echo "Self-signed certificate generated."
|
||||||
}
|
}
|
||||||
|
|
||||||
# Start gunicorn with appropriate settings
|
# Start gunicorn with appropriate settings
|
||||||
|
|||||||
53
rpi/setup.sh
53
rpi/setup.sh
@@ -184,6 +184,20 @@ else
|
|||||||
echo " gum already installed"
|
echo " gum already installed"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Install mkcert for browser-trusted certificates (no warning screen!)
|
||||||
|
echo " Installing mkcert for trusted HTTPS certificates..."
|
||||||
|
if ! command -v mkcert &>/dev/null; then
|
||||||
|
sudo apt-get install -y libnss3-tools
|
||||||
|
# Download mkcert for ARM64
|
||||||
|
sudo curl -sL "https://github.com/FiloSottile/mkcert/releases/download/v1.4.4/mkcert-v1.4.4-linux-arm64" -o /usr/local/bin/mkcert
|
||||||
|
sudo chmod +x /usr/local/bin/mkcert
|
||||||
|
# Install local CA (makes certs trusted on this Pi)
|
||||||
|
mkcert -install 2>/dev/null || true
|
||||||
|
echo " mkcert installed"
|
||||||
|
else
|
||||||
|
echo " mkcert already installed"
|
||||||
|
fi
|
||||||
|
|
||||||
echo -e "${GREEN}[4/12]${NC} Cloning Stegasoo..."
|
echo -e "${GREEN}[4/12]${NC} Cloning Stegasoo..."
|
||||||
|
|
||||||
# Clone Stegasoo first (needed to check for pre-built tarball)
|
# Clone Stegasoo first (needed to check for pre-built tarball)
|
||||||
@@ -593,19 +607,40 @@ if [ "$ENABLE_HTTPS" = "true" ]; then
|
|||||||
LOCAL_IP=$(hostname -I | awk '{print $1}')
|
LOCAL_IP=$(hostname -I | awk '{print $1}')
|
||||||
PI_HOSTNAME=$(hostname)
|
PI_HOSTNAME=$(hostname)
|
||||||
|
|
||||||
# Generate cert with SANs for IP, hostname, and localhost
|
# Try mkcert first (creates browser-trusted certs - no warning screen!)
|
||||||
openssl req -x509 -newkey rsa:2048 \
|
if command -v mkcert &> /dev/null; then
|
||||||
-keyout "$CERT_DIR/server.key" \
|
echo " Using mkcert for browser-trusted certificates..."
|
||||||
-out "$CERT_DIR/server.crt" \
|
cd "$CERT_DIR"
|
||||||
-days 365 -nodes \
|
mkcert -key-file server.key -cert-file server.crt \
|
||||||
-subj "/O=Stegasoo/CN=$PI_HOSTNAME" \
|
"$PI_HOSTNAME" "$PI_HOSTNAME.local" localhost "$LOCAL_IP" 127.0.0.1 ::1
|
||||||
-addext "subjectAltName=DNS:$PI_HOSTNAME,DNS:$PI_HOSTNAME.local,DNS:localhost,IP:$LOCAL_IP,IP:127.0.0.1" \
|
|
||||||
2>/dev/null
|
# Copy CA to web-accessible location for easy device setup
|
||||||
|
CA_ROOT=$(mkcert -CAROOT)
|
||||||
|
CA_DIR="$INSTALL_DIR/frontends/web/static/ca"
|
||||||
|
mkdir -p "$CA_DIR"
|
||||||
|
cp "$CA_ROOT/rootCA.pem" "$CA_DIR/"
|
||||||
|
|
||||||
|
echo -e " ${GREEN}✓${NC} Trusted certificates generated with mkcert"
|
||||||
|
echo -e " ${CYAN}Tip:${NC} New devices can get the CA from: http://$PI_HOSTNAME.local/static/ca/rootCA.pem"
|
||||||
|
else
|
||||||
|
# Fallback to self-signed (shows browser warning)
|
||||||
|
echo " Using self-signed certificate (browser will show warning)"
|
||||||
|
echo " Tip: Install mkcert for trusted certs without warnings"
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
echo -e " ${GREEN}✓${NC} Self-signed certificates generated"
|
||||||
|
fi
|
||||||
|
|
||||||
# Fix permissions
|
# Fix permissions
|
||||||
chmod 600 "$CERT_DIR/server.key"
|
chmod 600 "$CERT_DIR/server.key"
|
||||||
chown -R "$USER:$USER" "$CERT_DIR"
|
chown -R "$USER:$USER" "$CERT_DIR"
|
||||||
echo -e " ${GREEN}✓${NC} SSL certificates generated"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Setup port 443 redirect if requested
|
# Setup port 443 redirect if requested
|
||||||
|
|||||||
149
scripts/setup-trusted-certs.sh
Executable file
149
scripts/setup-trusted-certs.sh
Executable file
@@ -0,0 +1,149 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Setup trusted HTTPS certificates for Stegasoo
|
||||||
|
# Uses mkcert to create browser-trusted certs (no warning screens!)
|
||||||
|
#
|
||||||
|
# Usage: ./setup-trusted-certs.sh [hostname]
|
||||||
|
#
|
||||||
|
# This script:
|
||||||
|
# 1. Installs mkcert if needed
|
||||||
|
# 2. Creates a local CA (one-time)
|
||||||
|
# 3. Generates certs for your hostname
|
||||||
|
# 4. Shows how to trust the CA on other devices
|
||||||
|
#
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
HOSTNAME="${1:-stegasoo.local}"
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PROJECT_ROOT="$SCRIPT_DIR/.."
|
||||||
|
CERT_DIR="$PROJECT_ROOT/frontends/web/certs"
|
||||||
|
|
||||||
|
# Colors
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
CYAN='\033[0;36m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}╔═══════════════════════════════════════════════════════════════╗${NC}"
|
||||||
|
echo -e "${CYAN}║ Stegasoo Trusted Certificate Setup ║${NC}"
|
||||||
|
echo -e "${CYAN}╚═══════════════════════════════════════════════════════════════╝${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check/install mkcert
|
||||||
|
install_mkcert() {
|
||||||
|
if command -v mkcert &> /dev/null; then
|
||||||
|
echo -e "${GREEN}✓${NC} mkcert already installed"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${YELLOW}Installing mkcert...${NC}"
|
||||||
|
|
||||||
|
# Detect OS and install
|
||||||
|
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||||
|
# macOS
|
||||||
|
if command -v brew &> /dev/null; then
|
||||||
|
brew install mkcert
|
||||||
|
else
|
||||||
|
echo -e "${RED}Please install Homebrew first: https://brew.sh${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
elif [[ -f /etc/debian_version ]]; then
|
||||||
|
# Debian/Ubuntu/Raspberry Pi OS
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y libnss3-tools
|
||||||
|
|
||||||
|
# Download mkcert binary
|
||||||
|
ARCH=$(dpkg --print-architecture)
|
||||||
|
if [[ "$ARCH" == "arm64" ]] || [[ "$ARCH" == "aarch64" ]]; then
|
||||||
|
MKCERT_URL="https://github.com/FiloSottile/mkcert/releases/latest/download/mkcert-linux-arm64"
|
||||||
|
else
|
||||||
|
MKCERT_URL="https://github.com/FiloSottile/mkcert/releases/latest/download/mkcert-linux-amd64"
|
||||||
|
fi
|
||||||
|
|
||||||
|
sudo curl -L "$MKCERT_URL" -o /usr/local/bin/mkcert
|
||||||
|
sudo chmod +x /usr/local/bin/mkcert
|
||||||
|
elif [[ -f /etc/arch-release ]]; then
|
||||||
|
# Arch Linux
|
||||||
|
sudo pacman -S mkcert
|
||||||
|
else
|
||||||
|
echo -e "${RED}Unsupported OS. Please install mkcert manually:${NC}"
|
||||||
|
echo " https://github.com/FiloSottile/mkcert#installation"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${GREEN}✓${NC} mkcert installed"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Install local CA
|
||||||
|
setup_ca() {
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}Setting up local Certificate Authority...${NC}"
|
||||||
|
|
||||||
|
if mkcert -install 2>/dev/null; then
|
||||||
|
echo -e "${GREEN}✓${NC} Local CA installed in system trust store"
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}!${NC} Could not auto-install CA (may need manual browser import)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Generate certificates
|
||||||
|
generate_certs() {
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}Generating trusted certificate for: ${YELLOW}$HOSTNAME${NC}"
|
||||||
|
|
||||||
|
mkdir -p "$CERT_DIR"
|
||||||
|
cd "$CERT_DIR"
|
||||||
|
|
||||||
|
# Generate cert for hostname + common local names
|
||||||
|
mkcert -key-file key.pem -cert-file cert.pem \
|
||||||
|
"$HOSTNAME" \
|
||||||
|
localhost \
|
||||||
|
127.0.0.1 \
|
||||||
|
::1
|
||||||
|
|
||||||
|
echo -e "${GREEN}✓${NC} Certificates generated in: $CERT_DIR"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Show CA location for other devices
|
||||||
|
show_ca_info() {
|
||||||
|
CA_ROOT=$(mkcert -CAROOT)
|
||||||
|
CA_FILE="$CA_ROOT/rootCA.pem"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}════════════════════════════════════════════════════════════════${NC}"
|
||||||
|
echo -e "${GREEN} Setup Complete!${NC}"
|
||||||
|
echo -e "${CYAN}════════════════════════════════════════════════════════════════${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Your certificates are ready. Browsers on THIS machine will trust them."
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}To trust on OTHER devices (phones, tablets, other computers):${NC}"
|
||||||
|
echo ""
|
||||||
|
echo " 1. Copy the CA certificate to that device:"
|
||||||
|
echo -e " ${CYAN}$CA_FILE${NC}"
|
||||||
|
echo ""
|
||||||
|
echo " 2. Import it as a trusted CA:"
|
||||||
|
echo " - iOS: AirDrop/email the file, Settings > Profile Downloaded > Install"
|
||||||
|
echo " - Android: Settings > Security > Install from storage"
|
||||||
|
echo " - Windows: Double-click > Install > Trusted Root CAs"
|
||||||
|
echo " - macOS: Double-click > Keychain Access > Trust Always"
|
||||||
|
echo " - Linux: Copy to /usr/local/share/ca-certificates/ && update-ca-certificates"
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}Quick copy command:${NC}"
|
||||||
|
echo " scp $CA_FILE user@device:/path/"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Offer to serve CA file via HTTP for easy phone download
|
||||||
|
echo -e "${YELLOW}Or serve the CA for easy phone download:${NC}"
|
||||||
|
echo " python3 -m http.server 8080 -d $CA_ROOT"
|
||||||
|
echo " Then visit: http://$(hostname -I | awk '{print $1}'):8080/rootCA.pem"
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main
|
||||||
|
install_mkcert
|
||||||
|
setup_ca
|
||||||
|
generate_certs
|
||||||
|
show_ca_info
|
||||||
Reference in New Issue
Block a user