chore: updated

This commit is contained in:
protokey committed 2026-10-08 21:32:55 +04:00
1 parent d1bbf0a505
commit 5281f9b989
4 files changed
+581 -28

No files matched your search

+5
View File
@@ -1,2 +1,7 @@
all:
hugo new content $(path)
publish:
git add .
git commit -m 'chore: $(msg)'
git push
+29 -26
View File
@@ -1,52 +1,55 @@
---
title: "Sokolab"
title: "Обо мне"
date: 2026-10-08T19:18:00+04:00
---
## Кто я!
## Кто я
Я высококвалифицированный IT специалист широкого профиля. Умею добывать данные с сайтов, писать под них боты и обходить анти-бот защиту. Умею создавать сайты и сетевые сервисы, а так же обеспечивать безопасность инфраструктуры и разработки.
Меня зовут Александр Соколов, в сети s0k0l. Я IT специалист широкого профиля: имитация пользовательского поведения, добыча данных и защита информации. Пишу парсеры и ботов, которые проходят анти-бот защиту, делаю сайты и сетевые сервисы, настраиваю безопасность инфраструктуры и разработки.
Предпочитаю работать согласно ТЗ и вести проект от идеи до MVP или релиза. Выработал и следую определенным стандартам и применяю лучшие практики программирования - [Стандарты Разработки ПО](/code-style.md). При разработке программ начинаю с выработки требований и архитектуры. Считаю что мой результат это решенная проблема, а написанный исходный код - доказательство что мое решение работает.
Предпочитаю работать по ТЗ и вести проект от идеи до MVP или релиза. Если ТЗ нет, то составлю его сам: проведу ресерч, выберу технологии, оценю сложность и сроки. Работаю по своим [Стандартам Разработки ПО](/code-style.md) и всегда начинаю с требований и архитектуры, а не с кода.
Мой результат это решенная проблема. Исходный код это доказательство, что решение работает. Если мы о чем-то договорились, я сделаю все, чтобы выполнить обещание.
## Что я делаю
Создаю программные и аппаратные продукты для людей и бизнеса,
которые помогают жить лучше и работать удобнее.
Создаю программные и аппаратные продукты для людей и бизнеса, которые помогают жить лучше и работать удобнее.
- **Софт** — боты, сервисы, автоматизация.
### Софт: боты, сервисы, автоматизация
Ознакомиться с моим послужным списком вы можете в [резюме](/резюме/index.md), а тут я просто опишу какие задачи мне откликаются. Это задачи связанные с web и linux. Умею работать с криптой напрямую, так что пользователь может оплатить что-то на сайте on-chain переводом. Умею строить высоконагруженные системы на очередях и микросервисах.
Мне откликаются задачи связанные с web и linux:
- **Железо** — устройства, электроника, прототипы
- парсинг сайтов и обход анти-бот защиты
- боты для сайтов и чат-боты
- высоконагруженные системы на очередях и микросервисах
- сайты, веб-приложения и сервисы
- прием оплаты криптой напрямую, on-chain переводом без посредников
- информационная безопасность серверов и разработки
Освоил программирование микроконтроллеров, потому что мечтаю строить роботов. А пока что построил аппаратный менеджер паролей - Протоключ. Планирую развиваться в этом направлении и дальше. У меня много идей для полезных проектов.
Подробный послужной список в [резюме](/резюме/index.md).
## Ссылка
### Железо: устройства, электроника, прототипы
[stackoverflow](https://ru.stackoverflow.com/users/362019/alex)
Освоил программирование микроконтроллеров, потому что мечтаю строить роботов. А пока построил аппаратный менеджер паролей Протоключ. Планирую развиваться в этом направлении и дальше, идей для полезных устройств у меня много.
Когда-то это был незаменимый сайт для всех ИТишников, где можно было задать вопрос и получить ответ от коллег. Но со временем вопросы больше стали похожи на "решите за меня мою домашку", а с появлением ИИ подобный формат и вовсе умер. Теперь я использую эту ссылку лишь как демонстрацию своего бекграунда.
## Где меня можно найти
[medium](https://alexandrsokolov-41020.medium.com/)
Сейчас все живое находится тут:
Наслушавшить Егора Бугаенко завел блог на этой платформе и питал надежды что буду писать там что-то регулярно. Но что-то мне не понравилось и теперь я перезапускаю свой блог уже на своем решении.
- [Блог](/blog/) - статьи про разработку, сервера и автоматизацию
- [git.sokolab.xyz](https://git.sokolab.xyz/sokol) - мой код и примеры
[github](https://github.com/alexsok-bit/)
Раньше я был и на других площадках. Сейчас туда почти не захожу, но оставляю ссылки, там видно с чего я начинал.
Тут лежит код из статей на медиум и использовал аккаунт в работе. Это заброшенный аккаунт, он остается как бекграунд.
**[Stack Overflow](https://ru.stackoverflow.com/users/362019/alex)**. Много лет отвечал там на вопросы и сам спрашивал коллег. Сейчас площадка почти опустела, на такие вопросы теперь отвечает ИИ.
[mozilla addons](https://addons.mozilla.org/ru/firefox/user/16605688/)
**[Medium](https://alexandrsokolov-41020.medium.com/)**. Мой первый блог. Писал туда про разработку, но чужая платформа быстро надоела, поэтому теперь пишу у себя.
Так как я делал когда-то серьезные инструменты для сбора данных мне нужно было модифицировать фаерфокс. Для этого я забабахал пару аддонов для него и выложил в общую библиотеку. Делал я это в общем-то для себя, но кому-то тоже зашло. Я крайне удивился что в комментах меня разыскивают.
**[GitHub](https://github.com/alexsok-bit/)**. Код к статьям с Medium и старые проекты. Новый код живет на моем git.
Все это было когда-то, а теперь есть этот ресурс где:
- Лендинг - вы тут
- [/blog](https://sokolab.xyz/blog) - профессиональный блог
- [git.sokolab.xyz](https://git.sokolab.xyz) - примеры кода
**[Mozilla Add-ons](https://addons.mozilla.org/ru/firefox/user/16605688/)**. Когда я делал инструменты для сбора данных, мне нужно было доработать Firefox под свои задачи. Написал под это пару аддонов и выложил в общий каталог. Делал для себя, но ими начали пользоваться и другие люди, и в комментариях до сих пор пишут.
## Связаться
- [Telegram](https://t.me/sokolex)
- [Почта](mailto:xalex.sokol@gmail.com).
- [Telegram](https://t.me/sokolex)
- [Почта](mailto:xalex.sokol@gmail.com)
+360 -2
View File
@@ -1,6 +1,6 @@
---
title: Как программисту завести свой блог
date: 2026-10-07T17:39:30+04:00
date: 2026-10-08T19:22:00+04:00
draft: false
tags:
- hugo
@@ -8,4 +8,362 @@ tags:
summary: Как поднять свой блог
---
That's it!
## Intro
Первое что понадобится это сервер. Просто гуглишь "Debian VPS" и выбираешь из предложенного.
**Важно:** Если ты в России, то лучше выбирай хостера и сервер в своей стране. Тогда ищи в яндексе: "аренда виртуального сервера Debian".
Пока сервер устанавливается у вас есть около часа, чтобы купить доменное имя. И зарегистрировать аккаунт на cloudflare. Если первое понятно для чего, то cf нужен чтобы спрятать ip адрес вашего сервера от посетителей сайта. Это несомненно повысит безопасность сервера, но делать это нужно до первой привязки домена. Поэтому после покупки доменного имени, нужно в панели управления (там где был куплен домен) сменить NS сервера на сервера, которые выдаст cloudflare после добавления проекта в ЛК. После этого потребуется какое-то время (до 3х дней) на перепарковку доменного имени. Так как об этом должен узнать весь интернет. Зато после этого ни один посетитель не сможет узнать ip вашего сайта, если не допускать утечек, разумеется. Но об этом мы как нибудь потом поговорим.
На руках:
1. ip и пароль рута
2. доменное имя припаркованное у cf
3. В настройках cf прописаны записи:
1. yourdomain.com - IP - корневая запись, по имени домена откроется стартовая блога
2. git.yourdomain.com - IP - под-домен для Gitea
## Поехали
[Базовая настройка сервера Debian ->](/blog/zero-setup-debian-server/)
Ставим докер командой
```
curl -fsSL https://get.docker.com -o install-docker.sh
sh install-docker.sh
```
Выходим с сервера, так как теперь нужно все подготовить локально.
На свой компьютер ставим hugo из GitHub репозитория, так как в репозитории пакетов Debian он старой версии.
https://github.com/gohugoio/hugo/releases
Ставим на компьютер Git, чтобы работать с репозиторием.
На сервере в докере мы запустим 2 приложения:
- **git** - Gitea, сервис с git, типо GitHub
- **hugo** - сборщик сайта и Caddy. Caddy отдает готовые страницы и он же единственная точка входа снаружи, через него ходим и в Gitea
Hugo из Markdown файлов собирает страницы блога конвертируя все в html. Процесс работы выглядит так:
1. создаешь в репозитории md файл
2. заполняешь его, коммитишь и пушишь
3. сборщик в докере раз в минуту проверяет репозиторий и видит новый коммит
4. собирает сайт и подменяет старую версию на новую
Окей, теперь выбери место на компьютере и создай папку `apps`, в ней мы будем хранить конфигурацию Docker приложений на сервере. Итоговая структура будет такая:
```
apps/
├── git/
│ └── docker-compose.yaml
└── hugo/
├── docker-compose.yaml
├── caddy.conf
├── dot_env
├── .gitignore
└── data/
└── builder/
└── build.sh
```
Тут только конфиги. Все данные (репозитории, сертификаты, собранный сайт) живут в named volumes Docker, а не в папках рядом с конфигами. Так `apps` можно спокойно хранить в git и копировать на сервер, не боясь затереть данные. Про бекапы расскажу ниже.
Оба приложения общаются через общую сеть `web`. Наружу порты открывает только Caddy.
### git
`apps/git/docker-compose.yaml`:
```yaml
# Git application for docker-server
services:
gitea:
image: gitea/gitea:28.0-rootless
restart: unless-stopped
environment:
GITEA__server__DOMAIN: git.yourdomain.com
GITEA__server__ROOT_URL: https://git.yourdomain.com/
GITEA__server__DISABLE_SSH: "true"
GITEA__database__DB_TYPE: sqlite3
GITEA__service__DISABLE_REGISTRATION: "true"
volumes:
- data:/var/lib/gitea
- config:/etc/gitea
networks: [ web ]
networks:
web:
external: true
volumes:
data:
config:
```
Берем rootless образ, внутри контейнера Gitea работает не от root. Тут named volumes особенно к месту: Docker при создании тома сам выставит ему владельца из образа, и не надо руками делать `chown`.
Порты наружу не открываем, до Gitea ходит только Caddy через сеть `web`. SSH выключен, потому что Cloudflare его не проксирует, а светить ip сервера мы не хотим. Пушить будем по https. Регистрация закрыта, чтобы никто кроме вас не заводил там аккаунты.
### hugo
`apps/hugo/docker-compose.yaml`:
```yaml
# Сайт с лендингом и блогом на Hugo
services:
builder:
image: ghcr.io/gohugoio/hugo:v0.167.0
restart: unless-stopped
user: root
entrypoint: [ "sh", "/build.sh" ]
environment:
REPO_URL: ${REPO_URL}
volumes:
- ./data/builder/build.sh:/build.sh:ro
- src:/src
- public:/public
networks: [ web ] # чтобы достучаться до git-gitea-1
web:
image: caddy:2.10.2-alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./caddy.conf:/etc/caddy/Caddyfile:ro
- public:/srv:ro
- caddy_data:/data
- caddy_config:/config
networks: [ web ] # чтобы проксировать на git-gitea-1
networks:
web:
external: true
volumes:
src:
public:
caddy_data:
caddy_config:
```
Свой образ собирать не нужно. `builder` это официальный образ Hugo, в нем уже есть git. Версию ставьте ту же, что у вас на компьютере, посмотреть можно командой `hugo version`. `user: root` нужен, потому что в образе по умолчанию обычный пользователь, а volumes Docker создает от root.
`web` это Caddy. Он смотрит наружу на 80 и 443, отдает сайт из общего с `builder` тома `public` и проксирует поддомен git на Gitea. В `caddy_data` он хранит свои сертификаты.
`apps/hugo/caddy.conf`:
```
yourdomain.com {
tls internal
encode gzip
root * /srv/live
file_server
handle_errors {
rewrite * /404.html
file_server
}
}
www.yourdomain.com {
tls internal
redir https://yourdomain.com{uri} permanent
}
git.yourdomain.com {
tls internal
reverse_proxy git-gitea-1:3000
}
```
Сайт отдаем из `/srv/live`, почему именно оттуда, станет понятно из скрипта сборки. `handle_errors` нужен, чтобы на несуществующие страницы показывалась красивая 404 от темы, а не пустой ответ. `www` просто редиректит на основной домен, чтобы у сайта был один адрес.
Имя `git-gitea-1` Docker Compose собирает сам из имени папки и имени сервиса: папка `git`, сервис `gitea`, первый экземпляр. Поэтому важно, чтобы папка называлась именно так.
`tls internal` значит, что Caddy сам выпустит себе сертификат. Браузер его не увидит, потому что посетители подключаются к Cloudflare, а уже он ходит к нам. Поэтому в панели Cloudflare в разделе SSL/TLS выставите режим **Full**. Не Flexible, иначе трафик от Cloudflare до сервера пойдет без шифрования, и не Full (strict), он не примет такой сертификат.
`apps/hugo/dot_env` это шаблон для переменных:
```
REPO_URL=http://git-gitea-1:3000/<name>/site.git
```
На сервере копируете его в `.env` и подставляете свое имя пользователя и репозиторий. Сам `.env` в git не попадает, для этого рядом лежит `.gitignore` с одной строкой `.env`. Сборщик клонирует репозиторий напрямую из контейнера Gitea по внутренней сети, без выхода в интернет. Если репозиторий приватный, добавьте токен: `http://<name>:<token>@git-gitea-1:3000/<name>/site.git`. Токен выпускается в настройках пользователя Gitea, в разделе Приложения. В этом случае закройте файл от чужих глаз `chmod 600 .env`.
`apps/hugo/data/builder/build.sh`:
```sh
#!/bin/sh
# Раз в минуту: новый коммит -> сборка в /public/next -> подмена /public/live.
# Ошибка сборки - остается прежний сайт.
# Адрес репозитория берется из REPO_URL при каждом старте: смена в .env + перезаливка = новый источник.
cd /src
if [ -d .git ]; then
git remote set-url origin "$REPO_URL"
else
git clone --recurse-submodules "$REPO_URL" . || exit 1
fi
last=''
while true; do
if git fetch -q origin HEAD && git reset -q --hard FETCH_HEAD \
&& git submodule sync -q --recursive && git submodule update -q --init --recursive; then
rev=$(git rev-parse HEAD)
if [ "$rev" != "$last" ]; then
rm -rf /public/next
if hugo --minify -d /public/next; then
rm -rf /public/old
[ -d /public/live ] && mv /public/live /public/old
mv /public/next /public/live
last=$rev
echo "published $rev"
else
echo "build of $rev failed, site unchanged"
fi
fi
fi
sleep 60
done
```
Главная фишка скрипта в том, что сайт собирается в отдельную папку `next`, и только если сборка прошла успешно, она подменяет рабочую `live`. Если вы запушите коммит с ошибкой, сайт просто останется прежним, а в логе будет видно что сломалось. Предыдущая версия сохраняется в `old`, на случай если нужно быстро откатиться.
`git reset --hard` тут специально, а не `git pull`. Если вы сделаете force push или поправите историю, обычный pull сломается, а reset просто возьмет то, что лежит в репозитории. А `set-url` при старте позволяет поменять источник: правите `REPO_URL` в `.env`, перезапускаете контейнер, и сборщик тянет уже из нового места.
## Заливаем на сервер
Конфиги готовы, отправляем папку на сервер. `<name>` это имя хоста из `~/.ssh/config`, которое мы настроили в прошлой статье:
```
s0k0l:~$ scp -r apps <name>:/opt/
```
Перед запуском нужно открыть фаервол для сайта. В прошлой статье мы закрыли все входящее, и там же я предупреждал, что Docker живет по своим правилам. Сейчас разберемся.
Открываем на сервере `/etc/nftables.conf`. В цепочке `input` раскомментируем строку для сайта:
```
tcp dport { 80, 443 } accept
```
А цепочку `forward` меняем на такую:
```
chain forward {
type filter hook forward priority 0; policy drop;
ct state established,related accept
ct status dnat accept
iifname "docker0" accept
iifname "br-*" accept
}
}
```
Дело в том, что трафик к контейнерам идет не через `input`, а через `forward`. С нашим строгим `policy drop` контейнеры не смогут ни принимать подключения, ни выходить в интернет. Эти правила пропускают трафик на порты, которые Docker опубликовал (у нас это только 80 и 443 у Caddy), и исходящий трафик из самих контейнеров. Все остальное по прежнему закрыто.
Проверяем и применяем так же, как в прошлый раз, с таймером на всякий случай:
```
nft -c -f /etc/nftables.conf
(sleep 120 && nft flush ruleset) &
nft -f /etc/nftables.conf
```
Проверяем что ssh жив, отменяем таймер `kill %1` и перезапускаем Docker:
```
systemctl restart docker
```
Это обязательно. В начале нашего конфига стоит `flush ruleset`, он стирает в том числе правила, которые Docker создал для себя. После рестарта Docker создаст их заново. Запомните это: каждый раз когда перезапускаете nftables, перезапускайте и Docker.
## Запуск
Создаем общую сеть, через которую контейнеры будут видеть друг друга:
```
docker network create web
```
Поднимаем Gitea и пока только Caddy, без сборщика. Сборщику нечего клонировать, пока в Gitea нет репозитория:
```
cd /opt/apps/git && docker compose up -d
cd /opt/apps/hugo && cp dot_env .env && docker compose up -d web
```
Открываем в браузере `https://git.yourdomain.com`. Gitea покажет страницу первичной настройки, база уже указана в конфиге. Внизу страницы в разделе с аккаунтом администратора создаем себе пользователя. Регистрацию мы закрыли, так что это единственный способ завести аккаунт.
В Gitea создаем репозиторий `site`. Теперь на своем компьютере пушим туда сайт:
```
s0k0l:~$ cd my-blog
s0k0l:~$ git remote add origin https://git.yourdomain.com/<name>/site.git
s0k0l:~$ git push -u origin main
```
Если тема подключена как git submodule, то репозиторий темы тоже должен быть доступен сборщику по ссылке из `.gitmodules`. Самое простое, сделать зеркало темы у себя в Gitea и указать в `.gitmodules` его адрес.
Подставляем в `/opt/apps/hugo/.env` реальное имя пользователя и запускаем сборщик:
```
cd /opt/apps/hugo && docker compose up -d
```
Смотрим, что он собрал сайт:
```
docker logs -f hugo-builder-1
```
Если в логе появилось `published <хеш коммита>`, открывайте `https://yourdomain.com`, блог работает.
## Бекапы
Так как данные лежат в named volumes, их не видно в `apps`. Посмотреть список можно командой `docker volume ls`. Важные тут два: `git_data` и `git_config`, в них все репозитории и настройки Gitea. Сайт бекапить не нужно, он собирается из репозитория, а сертификаты Caddy выпустит заново.
Снять бекап Gitea:
```
docker compose -f /opt/apps/git/docker-compose.yaml stop
docker run --rm -v git_data:/data -v git_config:/config -v /root:/backup alpine \
tar czf /backup/gitea-$(date +%F).tgz /data /config
docker compose -f /opt/apps/git/docker-compose.yaml start
```
Gitea на время бекапа останавливаем, чтобы база SQLite не записалась наполовину. Готовый архив забираете к себе через `scp`.
## Как теперь писать
Весь процесс выглядит так:
1. создаешь пост `hugo new content blog/my-post/index.md`
2. пишешь, смотришь локально через `hugo server`
3. коммитишь и пушишь
4. через минуту пост на сайте
Я для удобства завел в репозитории `Makefile`, чтобы не набирать команды руками:
```makefile
all:
hugo new content $(path)
publish:
git add .
git commit -m 'chore: $(msg)'
git push
```
Новый пост `make path=blog/my-post/index.md`, публикация `make publish msg="новый пост"`.
На этом все. Сервер, свой git, блог и автодеплой, и все это на одной недорогой VPS без сторонних сервисов, кроме Cloudflare.
@@ -0,0 +1,187 @@
---
title: "Первичная настройка сервера Debian"
date: 2026-10-08T19:29:38+04:00
draft: false
tags: [server, linux]
summary: "Что делать с сервером Debian после покупки. Статья для линуксоидов."
---
Обычно, после покупки сервера на почту приходит письмо с IP-адресом и паролем пользователя root. Этого хватает чтобы подключиться к серверу по ssh и начать его настраивать.
Всего 3 шага:
1. Обновление системы
2. Настройка ssh
3. Настройка nftables
Нулевой шаг - подготовить ключи ssh и конфиг для сервера. Чтобы выпустить ключи выполнить:
```
s0k0l:~$ ssh-keygen
Generating public/private ed25519 key pair.
Enter file in which to save the key (/~/.ssh/id_ed25519):
```
Предлагается указать путь для ключа. Можно просто ввести желаемое имя ключа и пара будет создана в директории откуда запустили команду. Первый раз можно пропустить и нажать Enter.
Второй вопрос про пароль на доступ к ключу. Для ssh ключей от продакшен инфраструктуры лучше поставить. Если нет желания, то щелкаем Enter.
После того как хостер поднимет сервер, прокидываем ключ командой
```
s0k0l:~$ ssh-copy-id root@<ip>
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 3 key(s) remain to be installed -- if you are prompted now it is to install the new keys
root@<ip>'s password:
```
Введите пароль и должно появиться сообщение об успехе с предложением выполнить подключение к серверу, что и нужно сделать. Но сначала добавьте в файл `~/.ssh/config` новую конфигурацию:
```
Host <name>
HostName <ip>
Port 22
IdentityFile ~/.ssh/id_ed25519
```
Теперь можно подключаться по имени
```
ssh <name>
```
На сервере первым делом запускаем обновление:
```
apt update && apt dist-upgrade -y && reboot
```
Это выполнит обновление всей системы и отправит систему в перезагрузку. Ждите примерно 5 минут и можно пробовать подключиться назад.
Теперь нужно настроить ssh, чтобы никто больше не мог подключиться к серверу по стандартному порту и использовать пароль для входа. Это самое важное.
Открываем на редактирование файл `/etc/ssh/sshd_config` и ищем в нем параметры указывающие на адрес прослушивания и порт.
```
Port 5872
AddressFamily inet
ListenAddress <ip>
#ListenAddress ::
```
Ставим такие настройки, чтобы строго определить каким путем можно подключаться к серверу. У нас на руках ipv4 адрес, так что можно строго указать его в конфиге. А порт нужно поменять на рандомный в диапазоне от 1024 до 65535. Чтобы проверить какие порты не заняты выполните команду:
```
ss -tlnup
```
Если желаемого порта в выводе нет, то он свободен.
Далее ищем в конфиге ssh следующее:
```
PermitRootLogin prohibit-password
PubkeyAuthentication yes
PasswordAuthentication no
```
Эти настройки отключат авторизацию по паролю на ssh для всех пользователей включая root (PermitRootLogin).
После сохранения конфига нужно перезапустить сервис ssh. Оставайтесь в сессии пока не подключитесь параллельно.
```
systemctl restart ssh
```
Откройте соседний терминал, в конфиге поправьте порт `~/.ssh/config` и подключитесь к серверу. Если подключение прошло, то первый терминал можно закрыть. SSH готов.
Последний шаг - настройка nftables. Это стандартный фаервол в Debian, iptables уже не нужен.
Правила живут в одном файле `/etc/nftables.conf`. Принцип строгого конфига простой: все входящее запрещено, разрешаем только то, что реально нужно. Исходящее оставляем открытым, иначе сломаются обновления и DNS.
Открываем `/etc/nftables.conf` и заменяем содержимое целиком:
```
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
# уже установленные соединения пропускаем, мусор режем
ct state established,related accept
ct state invalid drop
# локальный интерфейс
iif lo accept
# ping, но не больше 5 в секунду
ip protocol icmp icmp type echo-request limit rate 5/second accept
ip protocol icmp accept
meta l4proto ipv6-icmp accept
# ssh на нашем порту, не больше 10 новых подключений в минуту с одного ip
tcp dport 5872 ct state new meter ssh_limit { ip saddr limit rate 10/minute } accept
# раскомментировать, если на сервере будет сайт
# tcp dport { 80, 443 } accept
}
chain forward {
type filter hook forward priority 0; policy drop;
}
chain output {
type filter hook output priority 0; policy accept;
}
}
```
Порт `5872` поменяйте на тот, что указали в `sshd_config`. Это самое важное место в конфиге, ошибка в нем закроет вам доступ к серверу.
Сначала проверяем конфиг на ошибки, ничего не применяя:
```
nft -c -f /etc/nftables.conf
```
Если команда ничего не вывела, синтаксис в порядке. Теперь страховка на случай, если все таки закроем себе доступ. Запускаем таймер, который через 2 минуты сбросит все правила:
```
(sleep 120 && nft flush ruleset) &
```
И применяем конфиг:
```
nft -f /etc/nftables.conf
```
Дальше как с ssh, открываем соседний терминал и подключаемся. Если зашли, значит все хорошо, отменяем таймер:
```
kill %1
```
Если не зашли, просто ждем 2 минуты, правила сбросятся сами и можно спокойно искать ошибку.
Посмотреть что сейчас реально применено можно командой:
```
nft list ruleset
```
Осталось включить автозагрузку правил, иначе после перезагрузки сервер окажется без фаервола:
```
systemctl enable --now nftables
```
Если на сервере будет Docker, то учтите, что он пишет свои правила и может открыть порты контейнеров в обход вашего конфига. Это тема для отдельной статьи.
На этом все. Сервер обновлен, ssh пускает только по ключу и на нестандартном порту, а фаервол закрывает все, что мы явно не разрешили. С этого уже можно начинать ставить свои сервисы.
Дисклеймер: прикол с таймером нужен, потому что firewall может оборвать даже уже установленное соединение. В конфиге из пример есть строчка `ct state established,related accept`, что означает "разрешать подключения, которые уже установлены". Т.е. по идее разрыва не должно быть.