# Инструкция по развёртыванию на VDS-хостинге (_виртуальная машина_) на примере masterhost.ru ## 1: Создание пользователя Изначально есть только root-доступ. Если мы залогированы под **root**, то следует создать пользователя от имени которого мы будем осуществлять все действия (позже root-доступ будет закрыт). Создадим пользователя ****: ```shell sudo useradd -c 'WEB-user' -m ``` Присвоим ему пароль: ```shell sudo passwd ``` Дадим ему права работать от имени root. Для этого откроим на редактирование конфигурационный файл `/etc/sudoers`: ```shell nano /etc/sudoers ``` и после строки `root ALL=(ALL:ALL) ALL` добавим в него строку: ```editorconfig ALL=(ALL:ALL) ALL ``` Сохраняем конфигурационный файл `Ctrl+O` и `Enter`, а выходим из редактора `Ctrl+X`. **ВАЖНО**: Если по какой-то причине файл `/etc/sudoers` пустой -- это означает, что служба sudoers не установлена. Её нужно установить: `apt-get install sudo`. Разлогируемся оз под root. ```shell logout ``` Теперь можно залогироваться от имени пользователя ****. Установим командную оболочку bash для пользователя: ```shell chsh -s /bin/bash ``` ## 2: Настройка SSH и окружения клиента ### Ограничиваем доступ root и повышение безопасности SSH После завершения перезагрузки произведённой в конце предыдущего этапа мы можем заходить на наш сервер по SSH под новым, только что созданным, пользователем [user] и паролем [user_pwd]. Теперь следует сделать небольшие изменения в файле `/etc/ssh/sshd_config` -- конфигурации ssh-доступа: ```shell sudo nano /etc/ssh/sshd_config ``` Полное описание настроек sshd_config [находится на сайте Ubuntu](https://help.ubuntu.ru/wiki/ssh). Там содержатся вполне разумные рекомендации по увеличению безопасности. Откроем на редактирование конфигурационный файл ` /etc/ssh/sshd_config`: ```shell sudo nano /etc/ssh/sshd_config ``` И изменим порт на котором будет отвечать SSH: ```editorconfig Port 2002 ``` Так же дописываем в конце следующие две строки в которых и запрещаем ssh-вход пользователя **root** и разрешаем доступ нашему пользователю ****: ``` DenyUsers root AllowUsers ``` Сохраняем конфигурационный файл `Ctrl+O` и `Enter`, а выходим из редактора `Ctrl+X`. Чтобы настройки подействовали нужно перезапустить ssh-сервис: ```shell sudo service ssh restart ``` Проверим, что сервис корректно перезапустился: ```shell 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-генератора. : ```shell sudo nano /etc/motd ``` Например, вот так: ``` ███████╗░░░░░░██████╗███████╗██████╗░░██████╗░ MASTERHOST.RU HOSTED: ██╔════╝░░░░░██╔════╝██╔════╝██╔══██╗██╔════╝░ █████╗░░████╗╚█████╗░█████╗░░██████╔╝██║░░██╗░ cadpoint.ru ██╔══╝░░╚═══╝░╚═══██╗██╔══╝░░██╔══██╗██║░░╚██╗ oknardia.ru ███████╗░░░░░██████╔╝███████╗██║░░██║╚██████╔╝ cube2.ru ╚══════╝░░░░░╚═════╝░╚══════╝╚═╝░░╚═╝░╚═════╝░ venturebox.org ``` Сохраняем баннер `Ctrl+O` и `Enter`, а выходим из редактора `Ctrl+X`. ### Раскрашиваем оболочку bash Чтобы сделать красочно-разноцветным нашу командную строку, откроем на редактирование файл настроек оболочки bash пользователя `.bashrc`: ```shell 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 ``` Всё! Перелогиниваемся чтобы настройки подействовали. ```shell logout ``` ## 3. Настраиваем службу времени (необязательно) Устанавливаем службу точного времени nlp: ```shell apt-get install ntp ``` Откроем на редактирование конфигурационный файл `/etc/ntp.conf`: ```shell sudo nano /etc/ntp.conf ``` Заменяем буржуйские адреса серверов точного времени на отечественные: ```editorconfig # 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`. Чтобы настройки подействовали сервис точного времени нужно перезапустить: ```shell sudo service ntp restart ``` Проверим, что сервис корректно перезапустился: ```shell 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 ``` Проверить какой сервер точного времени ближе, задержки между серверами и т.п.: ```shell ntpq -p ``` Увилим что-то типа: ```shell 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 для сохранения конфигураций настроенных соединений и их автоматического подключения после перегрузке компьютера. ```shell sudo -S apt-get install iptables ``` Далее создадим в домашней папке bash-скрипт `prepare-iptable.sh`: ```shell nano ~/prepare-iptable.sh ``` ...и вставим в него нижеследующий текст (исправьте если необходимо, следуя рекомендациям): ```shell #!/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-консоль и выполнить: ```shell sudo bash ~/prepare-iptable.sh ``` После выполнения скрипта все внешние соединения с виртуалкой будут разорваны. После перелогирования через ssh можно будет увидеть, что новые настройки сетевой фильтрации вступили в силу: ```shell sudo iptables -L -v -n ``` Увидим примерно следующее: ```text 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`: ```shell sudo -S apt-get install iptables-persistent ``` Будет запрошено сохранить ли и восстанавливать настройки в случае перезагрузки для IP-V4 и IP-V6 -- оба раза отвечаем `Y`. В будущем, если будут меняться правила фильтрации, то сохранить `sudo netfilter-persistent save`, восстановить `sudo netfilter-persistent start`. Для детальных разъяснений [см. по ссылке](https://losst.ru/kak-sohranit-pravila-iptables). ## 5. Установиv защиту DoS (брутфорса) по ssh DoS и DDoS-атака -- это агрессивное внешнее воздействие на сервер, проводимое с целью перегрузить его запросами, доведения его до отказа или брутфорс-взлома (brute force -- грубая сила, например, подбор паролей перебором и т.п.). Если атака проводится с одиночного компьютера -- ее называют DoS (Denial of Service), если с нескольких, распределенных в сети, компьютеров — DDoS (Distributed Denial of Service). Защиту от DoS (включая медленные атаки) может обеспечить пакет fail2ban. Установим его (уже должен быть установлен): ````shell sudo apt-get install fail2ban ```` Для его настройки перепишем конфигурационный файл `/etc/fail2ban/jail.local`: ```shell sudo nano /etc/fail2ban/jail.local ``` И поместим туда следующее: ```editorconfig # == новый конфиг /etc/fail2ban/jail.local ===================== [DEFAULT] #email, на который присылать уведомления destemail = root # подключить прафила бана из '/etc/fail2ban/action.d/iptables-multiport.conf' banaction = iptables-multiport # исключаем из потенциального бана ip машины с которых можем подключаться ignoreip = #### правила для 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: ```shell sudo service fail2ban restart ``` Проверим статус: ```shell 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 при заугрузке системы: ```shell sudo systemctl enable fail2ban.service ``` ## 5. Установка и настройка СУБД (MariaDB), создание пользователей Следуем [инструкциям](https://www.digitalocean.com/community/tutorials/how-to-install-mariadb-on-ubuntu-20-04), и у нас получится примерно такой порядок действий: ### Установка Устанавливаем сервер MariaDB (полный аналог MySQL, но без патронажа Oracle) и пакет MariaDB-клиента для разработчиков (чтобы после корректно установился MySQL-коннектор для Python): ```shell sudo apt install mariadb-server libmysqlclient-dev ``` Отредактируем конфигурационный файл, чтобы сменить кодовые таблицы в будущих базах с `utf8mb4` (со всякими глупыми эможи) на православную `utf8`: ```shell sudo nano /etc/mysql/mariadb.conf.d/50-server.cnf ``` Найдём в конфигурационном файле строки: ```editorconfig character-set-server = utf8mb4 collation-server = utf8mb4_general_ci ``` и заменим на: ```nginx configuration character-set-server = utf8 collation-server = utf8_general_ci ``` Сохраняем конфигурационный файл `Ctrl+O` и `Enter`, а выходим из редактора `Ctrl+X`. Перезапустим систему управления базы данных: ```shell sudo service mysql restart ``` Проверим статус: ```shell 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`: ```shell sudo mysql ``` Создаём пользователя ``, зададим ему пароль и дадим привилегии на все: ```mysql CREATE USER ''@'localhost' IDENTIFIED BY '*********************'; GRANT ALL PRIVILEGES ON *.* TO ''@'localhost'; ``` Создаем базу данных `django_cadpoint` для нашего сайта: ```mysql CREATE DATABASE django_cadpoint DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci; ``` И создадим пользователя `cadpint`, пароль для него и дадим ему права работать только с базой сайта: ```mysql CREATE USER 'cadpint'@'localhost' IDENTIFIED BY '*******'; GRANT ALL PRIVILEGES ON django_cadpoint.* TO 'cadpint'@'localhost'; ``` Проверим, что установлена правильная часовая зона времени: ```mysql SHOW VARIABLES LIKE '%time_zone%'; ``` Типа, она должна быть `MSK`: ``` +------------------+--------+ | Variable_name | Value | +------------------+--------+ | system_time_zone | MSK | | time_zone | SYSTEM | +------------------+--------+ 2 rows in set (0.001 sec) ``` Выходим из MariaDB: ```mysql quit; ``` переносим базу ## 6. ПОДГОТОВКА WEB-СЕРВЕРА NGINX Устанавливаем nginx ```shell sudo apt-get install nginx ``` На всякий случай (как выяснилось позже), лучше поставить nginx plus. В нем есть динамические модули. И один их таких динамических модулей -- **geoip** -- может понадобиться для ограничения посещения сайта только пользователями России. [Смотрим инструкцию по установке nginx plus](https://docs.nginx.com/nginx/admin-guide/installing-nginx/installing-nginx-plus/). Убедиться, что он запущен и работает корректно можно командой: ```shell 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: ```shell 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`: ```shell sudo nano /etc/nginx/nginx.conf ``` Находим в нем кусочек касающийся GZIP: ```nginx configuration ## # Gzip Settings ## ``` и пишем в нем следующее (там будет закомментированный блок, можно использовать его): ```nginx configuration 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: ```shell sudo service nginx restart ``` #### Проверка новой конфигурации Выполним тестовый запрос: ```shell 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: ```shell 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 а, в будущем, и все пакеты, батарейки, свистелки и хрюкалки нашего проекта). ```shell cd $HOME mkdir -p cadpoint ``` Чтобы для нашего проекта "заморозить" версию Python и все необходимые пакеты, и изьежать возможного кофликта при обновлении системного Python или с пакетвми установленными в других проектвх создадим виртуальное окружение в папке нашего сайта (`$HOME/cadpoint`): ```shell virtualenv -p python3 $HOME/cadpoint/env ``` Активируем созданное виртуальное окружение: ```shell source $HOME/cadpoint/env/bin/activate ``` Проверить, что теперь мы работаем в виртуальном окружении можно дав команды: ```shell python -V pip -V ``` Увидим > pip 20.0.2 from /home//cadpoint/env/lib/python3.8/site-packages/pip (python 3.8) Можно обновить менеджер пакетов `pip` по последней версии (сперва получим его с помощью `curl` в папку tmp, а после запустим установщик через Python) ```shell curl https://bootstrap.pypa.io/get-pip.py > ~/tmp/get-pip.py python3 ~/tmp/get-pip.py ``` Попробуем снова ```shell pip -V ``` Увидим > pip 22.0.4 from /home//cadpoint/env/lib/python3.8/site-packages/pip (python 3.8) ФУУ!! ## 8. Установка пакетов необходимых проекту Точный состав пакетов, обычно, находится в файле [requarement_dev_prod_dreamhost.txt](rsvo_new/requarement_dev_prod_nicru_vm.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` в последовательности: ```shell 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 ``` ### Разворачиваем и тестируем проект Создаём папки для хранения сокета и логов: ```shell mkdir -p $HOME/cadpoint/logs mkdir -p $HOME/cadpoint/socket ``` Переносим проект... например на тест-деплоя z7 (или на на бочем проекте мастерхост) архивируем проект. __Само-cобой это надо делать там, где проект находится, не на вирталке, а dev-серере (или или там находится наш проект)__. Например, так: ```shell zip -9 -r cadpoint.zip public/ rsvo_new/ config/ ``` Копируем архив по ssh на нашу виртуалку (masterhost или nic.ru) ```shell scp -P 2002 cadpoint.zip @:~/ ``` Возвращаемся на нашу виртуалку хостинга и разархивируем: ```shell cd ~/cadpoint unzip cadpoint.zip ``` __Внимание__, на разных серверах расположение папок проекта может отличаться. При архивации илм после распаковки, возможно, нам потребуется переместить некоторые папки. Мы должны получить следующую структуру проекта: ```text +---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//cadpoint/rsvo_new/rsvo_new/urls.py`) из-за изменения расположения методов в библиотеках django следует закоменить строчку: ```python from django.conf.urls import url, include ``` И добавить после нее: ```python from django.conf.urls import include from django.urls import re_path as url ``` должно получиться следующее: ```python # 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//cadpoint/env/lib/python3.8/site-packages/ckeditor_filebrowser_filer/urls.py`. Вместо: ```python from django.conf.urls import url ``` Следует написать: ```python from django.urls import re_path as url ``` должно получиться: ```python # from django.conf.urls import url from django.urls import re_path as url ``` Теперь можно произвести перенос статических файлов админки и батареек в папку для web-статики: ```shell source ~/cadpoint/env/bin/activate cd ~/cadpoint/rsvo_new python manage.py collectstatic ``` Теперь произведем подготовку нашей базы под проект. Для этого произведем миграции: ```shell python manage.py makemigrations python manage.py migrate ``` Миграция создаст все необходимые таблицы и заполнит таблицу миграций `django_cadpoint.django_migration`. Теперь можно произвести перенос базы с dev-проекта (или с другого рабочего сервера) в базу нашей виртуалки. Следует учесть, что переносить таблицу `django_cadpoint.django_migration` не следует (мы произвели чистую миграцию, и если мы перезапишем эту таблицу, то с высокой вероятностью сломаем наш проект). Настало время проверить, что наше web-приложение Django работает. Временно откроем порт _8080_ через `iptables`: ```shell sudo iptables -A INPUT -i eth0 -p tcp --dport 8080 -j ACCEPT ``` Запустим приложение с dev-режиме на нашем IP и этом порту: ```shell python manage.py runserver :8080 ``` Если мы обратимся из браузера по адресу __http://:8080__, то увидим как на нашей виртуалке "бежит лог". В браузере будет отображаться что-то похожее на наш проект (с тем отличием, что не будет работать статика... т.е. мы не увидим картинок, не будут подгружены стили, JavaScript и все такое). Завершаем работу web-сервера разработчика, нажав `Control+C`. ## 8. Конфигурируем nginx под наш проект ----------- Создаем и правим конфиг `/cadpoint/config/cadpoint.conf`: ```shell nano /cadpoint/config/cadpoint.conf ``` Помещаем в него следующее: ```nginx configuration # Разработка сайта CADPOINT.RU # == Конфикурационный файл nginx cadpoint.conf # Описываем апстрим-потоки которые должен подключить Nginx # Для каждого сайта надо настроить свйо поток, со своим уникальным именем. # Если будете настраивать несколько python (django) сайтов - измените название upstream upstream cadpoint-django { # расположение файла Unix-сокет для взаимодействие с uwsgi server unix:///home//cadpoint/socket/cadpoint.sock; # /home//cadpoint/socket/cadpoint.sock; # также можно использовать веб-сокет (порт) для взаимодействие с uwsgi. Но это медленнее # server 127.0.0.1:8001; # для взаимодействия с uwsgi через веб-порт keepalive_requests 200; } # конфигурируем сервер server { # server_name ; # доменное имя сайта server_name cadpoint.ru; # доменное имя сайта listen 80; charset utf-8; # кодировка по умолчанию access_log /home//cadpoint/logs/cadpoint-access.log; # логи с доступом error_log /home//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//cadpoint/public/media; } # Расположение media-файлов Django location /static { alias /home//cadpoint/public/static; } # Расположение static-файлов Django location /robots.txt { root /home//cadpoint/public; } # Расположение robots.txt location /favicon.ico { root /home//cadpoint/public; } # Расположение favicon.ico location /favicon.gif { root /home//cadpoint/public; } # Расположение favicon location /favicon.png { root /home//cadpoint/public; } # Расположение favicon location /favicon.svg { root /home//cadpoint/public; } # Расположение favicon location /author.txt { root /home//cadpoint/public; } # Расположение author.txt location = /404.html { root /home//cadpoint/cadpoint/templates/404.html; internal; } location = /500.html { root /home//cadpoint/cadpoint/templates/500.html; internal; } # location ~ \.(xml|html|htm)$ location ~ \.(html|htm|ico|svg|png|gif|jpg|jpeg)$ { root /home//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: ```shell sudo ln -s ~/cadpoint/config/cadpoint.conf /etc/nginx/sites-enabled/ ``` Протестируем конфигурацию: ```shell sudo nginx -t ``` Если все ок, можем "мягко" перезапустить nginx: ```shell sudo nginx -s reload ``` Или "по-жёсткому": ```shell 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 уже должен быть установлен. Но если он не установлены, или для проверки, инсталляция производится из так: ```shell sudo apt-get install uwsgi uwsgi-plugins-all ``` Проверим, что uWSGI работает: ```shell 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: ```shell nano ~/cadpoint/config/cadpoint.conf ``` Помещаем туда следующее: ```editorconfig # === Конфикурационный файл uwsgi cadpoint.ini [uwsgi] # НАСТРОЙКИ ДЛЯ DJANGO # Корневая папка проекта (полный путь) chdir = /home//cadpoint/cadpoint # Django wsgi файл cadpoint/wsgi.py записываем так: module = cadpoint.wsgi # полный путь к виртуальному окружению home = /home//cadpoint/env # полный путь к файлу сокета socket = /home//cadpoint/socket/cadpoint.sock # Исходящие сообщения в лог daemonize = /home//cadpoint/logs/cadpoint_uwsgi.log # ЗАГАДОЧНЫЕ НАСТРОЙКИ, ПО ИДЕЕ ОНИ НУЖНЫ, НО И БЕЗ НИХ ВСЁ РАБОТАЕТ # расположение wsgi.py wsgi-file = /home//cadpoint/cadpoint/cadpoint/wsgi.py # расположение виртуального окружения (как оно работает если этот параметр не указан, не ясно) virtualenv = /home//cadpoint/env # имя файла при изменении которого происходит авторестарт приложения # (когда этого параметра нет, то гичего не авторестартится, но с ним все рестартится. # Cтоит изменить любой Python-исходник проекта, как изменения сразу вступают в силу. touch-reload = /home//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: ```shell sudo ln -s ~/cadpoint/config/cadpoint.ini /etc/uwsgi/apps-enabled/ ``` Не забываем перезапустить uwsgi: ```shell sudo service uwsgi restart ``` Еще раз проверим, что uWSGI работает: ```shell sudo service uwsgi status ``` Все. Сайт развёрнут. Далее можно проводить улучшайзинг и усиливать защиту: ## 10. Нагрузочное тестирование (нужен dockers) Нагрузочное тестирование будем проводить при помощи Яндекс.Танк. Он будет "обстреливать" наш сайт запросами. Для запускать Яндекс.Танк проще всего использовать Dockers. Т.о. Dockers должен быть установлен на машину, с которой будем "обстреливать" сайт (и это не виртуалка хостинга... "обстреливать" самих себя бессмысленно). Создаем каталог `yandex.tank` (в моем случае `M:/VM/yandex.tank`) и в нем помещаем файл `load.yaml` следующего содержания (не забываем корректировать URL, если необходимо): ```yaml 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 (хоть он и пустой, но это важно). Теперь можем запустить Яндекс.Танк: ```shell 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 Let’s Encrypt — это удостоверяющий центр сертификации (ЦС), который предоставляет доступный способ получения и установки бесплатных сертификатов TLS/SSL , тем самым обеспечивая шифрование HTTPS на веб-серверах. Let’s Encrypt предоставляет программный клиент Certbot, который пытается автоматизировать большинство (если не все) необходимые шаги установки сертификатов, настройки web-севера nginx (и Apache тоже) и периодическое обновления сертификатов. ### Установка Certbot Если на сервере ранее была установлена более старая версия certbot, следует удалить ее: ```shell sudo apt remove certbot ``` Certbot рекомендует использовать для установки пакет моментальных снимков (снапшотов). Пакеты Snap работают почти со всеми дистрибутивами Linux, но для управления пакетами Snap требуется, чтобы вы сначала установили snapd. Ubuntu 22. 04 поставляется с поддержкой снапшотов «из коробки», поэтому нужно начать с того, что убедитесь, что ядро snapd обновлено: ```shell sudo snap install core; sudo snap refresh core ``` После этого можно установить certbot: ```shell sudo snap install --classic certbot ``` Наконец, свяжем certbot с командой создания снапшотов. Это не обязательно, но по умолчанию снимки обычно менее навязчивы, поэтому создают случайных конфликтов с какими-либо другими системными пакетами: ```shell 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 Получаем сертификаты ```shell 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). Нужно внести изменения в него: ```shell sudo nano /etc/letsencrypt/options-ssl-nginx.conf ``` В частности нам нужно изменить дерективы `ssl_protocols`, `ssl_ciphers`, `resolver`, `keepalive_requests` и `ssl_session_cache`. В результате наш файл должен выглядеть так: ```nginx configuration # 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 для нашего сайта: ```shell nano ~/cadpoint/config/cadpoint.conf ``` И добавим изменим директиву: ```nginx configuration listen 443 ssl; # managed by Certbot ``` на директиву: ```nginx configuration listen 443 ssl http2; # managed by Certbot ``` Перезагружаем nginx чтобы применились изменения: ```shell sudo service nginx restart ``` ## 12. Блокируем и баним зловредов (бот-сканеры и http-досеров) ### Ограничения числа запросов в nginx (не обязательно, но вдруг) Настроим nginx на ограничения числа запросов c одного IP-адреса с помощью модуля Limit Req Module. Открываем файл `/etc/nginx/nginx.conf` (конфиг для всех сайтов на сервере): ```shell sudo nano /etc/nginx/nginx.conf ``` И в блок `http {…}` добавляем строку: ```nginx configuration 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 для нашего сайта: ```shell nano ~/cadpoint/config/cadpoint.conf ``` И в блок `server {…}`, добавим строку: ```nginx configuration 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 … {…}` будет выглядеть так: ```nginx configuration 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`: ```shell sudo nano /etc/fail2ban/filter.d/nginx-noscript.conf ``` Со следующим содержанием: ```editorconfig [Definition] failregex = .*client: .*GET.*(\.php|\.asp|\.aspx|\.exe|\.pl|\.cgi|\.scgi|\.log|\.sql|\.jsp|\.csv|\.sh|\.key|\.py|\.pyc|\.asmx|\.asax) # failregex = ^ -.*GET.*(\.php|\.asp|\.aspx|\.exe|\.pl|\.cgi|\.scgi|\.log|\.sql|\.jsp|\.csv|\.sh|\.key|\.py|\.pyc|\.asmx|\.asax) ignoreregex = ``` Проверим, что фильтр написан верно: ```shell fail2ban-regex ~/cadpoint/logs/cadpoint-error.log /etc/fail2ban/filter.d/nginx-noscript.conf ``` Создаем еще одни фильтр для выявления подозрительной активности (ищут адинки популярных cms, log-и, ключи и тому подобное) анализируя access лог `/etc/fail2ban/filter.d/nginx-manual.conf`: ```shell sudo nano /etc/fail2ban/filter.d/nginx-manual.conf ``` со следующим содержанием: ```editorconfig [Definition] failregex = ^ -.*GET.*wp-content/ ^ -.*GET.*wp-admin/.* ^ -.*GET.*wp-includes/.* ^ -.*GET.*administrator/ ^ -.*GET.*user/register/ ^ -.*GET.*bitrix/admin/ ^ -.*GET.*minify/ ^ -.*GET.*(?:a|A)dmin/ ^ -.*GET.*netcat/ ^ -.*GET.*koobooCMS/ ^ -.*GET.*apanel/ ^ -.*GET.*netcat/ ^ -.*GET*/\.git/config ^ -.*GET*/\.well-known/ ^ -.*GET.*(\.sql|\.php5|\.mdb|\.db|\.yml|\.cgi|\.scgi|\.log|\.sql|\.jsp|\.csv|\.sh|\.key|\.py|\.pyc|\.asmx|\.asax) ignoreregex = ``` Снова проверим, что фильтр написан верно: ```shell fail2ban-regex ~/cadpoint/logs/cadpoint-access.log /etc/fail2ban/filter.d/nginx-manual.conf ``` Далее, редактируем файл `/etc/fail2ban/jail.local` для настройки параметров подключения и работы фильтра: ```shell sudo nano /etc/fail2ban/jail.local ``` Дописываем строки: ```editorconfig # ФИЛЬТР 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//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//cadpoint/logs/*access*.log bantime = 86400 maxretry = 6 findtime = 7200 ``` Перезапустим fail2ban: ```shell sudo service fail2ban restart ``` Чтобы посмотреть информацию по заблокированным ip по нашему фильтру используем команду: ```shell sudo fail2ban-client status nginx-noscript sudo fail2ban-client status nginx-manual ``` Чтобы разбанить IP (например 8.8.8.8) можно воспользоваться командой: ```shell sudo fail2ban-client set nginx-noscript unbanip 8.8.8.8 ``` ## 13. Ротация логов Обычно процесс ротации логов в системе уже установлен и работает. Но на всякий случай установить его можно так: ```shell sudo apt-get install logrotate ``` Для настройки ротации логов нашего сайта нужно отредактировать конфигурационный файл ротации для nginx `/etc/logrotate.d/nginx` ```shell sudo nano /etc/logrotate.d/nginx ``` И добавить в него (в конце) следующее: ```editorconfig /home//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 } ``` После чего процесс ротации надо перезапустить: ```shell sudo service logrotate restart ``` Убедимся, что ротация работает: ```shell 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