diff --git a/WEB_UI.md b/WEB_UI.md index 5bb8077..18e54cd 100644 --- a/WEB_UI.md +++ b/WEB_UI.md @@ -135,6 +135,19 @@ On first run with HTTPS enabled: **Note:** Browsers will show a security warning for self-signed certificates. This is expected for home network use. +**Tip:** To avoid browser warnings, use [mkcert](https://github.com/FiloSottile/mkcert) to generate locally-trusted certificates: + +```bash +# Install mkcert and create local CA (one-time) +mkcert -install + +# Generate trusted certs for your Pi +mkcert -key-file key.pem -cert-file cert.pem stegasoo.local localhost 127.0.0.1 YOUR_PI_IP + +# Copy to certs directory +mv key.pem cert.pem frontends/web/certs/ +``` + ### Disabling Authentication For development or trusted networks: diff --git a/data/WebUI_About.webp b/data/WebUI_About.webp new file mode 100644 index 0000000..81b8aee Binary files /dev/null and b/data/WebUI_About.webp differ diff --git a/data/WebUI_Account.webp b/data/WebUI_Account.webp new file mode 100644 index 0000000..b06130a Binary files /dev/null and b/data/WebUI_Account.webp differ diff --git a/data/WebUI_Login.webp b/data/WebUI_Login.webp new file mode 100644 index 0000000..f37e836 Binary files /dev/null and b/data/WebUI_Login.webp differ diff --git a/data/WebUI_Setup.webp b/data/WebUI_Setup.webp new file mode 100644 index 0000000..f37e836 Binary files /dev/null and b/data/WebUI_Setup.webp differ diff --git a/rpi/first-boot-wizard.sh b/rpi/first-boot-wizard.sh index baf0d8a..8b199e6 100755 --- a/rpi/first-boot-wizard.sh +++ b/rpi/first-boot-wizard.sh @@ -24,7 +24,7 @@ PROFILE_HOOK="/etc/profile.d/stegasoo-wizard.sh" # Check if this is first boot if [ ! -f "$FLAG_FILE" ]; then - exit 0 + exit 0 fi clear @@ -36,12 +36,12 @@ echo -e "${GRAY} . · . · . · . · . · . ${CYAN}/\\\\${GRAY} · . · ${CYAN}/ echo -e "${GRAY} · . · . · . · . · . · ${CYAN}\\\\/${GRAY} · . · ${CYAN}\\\\/${GRAY} · . · ${CYAN}\\\\/${GRAY} · . · ${CYAN}\\\\/${GRAY} · . · . · . ·${NC}" echo -e "${GRAY} . · . · . · . · . · . · . · . · . · . · . · . · . · . · . · . · .${NC}" echo -e "${GRAY} · . ${CYAN} ___ _____ ___ ___ _ ___ ___ ___ ${GRAY} . · . ·${NC}" -echo -e "${GRAY} . · ${CYAN}/ __||_ _|| __| / __| /_\\\\ / __| / _ \\\\ / _ \\\\${GRAY} · . · ·${NC}" -echo -e "${GRAY} · . ${CYAN}/ __||_ _|| __| / __| /_\\\\ / __| / _ \\\\ / _ \\\\${GRAY} . · . ·${NC}" +echo -e "${GRAY} . · ${CYAN}/ __||_ _|| __| / __| /_\\\\ / __| / _ \\\\ / _ \\\\${GRAY} · . · ·${NC}" +echo -e "${GRAY} · . ${CYAN}/ __||_ _|| __| / __| /_\\\\ / __| / _ \\\\ / _ \\\\${GRAY} . · . ·${NC}" echo -e "${GRAY} . · ${CYAN}\\\\__ \\\\ | | | _| | (_ | / _ \\\\ \\\\__ \\\\ | (_) || (_) |${GRAY} · . · ·${NC}" echo -e "${GRAY} · . ${CYAN}\\\\__ \\\\ | | | _| | (_ | / _ \\\\ \\\\__ \\\\ | (_) || (_) |${GRAY} . · . ·${NC}" -echo -e "${GRAY} . · ${CYAN}|___/ |_| |___| \\\\___|/_/ \\\\_\\\\|___/ \\\\___/ \\\\___/${GRAY} · . · ·${NC}" -echo -e "${GRAY} · . ${CYAN}|___/ |_| |___| \\\\___|/_/ \\\\_\\\\|___/ \\\\___/ \\\\___/${GRAY} . · . ·${NC}" +echo -e "${GRAY} . · ${CYAN}|___/ |_| |___| \\\\___|/_/ \\\\_\\\\|___/ \\\\___/ \\\\___/${GRAY} · . · ·${NC}" +echo -e "${GRAY} · . ${CYAN}|___/ |_| |___| \\\\___|/_/ \\\\_\\\\|___/ \\\\___/ \\\\___/${GRAY} . · . ·${NC}" echo -e "${GRAY} · . · . · . · . · . · . · . · . · . · . · . · . · . · . · . · . ·${NC}" echo -e "${GRAY} . · . · . · . · . · . · . · . · . · . · . · . · . · . · . · . · .${NC}" echo -e "${GRAY} · . · ${CYAN}~~~~${NC} ${GRAY}· . · . · .${NC} ${CYAN}First Boot Wizard${NC} ${GRAY}· . · . · ${CYAN}~~~~${NC} ${GRAY}· . · . ·${NC}" @@ -85,10 +85,10 @@ echo "" read -p "Enable HTTPS? [Y/n] " -n 1 -r echo if [[ ! $REPLY =~ ^[Nn]$ ]]; then - ENABLE_HTTPS="true" - echo "" - echo -e " ${GREEN}✓${NC} HTTPS will be enabled" - sleep 1 + ENABLE_HTTPS="true" + echo "" + echo -e " ${GREEN}✓${NC} HTTPS will be enabled" + sleep 1 fi # ============================================================================= @@ -96,29 +96,29 @@ fi # ============================================================================= if [ "$ENABLE_HTTPS" = "true" ]; then - clear - echo -e "${BOLD}Step 2 of 3: Port Configuration${NC}" - echo -e "${BLUE}-------------------------------------------------------${NC}" + clear + echo -e "${BOLD}Step 2 of 3: Port Configuration${NC}" + echo -e "${BLUE}-------------------------------------------------------${NC}" + echo "" + echo "The standard HTTPS port is 443, which means you can access" + echo "Stegasoo without specifying a port in the URL." + echo "" + echo " Port 443: https://stegasoo.local" + echo " Port 5000: https://stegasoo.local:5000" + echo "" + echo -e "${YELLOW}Note:${NC} Port 443 requires an iptables redirect rule." + echo "" + echo " [Y] Use port 443 (cleaner URLs)" + echo " [n] Use port 5000 (default, no extra config)" + echo "" + read -p "Use standard port 443? [Y/n] " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Nn]$ ]]; then + USE_PORT_443="true" echo "" - echo "The standard HTTPS port is 443, which means you can access" - echo "Stegasoo without specifying a port in the URL." - echo "" - echo " Port 443: https://stegasoo.local" - echo " Port 5000: https://stegasoo.local:5000" - echo "" - echo -e "${YELLOW}Note:${NC} Port 443 requires an iptables redirect rule." - echo "" - echo " [Y] Use port 443 (cleaner URLs)" - echo " [n] Use port 5000 (default, no extra config)" - echo "" - read -p "Use standard port 443? [Y/n] " -n 1 -r - echo - if [[ ! $REPLY =~ ^[Nn]$ ]]; then - USE_PORT_443="true" - echo "" - echo -e " ${GREEN}✓${NC} Port 443 will be configured" - sleep 1 - fi + echo -e " ${GREEN}✓${NC} Port 443 will be configured" + sleep 1 + fi fi # ============================================================================= @@ -143,32 +143,32 @@ echo "" read -p "Generate a private channel key? [y/N] " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then + echo "" + echo "Generating channel key..." + + # Source the venv and generate key + source "$INSTALL_DIR/venv/bin/activate" 2>/dev/null + CHANNEL_KEY=$(python -c "from stegasoo.channel import generate_channel_key; print(generate_channel_key())" 2>/dev/null) + + if [ -n "$CHANNEL_KEY" ]; then echo "" - echo "Generating channel key..." - - # Source the venv and generate key - source "$INSTALL_DIR/venv/bin/activate" 2>/dev/null - CHANNEL_KEY=$(python -c "from stegasoo.channel import generate_channel_key; print(generate_channel_key())" 2>/dev/null) - - if [ -n "$CHANNEL_KEY" ]; then - echo "" - echo -e " ${GREEN}✓${NC} Channel key generated!" - echo "" - echo -e " ${BOLD}${YELLOW}$CHANNEL_KEY${NC}" - echo "" - echo -e " ${RED}*** IMPORTANT: Write down or copy this key NOW! ***${NC}" - echo -e " ${RED}You'll need to share it with anyone who should decode${NC}" - echo -e " ${RED}your images. This key won't be shown again.${NC}" - echo "" - read -p "Press Enter when you've saved the key..." - else - echo -e " ${RED}✗${NC} Failed to generate key. Using public mode." - CHANNEL_KEY="" - fi + echo -e " ${GREEN}✓${NC} Channel key generated!" + echo "" + echo -e " ${BOLD}${YELLOW}$CHANNEL_KEY${NC}" + echo "" + echo -e " ${RED}*** IMPORTANT: Write down or copy this key NOW! ***${NC}" + echo -e " ${RED}You'll need to share it with anyone who should decode${NC}" + echo -e " ${RED}your images. This key won't be shown again.${NC}" + echo "" + read -p "Press Enter when you've saved the key..." + else + echo -e " ${RED}✗${NC} Failed to generate key. Using public mode." + CHANNEL_KEY="" + fi else - echo "" - echo -e " ${YELLOW}→${NC} Using public mode" - sleep 1 + echo "" + echo -e " ${YELLOW}→${NC} Using public mode" + sleep 1 fi # ============================================================================= @@ -185,7 +185,7 @@ STEGASOO_USER=$(stat -c '%U' "$INSTALL_DIR" 2>/dev/null || echo "pi") echo " Updating systemd service..." -sudo tee /etc/systemd/system/stegasoo.service > /dev/null </dev/null < /dev/null; then - sudo apt-get install -y iptables > /dev/null 2>&1 - fi + # Install iptables if needed + if ! command -v iptables &>/dev/null; then + sudo apt-get install -y iptables >/dev/null 2>&1 + fi - # Add redirect rule (check if it already exists) - if ! sudo iptables -t nat -C PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 5000 2>/dev/null; then - sudo iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 5000 - fi - sudo sh -c 'iptables-save > /etc/iptables.rules' + # Add redirect rule (check if it already exists) + if ! sudo iptables -t nat -C PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 5000 2>/dev/null; then + sudo iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 5000 + fi + sudo sh -c 'iptables-save > /etc/iptables.rules' - # Create/update persistence service - sudo tee /etc/systemd/system/iptables-restore.service > /dev/null </dev/null < /dev/null 2>&1 - echo -e " ${GREEN}✓${NC} Port 443 redirect configured" + sudo systemctl enable iptables-restore.service >/dev/null 2>&1 + echo -e " ${GREEN}✓${NC} Port 443 redirect configured" fi echo " Reloading systemd..." @@ -250,9 +250,9 @@ sudo systemctl restart stegasoo sleep 2 if systemctl is-active --quiet stegasoo; then - echo -e " ${GREEN}✓${NC} Stegasoo started successfully" + echo -e " ${GREEN}✓${NC} Stegasoo started successfully" else - echo -e " ${RED}✗${NC} Failed to start (check: journalctl -u stegasoo)" + echo -e " ${RED}✗${NC} Failed to start (check: journalctl -u stegasoo)" fi # Remove first-boot flag and profile hook @@ -270,7 +270,7 @@ PI_IP=$(hostname -I | awk '{print $1}') HOSTNAME=$(hostname) echo -e "${GREEN}" -cat << 'BANNER' +cat <<'BANNER' _____ _ / ____| | | (___ | |_ ___ __ _ __ _ ___ ___ ___ @@ -287,22 +287,22 @@ echo "" echo -e "${GREEN}Access URL:${NC}" if [ "$ENABLE_HTTPS" = "true" ]; then - if [ "$USE_PORT_443" = "true" ]; then - echo -e " ${BOLD}${YELLOW}https://$PI_IP${NC}" - echo -e " ${BOLD}${YELLOW}https://$HOSTNAME.local${NC} (if mDNS works)" - else - echo -e " ${BOLD}${YELLOW}https://$PI_IP:5000${NC}" - echo -e " ${BOLD}${YELLOW}https://$HOSTNAME.local:5000${NC} (if mDNS works)" - fi + if [ "$USE_PORT_443" = "true" ]; then + echo -e " ${BOLD}${YELLOW}https://$PI_IP${NC}" + echo -e " ${BOLD}${YELLOW}https://$HOSTNAME.local${NC} (if mDNS works)" + else + echo -e " ${BOLD}${YELLOW}https://$PI_IP:5000${NC}" + echo -e " ${BOLD}${YELLOW}https://$HOSTNAME.local:5000${NC} (if mDNS works)" + fi else - echo -e " ${BOLD}${YELLOW}http://$PI_IP:5000${NC}" + echo -e " ${BOLD}${YELLOW}http://$PI_IP:5000${NC}" fi echo "" if [ -n "$CHANNEL_KEY" ]; then - echo -e "${GREEN}Channel Key:${NC}" - echo -e " ${YELLOW}$CHANNEL_KEY${NC}" - echo "" + echo -e "${GREEN}Channel Key:${NC}" + echo -e " ${YELLOW}$CHANNEL_KEY${NC}" + echo "" fi echo -e "${GREEN}First Steps:${NC}" diff --git a/rpi/pull-image.sh b/rpi/pull-image.sh index 71a8510..4eec150 100755 --- a/rpi/pull-image.sh +++ b/rpi/pull-image.sh @@ -3,9 +3,11 @@ # Pull Stegasoo image from SD card # Auto-detects SD card, copies with progress, shrinks, and compresses # -# Usage: ./pull-image.sh [output-name] +# Usage: ./pull-image.sh [output-name] [device] # Output will be: stegasoo-rpi-YYYYMMDD.img.zst (or custom name) # +# If device is specified, skips auto-detection (useful for large drives) +# set -e @@ -30,12 +32,13 @@ if [ "$EUID" -ne 0 ]; then exit 1 fi -# Output filename +# Output filename and optional device if [ -n "$1" ]; then OUTPUT="$1" else OUTPUT="stegasoo-rpi-$(date +%Y%m%d).img.zst" fi +MANUAL_DEVICE="$2" # Remove .zst extension for intermediate file IMG_FILE="${OUTPUT%.zst}" @@ -50,71 +53,87 @@ echo "║ Stegasoo SD Card Image Puller ║" echo "╚═══════════════════════════════════════════════════════════════╝" echo -e "${NC}" -# Auto-detect SD card candidates -# Looking for: USB/removable, 8-128GB, not mounted as root filesystem -echo -e "${BOLD}Scanning for SD cards...${NC}" -echo "" - -declare -a CANDIDATES -declare -a CANDIDATE_INFO - -while IFS= read -r line; do - DEV=$(echo "$line" | awk '{print $1}') - SIZE=$(echo "$line" | awk '{print $2}') - TYPE=$(echo "$line" | awk '{print $3}') - TRAN=$(echo "$line" | awk '{print $4}') - MODEL=$(echo "$line" | awk '{print $5" "$6" "$7}' | xargs) - - # Skip if it's the root filesystem - if mount | grep -q "^/dev/${DEV}[0-9]* on / "; then - continue - fi - - # Skip if any partition is mounted as root - ROOT_DEV=$(mount | grep " on / " | awk '{print $1}' | sed 's/[0-9]*$//') - if [[ "/dev/$DEV" == "$ROOT_DEV" ]]; then - continue - fi - - # Get size in bytes for reliable comparison - SIZE_BYTES=$(lsblk -b -d -o SIZE -n "/dev/$DEV" 2>/dev/null | tr -d ' ') - SIZE_GB_INT=$((SIZE_BYTES / 1073741824)) # 1024^3 - - # Check if size is in SD card range (8GB - 128GB) - if [ "$SIZE_GB_INT" -ge 8 ] && [ "$SIZE_GB_INT" -le 128 ]; then - CANDIDATES+=("/dev/$DEV") - CANDIDATE_INFO+=("$SIZE $TYPE ${TRAN:-???} $MODEL") - fi -done < <(lsblk -d -o NAME,SIZE,TYPE,TRAN,MODEL -n | grep "disk") - -if [ ${#CANDIDATES[@]} -eq 0 ]; then - echo -e "${RED}No SD card candidates found.${NC}" - echo "Looking for USB/removable disks between 8GB and 128GB." - echo "" - echo "Available disks:" - lsblk -d -o NAME,SIZE,TYPE,TRAN,MODEL - exit 1 -fi - -echo -e "${GREEN}Found ${#CANDIDATES[@]} candidate(s):${NC}" -echo "" - -for i in "${!CANDIDATES[@]}"; do - echo -e " ${BOLD}[$((i+1))]${NC} ${CANDIDATES[$i]} - ${CANDIDATE_INFO[$i]}" -done - -echo "" - -if [ ${#CANDIDATES[@]} -eq 1 ]; then - SELECTED="${CANDIDATES[0]}" - echo -e "Auto-selected: ${YELLOW}$SELECTED${NC}" -else - read -p "Select device [1-${#CANDIDATES[@]}]: " -r - if [[ ! $REPLY =~ ^[0-9]+$ ]] || [ "$REPLY" -lt 1 ] || [ "$REPLY" -gt ${#CANDIDATES[@]} ]; then - echo -e "${RED}Invalid selection.${NC}" +# Use manual device or auto-detect +if [ -n "$MANUAL_DEVICE" ]; then + # Manual device specified + if [ ! -b "$MANUAL_DEVICE" ]; then + echo -e "${RED}Error: $MANUAL_DEVICE is not a block device${NC}" exit 1 fi - SELECTED="${CANDIDATES[$((REPLY-1))]}" + SELECTED="$MANUAL_DEVICE" + echo -e "Using specified device: ${YELLOW}$SELECTED${NC}" + echo "" + lsblk "$SELECTED" -o NAME,SIZE,TYPE,MODEL + echo "" +else + # Auto-detect SD card candidates + # Looking for: USB/removable, 8-128GB, not mounted as root filesystem + echo -e "${BOLD}Scanning for SD cards...${NC}" + echo "" + + declare -a CANDIDATES + declare -a CANDIDATE_INFO + + while IFS= read -r line; do + DEV=$(echo "$line" | awk '{print $1}') + SIZE=$(echo "$line" | awk '{print $2}') + TYPE=$(echo "$line" | awk '{print $3}') + TRAN=$(echo "$line" | awk '{print $4}') + MODEL=$(echo "$line" | awk '{print $5" "$6" "$7}' | xargs) + + # Skip if it's the root filesystem + if mount | grep -q "^/dev/${DEV}[0-9]* on / "; then + continue + fi + + # Skip if any partition is mounted as root + ROOT_DEV=$(mount | grep " on / " | awk '{print $1}' | sed 's/[0-9]*$//') + if [[ "/dev/$DEV" == "$ROOT_DEV" ]]; then + continue + fi + + # Get size in bytes for reliable comparison + SIZE_BYTES=$(lsblk -b -d -o SIZE -n "/dev/$DEV" 2>/dev/null | tr -d ' ') + SIZE_GB_INT=$((SIZE_BYTES / 1073741824)) # 1024^3 + + # Check if size is in SD card range (8GB - 128GB) + if [ "$SIZE_GB_INT" -ge 8 ] && [ "$SIZE_GB_INT" -le 128 ]; then + CANDIDATES+=("/dev/$DEV") + CANDIDATE_INFO+=("$SIZE $TYPE ${TRAN:-???} $MODEL") + fi + done < <(lsblk -d -o NAME,SIZE,TYPE,TRAN,MODEL -n | grep "disk") + + if [ ${#CANDIDATES[@]} -eq 0 ]; then + echo -e "${RED}No SD card candidates found.${NC}" + echo "Looking for USB/removable disks between 8GB and 128GB." + echo "" + echo "Available disks:" + lsblk -d -o NAME,SIZE,TYPE,TRAN,MODEL + echo "" + echo -e "${YELLOW}Tip: Specify device manually: $0 output.img.zst /dev/sdX${NC}" + exit 1 + fi + + echo -e "${GREEN}Found ${#CANDIDATES[@]} candidate(s):${NC}" + echo "" + + for i in "${!CANDIDATES[@]}"; do + echo -e " ${BOLD}[$((i+1))]${NC} ${CANDIDATES[$i]} - ${CANDIDATE_INFO[$i]}" + done + + echo "" + + if [ ${#CANDIDATES[@]} -eq 1 ]; then + SELECTED="${CANDIDATES[0]}" + echo -e "Auto-selected: ${YELLOW}$SELECTED${NC}" + else + read -p "Select device [1-${#CANDIDATES[@]}]: " -r + if [[ ! $REPLY =~ ^[0-9]+$ ]] || [ "$REPLY" -lt 1 ] || [ "$REPLY" -gt ${#CANDIDATES[@]} ]; then + echo -e "${RED}Invalid selection.${NC}" + exit 1 + fi + SELECTED="${CANDIDATES[$((REPLY-1))]}" + fi fi # Show partitions diff --git a/test_data/rpi_20260102.jpg b/test_data/rpi_20260102.jpg new file mode 100644 index 0000000..33560d6 Binary files /dev/null and b/test_data/rpi_20260102.jpg differ