From e1a04e065c77f847f6543344efd1b240c01ca90f Mon Sep 17 00:00:00 2001 From: SamNet-dev Date: Fri, 30 Jan 2026 16:56:34 -0600 Subject: [PATCH] Release v1.2: hardening, version bump, README update - Atomic settings.conf writes (write to tmp, then mv) - Secure temp dirs with mktemp (5 locations) - Add set -eo pipefail for pipe failure detection - Add timeout 10 to all docker stats calls - Update version from 1.2-Beta to 1.2 - Update URL from beta-releases to main - README updated for stable release --- README.md | 20 ++++++++---------- conduit.sh | 61 ++++++++++++++++++++++++++++-------------------------- 2 files changed, 41 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 4dfef4d..4d7f997 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ M A N A G E R ``` -![Version](https://img.shields.io/badge/version-1.2--Beta-blue) +![Version](https://img.shields.io/badge/version-1.2-blue) ![License](https://img.shields.io/badge/license-MIT-green) ![Platform](https://img.shields.io/badge/platform-Linux-orange) ![Docker](https://img.shields.io/badge/Docker-Required-2496ED?logo=docker&logoColor=white) @@ -18,24 +18,22 @@ A powerful management tool for deploying and managing Psiphon Conduit nodes on Linux servers. Help users access the open internet during network restrictions. -## Quick Install (Beta) +## Quick Install ```bash -curl -sL https://raw.githubusercontent.com/SamNet-dev/conduit-manager/beta-releases/conduit.sh | sudo bash +curl -sL https://raw.githubusercontent.com/SamNet-dev/conduit-manager/main/conduit.sh | sudo bash ``` Or download and run manually: ```bash -wget https://raw.githubusercontent.com/SamNet-dev/conduit-manager/beta-releases/conduit.sh +wget https://raw.githubusercontent.com/SamNet-dev/conduit-manager/main/conduit.sh sudo bash conduit.sh ``` -> For stable release, use `main` instead of `beta-releases` in the URL above. +> For beta/preview releases, use `beta-releases` instead of `main` in the URL above. -## v1.2-Beta Changelog - -> This list will grow as more features are added before the full v1.2 release. +## What's New in v1.2 **New Features** - Per-container CPU and memory resource limits via Settings menu @@ -72,6 +70,7 @@ sudo bash conduit.sh - PID management for background processes - Consistent `[STATS]` grep pattern across all screens - Temp dir cleanup to prevent stale data reads +- Atomic settings file writes to prevent corruption **Security** - Silent bot token input (not echoed) @@ -80,6 +79,7 @@ sudo bash conduit.sh - BotFather privacy guidance in setup wizard - OPSEC warning for operators in censored regions - Curl calls with `--max-filesize` and `--max-time` limits +- Secure temp directories with `mktemp` ## Features @@ -146,7 +146,7 @@ conduit uninstall # Remove all components ## Upgrading -Just run the install command above. When prompted, select **"Open management menu"** — existing containers are recognized automatically. Telegram settings are preserved across upgrades. +Just run the install command above or use `conduit update` from the menu. Existing containers are recognized automatically. Telegram settings are preserved across upgrades. ## Claim Rewards (OAT Tokens) @@ -172,8 +172,6 @@ MIT License Pull requests welcome. For major changes, open an issue first. -This is a **beta release** — please report any issues. - ## Links - [Psiphon](https://psiphon.ca/) diff --git a/conduit.sh b/conduit.sh index c5f5748..35aa3d2 100644 --- a/conduit.sh +++ b/conduit.sh @@ -1,7 +1,7 @@ #!/bin/bash # # ╔═══════════════════════════════════════════════════════════════════╗ -# ║ 🚀 PSIPHON CONDUIT MANAGER v1.2-Beta ║ +# ║ 🚀 PSIPHON CONDUIT MANAGER v1.2 ║ # ║ ║ # ║ One-click setup for Psiphon Conduit ║ # ║ ║ @@ -23,7 +23,7 @@ # -v, --verbose increase verbosity (-v for verbose, -vv for debug) # -set -e +set -eo pipefail # Require bash if [ -z "$BASH_VERSION" ]; then @@ -31,7 +31,7 @@ if [ -z "$BASH_VERSION" ]; then exit 1 fi -VERSION="1.2-Beta" +VERSION="1.2" CONDUIT_IMAGE="ghcr.io/ssmirr/conduit/conduit:latest" INSTALL_DIR="${INSTALL_DIR:-/opt/conduit}" BACKUP_DIR="$INSTALL_DIR/backups" @@ -701,7 +701,8 @@ save_settings_install() { _tg_label="${TELEGRAM_SERVER_LABEL:-}" _tg_start_hour="${TELEGRAM_START_HOUR:-0}" fi - cat > "$INSTALL_DIR/settings.conf" << EOF + local _tmp="$INSTALL_DIR/settings.conf.tmp.$$" + cat > "$_tmp" << EOF MAX_CLIENTS=$MAX_CLIENTS BANDWIDTH=$BANDWIDTH CONTAINER_COUNT=${CONTAINER_COUNT:-1} @@ -720,8 +721,8 @@ TELEGRAM_WEEKLY_SUMMARY=$_tg_weekly TELEGRAM_SERVER_LABEL="$_tg_label" TELEGRAM_START_HOUR=$_tg_start_hour EOF - - chmod 600 "$INSTALL_DIR/settings.conf" 2>/dev/null || true + chmod 600 "$_tmp" 2>/dev/null || true + mv "$_tmp" "$INSTALL_DIR/settings.conf" if [ ! -f "$INSTALL_DIR/settings.conf" ]; then log_error "Failed to save settings. Check disk space and permissions." @@ -841,7 +842,7 @@ create_management_script() { # Reference: https://github.com/ssmirr/conduit/releases/latest # -VERSION="1.2-Beta" +VERSION="1.2" INSTALL_DIR="REPLACE_ME_INSTALL_DIR" BACKUP_DIR="$INSTALL_DIR/backups" CONDUIT_IMAGE="ghcr.io/ssmirr/conduit/conduit:latest" @@ -1296,7 +1297,7 @@ get_container_stats() { for i in $(seq 1 $CONTAINER_COUNT); do names+=" $(get_container_name $i)" done - local all_stats=$(docker stats --no-stream --format "{{.CPUPerc}} {{.MemUsage}}" $names 2>/dev/null) + local all_stats=$(timeout 10 docker stats --no-stream --format "{{.CPUPerc}} {{.MemUsage}}" $names 2>/dev/null) if [ -z "$all_stats" ]; then echo "0% 0MiB" elif [ "$CONTAINER_COUNT" -le 1 ]; then @@ -1970,8 +1971,8 @@ show_advanced_stats() { # Fetch docker stats and all container logs in parallel local adv_running_names="" - local _adv_tmpdir="/tmp/.conduit_adv_$$" - rm -rf "$_adv_tmpdir"; mkdir -p "$_adv_tmpdir" + local _adv_tmpdir=$(mktemp -d /tmp/.conduit_adv.XXXXXX) + # mktemp already created the directory for ci in $(seq 1 $CONTAINER_COUNT); do local cname=$(get_container_name $ci) if echo "$docker_ps_cache" | grep -q "^${cname}$"; then @@ -1981,7 +1982,7 @@ show_advanced_stats() { done local adv_all_stats="" if [ -n "$adv_running_names" ]; then - ( docker stats --no-stream --format "{{.Name}}|{{.CPUPerc}}|{{.MemUsage}}" $adv_running_names > "$_adv_tmpdir/stats" 2>/dev/null ) & + ( timeout 10 docker stats --no-stream --format "{{.Name}}|{{.CPUPerc}}|{{.MemUsage}}" $adv_running_names > "$_adv_tmpdir/stats" 2>/dev/null ) & fi wait [ -f "$_adv_tmpdir/stats" ] && adv_all_stats=$(cat "$_adv_tmpdir/stats") @@ -2243,8 +2244,8 @@ show_peers() { # Get actual connected clients from docker logs (parallel) local total_clients=0 local docker_ps_cache=$(docker ps --format '{{.Names}}' 2>/dev/null) - local _peer_tmpdir="/tmp/.conduit_peer_$$" - rm -rf "$_peer_tmpdir"; mkdir -p "$_peer_tmpdir" + local _peer_tmpdir=$(mktemp -d /tmp/.conduit_peer.XXXXXX) + # mktemp already created the directory for ci in $(seq 1 $CONTAINER_COUNT); do local cname=$(get_container_name $ci) if echo "$docker_ps_cache" | grep -q "^${cname}$"; then @@ -2416,8 +2417,8 @@ show_status() { local uptime="" # Fetch all container logs in parallel - local _st_tmpdir="/tmp/.conduit_st_$$" - rm -rf "$_st_tmpdir"; mkdir -p "$_st_tmpdir" + local _st_tmpdir=$(mktemp -d /tmp/.conduit_st.XXXXXX) + # mktemp already created the directory for i in $(seq 1 $CONTAINER_COUNT); do local cname=$(get_container_name $i) _c_running[$i]=false @@ -2518,8 +2519,8 @@ show_status() { if [ "$running_count" -gt 0 ]; then # Run all 3 resource stat calls in parallel - local _rs_tmpdir="/tmp/.conduit_rs_$$" - rm -rf "$_rs_tmpdir"; mkdir -p "$_rs_tmpdir" + local _rs_tmpdir=$(mktemp -d /tmp/.conduit_rs.XXXXXX) + # mktemp already created the directory ( get_container_stats > "$_rs_tmpdir/cstats" ) & ( get_system_stats > "$_rs_tmpdir/sys" ) & ( get_net_speed > "$_rs_tmpdir/net" ) & @@ -3389,8 +3390,8 @@ manage_containers() { local docker_ps_cache=$(docker ps --format '{{.Names}}' 2>/dev/null) # Collect all docker data in parallel using a temp dir - local _mc_tmpdir="/tmp/.conduit_mc_$$" - rm -rf "$_mc_tmpdir"; mkdir -p "$_mc_tmpdir" + local _mc_tmpdir=$(mktemp -d /tmp/.conduit_mc.XXXXXX) + # mktemp already created the directory local running_names="" for ci in $(seq 1 $CONTAINER_COUNT); do @@ -3403,7 +3404,7 @@ manage_containers() { done # Fetch stats in parallel with logs if [ -n "$running_names" ]; then - ( docker stats --no-stream --format "{{.Name}} {{.CPUPerc}} {{.MemUsage}}" $running_names > "$_mc_tmpdir/stats" 2>/dev/null ) & + ( timeout 10 docker stats --no-stream --format "{{.Name}} {{.CPUPerc}} {{.MemUsage}}" $running_names > "$_mc_tmpdir/stats" 2>/dev/null ) & fi wait @@ -3984,7 +3985,8 @@ set_data_cap() { # Save all settings to file save_settings() { - cat > "$INSTALL_DIR/settings.conf" << EOF + local _tmp="$INSTALL_DIR/settings.conf.tmp.$$" + cat > "$_tmp" << EOF MAX_CLIENTS=$MAX_CLIENTS BANDWIDTH=$BANDWIDTH CONTAINER_COUNT=$CONTAINER_COUNT @@ -4011,12 +4013,13 @@ EOF local bw_var="BANDWIDTH_${i}" local cpu_var="CPUS_${i}" local mem_var="MEMORY_${i}" - [ -n "${!mc_var}" ] && echo "${mc_var}=${!mc_var}" >> "$INSTALL_DIR/settings.conf" - [ -n "${!bw_var}" ] && echo "${bw_var}=${!bw_var}" >> "$INSTALL_DIR/settings.conf" - [ -n "${!cpu_var}" ] && echo "${cpu_var}=${!cpu_var}" >> "$INSTALL_DIR/settings.conf" - [ -n "${!mem_var}" ] && echo "${mem_var}=${!mem_var}" >> "$INSTALL_DIR/settings.conf" + [ -n "${!mc_var}" ] && echo "${mc_var}=${!mc_var}" >> "$_tmp" + [ -n "${!bw_var}" ] && echo "${bw_var}=${!bw_var}" >> "$_tmp" + [ -n "${!cpu_var}" ] && echo "${cpu_var}=${!cpu_var}" >> "$_tmp" + [ -n "${!mem_var}" ] && echo "${mem_var}=${!mem_var}" >> "$_tmp" done - chmod 600 "$INSTALL_DIR/settings.conf" 2>/dev/null || true + chmod 600 "$_tmp" 2>/dev/null || true + mv "$_tmp" "$INSTALL_DIR/settings.conf" } # ─── Telegram Bot Functions ─────────────────────────────────────────────────── @@ -4827,7 +4830,7 @@ build_report() { fi # CPU / RAM - local stats=$(docker stats --no-stream --format "{{.CPUPerc}} {{.MemUsage}}" $(docker ps --format '{{.Names}}' 2>/dev/null | grep "^conduit") 2>/dev/null | head -1) + local stats=$(timeout 10 docker stats --no-stream --format "{{.CPUPerc}} {{.MemUsage}}" $(docker ps --format '{{.Names}}' 2>/dev/null | grep "^conduit") 2>/dev/null | head -1) local raw_cpu=$(echo "$stats" | awk '{print $1}') local cores=$(get_cpu_cores) local cpu=$(awk "BEGIN {printf \"%.1f%%\", ${raw_cpu%\%} / $cores}" 2>/dev/null || echo "$raw_cpu") @@ -6267,7 +6270,7 @@ update_conduit() { # --- Phase 1: Script update --- echo "Checking for script updates..." - local update_url="https://raw.githubusercontent.com/SamNet-dev/conduit-manager/beta-releases/conduit.sh" + local update_url="https://raw.githubusercontent.com/SamNet-dev/conduit-manager/main/conduit.sh" local tmp_script="/tmp/conduit_update_$$.sh" if curl -sL --max-time 30 --max-filesize 2097152 -o "$tmp_script" "$update_url" 2>/dev/null; then @@ -6645,7 +6648,7 @@ main() { fi } # -# REACHED END OF SCRIPT - VERSION 1.2-Beta +# REACHED END OF SCRIPT - VERSION 1.2 # ############################################################################### main "$@"