Conversation
# Add automated VPS installation script ## Summary This PR adds a comprehensive installation script (`install.sh`) that automates the complete setup of StreamNZB on a VPS with SSL/TLS encryption. ## Features ### SSL Options - **Cloudflare DNS API** (Recommended): End-to-end encryption with Full (Strict) mode - **Let's Encrypt HTTP Challenge**: Direct SSL certificate without Cloudflare ### Security - Caddy reverse proxy with security headers (HSTS, X-Frame-Options, etc.) - UFW firewall configuration (ports 80, 443, 1119, SSH) - Fail2Ban for brute-force protection - Auto-generated security token (or custom) ### Convenience - Interactive setup wizard - Existing installation detection (Update/Reinstall options) - DNS validation before SSL setup (IPv4 + IPv6 support) - Cloudflare API token verification - Automatic Zone ID lookup - Systemd service for auto-start on boot - Info file with all access details saved to `/opt/streamnzb/INFO.txt` ## Usage ```bash # Download and run curl -O https://raw.githubusercontent.com/Gaisberg/streamnzb/main/install.sh chmod +x install.sh sudo bash install.sh ``` ## Requirements - Ubuntu 20.04+ or Debian 11+ - Root access - Domain pointing to server - (Optional) Cloudflare account with API token for DNS challenge ## What it installs - Docker + Docker Compose - Caddy (with Cloudflare DNS plugin if selected) - StreamNZB container - UFW + Fail2Ban ## Screenshots / Example Output ``` ╔═══════════════════════════════════════════════════════════════════╗ ║ StreamNZB VPS Installation Script v2.2 ║ ║ End-to-End SSL via Cloudflare DNS API or Let's Encrypt ║ ╚═══════════════════════════════════════════════════════════════════╝ === SSL Configuration === Choose SSL method: 1) Cloudflare DNS API (Recommended - Most Secure) 2) Caddy + Let's Encrypt (HTTP Challenge) === DNS Check === ℹ Server IPv4: 77.77.77.77 ℹ Checking DNS for streamnzb.example.com... ✓ DNS correctly configured! (IPv4 match) ╔═══════════════════════════════════════════════════════════════════╗ ║ Installation successful! ║ ╚═══════════════════════════════════════════════════════════════════╝ ## Tested on - Ubuntu 24.04 LTS - Hetzner Cloud VPS ---
📝 WalkthroughWalkthroughAdds a new interactive Bash installer script that deploys StreamNZB with Docker Compose, supports Cloudflare DNS‑01 or Let’s Encrypt HTTP SSL, performs domain/IP/DNS validation, manages Cloudflare token/zone, writes config/env/Caddy/docker-compose files, applies UFW/Fail2Ban, creates a systemd service, and starts containers. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant Installer as Installer Script
participant Host as Server
participant DNS as DNS Resolver
participant Cloudflare as Cloudflare API
participant Docker as Docker/Compose
participant Caddy as Caddy (SSL)
participant Security as UFW/Fail2Ban
User->>Installer: run `install-streamnzb.sh` (root)
Installer->>Host: detect OS, check prerequisites
Installer->>User: prompt domain, email, SSL mode, Cloudflare token?
Installer->>DNS: resolve/verify domain -> server IP
alt Cloudflare DNS‑01
Installer->>Cloudflare: validate token, fetch zone ID
Cloudflare-->>Installer: zone ID
Installer->>Caddy: generate DNS‑01 configuration
else Let's Encrypt HTTP
Installer->>Caddy: generate HTTP challenge configuration
end
Installer->>Docker: write `docker-compose.yml`, pull images, start containers
Docker-->>Installer: container status
Installer->>Security: apply UFW rules, configure Fail2Ban
Installer->>Host: create systemd service, save `.install-config` and `.env`, write INFO.txt
Installer->>User: output UI/NNTP endpoints and next steps
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
⚔️ Resolve merge conflicts (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@install-streamnzb.sh`:
- Around line 732-739: The APT repo line currently hardcodes
"download.docker.com/linux/ubuntu" (the echo ... | tee ... that constructs
docker.list) which breaks on Debian; change it to derive the distro ID from
/etc/os-release (e.g. use $(. /etc/os-release && echo "$ID") or an OS_ID
variable) and use that in the URL instead of "ubuntu", with a sensible fallback
(e.g. ubuntu) to keep behavior safe; ensure the rest of the stanza (dpkg
--print-architecture, signed-by=/etc/apt/keyrings/docker.asc, and
VERSION_CODENAME usage) is left intact.
- Around line 947-956: Allow SSH access before enabling the firewall and detect
the actual SSH port to avoid lockout: parse /etc/ssh/sshd_config (look for an
explicit Port setting) or fallback to 22 into a variable (e.g., SSH_PORT), run
ufw allow for that port/protocol (ufw allow ${SSH_PORT}/tcp) — ideally before
running ufw --force enable — and keep ufw allow ssh as a fallback; ensure these
checks/allow rules occur prior to the call to ufw --force enable so a mid-script
failure cannot lock out SSH.
🧹 Nitpick comments (7)
install-streamnzb.sh (7)
147-159: Separate declaration and assignment to avoid masking return values.When
localand command substitution are combined, the exit status of the command is masked by the success oflocal. If the curl fails, the error won't be detected.♻️ Proposed fix
verify_cloudflare_token() { local token="$1" - - local response=$(curl -s -X GET "https://api.cloudflare.com/client/v4/user/tokens/verify" \ + local response + response=$(curl -s -X GET "https://api.cloudflare.com/client/v4/user/tokens/verify" \ -H "Authorization: Bearer ${token}" \ -H "Content-Type: application/json")
161-176: Separate declaration and assignment; root domain extraction may fail for multi-part TLDs.
- Same SC2155 issue: separate
localdeclaration from assignment forroot_domain,response, andzone_id.- The root domain extraction
'{print $(NF-1)"."$NF}'will produce incorrect results for domains with country-code second-level TLDs (e.g.,app.example.co.uk→co.ukinstead ofexample.co.uk).♻️ Proposed fix for declaration separation
get_cloudflare_zone_id() { local token="$1" local domain="$2" # Extract root domain (last two parts) - local root_domain=$(echo "$domain" | awk -F. '{print $(NF-1)"."$NF}') - - local response=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones?name=${root_domain}" \ + local root_domain + root_domain=$(echo "$domain" | awk -F. '{print $(NF-1)"."$NF}') + + local response + response=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones?name=${root_domain}" \ -H "Authorization: Bearer ${token}" \ -H "Content-Type: application/json") - local zone_id=$(echo "$response" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4) + local zone_id + zone_id=$(echo "$response" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)For multi-part TLDs, consider documenting this limitation or implementing a more robust extraction that handles common cases like
.co.uk,.com.au, etc.
706-707: Consider removing or making the full system upgrade optional.Running
apt-get upgrade -ycan be time-consuming and may introduce unexpected changes. For an installation script,apt-get updatealone is typically sufficient to ensure package lists are current before installing dependencies.♻️ Proposed fix
print_section "System Update" apt-get update -apt-get upgrade -y +# Uncomment the following line if you want to upgrade all packages: +# apt-get upgrade -y
812-814: Theversionkey in docker-compose.yml is obsolete.Docker Compose V2+ ignores the
versionfield and may emit deprecation warnings. Consider removing it for cleaner output.♻️ Proposed fix (Cloudflare mode)
-version: '3.8' - services:The same applies to the Let's Encrypt mode docker-compose.yml at line 898.
996-1002:TimeoutStartSec=0disables timeout, risking indefinite hangs.If Docker or containers fail to start, systemd will wait forever. Consider setting a reasonable timeout (e.g., 300 seconds).
♻️ Proposed fix
[Service] Type=oneshot RemainAfterExit=yes WorkingDirectory=${INSTALL_DIR} ExecStart=/usr/bin/docker compose up -d ExecStop=/usr/bin/docker compose down -TimeoutStartSec=0 +TimeoutStartSec=300
590-598: Consider validating the timezone input.Invalid timezones could cause container issues. A basic validation could check against
/usr/share/zoneinfoor usetimedatectl list-timezonesif available.♻️ Proposed validation
# Ask for timezone printf "\n" printf "${YELLOW}Timezone [${DEFAULT_TZ}]: ${NC}" read INPUT_TZ if [ -z "$INPUT_TZ" ]; then TIMEZONE="$DEFAULT_TZ" else + # Basic validation: check if timezone exists + if [ -f "/usr/share/zoneinfo/${INPUT_TZ}" ]; then + TIMEZONE="$INPUT_TZ" + else + print_warning "Timezone '${INPUT_TZ}' not found, using ${DEFAULT_TZ}" + TIMEZONE="$DEFAULT_TZ" + fi - TIMEZONE="$INPUT_TZ" fi
1080-1093: Container status check is fragile.The check
docker compose ps | grep -q "running"depends on output format which can vary between Docker Compose versions. Consider using a more robust approach.♻️ More robust alternative
-if docker compose ps | grep -q "running"; then +if docker compose ps --status running --quiet 2>/dev/null | grep -q .; then print_success "Containers are running!"Or check specific container:
if docker inspect --format='{{.State.Running}}' streamnzb 2>/dev/null | grep -q "true"; thenThe same pattern at line 381 in the update section should also be updated.
| install -m 0755 -d /etc/apt/keyrings | ||
| curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc | ||
| chmod a+r /etc/apt/keyrings/docker.asc | ||
|
|
||
| echo \ | ||
| "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \ | ||
| $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ | ||
| tee /etc/apt/sources.list.d/docker.list > /dev/null |
There was a problem hiding this comment.
Docker installation is Ubuntu-specific but script claims Debian support.
The Docker repository URL hardcodes download.docker.com/linux/ubuntu. This will fail on Debian systems. Consider using $ID from /etc/os-release to select the correct repository.
🐛 Proposed fix
install -m 0755 -d /etc/apt/keyrings
- curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
+ curl -fsSL "https://download.docker.com/linux/${OS_NAME}/gpg" -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
echo \
- "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
+ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/${OS_NAME} \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
tee /etc/apt/sources.list.d/docker.list > /dev/null🤖 Prompt for AI Agents
In `@install-streamnzb.sh` around lines 732 - 739, The APT repo line currently
hardcodes "download.docker.com/linux/ubuntu" (the echo ... | tee ... that
constructs docker.list) which breaks on Debian; change it to derive the distro
ID from /etc/os-release (e.g. use $(. /etc/os-release && echo "$ID") or an OS_ID
variable) and use that in the URL instead of "ubuntu", with a sensible fallback
(e.g. ubuntu) to keep behavior safe; ensure the rest of the stanza (dpkg
--print-architecture, signed-by=/etc/apt/keyrings/docker.asc, and
VERSION_CODENAME usage) is left intact.
| print_section "Firewall Configuration" | ||
|
|
||
| ufw --force enable | ||
| ufw allow ssh | ||
| ufw allow 80/tcp | ||
| ufw allow 443/tcp | ||
| ufw allow 1119/tcp | ||
| ufw reload | ||
|
|
||
| print_success "Firewall configured." |
There was a problem hiding this comment.
Risk of SSH lockout if script fails mid-execution or uses non-standard SSH port.
Two concerns:
- If the script fails between
ufw --force enableandufw allow ssh, the user could be locked out. ufw allow sshonly allows port 22. Users with non-standard SSH ports would be locked out immediately.
Consider allowing SSH before enabling ufw, and checking the actual SSH port.
🛡️ Proposed fix
print_section "Firewall Configuration"
+# Get current SSH port from sshd config (fallback to 22)
+SSH_PORT=$(grep -E "^Port\s+" /etc/ssh/sshd_config 2>/dev/null | awk '{print $2}' || echo "22")
+[ -z "$SSH_PORT" ] && SSH_PORT="22"
+
+# Allow SSH first before enabling firewall to prevent lockout
+ufw allow "${SSH_PORT}/tcp" comment 'SSH'
ufw --force enable
-ufw allow ssh
ufw allow 80/tcp
ufw allow 443/tcp
ufw allow 1119/tcp
ufw reload📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| print_section "Firewall Configuration" | |
| ufw --force enable | |
| ufw allow ssh | |
| ufw allow 80/tcp | |
| ufw allow 443/tcp | |
| ufw allow 1119/tcp | |
| ufw reload | |
| print_success "Firewall configured." | |
| print_section "Firewall Configuration" | |
| # Get current SSH port from sshd config (fallback to 22) | |
| SSH_PORT=$(grep -E "^Port\s+" /etc/ssh/sshd_config 2>/dev/null | awk '{print $2}' || echo "22") | |
| [ -z "$SSH_PORT" ] && SSH_PORT="22" | |
| # Allow SSH first before enabling firewall to prevent lockout | |
| ufw allow "${SSH_PORT}/tcp" comment 'SSH' | |
| ufw --force enable | |
| ufw allow 80/tcp | |
| ufw allow 443/tcp | |
| ufw allow 1119/tcp | |
| ufw reload | |
| print_success "Firewall configured." |
🤖 Prompt for AI Agents
In `@install-streamnzb.sh` around lines 947 - 956, Allow SSH access before
enabling the firewall and detect the actual SSH port to avoid lockout: parse
/etc/ssh/sshd_config (look for an explicit Port setting) or fallback to 22 into
a variable (e.g., SSH_PORT), run ufw allow for that port/protocol (ufw allow
${SSH_PORT}/tcp) — ideally before running ufw --force enable — and keep ufw
allow ssh as a fallback; ensure these checks/allow rules occur prior to the call
to ufw --force enable so a mid-script failure cannot lock out SSH.
|
Note Docstrings generation - SUCCESS |
Docstrings generation was requested by @Gaisberg. * #49 (comment) The following files were modified: * `install-streamnzb.sh`
synologyy
left a comment
There was a problem hiding this comment.
removed SECURITY_TOKEN and fixes docker
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In `@install-streamnzb.sh`:
- Around line 166-167: The root domain extraction using local root_domain=$(echo
"$domain" | awk -F. '{print $(NF-1)"."$NF}') fails on multi-part TLDs (e.g.,
co.uk) and yields incorrect Zone IDs; change the logic that sets root_domain
(and uses domain) to determine the registrable domain using a public
suffix-aware method (either fetch and consult the public suffix list from
publicsuffix.org or use an existing tld/psl utility) or, if that is not
available, prompt the user to confirm/enter the correct root domain before
performing the Zone ID lookup; update references to root_domain accordingly so
downstream Zone ID lookup uses the confirmed/PSL-derived registrable domain.
- Around line 263-270: The load_existing_config function currently sources the
CONFIG_FILE which can execute arbitrary code; change it to a safe parser that
reads CONFIG_FILE line-by-line and extracts only simple KEY=VALUE pairs (skip
empty lines, comments, and any lines containing shell metacharacters or command
substitutions), trim surrounding quotes, and assign the values to the same
variables instead of using `. "$CONFIG_FILE"`; update load_existing_config to
validate file permissions (ensure 600) and return non-zero on parse errors, and
reference CONFIG_FILE and load_existing_config in your change so reviewers can
find the new safe parsing logic.
- Around line 806-809: The CF_API_TOKEN is being embedded in docker-compose.yml
as plaintext; modify the installer to write the token into a .env file (use
INSTALL_DIR and the CF_API_TOKEN variable), set restrictive permissions (chmod
600), and ensure this .env is created before docker-compose is invoked, then
update docker-compose.yml to stop hardcoding the token in the service
environment and instead reference the .env via env_file (or rely on Compose
variable substitution from .env) so CF_API_TOKEN is read from the .env; change
the install logic that writes docker-compose.yml to remove the literal
CF_API_TOKEN entry and add an env_file reference and the installer should create
the .env prior to starting the stack.
🧹 Nitpick comments (5)
install-streamnzb.sh (5)
15-15: Consider adding an error trap for graceful failure handling.With
set -e, the script exits on first error but performs no cleanup. If the script fails mid-execution (e.g., during firewall configuration), the system could be left in an inconsistent state. Consider adding a trap to handle cleanup or at least notify the user.set -e + +cleanup_on_error() { + print_error "Installation failed! Check the output above for details." + print_info "The system may be in a partial state. Review before re-running." +} +trap cleanup_on_error ERR
147-176: Declare and assign separately to avoid masking return values (SC2155).When using
local var=$(command), the exit status ofcommandis masked by the successfullocaldeclaration. Ifcurlfails, the script won't detect it properly.♻️ Proposed fix
verify_cloudflare_token() { local token="$1" - - local response=$(curl -s -X GET "https://api.cloudflare.com/client/v4/user/tokens/verify" \ + local response + response=$(curl -s -X GET "https://api.cloudflare.com/client/v4/user/tokens/verify" \ -H "Authorization: Bearer ${token}" \ -H "Content-Type: application/json")get_cloudflare_zone_id() { local token="$1" local domain="$2" # Extract root domain (last two parts) - local root_domain=$(echo "$domain" | awk -F. '{print $(NF-1)"."$NF}') - - local response=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones?name=${root_domain}" \ + local root_domain + root_domain=$(echo "$domain" | awk -F. '{print $(NF-1)"."$NF}') + + local response + response=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones?name=${root_domain}" \ -H "Authorization: Bearer ${token}" \ -H "Content-Type: application/json") - local zone_id=$(echo "$response" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4) + local zone_id + zone_id=$(echo "$response" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)
336-338: Useread -rto prevent backslash interpretation.Multiple
readcommands throughout the script (lines 338, 412, 456, 483, 526, 569, 614, 667) lack the-rflag. Without it, backslashes in user input are interpreted as escape characters, potentially causing unexpected behavior.- read CHOICE + read -r CHOICEApply this pattern to all
readstatements in the script.
940-943: Consider detecting the auth log path for broader compatibility.The hardcoded
/var/log/auth.logworks for Ubuntu/Debian with rsyslog, but some systems use journald exclusively. Modern Debian 12+ installations may not have this file by default.♻️ Optional: Add log path detection
+# Detect auth log path +if [ -f /var/log/auth.log ]; then + AUTH_LOG="/var/log/auth.log" +elif [ -f /var/log/secure ]; then + AUTH_LOG="/var/log/secure" +else + AUTH_LOG="%(sshd_log)s" +fi + cat > /etc/fail2ban/jail.local << EOF [DEFAULT] bantime = 3600 findtime = 600 maxretry = 5 [sshd] enabled = true port = ssh filter = sshd -logpath = /var/log/auth.log +logpath = ${AUTH_LOG} maxretry = 3 EOF
1054-1064: Container health check could be more robust.The pattern
docker compose ps | grep -q "running"is fragile as the output format varies across Docker Compose versions. Consider using the--filteror--formatoptions for more reliable detection.-if docker compose ps | grep -q "running"; then +if docker compose ps --format '{{.State}}' | grep -q "running"; thenOr use exit code of specific service health:
if docker compose ps streamnzb --format '{{.State}}' | grep -q "running"; then
| # Extract root domain (last two parts) | ||
| local root_domain=$(echo "$domain" | awk -F. '{print $(NF-1)"."$NF}') |
There was a problem hiding this comment.
Root domain extraction fails for multi-part TLDs (e.g., co.uk, com.au).
The extraction awk -F. '{print $(NF-1)"."$NF}' assumes single-part TLDs. For streamnzb.example.co.uk, this returns co.uk instead of example.co.uk, causing the Zone ID lookup to fail.
Consider using a public suffix list or prompting the user to confirm the root domain.
🧰 Tools
🪛 Shellcheck (0.11.0)
[warning] 167-167: Declare and assign separately to avoid masking return values.
(SC2155)
🤖 Prompt for AI Agents
In `@install-streamnzb.sh` around lines 166 - 167, The root domain extraction
using local root_domain=$(echo "$domain" | awk -F. '{print $(NF-1)"."$NF}')
fails on multi-part TLDs (e.g., co.uk) and yields incorrect Zone IDs; change the
logic that sets root_domain (and uses domain) to determine the registrable
domain using a public suffix-aware method (either fetch and consult the public
suffix list from publicsuffix.org or use an existing tld/psl utility) or, if
that is not available, prompt the user to confirm/enter the correct root domain
before performing the Zone ID lookup; update references to root_domain
accordingly so downstream Zone ID lookup uses the confirmed/PSL-derived
registrable domain.
| # Load existing configuration | ||
| load_existing_config() { | ||
| if [ -f "$CONFIG_FILE" ]; then | ||
| . "$CONFIG_FILE" | ||
| return 0 | ||
| fi | ||
| return 1 | ||
| } |
There was a problem hiding this comment.
Sourcing config file poses a code injection risk (SC1090).
Using . "$CONFIG_FILE" executes arbitrary shell code if the config file is tampered with. While the file has chmod 600, a safer approach is to parse key-value pairs explicitly rather than executing the file.
🛡️ Proposed safer config loading
load_existing_config() {
if [ -f "$CONFIG_FILE" ]; then
- . "$CONFIG_FILE"
+ # Parse config safely without executing arbitrary code
+ while IFS='=' read -r key value; do
+ # Skip comments and empty lines
+ [[ "$key" =~ ^#.*$ || -z "$key" ]] && continue
+ # Remove surrounding quotes from value
+ value="${value%\"}"
+ value="${value#\"}"
+ case "$key" in
+ DOMAIN) DOMAIN="$value" ;;
+ EMAIL) EMAIL="$value" ;;
+ TIMEZONE) TIMEZONE="$value" ;;
+ SSL_MODE) SSL_MODE="$value" ;;
+ CF_API_TOKEN) CF_API_TOKEN="$value" ;;
+ INSTALL_DATE) INSTALL_DATE="$value" ;;
+ esac
+ done < "$CONFIG_FILE"
return 0
fi
return 1
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| # Load existing configuration | |
| load_existing_config() { | |
| if [ -f "$CONFIG_FILE" ]; then | |
| . "$CONFIG_FILE" | |
| return 0 | |
| fi | |
| return 1 | |
| } | |
| # Load existing configuration | |
| load_existing_config() { | |
| if [ -f "$CONFIG_FILE" ]; then | |
| # Parse config safely without executing arbitrary code | |
| while IFS='=' read -r key value; do | |
| # Skip comments and empty lines | |
| [[ "$key" =~ ^#.*$ || -z "$key" ]] && continue | |
| # Remove surrounding quotes from value | |
| value="${value%\"}" | |
| value="${value#\"}" | |
| case "$key" in | |
| DOMAIN) DOMAIN="$value" ;; | |
| EMAIL) EMAIL="$value" ;; | |
| TIMEZONE) TIMEZONE="$value" ;; | |
| SSL_MODE) SSL_MODE="$value" ;; | |
| CF_API_TOKEN) CF_API_TOKEN="$value" ;; | |
| INSTALL_DATE) INSTALL_DATE="$value" ;; | |
| esac | |
| done < "$CONFIG_FILE" | |
| return 0 | |
| fi | |
| return 1 | |
| } |
🧰 Tools
🪛 Shellcheck (0.11.0)
[warning] 266-266: ShellCheck can't follow non-constant source. Use a directive to specify location.
(SC1090)
🤖 Prompt for AI Agents
In `@install-streamnzb.sh` around lines 263 - 270, The load_existing_config
function currently sources the CONFIG_FILE which can execute arbitrary code;
change it to a safe parser that reads CONFIG_FILE line-by-line and extracts only
simple KEY=VALUE pairs (skip empty lines, comments, and any lines containing
shell metacharacters or command substitutions), trim surrounding quotes, and
assign the values to the same variables instead of using `. "$CONFIG_FILE"`;
update load_existing_config to validate file permissions (ensure 600) and return
non-zero on parse errors, and reference CONFIG_FILE and load_existing_config in
your change so reviewers can find the new safe parsing logic.
synologyy
left a comment
There was a problem hiding this comment.
CF_API_TOKEN in .env with chmod 600
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@install-streamnzb.sh`:
- Around line 707-708: The script currently runs a full system upgrade via the
apt-get upgrade -y command (immediately after apt-get update), which can cause
unintended side effects; remove the unconditional apt-get upgrade -y or gate it
behind an explicit opt-in (e.g., check an environment variable/flag like
SKIP_SYSTEM_UPGRADE or --upgrade, or prompt the user interactively) and document
the behavior; locate the apt-get update / apt-get upgrade -y block in
install-streamnzb.sh and either delete the apt-get upgrade -y line or wrap it in
a conditional that only runs when the user has explicitly requested upgrades.
- Line 800: Remove the deprecated header line X-XSS-Protection "1; mode=block"
and replace it with a Content-Security-Policy header; locate the exact string
X-XSS-Protection "1; mode=block" in the script and delete it, then add a CSP
response header with restrictive directives (e.g., at minimum default-src 'self'
and a tight script-src/style-src policy avoiding 'unsafe-*' tokens) so browsers
enforce an explicit content policy for XSS mitigation.
🧹 Nitpick comments (4)
install-streamnzb.sh (4)
175-175: Fragile JSON parsing with grep; consider using jq.Parsing JSON with
grep -o '"id":"[^"]*"'is brittle and may fail on edge cases (e.g., whitespace variations, nested structures). The script already installsdnsutils; consider addingjqto dependencies for reliable JSON parsing.- local zone_id=$(echo "$response" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4) + local zone_id + zone_id=$(echo "$response" | jq -r '.result[0].id // empty' 2>/dev/null)And add
jqto the apt-get install line (around line 711).
360-360: Useread -rto prevent backslash interpretation.Throughout the script,
readis used without the-rflag. This causes backslashes in user input to be interpreted as escape characters. While unlikely to cause issues for typical domain/email input, usingread -ris the safer pattern.Affected lines include: 360, 434, 482, 509, 552, 595, 640, 693.
- read CHOICE + read -r CHOICE
1090-1090: Container status check may be fragile across Docker Compose versions.The pattern
docker compose ps | grep -q "running"depends on the text output format, which varies between Docker Compose versions. Consider using a more robust check.-if docker compose ps | grep -q "running"; then +if docker compose ps --status running 2>/dev/null | grep -q "streamnzb\|caddy"; thenAlternatively, check specific container health:
if docker inspect --format='{{.State.Running}}' streamnzb 2>/dev/null | grep -q "true"; then
388-388: Add explicit directory existence check before cd.If the install directory doesn't exist (e.g., partially deleted installation),
cd "$INSTALL_DIR"will fail with an unclear error message.+if [ ! -d "$INSTALL_DIR" ]; then + print_error "Installation directory not found: ${INSTALL_DIR}" + exit 1 +fi cd "$INSTALL_DIR"
synologyy
left a comment
There was a problem hiding this comment.
apt update upgrade changes and Content-Security-Policy
|
This is a good addition but im wondering if its necessary as people with VPS's normally have the knowhow on this stuff. If I were to merge this it would required README instructions, maybe a wget oneliner or something like that and it should be place unders /scripts or something like that. |
Add automated VPS installation script
Summary
This PR adds a comprehensive installation script (
install.sh) that automates the complete setup of StreamNZB on a VPS with SSL/TLS encryption.Features
SSL Options
Security
Convenience
/opt/streamnzb/INFO.txtUsage
# Download and run curl -O https://raw.githubusercontent.com/Gaisberg/streamnzb/main/install.sh chmod +x install.sh sudo bash install.shRequirements
What it installs
Screenshots / Example Output