Перший bootstrap сервера: від сирого VPS до базового production baseline
Великий конспект першого дня сервера: оновлення Ubuntu, timezone, базові утиліти, UFW, fail2ban, SSH-політика, nginx, MariaDB, PHP, Docker, reverse proxy і логіка того, чому на Linux hardening важливіший за класичний “антивірус”.
Перший bootstrap сервера: від сирого VPS до базового production baseline
Це той запис, якого мені самому дуже бракувало на старті. Не "ось список модних інструментів", а чесний і зрозумілий конспект того, як із сирого VPS зробити нормальну базу під реальні сервіси.
Вхідні дані були прості:
- OVH VPS
- Ubuntu 22.04 LTS
- користувач
ubuntuзsudo - домен
bombaworkflows.com
Ціль теж була проста: не просто "щоб відкривався сайт", а щоб сервер одразу будувався як маленька, але вже доросла інфраструктура. Без зайвої магії, зате з логікою.
Крок 1. Оновити систему одразу, поки вона ще чиста
Перший крок після входу на сервер:
sudo apt update && sudo apt full-upgrade -yЦе банально, але саме тут закривається величезна частина стартових ризиків:
- старі пакети
- старі залежності
- security fixes, які вже доступні
Я дуже люблю починати саме з цього, бо далі весь стек ставиться вже на актуальну систему, а не на застарілу базу.
Крок 2. Виставити правильний timezone
Час на сервері - це не дрібниця. Якщо він налаштований криво, потім криво виглядають:
- логи
- cron-завдання
- часові мітки в моніторингу
- сертифікати і scheduled jobs
У моєму випадку timezone був таким:
sudo timedatectl set-timezone Europe/Kyiv
timedatectlПісля цього всі логи, cron і ручні перевірки вже жили в звичному часовому просторі.
Крок 3. Доставити базовий набір утиліт
Щоб сервером було зручно користуватись, я одразу поставив мінімальний набір корисних інструментів:
sudo apt install -y curl wget git htop rsync unzip zip tree ncdu jqКожен із них швидко окупається:
curlдля HTTP-перевірокgitдля майбутніх деплоївhtopдля живої діагностикиrsyncдля копій і переносу данихtreeіncduдля розуміння файлової структуриjqдля роботи з JSON-відповідями
Крок 4. Закрити все зайве через UFW
Після оновлення системи я одразу хотів, щоб сервер був не просто доступний, а контрольовано доступний.
Початковий набір відкритих портів був мінімальний:
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verboseУ результаті зовні залишилися відкритими лише:
22для SSH80для HTTP443для HTTPS
Усе інше було закрито.
Це одна з найважливіших базових звичок: не відкривати порт "про всяк випадок". Якщо сервіс можна заховати за reverse proxy або повісити на 127.0.0.1, значить саме так і треба робити.
Крок 5. Те, що часто називають "антивірусом", на Linux виглядає інакше
На Windows логіка часто така: ставимо антивірус і ніби вже спокійні.
На серверному Linux базовий захист зазвичай починається не з класичного AV, а з таких шарів:
- актуальні пакети
- мінімум відкритих портів
fail2ban- адекватна SSH-політика
- сервіси, прив'язані до
localhost - reverse proxy як єдина публічна точка входу
Тобто замість класичного "антивірусу" на практиці я будував саме hardening baseline. Для Linux-сервера це набагато ближче до реальності.
Крок 6. Увімкнути fail2ban як анти-bruteforce шар
Щоб сервер не приймав безкінечні спроби логіну, я поставив fail2ban:
sudo apt install -y fail2ban
sudo systemctl enable --now fail2ban
sudo systemctl status fail2ban --no-pagerЙого роль дуже практична: ловити повторні невдалі спроби входу і тимчасово банити джерело.
Тобто якщо спростити, то саме це і є той "бан-файл/бан-механізм", про який багато хто говорить на старті серверної безпеки.
Крок 7. Налаштувати SSH без зайвого героїзму
Я спеціально не поспішав повністю відключати парольний вхід, бо хотів залишити собі запасний спосіб зайти під час активного налаштування.
Поточна SSH-політика вийшла такою:
PubkeyAuthentication yesPasswordAuthentication yesтимчасовоPermitRootLogin without-password
Це хороший компроміс для перших днів:
- вже є ключі
- root не входить звичайним паролем
- пароль для
ubuntuще можна залишити як тимчасовий backup path
Пізніше це точно варто дотиснути до повного key-only доступу, але не обов'язково в першу ж годину життя сервера.
Крок 8. Поставити веб- і runtime-шар
Базовий LEMP-стек я зібрав так:
sudo apt install -y nginx mariadb-server php8.1-fpm php8.1-mysqlДалі логіка була така:
nginxяк головний reverse proxyMariaDBслухає тількиlocalhostPHP-FPMпідключений через socket
Це вже дає нормальну базу для класичних сайтів, WordPress або проміжного стейджингу.
Крок 9. Захистити MariaDB і не світити її назовні
Для бази даних ключове правило дуже просте: якщо до неї не треба ходити з інтернету, вона не повинна слухати інтернет.
Тут базові дії були такими:
sudo mysql_secure_installation
sudo ss -tulpn | grep 3306Після цього MariaDB залишилась доступною лише локально, а не на публічному інтерфейсі.
Крок 10. Поставити Docker і compose plugin
Окремий пласт завдань я відразу хотів запускати в контейнерах, тому наступним шаром став Docker:
- Docker Engine
- Docker Compose plugin
І ще важливий практичний момент:
- користувача
ubuntuя додав уdockergroup
Щоб далі працювати із сервісами без постійного sudo.
Крок 11. Виносити сервіси на 127.0.0.1, а не в публічний інтернет
Один із найсильніших архітектурних принципів цього сервера звучить так:
усі сервіси через reverse proxy, мінімум відкритих портів
Саме тому інфраструктурні сервіси були прив'язані не до 0.0.0.0, а до localhost:
127.0.0.1:3001дляUptime Kuma127.0.0.1:9443дляPortainer
Зовні користувач бачить лише:
8044322
А все інше ховається за nginx.
Крок 12. Побудувати reverse proxy під домени
Публічна схема з самого початку вийшла дуже чистою:
Internet
-> Cloudflare
-> Nginx
-> dev / kuma / portainer
-> локальні сервіси і web rootОкремі віртуальні хости були налаштовані для:
dev.bombaworkflows.comkuma.bombaworkflows.comportainer.bombaworkflows.com
Логіка по них така:
devспочатку дивився в/var/www/testsite/publickumaпроксіювався на127.0.0.1:3001portainerпроксіювався наhttps://127.0.0.1:9443
Крок 13. HTTPS одразу, а не "колись потім"
Сертифікати були випущені для:
dev.bombaworkflows.comkuma.bombaworkflows.comportainer.bombaworkflows.com
Ключова перевірка тут була двошарова:
sudo certbot renew --dry-run
systemctl status snap.certbot.renew.timer --no-pagerЦе теж важливий DevOps-урок: мало один раз отримати сертифікат. Треба ще перевірити, що renewal дійсно працюватиме.
Який стан сервера вийшов після першого циклу
Після всіх цих кроків VPS перестав бути просто "Ubuntu у хмарі" і перетворився на вже цілком серйозну базу:
- ОС оновлена
- timezone виставлено
- встановлені робочі утиліти
- UFW обмежує трафік
fail2banловить brute-force- SSH уже частково захардений
nginxпрацює як єдиний вхідний шарMariaDBне стирчить назовні- Docker готовий під інфраструктурні сервіси
- домени живуть за reverse proxy
- HTTPS вже автоматично поновлюється
Команди швидкої перевірки, які я б залишив собі як cheat sheet
Ось той набір, який дуже корисно мати під рукою після bootstrap:
sudo ufw status verbose
sudo systemctl status fail2ban --no-pager
sudo ss -tulpn
sudo nginx -t
systemctl status nginx --no-pager
docker ps
curl -I https://dev.bombaworkflows.com
curl -I https://kuma.bombaworkflows.com
curl -I https://portainer.bombaworkflows.comЦієї добірки вже достатньо, щоб за хвилину зрозуміти, в якому стані firewall, reverse proxy, Docker і публічні сервіси.
Висновок
Найголовніше відкриття першого дня було не в тому, який саме пакет ставити першим. Найголовніше - зрозуміти правильний порядок мислення:
- онови систему
- закрий мережу
- нормалізуй доступ
- побудуй єдину точку входу
- ховай сервіси за reverse proxy
- автоматизуй те, що зламається без автоматизації
Саме цей порядок і перетворює сирий VPS на базовий production-ready фундамент. Не ідеальний, не завершений, але вже такий, на який можна спиратись і спокійно нарощувати наступні шари: бекапи, моніторинг, compose, git deploy і далі вже CI/CD та IaC.