Fix: 3X-UI status shows SOCKS5+JSON; rewrite AmneziaWG backend per bandju reference

Made-with: Cursor
This commit is contained in:
anten-ka
2026-03-20 16:01:12 +03:00
parent 0a4b0c4620
commit 9c41c93507

240
warp.sh
View File

@@ -44,6 +44,8 @@ MODE=""
CONTAINER=""
AWG_VPN_CONF=""
AWG_VPN_IF=""
AWG_VPN_QUICK_CMD=""
AWG_CLIENTS_TABLE=""
AWG_START_SH=""
AWG_SUBNET=""
@@ -96,7 +98,8 @@ detect_mode() {
command -v docker &>/dev/null && has_docker=1
if [ "$has_docker" -eq 1 ]; then
local awg_ct
awg_ct=$(docker ps --format '{{.Names}}' 2>/dev/null | grep -i "amnezia" | head -1)
awg_ct=$(docker ps --format '{{.Names}}' 2>/dev/null | grep -E '^amnezia-awg2$|^amnezia-awg$' | head -1)
[ -z "$awg_ct" ] && awg_ct=$(docker ps --format '{{.Names}}' 2>/dev/null | grep -i "amnezia" | head -1)
[ -n "$awg_ct" ] && has_amnezia=1
fi
systemctl is-active x-ui &>/dev/null 2>&1 && has_3xui=1
@@ -367,10 +370,33 @@ show_status_3xui() {
local st; st=$(get_warp_status_3xui)
local sc="$RED"; [[ "$st" == "Подключён" ]] && sc="$GREEN"; [[ "$st" == "Отключён" ]] && sc="$YELLOW"
echo -e " ${WHITE}Статус: ${sc}${st}${NC}"
echo -e " ${WHITE}Порт SOCKS5: ${CYAN}127.0.0.1:${SOCKS_PORT}${NC}"
echo -e " ${WHITE}Реальный IP: ${GREEN}${MY_IP}${NC}"
is_warp_running_3xui && echo -e " ${WHITE}WARP IP: ${GREEN}$(get_warp_ip_3xui)${NC}"
echo -e "\n ${CYAN}── warp-cli status ──${NC}"
echo -e "\n${CYAN}── Настройки SOCKS5-прокси ──${NC}\n"
echo -e " ${WHITE}Адрес:${NC} ${GREEN}127.0.0.1${NC}"
echo -e " ${WHITE}Порт:${NC} ${GREEN}${SOCKS_PORT}${NC}"
echo -e " ${WHITE}Прокси:${NC} ${CYAN}socks5h://127.0.0.1:${SOCKS_PORT}${NC}"
echo -e "\n${CYAN}── JSON Outbound для 3X-UI (скопируйте в панель) ──${NC}\n"
echo -e "${GREEN}"
cat <<EOF
{
"tag": "warp",
"protocol": "socks",
"settings": {
"servers": [
{
"address": "127.0.0.1",
"port": ${SOCKS_PORT}
}
]
}
}
EOF
echo -e "${NC}"
echo -e "${CYAN}── warp-cli status ──${NC}"
warp-cli --accept-tos status 2>/dev/null | while IFS= read -r l; do echo -e " ${WHITE}$l${NC}"; done
echo ""; read -p "Enter..."
}
@@ -394,23 +420,31 @@ uninstall_3xui() {
awg_pick_container() {
if [ -n "${CONTAINER:-}" ]; then
docker inspect "$CONTAINER" &>/dev/null && return 0
docker exec "$CONTAINER" sh -c "true" 2>/dev/null && return 0
CONTAINER=""
fi
local -a containers=()
while IFS= read -r name; do
[ -n "$name" ] && containers+=("$name")
done < <(docker ps --format '{{.Names}}' 2>/dev/null | grep -i "amnezia")
mapfile -t containers < <(docker ps --format '{{.Names}}' | grep -E '^amnezia-awg2$|^amnezia-awg$' 2>/dev/null || true)
if [ ${#containers[@]} -eq 0 ]; then
echo -e "${RED}Не найден Docker-контейнер AmneziaWG.${NC}"
mapfile -t containers < <(docker ps --format '{{.Names}}' 2>/dev/null | grep -i "amnezia" || true)
fi
if [ ${#containers[@]} -eq 0 ]; then
echo -e "${RED}Контейнеры amnezia-awg / amnezia-awg2 не найдены.${NC}"
echo -e "${WHITE}Убедитесь, что AmneziaWG запущен через Docker.${NC}"
return 1
elif [ ${#containers[@]} -eq 1 ]; then
CONTAINER="${containers[0]}"
else
echo -e "\n${CYAN}Найдено несколько контейнеров:${NC}"
for i in "${!containers[@]}"; do echo -e " ${GREEN}$((i+1)))${NC} ${containers[$i]}"; done
echo -e "\n${CYAN}Доступные контейнеры:${NC}"
local i=1
for c in "${containers[@]}"; do echo -e " ${GREEN}$i)${NC} $c"; ((i++)); done
echo -e " ${DIM}0) Отмена${NC}"
while true; do
read -p "Выберите (1-${#containers[@]}): " ch
read -p "Выберите контейнер: " ch
[ "$ch" = "0" ] && return 1
[[ "$ch" =~ ^[0-9]+$ ]] && (( ch >= 1 && ch <= ${#containers[@]} )) && { CONTAINER="${containers[$((ch-1))]}"; break; }
done
fi
@@ -419,28 +453,34 @@ awg_pick_container() {
}
awg_load_container_data() {
local vpn_conf=""
for f in /opt/amnezia/awg/wg0.conf /etc/wireguard/wg0.conf /opt/amnezia/wg/wg0.conf; do
if docker exec "$CONTAINER" sh -c "[ -f '$f' ]" 2>/dev/null; then
vpn_conf="$f"; break
fi
done
[ -z "$vpn_conf" ] && { echo -e "${RED}Не найден VPN-конфиг в контейнере.${NC}"; return 1; }
AWG_VPN_CONF="$vpn_conf"
if [ "$CONTAINER" = "amnezia-awg2" ]; then
AWG_VPN_CONF="/opt/amnezia/awg/awg0.conf"
AWG_VPN_IF="awg0"
AWG_VPN_QUICK_CMD="awg-quick"
else
AWG_VPN_CONF="/opt/amnezia/awg/wg0.conf"
AWG_VPN_IF="wg0"
AWG_VPN_QUICK_CMD="wg-quick"
fi
for f in /opt/amnezia/awg/clientsTable /opt/amnezia/clientsTable /etc/wireguard/clientsTable; do
if docker exec "$CONTAINER" sh -c "[ -f '$f' ]" 2>/dev/null; then
AWG_CLIENTS_TABLE="$f"; break
fi
done
AWG_CLIENTS_TABLE="/opt/amnezia/awg/clientsTable"
AWG_START_SH="/opt/amnezia/start.sh"
for f in /opt/amnezia/start.sh /opt/amnezia/awg/start.sh; do
if docker exec "$CONTAINER" sh -c "[ -f '$f' ]" 2>/dev/null; then
AWG_START_SH="$f"; break
fi
done
docker exec "$CONTAINER" sh -c "[ -f '$AWG_VPN_CONF' ]" 2>/dev/null || {
for f in /opt/amnezia/awg/wg0.conf /opt/amnezia/awg/awg0.conf /etc/wireguard/wg0.conf; do
if docker exec "$CONTAINER" sh -c "[ -f '$f' ]" 2>/dev/null; then
AWG_VPN_CONF="$f"
break
fi
done
}
AWG_SUBNET=$(docker exec "$CONTAINER" sh -c "sed -n 's/^Address = \(.*\)$/\1/p' '$AWG_VPN_CONF' | head -1 | cut -d',' -f1" 2>/dev/null | tr -d '\r')
docker exec "$CONTAINER" sh -c "[ -f '$AWG_VPN_CONF' ]" 2>/dev/null || {
echo -e "${RED}Не найден конфиг VPN в контейнере: $AWG_VPN_CONF${NC}"
return 1
}
AWG_SUBNET=$(docker exec "$CONTAINER" sh -c "sed -n 's/^Address = \(.*\)$/\1/p' '$AWG_VPN_CONF' | head -n1 | cut -d',' -f1" 2>/dev/null | tr -d '\r')
return 0
}
@@ -510,12 +550,14 @@ awg_resolve_endpoint() {
awg_build_warp_conf() {
local endpoint_ip="$1"
local pk; pk=$(awk -F' = ' '/^PrivateKey = /{print $2}' "$WGCF_PROFILE")
local pub; pub=$(awk -F' = ' '/^PublicKey = /{print $2}' "$WGCF_PROFILE")
local addr; addr=$(awk -F' = ' '/^Address = /{print $2}' "$WGCF_PROFILE" | cut -d',' -f1)
local pk pub addr
pk=$(awk -F' = ' '/^PrivateKey = /{print $2}' "$WGCF_PROFILE")
pub=$(awk -F' = ' '/^PublicKey = /{print $2}' "$WGCF_PROFILE")
addr=$(awk -F' = ' '/^Address = /{print $2}' "$WGCF_PROFILE" | cut -d',' -f1)
docker exec "$CONTAINER" sh -c "mkdir -p '$AWG_WARP_DIR'"
docker exec "$CONTAINER" sh -c "cat > '$AWG_WARP_CONF' <<'EOF'
docker cp "$WGCF_PROFILE" "${CONTAINER}:${AWG_WARP_DIR}/wgcf-profile.conf" 2>/dev/null
docker exec "$CONTAINER" sh -c "cat > '$AWG_WARP_CONF' <<'WARPEOF'
[Interface]
PrivateKey = ${pk}
Address = ${addr}
@@ -527,7 +569,7 @@ PublicKey = ${pub}
AllowedIPs = 0.0.0.0/0
Endpoint = ${endpoint_ip}:2408
PersistentKeepalive = 25
EOF
WARPEOF
chmod 600 '$AWG_WARP_CONF'"
}
@@ -564,7 +606,6 @@ install_warp_awg() {
local ep; ep=$(awg_resolve_endpoint) || { read -p "Enter..."; return; }; echo -e "${GREEN}${ep}${NC}"
echo -e "${YELLOW}[6/7]${NC} Сборка warp.conf в контейнере..."
docker cp "$WGCF_PROFILE" "${CONTAINER}:${AWG_WARP_DIR}/wgcf-profile.conf" 2>/dev/null
awg_build_warp_conf "$ep"; echo -e "${GREEN}${NC}"
echo -e "${YELLOW}[7/7]${NC} Поднимаю warp-интерфейс..."
@@ -572,7 +613,7 @@ install_warp_awg() {
awg_detect_warp_exit_ip
[ -n "$AWG_WARP_EXIT_IP" ] && echo -e "\n ${WHITE}WARP IP: ${GREEN}${AWG_WARP_EXIT_IP}${NC}"
echo -e "\n${GREEN}WARP установлен! Добавьте клиентов через п.5.${NC}"
echo -e "\n${GREEN}WARP установлен! Добавьте клиентов через п.6.${NC}"
log_action "AWG INSTALL: endpoint=${ep}, warp_ip=${AWG_WARP_EXIT_IP}"
read -p "Enter..."
}
@@ -603,7 +644,6 @@ rekey_warp_awg() {
echo -e "${YELLOW}[4/5]${NC} Генерация профиля..."; awg_generate_profile || { read -p "Enter..."; return; }; echo -e "${GREEN}${NC}"
echo -e "${YELLOW}[5/5]${NC} Пересборка и запуск..."
local ep; ep=$(awg_resolve_endpoint) || { read -p "Enter..."; return; }
docker cp "$WGCF_PROFILE" "${CONTAINER}:${AWG_WARP_DIR}/wgcf-profile.conf" 2>/dev/null
awg_build_warp_conf "$ep"
awg_warp_up || { read -p "Enter..."; return; }
awg_apply_rules
@@ -707,33 +747,65 @@ ${content}EOF
awg_parse_clients_table() {
AWG_CLIENT_NAMES=()
[ -z "${AWG_CLIENTS_TABLE:-}" ] && return 0
local raw; raw=$(docker exec "$CONTAINER" sh -c "cat '$AWG_CLIENTS_TABLE' 2>/dev/null || true" | tr -d '\r')
local raw
raw=$(docker exec "$CONTAINER" sh -c "cat '$AWG_CLIENTS_TABLE' 2>/dev/null || true" | tr -d '\r')
[ -z "$raw" ] && return 0
declare -A key_to_name=()
local pairs; pairs=$(echo "$raw" | awk '
/"clientId"/ { s=$0; gsub(/.*"clientId"[[:space:]]*:[[:space:]]*"/,"",s); gsub(/".*/,"",s); cid=s }
/"clientName"/ { s=$0; gsub(/.*"clientName"[[:space:]]*:[[:space:]]*"/,"",s); gsub(/".*/,"",s); name=s;
if(cid!=""&&name!="") print cid"|"name }')
if [ -n "$pairs" ]; then
local id_name_pairs
id_name_pairs=$(echo "$raw" | awk '
/"clientId"/ {
s = $0
gsub(/.*"clientId"[[:space:]]*:[[:space:]]*"/, "", s)
gsub(/".*/, "", s)
cid = s
}
/"clientName"/ {
s = $0
gsub(/.*"clientName"[[:space:]]*:[[:space:]]*"/, "", s)
gsub(/".*/, "", s)
name = s
if (cid != "" && name != "") {
print cid "|" name
}
}')
if [ -n "$id_name_pairs" ]; then
while IFS='|' read -r cid name; do
[ -n "$cid" ] && [ -n "$name" ] && key_to_name["$cid"]="$name"
done <<< "$pairs"
done <<< "$id_name_pairs"
fi
local peers; peers=$(docker exec "$CONTAINER" sh -c "cat '$AWG_VPN_CONF' 2>/dev/null || true" | tr -d '\r' | awk '
/^\[Peer\]/ {pk="";ip=""}
/^PublicKey/ {s=$0; sub(/^[^=]*= */,"",s); pk=s}
/^AllowedIPs/ {s=$0; sub(/^[^=]*= */,"",s); ip=s; if(pk!=""&&ip!="") print pk"|"ip}')
if [ -n "$peers" ]; then
while IFS='|' read -r pk ip; do
if [ -n "$pk" ] && [ -n "$ip" ] && [ -n "${key_to_name[$pk]+_}" ]; then
AWG_CLIENT_NAMES["$ip"]="${key_to_name[$pk]}"
AWG_CLIENT_NAMES["${ip%/32}"]="${key_to_name[$pk]}"
local conf_peers
conf_peers=$(docker exec "$CONTAINER" sh -c "cat '$AWG_VPN_CONF' 2>/dev/null || true" | tr -d '\r' | awk '
/^\[Peer\]/ { pubkey=""; ip="" }
/^PublicKey/ {
s = $0
sub(/^[^=]*= */, "", s)
pubkey = s
}
/^AllowedIPs/ {
s = $0
sub(/^[^=]*= */, "", s)
ip = s
if (pubkey != "" && ip != "") {
print pubkey "|" ip
}
}')
if [ -n "$conf_peers" ]; then
while IFS='|' read -r pubkey ip; do
if [ -n "$pubkey" ] && [ -n "$ip" ] && [ -n "${key_to_name[$pubkey]+_}" ]; then
local name="${key_to_name[$pubkey]}"
AWG_CLIENT_NAMES["$ip"]="$name"
local bare="${ip%/32}"
AWG_CLIENT_NAMES["$bare"]="$name"
fi
done <<< "$peers"
done <<< "$conf_peers"
fi
return 0
}
awg_get_name() {
@@ -750,7 +822,10 @@ awg_format_label() {
awg_get_client_ips() {
AWG_CLIENT_IPS=()
mapfile -t AWG_CLIENT_IPS < <(docker exec "$CONTAINER" sh -c "sed -n 's/^AllowedIPs = \(.*\/32\)$/\1/p' '$AWG_VPN_CONF'" 2>/dev/null | tr -d '\r')
mapfile -t AWG_CLIENT_IPS < <(docker exec "$CONTAINER" sh -c "sed -n 's/^AllowedIPs[[:space:]]*=[[:space:]]*\(.*\/32\)[[:space:]]*$/\1/p' '$AWG_VPN_CONF'" 2>/dev/null | tr -d '\r')
if [ "${#AWG_CLIENT_IPS[@]}" -eq 0 ]; then
mapfile -t AWG_CLIENT_IPS < <(docker exec "$CONTAINER" sh -c "awk '/^\[Peer\]/,/^$/' '$AWG_VPN_CONF' | sed -n 's/^AllowedIPs[[:space:]]*=[[:space:]]*//p'" 2>/dev/null | tr -d '\r' | grep '/32')
fi
}
awg_add_clients_ssh() {
@@ -890,47 +965,52 @@ awg_patch_start_sh() {
[ -z "${AWG_START_SH:-}" ] && return
docker exec "$CONTAINER" sh -c "[ -f /opt/amnezia/start.sh.final-backup ] || cp '$AWG_START_SH' /opt/amnezia/start.sh.final-backup" 2>/dev/null
local block="${AWG_MARKER_BEGIN}
"
block+="if [ -f '${AWG_WARP_CONF}' ]; then wg-quick up '${AWG_WARP_CONF}' || true; fi
"
local warp_block=""
warp_block+="${AWG_MARKER_BEGIN}"$'\n'
warp_block+=""$'\n'
warp_block+="if [ -f '${AWG_WARP_CONF}' ]; then"$'\n'
warp_block+=" wg-quick up '${AWG_WARP_CONF}' || true"$'\n'
warp_block+="fi"$'\n'
warp_block+=""$'\n'
if [ ${#AWG_SELECTED_IPS[@]} -gt 0 ]; then
block+="ip route add default dev warp table 100 2>/dev/null || ip route replace default dev warp table 100 2>/dev/null || true
"
warp_block+="ip route add default dev warp table 100 2>/dev/null || ip route replace default dev warp table 100 2>/dev/null || true"$'\n'
warp_block+=""$'\n'
local prio=100
for ip in "${AWG_SELECTED_IPS[@]}"; do
block+="ip rule add from ${ip} table 100 priority ${prio} 2>/dev/null || true
"
block+="iptables -t nat -C POSTROUTING -s ${ip} -o warp -j MASQUERADE 2>/dev/null || iptables -t nat -I POSTROUTING 1 -s ${ip} -o warp -j MASQUERADE
"
warp_block+="ip rule add from ${ip} table 100 priority ${prio} 2>/dev/null || true"$'\n'
warp_block+="iptables -t nat -C POSTROUTING -s ${ip} -o warp -j MASQUERADE 2>/dev/null || iptables -t nat -I POSTROUTING 1 -s ${ip} -o warp -j MASQUERADE"$'\n'
((prio++))
done
fi
block+="${AWG_MARKER_END}"
warp_block+=""$'\n'
warp_block+="${AWG_MARKER_END}"
docker exec "$CONTAINER" sh -c "
if grep -qF '${AWG_MARKER_BEGIN}' '$AWG_START_SH' 2>/dev/null; then
sed -i '/${AWG_MARKER_BEGIN//\//\\/}/,/${AWG_MARKER_END//\//\\/}/d' '$AWG_START_SH'
if grep -qF '${AWG_MARKER_BEGIN}' '$AWG_START_SH'; then
sed -i '/# --- WARP-MANAGER BEGIN ---/,/# --- WARP-MANAGER END ---/d' '$AWG_START_SH'
fi
" 2>/dev/null
docker exec "$CONTAINER" sh -c "
if grep -qF 'tail -f /dev/null' '$AWG_START_SH'; then
tmpf=\$(mktemp)
tmpfile=\$(mktemp)
while IFS= read -r line; do
if echo \"\$line\" | grep -qF 'tail -f /dev/null'; then
cat <<'WBLOCK'
${block}
WBLOCK
cat <<'WARPBLOCK'
${warp_block}
WARPBLOCK
fi
echo \"\$line\"
done < '$AWG_START_SH' > \"\$tmpf\"
mv \"\$tmpf\" '$AWG_START_SH'; chmod +x '$AWG_START_SH'
done < '$AWG_START_SH' > \"\$tmpfile\"
mv \"\$tmpfile\" '$AWG_START_SH'
chmod +x '$AWG_START_SH'
else
cat >> '$AWG_START_SH' <<'WBLOCK'
cat >> '$AWG_START_SH' <<'WARPBLOCK'
${block}
WBLOCK
${warp_block}
WARPBLOCK
chmod +x '$AWG_START_SH'
fi
" 2>/dev/null
@@ -940,7 +1020,7 @@ awg_remove_from_start_sh() {
[ -z "${AWG_START_SH:-}" ] && return
docker exec "$CONTAINER" sh -c "
if grep -qF '${AWG_MARKER_BEGIN}' '$AWG_START_SH' 2>/dev/null; then
sed -i '/${AWG_MARKER_BEGIN//\//\\/}/,/${AWG_MARKER_END//\//\\/}/d' '$AWG_START_SH'
sed -i '/# --- WARP-MANAGER BEGIN ---/,/# --- WARP-MANAGER END ---/d' '$AWG_START_SH'
fi
" 2>/dev/null
}