#!/usr/bin/env bash set -euo pipefail umask 077 BASE='https://share.saeedghofrani.xyz' MODE='ssh' PROXY_MODE=tun case "${1:-}" in --help|-h) printf 'Usage: bash setup.sh [--proxy-only]\nInstall your private SSH setup and/or sing-box connection on Arch Linux.\nRun as your normal user. Sudo is requested only for packages and the proxy service.\n'; exit 0 ;; --proxy-only) PROXY_MODE=mixed ;; '') ;; *) printf 'Unknown option: %s\n' "$1" >&2; exit 1 ;; esac [[ $EUID -ne 0 ]] || { echo 'Run this script as your normal user, without sudo.' >&2; exit 1; } [[ -f /etc/arch-release ]] || { echo 'This installer is for Arch Linux and its derivatives.' >&2; exit 1; } [[ "$BASE" == https://* ]] || { echo 'A trusted HTTPS address is required.' >&2; exit 1; } missing=() for pair in 'curl:curl' 'python3:python' 'ssh:openssh' 'tar:tar' 'sha256sum:coreutils'; do command -v "${pair%%:*}" >/dev/null || missing+=("${pair#*:}") done if ((${#missing[@]})); then command -v sudo >/dev/null || { echo 'Install sudo and the required packages first.' >&2; exit 1; } echo 'Installing missing prerequisites using pacman (including a consistent system update).' sudo pacman -Syu --needed "${missing[@]}" ca-certificates fi TASK_TMP=$(mktemp -d) trap 'unset SHARE_PASS LOGIN_JSON; rm -rf -- "$TASK_TMP"' EXIT printf '\nSaeed Share ยท %s setup\n' "$MODE" read -r -p 'Share username: ' SHARE_USER "$TASK_TMP/session.json"; then echo 'Sign-in failed. Check the username/password or try again later.' >&2; exit 1 fi unset LOGIN_JSON curl --proto '=https' --tlsv1.2 --fail --silent --show-error --connect-timeout 15 --max-time 45 -b "$TASK_TMP/cookies" "$BASE/api/kit" -o "$TASK_TMP/manifest.json" get_asset() { local asset="$1" destination="$2" expected actual expected=$(python3 - "$TASK_TMP/manifest.json" "$asset" <<'PY' import json,sys items=json.load(open(sys.argv[1]))['items'] matches=[i for i in items if i.get('kitKey')==sys.argv[2]] if len(matches)!=1: raise SystemExit('Setup file is not available: '+sys.argv[2]) print(matches[0]['sha256']) PY ) curl --proto '=https' --tlsv1.2 --fail --silent --show-error --connect-timeout 15 --max-time 600 --retry 2 -b "$TASK_TMP/cookies" "$BASE/api/kit/$asset" -o "$destination" actual=$(sha256sum "$destination" | cut -d ' ' -f1) [[ "$expected" == "$actual" ]] || { echo "Integrity check failed for $asset" >&2; exit 1; } chmod 600 "$destination" } backup_file() { if [[ -e "$1" ]]; then cp -p -- "$1" "$1.backup-$(date +%Y%m%d%H%M%S)-$$"; fi; } install_ssh() { get_asset ssh-key "$TASK_TMP/key" get_asset ssh-known-hosts "$TASK_TMP/known-hosts" get_asset ssh-profile "$TASK_TMP/ssh-profile.json" ssh-keygen -y -f "$TASK_TMP/key" "$TASK_TMP/client.pub" local expected actual expected=$(python3 -c 'import json,sys;print(json.load(open(sys.argv[1]))["clientFingerprint"])' "$TASK_TMP/ssh-profile.json") actual=$(ssh-keygen -lf "$TASK_TMP/client.pub" | awk '{print $2}') [[ "$expected" == "$actual" ]] || { echo 'The SSH key does not match its verified profile.' >&2; exit 1; } install -d -m 700 "$HOME/.ssh" "$HOME/.ssh/config.d" for name in saeed-vps-deploy saeed-vps-known-hosts config.d/saeed-vps.conf; do backup_file "$HOME/.ssh/$name"; done install -m 600 "$TASK_TMP/key" "$HOME/.ssh/saeed-vps-deploy" install -m 600 "$TASK_TMP/known-hosts" "$HOME/.ssh/saeed-vps-known-hosts" python3 - "$TASK_TMP/ssh-profile.json" "$HOME/.ssh/config.d/saeed-vps.conf" <<'PY' import json,sys p=json.load(open(sys.argv[1])) assert p['host']=='185.221.237.76' and p['port']==22 and p['user']=='root' open(sys.argv[2],'w').write('Host saeed-vps saeed-alt\n HostName '+p['host']+'\n User root\n Port 22\n IdentityFile ~/.ssh/saeed-vps-deploy\n IdentitiesOnly yes\n UserKnownHostsFile ~/.ssh/saeed-vps-known-hosts\n StrictHostKeyChecking yes\n ServerAliveInterval 30\n ServerAliveCountMax 3\n') PY chmod 600 "$HOME/.ssh/config.d/saeed-vps.conf" if [[ -L "$HOME/.ssh/config" ]]; then echo 'Your SSH config is a symlink. Add Include ~/.ssh/config.d/saeed-vps.conf at the top manually.' >&2 elif ! grep -Fqx 'Include ~/.ssh/config.d/saeed-vps.conf' "$HOME/.ssh/config" 2>/dev/null; then backup_file "$HOME/.ssh/config" { printf 'Include ~/.ssh/config.d/saeed-vps.conf\n'; [[ ! -f "$HOME/.ssh/config" ]] || cat "$HOME/.ssh/config"; } >"$TASK_TMP/ssh-config" install -m 600 "$TASK_TMP/ssh-config" "$HOME/.ssh/config" fi echo 'SSH installed. Connect with: ssh saeed-vps' if ssh -F "$HOME/.ssh/config.d/saeed-vps.conf" -o BatchMode=yes -o ConnectTimeout=12 saeed-vps true; then echo 'SSH connection verified.' else echo 'SSH is configured. The network connection could not be verified; proxy setup can still continue.' >&2; fi } install_proxy() { local arch asset binary before_active=false before_enabled=false backup_dir case "$(uname -m)" in x86_64) arch=amd64;; aarch64|arm64) arch=arm64;; *) echo 'Supported architectures: x86_64 and aarch64.' >&2; exit 1;; esac get_asset "singbox-$arch" "$TASK_TMP/sing-box.tar.gz" get_asset proxy-profile "$TASK_TMP/profile.json" mkdir "$TASK_TMP/core" # The pinned upstream archive hash is checked before extraction. tar -xzf "$TASK_TMP/sing-box.tar.gz" -C "$TASK_TMP/core" --no-same-owner binary=$(find "$TASK_TMP/core" -type f -name sing-box -print -quit) [[ -n "$binary" ]] || { echo 'The sing-box binary is missing.' >&2; exit 1; } chmod 700 "$binary" "$binary" version | head -n 1 if [[ "$PROXY_MODE" == tun && ! -c /dev/net/tun ]]; then echo 'TUN device is unavailable. Installing local proxy mode instead.' PROXY_MODE=mixed fi python3 - "$TASK_TMP/profile.json" "$TASK_TMP/config.json" "$PROXY_MODE" <<'PY' import json,sys p=json.load(open(sys.argv[1]));mode=sys.argv[3] assert p['type']=='vless' and p['server']=='185.221.237.76' p['tag']='proxy' c={'log':{'level':'warn','timestamp':True},'inbounds':[{'type':'mixed','tag':'local','listen':'127.0.0.1','listen_port':10808}],'outbounds':[p,{'type':'direct','tag':'direct'}],'route':{'auto_detect_interface':True,'final':'proxy'}} if mode=='tun': c['inbounds'].append({'type':'tun','tag':'tun','interface_name':'saeedtun0','address':['172.31.252.1/30'],'mtu':1400,'auto_route':True,'strict_route':True,'stack':'mixed'}) c['dns']={'servers':[{'type':'https','tag':'remote','server':'1.1.1.1','server_port':443,'path':'/dns-query','tls':{'enabled':True,'server_name':'cloudflare-dns.com'},'detour':'proxy'}],'final':'remote','strategy':'ipv4_only'} c['route']['rules']=[{'action':'sniff'},{'protocol':'dns','action':'hijack-dns'},{'ip_cidr':['185.221.237.76/32'],'action':'route','outbound':'direct'}] open(sys.argv[2],'w').write(json.dumps(c,indent=2)+'\n') PY "$binary" check -c "$TASK_TMP/config.json" command -v systemctl >/dev/null || { echo 'systemd is required for the automatic proxy service.' >&2; exit 1; } sudo -v systemctl is-active --quiet saeed-proxy.service && before_active=true systemctl is-enabled --quiet saeed-proxy.service 2>/dev/null && before_enabled=true backup_dir="/etc/saeed-proxy/backups/$(date +%Y%m%d%H%M%S)-$$" sudo install -d -m 700 /etc/saeed-proxy "$backup_dir" /usr/local/lib/saeed-proxy for path in /etc/saeed-proxy/config.json /usr/local/lib/saeed-proxy/sing-box /etc/systemd/system/saeed-proxy.service /usr/local/bin/saeed-proxy; do if sudo test -f "$path"; then sudo cp -p -- "$path" "$backup_dir/$(basename "$path")"; fi done sudo install -m 755 "$binary" /usr/local/lib/saeed-proxy/sing-box sudo install -m 600 "$TASK_TMP/config.json" /etc/saeed-proxy/config.json cat >"$TASK_TMP/saeed-proxy.service" <<'UNIT' [Unit] Description=Saeed private proxy connection Wants=network-online.target After=network-online.target [Service] Type=simple ExecStart=/usr/local/lib/saeed-proxy/sing-box run -c /etc/saeed-proxy/config.json Restart=on-failure RestartSec=3 LimitNOFILE=65536 NoNewPrivileges=true ProtectHome=true ProtectSystem=strict PrivateTmp=true CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE [Install] WantedBy=multi-user.target UNIT sudo install -m 644 "$TASK_TMP/saeed-proxy.service" /etc/systemd/system/saeed-proxy.service cat >"$TASK_TMP/saeed-proxy" <<'HELPER' #!/usr/bin/env bash set -euo pipefail case "${1:-status}" in on) sudo systemctl start saeed-proxy.service;; off) sudo systemctl stop saeed-proxy.service;; status) systemctl --no-pager status saeed-proxy.service;; logs) sudo journalctl -u saeed-proxy.service -n 50 --no-pager;; env) printf 'export HTTP_PROXY=http://127.0.0.1:10808\nexport HTTPS_PROXY=http://127.0.0.1:10808\nexport ALL_PROXY=socks5h://127.0.0.1:10808\n';; *) echo 'Usage: saeed-proxy {on|off|status|logs|env}' >&2;exit 1;; esac HELPER sudo install -m 755 "$TASK_TMP/saeed-proxy" /usr/local/bin/saeed-proxy sudo systemctl daemon-reload local verified=false started=false if sudo systemctl enable saeed-proxy.service && sudo systemctl restart saeed-proxy.service; then started=true; fi for attempt in 1 2 3; do if [[ "$started" == true ]] && curl --fail --silent --show-error --connect-timeout 10 --max-time 25 --proxy socks5h://127.0.0.1:10808 --noproxy '' https://example.com/ -o /dev/null; then verified=true;break;fi sleep 2 done if [[ "$verified" != true ]]; then echo 'Proxy test failed. Rolling back this dedicated service; your existing VPN settings were not changed.' >&2 sudo systemctl stop saeed-proxy.service if [[ "$before_enabled" != true ]]; then sudo systemctl disable saeed-proxy.service; fi for pair in 'config.json:/etc/saeed-proxy/config.json' 'sing-box:/usr/local/lib/saeed-proxy/sing-box' 'saeed-proxy.service:/etc/systemd/system/saeed-proxy.service' 'saeed-proxy:/usr/local/bin/saeed-proxy'; do if sudo test -f "$backup_dir/${pair%%:*}"; then sudo cp -p -- "$backup_dir/${pair%%:*}" "${pair#*:}"; fi done sudo systemctl daemon-reload if [[ "$before_active" == true ]]; then sudo systemctl start saeed-proxy.service; fi exit 1 fi echo 'Proxy connection verified. It will start automatically after reboot.' echo 'Control: saeed-proxy on | saeed-proxy off | saeed-proxy status' if [[ "$PROXY_MODE" == tun ]]; then echo 'TUN mode is active: system traffic uses your proxy; the VPS itself stays directly reachable.' else echo 'Local HTTP/SOCKS proxy: 127.0.0.1:10808';echo 'For this terminal run: eval "$(saeed-proxy env)"';fi } if [[ "$MODE" == ssh || "$MODE" == all ]]; then install_ssh; fi if [[ "$MODE" == proxy || "$MODE" == all ]]; then install_proxy; fi echo 'Setup complete. Your Share password was not saved.'