Summary
Домашняя инфраструктура - это бесконечный сериал мелких багов. Собрал восемь эпизодов из последних месяцев: у каждого есть причина, которую невозможно угадать, и фикс в одну строчку. Самый обидный - про мост, который Docker тихо переименовал.
Эпизод 1. Контейнер есть, пинга нет
Я добавил в свой VPN новый контейнер. Все соседние сервисы открываются из VPN, а он - нет. Адрес правильный, порт правильный, контейнер живой.
Причина нашлась в firewall. У меня есть «охранное» правило: пакеты на адрес этого контейнера принимаются только с определённого сетевого моста. И в правиле стоял имя моста, вот такое: br-3f9a….
Так вот, Docker называет мосты по идентификатору сети. После перезапуска демона systemctl restart docker сеть может пересоздаться, имя моста поменяется, а правило продолжит охранять призрак. Новый мост для него чужой - и всё режется.
Фикс из трёх частей:
- Задать мосту постоянное имя:
com.docker.network.bridge.name: br-nginx. - Скрипт запуска после рестарта Docker заново определяет мост.
- Единая команда
start_vpnподнимает всё в правильном порядке.
Эпизод 2. Скрипт, который Linux не видит
Я написал bash-скрипт в Windows-редакторе и залил на сервер. Linux ответил, что такого файла нет. Файл был, но вместо \n в конце строк стояли \r\n. Интерпретатор искал bash\r.
Теперь в репозитории есть правило: для скриптов всегда LF. Заодно я узнал, что sysctl -w нужен не везде, и что скрипт на awk бывает хрупким.
Эпизод 3. Знак доллара в хэше пароля
Положил в .env bcrypt-хэш пароля. Compose ругается на «неизвестные переменные». Bash при source тоже ломается. Виноват символ $ внутри хэша - для оболочки это начало переменной.
Решение: написать безопасный загрузчик переменных окружения, а хэш экранировать.
Эпизод 4. Конфиг, который затёрся дважды
Этот эпизод мне обошёлся дороже всех. Я залил шаблон конфига, и файл сегмента остался без клиентов. Затем awg-quick down записал файл поверх ещё раз, потому что в нём стояло SaveConfig = true, и интерфейс при остановке сохраняет своё (пустое) состояние обратно.
Клиентов спасло то, что все они лежали в базе панели. Из неё я восстановил десять ключей. Вывод: бэкап перед любой заливкой и осторожность с SaveConfig.
Эпизод 5. Сменил подсеть и остался без интернета
Мне понадобилось поменять адресный план: одна подсеть стала другой. Я обновил базу панели и живой интерфейс. Но один забытый конфиг держал старый адрес, и все клиенты потеряли интернет.
Теперь любое изменение адресации - это чек-лист: база, конфиги, интерфейс, правила firewall, DNS.
Эпизод 6. Порты открыты, но снаружи тишина
Я проверял порт снаружи, получал таймаут. Не отказ в соединении, а именно молчание. Молчание почти всегда означает DROP в firewall, а не отсутствие сервиса. В моём случае правило в цепочке FORWARD отбрасывало пакеты моего же периметра.
Итоговая схема: публично открыты только порты 80 и 443, остальное спрятано на внутренних адресах и доступно из админского сегмента.
Эпизод 7. Туннель между серверами «очень узкий»
Канал между двумя серверами оказался мучительно медленным. Подозреваемые были такие:
- обфускация AmneziaWG на межсерверном канале;
- один процессорный ядро на одном из узлов;
- ядро без тюнинга.
Что я сделал:
- межсерверные туннели перевёл на обычный WireGuard, а обфускацию оставил только клиентам;
- выставил MTU 1420 на подложке;
- включил BBR и
fq; - заменил
MASQUERADEна статическийSNAT; - сделал три параллельных выходных интерфейса.
Честно скажу: итоговых цифр у меня нет. Тесты iperf3 зависали и ничего полезного не показали. Поэтому не обещаю «гигабиты». Делюсь только тем, что проверено логикой, а не измерениями.
Эпизод 8. Панель, которая падает после обновления
В одной из версий образа панели WGDashboard страница настроек падала с ошибкой 401 на служебном запросе. Помог переход на :latest. А заодно я перестал перезаписывать её рабочие файлы из репозитория: у панели своя «первая регистрация», и она обижается.
Что объединяет эти эпизоды
| Что сломалось | Почему |
|---|---|
| Мост Docker | Правило привязано к имени, а не к смыслу |
| Bash-скрипт | Невидимые символы в конце строки |
.env | Специальный символ внутри значения |
| Конфиг AWG | Автосохранение затирает файл |
| Подсеть | Забытое место, где остался старый адрес |
| Порты | DROP выглядит как «ничего нет» |
Правила, которые я себе вывел:
- Скрипты должны быть идемпотентными. Запустил дважды - получил то же самое.
- Бэкап перед заливкой. Всегда.
- Не привязывайте правила к генерируемым именам. Задавайте постоянные.
- Состояние клиентов хранится в базе, а не в конфиге. Конфиг - производный артефакт.
- Измеряйте, а не предполагайте. Особенно скорость.
Это не мудрость, а шрамы. Но они полезнее любой документации.