diff --git a/deploy_to_dreamhost.md b/deploy_to_dreamhost.md index a00a7e9..c7223d2 100644 --- a/deploy_to_dreamhost.md +++ b/deploy_to_dreamhost.md @@ -11,6 +11,7 @@ 7. Сконфигурируем будущую компиляцию на размещение готовой версии Python в папку `~/opt/python-3.8.6` 8. Компилируем Python (в том числе будут запущены тесты) 9. Устанавливаем Python 3.8.6 + ``` cd ~ diff --git a/deploy_to_masterhost-vm.md b/deploy_to_masterhost-vm.md new file mode 100644 index 0000000..4da8af3 --- /dev/null +++ b/deploy_to_masterhost-vm.md @@ -0,0 +1,1603 @@ +# Инструкция по развёртыванию на VDS-хостинге (_виртуальная машина_) на примере masterhost.ru + +## 1: Создание пользователя + +Изначально есть только root-доступ. Если мы залогированы под **root**, то следует создать пользователя от имени +которого мы будем осуществлять все действия (позже root-доступ будет закрыт). Создадим пользователя **web**: + +```shell +sudo useradd -c 'WEB-user' -m +``` + +Присвоим ему пароль: +```shell +sudo passwd +``` + +Дадим ему права работать от имени root. Для этого откроим на редактирование конфигурационный файл `/etc/sudoers`: +```shell +nano /etc/sudoers +``` + +и после строки `root ALL=(ALL:ALL) ALL` добавим в него строку: +```editorconfig +web ALL=(ALL:ALL) ALL +``` + +Сохраняем конфигурационный файл `Ctrl+O` и `Enter`, а выходим из редактора `Ctrl+X`. + +**ВАЖНО**: Если по какой-то причине файл `/etc/sudoers` пустой -- это означает, что служба sudoers не установлена. +Её нужно установить: `apt-get install sudo`. + +Разлогируемся оз под root. +```shell +logout +``` + +Теперь можно залогироваться от имени пользователя **web**. + +Установим командную оболочку 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** и разрешаем доступ нашему пользователю **web**: +``` +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 +``` + +Создаём пользователя `web`, зададим ему пароль и дадим привилегии на все: +```mysql +CREATE USER 'web'@'localhost' IDENTIFIED BY '*********************'; +GRANT ALL PRIVILEGES ON *.* TO 'web'@'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 @:~/cadpoint +``` + +Возвращаемся на нашу виртуалку хостера и разархивируем: +```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 +