Как открыть веб-интерфейс DSH на удаленном VPS
Веб-интерфейс DSH привязывается к 127.0.0.1:3080, поэтому локальный URL не работает на вашем ноутбуке. Используйте SSH-туннелирование для безопасного доступа к порту 3080.
Почему веб-интерфейс DSH не открывается с вашего ноутбука
Веб-интерфейс DSH привязывается к интерфейсу loopback, поэтому URL, выведенный в вашем терминале, работает только на той машине, которая его сгенерировала. npx @deepseek-ai/dsh web сообщает http://127.0.0.1:3080, а 127.0.0.1 означает «компьютер, считывающий этот адрес» для любого устройства, которое его интерпретирует. Ваш ноутбук считывает его, обращается к собственному интерфейсу loopback и обнаруживает, что там никто не ожидает соединений. Если вас смущает сам адрес, в почему DSH выводит http://127.0.0.1:3080 этот вопрос разобран подробнее. Не меняйте параметры привязки DSH. Вместо этого настройте аутентифицированный путь от вашего ноутбука к адресу loopback на VPS.
Настройка по умолчанию для loopback верна, и все методы на этой странице сохраняют её. За портом 3080 скрывается агент, который выполняет shell-команды на вашем сервере, а веб-интерфейс DSH не содержит страницы входа. Привязка к loopback — единственный механизм контроля доступа в DSH в данном случае: чтобы получить доступ к сокету, вы уже должны иметь shell-доступ к серверу.
К чему привязывается DSH и как это проверить
Согласно README для DeepSeek Harness, проверенному 17 августа 2026 года, npx @deepseek-ai/dsh web «запускает веб-интерфейс, который по умолчанию доступен по адресу http://127.0.0.1:3080». В справочнике CLI того же репозитория --port <num> указан как параметр переопределения со значением по умолчанию 3080, а --host <addr> — как параметр, который «преднамеренно отклоняет 0.0.0.0». DSH находится на стадии предварительного просмотра для разработчиков, и в README заглавными буквами указано, что возможны изменения, нарушающие обратную совместимость. Подтвердите оба значения в своей установке, не полагаясь на документацию, включая эту страницу.
dsh --profile web --dump-config
ss -ltnp | grep 3080Команда --dump-config выводит итоговое дерево конфигурации без запуска агента, поэтому она показывает хост и порт, которые применяются после всех уровней патчей. Команда ss показывает, что именно прослушивает порты в данный момент. Корректная строка выглядит так:
LISTEN 0 511 127.0.0.1:3080 0.0.0.0:* users:(("node",pid=8123,fd=24))Смотрите только на адрес перед двоеточием. 127.0.0.1:3080 означает привязку только к loopback, что и требуется. 0.0.0.0:3080 означает все сетевые интерфейсы на сервере, включая публичный. Если ss не выводит ни одной строки, значит, DSH не запущен, и никакой туннель не поможет, пока сервис не заработает. Процесс запуска описан в разделе установка и запуск DeepSeek Harness на VPS.
Флаги применяются только к одному запуску. Чтобы сделать значение постоянным, отредактируйте уровень патча профиля. Команда dsh --profile <name> загружает профиль из $DSH_HOME/profiles/<name>, при этом уровни накладываются в следующем порядке: патчи пакета, собственный файл cordis.patch.yml профиля, файл $DSH_HOME/cordis.patch.yml в домашней директории, а затем любой оверлей --patch. Настройки прослушивания находятся в плагине @deepseek-ai/dsh-host-webserver под именами host и port. Перед редактированием прочитайте итоговое дерево конфигурации с помощью --dump-config, так как веб-профиль при первом запуске создается из поставляемого шаблона, а структура этого шаблона в текущей версии постоянно меняется.
Второй барьер: проверка доверия /api
Доставка пакетов на порт 3080 — это лишь половина дела. В DSH предусмотрена вторая проверка, которая вызывает сбивающую с толку ошибку: страница загружается, макет отображается, но ничего не работает.
Плагин @deepseek-ai/dsh-client-connection содержит настройку trustedHosts, которая в документации описана как «Авторитетные источники (Authorities), обслуживаемые данным развертыванием помимо loopback: точное значение host:port или имя без порта host, соответствующее любому порту». Этот барьер отклоняет любой запрос /api, если его заголовок Host не является loopback и не указан в списке. Браузер сообщает об отказе следующим образом:
transport failure for /api/host.describe: HTTP 403Поэтому любой прокси, установленный перед DSH, должен объявлять то имя, которое пользователь вводит в браузере. Интерфейс командной строки принимает --trusted-host <authority>, и этот параметр можно указывать несколько раз:
dsh web --trusted-host dsh.example.com --trusted-host dsh.example.com:8443Барьер сравнивает заголовок Host как обычную строку, поэтому выше приведены оба варианта написания. Для него dsh.example.com и dsh.example.com:8443 — это два разных авторитетных источника, так же как localhost:3080 и 127.0.0.1:3080. Ошибка 403, которую вы не можете объяснить, почти всегда вызвана различием в написании между URL в адресной строке и вашим списком доверенных имен.
Этот барьер является проверкой источника (origin check). Это не аутентификация. Браузеру самим браузером запрещено устанавливать заголовок Host, поэтому данная проверка действительно не позволяет странице на другом сайте управлять вашим агентом. Любой клиент, не являющийся браузером, записывает этот заголовок свободно. Рассматривайте trustedHosts как настройку совместимости для прокси, а не как средство обеспечения безопасности.
Вариант 1: локальное перенаправление SSH
Используйте этот метод в первую очередь, так как он не требует внесения изменений в настройки сервера.
ssh -N -L 127.0.0.1:3080:127.0.0.1:3080 you@your-vps-L открывает порт для прослушивания на вашем ноутбуке и перенаправляет каждое соединение на 127.0.0.1:3080, как оно разрешается на VPS. -N означает «не выполнять удаленную команду», поэтому сессия поддерживает только перенаправление и ничего более. Указание локальной стороны как 127.0.0.1:3080, а не просто 3080, сделано намеренно: это привязывает слушающий порт на стороне ноутбука к интерфейсу loopback, поэтому настройка GatewayPorts в конфигурации вашего SSH-клиента не сможет незаметно опубликовать агент в сети, к которой вы подключены.
Теперь откройте http://localhost:3080 в браузере. Здесь автоматически работают две вещи. Заголовок Host является доверенным источником (loopback), поэтому проверка /api проходит без какой-либо дополнительной настройки. Кроме того, браузеры рассматривают http://localhost как безопасный контекст, что важно, так как веб-приложение DSH вызывает crypto.randomUUID() во время загрузки, а браузеры предоставляют эту функцию только через HTTPS или при обращении с источника loopback.
Добавьте -f, чтобы ssh переводил процесс в фоновый режим после установки перенаправления:
ssh -f -N -L 127.0.0.1:3080:127.0.0.1:3080 you@your-vpsЧестный компромисс. Никакие новые службы не прослушивают публичные адреса, правила межсетевого экрана не меняются, поэтому это минимально возможное увеличение поверхности атаки. Весь контроль доступа осуществляется через конфигурацию SSH, что делает SSH только по ключам с отключенным входом по паролю обязательным условием, а не просто полезным дополнением. Недостаток заключается в том, что туннель привязан к одной клиентской сессии. Он разрывается при переходе ноутбука в спящий режим, его нужно перезапускать вручную, и он не обеспечивает доступ с мобильных устройств.
Вариант 2: Использование Tailscale serve на VPS
Tailscale создает частную сеть между вашими устройствами. Установка Tailscale на VPS предоставляет этой машине адрес 100.x.y.z, маршрутизация к которому доступна только вашим устройствам.
Простого подключения к сети недостаточно, и именно здесь часто возникают трудности. DSH не прослушивает адрес 100.x.y.z, так как он привязан к loopback. Браузер, направленный на http://100.x.y.z:3080, получит ошибку connection refused, поскольку на этом адресе нет открытого сокета.
tailscale serve — это компонент, который связывает эти два элемента. Он работает на VPS, принимает трафик из вашей частной сети и проксирует его на локальный адрес:
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up
sudo tailscale serve --bg --https=443 localhost:3080
tailscale serve statustailscale serve status выводит URL в формате https://<machine>.<tailnet>.ts.net/. Запустите DSH с доверием к этому имени, иначе /api вернет ошибку 403, как описано выше:
dsh web --trusted-host your-vps.your-tailnet.ts.netЭто также решает проблему с безопасным контекстом (secure context). Tailscale терминирует реальный HTTPS с помощью сертификата, выданного для этого имени, поэтому crypto.randomUUID() становится доступным, и интерфейс загружается в мобильном браузере. Трафик не выходит в публичный интернет, так как serve публикует сервис только внутри вашей частной сети.
Не используйте здесь tailscale funnel. Это команда из того же семейства, но предназначенная для публичного интернета; она разместит агент с доступом к оболочке (shell) на имени хоста, которое может разрешить любой желающий. Прочитайте как serve удерживает трафик внутри вашей tailnet, в то время как funnel публикует его, прежде чем вводить любую из этих команд. Информацию о мобильной версии этой настройки см. в доступе к self-hosted агенту с телефона.
Компромиссом является наличие зависимости. Каждое устройство, которому нужен интерфейс, должно быть подключено к сети, а сервер координации, которым вы не управляете, определяет состав участников. Если это неприемлемо, self-hosted сервер управления Headscale использует тот же протокол на оборудовании, которое принадлежит вам.
Вариант 3: аутентифицированный TLS reverse proxy
Используйте этот метод, если браузер не может подключиться к частной сети, например, при работе с компьютера, который вы не администрируете. Вы публикуете имя хоста в Интернете, поэтому аутентификация должна быть реальной и выполняться на стороне прокси. DSH не предоставляет встроенных средств аутентификации.
TLS означает transport layer security, шифрование, лежащее в основе https://. Направьте nginx на порт loopback и установите пароль перед доступом. Блок map относится к контексту http, поэтому вынесите его в отдельный файл в /etc/nginx/conf.d/:
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}Затем настройте сам сайт:
server {
listen 443 ssl;
server_name dsh.example.com;
ssl_certificate /etc/letsencrypt/live/dsh.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/dsh.example.com/privkey.pem;
auth_basic "dsh";
auth_basic_user_file /etc/nginx/dsh.htpasswd;
location / {
proxy_pass http://127.0.0.1:3080;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 3600s;
proxy_buffering off;
}
}Создайте файл паролей, протестируйте конфигурацию и перезагрузите:
sudo apt install -y apache2-utils
sudo htpasswd -c /etc/nginx/dsh.htpasswd you
sudo nginx -t && sudo systemctl reload nginxnginx -t должен вывести syntax is ok и test is successful. Если вместо этого выводится ошибка, nginx продолжит использовать старую конфигурацию, так что ничего не сломается. Затем запустите DSH с доверенным публичным именем, так как proxy_set_header Host $host пересылает dsh.example.com, и в противном случае защитный механизм отклонит запрос:
dsh web --trusted-host dsh.example.comДве директивы здесь критически важны. Заголовки Upgrade и Connection поддерживают долгоживущее соединение интерфейса открытым; без них nginx закрывает его, из-за чего интерфейс постоянно переподключается, отображая устаревшие данные. proxy_read_timeout 3600s предотвращает принудительное завершение работы nginx, если выполнение агента длится дольше стандартных 60 секунд. Обоснование остальных параметров приведено в разборе конфигурации nginx reverse proxy по директивам, а выбор самого прокси — в сравнении nginx с Caddy и Traefik.
Чётко осознавайте, что даёт basic auth. Он защищает от автоматических сканеров и гораздо лучше, чем открытый порт. Однако это один общий пароль перед командной оболочкой, без второго фактора и возможности отозвать доступ для конкретного пользователя. Любой, кто узнает пароль, сможет выполнять команды от имени пользователя, под которым запущен DSH. Если доступ нужен нескольким людям, используйте полноценный прокси идентификации и ознакомьтесь с правилами безопасности при запуске агента для программирования на VPS, прежде чем расширять доступ дальше.
Что произойдет, если привязаться к 0.0.0.0 и открыть порт
Очевидный путь — изменить привязку и открыть порт в межсетевом экране. DSH блокирует первую часть. --host <addr> намеренно отклоняет 0.0.0.0, а в документации плагина @deepseek-ai/dsh-host-webserver указано, что host принимает только 127.0.0.1 или 0.0.0.0. Таким образом, существует одно поддерживаемое значение и второе, которое CLI отвергает. Существуют сторонние плагины, которые отключают эту проверку. Они поставляются с предупреждениями, и эти предупреждения обоснованы. Тот же уровень плагинов гораздо полезнее использовать в обратном направлении, где лимиты расходов и правила разрешений для инструментов ограничивают действия агента, а не расширяют область его прослушивания.
Вот что на самом деле дает принудительная настройка. Порт 3080 отвечает на вашем публичном IP-адресе. Страницы входа нет. Защита /api проверяет заголовок Host, который любой небраузерный клиент может задать самостоятельно, поэтому указание вашего адреса в trustedHosts ничего не меняет для злоумышленника, у которого есть curl. Вы опубликовали агента, который выполняет shell-команды от имени вашего пользователя, вместе с учетными данными из $DSH_HOME, где DSH хранит профили и API-ключи. Это удаленное выполнение кода на вашем сервере с привязкой к вашему счету у провайдера. Сканеры постоянно проверяют всё адресное пространство, поэтому скрытый IP-адрес не является защитой. Любой секрет, который может прочитать агент, будет скомпрометирован вместе с доступом к shell.
Все описанные выше методы существуют для того, чтобы вам никогда не пришлось этого делать. SSH-проброс — стандартный и правильный вариант для одного пользователя с одним ноутбуком. Переходите на tailscale serve, если используется телефон или требуется настоящий сертификат. Используйте публичный reverse proxy в том случае, если к интерфейсу должен обращаться браузер, которым вы не управляете, при условии наличия перед ним полноценной аутентификации.
FAQ
Почему http://127.0.0.1:3080 не открывается с моего ноутбука?
Потому что 127.0.0.1 означает «машина, считывающая этот адрес». DSH выводит его на VPS, где это корректно. Ваш ноутбук считывает ту же строку и обращается к собственному loopback-интерфейсу, где ничего не запущено. Проверьте состояние сервера с помощью ss -ltnp | grep 3080 на VPS. Строка с 127.0.0.1:3080 означает, что DSH запущен и намеренно привязан к loopback. Вам нужен туннель или прокси, а не другой адрес привязки.
Могу ли я запустить dsh web с --host 0.0.0.0?
Нет. Справочник CLI репозитория, проверенный 17 августа 2026 года, документирует --host <addr> как переопределение, которое намеренно отклоняет 0.0.0.0. Причина в том, что DSH выполняет shell-команды и не имеет страницы входа, поэтому привязка ко всем интерфейсам делает неаутентифицированный shell доступным по вашему публичному IP-адресу. Пользовательские патчи убирают эту проверку. Если вы примените такой патч, ответственность за firewall и аутентификацию, которую DSH не предоставляет, ложится на вас.
Почему все вызовы /api возвращают HTTP 403 при работе через reverse proxy?
Механизм защиты /api отклоняет любой запрос, если заголовок Host не является loopback-адресом или не указан в trustedHosts. При работе через прокси этот заголовок содержит ваше публичное имя, поэтому защита отклоняет запрос, а браузер фиксирует transport failure for /api/host.describe: HTTP 403. Запустите DSH с параметром --trusted-host <your name> и в точности соблюдайте написание, включая порт, если он есть в вашем URL, так как сравнение выполняется как сопоставление строковых литералов.
Почему интерфейс DSH загружается, но не завершает инициализацию при работе по обычному HTTP?
Веб-приложение вызывает crypto.randomUUID() во время загрузки, а браузеры предоставляют эту функцию только в безопасном контексте: HTTPS или origin типа loopback, например http://localhost. При передаче по обычному HTTP с голого IP-адреса функция не определена, поэтому зависящие от неё вызовы выдают ошибку, и интерфейс не заполняется данными. Обслуживание через HTTPS с помощью tailscale serve или TLS reverse proxy устраняет проблему; также помогает доступ через SSH-форвард на http://localhost.
Какой метод следует использовать разработчику-одиночке?
SSH local forward. Он не требует изменений на сервере, не меняет правила firewall и использует SSH-ключ, которому вы уже доверяете. Выполните ssh -f -N -L 127.0.0.1:3080:127.0.0.1:3080 you@your-vps, затем откройте http://localhost:3080. Переходите к tailscale serve, если вам нужен интерфейс на телефоне или доступ, который сохраняется после перехода ноутбука в спящий режим.