chore: updated
This commit is contained in:
1 parent
d1bbf0a505
commit
5281f9b989
4 files changed
+580
-27
No files matched your search
@@ -1,2 +1,7 @@
|
||||
all:
|
||||
hugo new content $(path)
|
||||
|
||||
publish:
|
||||
git add .
|
||||
git commit -m 'chore: $(msg)'
|
||||
git push
|
||||
+28
-25
@@ -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).
|
||||
- [Почта](mailto:xalex.sokol@gmail.com)
|
||||
@@ -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`, что означает "разрешать подключения, которые уже установлены". Т.е. по идее разрыва не должно быть.
|
||||
Reference in new issue
Block a user