ssrshell/shadowsocks-all-mihomo.sh
2026-05-22 20:50:26 +08:00

354 lines
8.4 KiB
Bash
Executable File

#!/usr/bin/env bash
set -Eeuo pipefail
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
red='\033[0;31m'
green='\033[0;32m'
yellow='\033[0;33m'
plain='\033[0m'
service_name='ssr-shell'
config_dir='/etc/shadowsocks-rust'
config_file="${config_dir}/server.json"
service_file="/etc/systemd/system/${service_name}.service"
method='2022-blake3-aes-256-gcm'
node_name='HKIXhuoshan-akcdn'
info() {
echo -e "[${green}Info${plain}] $*"
}
warn() {
echo -e "[${yellow}Warn${plain}] $*"
}
fail() {
echo -e "[${red}Error${plain}] $*" >&2
exit 1
}
require_root() {
[ "${EUID}" -eq 0 ] || fail "Please run as root."
}
command_exists() {
command -v "$1" >/dev/null 2>&1
}
detect_package_manager() {
if command_exists apt-get; then
echo apt
elif command_exists dnf; then
echo dnf
elif command_exists yum; then
echo yum
else
echo unknown
fi
}
install_base_dependencies() {
local pm
pm=$(detect_package_manager)
case "${pm}" in
apt)
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y ca-certificates curl tar xz-utils openssl iproute2
;;
dnf)
dnf install -y ca-certificates curl tar xz openssl iproute
;;
yum)
yum install -y ca-certificates curl tar xz openssl iproute
;;
*)
fail "Unsupported package manager. Please install curl, tar, xz, openssl manually."
;;
esac
}
try_install_from_package_manager() {
local pm
pm=$(detect_package_manager)
case "${pm}" in
apt)
export DEBIAN_FRONTEND=noninteractive
apt-get install -y shadowsocks-rust || true
;;
dnf)
dnf install -y shadowsocks-rust || true
;;
yum)
yum install -y shadowsocks-rust || true
;;
esac
}
detect_release_target() {
local arch
arch=$(uname -m)
case "${arch}" in
x86_64|amd64)
echo x86_64-unknown-linux-gnu
;;
aarch64|arm64)
echo aarch64-unknown-linux-gnu
;;
armv7l|armv7)
echo armv7-unknown-linux-gnueabihf
;;
*)
fail "Unsupported CPU architecture: ${arch}"
;;
esac
}
install_from_github_release() {
local target api url tmp archive ssserver_path ssservice_path
target=$(detect_release_target)
api='https://api.github.com/repos/shadowsocks/shadowsocks-rust/releases/latest'
tmp=$(mktemp -d)
archive="${tmp}/shadowsocks-rust.tar"
info "Downloading official shadowsocks-rust release for ${target}..."
url=$(curl -fsSL "${api}" \
| grep -E '"browser_download_url":' \
| grep "${target}" \
| grep -E 'tar\.(xz|gz)"' \
| head -n 1 \
| sed -E 's/.*"([^"]+)".*/\1/')
[ -n "${url}" ] || fail "Cannot find a shadowsocks-rust release asset for ${target}."
curl -fL "${url}" -o "${archive}"
tar -xf "${archive}" -C "${tmp}"
ssserver_path=$(find "${tmp}" -type f -name ssserver | head -n 1)
ssservice_path=$(find "${tmp}" -type f -name ssservice | head -n 1)
[ -n "${ssserver_path}" ] || fail "ssserver binary not found in release archive."
install -m 0755 "${ssserver_path}" /usr/local/bin/ssserver
if [ -n "${ssservice_path}" ]; then
install -m 0755 "${ssservice_path}" /usr/local/bin/ssservice
fi
rm -rf "${tmp}"
}
install_shadowsocks_rust() {
if command_exists ssserver; then
info "ssserver already installed: $(command -v ssserver)"
return 0
fi
try_install_from_package_manager
if command_exists ssserver; then
info "ssserver installed: $(command -v ssserver)"
return 0
fi
install_from_github_release
command_exists ssserver || fail "Failed to install ssserver."
}
is_valid_port() {
[[ "$1" =~ ^[0-9]+$ ]] && [ "$1" -ge 1 ] && [ "$1" -le 65535 ]
}
random_port() {
echo $((10000 + RANDOM % 50000))
}
public_ip() {
local ip
ip=$(curl -4fsS --max-time 3 https://api.ipify.org 2>/dev/null || true)
if [ -z "${ip}" ] && command_exists ip; then
ip=$(ip -4 addr show scope global | awk '/inet / { sub(/\/.*/, "", $2); print $2; exit }')
fi
echo "${ip}"
}
prompt_value() {
local prompt default value
prompt="$1"
default="$2"
read -r -p "${prompt} (Default: ${default}): " value
if [ -z "${value}" ]; then
value="${default}"
fi
echo "${value}"
}
prompt_port() {
local default_port value
default_port=$(random_port)
while true; do
value=$(prompt_value "Please enter Shadowsocks server port" "${default_port}")
if is_valid_port "${value}"; then
echo "${value}"
return 0
fi
warn "Invalid port: ${value}"
done
}
generate_key() {
if command_exists ssservice; then
if ssservice genkey -m "${method}" 2>/dev/null; then
return 0
fi
fi
openssl rand -base64 32
}
write_config() {
local port password
port="$1"
password="$2"
mkdir -p "${config_dir}"
chmod 0755 "${config_dir}"
cat > "${config_file}" <<EOF
{
"server": "0.0.0.0",
"server_port": ${port},
"password": "${password}",
"method": "${method}",
"mode": "tcp_and_udp",
"timeout": 300,
"fast_open": false,
"no_delay": true
}
EOF
chmod 0600 "${config_file}"
}
write_service() {
local ssserver_bin
ssserver_bin=$(command -v ssserver)
cat > "${service_file}" <<EOF
[Unit]
Description=ssr-shell Shadowsocks-rust server
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=${ssserver_bin} -c ${config_file}
Restart=on-failure
RestartSec=3
LimitNOFILE=1048576
[Install]
WantedBy=multi-user.target
EOF
}
open_firewall() {
local port
port="$1"
if command_exists firewall-cmd && systemctl is-active --quiet firewalld; then
firewall-cmd --permanent --add-port="${port}/tcp" >/dev/null 2>&1 || true
firewall-cmd --permanent --add-port="${port}/udp" >/dev/null 2>&1 || true
firewall-cmd --reload >/dev/null 2>&1 || true
info "firewalld opened ${port}/tcp and ${port}/udp."
elif command_exists ufw && ufw status 2>/dev/null | grep -qi active; then
ufw allow "${port}/tcp" >/dev/null 2>&1 || true
ufw allow "${port}/udp" >/dev/null 2>&1 || true
info "ufw opened ${port}/tcp and ${port}/udp."
else
warn "No active firewalld/ufw detected. Open ${port}/tcp and ${port}/udp manually if needed."
fi
}
start_service() {
systemctl daemon-reload
systemctl enable --now "${service_name}"
systemctl --no-pager --full status "${service_name}" || true
}
print_node() {
local server port password name
server="$1"
port="$2"
password="$3"
name="$4"
echo
echo "Your Mihomo ss node:"
printf ' - {name: %s, type: ss, server: %s, port: %s, cipher: "%s", password: "%s", udp: true}\n' "${name}" "${server}" "${port}" "${method}" "${password}"
}
install_action() {
local port password default_server client_server name
require_root
install_base_dependencies
install_shadowsocks_rust
port=$(prompt_port)
password=$(generate_key)
default_server=$(public_ip)
[ -n "${default_server}" ] || default_server='your.server.ip'
client_server=$(prompt_value "Please enter server IP/domain for Mihomo node" "${default_server}")
name=$(prompt_value "Please enter Mihomo node name" "${node_name}")
write_config "${port}" "${password}"
write_service
open_firewall "${port}"
start_service
echo
info "Config file: ${config_file}"
info "Service name: ${service_name}"
info "Cipher: ${method}"
info "Port: ${port}"
print_node "${client_server}" "${port}" "${password}" "${name}"
}
uninstall_action() {
require_root
systemctl disable --now "${service_name}" >/dev/null 2>&1 || true
rm -f "${service_file}"
rm -rf "${config_dir}"
systemctl daemon-reload
info "Uninstalled ${service_name}."
}
status_action() {
systemctl --no-pager --full status "${service_name}"
}
usage() {
echo "Usage: $(basename "$0") [install|uninstall|status]"
}
action="${1:-install}"
case "${action}" in
install)
install_action
;;
uninstall)
uninstall_action
;;
status)
status_action
;;
*)
usage
exit 1
;;
esac