diff --git a/warp.sh b/warp.sh index ab24b9e..2c77009 100644 --- a/warp.sh +++ b/warp.sh @@ -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 </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 }