fix desktop
This commit is contained in:
13
.gitignore
vendored
13
.gitignore
vendored
@@ -1,8 +1,21 @@
|
||||
.old
|
||||
**/node_modules
|
||||
**/dist
|
||||
**/bin
|
||||
**/target
|
||||
**/target/**/*
|
||||
**/target/**/.*
|
||||
**/dist/**/*
|
||||
**/dist/**/.*
|
||||
**/bin/**/*
|
||||
**/bin/**/.*
|
||||
ui/node_modules
|
||||
desktop/node_modules
|
||||
desktop-angular/node_modules
|
||||
desktop-angular/bin
|
||||
ui/dist
|
||||
desktop/dist
|
||||
desktop-angular/dist
|
||||
desktop/bin
|
||||
desktop/bin/**/*
|
||||
desktop/bin/**/.*
|
||||
|
@@ -352,11 +352,11 @@ Renderer остался «чистым» фронтом: получает `windo
|
||||
|
||||
## Глава 4. Секретный Go-движок внутри
|
||||
|
||||
Написан маленький Go-хелпер, который делает РОВНО то, что нужно:
|
||||
Написан маленький Go-хелпер, который делает кнокинг портов в соответствии с конфигурацией:
|
||||
|
||||
- читает из stdin JSON: `targets`, `delay`, `gateway`,
|
||||
- превращает в конфиг,
|
||||
- вызывает `internal.PortKnocker()` (в нём `SO_BINDTODEVICE`, `LocalAddr`, TCP/UDP),
|
||||
- превращает их в конфиг,
|
||||
- вызывает `internal.PortKnocker()` (в нём `SO_BINDTODEVICE`),
|
||||
- печатает в stdout один короткий JSON: «успех/ошибка».
|
||||
|
||||
```go
|
||||
@@ -512,7 +512,7 @@ clearTimeout(timeout);
|
||||
|
||||
## Глава 6. UI только для UI
|
||||
|
||||
В `renderer` ничего такого что непосредственно работает с ядром системы.
|
||||
В `renderer` ничего такого что непосредственно работает с ядром системы.
|
||||
Собираем `targets`, `delay`, забираем `gateway` с формы — и даём команду:
|
||||
|
||||
```js
|
||||
@@ -552,18 +552,18 @@ Renderer ничего не знает про сокеты, туннели и Go.
|
||||
|
||||
При разработке запускаем `npm run dev` — он сначала соберёт Go, потом стартанёт Electron. В проде `electron-builder` положит бинарь в `resources/bin`. Пользователь вообще не в курсе, что внутри слишком умный Go сидит и пинает пакеты в нужные цели.
|
||||
|
||||
Можно конечно и не Го-хелпер запилить - например на Rust или на С++
|
||||
Можно конечно и не Го-хелпер запилить - например на Rust (смотри последнюю версию проекта в репозитории) или на С++
|
||||
|
||||
---
|
||||
|
||||
## Глава 8. Почему мы пошли этим путём (немного философии)
|
||||
## Глава 8. Почему именно так
|
||||
|
||||
- Node — шикарен для UI и связки слоёв, но ему не хватает «низкоуровневого директа» в сокетах.
|
||||
- Go умеет то, что нам нужно: `SO_BINDTODEVICE`, привязка к интерфейсу, «я сказал — поедешь тут».
|
||||
- Electron — как контейнер: упаковали веб, настроили мосты, подложили Go, включили DevTools — красота.
|
||||
- Node — хорош для UI и связки слоёв, но ему не хватает «низкоуровневого директа» в сокетах.
|
||||
- Go умеет то, что нужно: `SO_BINDTODEVICE`, привязка к интерфейсу.
|
||||
- Electron — как контейнер: упаковали веб, настроили мосты, подложили Go, включили DevTools = красота.
|
||||
|
||||
Минус: надо собирать маленький бинарь.
|
||||
Плюс: он работает невидимо и делает «как надо».
|
||||
Минус: надо собирать маленький бинарничек, который немного увеличит размер пакета (так как он и так ого-го-то несущественно).
|
||||
Плюс: он работает невидимо и делает что требуется.
|
||||
|
||||
---
|
||||
|
||||
@@ -573,13 +573,209 @@ Renderer ничего не знает про сокеты, туннели и Go.
|
||||
- Следите за stdout хелпера — там должен быть только JSON (мы жёстко это контролируем).
|
||||
- Путь к бинарю в деве и проде разный — мы это учли (ищем сначала `bin/`, потом `resources/bin`).
|
||||
|
||||
Мем-чекилст:
|
||||
- [x] «Всё уходит в WireGuard» → ставим `gateway` → Go рулит.
|
||||
- [x] «JSON не парсится» → убрали verbose → теперь парсится.
|
||||
- [x] «Где бинарь?» → смотрим `resources/bin`.
|
||||
---
|
||||
|
||||
## Эпилог
|
||||
|
||||
Мы сделали десктопное приложение, которое вообще-то «веб», но изнутри умеет кое что еще.
|
||||
|
||||
---
|
||||
|
||||
## Эпилог: помогло?
|
||||
## А как же фреймворки?
|
||||
|
||||
Мы сделали десктопное приложение, которое вообще-то «веб», но изнутри умеет очень взрослые вещи. Пользователь нажимает кнопку — а Go в это время спорит с системой маршрутизации, выигрывает и бьёт туда, куда надо.
|
||||
На Vanilla js все просто и понятно писать но очень многсловно и трудно сделать все структурировано и красиво "по фен-шую".
|
||||
Давайте коротенько рассмотрим, а как же нам портировать имеющийся ui ангуляр проект, который героически был вшит в go-knocker в десктопном проекте electron.
|
||||
|
||||
Итак, у нас есть готовый или не очень Angular проект который уже работает как веб-приложение. Теперь нужно интегрировать его в Electron так, чтобы получить все возможности поюзать его как графическое приложение.
|
||||
|
||||
### Архитектурное решение
|
||||
|
||||
Мы создали отдельную папку `desktop-angular/`, которая содержит:
|
||||
|
||||
1. **Electron обертку** - основной процесс и настройки
|
||||
2. **Копию Angular проекта** в `src/frontend/` - для удобства разработки
|
||||
3. **IPC сервисы** - для взаимодействия с нативным кодом
|
||||
4. **Кастомные модальные окна** - нативные диалоги Electron
|
||||
|
||||
### Структура проекта
|
||||
|
||||
``` text
|
||||
desktop-angular/
|
||||
├── src/
|
||||
│ ├── main/ # Electron main process
|
||||
│ │ ├── main.js # Основной процесс
|
||||
│ │ ├── modal.html # Кастомные модальные окна
|
||||
│ │ ├── open-dialog.html # Диалог открытия файлов
|
||||
│ │ └── save-dialog.html # Диалог сохранения файлов
|
||||
│ ├── preload/ # Безопасный мост
|
||||
│ │ └── preload.js # API для рендерера
|
||||
│ └── frontend/ # Angular приложение
|
||||
│ ├── src/app/
|
||||
│ │ ├── ipc.service.ts # Сервис для IPC
|
||||
│ │ ├── modal.service.ts # Сервис модальных окон
|
||||
│ │ └── root.component.ts # Главный компонент
|
||||
│ └── package.json # Зависимости Angular
|
||||
├── package.json # Конфигурация Electron
|
||||
└── bin/ # Скомпилированный Go бэкенд
|
||||
```
|
||||
|
||||
|
||||
### Ключевые особенности реализации
|
||||
|
||||
#### 1. Двойной режим работы
|
||||
|
||||
В `main.js` реализована логика, которая определяет режим работы:
|
||||
|
||||
```javascript
|
||||
const isDev = process.env.NODE_ENV !== "production" && !app.isPackaged;
|
||||
|
||||
if (isDev) {
|
||||
// В режиме разработки загружаем с ng serve
|
||||
win.loadURL('http://localhost:4200');
|
||||
win.webContents.openDevTools();
|
||||
} else {
|
||||
// В продакшене загружаем собранные файлы
|
||||
const indexPath = app.isPackaged
|
||||
? path.join(process.resourcesPath, 'ui-dist', 'index.html')
|
||||
: path.resolve(__dirname, '../frontend/dist/project-front/browser/index.html');
|
||||
win.loadFile(indexPath);
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. IPC сервис для Angular
|
||||
|
||||
Создан специальный сервис `IpcService`, который предоставляет удобный API для взаимодействия с Electron:
|
||||
|
||||
```typescript
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class IpcService {
|
||||
|
||||
async showNativeModal(config: ModalConfig): Promise<string> {
|
||||
return await (window as any).api.showNativeModal(config);
|
||||
}
|
||||
|
||||
async openFileDialog(config: FileDialogConfig): Promise<string[]> {
|
||||
return await (window as any).api.openFileDialog(config);
|
||||
}
|
||||
|
||||
async saveFileDialog(config: SaveDialogConfig): Promise<string> {
|
||||
return await (window as any).api.saveFileDialog(config);
|
||||
}
|
||||
|
||||
async loadFileContent(filePath: string): Promise<string> {
|
||||
return await (window as any).api.loadFileContent(filePath);
|
||||
}
|
||||
|
||||
async saveFileContent(filePath: string, content: string): Promise<void> {
|
||||
return await (window as any).api.saveFileContent(filePath, content);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. Кастомные нативные диалоги
|
||||
|
||||
Вместо стандартных системных диалогов созданы кастомные HTML/CSS/JS диалоги, которые:
|
||||
|
||||
- Работают даже если Angular UI "зависла"
|
||||
- Имеют единый стиль с приложением
|
||||
- Поддерживают превью файлов
|
||||
- Имеют расширенную функциональность
|
||||
|
||||
#### 4. Интеграция с Go бэкендом
|
||||
|
||||
Angular приложение работает с тем же Go бэкендом, что и веб-версия:
|
||||
|
||||
```typescript
|
||||
export class KnockService {
|
||||
private apiBase = 'http://localhost:8080/api/v1';
|
||||
|
||||
async knock(config: KnockConfig): Promise<KnockResult> {
|
||||
const response = await fetch(`${this.apiBase}/knock`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(config)
|
||||
});
|
||||
return await response.json();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Скрипты сборки
|
||||
|
||||
В `package.json` настроены удобные команды:
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"dev": "concurrently -k -n UI,ELECTRON -c green,cyan \"cd src/frontend && npm start\" \"wait-on http://localhost:4200 && cross-env NODE_ENV=development electron .\"",
|
||||
"build:ui": "cd src/frontend && npm run build",
|
||||
"go:build": "bash -lc 'mkdir -p bin && cd ../back && go build -o ../desktop-angular/bin/full-go-knocker .'",
|
||||
"dist": "npm run build:ui && npm run go:build && cross-env NODE_ENV=production electron-builder"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Конфигурация упаковки
|
||||
|
||||
Electron Builder настроен для включения всех необходимых файлов:
|
||||
|
||||
```json
|
||||
{
|
||||
"build": {
|
||||
"files": [
|
||||
"src/main/**/*",
|
||||
"src/preload/**/*",
|
||||
"package.json",
|
||||
"bin/**/*"
|
||||
],
|
||||
"extraResources": [
|
||||
{
|
||||
"from": "src/frontend/dist/project-front/browser",
|
||||
"to": "ui-dist"
|
||||
},
|
||||
{
|
||||
"from": "bin",
|
||||
"to": "bin"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Преимущества такого подхода
|
||||
|
||||
1. **Переиспользование кода** - Angular приложение остается тем же самым
|
||||
2. **Нативные возможности** - доступ к файловой системе, системным диалогам
|
||||
3. **Единая кодовая база** - один Angular проект для веба и десктопа
|
||||
4. **Удобная разработка** - hot reload в режиме разработки
|
||||
5. **Простая сборка** - автоматическая упаковка всех компонентов
|
||||
|
||||
### Результат
|
||||
|
||||
Получилось полноценное десктопное приложение, которое:
|
||||
|
||||
- Выглядит и работает как нативное
|
||||
- Использует весь функционал Angular
|
||||
- Интегрировано с Go бэкендом
|
||||
- Имеет кастомные нативные диалоги
|
||||
- Упаковывается в единый исполняемый файл
|
||||
|
||||
Это решение демонстрирует, как можно элегантно интегрировать современный веб-фреймворк в десктопное приложение, сохраняя все преимущества обеих платформ.
|
||||
|
||||
---
|
||||
|
||||
## Заключение
|
||||
|
||||
Мы рассмотрели два подхода к созданию десктопных приложений:
|
||||
|
||||
1. **Vanilla JavaScript** - простой, быстрый, но многословный
|
||||
2. **Angular интеграция** - структурированный, переиспользуемый, но более сложный
|
||||
|
||||
Оба подхода имеют свои преимущества и недостатки. Выбор зависит от ваших потребностей:
|
||||
|
||||
- Для быстрого прототипа или простого UI - Vanilla JS
|
||||
- Для сложного приложения с переиспользованием веб-кода - Angular
|
||||
|
||||
Главное - правильно спроектировать архитектуру взаимодействия между Electron и вашим UI, используя IPC и preload скрипты для безопасной передачи данных.
|
||||
|
@@ -24,10 +24,13 @@ func setupKnockRoutes(api *gin.RouterGroup) {
|
||||
ConfigYaml string `json:"config_yaml"`
|
||||
}
|
||||
if err := c.BindJSON(&req); err != nil {
|
||||
// fmt.Printf("bad json: %v\n", err)
|
||||
c.JSON(400, gin.H{"error": fmt.Sprintf("bad json: %v", err)})
|
||||
return
|
||||
}
|
||||
|
||||
// fmt.Printf("req: %+v\n", req)
|
||||
|
||||
knocker := internal.NewPortKnocker()
|
||||
|
||||
// Определяем режим: inline или YAML
|
||||
@@ -39,6 +42,8 @@ func setupKnockRoutes(api *gin.RouterGroup) {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("config: %+v\n", config)
|
||||
|
||||
// Применяем дополнительные параметры из запроса
|
||||
if req.Gateway != "" {
|
||||
for i := range config.Targets {
|
||||
@@ -47,7 +52,8 @@ func setupKnockRoutes(api *gin.RouterGroup) {
|
||||
}
|
||||
|
||||
if err := knocker.ExecuteWithConfig(config, req.Verbose, req.WaitConnection); err != nil {
|
||||
c.JSON(400, gin.H{"error": err.Error()})
|
||||
fmt.Printf("error: %v\n", err)
|
||||
c.JSON(400, gin.H{"status": "error","error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(200, gin.H{"status": "ok"})
|
||||
|
@@ -1,6 +1,6 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -100,13 +100,17 @@ func parseInlineTargets(targetsStr, delayStr string) (*internal.Config, error) {
|
||||
|
||||
// Разбираем формат [proto]:[host]:[port]
|
||||
parts := strings.Split(targetStr, ":")
|
||||
if len(parts) != 3 {
|
||||
return nil, fmt.Errorf("неверный формат цели '%s', ожидается [proto]:[host]:[port]", targetStr)
|
||||
if len(parts) != 3 && len(parts) != 4 {
|
||||
return nil, fmt.Errorf("неверный формат цели '%s', ожидается [proto]:[host]:[port] или [proto]:[host]:[port]:[gateway]", targetStr)
|
||||
}
|
||||
|
||||
protocol := strings.TrimSpace(parts[0])
|
||||
host := strings.TrimSpace(parts[1])
|
||||
portStr := strings.TrimSpace(parts[2])
|
||||
gateway := ""
|
||||
if len(parts) == 4 {
|
||||
gateway = strings.TrimSpace(parts[3])
|
||||
}
|
||||
|
||||
// Проверяем протокол
|
||||
if protocol != "tcp" && protocol != "udp" {
|
||||
@@ -130,7 +134,7 @@ func parseInlineTargets(targetsStr, delayStr string) (*internal.Config, error) {
|
||||
Protocol: protocol,
|
||||
Delay: internal.Duration(delay),
|
||||
WaitConnection: false,
|
||||
Gateway: "",
|
||||
Gateway: gateway,
|
||||
}
|
||||
|
||||
config.Targets = append(config.Targets, target)
|
||||
|
@@ -43,11 +43,16 @@ func runServe(cmd *cobra.Command, args []string) error {
|
||||
port = "8888"
|
||||
}
|
||||
|
||||
host := os.Getenv("GO_KNOCKER_SERVE_HOST")
|
||||
if strings.TrimSpace(port) == "" {
|
||||
host = ""
|
||||
}
|
||||
|
||||
r := gin.Default()
|
||||
|
||||
// CORS: разрешаем для локальной разработки
|
||||
r.Use(cors.New(cors.Config{
|
||||
AllowOrigins: []string{"http://localhost:4200", "http://127.0.0.1:8888", "http://localhost:8888"},
|
||||
AllowOrigins: []string{"http://localhost:4200", "http://127.0.0.1:8888", "http://localhost:" + port},
|
||||
AllowMethods: []string{"GET", "POST", "OPTIONS"},
|
||||
AllowHeaders: []string{"Authorization", "Content-Type"},
|
||||
AllowCredentials: true,
|
||||
@@ -65,6 +70,6 @@ func runServe(cmd *cobra.Command, args []string) error {
|
||||
setupCryptoRoutes(api, passHash)
|
||||
setupStaticRoutes(r, embeddedFS)
|
||||
|
||||
fmt.Printf("Serving on :%s\n", port)
|
||||
return r.Run(":" + port)
|
||||
fmt.Printf("Serving on %s:%s\n", host, port)
|
||||
return r.Run(host + ":" + port)
|
||||
}
|
||||
|
@@ -1,187 +0,0 @@
|
||||
# Диагностика обхода VPN для Gateway
|
||||
|
||||
## Проблема
|
||||
Gateway не работает - пакеты все еще идут через WireGuard туннель вместо локального шлюза.
|
||||
|
||||
## Диагностика
|
||||
|
||||
### 1. Проверьте сетевые интерфейсы
|
||||
|
||||
Откройте DevTools (F12) и выполните:
|
||||
|
||||
```javascript
|
||||
window.testNetworkInterfaces()
|
||||
```
|
||||
|
||||
Это покажет все сетевые интерфейсы и их IP-адреса. Убедитесь, что `192.168.89.18` действительно существует.
|
||||
|
||||
### 2. Проверьте тестовое подключение
|
||||
|
||||
В DevTools выполните:
|
||||
|
||||
```javascript
|
||||
window.testConnection()
|
||||
```
|
||||
|
||||
Это протестирует подключение к `192.168.89.1:2655` с использованием gateway `192.168.89.18`.
|
||||
|
||||
### 3. Проверьте логи в консоли main процесса
|
||||
|
||||
При выполнении простукивания должны появиться логи:
|
||||
|
||||
```
|
||||
Binding socket to localAddress 192.168.89.18 to bypass VPN/tunnel
|
||||
TCP connected from 192.168.89.18:XXXXX to 192.168.89.1:2655
|
||||
```
|
||||
|
||||
### 4. Проверьте системные маршруты
|
||||
|
||||
Выполните в терминале:
|
||||
|
||||
```bash
|
||||
# Показать все маршруты
|
||||
ip route show
|
||||
|
||||
# Показать маршрут к конкретной цели
|
||||
ip route get 192.168.89.1
|
||||
|
||||
# Показать интерфейсы
|
||||
ip addr show
|
||||
```
|
||||
|
||||
## Возможные проблемы и решения
|
||||
|
||||
### Проблема 1: IP-адрес не существует
|
||||
**Симптом**: Ошибка "EADDRNOTAVAIL"
|
||||
**Решение**: Убедитесь, что `192.168.89.18` действительно привязан к интерфейсу
|
||||
|
||||
### Проблема 2: Нет маршрута к цели
|
||||
**Симптом**: Ошибка "ENETUNREACH" или таймаут
|
||||
**Решение**: Проверьте, что есть маршрут к `192.168.89.1` через интерфейс с IP `192.168.89.18`
|
||||
|
||||
### Проблема 3: WireGuard перехватывает трафик
|
||||
**Симптом**: Трафик все еще идет через туннель
|
||||
**Решение**:
|
||||
1. Проверьте таблицу маршрутизации WireGuard
|
||||
2. Убедитесь, что `192.168.89.0/24` не входит в AllowedIPs WireGuard
|
||||
3. Проверьте приоритет маршрутов
|
||||
|
||||
### Проблема 4: Неправильное использование bind() для TCP
|
||||
**Симптом**: Ошибка "socket.bind is not a function"
|
||||
**Решение**: TCP сокеты НЕ поддерживают `bind()`. Используйте `localAddress` в `connect()`:
|
||||
```javascript
|
||||
// Неправильно (для TCP):
|
||||
socket.bind(0, gateway);
|
||||
socket.connect(port, host);
|
||||
|
||||
// Правильно (для TCP):
|
||||
socket.connect({
|
||||
port: port,
|
||||
host: host,
|
||||
localAddress: gateway
|
||||
});
|
||||
```
|
||||
|
||||
## Альтернативные подходы
|
||||
|
||||
### 1. Использование SO_BINDTODEVICE (Linux)
|
||||
Если доступно, можно привязать сокет к конкретному интерфейсу:
|
||||
|
||||
```javascript
|
||||
// Требует root права
|
||||
socket.bind(0, '192.168.89.18');
|
||||
socket._handle.setsockopt(socket._handle.constructor.SOL_SOCKET, 25, 'eth0'); // SO_BINDTODEVICE
|
||||
```
|
||||
|
||||
### 2. Использование netstat для проверки
|
||||
|
||||
```bash
|
||||
# Мониторинг активных соединений
|
||||
netstat -an | grep 192.168.89.1
|
||||
|
||||
# Мониторинг с tcpdump
|
||||
sudo tcpdump -i any -n host 192.168.89.1
|
||||
```
|
||||
|
||||
### 3. Проверка через ss
|
||||
|
||||
```bash
|
||||
# Показать активные соединения
|
||||
ss -tuln | grep 192.168.89.1
|
||||
|
||||
# Показать соединения с конкретным локальным IP
|
||||
ss -tuln src 192.168.89.18
|
||||
```
|
||||
|
||||
## Тестирование
|
||||
|
||||
### Шаг 1: Проверьте интерфейсы
|
||||
|
||||
```javascript
|
||||
window.testNetworkInterfaces()
|
||||
```
|
||||
|
||||
### Шаг 2: Проверьте тестовое подключение
|
||||
|
||||
```javascript
|
||||
window.testConnection()
|
||||
```
|
||||
|
||||
### Шаг 3: Выполните реальное простукивание
|
||||
|
||||
```json
|
||||
{
|
||||
"apiBase": "internal",
|
||||
"gateway": "192.168.89.18",
|
||||
"inlineTargets": "tcp:192.168.89.1:2655",
|
||||
"delay": "2s"
|
||||
}
|
||||
```
|
||||
|
||||
### Шаг 4: Проверьте логи
|
||||
В консоли main процесса должны быть:
|
||||
|
||||
```
|
||||
Using localAddress 192.168.89.18 to bypass VPN/tunnel
|
||||
Knocking TCP 192.168.89.1:2655 via 192.168.89.18
|
||||
TCP connected from 192.168.89.18:XXXXX to 192.168.89.1:2655
|
||||
```
|
||||
|
||||
## Ожидаемые результаты
|
||||
|
||||
### Успешный обход VPN:
|
||||
- Локальный IP в логах: `192.168.89.18`
|
||||
- Подключение успешно
|
||||
- На шлюзе `192.168.89.1` видны пакеты от `192.168.89.18`
|
||||
|
||||
### Неуспешный обход VPN:
|
||||
- Локальный IP в логах: IP туннеля WireGuard
|
||||
- Подключение может быть успешным, но через туннель
|
||||
- На шлюзе `192.168.89.1` НЕ видны пакеты от `192.168.89.18`
|
||||
|
||||
## Дополнительная диагностика
|
||||
|
||||
### Проверка WireGuard конфигурации
|
||||
|
||||
```bash
|
||||
# Показать статус WireGuard
|
||||
sudo wg show
|
||||
|
||||
# Показать маршруты WireGuard
|
||||
ip route show table 51820 # или другой номер таблицы
|
||||
```
|
||||
|
||||
### Проверка таблицы маршрутизации
|
||||
|
||||
```bash
|
||||
# Показать все таблицы маршрутизации
|
||||
ip rule show
|
||||
|
||||
# Показать маршруты в конкретной таблице
|
||||
ip route show table main
|
||||
ip route show table local
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Важно**: Если диагностика показывает, что `bind()` работает, но трафик все еще идет через VPN, проблема может быть в настройках WireGuard или системной маршрутизации, а не в коде приложения.
|
Reference in New Issue
Block a user