Часто на первом проекте кажется, что самое сложное позади: приложение готово, осталось только показать его миру. Но что, если сервер под угрозой?

В этой статье — простая и проверенная инструкция по настройке безопасного сервера для вашего первого fullstack-приложения. От SSH до SSL и двухфакторной аутентификации — рассказываю, как я защитил свой SaaS-проект Transcribator.

Шаг 1: Подключение и создание нового пользователя

Для начала подключаемся к серверу через IP-адрес и пароль, предоставленные провайдером. Обычно эти данные указываются в личном кабинете сервиса. Пример подключения:

ssh root@ваш_IP_адрес

Система запросит пароль, который вы получили от провайдера. После успешного входа создаём нового пользователя:

  1. Создаём пользователя:

    sudo adduser admin

    Система попросит ввести пароль для нового пользователя — выберите надёжный пароль и сохраните его.

  2. Добавляем пользователя в группу sudo:

    Что предоставляет ему административные права на сервере. Это необходимо для того, чтобы новый пользователь мог выполнять команды с повышенными привилегиями, такие как установка программного обеспечения или настройка системных параметров, не заходя под учетной записью root. Это улучшает безопасность, так как использование root-доступа напрямую считается рискованным

    sudo usermod -aG sudo admin
  3. Для проверки подключаемся в новом окне терминала от имени нового пользователя:

    ssh admin@ваш_IP_адрес

Теперь можно продолжить настройку.

Шаг 2: Установка nano

Прежде чем редактировать конфигурационные файлы, установим текстовый редактор nano. Это удобный и понятный инструмент для работы с файлами.

sudo apt install nano -y

Основные команды nano:

  • Ctrl+O – сохранить файл.

  • Ctrl+X – выйти из редактора.

  • Ctrl+K – вырезать строку.

Шаг 3: Настройка SSH

SSH (Secure Shell) позволяет безопасно управлять сервером удалённо, в отличие от обычных подключений, которые менее защищены. Если на вашем локальном компьютере ещё не настроены SSH-ключи, сгенерируйте их:

  1. Генерация SSH-ключей на локальном компьютере (локально):

    ssh-keygen -t rsa -b 4096

    Система попросит указать путь для сохранения ключа (можно оставить по умолчанию) и установить пароль (опционально).

  2. Копирование публичного ключа на сервер (локально):

    Выполните следующую команду, чтобы добавить ваш публичный ключ на сервер:

    ssh-copy-id admin@ваш_IP_адрес

    Если команда не доступна, можно вручную скопировать содержимое файла ~/.ssh/id_rsa.pub на локальном компьютере и добавить его в файл ~/.ssh/authorized_keys на сервере:

    cat ~/.ssh/id_rsa.pub | ssh admin@ваш_IP_адрес "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
  3. Запретить подключение по root и паролю:

    Открываем файл настройки SSH:

    sudo nano /etc/ssh/sshd_config

    Вносим изменения:

    PermitRootLogin no
    PasswordAuthentication no
  4. Перезапускаем SSH:

    sudo systemctl restart ssh
  5. В новом окне пробуем подключиться:

ssh -i ~/.ssh/id_rsa admin@<ваш IP>

Шаг 4: Установка и настройка брандмауэра

Брандмауэр (ufw) защищает сервер, разрешая подключение только на определённые порты.

  1. Устанавливаем ufw:

    sudo apt update
    sudo apt install ufw -y
  2. Открываем только нужные порты:

    • Порт 22 — для SSH (удалённое управление).

    • Порт 80 — для HTTP (незащищённые соединения).

    • Порт 443 — для HTTPS (защищённые соединения).

    sudo ufw allow 22
    sudo ufw allow 80
    sudo ufw allow 443
  3. Включаем брандмауэр:

    sudo ufw enable
  4. Проверяем статус:

    sudo ufw status

    Убедитесь, что отображаются разрешённые порты (22, 80, 443).

Шаг 5: Установка Fail2ban

Fail2ban защищает сервер от атак брутфорс, блокируя IP-адреса после нескольких неудачных попыток подключения.

  1. Установка:

    sudo apt install fail2ban -y
  2. Создаём конфигурацию:

    sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
    sudo nano /etc/fail2ban/jail.local
  3. Добавляем базовые правила:

    • bantime — время блокировки IP-адреса (30 минут).

    • findtime — период, за который учитываются неудачные попытки.

    • maxretry — максимальное число попыток перед блокировкой.

    [DEFAULT]
    bantime = 30m
    findtime = 10m
    maxretry = 5
    
    [sshd]
    enabled = true
  4. Перезапускаем Fail2ban:

    sudo systemctl restart fail2ban

Fail2ban будет анализировать логи SSH и блокировать подозрительные попытки подключения.

Шаг 6: Установка SSL

SSL (Secure Sockets Layer) обеспечивает шифрование данных между клиентом и сервером, предотвращая перехват данных злоумышленниками. Без SSL браузеры предупреждают пользователей о небезопасном соединении, что негативно сказывается на пользовательском опыте.

Сертификаты выдаются на определённое время, поэтому важно настроить их автоматическое обновление.

  1. Устанавливаем Certbot:

    sudo apt install certbot python3-certbot-nginx -y
  2. Получаем SSL-сертификат:

    sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
  3. Автоматическое обновление сертификатов:

    Открываем crontab:

    sudo crontab -e

    Добавляем строку:

    0 0 * * * certbot renew --quiet

Теперь ваши данные защищены шифрованием, а сертификаты будут обновляться автоматически.

Шаг 7: Настройка двухфакторной аутентификации

Двухфакторная аутентификация добавляет дополнительный уровень защиты, требуя ввода одноразового кода из приложения Google Authenticator.

  1. Устанавливаем Google Authenticator:

    sudo apt install libpam-google-authenticator -y
    google-authenticator

    Следуйте инструкциям и сохраните ключ для приложения аутентификации.

  2. Включаем двухфакторную аутентификацию:

    Открываем PAM:

    sudo nano /etc/pam.d/sshd

    Добавляем строку:

    auth required pam_google_authenticator.so
  3. Редактируем настройки SSH:

    sudo nano /etc/ssh/sshd_config

    Находим или добавляем такие значения для работы Google авторизации:

    KbdInteractiveAuthentication yes
    ChallengeResponseAuthentication yes
    PasswordAuthentication no
    PubkeyAuthentication yes
    UsePAM yes
    Match Address <IP1>,<IP2>,<IP3>
        AuthenticationMethods publickey
    
    Match all
        AuthenticationMethods keyboard-interactive

    Match Address - это разрешенные адреса по которым не будет запрашиваться пароль.
    Match all - для всех остальных запрашивается пароль из google и потом для пользователя admin под которым мы входим.
    IP - ваш текущий можно узнать на сайте Мой IP

  4. Перезапускаем SSH:

    sudo systemctl restart ssh

Теперь при попытке входа сервер запросит код из приложения Google Authenticator.

Итог

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

P.S. Моё приложение развернуто через Docker Compose, где объединены Nginx, PostgreSQL, Redis, бэкенд, фронтенд и лендинг. Если вам интересно, как это было реализовано, напишите в комментариях — я подготовлю статью с подробным описанием.

Комментарии (20)


  1. abcdsash
    03.01.2025 08:24

    а почему автор считает, что сервер по умолчанию будет на чем то отличном от венды?

    а вдруг на вендовсе сервере каком нить?


    1. Lainhard
      03.01.2025 08:24

      Потому что это действительно "по умолчанию"


    1. HyperWin
      03.01.2025 08:24

      Сервер на винде это сюр. Если только такими пользуетесь - ищите свой гайд.


  1. Lev3250
    03.01.2025 08:24

    Если уж меняем рут на что-то другое, то есть смысл указать рандомное имя, а не админ. Остальное на удивление ёмко и по делу. Спасибо!


  1. ToSHiC
    03.01.2025 08:24

    Если у вас mkdir -p ~/.ssh таки выполнится - вы потом долго будете дебажить, почему вход по ключу не работает. Права на директорию и файл должны быть 0700 и 0600 соответственно.

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


  1. aborouhin
    03.01.2025 08:24

    ufw с docker дружатся, мягко говоря, неочевидно. Да и в целом, у nftables на удивление простой и приятный синтаксис, так что лишний уровень абстракции в виде ufw не особо что упрощает, а когда захочется подкрутить что-то посложнее - наоборот, мешает.


    1. Kenya-West
      03.01.2025 08:24

      Эта проблема впоследствии решается Ansible. Например, у меня он сам после работы CI/CD ходит и выдирает из .env'ов и docker-compose.yml'ов значения, которые потом прописывает в ufw allow.

      Но сразу говорю, что мой совет из разряда "проблема воробьёв решается пушкой". Юзать Ansible на этапе, когда это твой первый сервер в жизни — это лютый оверкилл.

      Я лично до использования Ansible дорос самостоятельно, просто потому что личных серверов уже за два десятка перевалило.


      1. aborouhin
        03.01.2025 08:24

        Ну про Ansible я не стал писать ровно по этой же причине, что для первого сервера пока рановато. Хотя, с другой стороны, это очень сильно дисциплинирует записывать (и тем самым документировать) все свои настройки. Ну и в случае переноса на новый сервер разворачивать всё там одной командой (ну почти одной, юзера-то создать и SSH настроить придётся руками - разговор про Cloud-Init и тем более Terraform у нас тоже впереди...)

        Но с firewall'ом и тут, на мой взгляд, ufw только добавляет сложности. Хоть для него и есть community.general.ufw, но для чистого nftables куда удобнее сделать каталог /etc/nftables.conf.d/, куда Ansible'ом класть по шаблону файлик для каждого сервиса. А что-то более или менее сложное без ручной магии с /etc/ufw/before.rules всё равно не настроить, и вот тут уже начинаются чреватые ошибками костыли.


  1. pae174
    03.01.2025 08:24

    KbdInteractiveAuthentication yes
    ChallengeResponseAuthentication yes
    PasswordAuthentication no

    1. ChallengeResponseAuthentication это на самом деле алиас для KbdInteractiveAuthentication. То есть эти две штуки это одно и то же, но ChallengeResponseAuthentication позже могут и выкинуть на фиг.

    2. KbdInteractiveAuthentication=yes на самом деле делает возможным вход по паролю, так что последующая PasswordAuthentication=no становится бесполезной и дает вам только иллюзию безопасности.

    3. Выданный вам начальный рутовый пароль вообще-то надо сразу поменять на свой. Просто потому что ломать пароль вам все равно будут из-за KbdInteractiveAuthentication=yes а изначальный пароль скорее всего а) простой и б) еще часто посылается клиенту по электронной почте и, таким образом, тихо-мирно ждет своего часа.

    4. Про права доступа на файлы authorized_keys уже написали комментом выше.

    5. Нет никакого упоминания о том, что надо на первом коннекте по ssh сверить слепок серверного ключа. А то вдруг у вас там с самого начала MitM, а вы и не в курсе.


  1. kvazimoda24
    03.01.2025 08:24

    Так и подмывает спросить, а когда будут советы по настройке сервера от уборщицы, которая полы мыла в машинном зале?

    Фронтендер не должен заниматься настройкой сервера.

    Ufw — какое-то очень странное поделие, которое генерит правила в стиле iptables, а затем скармливает их ему, притом что iptables уже довольно давно является устаревшим и по сути ещё раз преобразует правила уже в формат nftables. И я вот не помню, нужно ли в ufw отдельно разрешать исходящий трафик.

    Есть сомнения в необходимости использования двухфакторной аутентификации при использовании SSH ключей.

    Почему бы не использовать ключи на эллептических кривых? Как для SSH, так и для Web сервера.

    Fail2ban для SSH сомнительное решение. Если отключили вход по паролю, то в целом подбор паролей ботами только логи засирает. Но эти боты прекрасно распознают настройки fail2ban, и долбятся с разных IP. И тут либо белые списки, либо knok-knok (или как там оно называется, когда чтобы порт тебе открыли, надо постучаться на какие-то другие порты в определённой последовательности).

    История про работу из под root тоже уже порядком достала. Если ты один админишь машину, то проще сразу под рутом логиниться по ключу. Всё равно первой командой после логина будет sudo -i. Ну или будешь ко всем командам приписывать sudo в начале. А вся эта история с безопасностью — это когда пишут правила для sudo, кому какие команды можно выполнять и под какими пользователями. Но тут надо ооочень грамотно подходить к вопросу, т.к. методов подняться до рута в этом случае ну очень много.

    По правам на каталог ~/.ssh уже сказали.


    1. aborouhin
      03.01.2025 08:24

      Фронтендер не должен заниматься настройкой сервера.

      Вот меня всегда поражает этот корпоративный снобизм. Конечно, если фронтэндер решил запилить свой проект, он должен взять кредит на многомиллионов, нанять бэкэндера, архитектора, пару аналитиков, девопса, девсекопса, тестировщика, маркетолога, бухгалтера, финансиста, юриста, водителя и секретаршу, - чтобы не дай Бог не сделать чужую работу... и поняв, что MVP не взлетел, подавать на личное банкротство :)


      1. kvazimoda24
        03.01.2025 08:24

        Снобизм? Ну так советы от этого фронтендера такие себе. Работать, конечно, будет, но больше смахивает на то, что человек где-то что-то услышал, но толком не разобрался что это и для чего.

        Ну и если это не страничка визитка, то подразумевается ещё и бекенд. Его тоже фронтендер писать будет? И тоже советы тут будет давать?

        Ведь, одно дело пилить проект для себя, а другое дело советы раздавать. Я вот немножко программирую на Си, Питон, JS, Bash... Но как-то советы раздовать по этому поводу мне хватает компетенции не раздавать, т.к. я прекрасно понимаю, что программист из меня паршивый.


        1. aborouhin
          03.01.2025 08:24

          Насчёт того, что с написанием гайдов для Хабра автор поторопился, я полностью согласен, ему тут по делу накидали даже относительно того, что он описал (а это только самая база и верхушка айсберга) Но стремление познавать смежные области я только поддерживаю. Когда сам разберёшься - уже и делегировать другим будет проще осознанно, если до этого дойдёт.


        1. IvanBodhidharma
          03.01.2025 08:24

          Ну и если это не страничка визитка, то подразумевается ещё и бекенд. Его тоже фронтендер писать будет?

          Почему бы и нет, что не так?


          1. kvazimoda24
            03.01.2025 08:24

            Да ничего. Просто код будет такого же качества, как эта статья.


  1. VenbergV
    03.01.2025 08:24

    1. Перестаньте править sshd_config! Это файл системных настроек. Который может быть затерт при обновлении системы. Для пользовательских настроек есть каталог sshd_config.d.

    2. Зачем RSA ключи для новых систем в 2025 году? Они скорее понадобятся для старых систем и старого оборудования. Для остального есть ed25519.

    3. Зачем ufw в новом сервере в 2025 году? Debian12, Ubuntu 22.04-24.04 используют nftables.

    4. ИМХО. Из-за отсутствия поддержки nftables в Docker, лучше перейти на Podman. Правда проскакивала информация, что новую версию Docker переведут на nftables. Но срок пока не определен.


    1. aborouhin
      03.01.2025 08:24

      • Перестаньте править sshd_config! Это файл системных настроек. Который может быть затерт при обновлении системы. Для пользовательских настроек есть каталог sshd_config.d.

      Все дистрибутивы, с которыми я сталкивался, в случае, если конфиг изменён пользователем, при обновлении в интерактивном режиме спрашивали, перезаписать ли его (по умолчанию предлагая оставить), в unattended режиме - молча оставляли пользовательскую версию. А вот если конфиг не менять - его правда молча перезапишет, и не факт, что очередное обновление не добавит каких-то новых дефолтных настроек, которые надо бы переопределить.

      Из-за отсутствия поддержки nftables в Docker, лучше перейти на Podman.

      Ну на самом деле docker с nftables работает, он вызывает iptables-nft, который транслирует правила в формат nftables. Беда в том, что если потом хочется добавить свои правила и перезапустить nftables - всё это автомагическое творчество docker'а сбрасывается. С podman ровно та же история, не заметил практической разницы. В общем, если есть автоматическая настройка правил nftables, например через Ansible, и что-то не вполне тривиальное, - то проще docker'у в настройках запретить создавать правила, вручную определять его сети и задавать правила для них.


  1. mr-andry
    03.01.2025 08:24

    Спасибо за статью!
    Дополнительно можно обратить внимание на автоматические обновления безопасности для дистрибутива (например, с помощью unattended-upgrades в Ubuntu), чтобы своевременно получать патчи уязвимостей. Также не лишним будет настроить мониторинг (например, Netdata или Prometheus + Grafana) — так можно быстро реагировать на рост нагрузки или подозрительную активность. В остальном, для первого развёртывания fullstack-проекта этого гида более чем достаточно!


  1. Balintrue
    03.01.2025 08:24

    • Ctrl+O – сохранить файл.

    Мне кажется, в nano для сохранения используется Ctrl+S


  1. melkorus
    03.01.2025 08:24

    Базовая настройка, под определенные нужды. Нет ничего плохого или хорошего в этом, просто гайд для людей которые чего то недопонимают. (Люди которые понимают, им он в принципе не нужен, те люди которые не понимают, не набив шишек не поймут). К сожалению, не нашел тут тип OS, на сколько понял что то Дебиан подобное (Вероятнее всего убунту). Так же видел комментарий по поводу Windows server, вообще проект и на IIS можно запилить, а вот стоит ли, это уже другой вопрос. Считаю , то что статья должна быть продолжением чего то изначального, если был посыл показать, как проще настроить свой сервер (локальный само собой).