Создание self-hosted аналога ngrok на Go с Claude Code за 10 часов | AiManual
AiManual Logo Ai / Manual.
29 Дек 2025 Инструмент

Как я написал свой аналог ngrok за 10 часов с помощью Claude Code (Go + yamux)

Пошаговое руководство по созданию собственного туннелирующего решения на Go с использованием yamux и Claude Code. Полный код и сравнение с альтернативами.

Почему я решил создать свой ngrok

В последнее время многие разработчики сталкиваются с проблемой блокировок внешних сервисов туннелирования. Популярные решения вроде ngrok, хотя и удобны, имеют ограничения в бесплатных тарифах и иногда недоступны в определенных регионах. Мне нужно было простое решение для тестирования веб-приложений на локальной машине с доступом из интернета, но без зависимости от сторонних сервисов.

💡
Идея пришла после того, как я попробовал Claude Code как инструмент для работы с локальными LLM и понял, что современные AI-ассистенты могут значительно ускорить разработку сложных сетевых приложений.

Архитектура решения

Мой self-hosted аналог ngrok состоит из двух компонентов:

  • Клиент — запускается на локальной машине, устанавливает постоянное соединение с сервером и перенаправляет трафик
  • Сервер — работает на VPS с публичным IP, принимает входящие соединения и перенаправляет их клиенту

Для мультиплексирования соединений (несколько запросов через одно TCP-соединение) я использовал библиотеку yamux, которая реализует протокол, похожий на HTTP/2, но для произвольных потоков данных.

1 Настройка проекта и зависимостей

Сначала я создал структуру проекта и определил зависимости в go.mod:

module myngrok

go 1.21

require (
    github.com/hashicorp/yamux v0.1.1
    github.com/spf13/cobra v1.8.0
)

2 Реализация серверной части

Сервер слушает два порта: один для управления (установка туннеля), другой для пользовательского трафика:

package main

import (
    "fmt"
    "io"
    "net"
    "sync"
    
    "github.com/hashicorp/yamux"
)

type TunnelServer struct {
    controlPort string
    publicPort  string
    tunnels     map[string]*TunnelSession
    mu          sync.RWMutex
}

type TunnelSession struct {
    session *yamux.Session
    domain  string
}

func (s *TunnelServer) Start() error {
    // Запускаем контрольный порт
    go s.listenControl()
    
    // Запускаем публичный порт
    go s.listenPublic()
    
    return nil
}

func (s *TunnelServer) listenControl() {
    listener, err := net.Listen("tcp", ":"+s.controlPort)
    if err != nil {
        panic(err)
    }
    
    for {
        conn, err := listener.Accept()
        if err != nil {
            continue
        }
        go s.handleControl(conn)
    }
}

3 Реализация клиента

Клиент подключается к серверу и создает туннель для локального сервиса:

package main

import (
    "fmt"
    "io"
    "net"
    
    "github.com/hashicorp/yamux"
)

type TunnelClient struct {
    serverAddr string
    localAddr  string
    subdomain  string
}

func (c *TunnelClient) Start() error {
    // Подключаемся к серверу
    conn, err := net.Dial("tcp", c.serverAddr)
    if err != nil {
        return err
    }
    
    // Создаем yamux сессию
    session, err := yamux.Client(conn, nil)
    if err != nil {
        return err
    }
    
    // Регистрируем туннель
    if err := c.registerTunnel(session); err != nil {
        return err
    }
    
    // Запускаем проксирование
    return c.proxyTraffic(session)
}

func (c *TunnelClient) proxyTraffic(session *yamux.Session) error {
    for {
        // Принимаем входящий поток от сервера
        stream, err := session.Accept()
        if err != nil {
            return err
        }
        
        go func() {
            // Подключаемся к локальному сервису
            localConn, err := net.Dial("tcp", c.localAddr)
            if err != nil {
                stream.Close()
                return
            }
            
            // Проксируем данные в обе стороны
            go io.Copy(stream, localConn)
            io.Copy(localConn, stream)
        }()
    }
}

Как Claude Code помог в разработке

Claude Code оказался невероятно полезным на нескольких этапах:

  1. Проектирование архитектуры — помог выбрать правильные библиотеки и спроектировать взаимодействие компонентов
  2. Работа с yamux — объяснил тонкости работы с мультиплексированием и помог избежать распространенных ошибок
  3. Обработка ошибок — предложил robust-решения для обработки сетевых сбоев и переподключений
  4. Оптимизация — помог написать эффективный код для проксирования данных с минимальной задержкой

Важно: Хотя AI-ассистенты вроде Claude Code значительно ускоряют разработку, важно понимать код, который они генерируют. Всегда проверяйте безопасность сетевого кода, особенно когда дело касается туннелирования трафика.

Сравнение с альтернативами

Решение Self-hosted Сложность настройки Производительность Особенности
Оригинальный ngrok Нет Очень низкая Высокая Облачный сервис, ограничения в бесплатной версии
Cloudflare Tunnel Частично Средняя Очень высокая Интеграция с Cloudflare, бесплатный тариф
bore (Rust) Да Низкая Высокая Простой, но меньше возможностей
Наше решение Да Средняя Высокая Полный контроль, можно модифицировать под свои нужды

Примеры использования

Вот как использовать готовое решение:

Запуск сервера на VPS:

# Компилируем сервер
GOOS=linux GOARCH=amd64 go build -o myngrok-server ./cmd/server

# Копируем на VPS
scp myngrok-server user@vps.example.com:/opt/myngrok/

# Запускаем на VPS
ssh user@vps.example.com
cd /opt/myngrok
./myngrok-server --control-port 4444 --public-port 80

Запуск клиента на локальной машине:

# Компилируем клиент
go build -o myngrok-client ./cmd/client

# Запускаем туннель для локального веб-сервера
./myngrok-client \
  --server vps.example.com:4444 \
  --local localhost:3000 \
  --subdomain myapp

После этого ваш локальный сервер на порту 3000 будет доступен по адресу http://myapp.vps.example.com (если настроить DNS или использовать IP напрямую).

Кому подойдет это решение

Мой self-hosted аналог ngrok идеально подойдет:

  • Разработчикам, которые хотят тестировать веб-приложения с внешним доступом без облачных сервисов
  • Компаниям с требованиями к безопасности, которым нельзя использовать сторонние туннелирующие сервисы
  • Энтузиастам, которые хотят понять, как работают инструменты туннелирования изнутри
  • Образовательным проектам — как практический пример сетевого программирования на Go
💡
Если вам интересны другие проекты с использованием AI-ассистентов, посмотрите статью про FlaxeoUI — новый фронтенд для локальной генерации изображений, который тоже был создан с помощью AI-помощников.

Дальнейшее развитие

За 10 часов я создал рабочее ядро, но есть много возможностей для улучшения:

  1. Поддержка HTTPS — добавление TLS для шифрования трафика между клиентом и сервером
  2. Веб-интерфейс — мониторинг активных туннелей и управление через браузер
  3. Аутентификация — добавление токенов для контроля доступа
  4. Метрики — сбор статистики по трафику и подключениям
  5. Поддержка UDP — для туннелирования игровых серверов или VoIP

Предупреждение: При использовании self-hosted решений для туннелирования убедитесь, что вы соблюдаете законодательство вашей страны и политики хостинг-провайдера. Некоторые провайдеры могут ограничивать входящий трафик на определенные порты.

Заключение

Создание собственного аналога ngrok оказалось удивительно доступной задачей благодаря современным инструментам. Go с его богатой стандартной библиотекой и простой моделью конкурентности идеально подходит для сетевых приложений. Claude Code значительно ускорил процесс, помогая с архитектурными решениями и написанием boilerplate-кода.

Полный код проекта доступен в открытом репозитории, и я приглашаю всех желающих его улучшать. Это отличный пример того, как современные разработчики могут создавать сложные сетевые инструменты за считанные часы, комбинируя мощь AI-ассистентов с эффективными языками программирования вроде Go.

Как показывает опыт создания таких инструментов, как LLaMA 3.1 для генерации 3D-мебели, грамотное использование AI-помощников открывает новые возможности для быстрого прототипирования и реализации сложных проектов.