start-transparent.sh 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. #!/usr/bin/env bash
  2. set -euo pipefail
  3. usage() {
  4. cat <<'EOF'
  5. Usage: start-transparent.sh [-v|--verbose] [--enable-udp] [--capture-uid UID] [config_path]
  6. Options:
  7. -v, --verbose 启动后实时输出 mynetspeeder 日志
  8. --capture-uid UID 指定时只接管该 UID;不指定时接管所有用户流量
  9. --enable-udp 额外启用 UDP 透明接管(实验性,默认关闭)
  10. -h, --help 显示帮助
  11. EOF
  12. }
  13. VERBOSE=0
  14. ENABLE_UDP=0
  15. CONFIG_PATH="/home/mynetspeeder/config.json"
  16. CAPTURE_UID="${MYNETSPEEDER_CAPTURE_UID:-}"
  17. while [[ $# -gt 0 ]]; do
  18. case "$1" in
  19. -v|--verbose)
  20. VERBOSE=1; shift ;;
  21. --enable-udp)
  22. ENABLE_UDP=1; shift ;;
  23. --capture-uid)
  24. CAPTURE_UID="${2:-}"
  25. [[ -n "$CAPTURE_UID" ]] || { echo "missing value for --capture-uid"; exit 1; }
  26. shift 2 ;;
  27. -h|--help)
  28. usage; exit 0 ;;
  29. *)
  30. CONFIG_PATH="$1"; shift ;;
  31. esac
  32. done
  33. LISTEN_HOST="${MYNETSPEEDER_LISTEN_HOST:-127.0.0.1}"
  34. LISTEN_PORT="${MYNETSPEEDER_LISTEN_PORT:-19080}"
  35. RUNTIME_USER="${MYNETSPEEDER_USER:-mynetspeeder}"
  36. PID_FILE="/var/run/mynetspeeder-edge.pid"
  37. LOG_FILE="/var/log/mynetspeeder-edge.log"
  38. LOG_MAX_MB="${MYNETSPEEDER_LOG_MAX_MB:-50}"
  39. LOG_BACKUPS="${MYNETSPEEDER_LOG_BACKUPS:-3}"
  40. CHAIN4="MYNETSPEEDER"
  41. CHAIN6="MYNETSPEEDER6"
  42. SSH_PORTS="${MYNETSPEEDER_SSH_PORTS:-22}"
  43. if [[ $EUID -ne 0 ]]; then echo "need root"; exit 1; fi
  44. if [[ ! -f "$CONFIG_PATH" ]]; then echo "config not found: $CONFIG_PATH"; exit 1; fi
  45. if [[ -n "$CAPTURE_UID" ]] && ! [[ "$CAPTURE_UID" =~ ^[0-9]+$ ]]; then echo "capture uid must be numeric"; exit 1; fi
  46. IFS=',' read -r -a SSH_PORT_ARRAY <<< "$SSH_PORTS"
  47. for ssh_port in "${SSH_PORT_ARRAY[@]}"; do
  48. [[ "$ssh_port" =~ ^[0-9]+$ ]] || { echo "ssh ports must be numeric, got: $ssh_port"; exit 1; }
  49. done
  50. id -u "$RUNTIME_USER" >/dev/null 2>&1 || useradd --system --no-create-home --shell /usr/sbin/nologin "$RUNTIME_USER"
  51. mkdir -p /var/log
  52. touch "$LOG_FILE"
  53. chown "$RUNTIME_USER":"$RUNTIME_USER" "$LOG_FILE"
  54. if ! [[ "$LOG_MAX_MB" =~ ^[0-9]+$ ]] || ! [[ "$LOG_BACKUPS" =~ ^[0-9]+$ ]]; then echo "log limits must be numeric"; exit 1; fi
  55. LOG_MAX_BYTES=$((LOG_MAX_MB * 1024 * 1024))
  56. pkill -f 'python3 -m mynetspeeder edge' || true
  57. EDGE_UDP_FLAG=""
  58. if [[ "$ENABLE_UDP" == "1" ]]; then
  59. EDGE_UDP_FLAG="--enable-udp"
  60. fi
  61. runuser -u "$RUNTIME_USER" -- bash -lc "export PYTHONUNBUFFERED=1; export PYTHONPATH=/home; cd /home && exec nohup python3 -m mynetspeeder edge --listen-host ${LISTEN_HOST} --listen-port ${LISTEN_PORT} --config ${CONFIG_PATH} ${EDGE_UDP_FLAG} 2>&1 | python3 /home/mynetspeeder/scripts/rotate-log.py ${LOG_FILE} ${LOG_MAX_BYTES} ${LOG_BACKUPS}" &
  62. EDGE_PID=$!
  63. echo "$EDGE_PID" > "$PID_FILE"
  64. sleep 1
  65. ss -ln | grep -qE "[:.]${LISTEN_PORT}( |$)" || { echo "edge failed to listen"; tail -n 50 "$LOG_FILE" || true; exit 1; }
  66. iptables -t nat -N "$CHAIN4" 2>/dev/null || true
  67. iptables -t nat -F "$CHAIN4"
  68. iptables -t nat -A "$CHAIN4" -d 127.0.0.0/8 -j RETURN
  69. iptables -t nat -A "$CHAIN4" -m owner --uid-owner "$RUNTIME_USER" -j RETURN
  70. for ssh_port in "${SSH_PORT_ARRAY[@]}"; do
  71. iptables -t nat -A "$CHAIN4" -p tcp --sport "$ssh_port" -j RETURN
  72. done
  73. while read -r host; do
  74. [[ -n "$host" && "$host" != *:* ]] && iptables -t nat -A "$CHAIN4" -d "$host" -j RETURN
  75. done < <(python3 - <<'PY' "$CONFIG_PATH"
  76. import json, sys
  77. cfg = json.load(open(sys.argv[1]))
  78. for relay in cfg.get('relays', []):
  79. print(relay['host'])
  80. PY
  81. )
  82. if [[ -n "$CAPTURE_UID" ]]; then
  83. iptables -t nat -A "$CHAIN4" -p tcp -m owner --uid-owner "$CAPTURE_UID" -j REDIRECT --to-ports "$LISTEN_PORT"
  84. else
  85. iptables -t nat -A "$CHAIN4" -p tcp -j REDIRECT --to-ports "$LISTEN_PORT"
  86. fi
  87. iptables -t nat -C OUTPUT -p tcp -j "$CHAIN4" 2>/dev/null || iptables -t nat -A OUTPUT -p tcp -j "$CHAIN4"
  88. if [[ "$ENABLE_UDP" == "1" ]]; then
  89. if [[ -n "$CAPTURE_UID" ]]; then
  90. iptables -t nat -A "$CHAIN4" -p udp -m owner --uid-owner "$CAPTURE_UID" -j REDIRECT --to-ports "$LISTEN_PORT"
  91. else
  92. iptables -t nat -A "$CHAIN4" -p udp -j REDIRECT --to-ports "$LISTEN_PORT"
  93. fi
  94. iptables -t nat -C OUTPUT -p udp -j "$CHAIN4" 2>/dev/null || iptables -t nat -A OUTPUT -p udp -j "$CHAIN4"
  95. fi
  96. if command -v ip6tables >/dev/null 2>&1; then
  97. ip6tables -t nat -N "$CHAIN6" 2>/dev/null || true
  98. ip6tables -t nat -F "$CHAIN6"
  99. ip6tables -t nat -A "$CHAIN6" -d ::1/128 -j RETURN
  100. ip6tables -t nat -A "$CHAIN6" -m owner --uid-owner "$RUNTIME_USER" -j RETURN
  101. for ssh_port in "${SSH_PORT_ARRAY[@]}"; do
  102. ip6tables -t nat -A "$CHAIN6" -p tcp --sport "$ssh_port" -j RETURN
  103. done
  104. while read -r host; do
  105. [[ -n "$host" && "$host" == *:* ]] && ip6tables -t nat -A "$CHAIN6" -d "$host" -j RETURN
  106. done < <(python3 - <<'PY' "$CONFIG_PATH"
  107. import json, sys
  108. cfg = json.load(open(sys.argv[1]))
  109. for relay in cfg.get('relays', []):
  110. print(relay['host'])
  111. PY
  112. )
  113. if [[ -n "$CAPTURE_UID" ]]; then
  114. ip6tables -t nat -A "$CHAIN6" -p tcp -m owner --uid-owner "$CAPTURE_UID" -j REDIRECT --to-ports "$LISTEN_PORT"
  115. else
  116. ip6tables -t nat -A "$CHAIN6" -p tcp -j REDIRECT --to-ports "$LISTEN_PORT"
  117. fi
  118. ip6tables -t nat -C OUTPUT -p tcp -j "$CHAIN6" 2>/dev/null || ip6tables -t nat -A OUTPUT -p tcp -j "$CHAIN6"
  119. if [[ "$ENABLE_UDP" == "1" ]]; then
  120. if [[ -n "$CAPTURE_UID" ]]; then
  121. ip6tables -t nat -A "$CHAIN6" -p udp -m owner --uid-owner "$CAPTURE_UID" -j REDIRECT --to-ports "$LISTEN_PORT"
  122. else
  123. ip6tables -t nat -A "$CHAIN6" -p udp -j REDIRECT --to-ports "$LISTEN_PORT"
  124. fi
  125. ip6tables -t nat -C OUTPUT -p udp -j "$CHAIN6" 2>/dev/null || ip6tables -t nat -A OUTPUT -p udp -j "$CHAIN6"
  126. fi
  127. fi
  128. echo "mynetspeeder transparent mode started on ${LISTEN_HOST}:${LISTEN_PORT}"
  129. if [[ -n "$CAPTURE_UID" ]]; then
  130. echo "capture uid: $CAPTURE_UID"
  131. else
  132. echo "capture uid: all users"
  133. fi
  134. echo "udp capture: $ENABLE_UDP"
  135. echo "log file: $LOG_FILE"
  136. echo "log max: ${LOG_MAX_MB}MB x ${LOG_BACKUPS}"
  137. if [[ "$VERBOSE" == "1" ]]; then
  138. echo "verbose mode: press Ctrl+C to stop viewing logs, service keeps running"
  139. exec tail -n 80 -f "$LOG_FILE"
  140. fi