diff --git a/services/email/deploy.sh b/services/email/deploy.sh index dcd0ca0..f4ae774 100755 --- a/services/email/deploy.sh +++ b/services/email/deploy.sh @@ -8,7 +8,7 @@ source "$SCRIPT_DIR/../../bin/lib/common.sh" ENV_FILE="${INFRA_DIR:-.}/.env" [ -f "$ENV_FILE" ] || { log_error "No .env found at $ENV_FILE"; exit 1; } -source "$ENV_FILE" +set -a; source "$ENV_FILE"; set +a require_env DOMAIN MAIL_HOST MAIL_USER diff --git a/services/frp/client/deploy.sh b/services/frp/client/deploy.sh index 4dd9f4b..55b747e 100755 --- a/services/frp/client/deploy.sh +++ b/services/frp/client/deploy.sh @@ -8,26 +8,44 @@ source "$SCRIPT_DIR/../../../bin/lib/common.sh" ENV_FILE="${INFRA_DIR:-.}/.env" [ -f "$ENV_FILE" ] || { log_error "No .env found at $ENV_FILE"; exit 1; } -source "$ENV_FILE" +set -a; source "$ENV_FILE"; set +a require_env FRP_SERVER_ADDR FRP_SERVER_PORT FRP_TOKEN -log_info "Downloading FRP..." -VERSION=$(curl -s https://api.github.com/repos/fatedier/frp/releases/latest \ - | python3 -c "import sys,json; print(json.load(sys.stdin)['tag_name'][1:])") -ARCHIVE="frp_${VERSION}_linux_amd64.tar.gz" -wget -q "https://github.com/fatedier/frp/releases/download/v${VERSION}/${ARCHIVE}" -tar -xf "$ARCHIVE" -mkdir -p /opt/frp -cp "frp_${VERSION}_linux_amd64/frpc" /opt/frp/ -chmod +x /opt/frp/frpc -rm -rf "$ARCHIVE" "frp_${VERSION}_linux_amd64" +find_template() { + local f="$1" + if [[ -n "${INFRA_DIR:-}" && -f "${INFRA_DIR}/$f" ]]; then + echo "${INFRA_DIR}/$f" + elif [[ -f "$SCRIPT_DIR/$f" ]]; then + echo "$SCRIPT_DIR/$f" + else + log_error "Template not found: $f" + return 1 + fi +} + +FRPC_BIN="/opt/frp/frpc" + +if [[ -x "$FRPC_BIN" ]]; then + log_info "frpc already at $FRPC_BIN, skipping download" +else + log_info "Downloading FRP..." + VERSION=$(curl -s https://api.github.com/repos/fatedier/frp/releases/latest \ + | python3 -c "import sys,json; print(json.load(sys.stdin)['tag_name'][1:])") + ARCHIVE="frp_${VERSION}_linux_amd64.tar.gz" + wget -q "https://github.com/fatedier/frp/releases/download/v${VERSION}/${ARCHIVE}" + tar -xf "$ARCHIVE" + mkdir -p /opt/frp + cp "frp_${VERSION}_linux_amd64/frpc" /opt/frp/ + chmod +x /opt/frp/frpc + rm -rf "$ARCHIVE" "frp_${VERSION}_linux_amd64" +fi log_info "Deploying config..." -envsubst < "${INFRA_DIR}/frpc.toml.example" > /opt/frp/frpc.toml +envsubst < "$(find_template frpc.toml.example)" > /opt/frp/frpc.toml log_info "Installing service..." -cp "${INFRA_DIR}/frpc.service" /etc/systemd/system/ +cp "$(find_template frpc.service)" /etc/systemd/system/ systemctl daemon-reload systemctl enable --now frpc diff --git a/services/frp/client/frpc.service b/services/frp/client/frpc.service new file mode 100644 index 0000000..5154d26 --- /dev/null +++ b/services/frp/client/frpc.service @@ -0,0 +1,15 @@ +[Unit] +Description=FRP Client +After=network.target + +[Service] +Type=simple +ExecStart=/opt/frp/frpc -c /opt/frp/frpc.toml +WorkingDirectory=/opt/frp +Restart=on-failure +RestartSec=5 +User=root +Group=root + +[Install] +WantedBy=multi-user.target diff --git a/services/frp/client/frpc.toml.example b/services/frp/client/frpc.toml.example new file mode 100644 index 0000000..1344512 --- /dev/null +++ b/services/frp/client/frpc.toml.example @@ -0,0 +1,21 @@ +serverAddr = "${FRP_SERVER_ADDR}" +serverPort = ${FRP_SERVER_PORT} + +auth.method = "token" +auth.token = "${FRP_TOKEN}" + +# Example: expose home SSH +[[proxies]] +name = "home-ssh" +type = "tcp" +localIP = "127.0.0.1" +localPort = 22 +remotePort = 1234 + +# Example: expose Minecraft +[[proxies]] +name = "minecraft" +type = "tcp" +localIP = "127.0.0.1" +localPort = 25565 +remotePort = 25565 diff --git a/services/frp/server/deploy.sh b/services/frp/server/deploy.sh index 377b9a9..dbe27b7 100755 --- a/services/frp/server/deploy.sh +++ b/services/frp/server/deploy.sh @@ -8,26 +8,44 @@ source "$SCRIPT_DIR/../../../bin/lib/common.sh" ENV_FILE="${INFRA_DIR:-.}/.env" [ -f "$ENV_FILE" ] || { log_error "No .env found at $ENV_FILE"; exit 1; } -source "$ENV_FILE" +set -a; source "$ENV_FILE"; set +a require_env FRP_TOKEN FRP_WEB_PASSWORD FRP_BIND_PORT -log_info "Downloading FRP..." -VERSION=$(curl -s https://api.github.com/repos/fatedier/frp/releases/latest \ - | python3 -c "import sys,json; print(json.load(sys.stdin)['tag_name'][1:])") -ARCHIVE="frp_${VERSION}_linux_amd64.tar.gz" -wget -q "https://github.com/fatedier/frp/releases/download/v${VERSION}/${ARCHIVE}" -tar -xf "$ARCHIVE" -mkdir -p /opt/frp -cp "frp_${VERSION}_linux_amd64/frps" /opt/frp/ -chmod +x /opt/frp/frps -rm -rf "$ARCHIVE" "frp_${VERSION}_linux_amd64" +find_template() { + local f="$1" + if [[ -n "${INFRA_DIR:-}" && -f "${INFRA_DIR}/$f" ]]; then + echo "${INFRA_DIR}/$f" + elif [[ -f "$SCRIPT_DIR/$f" ]]; then + echo "$SCRIPT_DIR/$f" + else + log_error "Template not found: $f" + return 1 + fi +} + +FRPS_BIN="/opt/frp/frps" + +if [[ -x "$FRPS_BIN" ]]; then + log_info "frps already at $FRPS_BIN, skipping download" +else + log_info "Downloading FRP..." + VERSION=$(curl -s https://api.github.com/repos/fatedier/frp/releases/latest \ + | python3 -c "import sys,json; print(json.load(sys.stdin)['tag_name'][1:])") + ARCHIVE="frp_${VERSION}_linux_amd64.tar.gz" + wget -q "https://github.com/fatedier/frp/releases/download/v${VERSION}/${ARCHIVE}" + tar -xf "$ARCHIVE" + mkdir -p /opt/frp + cp "frp_${VERSION}_linux_amd64/frps" /opt/frp/ + chmod +x /opt/frp/frps + rm -rf "$ARCHIVE" "frp_${VERSION}_linux_amd64" +fi log_info "Deploying config..." -envsubst < "${INFRA_DIR}/frps.toml.example" > /opt/frp/frps.toml +envsubst < "$(find_template frps.toml.example)" > /opt/frp/frps.toml log_info "Installing service..." -cp "${INFRA_DIR}/frps.service" /etc/systemd/system/ +cp "$(find_template frps.service)" /etc/systemd/system/ systemctl daemon-reload systemctl enable --now frps diff --git a/services/frp/server/frps.service b/services/frp/server/frps.service new file mode 100644 index 0000000..64a921d --- /dev/null +++ b/services/frp/server/frps.service @@ -0,0 +1,15 @@ +[Unit] +Description=FRP Server +After=network.target + +[Service] +Type=simple +ExecStart=/opt/frp/frps -c /opt/frp/frps.toml +WorkingDirectory=/opt/frp +Restart=on-failure +RestartSec=5 +User=root +Group=root + +[Install] +WantedBy=multi-user.target diff --git a/services/frp/server/frps.toml.example b/services/frp/server/frps.toml.example new file mode 100644 index 0000000..c9e9834 --- /dev/null +++ b/services/frp/server/frps.toml.example @@ -0,0 +1,17 @@ +bindAddr = "0.0.0.0" +bindPort = ${FRP_BIND_PORT} + +vhostHTTPPort = 8080 +vhostHTTPSPort = 8443 + +webServer.addr = "127.0.0.1" +webServer.port = 7500 +webServer.user = "root" +webServer.password = "${FRP_WEB_PASSWORD}" + +log.to = "./frps.log" +log.level = "info" +log.maxDays = 3 + +auth.method = "token" +auth.token = "${FRP_TOKEN}" diff --git a/services/nginx/deploy.sh b/services/nginx/deploy.sh index 5812945..f9c6d59 100755 --- a/services/nginx/deploy.sh +++ b/services/nginx/deploy.sh @@ -8,7 +8,7 @@ source "$SCRIPT_DIR/../../bin/lib/common.sh" ENV_FILE="${INFRA_DIR:-.}/.env" [ -f "$ENV_FILE" ] || { log_error "No .env found at $ENV_FILE"; exit 1; } -source "$ENV_FILE" +set -a; source "$ENV_FILE"; set +a require_env DOMAIN diff --git a/services/shadowsocks/client/config.json.example b/services/shadowsocks/client/config.json.example new file mode 100644 index 0000000..7d6b57b --- /dev/null +++ b/services/shadowsocks/client/config.json.example @@ -0,0 +1,11 @@ +{ + "server": "${SS_SERVER}", + "server_port": ${SS_PORT}, + "password": "${SS_PASSWORD}", + "method": "${SS_METHOD}", + "local_address": "127.0.0.1", + "local_port": 1080, + "timeout": 300, + "fast_open": true, + "mode": "tcp_and_udp" +} diff --git a/services/shadowsocks/client/deploy.sh b/services/shadowsocks/client/deploy.sh index aab663b..3a2d172 100755 --- a/services/shadowsocks/client/deploy.sh +++ b/services/shadowsocks/client/deploy.sh @@ -8,30 +8,61 @@ source "$SCRIPT_DIR/../../../bin/lib/common.sh" ENV_FILE="${INFRA_DIR:-.}/.env" [ -f "$ENV_FILE" ] || { log_error "No .env found at $ENV_FILE"; exit 1; } -source "$ENV_FILE" +set -a; source "$ENV_FILE"; set +a require_env SS_SERVER SS_PORT SS_PASSWORD SS_METHOD -log_info "Downloading shadowsocks-rust client..." -VERSION=$(curl -s https://api.github.com/repos/shadowsocks/shadowsocks-rust/releases/latest \ - | python3 -c "import sys,json; print(json.load(sys.stdin)['tag_name'])") -ARCHIVE="shadowsocks-${VERSION}.x86_64-unknown-linux-gnu.tar.xz" -wget -q "https://github.com/shadowsocks/shadowsocks-rust/releases/download/${VERSION}/${ARCHIVE}" -tar -xf "$ARCHIVE" -cp sslocal /usr/local/bin/sslocal -chmod +x /usr/local/bin/sslocal -rm -f "$ARCHIVE" ssserver sslocal ssurl ssmanager redir tunnel +# Find a template file: prefer INFRA_DIR override, fall back to bundled default. +find_template() { + local f="$1" + if [[ -n "${INFRA_DIR:-}" && -f "${INFRA_DIR}/$f" ]]; then + echo "${INFRA_DIR}/$f" + elif [[ -f "$SCRIPT_DIR/$f" ]]; then + echo "$SCRIPT_DIR/$f" + else + log_error "Template not found: $f" + return 1 + fi +} + +SSLOCAL_BIN="/usr/local/bin/sslocal" + +# Install sslocal — skip if already present, symlink if installed elsewhere. +if [[ -x "$SSLOCAL_BIN" ]]; then + log_info "sslocal already at $SSLOCAL_BIN ($($SSLOCAL_BIN --version 2>&1 | head -1)), skipping download" +elif command -v sslocal &>/dev/null; then + existing="$(command -v sslocal)" + log_info "sslocal found at $existing, symlinking to $SSLOCAL_BIN" + ln -sf "$existing" "$SSLOCAL_BIN" +else + log_info "Downloading shadowsocks-rust client..." + VERSION=$(curl -s https://api.github.com/repos/shadowsocks/shadowsocks-rust/releases/latest \ + | python3 -c "import sys,json; print(json.load(sys.stdin)['tag_name'])") + ARCHIVE="shadowsocks-${VERSION}.x86_64-unknown-linux-gnu.tar.xz" + wget -q "https://github.com/shadowsocks/shadowsocks-rust/releases/download/${VERSION}/${ARCHIVE}" + tar -xf "$ARCHIVE" + cp sslocal "$SSLOCAL_BIN" + chmod +x "$SSLOCAL_BIN" + rm -f "$ARCHIVE" ssserver sslocal ssurl ssmanager redir tunnel +fi log_info "Installing privoxy..." -apt-get install -y privoxy +if command -v pacman &>/dev/null; then + pacman -S --noconfirm --needed privoxy +else + apt-get install -y privoxy +fi log_info "Deploying configs..." mkdir -p /etc/shadowsocks-rust -envsubst < "${INFRA_DIR}/config.json.example" > /etc/shadowsocks-rust/client.json -cp "${INFRA_DIR}/privoxy.conf.example" /etc/privoxy/config +envsubst < "$(find_template config.json.example)" > /etc/shadowsocks-rust/client.json +cp "$(find_template privoxy.conf.example)" /etc/privoxy/config + +log_info "Stopping any existing shadowsocks service on port 1080..." +systemctl stop shadowsocks 2>/dev/null || true log_info "Installing service..." -cp "${INFRA_DIR}/shadowsocks-client.service" /etc/systemd/system/ +cp "$(find_template shadowsocks-client.service)" /etc/systemd/system/ systemctl daemon-reload systemctl enable --now shadowsocks-client systemctl enable --now privoxy diff --git a/services/shadowsocks/client/privoxy.conf.example b/services/shadowsocks/client/privoxy.conf.example new file mode 100644 index 0000000..271210c --- /dev/null +++ b/services/shadowsocks/client/privoxy.conf.example @@ -0,0 +1,5 @@ +# Privoxy config — bridges SOCKS5 (sslocal) to HTTP proxy +# Listens on :8118, forwards to sslocal SOCKS5 on :1080 + +listen-address 127.0.0.1:8118 +forward-socks5t / 127.0.0.1:1080 . diff --git a/services/shadowsocks/client/shadowsocks-client.service b/services/shadowsocks/client/shadowsocks-client.service new file mode 100644 index 0000000..4977c01 --- /dev/null +++ b/services/shadowsocks/client/shadowsocks-client.service @@ -0,0 +1,14 @@ +[Unit] +Description=Shadowsocks-Rust Client +Documentation=https://github.com/shadowsocks/shadowsocks-rust +After=network.target + +[Service] +Type=simple +ExecStart=/usr/local/bin/sslocal -c /etc/shadowsocks-rust/client.json +Restart=on-failure +RestartSec=5 +LimitNOFILE=51200 + +[Install] +WantedBy=multi-user.target diff --git a/services/shadowsocks/server/config.json.example b/services/shadowsocks/server/config.json.example new file mode 100644 index 0000000..e1c9468 --- /dev/null +++ b/services/shadowsocks/server/config.json.example @@ -0,0 +1,10 @@ +{ + "server": "0.0.0.0", + "server_port": ${SS_PORT}, + "password": "${SS_PASSWORD}", + "method": "${SS_METHOD}", + "timeout": 300, + "fast_open": true, + "no_delay": true, + "mode": "tcp_and_udp" +} diff --git a/services/shadowsocks/server/deploy.sh b/services/shadowsocks/server/deploy.sh index 88cf951..4f648a1 100755 --- a/services/shadowsocks/server/deploy.sh +++ b/services/shadowsocks/server/deploy.sh @@ -8,26 +8,48 @@ source "$SCRIPT_DIR/../../../bin/lib/common.sh" ENV_FILE="${INFRA_DIR:-.}/.env" [ -f "$ENV_FILE" ] || { log_error "No .env found at $ENV_FILE"; exit 1; } -source "$ENV_FILE" +set -a; source "$ENV_FILE"; set +a require_env SS_PORT SS_PASSWORD SS_METHOD -log_info "Downloading shadowsocks-rust..." -VERSION=$(curl -s https://api.github.com/repos/shadowsocks/shadowsocks-rust/releases/latest \ - | python3 -c "import sys,json; print(json.load(sys.stdin)['tag_name'])") -ARCHIVE="shadowsocks-${VERSION}.x86_64-unknown-linux-gnu.tar.xz" -wget -q "https://github.com/shadowsocks/shadowsocks-rust/releases/download/${VERSION}/${ARCHIVE}" -tar -xf "$ARCHIVE" -cp ssserver /usr/local/bin/ssserver-rust -chmod +x /usr/local/bin/ssserver-rust -rm -f "$ARCHIVE" ssserver sslocal ssurl ssmanager redir tunnel +find_template() { + local f="$1" + if [[ -n "${INFRA_DIR:-}" && -f "${INFRA_DIR}/$f" ]]; then + echo "${INFRA_DIR}/$f" + elif [[ -f "$SCRIPT_DIR/$f" ]]; then + echo "$SCRIPT_DIR/$f" + else + log_error "Template not found: $f" + return 1 + fi +} + +SSSERVER_BIN="/usr/local/bin/ssserver-rust" + +if [[ -x "$SSSERVER_BIN" ]]; then + log_info "ssserver-rust already at $SSSERVER_BIN ($($SSSERVER_BIN --version 2>&1 | head -1)), skipping download" +elif command -v ssserver &>/dev/null; then + existing="$(command -v ssserver)" + log_info "ssserver found at $existing, symlinking to $SSSERVER_BIN" + ln -sf "$existing" "$SSSERVER_BIN" +else + log_info "Downloading shadowsocks-rust..." + VERSION=$(curl -s https://api.github.com/repos/shadowsocks/shadowsocks-rust/releases/latest \ + | python3 -c "import sys,json; print(json.load(sys.stdin)['tag_name'])") + ARCHIVE="shadowsocks-${VERSION}.x86_64-unknown-linux-gnu.tar.xz" + wget -q "https://github.com/shadowsocks/shadowsocks-rust/releases/download/${VERSION}/${ARCHIVE}" + tar -xf "$ARCHIVE" + cp ssserver "$SSSERVER_BIN" + chmod +x "$SSSERVER_BIN" + rm -f "$ARCHIVE" ssserver sslocal ssurl ssmanager redir tunnel +fi log_info "Deploying config..." mkdir -p /etc/shadowsocks-rust -envsubst < "${INFRA_DIR}/config.json.example" > /etc/shadowsocks-rust/config.json +envsubst < "$(find_template config.json.example)" > /etc/shadowsocks-rust/config.json log_info "Installing service..." -cp "${INFRA_DIR}/shadowsocks-rust.service" /etc/systemd/system/ +cp "$(find_template shadowsocks-rust.service)" /etc/systemd/system/ systemctl daemon-reload systemctl enable --now shadowsocks-rust diff --git a/services/shadowsocks/server/shadowsocks-rust.service b/services/shadowsocks/server/shadowsocks-rust.service new file mode 100644 index 0000000..c1dec81 --- /dev/null +++ b/services/shadowsocks/server/shadowsocks-rust.service @@ -0,0 +1,14 @@ +[Unit] +Description=Shadowsocks-Rust Server +Documentation=https://github.com/shadowsocks/shadowsocks-rust +After=network.target + +[Service] +Type=simple +ExecStart=/usr/local/bin/ssserver-rust -c /etc/shadowsocks-rust/config.json +Restart=on-failure +RestartSec=5 +LimitNOFILE=51200 + +[Install] +WantedBy=multi-user.target