Files
2021-cadpoint-ru/deploy_to_masterhost-vm.md
2026-04-03 12:27:25 +03:00

78 KiB
Raw Permalink Blame History

Инструкция по развёртыванию на VDS-хостинге (виртуальная машина) на примере masterhost.ru

1: Создание пользователя

Изначально есть только root-доступ. Если мы залогированы под root, то следует создать пользователя от имени которого мы будем осуществлять все действия (позже root-доступ будет закрыт). Создадим пользователя <ssh_user>:

sudo useradd -c 'WEB-user' -m <ssh_user>

Присвоим ему пароль:

sudo passwd <ssh_user>

Дадим ему права работать от имени root. Для этого откроим на редактирование конфигурационный файл /etc/sudoers:

nano /etc/sudoers

и после строки root ALL=(ALL:ALL) ALL добавим в него строку:

<ssh_user>     ALL=(ALL:ALL) ALL

Сохраняем конфигурационный файл Ctrl+O и Enter, а выходим из редактора Ctrl+X.

ВАЖНО: Если по какой-то причине файл /etc/sudoers пустой -- это означает, что служба sudoers не установлена. Её нужно установить: apt-get install sudo.

Разлогируемся оз под root.

logout

Теперь можно залогироваться от имени пользователя <ssh_user>.

Установим командную оболочку bash для пользователя:

chsh -s /bin/bash

2: Настройка SSH и окружения клиента

Ограничиваем доступ root и повышение безопасности SSH

После завершения перезагрузки произведённой в конце предыдущего этапа мы можем заходить на наш сервер по SSH под новым, только что созданным, пользователем [user] и паролем [user_pwd]. Теперь следует сделать небольшие изменения в файле /etc/ssh/sshd_config -- конфигурации ssh-доступа:

sudo nano /etc/ssh/sshd_config

Полное описание настроек sshd_config находится на сайте Ubuntu. Там содержатся вполне разумные рекомендации по увеличению безопасности. Откроем на редактирование конфигурационный файл /etc/ssh/sshd_config:

sudo nano /etc/ssh/sshd_config

И изменим порт на котором будет отвечать SSH:

Port 2002

Так же дописываем в конце следующие две строки в которых и запрещаем ssh-вход пользователя root и разрешаем доступ нашему пользователю <ssh_user>:

DenyUsers root
AllowUsers <ssh_user>

Сохраняем конфигурационный файл Ctrl+O и Enter, а выходим из редактора Ctrl+X.

Чтобы настройки подействовали нужно перезапустить ssh-сервис:

sudo service ssh restart

Проверим, что сервис корректно перезапустился:

sudo service sshd status

Увидим, что все работает:

● ssh.service - OpenBSD Secure Shell server
     Loaded: loaded (/lib/systemd/system/ssh.service; enabled; vendor preset: enabled)
     Active: active (running) since Wed 2022-05-25 01:01:24 MSK; 2 days ago
       Docs: man:sshd(8)
             man:sshd_config(5)
   Main PID: 647 (sshd)
      Tasks: 1 (limit: 1066)
     Memory: 5.8M
     CGroup: /system.slice/ssh.service
             └─647 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups

Разлогируемся -- logout. Входим ещё раз и можем проверяем, что теперь пользователя root больше в систему по SSH не пускают. Логируется вновь созданным пользователем [user].

Упрощение идентификации сервера при входе

В конфигурационном файле /etc/ssh/sshd_config есть специальный параметр Banner в котором указывается файл, который следует показать при входе (обычно /etc/ssh_banner). Его отображение упрощает идентификацию сервера при входе, его сложнее спутать с другими, особенно когда работа идёт одновременно с терминалами нескольких серверов. Но он этот баннер отображается только при входе через SSH. Существует более радикальное решение, отображающееся и при обычном входе. Оно упростит идентификацию даже если мы работаем с консоли (например, в случае нескольких виртуальных серверов на одном физическом).

Очистим файл /etc/motd, и поместим туда какой-нибудь красивый ASCII-арт текст из ASCII-генератора. :

sudo nano /etc/motd

Например, вот так:

███████╗░░░░░░██████╗███████╗██████╗░░██████╗░ MASTERHOST.RU HOSTED:
██╔════╝░░░░░██╔════╝██╔════╝██╔══██╗██╔════╝░
█████╗░░████╗╚█████╗░█████╗░░██████╔╝██║░░██╗░ cadpoint.ru
██╔══╝░░╚═══╝░╚═══██╗██╔══╝░░██╔══██╗██║░░╚██╗ oknardia.ru
███████╗░░░░░██████╔╝███████╗██║░░██║╚██████╔╝ cube2.ru
╚══════╝░░░░░╚═════╝░╚══════╝╚═╝░░╚═╝░╚═════╝░ venturebox.org

Сохраняем баннер Ctrl+O и Enter, а выходим из редактора Ctrl+X.

Раскрашиваем оболочку bash

Чтобы сделать красочно-разноцветным нашу командную строку, откроем на редактирование файл настроек оболочки bash пользователя .bashrc:

nano ~/.bashrc

находим там строку #force_color_prompt=yes раскомментируем её (и удаляем в ней #). И чтобы совсем отпад, находим блок:

if [ "$color_prompt" = yes ]; then
    PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
else
    PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
fi

И меняем на блок

if [ "$color_prompt" = yes ]; then
    PS1='${debian_chroot:+($debian_chroot)}\[\033[01;31m\]\u\[\033[01;34m\]@\[\033[01;31m\]\h\[\033[00m\]:\[\033[00;34m\]\w\[\033[00m\]\>
else
    PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
fi

Всё! Перелогиниваемся чтобы настройки подействовали.

logout

3. Настраиваем службу времени (необязательно)

Устанавливаем службу точного времени nlp:

apt-get install ntp

Откроем на редактирование конфигурационный файл /etc/ntp.conf:

sudo nano /etc/ntp.conf

Заменяем буржуйские адреса серверов точного времени на отечественные:

# Specify one or more NTP servers.

# Use servers from the NTP Pool Project. Approved by Ubuntu Technical Board
# on 2011-02-08 (LP: #104525). See http://www.pool.ntp.org/join.html for
# more information.
pool ntp.msk-ix.ru iburst prefer
pool 194.190.168.1 iburst
pool 2001:6d0:ffd4::1 
# pool ntp.ix.ru
# pool 1.ubuntu.pool.ntp.org iburst
# pool 2.ubuntu.pool.ntp.org iburst
# pool 3.ubuntu.pool.ntp.org iburst

# Use Ubuntu's ntp server as a fallback.
pool ntp.msk-ix.ru

Сохраняем баннер Ctrl+O и Enter, а выходим из редактора Ctrl+X.

Чтобы настройки подействовали сервис точного времени нужно перезапустить:

sudo service ntp restart

Проверим, что сервис корректно перезапустился:

sudo service ntp status

Увидим, что все ок:

● ntp.service - Network Time Service
     Loaded: loaded (/lib/systemd/system/ntp.service; enabled; vendor preset: enabled)
     Active: active (running) since Wed 2022-05-25 01:01:24 MSK; 2 days ago
       Docs: man:ntpd(8)
   Main PID: 642 (ntpd)
      Tasks: 2 (limit: 1066)
     Memory: 1.5M
     CGroup: /system.slice/ntp.service
             └─642 /usr/sbin/ntpd -p /var/run/ntpd.pid -g -u 112:119

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

ntpq -p

Увилим что-то типа:

     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
 ntp.msk-ix.ru   .POOL.          16 p    -   64    0    0.000    0.000   0.000
 194.190.168.1   .POOL.          16 p    -   64    0    0.000    0.000   0.000
 2001:6d0:ffd4:: .POOL.          16 p    -   64    0    0.000    0.000   0.000
 ntp.ix.ru       .POOL.          16 p    -   64    0    0.000    0.000   0.000
*ntp.ix.ru       .GPS.            1 u  450 1024  377    2.442   -0.254   0.117

4. Настраиваем бренмауэр для использования только с допустимыми внешними интерфейсами (защита портов)

Устанавливаем iptables для управления IP-соединения и iptables-persistent для сохранения конфигураций настроенных соединений и их автоматического подключения после перегрузке компьютера.

sudo -S apt-get install iptables

Далее создадим в домашней папке bash-скрипт prepare-iptable.sh:

nano ~/prepare-iptable.sh

...и вставим в него нижеследующий текст (исправьте если необходимо, следуя рекомендациям):

#!/bin/bash

# You have to set the rules of your firewall on your server only with the
# services used outside the VM.

# Вы должны установить правила брандмауэра на своем сервере только
# с сервисами, используемыми вне виртуальной машины.

echo "Сетевые настройки: разрешения трафика и портов";
echo "";
echo "ЭТОТ СКРИПТ НУЖНО ЗАПУСКАТЬ С ПРАВАМИ АДМИНИСТРАТОРА (ИЗ ПОД SUDO).";
echo "ОБАЗАТЕЛЬНО ИЗ KVM-КОНСОЛИ! ИЗ ПОД SSH МОЖЕТ СЛОМАТЬ ВАШУ ВИРТАЛКУ!";
echo "===================================================================";
echo "";
read -p "Хотите чтобы скрипт сделал это (возможно, и сломает)? (Y/N):" -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]
then
    exit 1
fi

# Установим пакет iptables (если он уже установлен, попытка повторной установки "проскочит"
sudo -S apt-get install iptables

# ПРИСВАИВАЕМ ПЕРМЕННЫЕ:
# $IPT     = "/usr/sbin/iptables"
# $WAN     = текущий внешний сетевой интерфейс виртуалки
# $WAN_IP  = текущий IP V4 назначенный на внешний сетевой интерфейс виртуалки
# $WAN_IP6 = текущий IP V6 назначенный на внешний сетевой интерфейс виртуалки
export IPT="$(sudo -S which iptables)"
export WAN="$(ip add | grep 'BROADCAST' |  awk '{print $2}' | cut -d ':' -f 1)"
export WAN_IP="$(ip ad | grep 'inet ' | awk '(NR == 2)' | awk '{print $2}' | cut -d '/' -f 1)"
export WAN_IP6="$(ip ad | grep 'inet6 ' | awk '(NR == 2)' | awk '{print $2}' | cut -d '/' -f 1)"

echo "-------------------ТЕКУЩИЕ ПРАВИЛА--------------------";
$IPT -L -v -n

# СБРАСЫВАЕМ старые правила:
$IPT -F
$IPT -X

# БАЗОВЫЕ правила (политика по умолчанию) -- все обрубаем!
$IPT -P INPUT DROP
$IPT -P OUTPUT DROP
$IPT -P FORWARD DROP

# Разрешаем локальный траффик для loopback (для localhost)
$IPT -A INPUT -i lo -j ACCEPT
$IPT -A OUTPUT -o lo -j ACCEPT

# Разрешаем пинги
# $IPT -A INPUT -p icmp --icmp-type echo-reply -j ACCEPT
# $IPT -A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT
# $IPT -A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT
# $IPT -A INPUT -p icmp --icmp-type echo-request -j ACCEPT

# Разрешаем исходящие соединения с самого сервера
$IPT -A OUTPUT -o $WAN -j ACCEPT

# Состояние ESTABLISHED говорит о том, что это не первый пакет в соединении.
# Пропускать все уже инициированные соединения, а также дочерние от них.
# Так мы разрешим использование текущего порта на который настроен и
# открыт, в настощий момент, SSH а еще доступ к репозиториям для получения
# новых пакетов и их обновлений.
$IPT -A INPUT -p all -m state --state ESTABLISHED,RELATED -j ACCEPT
# Разрешить новые, а так же уже инициированные и их дочерние соединения
$IPT -A OUTPUT -p all -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
# Разрешить форвардинг для уже инициированных и их дочерних соединений
$IPT -A FORWARD -p all -m state --state ESTABLISHED,RELATED -j ACCEPT

# Включаем фрагментацию пакетов. Необходимо из-за разных значений MTU
$IPT -I FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

# Отбрасывать все пакеты, которые не могут быть идентифицированы
# и поэтому не могут иметь определенного статуса.
$IPT -A INPUT -m state --state INVALID -j DROP
$IPT -A FORWARD -m state --state INVALID -j DROP

# Приводит к связыванию системных ресурсов, так что реальный
# обмен данными становится не возможным, обрубаем
$IPT -A INPUT -p tcp ! --syn -m state --state NEW -j DROP
$IPT -A OUTPUT -p tcp ! --syn -m state --state NEW -j DROP

# Открываем только нужные порты на вход:
# !!! указать свой порт, который вы указали для SSH ранее !!!)
$IPT -A INPUT -i $WAN -p tcp --dport 2002 -j ACCEPT
# порт для web сервера (для http)
$IPT -A INPUT -i $WAN -p tcp --dport 80 -j ACCEPT
# порт для web сервера (для https)
$IPT -A INPUT -i $WAN -p tcp --dport 443 -j ACCEPT

## Логирование
## Все что не разрешено, но ломится отправим в цепочку undef
# $IPT -N undef_in
# $IPT -N undef_out
# $IPT -N undef_fw
# $IPT -A INPUT -j undef_in
# $IPT -A OUTPUT -j undef_out
# $IPT -A FORWARD -j undef_fw

# Логируем все из undef
# $IPT -A undef_in -j LOG --log-level info --log-prefix "-- IN -- DROP "
# $IPT -A undef_in -j DROP
# $IPT -A undef_out -j LOG --log-level info --log-prefix "-- OUT -- DROP "
# $IPT -A undef_out -j DROP
# $IPT -A undef_fw -j LOG --log-level info --log-prefix "-- FW -- DROP "
# $IPT -A undef_fw -j DROP

# Записываем правила
$IPT-save  > $IPT.rules

# Выводим правила на экран:
echo "----------ПРАВИЛА ДЛЯ TCP-ПОРТОВ УСТАНОВЛЕНЫ----------";

cat $IPT.rules

echo "---------------------И ПРИМЕНЕНЫ----------------------";

$IPT -L -v -n

echo "------------------------------------------------------";
echo "";
echo "Правила сохранены в файле: $IPT.rules";
echo "";
echo "Для восстановления правил запустите скрипт повторно или";
echo "исполните команду:";
echo "";
echo "sudo $IPT-restore < $IPT.rules";

echo "";
echo "";
echo " _._     _,-'\"\"\`-._";
echo "(,-.\`._,'(       |\\\`-/|";
echo "    \`-.-' \\ )-\`( , o o)";
echo "          \`-    \\\`_\`\"'-  Mi-mi-mi... Ok!";
echo "";

Сохраняем скрипт Ctrl+O и Enter, а выходим из редактора Ctrl+X.

Далее нужно зайти на вирталку через KVM-консоль и выполнить:

sudo bash ~/prepare-iptable.sh

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

sudo iptables -L -v -n

Увидим примерно следующее:

Chain INPUT (policy DROP 150 packets, 5768 bytes)
 pkts bytes target     prot opt in     out     source               destination         
   28  2204 ACCEPT     all  --  lo     *       0.0.0.0/0            0.0.0.0/0           
   81  7202 ACCEPT     all  --  *      *       0.0.0.0/0            0.0.0.0/0            state RELATED,ESTABLISHED
    2    84 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0            state INVALID
    8   608 DROP       tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp flags:!0x17/0x02 state NEW
    1    52 ACCEPT     tcp  --  eth0   *       0.0.0.0/0            0.0.0.0/0            tcp dpt:2002
    0     0 ACCEPT     tcp  --  eth0   *       0.0.0.0/0            0.0.0.0/0            tcp dpt:80
    0     0 ACCEPT     tcp  --  eth0   *       0.0.0.0/0            0.0.0.0/0            tcp dpt:443

Chain FORWARD (policy DROP 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 TCPMSS     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp flags:0x06/0x02 TCPMSS clamp to PMTU
    0     0 ACCEPT     all  --  *      *       0.0.0.0/0            0.0.0.0/0            state RELATED,ESTABLISHED
    0     0 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0            state INVALID

Chain OUTPUT (policy DROP 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
   28  2204 ACCEPT     all  --  *      lo      0.0.0.0/0            0.0.0.0/0           
   69 10113 ACCEPT     all  --  *      eth0    0.0.0.0/0            0.0.0.0/0           
    0     0 ACCEPT     all  --  *      *       0.0.0.0/0            0.0.0.0/0            state NEW,RELATED,ESTABLISHED
    0     0 DROP       tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp flags:!0x17/0x02 state NEW

Чтобы сохранить настройки и чтобы они автоматически вступали в силу в случае перезагрузки установим пакет iptables-persistent:

sudo -S apt-get install iptables-persistent

Будет запрошено сохранить ли и восстанавливать настройки в случае перезагрузки для IP-V4 и IP-V6 -- оба раза отвечаем Y.

В будущем, если будут меняться правила фильтрации, то сохранить sudo netfilter-persistent save, восстановить sudo netfilter-persistent start. Для детальных разъяснений см. по ссылке.

5. Установиv защиту DoS (брутфорса) по ssh

DoS и DDoS-атака -- это агрессивное внешнее воздействие на сервер, проводимое с целью перегрузить его запросами, доведения его до отказа или брутфорс-взлома (brute force -- грубая сила, например, подбор паролей перебором и т.п.).

Если атака проводится с одиночного компьютера -- ее называют DoS (Denial of Service), если с нескольких, распределенных в сети, компьютеров — DDoS (Distributed Denial of Service).

Защиту от DoS (включая медленные атаки) может обеспечить пакет fail2ban.

Установим его (уже должен быть установлен):

sudo apt-get install fail2ban

Для его настройки перепишем конфигурационный файл /etc/fail2ban/jail.local:

sudo nano /etc/fail2ban/jail.local

И поместим туда следующее:

# == новый конфиг /etc/fail2ban/jail.local =====================
[DEFAULT]
#email, на который присылать уведомления                             
destemail       = root 
# подключить прафила бана из '/etc/fail2ban/action.d/iptables-multiport.conf'
banaction       = iptables-multiport 
# исключаем из потенциального бана ip машины с которых можем подключаться
ignoreip        = <trusted_ip_1> <trusted_ip_2> <trusted_ip_3> <trusted_ip_4>

#### правила для SSH ####                             
[sshd]
enabled = true 
port = ssh,2002 
# filter — подключить правила фильтрации из '/etc/fail2ban/filter.d/sshd.conf'
filter  = sshd
# logpath — какой лог наблюдаем (на тот случай, если он не по умолчанию)
logpath = /var/log/auth.log 
# bantime — время (секунды) на которое баним. На неделю — 60*60*24*7=604800
bantime = 604800
# maxretry — число попыток (1) и получаешь бан!                            
maxretry = 1 
# findtime — определяет длительность интервала в секундах, за которое
# событие должно повториться определённое количество раз, после чего санкции
# вступят в силу. Если специально не определить этот параметр, то будет
# установлено значение по умолчанию равное 600 (10 минут). Проблема в том,
# что ботнеты, участвующие в «медленном брутфорсе», умеют обманывать
# стандартное значение. Иначе говоря, при maxretry равным 6,
# атакующий может проверить 5 паролей, затем выждать 10 минут,
# проверить ещё 5 паролей, повторять это снова и снова, и его IP забанен
# не будет. В целом, это не угроза, но всё же лучше банить таких ботов
findtime = 7200

# СТАРЫЙ КОНФИГ ПО УМОЛЧАНИЮ
# [DEFAULT]
# bantime = 600
# findtime = 60
# maxretry = 6
# banaction = iptables-multiport
# [sshd]
# enabled = true

Сохраняем конфигурационный файл Ctrl+O и Enter, а выходим из редактора Ctrl+X.

Перезапустим fail2ban:

sudo service fail2ban restart

Проверим статус:

sudo service fail2ban status

Увидим, что все работает:

● fail2ban.service - Fail2Ban Service
     Loaded: loaded (/lib/systemd/system/fail2ban.service; enabled; vendor preset: enabled)
     Active: active (running) since Tue 2022-05-17 14:33:22 MSK; 1h 53min ago
       Docs: man:fail2ban(1)
    Process: 7635 ExecStartPre=/bin/mkdir -p /run/fail2ban (code=exited, status=0/SUCCESS)
   Main PID: 7640 (f2b/server)
      Tasks: 5 (limit: 1066)
     Memory: 11.0M
     CGroup: /system.slice/fail2ban.service
             └─7640 /usr/bin/python3 /usr/bin/fail2ban-server -xf start

May 17 14:33:22 vm2203242538 systemd[1]: Starting Fail2Ban Service...
May 17 14:33:22 vm2203242538 systemd[1]: Started Fail2Ban Service.
May 17 14:33:22 vm2203242538 fail2ban-server[7640]: Server ready

Включаем автозапуск fail2ban при заугрузке системы:

sudo systemctl enable fail2ban.service

5. Установка и настройка СУБД (MariaDB), создание пользователей

Следуем инструкциям, и у нас получится примерно такой порядок действий:

Установка

Устанавливаем сервер MariaDB (полный аналог MySQL, но без патронажа Oracle) и пакет MariaDB-клиента для разработчиков (чтобы после корректно установился MySQL-коннектор для Python):

sudo apt install mariadb-server libmysqlclient-dev

Отредактируем конфигурационный файл, чтобы сменить кодовые таблицы в будущих базах с utf8mb4 (со всякими глупыми эможи) на православную utf8:

sudo nano /etc/mysql/mariadb.conf.d/50-server.cnf

Найдём в конфигурационном файле строки:

character-set-server  = utf8mb4
collation-server      = utf8mb4_general_ci

и заменим на:

character-set-server    = utf8
collation-server        = utf8_general_ci

Сохраняем конфигурационный файл Ctrl+O и Enter, а выходим из редактора Ctrl+X.

Перезапустим систему управления базы данных:

sudo service mysql restart

Проверим статус:

sudo service mysql status

Если все сделали правильно, должны увидеть что-то типа:

● mariadb.service - MariaDB 10.6.7 database server
     Loaded: loaded (/lib/systemd/system/mariadb.service; enabled; vendor preset: enabled)
     Active: active (running) since Tue 2022-08-09 09:25:26 MSK; 3 days ago
       Docs: man:mariadbd(8)
             https://mariadb.com/kb/en/library/systemd/
   Main PID: 1249 (mariadbd)
     Status: "Taking your SQL requests now..."
      Tasks: 9 (limit: 935)
     Memory: 82.5M
        CPU: 13min 42.376s
     CGroup: /system.slice/mariadb.service
             └─1249 /usr/sbin/mariadbd

Создание пользователей

Для этого запустим mysql с супер-правами root:

sudo mysql

Создаём пользователя <ssh_user>, зададим ему пароль и дадим привилегии на все:

CREATE USER '<ssh_user>'@'localhost' IDENTIFIED BY '*********************';
GRANT ALL PRIVILEGES ON *.* TO '<ssh_user>'@'localhost';

Создаем базу данных django_cadpoint для нашего сайта:

CREATE DATABASE django_cadpoint DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;

И создадим пользователя cadpint, пароль для него и дадим ему права работать только с базой сайта:

CREATE USER 'cadpint'@'localhost' IDENTIFIED BY '*******';
GRANT ALL PRIVILEGES ON django_cadpoint.* TO 'cadpint'@'localhost';

Проверим, что установлена правильная часовая зона времени:

SHOW VARIABLES LIKE '%time_zone%';

Типа, она должна быть MSK:

+------------------+--------+
| Variable_name    | Value  |
+------------------+--------+
| system_time_zone | MSK    |
| time_zone        | SYSTEM |
+------------------+--------+
2 rows in set (0.001 sec)

Выходим из MariaDB:

quit;

переносим базу

6. ПОДГОТОВКА WEB-СЕРВЕРА NGINX

Устанавливаем nginx

sudo apt-get install nginx

На всякий случай (как выяснилось позже), лучше поставить nginx plus. В нем есть динамические модули. И один их таких динамических модулей -- geoip -- может понадобиться для ограничения посещения сайта только пользователями России. Смотрим инструкцию по установке nginx plus.

Убедиться, что он запущен и работает корректно можно командой:

sudo service nginx status

Результат должен быть примерно таким:

● nginx.service - A high performance web server and a reverse proxy server
     Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
     Active: active (running) since Thu 2022-01-20 18:45:16 MSK; 56s ago
       Docs: man:nginx(8)
   Main PID: 2200 (nginx)
      Tasks: 2 (limit: 1037)
     Memory: 4.6M
     CGroup: /system.slice/nginx.service
             ├─2200 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
             └─2201 nginx: worker process

Зайдем по ip нашего сервера или через localhost:

curl -I http://localhost/

Получим ответ:

HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Tue, 03 May 2022 18:16:46 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 03 May 2022 12:49:44 GMT
Connection: keep-alive
Vary: Accept-Encoding
ETag: "627124e8-264"
Accept-Ranges: bytes

Настройка GZIP

По умолчанию модуль GZIP установлен в NGINX. Он осуществляет сжатие данных «на лету», они отправляются nginx, и, после получения, распаковываются браузерами (само-собой теми, которые поддерживают такую возможность). При этом между веб-сервером и браузером передается меньшее данных, ускоряя тем самым . Сжатие использует ресурсы сервера, поэтому лучше всего сжимать только те файлы, которые хорощо «сжимаютсяу» (а еще лучще, есди еще и могут кэшироваться на стороне браузера). Текстовые файлы сжимаются хорошо, JPEG или PNG уже сжаты по своей природе и большого результата при сжатии GZIP можно не ожидать.

Настроим модуль GZIP в NGINX. Для этого открываем на редактирование конфигурационный файл nginx /etc/nginx/nginx.conf:

sudo nano /etc/nginx/nginx.conf

Находим в нем кусочек касающийся GZIP:

        ##
        # Gzip Settings
        ##

и пишем в нем следующее (там будет закомментированный блок, можно использовать его):

    gzip_proxied any;
    gzip_comp_level 6;
    gzip_buffers 16 8k;
    gzip_http_version 1.1;
    gzip_disable "msie6";
    gzip_vary  on;
    gzip_min_length 512;
    gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript application/vnd.ms-fontobject application/x-font-ttf font/opentype image/svg+xml image/x-icon;

Если интересно, расшифруем что тут происходит:

  • gzip_proxied any - сжимать данные ответов для proxy-серверов.
  • gzip_comp_level 6 - устанавливаем, степень сжатия файлов. Чем выше число, тем выше уровень сжатия и использование ресурсов. уровень сжатия, 1
  • минимальное, 9 - максимальное.
  • gzip_buffers 16 8k - задаёт число и размер буферов, в которые будет сжиматься ответ. По умолчанию размер одного буфера равен размеру страницы. В зависимости от платформы это или 4K, или 8K.
  • gzip_http_version 1.1 - директива используется для ограничения сжатия gzip для браузеров, поддерживающих протокол HTTP/1.1. Если браузер не поддерживает его, вероятно, что он не поддерживает и gzip.
  • gzip_disable "msie6" - исключаем IE6 из браузеров, которые будут получать сжатые файлы (этот древний браузер не поддерживает GZIP).
  • gzip_vary on - включает добавление в ответ заголовка "Vary: Accept-Encoding" (для IE4-6 это приведёт к не кешированию данных из-за бага).
  • gzip_min_length 512 - сообщаем NGINX не сжимать файлы размером менее 512 байт.
  • gzip_types - отображает все типы MIME, которые будут сжаты. В этом случае список включает страницы HTML, таблицы стилей CSS, файлы Javascript и JSON, файлы XML, иконки (BMP-изображения очень даже сжимаются), изображения SVG и веб-шрифты.

Ещё в этом же /etc/nginx/nginx.conf нужно заменить дерективу user www-data; на user root; (в самой первой строчке... почему-то на Ubuntu 2022.04 по другому не работает uwsgi... похоже не может получить доступ к сокету... странно...)

Сохраняем конфигурационый файл Ctrl+O и Enter, а выходим из редактора Ctrl+X и перезагружаем nginx:

sudo service nginx restart

Проверка новой конфигурации

Выполним тестовый запрос:

curl -H "Accept-Encoding: gzip" -I http://localhost/

Мы получим ответ:

HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Tue, 03 May 2022 18:16:53 GMT
Content-Type: text/html
Last-Modified: Tue, 03 May 2022 12:49:44 GMT
Connection: keep-alive
Vary: Accept-Encoding
ETag: W/"627124e8-264"
Content-Encoding: gzip

Как видим, сжатие включилось.

7. Развёртывание окружения проекта

Настройка виртуального окружения проекта

Установим python с набором для разработчиков (для сборки коннектора к сСУБД), пакетный менеджер и утилиту для создания виртуального окружения python:

sudo apt-get install python3-pip python3-virtualenv python3-dev

Проверим, что установлена нужная нам версия Python (нам нужно Python 3.8.10):

python3 -V

Теперь создадим папку для нашего сайта ~/cadpoint и развернём в ней виртуальное окружение, указав, что в нем нужно использовать версию Python 3.7 /usr/bin/python3.7. Будет создан каталог, с файлами виртуального окружение (версия Python, установщик пакетов pip, wheel, setuptools а, в будущем, и все пакеты, батарейки, свистелки и хрюкалки нашего проекта).

cd $HOME
mkdir -p cadpoint

Чтобы для нашего проекта "заморозить" версию Python и все необходимые пакеты, и изьежать возможного кофликта при обновлении системного Python или с пакетвми установленными в других проектвх создадим виртуальное окружение в папке нашего сайта ($HOME/cadpoint):

virtualenv -p python3 $HOME/cadpoint/env

Активируем созданное виртуальное окружение:

source $HOME/cadpoint/env/bin/activate

Проверить, что теперь мы работаем в виртуальном окружении можно дав команды:

python -V
pip -V

Увидим

pip 20.0.2 from /home/<ssh_user>/cadpoint/env/lib/python3.8/site-packages/pip (python 3.8)

Можно обновить менеджер пакетов pip по последней версии (сперва получим его с помощью curl в папку tmp, а после запустим установщик через Python)

curl https://bootstrap.pypa.io/get-pip.py > ~/tmp/get-pip.py
python3 ~/tmp/get-pip.py

Попробуем снова

pip -V

Увидим

pip 22.0.4 from /home/<ssh_user>/cadpoint/env/lib/python3.8/site-packages/pip (python 3.8)

ФУУ!!

8. Установка пакетов необходимых проекту

Точный состав пакетов, обычно, находится в файле requarement_dev_prod_dreamhost.txt. Но на всякий случай приведем список пакетов здесь (он может отличаться от действительно актуального, в файле) т.к., на самом деле, большинство пакетов будут установлены автоматически как зависимости:

Пакет Версия Назначение Зависимости
django 3.2.15 Фреймворк Django притащит с собой пакеты: asgiref, pytz и sqlparse
mysqlclient 2.1.1 Коннектор MySQL нет
django-filer 2.2.2 Система управления медиа-файлами с фишками подготовки ресайз-картинок, превьюшек и прочими плюшками притащит с собой пакеты: Unidecode, django-js-asset, django-mptt, django-polymorphic, easy-thumbnails и pillow
django-ckeditor 6.4.2 Wysiwyg-редактор (ckeditor) для админки нет
django-taggit 3.0.0 Ситема управления тегами нет
pytils-safe 0.3.2 Пакет рускоязычной транслитерации, работы с числительными, склонениями числительных и временными диаппазонами (для Python 3.x) нет
urllib3 1.26.11 пакет для работы с web-запросами (проекту этот пакет нужен для работы с API внешний HTML-типографов) нет
django-ckeditor-filebrowser-filer 0.3.0 Плугин для дружбы Wysiwyg-редквтора (ckeditor) и django-filer нет, все зависимости уже притащил django-filer

Все эти пакеты устанавливаются в виртуальное окружение с помощью пакетного менеджера pip в последовательности:

pip install Django==3.2.15
pip install mysqlclient==2.1.1
pip install django-filer==2.2.2
pip install django-ckeditor==6.4.2
pip install django-taggit==3.0.0
pip install django-ckeditor-filebrowser-filer
pip install pytils-safe==0.3.2
pip install urllib3==1.26.11

Разворачиваем и тестируем проект

Создаём папки для хранения сокета и логов:

mkdir -p $HOME/cadpoint/logs
mkdir -p $HOME/cadpoint/socket

Переносим проект... например на тест-деплоя z7 (или на на бочем проекте мастерхост) архивируем проект. Само-cобой это надо делать там, где проект находится, не на вирталке, а dev-серере (или или там находится наш проект). Например, так:

zip -9 -r cadpoint.zip public/ rsvo_new/ config/

Копируем архив по ssh на нашу виртуалку (masterhost или nic.ru)

scp -P 2002 cadpoint.zip <ssh_user>@<server_ip>:~/<project_root>

Возвращаемся на нашу виртуалку хостинга и разархивируем:

cd ~/cadpoint
unzip cadpoint.zip

Внимание, на разных серверах расположение папок проекта может отличаться. При архивации илм после распаковки, возможно, нам потребуется переместить некоторые папки. Мы должны получить следующую структуру проекта:

+---public
¦   +---media
¦   L---static
+---rsvo_new
¦   +---rsvo_new
¦   +---templates
¦   L---web
+---logs
+---config
+---env
L---socket

Папки logs, config, env и socket на данном этапе могут отсутствовать. Мы создадим их позже:

Еще важно, если у нас установилось Django 4 (а изначально проект создавался для Django 3.2.х) то нам надо сделать небольшие изменения в коде.

В файле проекта urls.py (он должен быть расположен тут: /home/<ssh_user>/cadpoint/rsvo_new/rsvo_new/urls.py) из-за изменения расположения методов в библиотеках django следует закоменить строчку:

from django.conf.urls import url, include

И добавить после нее:

from django.conf.urls import include
from django.urls import re_path as url

должно получиться следующее:

# from django.conf.urls import url, include
from django.conf.urls import include
from django.urls import re_path as url

Подобные изменения следует сделать так же в файле urls.py батарейки ckeditor_filebrowser_filer /home/<ssh_user>/cadpoint/env/lib/python3.8/site-packages/ckeditor_filebrowser_filer/urls.py. Вместо:

from django.conf.urls import url

Следует написать:

from django.urls import re_path as url

должно получиться:

# from django.conf.urls import url
from django.urls import re_path as url

Теперь можно произвести перенос статических файлов админки и батареек в папку для web-статики:

source ~/cadpoint/env/bin/activate
cd ~/cadpoint/rsvo_new
python manage.py collectstatic

Теперь произведем подготовку нашей базы под проект. Для этого произведем миграции:

python manage.py makemigrations
python manage.py migrate

Миграция создаст все необходимые таблицы и заполнит таблицу миграций django_cadpoint.django_migration.

Теперь можно произвести перенос базы с dev-проекта (или с другого рабочего сервера) в базу нашей виртуалки. Следует учесть, что переносить таблицу django_cadpoint.django_migration не следует (мы произвели чистую миграцию, и если мы перезапишем эту таблицу, то с высокой вероятностью сломаем наш проект).

Настало время проверить, что наше web-приложение Django работает.

Временно откроем порт 8080 через iptables:

sudo iptables -A INPUT -i eth0 -p tcp --dport 8080 -j ACCEPT

Запустим приложение с dev-режиме на нашем IP и этом порту:

python manage.py runserver <server_ip>:8080

Если мы обратимся из браузера по адресу http://<server_ip>:8080, то увидим как на нашей виртуалке "бежит лог". В браузере будет отображаться что-то похожее на наш проект (с тем отличием, что не будет работать статика... т.е. мы не увидим картинок, не будут подгружены стили, JavaScript и все такое).

Завершаем работу web-сервера разработчика, нажав Control+C.

8. Конфигурируем nginx под наш проект


Создаем и правим конфиг /cadpoint/config/cadpoint.conf:

nano /cadpoint/config/cadpoint.conf

Помещаем в него следующее:

#  Разработка сайта CADPOINT.RU
#  ==  Конфикурационный файл nginx cadpoint.conf

# Описываем апстрим-потоки которые должен подключить Nginx
# Для каждого сайта надо настроить свйо поток, со своим уникальным именем.
# Если будете настраивать несколько python (django) сайтов - измените название upstream

upstream cadpoint-django {
    # расположение файла Unix-сокет для взаимодействие с uwsgi
    server unix:///home/<ssh_user>/cadpoint/socket/cadpoint.sock;
    #             /home/<ssh_user>/cadpoint/socket/cadpoint.sock;
    # также можно использовать веб-сокет (порт) для взаимодействие с uwsgi. Но это медленнее
    # server 127.0.0.1:8001; # для взаимодействия с uwsgi через веб-порт
    keepalive_requests 200;
}

# конфигурируем сервер
server {
    # server_name           <server_ip>;    # доменное имя сайта
    server_name     cadpoint.ru;        # доменное имя сайта
    listen          80;
    charset         utf-8;              # кодировка по умолчанию
    access_log  /home/<ssh_user>/cadpoint/logs/cadpoint-access.log;    # логи с доступом
    error_log   /home/<ssh_user>/cadpoint/logs/cadpoint-error.log;     # логи с ошибками
    client_max_body_size 100M; # максимальный объем файла для загрузки на сайт (max upload size)
    error_page  404  /404.html;
    error_page  500  /500.html;

    location /media	{ alias	/home/<ssh_user>/cadpoint/public/media;  }	# Расположение media-файлов Django
    location /static	{ alias	/home/<ssh_user>/cadpoint/public/static; }	# Расположение static-файлов Django

    location /robots.txt	{ root	/home/<ssh_user>/cadpoint/public; }	# Расположение robots.txt
    location /favicon.ico	{ root	/home/<ssh_user>/cadpoint/public; }	# Расположение favicon.ico
    location /favicon.gif	{ root	/home/<ssh_user>/cadpoint/public; }	# Расположение favicon
    location /favicon.png	{ root	/home/<ssh_user>/cadpoint/public; }	# Расположение favicon
    location /favicon.svg	{ root	/home/<ssh_user>/cadpoint/public; }	# Расположение favicon
    location /author.txt	{ root	/home/<ssh_user>/cadpoint/public; }	# Расположение author.txt
    location = /404.html	{
         root	/home/<ssh_user>/cadpoint/cadpoint/templates/404.html;
         internal;
    }
    location = /500.html	{
         root	/home/<ssh_user>/cadpoint/cadpoint/templates/500.html;
         internal;
    }
    # location ~ \.(xml|html|htm)$
    location ~ \.(html|htm|ico|svg|png|gif|jpg|jpeg)$	{
        root  /home/<ssh_user>/cadpoint/public;        # Расположение статичных *.xml, *.html и  *.txt
    }

    location / {
        uwsgi_pass           cadpoint-django;            # upstream обрабатывающий обращений
        include              uwsgi_params;               # конфигурационный файл uwsgi;
        proxy_set_header Host $host;

        # ограничение количества запросов c одного IP-адреса с помощью модуля Limit_Req_Module
        # limit_req zone=one burst=20 nodelay;
        # one     — имя зоны настроеной в /etc/nginx/nginx.conf (для всех сайтов сервера) в блоке http {…}
        # burst   — максимальный всплеск активности, можно регулировать до какого значения запросов
        #           в секунду может быть всплеск запросов;
        # nodelay — незамедлительно, при достижении лимита подключений, выдавать код 503
        #           (Service Unavailable) для этого IP

        fastcgi_keep_conn    on;
        uwsgi_read_timeout   1800;     # вдруг некоторые запросы очень долго обрабатываются?
        uwsgi_send_timeout   200;      # на всякий случай время записи в сокет побольше... 
    }
}

# переадресация с www на "без" www
server {
    server_name     www.cadpoint.ru;
    listen          80;
    return          301 http://cadpoint.ru$request_uri;
}

Делаем симлинк этого конфигурационного файла в папку конфигов сайтов nginx:

sudo ln -s ~/cadpoint/config/cadpoint.conf /etc/nginx/sites-enabled/

Протестируем конфигурацию:

sudo nginx -t

Если все ок, можем "мягко" перезапустить nginx:

sudo nginx -s reload

Или "по-жёсткому":

sudo service nginx restart

Теперь если мы из браузера обратимся по адресу http://cadpoint.ru/robots.txt, http://cadpoint.ru/favicon.ico, http://2cadpoint.ru/favicon.png или http://cadpointu/favicon.svg то увидим соответсвующий контент.

9. НАСТРОЙКА uWSGI

И uWSGI, и Python-плагин uWSGI уже должен быть установлен. Но если он не установлены, или для проверки, инсталляция производится из так:

sudo apt-get install uwsgi uwsgi-plugins-all

Проверим, что uWSGI работает:

sudo service uwsgi status

Увидим примерно следующее:

● uwsgi.service - LSB: Start/stop uWSGI server instance(s)
     Loaded: loaded (/etc/init.d/uwsgi; generated)
     Active: active (exited) since Thu 2022-01-20 20:36:20 MSK; 2min 57s ago
       Docs: man:systemd-sysv-generator(8)
      Tasks: 0 (limit: 1037)
     Memory: 0B
     CGroup: /system.slice/uwsgi.service

Создаём и правим ini-конфиг ~/cadpoint/config/cadpoint.ini для uWSGI:

nano ~/cadpoint/config/cadpoint.conf

Помещаем туда следующее:

# ===  Конфикурационный файл uwsgi cadpoint.ini
[uwsgi]

# НАСТРОЙКИ ДЛЯ DJANGO
# Корневая папка проекта (полный путь)
chdir           = /home/<ssh_user>/cadpoint/cadpoint
# Django wsgi файл cadpoint/wsgi.py записываем так:
module          = cadpoint.wsgi
# полный путь к виртуальному окружению
home            = /home/<ssh_user>/cadpoint/env
# полный путь к файлу сокета
socket          = /home/<ssh_user>/cadpoint/socket/cadpoint.sock
# Исходящие сообщения в лог
daemonize       = /home/<ssh_user>/cadpoint/logs/cadpoint_uwsgi.log

# ЗАГАДОЧНЫЕ НАСТРОЙКИ, ПО ИДЕЕ ОНИ НУЖНЫ, НО И БЕЗ НИХ ВСЁ РАБОТАЕТ
# расположение wsgi.py
wsgi-file       = /home/<ssh_user>/cadpoint/cadpoint/cadpoint/wsgi.py
# расположение виртуального окружения (как оно работает если этот параметр не указан, не ясно)
virtualenv      = /home/<ssh_user>/cadpoint/env
# имя файла при изменении которого происходит авторестарт приложения
# (когда этого параметра нет, то гичего не авторестартится, но с ним все рестартится.
# Cтоит изменить любой Python-исходник проекта, как изменения сразу вступают в силу.
touch-reload    = /home/<ssh_user>/cadpoint/logs/cadpoint_reload
py-autoreload	= 5

#  НАСТРОЙКИ ОБЩИЕ
# быть master-процессом
master          = true
# максимальное количество процессов
processes       = 1
# если uWSGI устнаовлен как сервис через apt-get то нужно установить еще плугин:
# sudo apt-get install uwsgi-plugin-python
# и добавить в этот конфиг: plugin = python
plugin          = python3
                        # права доступа к файлу сокета. По умолчанию должно хватать 664. Но каких-то прав не хватает, поэтому 666.
chmod-socket    = 666
# очищать окружение от служебных файлов uwsgi по завершению
vacuum          = true
# количество секунд после которых подвисший процес будет перезапущен
# Так как некоторе скрипты требуют изрядно времени (особенно полная переиндексация) то ставим значение побольще
harakiri      = 2600
# В общем случае, при некотых значениях harakiri логах uWSGI может вываливаться предупреждение:
# WARNING: you have enabled harakiri without post buffering. Slow upload could be rejected on post-unbuffered webservers
# можно оставить harakiri закоментированным, но нам нужно 900 и на него не ругается. Ругается на 30.

# разрешаем многопоточность
enable-threads  = true
vacuum          = true
thunder-lock    = true
max-requests    = 500

# пользователь и группа пользователей от имени которых запускать uWSGI
# указываем www-data: к этой группе относится nginz, и ранее мы включили в эту группу нашего [user]
uid             = root
gid             = root


print           = ---------------- Запущен uWSGI для cadpoint ----------------

Делаем симлинк этого конфигурационного файла в папку конфигов uwsgi:

sudo ln -s ~/cadpoint/config/cadpoint.ini /etc/uwsgi/apps-enabled/

Не забываем перезапустить uwsgi:

sudo service uwsgi restart

Еще раз проверим, что uWSGI работает:

sudo service uwsgi status

Все. Сайт развёрнут. Далее можно проводить улучшайзинг и усиливать защиту:

10. Нагрузочное тестирование (нужен dockers)

Нагрузочное тестирование будем проводить при помощи Яндекс.Танк. Он будет "обстреливать" наш сайт запросами.

Для запускать Яндекс.Танк проще всего использовать Dockers. Т.о. Dockers должен быть установлен на машину, с которой будем "обстреливать" сайт (и это не виртуалка хостинга... "обстреливать" самих себя бессмысленно).

Создаем каталог yandex.tank (в моем случае M:/VM/yandex.tank) и в нем помещаем файл load.yaml следующего содержания (не забываем корректировать URL, если необходимо):

overload:
  enabled: true
  package: yandextank.plugins.DataUploader
  token_file: "token.txt"
phantom:
  address: 2022.rsvo.ru:80
  header_http: "1.1"
  headers:
    - "[Host: 2022.rsvo.ru]"
    - "[Connection: close]"
  uris:
    - /
    - /about/
    - /about/management
    - /about/news
    - /about/news/184-fgup-rsvo-prinyalo-uchastie-v-ucheniyah-mchs?p=0&n=5
    - /about/about/news/186-uchebnyij-tsentr-fgup-rsvo-prinyal-uchastie-v-mero?p=1&n=2
    - /about/news/132-gromkij-maks-2017?p=30&n=1
    - /about/news/128-fgup-rsvo-prinyalo-uchastie-v-dne-innovatsij-mchs?p=29&n=2
    - /wired-radio-broadcasting/moskva-abonentam-yuridicheskim-litsam
    - /upac
    - /integrated-security/solutions-for-industrial
    - /integrated-security/safe-cities-solutions
    - /sound-technical-support/
    - /about/contacts
    - /about/smi
    - /about/nagradyi-i-blagodarnosti
  load_profile:
    load_type: rps
    # schedule: line(5, 5000, 3m)
    # schedule: const(1,30s) line(1,1000,2m) const(10000,5m)
    schedule: line(10,5000,2m) const(100,1m)
  ssl: true
autostop:
  autostop:
    - http(5xx,10%,5s)
console:
  enabled: true
telegraf:
  enabled: false

Так же создаем пустой файл token.txt в той же папке. ВНИМАНИЕ файл должен быть в кодировке ANSI (хоть он и пустой, но это важно).

Теперь можем запустить Яндекс.Танк:

docker run --rm -v M:/VM/yandex.tank:/var/loadtest -it direvius/yandex-tank

11. Установка SSL Let's Encrypt (переход с http на https)

Хорошую инструкцию по установке Let's Encript: https://www.digitalocean. com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-22-04

Lets Encrypt — это удостоверяющий центр сертификации (ЦС), который предоставляет доступный способ получения и установки бесплатных сертификатов TLS/SSL , тем самым обеспечивая шифрование HTTPS на веб-серверах. Lets Encrypt предоставляет программный клиент Certbot, который пытается автоматизировать большинство (если не все) необходимые шаги установки сертификатов, настройки web-севера nginx (и Apache тоже) и периодическое обновления сертификатов.

Установка Certbot

Если на сервере ранее была установлена более старая версия certbot, следует удалить ее:

sudo apt remove certbot

Certbot рекомендует использовать для установки пакет моментальных снимков (снапшотов). Пакеты Snap работают почти со всеми дистрибутивами Linux, но для управления пакетами Snap требуется, чтобы вы сначала установили snapd. Ubuntu 22. 04 поставляется с поддержкой снапшотов «из коробки», поэтому нужно начать с того, что убедитесь, что ядро snapd обновлено:

sudo snap install core; sudo snap refresh core

После этого можно установить certbot:

sudo snap install --classic certbot

Наконец, свяжем certbot с командой создания снапшотов. Это не обязательно, но по умолчанию снимки обычно менее навязчивы, поэтому создают случайных конфликтов с какими-либо другими системными пакетами:

sudo ln -s /snap/bin/certbot /usr/bin/certbot

Теперь, Certbot установлен. Можно запустить его и получить наш сертификат.

Получение сертификата и тюнинг конфигурации Nginx

Certbot должен иметь возможность найти правильный блок server в конфигурации Nginx. В частности, он ищет директиву server_name, соответствующую домену, для которого вы запрашиваете сертификат. В нашем случае, это домен cadpoint.ru. В предыдущем пункте мы создали этот конфигурационный файл в каталоге ~/cadpoint/config/cadpoint.conf и связали его через симлинк с каталогом настроек nginx /etc/nginx/sites-enabled/. Плагин Certbot Nginx позаботится о перенастройке этого конфигурационного файла и перезагрузке nginx

Получаем сертификаты

sudo certbot --nginx -d cadpoint.ru -d www.cadpoint.ru

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

IMPORTANT NOTES:
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/cadpoint.ru/fullchain.pem
Key is saved at: /etc/letsencrypt/live/cadpoint.ru/privkey.pem
This certificate expires on 2022-11-01.
These files will be updated when the certificate renews.
Certbot has set up a scheduled task to automatically renew this certificate in the background.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
If you like Certbot, please consider supporting our work by:
* Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
* Donating to EFF: https://eff.org/donate-le

Серификат загружен, конфигурация nginx изменена и, даже, настроен редирект c http на https... В принципе на этом можно закончить. Но будет не лишним добавить немного тюнинга. Certbot в конфиг nginx для нашего сайта подключает файл /etc/letsencrypt/options-ssl-nginx.conf (он единый для nginx подключается в конфиги всех сайтов работающих по ssl с сертификатом Let's Encrypt). Нужно внести изменения в него:

sudo nano /etc/letsencrypt/options-ssl-nginx.conf

В частности нам нужно изменить дерективы ssl_protocols, ssl_ciphers, resolver, keepalive_requests и ssl_session_cache. В результате наш файл должен выглядеть так:

# This file contains important security parameters. If you modify this file
# manually, Certbot will be unable to automatically provide future security
# updates. Instead, Certbot will print and log an error message with a path to
# the up-to-date file that you will need to refer to when manually updating
# this file. Contents are based on https://ssl-config.mozilla.org

# ssl_session_cache shared:le_nginx_SSL:10m;
ssl_session_cache builtin:1000 shared:le_nginx_SSL:25m;
ssl_session_timeout 1440m;
ssl_session_tickets off;

# ssl_protocols SSLv2 TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
ssl_protocols SSLv2 SSLv3 TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;

# ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384";
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';

resolver 77.88.8.8;
keepalive_requests 200;

Сохраняем конфигурационый файл Ctrl+O и Enter, а выходим из редактора Ctrl+X. И еще надо добавить поддержку cnfylfhnf http2 (много поточная загрузка web-страниц). Откроем конфигурационный файл nginx для нашего сайта:

nano ~/cadpoint/config/cadpoint.conf

И добавим изменим директиву:

    listen 443 ssl; # managed by Certbot

на директиву:

    listen 443 ssl http2; # managed by Certbot

Перезагружаем nginx чтобы применились изменения:

sudo service nginx restart

12. Блокируем и баним зловредов (бот-сканеры и http-досеров)

Ограничения числа запросов в nginx (не обязательно, но вдруг)

Настроим nginx на ограничения числа запросов c одного IP-адреса с помощью модуля Limit Req Module.

Открываем файл /etc/nginx/nginx.conf (конфиг для всех сайтов на сервере):

sudo nano /etc/nginx/nginx.conf

И в блок http {…} добавляем строку:

http {
    ...
    limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
    ...
}

где:

  • $binary_remote_addr — переменная Nginx содержащая IP с которого пришёл запрос;
  • zone=one — имя зоны. Если настройка делается в нескольких vhost файлах, имена должны быть разные;
  • 10m — размер зоны. 1m может содержать 16000 состояний, т.е. 16000 уникальных IP-адресов;
  • rate=1r/s — разрешено 1 запрос в секунду. Так же можно указать количество запросов в минуту (30r/m — 30 запросов в минуту)

Затем открываем на редактирование конфигурационный файл nginx для нашего сайта:

nano ~/cadpoint/config/cadpoint.conf

И в блок server {…}, добавим строку:

        limit_req zone=one burst=20 nodelay;

где:

  • one — имя зоны настроеной в /etc/nginx/nginx.conf (для всех сайтов сервера) в блоке http {…};
  • burst — максимальный всплеск активности, можно регулировать до какого значения запросов
  • в секунду может быть всплеск запросов;
  • nodelay — незамедлительно, при достижении лимита подключений, выдавать код 503
  • (Service Unavailable) для этого IP.

Строку limit_req zone=one burst=5 nodelay; можно добавить как непосредственно в блок server {…}, и тогда будет ограничиваться число запросов ко всем файлам сайта, так и в расположенные в нем блоки location … {…}. Целесообразнее ограничить число запросов к uwsgi_pass, т.к. он отвечает только за странички сайта (без статики... статика может быть запрощена клиентом довольна, т.к. одна страничка может содержать внутри себя много встроенных картинок, стилей и скриптов).

Таким образом наш блок location … {…} будет выглядеть так:

    location / {
        uwsgi_pass           cadpoint-django;            # upstream обрабатывающий обращений
        include              uwsgi_params;               # конфигурационный файл uwsgi;
        proxy_set_header Host $host;
        limit_req zone=one burst=5 nodelay;
        fastcgi_keep_conn    on;
        uwsgi_read_timeout   1800;
        uwsgi_send_timeout   200; 
    }
}

Баним ботов и подозрительную активность

Настроим fail2ban для выявления ботов, которые ищут скрипты, дампы баз, логи, ключи и тому подобное, анализируя error-лог. Создаем конфиг-файл фильтра /etc/fail2ban/filter.d/nginx-noscript.conf:

sudo nano /etc/fail2ban/filter.d/nginx-noscript.conf

Со следующим содержанием:

[Definition]
failregex = .*client: <HOST>.*GET.*(\.php|\.asp|\.aspx|\.exe|\.pl|\.cgi|\.scgi|\.log|\.sql|\.jsp|\.csv|\.sh|\.key|\.py|\.pyc|\.asmx|\.asax)
# failregex = ^<HOST> -.*GET.*(\.php|\.asp|\.aspx|\.exe|\.pl|\.cgi|\.scgi|\.log|\.sql|\.jsp|\.csv|\.sh|\.key|\.py|\.pyc|\.asmx|\.asax)
ignoreregex =

Проверим, что фильтр написан верно:

fail2ban-regex ~/cadpoint/logs/cadpoint-error.log /etc/fail2ban/filter.d/nginx-noscript.conf

Создаем еще одни фильтр для выявления подозрительной активности (ищут адинки популярных cms, log-и, ключи и тому подобное) анализируя access лог /etc/fail2ban/filter.d/nginx-manual.conf:

sudo nano /etc/fail2ban/filter.d/nginx-manual.conf

со следующим содержанием:

[Definition]
failregex = ^<HOST> -.*GET.*wp-content/
            ^<HOST> -.*GET.*wp-admin/.*
            ^<HOST> -.*GET.*wp-includes/.*
            ^<HOST> -.*GET.*administrator/
            ^<HOST> -.*GET.*user/register/
            ^<HOST> -.*GET.*bitrix/admin/
            ^<HOST> -.*GET.*minify/
            ^<HOST> -.*GET.*(?:a|A)dmin/
            ^<HOST> -.*GET.*netcat/
            ^<HOST> -.*GET.*koobooCMS/
            ^<HOST> -.*GET.*apanel/
            ^<HOST> -.*GET.*netcat/
            ^<HOST> -.*GET*/\.git/config
            ^<HOST> -.*GET*/\.well-known/
            ^<HOST> -.*GET.*(\.sql|\.php5|\.mdb|\.db|\.yml|\.cgi|\.scgi|\.log|\.sql|\.jsp|\.csv|\.sh|\.key|\.py|\.pyc|\.asmx|\.asax)
ignoreregex =

Снова проверим, что фильтр написан верно:

fail2ban-regex ~/cadpoint/logs/cadpoint-access.log /etc/fail2ban/filter.d/nginx-manual.conf

Далее, редактируем файл /etc/fail2ban/jail.local для настройки параметров подключения и работы фильтра:

sudo nano /etc/fail2ban/jail.local

Дописываем строки:

# ФИЛЬТР nginx-noscript 
[nginx-noscript]
enabled  = true
filter   = nginx-noscript
port     = http,https
action = iptables-multiport[name=NoAuthFailures, port="http,https"]
logpath  = /var/log/nginx/*error*.log
           /home/<ssh_user>/cadpoint/logs/*error*.log
bantime  = 86400
maxretry = 6
findtime = 7200

# ФИЛЬТР manual 
[nginx-manual]
enabled  = true
filter   = nginx-manual
port     = http,https
action = iptables-multiport[name=NoAuthFailures, port="http,https"]
logpath  = /var/log/nginx/*access*.log
           /home/<ssh_user>/cadpoint/logs/*access*.log
bantime  = 86400
maxretry = 6
findtime = 7200

Перезапустим fail2ban:

sudo service fail2ban restart

Чтобы посмотреть информацию по заблокированным ip по нашему фильтру используем команду:

sudo fail2ban-client status nginx-noscript
sudo fail2ban-client status nginx-manual

Чтобы разбанить IP (например 8.8.8.8) можно воспользоваться командой:

sudo fail2ban-client set nginx-noscript unbanip 8.8.8.8

13. Ротация логов

Обычно процесс ротации логов в системе уже установлен и работает. Но на всякий случай установить его можно так:

sudo apt-get install logrotate

Для настройки ротации логов нашего сайта нужно отредактировать конфигурационный файл ротации для nginx /etc/logrotate.d/nginx

sudo nano /etc/logrotate.d/nginx

И добавить в него (в конце) следующее:

/home/<ssh_user>/cadpoint/logs/*.log {
        daily
        missingok
        rotate 14
        compress
        delaycompress
        notifempty
        create 0644 www-data adm
        sharedscripts
        prerotate
                if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
                        run-parts /etc/logrotate.d/httpd-prerotate; \
                fi \
        endscript
        postrotate
                invoke-rc.d nginx rotate >/dev/null 2>&1
        endscript
}

После чего процесс ротации надо перезапустить:

sudo service logrotate restart

Убедимся, что ротация работает:

sudo service logrotate status

Увидим:

● logrotate.service - Rotate log files
     Loaded: loaded (/lib/systemd/system/logrotate.service; static; vendor preset: enabled)
     Active: inactive (dead) since Wed 2022-05-18 16:47:06 MSK; 4s ago
TriggeredBy: ● logrotate.timer
       Docs: man:logrotate(8)
             man:logrotate.conf(5)
    Process: 20860 ExecStart=/usr/sbin/logrotate /etc/logrotate.conf (code=exited, status=0/SUCCESS)
   Main PID: 20860 (code=exited, status=0/SUCCESS)

14. Геограничения

? https://stackoverflow.com/questions/62213884/how-install-the-geoip2-module-on-a-nginx-running-in-a-production-environment

https://docs.nginx.com/nginx/admin-guide/installing-nginx/installing-nginx-plus/ https://docs.nginx.com/nginx/admin-guide/dynamic-modules/geoip2/

https://nginx.org/ru/docs/http/ngx_http_geoip_module.html https://dev.maxmind.com/?lang=en