BorisovAI
Все потоки
Новая функцияborisovai-admin16 заметок

Разработка: borisovai-admin

Хроника разработки проекта borisovai-admin. Основные направления: новые функции, исправления багов, общее. Всего 19 записей в потоке. Последние темы: Как мы починили админку Authelia: от отключённого пользователя до полного управления; Как я загрузил 19 ГБ моделей для боевого сервера; От SQLite к Kubernetes: как выбрать стек для сервера.

#claude#ai#git#security
Начало
Завершён
1
Новая функция

Параллелизм в действии: как я за один день собрал туннельное решение для borisovai-admin

Когда ты работаешь над проектом borisovai-admin, появляются моменты, когда нужно сделать сразу много однотипной работы. У меня была ровно такая задача: реализовать систему frp tunneling — нужно было создать четыре новых файла, переделать четыре существующих и не запутаться в деталях. Обычно такие дни начинаются с вопроса: «С чего начать?» Я выбрал ответ: со всем одновременно.

Задача: соединить машины, не ломая архитектуру

Проблема была в том, что нам нужна была система туннелирования для соединения удалённых серверов через контрольный канал. frp (fast reverse proxy) — отличный инструмент для этого, но его нужно было интегрировать в существующую инфраструктуру. При этом всё должно было работать параллельно с Traefik и не конфликтовать с уже развёрнутой системой.

Первым делом я понял: это не может быть один огромный рефакторинг. Нужен был план, разбитый на логические части.

Что я создал: четыре ключевых компонента

install-frps.sh стал сердцем всей системы — 210-строчный скрипт установки, который берёт на себя всю грязную работу: скачивает бинарник, генерирует конфиг, создаёт systemd unit, настраивает DNS и firewall. Это не просто скрипт — это полноценный конвейер, который должен работать на production-сервере без человеческого вмешательства.

Параллельно я подготовил шаблон frpc.toml для Windows-клиентов, чтобы разработчик мог просто заполнить пару полей и запустить. И конечно, systemd unit и Traefik конфиг для основного сервера — чтобы всё было pre-built и готово к развёртыванию.

Неожиданный момент: три порта вместо одного

Когда я раскладывал архитектуру по полочкам, выяснилось, что frp использует три разных порта: 17420 (control channel), 17480 (HTTP vhost за Traefik), 17490 (dashboard только для localhost). Первый импульс был открыть всё в firewall, но стоп — нужна была безопасность. В итоге получилось изящное решение: контрольный канал открыт, vhost спрятан за Traefik с wildcard HostRegexp, dashboard доступен только локально.

Интересный факт про reverse proxy

Знаете, что смешного в reverse proxy? Обычный proxy скрывает клиента (вы видите proxy, а не пользователя). Reverse proxy делает противоположное — скрывает сервер (клиент видит публичный адрес, а не реальную машину). frp — это именно reverse proxy в его самом полезном проявлении для распределённых систем.

Что дальше

В итоге я обновил четыре существующих файла, добавил скрипт установки в upload-процесс, расширил конфиг примеров четырьмя новыми полями. Теперь разработчик может развернуть frps-сервер одной командой и подключить Windows-клиент без боли.

Главный урок дня: когда задача кажется большой, попробуй разделить её не на последовательные шаги, а на параллельные потоки. Четыре файла создавались одновременно в моей голове — и в итоге собрались в цельную систему, которая просто работает.

😄 Что общего у Bun и подростка? Оба непредсказуемы и требуют постоянного внимания.

#claude#ai#javascript#api
Читать далее
2
Новая функция

Система туннелей для admin-панели: от идеи к функциональности

Когда работаешь над borisovai-admin — панелью управления инфраструктурой — рано или поздно встречаешься с проблемой удалённого доступа к сервисам. Задача была классической: нужно добавить в админ-панель возможность управления FRP-туннелями (Fast Reverse Proxy). Это скромные 5 шагов, которые, как выяснилось, требовали куда больше внимания к деталям, чем казалось изначально.

Завязка простая. Пользователь должен видеть, какие туннели сейчас активны, какой статус у FRP-сервера, и уметь сгенерировать конфиг для клиентской части. Всё это через красивый интерфейс прямо в админ-панели. Типичный запрос, но именно в таких задачах проявляются все неожиданные подводные камни.

Первым делом обновил навигацию — добавил ссылку “Туннели” во все четыре HTML-файла (index.html, tokens.html, projects.html, dns.html). Казалось бы, мелочь, но когда навигация должна быть идентична на каждой странице, нужно быть аккуратнее: всего одна опечатка — и юзер потеряется. Все ссылки расположены на одинаковых позициях в строках 195–238, что удобно для поддержки.

Потом столкнулся с архитектурой бэкенда. В server.js добавил две вспомогательные функции: readFrpsConfig для чтения конфигурации FRP-сервера и frpsDashboardRequest для безопасного запроса данных к dashboard FRP. Это не просто HTTP-вызовы — это минимальная абстракция, которая упрощает тестирование и повторное использование. Далее идут четыре GET-эндпоинта:

  1. Статус FRP-сервера (жив ли?)
  2. Список активных туннелей с метаинформацией
  3. Текущая конфигурация в JSON
  4. Генерация frpc.toml — клиентского конфига, который можно скачать одной кнопкой

Неожиданно выяснилось — FRP-сервер нужно ещё установить и запустить. Поэтому обновил скрипт install-all.sh: добавил FRP как опциональный компонент установки. Это важно, потому что не все инсталляции нуждаются в туннелях, а если выбрал — получишь полный стек.

На фронте создал новую страницу tunnels.html с тремя блоками:

  • Карточка статуса — простая информация о том, работает ли FRP
  • Список туннелей с авто-обновлением каждые 10 секунд (классический полинг, проще чем WebSocket для такого масштаба)
  • Генератор клиентского конфига — вводишь параметры, видишь готовый frpc.toml

Интересный факт про FRP: это вообще проект из Китая (автор — fatedier), но в экосистеме DevOps он стал де-факто стандартом для туннелирования благодаря простоте и надёжности. Многие не знают, что FRP может работать не только как reverse proxy, но и как VPN, и даже как load balancer — просто конфиг нужен другой.

В итоге получилась полнофункциональная система управления туннелями, интегрированная в админ-панель. Теперь администратор может с одного места видеть всё: какие туннели работают, генерировать конфиги для новых серверов, проверять статус. Документация пошла в CLAUDE.md, чтобы следующий разработчик не переобнаруживал велосипед.

Главный урок: даже в мелких фичах типа “добавить ссылку в навигацию” скрывается целая архитектура. Лучше потратить час на планирование, чем потом переделывать интеграцию FRP.

😄 FRP — это когда твой сервер вдруг получает способность ходить в гости через NAT, как путник с волшебным клаком.

#claude#ai#javascript#api#security
Читать далее
3
Общее

Туннели, фронт и конфиги: как мы выстроили инфраструктуру для нескольких машин

Проект borisovai-admin достиг того момента, когда одного сервера стало недостаточно. Нужно было управлять несколькими машинами, пробрасывать сетевые соединения между ними и всё это как-то красиво завернуть для пользователя. История о том, как мы за один вечер построили систему туннелей с веб-интерфейсом и потом долго разбирались с таймаутами Traefik.

Начало: туннели нужны вчера

Задача выглядела просто: нужен интерфейс для управления туннелями между машинами. Но просто никогда не бывает, правда? Первое, что я сделал — запустил фреймворк frp (Fast Reverse Proxy). Это отличный инструмент для туннелирования, когда основной сервер скрыт за NAT или брандмауэром. Быстрый, надёжный, с минимальными зависимостями.

Спроектировал простую UI в tunnels.html — список активных туннелей, кнопки для создания новых, удаления старых. Ничего сложного, но эффективно. На бэкенде добавил 5 API endpoints в server.js для управления состоянием туннелей. Параллельно обновил скрипты инсталляции: install-all.sh и отдельный install-frps.sh для установки FRP сервера, плюс frpc-template для конфигурации клиентов на каждой машине.

Главное — добавил навигационную ссылку «Туннели» на все страницы админ-панели. Мелочь, но юзабилити выросла в разы.

Неожиданный враг: Traefik и его таймауты

Вроде всё работало, но потом начали падать большие файлы при скачивании через GitLab. Проблема: Traefik по умолчанию использует достаточно агрессивные таймауты. Стоило большому файлу загружаться более пары минут — и соединение рубилось.

Пришлось менять конфигурацию Traefik: установил readTimeout в 600 секунд (10 минут) и добавил специальный serversTransport именно для GitLab. Создал скрипт configure-traefik.sh, который генерирует две динамические конфигурации: gitlab-buffering и serversTransport. Теперь файлы загружаются спокойно, даже если это 500 мегабайт архива.

Пока делал это, понял одно

Знаете, что самое интересное в Traefik? Это микросервис-балансировщик, который любит называться облегчённым, но на практике требует огромного внимания к деталям. Неправильный таймаут — и ваше приложение выглядит медленным. Правильный — и всё летает. Это как тюнинг двигателя: одна скрепка в нужном месте, и мир меняется.

Реорганизация и масштабирование

Пока занимался инфраструктурой, понял, что документация разрослась и стала трудна в навигации. Переделал структуру docs/ под новые реальности: разделил на agents/, dns/, plans/, setup/, troubleshooting/. Каждая папка отвечает за свой кусок практики.

Добавил в config/contabo-sm-139/ полный набор конфигураций конкретного сервера (traefik, systemd, mailu, gitlab) и обновил upload-single-machine.sh для поддержки загрузки этих конфигов. Теперь новую машину можно развернуть, не пересматривая весь интернет.

Что получилось в итоге

За вечер родилась полноценная система управления туннелями с приличным интерфейсом, автоматизацией и нормальной документацией. Проект теперь легко масштабируется на новые серверы. Плюс узнал, что Traefik — это не просто балансировщик, а целая философия правильной конфигурации микросервисов.

Дальше в планах: расширение аналитики для туннелей, SSO интеграция и лучший мониторинг сетевых соединений. Но это уже другая история.

😄 Разработчик: «Я знаю Traefik». HR: «На каком уровне?». Разработчик: «На уровне стака StackOverflow с пятью вкладками одновременно».

#git#commit#javascript#api#security
Читать далее
4
Новая функция

Authelia в боевых условиях: как я собрал Single Sign-On за выходные

Задача была амбициозная: в проекте borisovai-admin нужно было внедрить полноценную систему единой авторизации. На площадке работают несколько приложений — Management UI, n8n, Mailu, и каждое требует свой вход. Кошмар для пользователя и сущее издевательство над принципом DRY. Решение напрашивалось само: Authelia — современный SSO-сервер, который справляется с аутентификацией одной рукой и может интегрироваться практически с чем угодно.

С чего я начал

Первым делом создал install-authelia.sh — полный скрипт установки, который берёт на себя всю рутину: скачивает бинарник, генерирует секреты, прописывает конфиги и регистрирует Authelia как systemd-сервис. Это был ключевой момент — автоматизация означала, что процесс установки можно повторить в три команды без магических танцев с палочкой.

Потом встала задача интеграции с Traefik, который у нас отвечает за маршрутизацию. Здесь нужен был ForwardAuth — middleware, который перехватывает запросы и проверяет, авторизован ли пользователь. Создал authelia.yml с настройкой ForwardAuth для auth.borisovai.ru/tech. Суть простая: любой запрос сначала идёт в Authelia, и если она вас узнала — пропускаем дальше, если нет — отправляем на страницу входа.

Dual-mode, или как угодить двум господам одновременно

Самое интересное началось, когда понадобилось поддержать сразу два способа авторизации. Management UI должна работать и как классическое веб-приложение с сессиями, и как API с Bearer-токенами через OIDC (OpenID Connect). Пришлось написать server.js с логикой, которая проверяет, что именно пришло в запросе: если есть Bearer-токен — валидируем через OIDC, если нет — смотрим на сессию.

Включил в проект express-openid-connect — стандартную библиотеку для интеграции OIDC в Express. Хитрость в том, что Authelia может быть и провайдером OIDC, и middleware ForwardAuth одновременно. Просто берёшь конфиг для OIDC из Management UI, подтягиваешь его в config.json через автоопределение (этим займется install-management-ui.sh), и всё начинает работать как часы.

Неожиданный поворот с logout

Оказалось, что обычный logout в веб-приложении — это не просто удалить cookie. Если вы авторизовались через OIDC, нужно ещё уведомить Authelia, что сессия закончена. Пришлось настроить пять HTML-страниц с поддержкой OIDC redirect: пользователь нажимает logout, приложение отправляет его в Authelia, Authelia убивает сессию и редиректит обратно на страницу выхода. Выглядит просто, но заставляет задуматься о том, как много движущихся частей в современном веб.

Интересный факт: ForwardAuth vs Reverse Proxy Authentication

Знаешь ли ты, что многие разработчики путают эти два подхода? ForwardAuth — это когда сам прокси отправляет запрос на сервер аутентификации. А Reverse Proxy Authentication — это когда сервер приложения полностью отдаёт авторизацию на откуп прокси. Authelia работает с обоими, но ForwardAuth даёт больше контроля — приложение всё равно может принять дополнительные решения на основе данных пользователя.

Итог: от идеи к prod

Всё сложилось в единую систему благодаря интеграции на уровне install-all.sh — компонент INSTALL_AUTHELIA занимает шаг [7.5/10], что означает: это не первый день, но далеко не последний штрих. Management UI теперь умеет сама себя конфигурировать, находя Authelia в сети, подтягивая OIDC-конфиг и автоматически подключаясь.

Главное, чему я научился: SSO — это не просто чёрный ящик, куда ты кидаешь пароли. Это экосистема, где каждый компонент должен понимать друг друга: ForwardAuth, OIDC, сессии, logout. И когда всё это работает вместе, пользователь вводит пароль один раз и может спокойно прыгать между всеми приложениями. Вот это да.

Почему React расстался с разработчиком? Слишком много зависимостей в отношениях 😄

#git#commit#javascript#security
Читать далее
5
Исправление

Когда конфиги кусаются: история про зависимые middleware в Traefik

Проект borisovai-admin — это не просто админ-панель, это целая инфраструктурная система с аутентификацией через Authelia, обратным прокси на Traefik и кучей moving parts, которые должны работать в идеальной гармонии. И вот в один прекрасный день выясняется: когда ты разворачиваешь систему без Authelia, всё падает с ошибкой 502, потому что Traefik мечтательно ищет middleware authelia@file, которого просто нет в конфиге.

Завязка проблемы была в статических конфигах. Мы жёстко прописали ссылку на authelia@file прямо в Traefik-конфигурацию, и это сработало, когда Authelia установлена. Но стоило её отключить или просто не устанавливать — бум, 502 ошибка. Получается, конфиги были сильно связаны с опциональным компонентом. Это классический случай, когда инфраструктурный код требует гибкости.

Решение разбилось на несколько фронтов. Во-первых, пришлось убрать жёсткую ссылку на authelia@file из статических конфигов Traefik — теперь это просто не указывается в базовых настройках. Во-вторых, создали правильную цепочку инициализации:

  • install-authelia.sh теперь сам добавляет authelia@file в config.json и настраивает OIDC при установке Authelia;
  • configure-traefik.sh проверяет переменную AUTHELIA_INSTALLED и условно подключает middleware;
  • deploy-traefik.sh перепроверяет, установлена ли Authelia на сервере, и если да — переустанавливает authelia@file.

Неожиданный бонус обнаружился в install-management-ui.sh — там был неправильный путь к mgmt_client_secret. Исправили по ходу. А authelia.yml вообще выкинули из репозитория, потому что его генерирует сам скрипт установки. Зачем держать в git то, что всегда одинаково генерируется?

Интересный момент про middleware в Docker-сообществе: люди часто забывают, что middleware — это не просто функция, это объект конфигурации, который должен быть определён до использования. Traefik здесь строг: ты не можешь ссылаться на middleware, которого не существует. Это похоже на попытку вызвать функцию, которая не импортирована в Python. Простая ошибка, но очень болезненная в production-системах, потому что приводит к отказу в обслуживании.

Итоговая архитектура получилась намного гибче: система работает как с Authelia, так и без неё, конфиги не лежат мёртвым грузом в репо, а инсталляторы действительно знают, что они делают. Это хороший пример того, как опциональные зависимости требуют условной логики не только в коде приложения, но и в инфраструктурных скриптах.

Главный урок: если компонент опциональный, не прописывай его в статические конфиги. Пусть они туда добавляются динамически при необходимости.

😄 Что будет, если Fedora обретёт сознание? Первым делом она удалит свою документацию.

#git#commit#javascript#security
Читать далее
6
Изменение кода

Запускаем Authelia: логины, пароли и первый вход в админку

Проект borisovai-admin требовал серьёзной работы с аутентификацией. Стояла простая на первый взгляд задача: развернуть Authelia — современный сервер аутентификации и авторизации — и убедиться, что всё работает как надо. Но перед тем как запустить систему в боевых условиях, нужно было разобраться с креденшалами и убедиться, что они безопасно хранятся.

Первым делом я заглянул в скрипт установки install-authelia.sh. Это был не просто набор команд, а целая инструкция по настройке системы с нуля — 400+ строк, описывающих каждый шаг. И там я нашёл ответ на главный вопрос: логин для Authelia — это просто admin, а пароль… вот тут начиналось интересное.

Оказалось, что пароль хранится в двух местах одновременно. В конфиге Authelia (/etc/authelia/users_database.yml) он лежит в виде Argon2-хеша — это криптографический алгоритм хеширования, специально разработанный для защиты паролей от перебора. Но на сервере управления (/etc/management-ui/auth.json) пароль хранится в открытом виде. Логика понятна: Management UI должна иметь возможность проверить, что введён правильный пароль, но хранить его в открытом виде — это классическая дилемма безопасности.

Неожиданно выяснилось, что это не баг, а фича. Разработчики системы сделали так специально: пароль Management UI и пароль администратора Authelia — это один и тот же секрет, синхронизированный между компонентами. Это упрощает управление, но требует осторожности — нужно убедиться, что никто не получит доступ к этим файлам на сервере.

Я закоммитил все необходимые изменения в ветку main (коммит e287a26), и pipeline автоматически задеплоил обновлённые скрипты на продакшн. Теперь, если кому-то понадобится сбросить пароль администратора, достаточно просто зайти на сервер, открыть /etc/management-ui/auth.json и посмотреть текущее значение. Не самый secure способ, но он работает, пока файл лежит в защищённой директории с правильными permissions.

Главный вывод: при работе с аутентификацией нет мелочей. Каждое хранилище пароля — это потенциальная точка входа для атакующего. Argon2 защищает от перебора, но открытые пароли в конфигах требуют ещё более строгого контроля доступа. В идеальном мире мы бы использовали системы управления секретами вроде HashiCorp Vault, но для локального dev-сервера такой подход сойдёт.

Дальше нужно будет настроить интеграцию Authelia с остальными компонентами системы и убедиться, что она не станет узким местом при масштабировании. Но это история для следующего поста.

😄 Что общего у Scala и подростка? Оба непредсказуемы и требуют постоянного внимания.

#claude#ai#python#javascript#security
Читать далее
7
Общее

Authelia: когда админу нужна двухфакторная аутентификация прямо сейчас

Проект borisovai-admin дошёл до критической точки. Система аутентификации Authelia уже поднята, админ успешно залогинился… но дальше — стена. Нужна двухфакторная аутентификация, и нужна сейчас, потому что без 2FA ключи админ-панели будут висеть в открытом доступе.

Первым делом разобрались, что Authelia уже готова работать с TOTP (Time-based One-Time Password). Это удачнее всего — не нужны внешние SMS-сервисы, которые стоят денег и работают как хотят. Просто приложение на телефоне, которое генерирует коды каждые 30 секунд. Google Authenticator, Authy, Bitwarden — все поддерживают этот стандарт.

Система работает просто: админ кликает на красную кнопку METHODS в интерфейсе Authelia, выбирает One-Time Password, получает QR-код и сканирует его своим аутентификатором. Потом вводит первый код для проверки — и готово, 2FA активирована. Ничего сложнее, чем настроить Wi-Fi на новом телефоне.

Но тут всплыл забавный момент. У нас используется notifier: filesystem вместо полноценного SMTP-сервера. Это значит, что все уведомления летят не по почте, а в файл /var/lib/authelia/notifications.txt на сервере. Казалось бы, неудобно, но на самом деле удобнее для локальной разработки — не нужно иметь рабочий почтовый сервис, не нужно ждать письма в спаме. Просто залезь по SSH на машину и прочитай файл. Правда, для production это так не прокатит, но сейчас это даже плюс.

Вот интересный факт про TOTP: стандарт RFC 6238, на котором это работает, разработан в 2011 году и по сути не менялся. Во всех приложениях для аутентификации используется один и тот же алгоритм HMAC-SHA1 — поэтому коды из Authenticator работают и в Authy, и в 1Password, и в Bitwarden. Один стандарт на всех. Это редкость в IT — обычно каждый сервис хочет своё решение. TOTP же стал поистине универсальным языком двухфакторной аутентификации.

Итог: админ просканировал QR-код, ввёл код подтверждения, и теперь каждый вход на admin.borisovai.tech или admin.borisovai.ru требует второго фактора. Брутфорс админ-панели стал значительно сложнее. Следующий шаг — поднять SMTP для нормальных уведомлений и может быть добавить backup-коды на случай, если админ потеряет доступ к аутентификатору. Но это уже совсем другая история.

Разработчик: «У нас есть Authelia, у нас есть TOTP, у нас есть двухфакторная аутентификация». HR: «На каком уровне безопасности?». Разработчик: «На уровне, когда даже я сам не смогу залезть в админ-панель, если потеряю телефон». 😄

#claude#ai#security
Читать далее
8
Обучение

Когда QR-код спрятался в файл: история отладки Authelia

Проект borisovai-admin требовал добавить двухфакторную аутентификацию. Казалось бы, что может быть проще — установили Authelia, настроили по документации, и хотели включить TOTP для повышения безопасности. Но когда тестировщик нажал кнопку «Register device», экран остался чёрным. QR-код просто не появился.

Первые полчаса ушли на классическую отладку: проверка консоли браузера, логов Authelia, конфига. Всё выглядело нормально. Сертификаты в порядке, порты открыты, контейнеры запущены. Но QR так и не появлялся. В какой-то момент возникла идея: а что если Authelia вообще не отправляет уведомление?

Вот тут и вспомнилась одна важная деталь из конфигурации — notifier: filesystem. Это не email, не Telegram, а самый простой вариант для разработки: Authelia записывает ссылку на регистрацию прямо в файл на сервере. Никаких стандартных каналов связи, никакой магии с SMTP.

Пришлось подключиться по SSH к серверу и выполнить простую команду:

cat /var/lib/authelia/notifications.txt

И вот оно! В файле лежала ссылка вида https://auth.borisovai.tech/...token... — та самая ссылка, которая должна была привести к QR-коду. Оказалось, Authelia всё делала правильно. Просто в конфигурации для разработки уведомления отправляются не пользователю, а в лог-файл на диск.

Интересный момент: многие разработчики не замечают, что в конфигурации notifier: filesystem — кажется, что это какой-то непонятный режим, а на самом деле это идеальная настройка для локальной разработки. Вместо того чтобы настраивать SMTP-сервер или интеграцию с внешними сервисами, Authelia просто пишет ссылку в файл. Быстро, просто, полезно.

Когда я открыл эту ссылку в браузере, QR-код тут же появился. Сканировали его в TOTP-приложении, всё сработало. Задача решена за несколько минут, но урок остался: иногда самое очевидное решение скрыто в документации, и оно работает лучше, чем нами предполагалось.

Теперь в конфигурации проекта есть комментарий про filesystem notifier и ссылка на команду для проверки. Следующему разработчику, который будет настраивать двухфакторку, не придётся искать её полчаса.


Authelia: когда QR-код путешествует по файловой системе вместо того, чтобы сразу показаться в браузере 😄

#claude#ai#api#security
Читать далее
9
Новая функция

Охота на привидения в DNS: как потерянная запись чуть не сломала аутентификацию

Проект borisovai-admin — это админ-панель с полноценной системой аутентификации. Задача казалась простой: настроить поддомены для разных частей приложения. admin.borisovai.tech уже работал безупречно, а вот auth.borisovai.tech и auth.borisovai.ru упорно отказывались резолвиться. Казалось, что может быть проще — добавить записи и забыть? Не так всё оказалось.

Первым делом я начал с базовой диагностики. Проверил nslookup и dig — и вот тут началось веселье. auth.borisovai.tech не резолвился с локального DNS, а вот Google DNS (8.8.8.8) прекрасно возвращал 144.91.108.139. Это был явный признак того, что проблема не в глобальной DNS-иерархии, а где-то рядом.

Полез в DNS API — может, записи просто не создались? Нашёл в базе пусто: records: []. Автоматического создания не было. Но вот странный момент — admin.borisovai.tech работал без проблем. Почему одна запись есть в DNS API, а другая нет? Начал разбираться в истории создания записей.

Выяснилось, что admin.borisovai.tech был добавлен напрямую у регистратора, минуя DNS API. А вот auth.* я стал добавлять через API, и тут столкнулся с интересным поведением: локальный AdGuard DNS (94.140.14.14), который использовался как основной рекурсивный резолвер, кэшировал старые данные или просто не видел новые записи из-за задержки распространения.

Это была классическая ловушка DNS-администраторов: разные пути создания записей приводят к рассинхронизации. Когда у тебя есть несколько источников истины (регистратор, API, локальный кэш), они начинают рассказывать разные истории. Сервис аутентификации попадал на несуществующий адрес, и всё падало в момент, когда требовалась верификация токена.

Интересный факт: DNS работает на принципе давности — записи имеют TTL (Time To Live), и если кэширующий резолвер уверен, что всё верно, он будет возвращать старые данные до истечения TTL, даже если на авторитативном сервере уже всё изменилось. В нашем случае TTL был достаточно высоким, поэтому AdGuard упорно держался за мысль, что auth.borisovai.tech не существует.

Решение: я привёл все записи в единую систему — создал их через DNS API, настроил правильные TTL (600 секунд вместо 3600) и добавил явное перенаправление с auth.borisovai.ru на основной домен. Проблема испарилась.

Главный урок: в распределённых системах всегда проверяй, откуда берётся каждый кусок информации. DNS выглядит просто, пока не начнёшь складывать вместе мнения разных серверов. И да, не забывай очищать кэш после изменений — Google DNS обновляется быстрее, чем AdGuard, и это сейчас спасало жизнь.

😄 А знаешь, почему DNS никогда не ходит к психологу? Потому что у него всё равно проблемы с разрешением.

#claude#ai#api#security
Читать далее
10
Новая функция

Когда DNS кеш становится врагом: охота на призрачный домен в проекте borisovai-admin

Казалось бы, добавил DNS-записи для auth.borisovai.tech — и готово. Но нет. Домен упорно не резолвился с одного DNS-сервера, зато с другого всё работало как часы. Началась охота.

Первые подозрения

Работаю над проектом borisovai-admin — админ-панель с собственной системой аутентификации. Задача простая: перенести auth-сервис на новый поддомен и убедиться, что всё резолвится. Добавил записи в регистратор, обновил конфиги — и вот тут началось веселье.

Первый звонок: с Google DNS (8.8.8.8) всё отлично. auth.borisovai.tech резолвится на 144.91.108.139. Но когда переключился на AdGuard DNS (94.140.14.14), который был настроен по умолчанию, домен превратился в привидение — NXDOMAIN, записи как будто не существуют.

А вот admin.borisovai.tech спокойно резолвился везде. Что-то не так с auth.*.

Расследование

Началось с простого: запросить обе версии домена через оба резолвера. auth.borisovai.tech и auth.borisovai.ru вели себя одинаково — видны у Google, невидимы у AdGuard. Явный признак того, что записи в регистраторе были добавлены после того, как AdGuard их закешировал.

Вот в чём суть: когда запрашиваешь несуществующий домен, DNS-резолвер кеширует отрицательный ответ (NXDOMAIN) на какое-то время. Даже если позже ты добавишь запись, старый кеш будет отправлять безутешный “нет такого домена”. У AdGuard этот кеш может жить до часа.

Как я это решил

Вариант первый — просто подождать. AdGuard истечёт кеш, записи проявятся сами. Но тестировать нужно было сейчас.

Вариант второй — переключиться на Google DNS. Работает мгновенно, но это костыль.

Вариант третий — очистить локальный кеш на машине. В Windows команда ipconfig /flushdns чистит кеш операционной системы, а не самого DNS-резолвера. Но иногда помогает.

На самом деле я использовал комбинацию: временно переключился на Google DNS для тестирования, а затем дождался, пока AdGuard обновит свои данные.

Интересный факт о DNS

Мало кто знает, что DNS-записи имеют собственное поле TTL (Time To Live) — “время жизни” в кеше. По умолчанию обычно ставят 3600 секунд (час). Google использует более агрессивную стратегию кеширования, AdGuard — более консервативную. Вот поэтому один резолвер сразу видит новую запись, а другой ещё час её “забывает”.

Вывод

Простой урок: при добавлении новых DNS-записей всегда проверяй через несколько резолверов. Если в сети настроены кастомные DNS (как AdGuard или Pihole), они могут сыграть с тобой в злую шутку. И никогда не забывай про ipconfig /flushdns или sudo systemd-resolve --flush-caches на Linux — иногда это спасает часы дебага.

Дальше — уже знаю, где искать, если история повторится. А повторится ещё не раз.

Что общего у Netlify и кота? 😄 Оба делают только то, что хотят, и игнорируют инструкции.

#claude#ai#security
Читать далее
11
Новая функция

Двойная защита убивает саму себя: как я развязал узел конфликтующей аутентификации

Задача стояла простая на первый взгляд: запустить Management UI для проекта borisovai-admin. Казалось, что админ-панель встанет и будет работать. Но когда я подключил её к боевой инфраструктуре, выяснилось нечто интересное — UI запустилась, но пройти аутентификацию было невозможно.

Когда две защиты становятся одной проблемой

Начал копать логи и вот что нашёл. В инфраструктуре уже была слоёная защита: Traefik с плагином ForwardAuth отправлял все запросы на Authelia для двухфакторной аутентификации. Это первый уровень охраны — на уровне прокси. Здесь логика простая: если запрос идёт на admin.borisovai.tech, Traefik вежливо перенаправляет пользователя в Authelia.

Но когда я добавил Management UI с встроенной OIDC-аутентификацией через express-openid-connect, произошло вот что: пользователь уже прошёл Authelia на уровне Traefik, но Management UI не поверил ему и снова отправил на Authelia через OIDC. Два редиректа подряд — и браузер начинает петлять между разными провайдерами аутентификации. Типичная ситуация, когда каждый охранник требует личный документ, не доверяя соседу.

Выбор между защитами

Встал вопрос: какой уровень аутентификации оставить? Отключить Traefik ForwardAuth? Отключить OIDC в Management UI? Или искать способ их синхронизировать?

Я выбрал проверенный путь — оставить Traefik ForwardAuth как основную защиту, а OIDC отключить. Логика здесь такая: раз у нас уже есть надёжная защита на уровне прокси с поддержкой 2FA через Authelia, зачем добавлять второй слой? Внутри же Management UI я оставил legacy session — простую аутентификацию по логину и паролю. Получилось двухуровневое решение, но на разных слоях: внешняя защита через прокси и внутренняя через сессию.

После изменений Management UI перезапустился без OIDC-интеграции. Теперь схема работает так: вы входите в https://admin.borisovai.tech, Traefik перенаправляет вас в Authelia, вы проходите двухфакторную аутентификацию, а потом попадаете на страницу логина самой админ-панели, где вводите учётные данные Management UI.

Интересный факт о OIDC

Стандарт OpenID Connect создан в 2014 году поверх OAuth 2.0 именно для решения проблем единого входа. Но мало кто знает, что OIDC работает лучше всего, когда он — единственный поставщик идентификации в системе. Как только вы пытаетесь слоить несколько провайдеров, начинаются конфликты. Классическая ловушка — стараться защитить приложение со всех сторон и получить вместо этого лабиринт редиректов.

Неприятный бонус: проблема с .ru доменами

Во время работы я обнаружил, что A-записи для admin.borisovai.ru и auth.borisovai.ru не добавлены у регистратора IHC. Let’s Encrypt не может выдать сертификаты для доменов, которых нет в DNS. Решение пришло быстро — нужно добавить эти A-записи в панели регистратора, указывая на IP 144.91.108.139. Казалось бы, мелочь, но именно такие детали часто становятся причиной того, что production не поднимается.

Что я вынес из этого

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

Теперь Management UI работает, защита работает, и никто не просит удостоверения дважды. Инфраструктура проекта borisovai-admin стала на один уровень надёжнее.

😄 Почему Prometheus не пришёл на вечеринку? Его заблокировал firewall.

#claude#ai#api#security
Читать далее
12
Исправление

Охота на невидимых врагов: как я отловил проблемы с сертификатами в Traefik

Когда ты администрируешь borisovai-admin и вдруг замечаешь, что половина пользователей не может зайти в систему из-за ошибок сертификатов, начинается самая интересная работа. Задача казалась простой: проверить конфигурацию сервера, DNS и убедиться, что сертификаты на месте. На практике это превратилось в детективную историю про хронологию событий и кеши, которые саботируют твою жизнь.

Первый подозреваемый: DNS

Первым делом я проверил, резолвятся ли доменные имена с сервера. Оказалось, что DNS работает — это был хороший знак. Но почему Traefik выглядит так, будто ему не хватает сертификатов? Я полез в acme.json, где Traefik хранит выданные Let’s Encrypt сертификаты.

И вот тут началось самое интересное.

Сюрприз в acme.json

В файле лежали все четыре сертификата, которые мне были нужны:

  • admin.borisovai.tech — Let’s Encrypt R12, выдан 4 февраля, истекает 5 мая
  • admin.borisovai.ru — Let’s Encrypt R12, выдан 8 февраля, истекает 9 мая
  • auth.borisovai.tech — Let’s Encrypt R13, выдан 8 февраля, истекает 9 мая
  • auth.borisovai.ru — Let’s Encrypt R12, выдан 8 февраля, истекает 9 мая

Все они были валидны и активны. Traefik их отдавал при подключении. Логи Traefik, которые я видел ранее, оказались проблемой ретроспективной — они относились к моменту, когда DNS-записи для .ru доменов ещё не пропагировались по сети. Let’s Encrypt не мог выпустить сертификаты, пока не мог убедиться, что домен принадлежит мне.

Невидимый враг: браузерный кеш

Последний вопрос был ужасающе простым: почему браузер по-прежнему ругался на сертификаты, если сами сертификаты в порядке?

DNS кеш. Браузер запомнил старую информацию и упорно её использовал.

Финальный диагноз

Вся история сводилась к тому, что системные часы интернета движутся медленнее, чем кажется. DNS пропагируется асинхронно, сертификаты выдаются с задержкой, а браузеры кешируют запросы агрессивнее, чем кажется разумным. Решение? Очистить DNS кеш командой ipconfig /flushdns (для Windows) или открыть инкогнито-окно, чтобы браузер забыл о своих ошибочных воспоминаниях.

Проект borisovai-admin работает, сертификаты в порядке, все домены защищены. Ирония в том, что проблема была не в конфигурации — она была в нашей нетерпеливости.

Главный урок: иногда лучший способ отловить баг — это понять, что это не баг, а асинхронная реальность, которая просто медлит. 😄

#claude#ai#javascript#security
Читать далее
13
Новая функция

Самостоятельная аналитика: как я превратил borisovai-admin в data-driven продукт

Несколько месяцев назад передо мной встала типичная для любого владельца проекта проблема: я совершенно не видел, кто и как использует мою админ-панель borisovai-admin. Google Analytics казался избыточным (и страшным с точки зрения приватности), а простой счётчик посещений — примитивным. Нужно было что-то лёгкое, приватное и полностью под своим контролем.

Выбор пал на Umami Analytics — открытую веб-аналитику, которая уважает приватность пользователей, не использует cookies и полностью GDPR-compliant. Главное же — её можно развернуть самостоятельно, прямо в своей инфраструктуре.

Четыре этапа внедрения

Первый шаг — упростить развёртывание. Стандартная Umami требует двух контейнеров (приложение + PostgreSQL), но для небольшого проекта это избыточно. Я нашёл fork maxime-j/umami-sqlite, который использует SQLite — файловую БД в одном контейнере. Экономия памяти была существенной: вместо ~300 MB получил ~100 MB. Затем написал скрипт install-umami.sh из семи шагов, который может быть запущен много раз без побочных эффектов (идемпотентный — именно это было важно для автоматизации).

Второй этап — автоматизировать через CI/CD. Создал два job’а в пайплайне: один автоматически ставит Docker (если его нет), второй — развёртывает саму Umami. Добавил health check, чтобы пайплайн не переходил к следующему шагу, пока контейнер не будет готов. Инкрементальный деплой через deploy-umami.sh позволяет обновлять конфигурацию без перезагрузки приложения.

Третий этап — дать пользователям интерфейс. Создал страницу analytics.html, где каждый новый сервис может получить код для интеграции отслеживания. Плюс добавил API endpoint GET /api/analytics/status для проверки, всё ли работает. Async-скрипт Umami весит всего ~2 KB и не блокирует рендеринг страницы — вот это я ценю.

Четвёртый этап — документировать. Написал AGENT_ANALYTICS.md с инструкциями для будущих разработчиков, обновил главный CLAUDE.md таблицей всех сервисов.

Что интересного я узнал

Оказывается, боль большинства разработчиков с традиционной аналитикой — это не функциональность, а приватность. Umami решает это элегантно: скрипт отправляет только агрегированные данные (сессии, страницы, источники трафика) без ID пользователей и истории кликов. А главное — нет необходимости в consent banner, который все равно раздражает пользователей.

Порт 3001 внутри контейнера пробросил через Traefik на HTTPS-домены analytics.borisovai.ru и analytics.borisovai.tech. Вообще, это я оценил: такая простота развёртывания чуть ли не впервые в моём опыте с self-hosted решениями. Встроенная авторизация в самой Umami (не потребовался дополнительный Authelia) — и это экономия на инфраструктуре.

Один лайфхак: чтобы скрипт аналитики не блокировался AdBlock, назвал его stats вместо стандартного umami — простой способ обойти базовые фильтры.

Итог

Теперь borisovai-admin наконец-то видит себя со стороны. Я получил данные о том, какие страницы реально используют люди, откуда они приходят и сколько времени длятся сессии. Всё это — на своём сервере, без третьих лиц и без чувства вины перед пользователями.

Следующий шаг — подключить аналитику ко всем остальным сервисам проекта. Это уже не задача месяца, а скорее вопрос пары часов на каждый сервис.

Учимся: иногда лучший инструмент — это не самый популярный, а самый честный. 😄

#git#commit#api#security
Читать далее
14
Новая функция

Как я чуть не сломал CI/CD, ища волшебный токен

В проекте borisovai-admin встала задача: нужно проверять статус GitLab pipeline прямо из CI/CD, чтобы убедиться, что деплой прошёл успешно. Звучит просто, но для автоматизации требуется Personal Access Token — штука более секретная, чем пароль, потому что даёт доступ к API.

Первым делом я попытался вспомнить, где в GitLab хранятся эти токены. Инстинкт подсказал: где-то в настройках профиля. Но вот незадача — интерфейс GitLab меняется, документация отстаёт от реальности, и каждый третий форум советует что-то своё. Начал искать по URL-адресам, как детектив, собирающий пазл.

Выяснилось, что нужно открыть ровно вот этот URL: https://gitlab.dev.borisovai.ru/-/user_settings/personal_access_tokens. Не Settings, не API, не Profile — именно этот путь. Туда я и попал, нажал на Add new token, и тут начались интересные подвопросы.

Правило первое: токену нужно дать имя, которое потом разберёшься. Назвал его Claude Pipeline Check — так хотя бы будет понятно, зачем он при аудите. Правило второе: scope. Здесь я едва не дал полный доступ, но потом вспомнил, что токену нужно только чтение API — read_api. Ни write, ни delete. Безопасность прежде всего.

После создания токен показывается ровно один раз. Это не шутка. Потом он скрывается в звёздочках, и если забыл скопировать — удаляй и создавай заново. Я это, конечно, проверил на практике 😅

Интересный момент: GitLab разделяет токены по scopes, как OAuth, но работают они как обычные API-ключи. Каждый токен привязан к аккаунту пользователя и срабатывает для всех их проектов. Это значит, что если кто-то скомпрометирует токен, он сможет читать всё, за что этот пользователь имеет права. Поэтому в боевых системах их хранят в secret переменных CI/CD, а не в коде.

Что дальше? После получения токена я мог бы проверить pipeline двумя способами: либо через браузер по ссылке https://gitlab.dev.borisovai.ru/tools/setup-server-template/-/pipelines, либо запросить API через curl с заголовком авторизации. Для borisovai-admin выбрали первый вариант — простой и понятный.

Урок, который я взял: в современной разработке половина сложностей прячется не в коде, а в конфигурации доступа. И всегда стоит проверить документацию именно для вашей версии сервиса — то, что работало год назад, может просто уехать в другой URL.


Что сказал GitLab, когда разработчик забыл скопировать токен? «Вот тебе урок — я показываю его только один раз!» 😄

#claude#ai#python#git#api#security
Читать далее
15
Новая функция

Как мы спроектировали DevOps-платформу за день: конкурентный анализ на стероидах

Проект borisovai-admin требовал системного подхода к управлению инфраструктурой. Стояла непростая задача: нужно было разобраться, что вообще делают конкуренты в DevOps, и построить свою систему с трёхуровневой архитектурой. Главный вопрос: какой стек выбрать, чтобы не переплатить и не потерять гибкость?

Первым делом я понимал, что нельзя прыгать в реализацию вслепую. Нужно провести честный конкурентный анализ — посмотреть, как это решают HashiCorp с их экосистемой (Terraform, Nomad, Vault), как это делается в Kubernetes с GitOps подходом, и что там у Spotify и Netflix в их Platform Engineering. Параллельно изучил облачные решения от AWS, GCP, Azure и даже AI-powered DevOps системы, которые только появляются на рынке.

Результат был обширный: создал три больших документа объёмом в 8500 слов. COMPETITIVE_ANALYSIS.md — это развёрнутое исследование шести ключевых подходов с их архитектурными особенностями. COMPARISON_MATRIX.md — матрица сравнения по девяти параметрам (Time-to-Deploy, Cost, Learning Curve) с рекомендациями для каждого уровня системы. И финальный BEST_PRACTICES.md с практическими рекомендациями: Git как source of truth, state-driven архитектура, zero-downtime deployments.

Неожиданно выяснилось, что для нас идеально подходит многоуровневый подход: Tier 1 — простой вариант с Ansible и JSON конфигами в Git; Tier 2 — уже Terraform с Vault для секретов и Prometheus+Grafana для мониторинга; Tier 3 — полноценный Kubernetes со всеми OpenSource инструментами. Самое интересное: мы обнаружили, что production-ready AI для DevOps пока не существует — это огромная возможность для инноваций.

Вот что важно знать про DevOps платформы: state-driven архитектура работает несравненно лучше, чем imperative approach. Почему? Потому что система всегда знает целевое состояние и может к нему стремиться. GitOps как source of truth — это не мода, а необходимость для аудитируемости и восстанавливаемости. И про многооблачность: vendor lock-in — это не просто дорого, это опасно.

В результате я готов параллельно запустить остальные треки: Selection of Technologies (используя findings из анализа), Agent Architecture (на основе Nomad pattern) и Security (с best practices). К концу будет полная MASTER_ARCHITECTURE и IMPLEMENTATION_ROADMAP. Track 1 на 50% завершено — основной анализ готов, осталась финализация.

Главный вывод: правильная предварительная работа экономит месяцы разработки. Если в DevOps всё работает — переходи к следующему треку, если не работает — всё равно переходи, но с документацией в руках.

#claude#ai#javascript#git#security
Читать далее
16
Новая функция

Выбираем стек для боевого сервера: от SQLite до Kubernetes

Вот я и дошёл до самой мясной части проекта borisovai-admin — нужно было решить, на чём строить технологический фундамент. Не просто выбрать, а выбрать правильно, с прицелом на масштабирование.

Задача была масштабная: разобраться в 10 ключевых компонентах инфраструктуры и дать рекомендации для трёх разных уровней — от стартапа на $50–100 в месяц до полноценной облачной системы. Infrastructure as Code, управление конфигами, базы данных, оркестрация контейнеров, мониторинг — всё нужно было проанализировать и обосновать.

Первым делом я создал структурированный анализ для каждого компонента. Взял Terraform для Infrastructure as Code (почему? потому что YAML в Ansible проще писать, но Terraform лучше управляет состоянием), Ansible для конфигурации (когда нужна простота без лишних абстракций), и вот тут начиналась интересная часть — выбор между SQLite и PostgreSQL.

SQLite для первого тира — это не просто выбор экономии, это выбор разума. Встроенная база, ноль настройки, ноль инфраструктуры. Новичок может развернуть систему буквально за минуту. Но когда трафик растёт? Тогда я рекомендую чёткую миграционную дорожку: сначала dual-write (две базы параллельно, неделю собирали данные в обе), потом гибридный подход и только потом полная миграция на PostgreSQL с тремя серверами.

Для оркестрации я выстроил пирамиду: systemd на одном сервере (t1), потом Docker + Docker Compose (t2) и наконец Kubernetes (t3) для тех, кто готов платить. Каждый уровень вносит свою сложность, но при правильной архитектуре переходы между ними — почти безболезненны.

Вот забавный факт про выбор инструментов: Terraform и Ansible созданы в разных мирах. Terraform — это декларативный язык состояния (вы описываете, что хотите). Ansible — это процедурный язык действий (вы описываете, что делать). Профессионалы часто используют их вместе: Terraform создаёт инфраструктуру, Ansible её настраивает. Это как иметь архитектора и прораба на одном проекте.

В итоге я подготовил три документа на 10 000+ слов: матрицу выбора с оценками по 10 критериям, полный анализ каждого компонента и готовый набор миграционных сценариев. Теперь у меня есть чёткая дорожная карта, и любой разработчик может взять этот стек и масштабировать систему вверх, не переделывая всё с нуля. Впереди — Track 3 с архитектурой AI-агента, и я уже вижу, как туда впишется этот технологический фундамент.

😄 Что общего у Terraform и кота? Оба отказываются делать то, что вы просили, пока не напишете ровно то, что они хотят видеть.

#claude#ai#git#security
Читать далее

Оцените материал

0/1000