feat(diagnostics): add read-only WireGuard health checks
This commit is contained in:
+180
-6
@@ -54,7 +54,7 @@ print_usage() {
|
||||
Использование:
|
||||
wg-manager.sh help
|
||||
wg-manager.sh version
|
||||
wg-manager.sh {status|list|add NAME [--mode MODE] [--endpoint HOST]|show NAME|config NAME [--show]|qr NAME|remove NAME [--yes]|diagnose|logs|backups|restore|tui}
|
||||
wg-manager.sh {status|list|add NAME [--mode MODE] [--endpoint HOST]|show NAME|config NAME [--show]|qr NAME|remove NAME [--yes]|diagnose [--quiet]|logs|backups|restore|tui}
|
||||
|
||||
Команды управления WireGuard будут добавлены в следующих версиях.
|
||||
USAGE
|
||||
@@ -261,6 +261,8 @@ load_config() { set_runtime_paths || return $?; parse_config_file "$CONFIG_PATH"
|
||||
readonly MANAGED_MARKER_PREFIX='wg-manager-client:'
|
||||
declare -a CLIENT_KEYS=() CLIENT_NAMES=() CLIENT_ADDRESSES=() CLIENT_ENDPOINTS=() CLIENT_HANDSHAKES=() CLIENT_RX=() CLIENT_TX=() CLIENT_STATES=() CLIENT_ORIGINS=()
|
||||
declare -A DISK_ALLOWED DISK_ENDPOINT DISK_MARKER DISK_LEGACY DISK_IPV4 DISK_IPV6
|
||||
PARSE_DUPLICATE_KEYS=0
|
||||
PARSE_ALLOWEDIPS_OVERLAP=0
|
||||
DISK_LISTEN_PORT='-'
|
||||
declare -A META_NAME META_IPV4 META_IPV6 META_CONFIG META_MODE META_MANAGED META_CREATED META_FILE
|
||||
declare -A RUNTIME_ENDPOINT RUNTIME_HANDSHAKE RUNTIME_RX RUNTIME_TX RUNTIME_ALLOWED
|
||||
@@ -379,11 +381,11 @@ commit_disk_peer() {
|
||||
local key=$1 allowed=$2
|
||||
[[ -n "$key" && -n "$allowed" ]] || { die "$EXIT_VALIDATION" 'Peer не содержит PublicKey или AllowedIPs'; return $?; }
|
||||
is_wireguard_key "$key" || { die "$EXIT_VALIDATION" 'Некорректный PublicKey в конфигурации'; return $?; }
|
||||
[[ -z ${DISK_ALLOWED[$key]+x} ]] || { die "$EXIT_VALIDATION" 'Дублирующийся PublicKey в конфигурации'; return $?; }
|
||||
[[ -z ${DISK_ALLOWED[$key]+x} ]] || { PARSE_DUPLICATE_KEYS=1; die "$EXIT_VALIDATION" 'Дублирующийся PublicKey в конфигурации'; return $?; }
|
||||
local normalized existing_key
|
||||
normalized=$(normalize_allowed_ips "$allowed") || { die "$EXIT_VALIDATION" 'Некорректные или пересекающиеся AllowedIPs'; return $?; }
|
||||
normalized=$(normalize_allowed_ips "$allowed") || { PARSE_ALLOWEDIPS_OVERLAP=1; die "$EXIT_VALIDATION" 'Некорректные или пересекающиеся AllowedIPs'; return $?; }
|
||||
for existing_key in "${!DISK_ALLOWED[@]}"; do
|
||||
allowed_sets_overlap "$normalized" "${DISK_ALLOWED[$existing_key]}" && { die "$EXIT_VALIDATION" 'Пересекающиеся AllowedIPs между peers'; return $?; }
|
||||
allowed_sets_overlap "$normalized" "${DISK_ALLOWED[$existing_key]}" && { PARSE_ALLOWEDIPS_OVERLAP=1; die "$EXIT_VALIDATION" 'Пересекающиеся AllowedIPs между peers'; return $?; }
|
||||
done
|
||||
DISK_ALLOWED["$key"]=$normalized
|
||||
local addresses; addresses=$(extract_client_addresses "${DISK_ALLOWED[$key]}")
|
||||
@@ -397,7 +399,7 @@ commit_disk_peer() {
|
||||
parse_wireguard_config() {
|
||||
local file=$1 line trimmed section='' key value peer_key='' peer_allowed='' peer_endpoint='-' peer_marker='' peer_legacy='' pending_marker='' pending_legacy='' line_number=0
|
||||
local -A interface_fields=() peer_fields=()
|
||||
DISK_ALLOWED=(); DISK_ENDPOINT=(); DISK_MARKER=(); DISK_LEGACY=(); DISK_IPV4=(); DISK_IPV6=()
|
||||
DISK_ALLOWED=(); DISK_ENDPOINT=(); DISK_MARKER=(); DISK_LEGACY=(); DISK_IPV4=(); DISK_IPV6=(); PARSE_DUPLICATE_KEYS=0; PARSE_ALLOWEDIPS_OVERLAP=0
|
||||
DISK_LISTEN_PORT='-'
|
||||
[[ -r "$file" && ! -L "$file" ]] || { die "$EXIT_VALIDATION" 'Конфигурация WireGuard недоступна или является ссылкой'; return $?; }
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
@@ -1405,6 +1407,173 @@ resolve_gum() {
|
||||
|
||||
print_version() { printf 'WireGuard Manager %s\n' "$WG_MANAGER_VERSION"; }
|
||||
run_placeholder() { load_config || return $?; die "$EXIT_OPERATION" "Команда «$1» пока не реализована"; }
|
||||
DIAG_FAILS=0
|
||||
DIAG_QUIET=0
|
||||
declare -A DIAG_IDS=()
|
||||
diag_result() {
|
||||
local ident=$1 status=$2 summary=$3 guidance=''
|
||||
(( $# >= 4 )) && guidance=$4
|
||||
[[ $ident =~ ^[a-z0-9-]+$ ]] || { ((DIAG_FAILS += 1)); return 1; }
|
||||
[[ $status == OK || $status == WARN || $status == FAIL || $status == SKIP ]] || { ((DIAG_FAILS += 1)); return 1; }
|
||||
if [[ -n ${DIAG_IDS[$ident]+x} ]]; then ((DIAG_FAILS += 1)); return 1; fi
|
||||
DIAG_IDS[$ident]=1
|
||||
if ((DIAG_QUIET == 1)); then printf '%s=%s\n' "$ident" "$status"; else printf '%s [%s] %s' "$ident" "$status" "$summary"; [[ -n $guidance ]] && printf ' — %s' "$guidance"; printf '\n'; fi
|
||||
[[ $status == FAIL ]] && ((DIAG_FAILS += 1)); return 0
|
||||
}
|
||||
diag_command_available() {
|
||||
local command_name=$1 disabled
|
||||
local -a disabled_commands=()
|
||||
if transaction_test_hooks_enabled; then
|
||||
IFS=, read -r -a disabled_commands <<< "${DIAG_DISABLE_COMMANDS:-}"
|
||||
for disabled in "${disabled_commands[@]:-}"; do [[ $disabled == "$command_name" ]] && return 1; done
|
||||
fi
|
||||
command -v -- "$command_name" >/dev/null 2>&1
|
||||
}
|
||||
diag_read_value() { local value; [[ -r $1 && ! -L $1 ]] || return 1; IFS= read -r value <"$1" || return 1; printf '%s' "$value"; }
|
||||
diagnose() {
|
||||
local manager_config_ok=0 output path value command_name dump_ok=0
|
||||
local disk_peer_count=0 runtime_peer_count=0 route_output systemd_state runtime_match=1 disk_parse_ok=0 systemd_usable=0
|
||||
local interface_name=wg0 server_vpn_ip=0.0.0.0 vpn_prefix=0 server_port=0 use_ipv6=0 ipv6_prefix='' ipv4_network=''
|
||||
local lower_output='' line nat_ok=0 key token key_owner index
|
||||
local address_ok=0 route_ok=0 ipv6_address_ok=0 ipv6_route_ok=0
|
||||
local line_network=0 line_interface=0 line_nat=0
|
||||
local -a tokens=()
|
||||
DIAG_FAILS=0; DIAG_QUIET=0; DIAG_IDS=()
|
||||
(($# <= 1)) || { die "$EXIT_USAGE" 'Лишние аргументы diagnose'; return $?; }
|
||||
if (($# == 1)); then [[ $1 == --quiet ]] || { die "$EXIT_USAGE" 'Разрешён только флаг diagnose --quiet'; return $?; }; DIAG_QUIET=1; fi
|
||||
set_runtime_paths || { diag_result paths FAIL 'Некорректные пути состояния' 'Проверьте WG_MANAGER_*'; return "$EXIT_VALIDATION"; }
|
||||
if [[ -r $CONFIG_PATH && -f $CONFIG_PATH && ! -L $CONFIG_PATH ]] && path_components_safe "$CONFIG_PATH"; then
|
||||
diag_result config-readable OK 'Конфигурация доступна'
|
||||
if load_config >/dev/null 2>&1; then manager_config_ok=1; interface_name=$WG_INTERFACE; server_vpn_ip=$SERVER_VPN_IP; vpn_prefix=$VPN_SUBNET_PREFIX; server_port=$SERVER_PORT; use_ipv6=$USE_IPV6; ipv6_prefix=$IPV6_PREFIX; ipv4_network="${vpn_prefix}.0/24"; fi
|
||||
else
|
||||
diag_result config-readable FAIL 'Конфигурация недоступна'
|
||||
fi
|
||||
if ((manager_config_ok == 1)); then
|
||||
local config_mode wg_mode config_owner wg_owner wg_config_safe=0
|
||||
config_mode=$(stat -c '%a' -- "$CONFIG_PATH" 2>/dev/null) || config_mode=''
|
||||
config_owner=$(stat -c '%u:%g' -- "$CONFIG_PATH" 2>/dev/null) || config_owner=''
|
||||
if [[ -r $WG_CONF && -f $WG_CONF && ! -L $WG_CONF ]] && path_components_safe "$WG_CONF"; then wg_config_safe=1; fi
|
||||
if ((wg_config_safe == 1)); then
|
||||
wg_mode=$(stat -c '%a' -- "$WG_CONF" 2>/dev/null) || wg_mode=''
|
||||
wg_owner=$(stat -c '%u:%g' -- "$WG_CONF" 2>/dev/null) || wg_owner=''
|
||||
else
|
||||
wg_mode=''; wg_owner=''
|
||||
fi
|
||||
if [[ -f $CONFIG_PATH && ! -L $CONFIG_PATH && ( $config_mode == 400 || $config_mode == 600 ) && ( $wg_mode == 400 || $wg_mode == 600 ) && $(stat -c '%h' -- "$CONFIG_PATH") == 1 && $wg_config_safe == 1 && $(stat -c '%h' -- "$WG_CONF") == 1 && $config_owner == "$wg_owner" ]]; then diag_result config-permissions OK 'Права конфигураций безопасны'; else diag_result config-permissions FAIL 'Небезопасные права конфигураций'; fi
|
||||
if ((wg_config_safe == 1)) && parse_wireguard_config "$WG_CONF" >/dev/null 2>&1; then disk_parse_ok=1; diag_result config-parse OK 'Конфигурация WireGuard корректна'; else diag_result config-parse FAIL 'Конфигурация WireGuard не прошла проверку'; fi
|
||||
if ((disk_parse_ok == 1)); then
|
||||
(( PARSE_DUPLICATE_KEYS == 1 )) && diag_result duplicate-keys FAIL 'Обнаружены повторяющиеся поля' || diag_result duplicate-keys OK 'Повторяющихся ключей не найдено'
|
||||
(( PARSE_ALLOWEDIPS_OVERLAP == 1 )) && diag_result allowedips-overlap FAIL 'Обнаружены дублирующиеся или пересекающиеся AllowedIPs' || diag_result allowedips-overlap OK 'AllowedIPs не пересекаются'
|
||||
else
|
||||
(( PARSE_DUPLICATE_KEYS == 1 )) && diag_result duplicate-keys FAIL 'Обнаружены повторяющиеся поля' || diag_result duplicate-keys SKIP 'Полный разбор конфигурации невозможен'
|
||||
(( PARSE_ALLOWEDIPS_OVERLAP == 1 )) && diag_result allowedips-overlap FAIL 'Обнаружены дублирующиеся или пересекающиеся AllowedIPs' || diag_result allowedips-overlap SKIP 'Полный разбор конфигурации невозможен'
|
||||
fi
|
||||
local key_file key_mode key_links key_found=0 key_bad=0 client_dir_mode client_dir_owner
|
||||
client_dir_mode=$(stat -c '%a' -- "$CLIENT_DIR" 2>/dev/null) || client_dir_mode=''; client_dir_owner=$(stat -c '%u:%g' -- "$CLIENT_DIR" 2>/dev/null) || client_dir_owner=''
|
||||
if [[ -d $CLIENT_DIR && ! -L $CLIENT_DIR && $client_dir_mode == 700 && $client_dir_owner == "$wg_owner" ]] && path_components_safe "$CLIENT_DIR"; then
|
||||
for key_file in "${CLIENT_DIR%/}"/*.key "${CLIENT_DIR%/}"/*.psk; do
|
||||
[[ -e $key_file || -L $key_file ]] || continue
|
||||
key_found=1
|
||||
key_mode=$(stat -c '%a' -- "$key_file" 2>/dev/null) || key_mode=''; key_links=$(stat -c '%h' -- "$key_file" 2>/dev/null) || key_links=''
|
||||
key_owner=$(stat -c '%u:%g' -- "$key_file" 2>/dev/null) || key_owner=''
|
||||
if [[ ! -f $key_file || -L $key_file || ( $key_mode != 400 && $key_mode != 600 ) || $key_links != 1 || $key_owner != "$wg_owner" ]]; then key_bad=1; fi
|
||||
done
|
||||
if ((key_found == 0)); then diag_result key-permissions SKIP 'Отдельные ключевые файлы не найдены'; elif ((key_bad == 0)); then diag_result key-permissions OK 'Права ключевых файлов безопасны'; else diag_result key-permissions FAIL 'Небезопасные права ключевых файлов'; fi
|
||||
else diag_result key-permissions FAIL 'Каталог клиентских ключей небезопасен'; fi
|
||||
else diag_result config-permissions FAIL 'Параметры конфигурации недействительны'; diag_result config-parse FAIL 'Конфигурация менеджера не загружена'; diag_result duplicate-keys SKIP 'Конфигурация не загружена'; diag_result allowedips-overlap SKIP 'Конфигурация не загружена'; diag_result key-permissions SKIP 'Конфигурация не загружена'; fi
|
||||
for command_name in wg wg-quick ip; do diag_command_available "$command_name" && diag_result "command-$command_name" OK "Команда $command_name доступна" || diag_result "command-$command_name" FAIL "Не найдена обязательная команда $command_name"; done
|
||||
if ((manager_config_ok == 1)) && diag_command_available ip && ip link show dev "$interface_name" >/dev/null 2>&1; then diag_result interface-exists OK 'Интерфейс существует'; elif ((manager_config_ok == 1)); then diag_result interface-exists FAIL 'Интерфейс не найден'; else diag_result interface-exists SKIP 'Конфигурация менеджера не загружена'; fi
|
||||
if ((manager_config_ok == 1)) && diag_command_available wg && output=$(wg show "$interface_name" dump 2>/dev/null) && parse_runtime_dump "$output" >/dev/null 2>&1; then dump_ok=1; diag_result interface-runtime OK 'Дамп WireGuard корректен'; elif ((manager_config_ok == 1)); then diag_result interface-runtime FAIL 'Дамп WireGuard недоступен или неверен'; else diag_result interface-runtime SKIP 'Конфигурация менеджера не загружена'; fi
|
||||
if diag_command_available systemctl; then
|
||||
if ! systemd_state=$(systemctl is-system-running 2>/dev/null); then :; fi
|
||||
if [[ $systemd_state == running || $systemd_state == degraded ]]; then
|
||||
systemd_usable=1
|
||||
if systemd_state=$(systemctl is-active -- "wg-quick@$interface_name" 2>/dev/null); then
|
||||
case $systemd_state in active) diag_result systemd OK 'Сервис WireGuard активен';; inactive|failed|dead) diag_result systemd FAIL 'Сервис WireGuard неактивен';; *) diag_result systemd WARN 'Состояние systemd неясно';; esac
|
||||
else
|
||||
case $systemd_state in inactive|failed|dead) diag_result systemd FAIL 'Сервис WireGuard неактивен';; *) diag_result systemd WARN 'Не удалось получить состояние systemd';; esac
|
||||
fi
|
||||
else diag_result systemd SKIP 'systemd не является активным init'; fi
|
||||
else diag_result systemd SKIP 'systemctl не найден'; fi
|
||||
path=${WG_MANAGER_IPV4_FORWARD_FILE:-/proc/sys/net/ipv4/ip_forward}; value=$(diag_read_value "$path" 2>/dev/null) || value=''; if [[ $value == 1 ]]; then diag_result forwarding-ipv4 OK 'IPv4 forwarding включён'; elif [[ -n $value ]]; then diag_result forwarding-ipv4 FAIL 'IPv4 forwarding имеет небезопасное значение'; else diag_result forwarding-ipv4 SKIP 'Нет доступа к IPv4 forwarding'; fi
|
||||
if [[ $use_ipv6 == 1 ]]; then path=${WG_MANAGER_IPV6_FORWARD_FILE:-/proc/sys/net/ipv6/conf/all/forwarding}; value=$(diag_read_value "$path" 2>/dev/null) || value=''; [[ $value == 1 ]] && diag_result forwarding-ipv6 OK 'IPv6 forwarding включён' || [[ -n $value ]] && diag_result forwarding-ipv6 FAIL 'IPv6 forwarding имеет небезопасное значение' || diag_result forwarding-ipv6 SKIP 'Нет доступа к IPv6 forwarding'; else diag_result forwarding-ipv6 SKIP 'IPv6 отключён'; fi
|
||||
if ((manager_config_ok == 1)) && diag_command_available ip; then
|
||||
output=$(ip -o addr show dev "$interface_name" 2>/dev/null); address_ok=0
|
||||
while IFS= read -r line; do
|
||||
read -r -a tokens <<<"$line"
|
||||
for token in "${tokens[@]}"; do token=${token%%/*}; [[ $token == "$server_vpn_ip" ]] && address_ok=1; done
|
||||
done <<<"$output"
|
||||
((address_ok == 1)) && diag_result interface-address OK 'Адрес интерфейса подтверждён' || diag_result interface-address WARN 'Адрес интерфейса не подтверждён'
|
||||
route_output=$(ip -o route show "$ipv4_network" dev "$interface_name" 2>/dev/null); tokens=(); read -r -a tokens <<<"$route_output"; route_ok=0
|
||||
for ((index=0; index<${#tokens[@]}; index++)); do [[ ${tokens[index]} == "$ipv4_network" && ${tokens[index+1]:-} == dev && ${tokens[index+2]:-} == "$interface_name" ]] && route_ok=1; done
|
||||
((route_ok == 1)) && diag_result routes OK 'Маршрут VPN подтверждён' || diag_result routes WARN 'Маршрут VPN не подтверждён'
|
||||
else diag_result interface-address SKIP 'ip не найден'; diag_result routes SKIP 'ip не найден'; fi
|
||||
if [[ $use_ipv6 == 1 ]] && diag_command_available ip; then
|
||||
local expected_ipv6="${ipv6_prefix}1" expected_ipv6_route="${ipv6_prefix}/64"
|
||||
output=$(ip -o addr show dev "$interface_name" 2>/dev/null); ipv6_address_ok=0
|
||||
while IFS= read -r line; do
|
||||
read -r -a tokens <<<"$line"
|
||||
for token in "${tokens[@]}"; do token=${token%%/*}; [[ $token == "$expected_ipv6" ]] && ipv6_address_ok=1; done
|
||||
done <<<"$output"
|
||||
((ipv6_address_ok == 1)) && diag_result interface-address-ipv6 OK 'IPv6 адрес интерфейса подтверждён' || diag_result interface-address-ipv6 WARN 'IPv6 адрес интерфейса не подтверждён'
|
||||
route_output=$(ip -o route show "$expected_ipv6_route" dev "$interface_name" 2>/dev/null); tokens=(); read -r -a tokens <<<"$route_output"; ipv6_route_ok=0
|
||||
for ((index=0; index<${#tokens[@]}; index++)); do [[ ${tokens[index]} == "$expected_ipv6_route" && ${tokens[index+1]:-} == dev && ${tokens[index+2]:-} == "$interface_name" ]] && ipv6_route_ok=1; done
|
||||
((ipv6_route_ok == 1)) && diag_result routes-ipv6 OK 'IPv6 маршрут VPN подтверждён' || diag_result routes-ipv6 WARN 'IPv6 маршрут VPN не подтверждён'
|
||||
else diag_result interface-address-ipv6 SKIP 'IPv6 отключён'; diag_result routes-ipv6 SKIP 'IPv6 отключён'; fi
|
||||
if diag_command_available ss; then
|
||||
local udp_ok=0 endpoint
|
||||
if output=$(ss -lun 2>/dev/null); then
|
||||
tokens=()
|
||||
while IFS= read -r line; do
|
||||
read -r -a tokens <<<"$line"
|
||||
for token in "${tokens[@]}"; do
|
||||
token=${token//\[/}; token=${token//\]/}
|
||||
[[ $token == *":$server_port" ]] || continue
|
||||
endpoint=${token%:"$server_port"}
|
||||
if [[ $endpoint == '*' || $endpoint == '::' || $endpoint =~ ^[0-9]+(\.[0-9]+){3}$ ]]; then udp_ok=1; fi
|
||||
done
|
||||
done <<<"$output"
|
||||
fi
|
||||
((udp_ok == 1)) && diag_result udp-listener OK 'UDP-порт подтверждён' || diag_result udp-listener WARN 'UDP-порт не подтверждён'
|
||||
else diag_result udp-listener SKIP 'ss не найден'; fi
|
||||
local firewall_checked=0
|
||||
if diag_command_available nft; then
|
||||
if output=$(nft list ruleset 2>/dev/null); then
|
||||
firewall_checked=1; nat_ok=0; lower_output=${output,,}
|
||||
while IFS= read -r line; do
|
||||
line=${line,,}
|
||||
tokens=(); read -r -a tokens <<<"$line"; line_network=0; line_interface=0; line_nat=0
|
||||
for token in "${tokens[@]}"; do token=${token//\"/}; [[ $token == "$ipv4_network" ]] && line_network=1; [[ $token == "$interface_name" ]] && line_interface=1; [[ $token == masquerade || $token == snat ]] && line_nat=1; done
|
||||
((line_network == 1 && line_interface == 1 && line_nat == 1)) && nat_ok=1
|
||||
((nat_ok == 1)) && break
|
||||
done <<<"$lower_output"
|
||||
((nat_ok == 1)) && diag_result firewall OK 'Видно ожидаемое NAT-свидетельство' || diag_result firewall WARN 'Не найдено убедительное NAT-свидетельство'
|
||||
fi
|
||||
fi
|
||||
if ((firewall_checked == 0)) && diag_command_available iptables; then
|
||||
if output=$(iptables -t nat -S 2>/dev/null); then
|
||||
firewall_checked=1; nat_ok=0; lower_output=${output,,}
|
||||
while IFS= read -r line; do
|
||||
line=${line,,}
|
||||
tokens=(); read -r -a tokens <<<"$line"; line_network=0; line_interface=0; line_nat=0
|
||||
for token in "${tokens[@]}"; do token=${token//\"/}; [[ $token == "$ipv4_network" ]] && line_network=1; [[ $token == "$interface_name" ]] && line_interface=1; [[ $token == masquerade || $token == snat ]] && line_nat=1; done
|
||||
((line_network == 1 && line_interface == 1 && line_nat == 1)) && nat_ok=1
|
||||
((nat_ok == 1)) && break
|
||||
done <<<"$lower_output"
|
||||
((nat_ok == 1)) && diag_result firewall OK 'Видно ожидаемое NAT-свидетельство' || diag_result firewall WARN 'Не найдено убедительное NAT-свидетельство'
|
||||
fi
|
||||
fi
|
||||
((firewall_checked == 1)) || { if diag_command_available nft || diag_command_available iptables; then diag_result firewall WARN 'Команда firewall недоступна'; else diag_result firewall SKIP 'Нет nftables или iptables'; fi; }
|
||||
if ((manager_config_ok == 1 && disk_parse_ok == 1 && dump_ok == 1)); then
|
||||
disk_peer_count=${#DISK_ALLOWED[@]}; runtime_peer_count=${#RUNTIME_ALLOWED[@]};
|
||||
((disk_peer_count == runtime_peer_count)) || runtime_match=0
|
||||
for key in "${!DISK_ALLOWED[@]}"; do [[ -n ${RUNTIME_ALLOWED[$key]+x} && ${RUNTIME_ALLOWED[$key]} == "${DISK_ALLOWED[$key]}" ]] || runtime_match=0; done
|
||||
for key in "${!RUNTIME_ALLOWED[@]}"; do [[ -n ${DISK_ALLOWED[$key]+x} ]] || runtime_match=0; done
|
||||
((runtime_match == 1)) && diag_result runtime-disk OK 'Наборы peers совпадают' || diag_result runtime-disk WARN 'Наборы peers имеют различия';
|
||||
else diag_result runtime-disk SKIP 'Сравнение peers невозможно'; fi
|
||||
if ((systemd_usable == 1)) && diag_command_available journalctl; then if output=$(journalctl -u "wg-quick@$interface_name" --since '24 hours ago' --no-pager 2>/dev/null); then lower_output=${output,,}; [[ $lower_output == *error* || $lower_output == *failed* ]] && diag_result journal WARN 'Журнал содержит ошибки' || diag_result journal OK 'Свежих ошибок в журнале не найдено'; else diag_result journal WARN 'Журнал недоступен'; fi; else diag_result journal SKIP 'Журнал недоступен без systemd'; fi
|
||||
((DIAG_FAILS == 0)) || return "$EXIT_OPERATION"
|
||||
}
|
||||
|
||||
resolve_named_client() {
|
||||
local name=$1 index count=0 found=-1
|
||||
@@ -1844,6 +2013,11 @@ main() {
|
||||
case $command in show) show_client "$name" ;; qr) qr_client "$name" ;; esac
|
||||
return $?
|
||||
fi
|
||||
if [[ $1 == diagnose ]]; then
|
||||
shift
|
||||
diagnose "$@"
|
||||
return $?
|
||||
fi
|
||||
if (( $# != 1 )); then
|
||||
if die "$EXIT_USAGE" 'Ожидается одна команда'; then status=0; else status=$?; fi
|
||||
print_usage >&2
|
||||
@@ -1858,7 +2032,7 @@ main() {
|
||||
shift
|
||||
(($# >= 1)) || { die "$EXIT_USAGE" 'Ожидается имя клиента'; return $?; }
|
||||
remove_client "$@" ;;
|
||||
diagnose|logs|backups|restore|tui) run_placeholder "$1" ;;
|
||||
logs|backups|restore|tui) run_placeholder "$1" ;;
|
||||
*)
|
||||
if die "$EXIT_USAGE" "неизвестная команда: $1"; then status=0; else status=$?; fi
|
||||
print_usage >&2
|
||||
|
||||
Reference in New Issue
Block a user