fix: detect macos client port conflicts
This commit is contained in:
12
README.md
12
README.md
@@ -10,17 +10,23 @@
|
|||||||
curl -fsSL https://git.dokops.ru/dokril/vpn-proxy/raw/branch/master/scripts/install-macos-client.sh | bash
|
curl -fsSL https://git.dokops.ru/dokril/vpn-proxy/raw/branch/master/scripts/install-macos-client.sh | bash
|
||||||
```
|
```
|
||||||
|
|
||||||
После запуска:
|
После запуска по умолчанию:
|
||||||
|
|
||||||
- UI: `http://127.0.0.1:3456`
|
- UI: `http://127.0.0.1:3456`
|
||||||
- HTTP/SOCKS proxy: `127.0.0.1:8080` по умолчанию; в UI можно выбрать порт из Docker-диапазона `8080–8090`
|
- HTTP/SOCKS proxy: `127.0.0.1:8080` по умолчанию; в UI можно выбрать порт из Docker-диапазона `8080–8090`
|
||||||
|
|
||||||
Установщик интерактивно спросит proxy-порт. Для неинтерактивного запуска можно задать его заранее; тогда вопрос не появится:
|
Установщик интерактивно спросит proxy-порт. Если стандартный UI-порт `3456` занят другим контейнером, установщик попросит выбрать свободный UI-порт. Для неинтерактивного запуска можно задать порты заранее; тогда вопросы не появятся:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -fsSL https://git.dokops.ru/dokril/vpn-proxy/raw/branch/master/scripts/install-macos-client.sh | VPN_PROXY_CLIENT_PORT=18080 bash
|
curl -fsSL https://git.dokops.ru/dokril/vpn-proxy/raw/branch/master/scripts/install-macos-client.sh | VPN_PROXY_CLIENT_PORT=18080 bash
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Если старый gateway/client уже занимает `3456` или выбранный proxy-порт, можно не трогать старый контейнер и поставить новый клиент на другие порты:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -fsSL https://git.dokops.ru/dokril/vpn-proxy/raw/branch/master/scripts/install-macos-client.sh | VPN_PROXY_CLIENT_UI_PORT=3457 VPN_PROXY_CLIENT_PORT=18080 bash
|
||||||
|
```
|
||||||
|
|
||||||
После запуска скрипт проверяет, что UI реально ответил на `/api/state`. Если контейнер сразу упал или порт занят, он покажет `docker compose ps` и последние логи вместо ложного сообщения о готовности.
|
После запуска скрипт проверяет, что UI реально ответил на `/api/state`. Если контейнер сразу упал или порт занят, он покажет `docker compose ps` и последние логи вместо ложного сообщения о готовности.
|
||||||
|
|
||||||
В Mac UI есть **Домашний режим**. Когда он включён, приложения по-прежнему используют выбранный локальный proxy-порт, но весь proxy-трафик идёт напрямую без VPN.
|
В Mac UI есть **Домашний режим**. Когда он включён, приложения по-прежнему используют выбранный локальный proxy-порт, но весь proxy-трафик идёт напрямую без VPN.
|
||||||
@@ -351,6 +357,8 @@ UI доступен на `http://<gateway-ip>:3456`.
|
|||||||
| ------------------- | -------------------- | -------------------------------------- |
|
| ------------------- | -------------------- | -------------------------------------- |
|
||||||
| `APP_MODE` | `gateway` | `gateway` или `client`; compose клиента задаёт `client` автоматически |
|
| `APP_MODE` | `gateway` | `gateway` или `client`; compose клиента задаёт `client` автоматически |
|
||||||
| `CLIENT_UI_PORT` | `3456` | Host-порт UI для `docker-compose.client.yml` |
|
| `CLIENT_UI_PORT` | `3456` | Host-порт UI для `docker-compose.client.yml` |
|
||||||
|
| `VPN_PROXY_CLIENT_UI_PORT` | unset | UI-порт для macOS installer; записывается в `CLIENT_UI_PORT` |
|
||||||
|
| `VPN_PROXY_CLIENT_PORT` | unset | Proxy-порт для macOS installer; записывает `CLIENT_PROXY_PORT_START/END` |
|
||||||
| `CLIENT_PROXY_PORT_START` | `8080` | Первый host/container proxy-порт для `docker-compose.client.yml` |
|
| `CLIENT_PROXY_PORT_START` | `8080` | Первый host/container proxy-порт для `docker-compose.client.yml` |
|
||||||
| `CLIENT_PROXY_PORT_END` | `8090` | Последний host/container proxy-порт для `docker-compose.client.yml` |
|
| `CLIENT_PROXY_PORT_END` | `8090` | Последний host/container proxy-порт для `docker-compose.client.yml` |
|
||||||
| `PORT` | `3456` | Порт веб-интерфейса |
|
| `PORT` | `3456` | Порт веб-интерфейса |
|
||||||
|
|||||||
@@ -19,6 +19,14 @@ services:
|
|||||||
ROUTING_RU_DIRECT: ${ROUTING_RU_DIRECT:-true}
|
ROUTING_RU_DIRECT: ${ROUTING_RU_DIRECT:-true}
|
||||||
RULE_SET_DOWNLOAD_DETOUR: ${RULE_SET_DOWNLOAD_DETOUR:-vpn}
|
RULE_SET_DOWNLOAD_DETOUR: ${RULE_SET_DOWNLOAD_DETOUR:-vpn}
|
||||||
LOG_LEVEL: ${LOG_LEVEL:-info}
|
LOG_LEVEL: ${LOG_LEVEL:-info}
|
||||||
|
HTTP_PROXY: ""
|
||||||
|
HTTPS_PROXY: ""
|
||||||
|
ALL_PROXY: ""
|
||||||
|
http_proxy: ""
|
||||||
|
https_proxy: ""
|
||||||
|
all_proxy: ""
|
||||||
|
NO_PROXY: "localhost,127.0.0.1,host.docker.internal"
|
||||||
|
no_proxy: "localhost,127.0.0.1,host.docker.internal"
|
||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:${CLIENT_UI_PORT:-3456}:${PORT:-3456}"
|
- "127.0.0.1:${CLIENT_UI_PORT:-3456}:${PORT:-3456}"
|
||||||
- "127.0.0.1:${CLIENT_PROXY_PORT_START:-8080}-${CLIENT_PROXY_PORT_END:-8090}:${CLIENT_PROXY_PORT_START:-8080}-${CLIENT_PROXY_PORT_END:-8090}"
|
- "127.0.0.1:${CLIENT_PROXY_PORT_START:-8080}-${CLIENT_PROXY_PORT_END:-8090}:${CLIENT_PROXY_PORT_START:-8080}-${CLIENT_PROXY_PORT_END:-8090}"
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ BRANCH="${VPN_PROXY_BRANCH:-master}"
|
|||||||
COMPOSE_FILE="docker-compose.client.yml"
|
COMPOSE_FILE="docker-compose.client.yml"
|
||||||
DEFAULT_PROXY_PORT="8080"
|
DEFAULT_PROXY_PORT="8080"
|
||||||
REQUESTED_PROXY_PORT="${VPN_PROXY_CLIENT_PORT:-}"
|
REQUESTED_PROXY_PORT="${VPN_PROXY_CLIENT_PORT:-}"
|
||||||
|
REQUESTED_UI_PORT="${VPN_PROXY_CLIENT_UI_PORT:-${CLIENT_UI_PORT:-}}"
|
||||||
|
CLIENT_CONTAINER_NAME="vpn-proxy-client"
|
||||||
|
|
||||||
log() {
|
log() {
|
||||||
printf '[vpn-proxy-client] %s\n' "$*"
|
printf '[vpn-proxy-client] %s\n' "$*"
|
||||||
@@ -57,6 +59,122 @@ ask_proxy_port() {
|
|||||||
printf '%s\n' "$DEFAULT_PROXY_PORT"
|
printf '%s\n' "$DEFAULT_PROXY_PORT"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
port_range_end() {
|
||||||
|
local start="$1"
|
||||||
|
local end="$((start + 10))"
|
||||||
|
if [ "$end" -gt 65535 ]; then
|
||||||
|
end=65535
|
||||||
|
fi
|
||||||
|
printf '%s\n' "$end"
|
||||||
|
}
|
||||||
|
|
||||||
|
published_port_conflicts() {
|
||||||
|
local port="$1"
|
||||||
|
local line
|
||||||
|
|
||||||
|
while IFS= read -r line; do
|
||||||
|
[ -n "$line" ] || continue
|
||||||
|
case "$line" in
|
||||||
|
"${CLIENT_CONTAINER_NAME}"$'\t'*) ;;
|
||||||
|
*) printf '%s\n' "$line" ;;
|
||||||
|
esac
|
||||||
|
done < <(docker ps --filter "publish=${port}" --format '{{.Names}} {{.Ports}}')
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy_port_conflicts() {
|
||||||
|
local start="$1"
|
||||||
|
local end
|
||||||
|
local port
|
||||||
|
local conflicts
|
||||||
|
|
||||||
|
end="$(port_range_end "$start")"
|
||||||
|
for port in $(seq "$start" "$end"); do
|
||||||
|
conflicts="$(published_port_conflicts "$port")"
|
||||||
|
if [ -n "$conflicts" ]; then
|
||||||
|
printf 'port %s: %s\n' "$port" "$conflicts"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_proxy_port_available() {
|
||||||
|
local port="$1"
|
||||||
|
local conflicts
|
||||||
|
|
||||||
|
conflicts="$(proxy_port_conflicts "$port")"
|
||||||
|
if [ -z "$conflicts" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf '[vpn-proxy-client] proxy port range %s-%s is already used:\n%s\n' \
|
||||||
|
"$port" "$(port_range_end "$port")" "$conflicts" >&2
|
||||||
|
die "choose another proxy port with VPN_PROXY_CLIENT_PORT=<port> or stop the conflicting container"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_single_port_available() {
|
||||||
|
local label="$1"
|
||||||
|
local port="$2"
|
||||||
|
local conflicts
|
||||||
|
|
||||||
|
conflicts="$(published_port_conflicts "$port")"
|
||||||
|
if [ -z "$conflicts" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf '[vpn-proxy-client] %s port %s is already used:\n%s\n' \
|
||||||
|
"$label" "$port" "$conflicts" >&2
|
||||||
|
die "choose another ${label} port or stop the conflicting container"
|
||||||
|
}
|
||||||
|
|
||||||
|
first_free_port() {
|
||||||
|
local start="$1"
|
||||||
|
local port
|
||||||
|
|
||||||
|
for port in $(seq "$start" 65535); do
|
||||||
|
if [ -z "$(published_port_conflicts "$port")" ]; then
|
||||||
|
printf '%s\n' "$port"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
choose_ui_port() {
|
||||||
|
local value="$1"
|
||||||
|
local suggested
|
||||||
|
|
||||||
|
if ! is_valid_port "$value"; then
|
||||||
|
die "CLIENT_UI_PORT must be a port from 1024 to 65535"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$(published_port_conflicts "$value")" ]; then
|
||||||
|
printf '%s\n' "$value"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$REQUESTED_UI_PORT" ] || [ ! -r /dev/tty ]; then
|
||||||
|
assert_single_port_available "UI" "$value"
|
||||||
|
fi
|
||||||
|
|
||||||
|
suggested="$(first_free_port "$((value + 1))" || true)"
|
||||||
|
suggested="${suggested:-3457}"
|
||||||
|
while true; do
|
||||||
|
printf 'UI port %s is busy. Choose UI port [%s]: ' "$value" "$suggested" >/dev/tty
|
||||||
|
IFS= read -r value </dev/tty || value=""
|
||||||
|
value="${value:-$suggested}"
|
||||||
|
if is_valid_port "$value" && [ -z "$(published_port_conflicts "$value")" ]; then
|
||||||
|
printf '%s\n' "$value"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
printf 'Enter a free port from 1024 to 65535.\n' >/dev/tty
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_ui_outside_proxy_range() {
|
||||||
|
if [ "$UI_PORT" -ge "$PROXY_PORT" ] && [ "$UI_PORT" -le "$PROXY_PORT_END" ]; then
|
||||||
|
die "UI port ${UI_PORT} overlaps proxy port range ${PROXY_PORT}-${PROXY_PORT_END}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
wait_for_client_ui() {
|
wait_for_client_ui() {
|
||||||
local ui_port="${UI_PORT:-3456}"
|
local ui_port="${UI_PORT:-3456}"
|
||||||
local ui_url="http://127.0.0.1:${ui_port}/api/state"
|
local ui_url="http://127.0.0.1:${ui_port}/api/state"
|
||||||
@@ -135,18 +253,21 @@ if [[ ! -f .env && -f .env.example ]]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
PROXY_PORT="$(ask_proxy_port)"
|
PROXY_PORT="$(ask_proxy_port)"
|
||||||
PROXY_PORT_END="$((PROXY_PORT + 10))"
|
assert_proxy_port_available "$PROXY_PORT"
|
||||||
if [ "$PROXY_PORT_END" -gt 65535 ]; then
|
PROXY_PORT_END="$(port_range_end "$PROXY_PORT")"
|
||||||
PROXY_PORT_END=65535
|
UI_PORT="${REQUESTED_UI_PORT:-$(get_env_value CLIENT_UI_PORT)}"
|
||||||
fi
|
|
||||||
UI_PORT="${CLIENT_UI_PORT:-$(get_env_value CLIENT_UI_PORT)}"
|
|
||||||
UI_PORT="${UI_PORT:-3456}"
|
UI_PORT="${UI_PORT:-3456}"
|
||||||
|
UI_PORT="$(choose_ui_port "$UI_PORT")"
|
||||||
|
assert_ui_outside_proxy_range
|
||||||
|
|
||||||
set_env_value APP_MODE client
|
set_env_value APP_MODE client
|
||||||
|
set_env_value CLIENT_UI_PORT "$UI_PORT"
|
||||||
|
set_env_value CLIENT_PROXY_PORT "$PROXY_PORT"
|
||||||
set_env_value CLIENT_PROXY_PORT_START "$PROXY_PORT"
|
set_env_value CLIENT_PROXY_PORT_START "$PROXY_PORT"
|
||||||
set_env_value CLIENT_PROXY_PORT_END "$PROXY_PORT_END"
|
set_env_value CLIENT_PROXY_PORT_END "$PROXY_PORT_END"
|
||||||
set_env_value PROXY_PORT "$PROXY_PORT"
|
set_env_value PROXY_PORT "$PROXY_PORT"
|
||||||
|
|
||||||
|
log "UI port: http://127.0.0.1:${UI_PORT}"
|
||||||
log "proxy port: 127.0.0.1:${PROXY_PORT} (reserved range ${PROXY_PORT}-${PROXY_PORT_END})"
|
log "proxy port: 127.0.0.1:${PROXY_PORT} (reserved range ${PROXY_PORT}-${PROXY_PORT_END})"
|
||||||
|
|
||||||
log "building and starting Docker client"
|
log "building and starting Docker client"
|
||||||
|
|||||||
Reference in New Issue
Block a user