Ограничить количество одновременных подключений в iptables

Дата: 26.04.2017Метки:

Допустим у вас есть обычный VPS, где крутится популярный среди группы людей сайт и вы хотите защитить его от примитивных атак недоброжелателей. Основная задача которых состоит в том, что бы вызвать исчерпание вычислительных ресурсов VPS с помощью искусственно спровоцированной нагрузки на сервер. В следствие которой, память уйдет в swap и подключится к серверу станет проблематично.

В таком случае для ограничения потребления ресурсов сервера наиболее эффективно использовать cgroups — механизм ядра, который ограничивает вычислительные ресурсы для групп процессов. Используя cgroups мы можем предотвратить полную утилизацию вычислительных ресурсов процессора и памяти сервера, которые могут вызваны нагрузкой на веб-сервер и PHP. Таким образом мы всегда можем сохранить контроль над сервером и обеспечить доступ к серверу по SSH.

О использовании cgroups я постараюсь рассказать позже. Но даже если правильно настроить cgroups, в случае всплеска нагрузки, работа сайта будет нарушена. Как вариант решения проблемы предлагаю с помощью iptables ограничить количество одновременных подключений к серверу. Таким образом можно попытаться отсечь основной источник нагрузки, сохранив доступность ресурса для других посетителей.

Ситуацию я привел в качестве примера, но думаю, что ее можно адаптировать для других задач. Теперь переходим непосредственно к примеру, для начала добавьте следующие правила в iptables:

iptables -t filter -I INPUT -p tcp --dport 80 -j ACCEPT
iptables -t filter -I INPUT -p tcp --dport 80 -m state --state RELATED,ESTABLISHED -j ACCEPT

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

iptables -t filter -I INPUT -p tcp --syn --dport 80 -m connlimit --connlimit-above 30 --connlimit-mask 32 -j DROP

Это правило устанавливает ограничение на 30 одновременных подключений с одного ip-адреса.

Если вам необходимо ограничить количество одновременных подключений со всех ip, тогда необходимо использовать следующую команду:

iptables -t filter -I INPUT -p tcp --syn --dport 80 -m connlimit --connlimit-above 500 -j DROP

Установка Deluge в Ubuntu Gnome

Из множества BitTorrent-клиентов мне больше всего приглянулся Deluge. Программа состоит из двух независимых компонентов: демона и клиента. Их можно установит сразу на один компьютер, в таком случае Deluge будет работать как большинство обычных BitTorrent-клиентов. Или если у вас NAS, можно установить демон отдельно на сервер, а клиент Deluge на домашний компьютер. После чего удаленно управлять загрузками.

В плане оформления Deluge имеет простой и лаконичный GUI, который использует библиотеку GTK+, а значит не будет выделятся на фоне общего оформления Gnome. За обмен данными по протоколу BitTorrent отвечает библиотека libtorrent, которая поддерживает большинство функций протокола.

По сути нет ничего сложного в установке Deluge в Ubuntu Gnome, но есть некоторые нюансы, которые можно забыть со временем.

Для начала устанавливаем демон и клиент Deluge:

sudo apt-get install deluged
sudo apt-get install deluge

Далее настроим автозапуск Deluge, для этого идем в Gnome Tweak Tool — Startup Applications — жмем ‘+’ и выбираем из списка Deluge. Теперь каждый раз при входе в систему Deluge будет запускаться автоматически.

Было бы удобно, если бы при автозапуске и закрытии программа сворачивалась в трей: открываем Deluge — Edit — Preferences — Interface — и ставим галочки напротив Enable system icon tray, Minimize to tray on close, Start in tray.

И напоследок хочу отметить, что лично мне не нравится расположение и вид трея в Gnome. Я предпочитаю перенести его в верхнюю часть экрана. Установим дополнение TopIcons из каталога дополнений Gnome. Переходим в Gnome Tweak Tool — Extension и включаем дополнение TopIcons. На этом установка и настройка Deluge в Ubuntu Gnome закончена.

Переключение раскладки GNOME

В отличии от Fedora в процессе установки Ubuntu GNOME нельзя выбрать сочетания клавиш для переключения раскладки клавиатуры. Установил я систему, но Alt-Shift ни Ctrl-Shift тут не работают. По умолчанию для переключения раскладки используются Super + Space. Стандартным способом изменить раскладку можно через Settings — Keyboard — Typing — Switch to next input source.

Вот только назначить Ctrl-Shift тут нельзя, возможность выбора сочетаний ограничена до невозможности. Что подтолкнуло разработчиков к такому решению остается только догадываться. Я уж думал плюнуть на это дело и забыть. Но как оказалось, я не тот человек, который быстро меняет свои привычки. Как не пытался, а рука сама нажимает привычные мне клавиши.

Случайно в процессе работы наткнулся на необходимые мне настройки. Изменить переключения раскладки клавиатуры на привычные сочетания можно через Gnome Tweak Tool переходим в Typing — Switch to another layout. Выбираем привычные Alt-Shift или Ctrl-Shift и продолжаем радоваться жизни.

Установка Chrome и Chromium в Ubuntu

Многие уже слышали про прекращение дальнейшей разработки Unity в Ubuntu. Это решение было настолько неожиданно, что многие приняли эту новость за очередную утку. Теперь по умолчанию место Unity в Ubuntu будет занимать Gnome 3. Лично для меня это отличный повод перейти на Ubuntu, а вместе с тем я начинаю цикл записей о том как довести ее до рабочего состояния.

И так сегодня я установил на свой домашний компьютер последнюю версию Ubuntu GNOME 17.04 (Zesty Zapus). Первые впечатления сложились положительные. Система ведет себя достаточно стабильно. И первое, что я сделаю на новой системе выполню установку Chrome. Дополнительно в этой записи я расскажу об установке Chromium. Какой из браузеров устанавливать зависит от ваших личных предпочтений.

Установить Google Chromium в Ubuntu

Начну я пожалуй с установки Chromium в Ubuntu. Если я не ошибаюсь, Chromium полностью соответствует версии браузера после установки пакета google-chrome-unstable. Для установки Chromium в Ubuntu достаточно просто выполнить команду:

sudo apt-get install chromium-browser

В Gnome после установки Chromium по аналогии с Firefox отображается заголовок окна. Не смотря на то, что он добавлен для соответствия концепции интерфейса Gnome, я предпочитаю его отключить. Для этого в настройках Chromium нужно убрать галочку возле опции Use system title bar and borders.

Установить Flash для Chromium в Ubuntu

Если на этапе установки Ubuntu вы выбрали установку программного обеспечения от сторонних производителей, то дополнительно устанавливать Flash не потребуется. В противном случае необходимо включить дополнительный репозиторий. Я использую английскую локализацию поэтому описание приложений и элементов меню будет на английском.

Что бы установить Flash для Chromium в Ubuntu включаем репозиторий Canonical partners. Для этого переходим в Software and Updates — Other Software — установить галочку напротив Canonical partners.

Далее в терминале выполнить:

sudo apt update
sudo apt install adobe-flashplugin

Проверить поддержку HTML5, H.264 и Flash можно на странице youtube.com.

Установить Chrome в Ubuntu

Чтобы установить браузер Chrome из репозитория, откройте терминал и выполните следующие команды:

wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add -

Если у вас установлена Ubuntu 64-Bit:

sudo sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'

Или для Ubuntu 32-Bit:

sudo sh -c 'echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'

Обновляем локальный кэш пакетов и запускаем установку:

sudo apt-get update
sudo apt-get install google-chrome-stable

Для установки бета или нестабильно версии браузера выполним в терминале одну из команд:

sudo apt-get install google-chrome-beta
sudo apt-get install google-chrome-unstable

Зомби процессы в Linux

Дата: 10.04.2017Метки:

Если в вашей системе завелись зомби процессы, не расстраивайтесь. Не смотря на страшное название зомби процесс не может нанести серьезный вред вашей системе. Для начала давайте разберемся как обычный процесс превращается в зомби.

Когда дочерний процесс получает команду на завершение, он освобождает все используемые ресурсы и продолжает как зомби существовать в системе с ранее присвоенным ему PID. Далее с помощью сигнала SIGCHLD система уведомляет родительский процесс о завершении зомби процесса. Если по какой-либо причине родительский процесс игнорирует этот сигнал, то зомби процесс так и продолжает отображаться в системе.

Как вы уже поняли, зомби процессы не используют системные ресурсы, поэтому их можно просто игнорировать. Убить зомби процесс на прямую командой kill у вас не получиться. В данном случае единственный вариант полностью убрать зомби процесс — убить или перезапустить его родительский процесс.

Что бы убить зомби процесс, для начала нужно получить его PID:

# ps aux | grep -w Z
root      2037  0.0  0.0      0     0 ?        Z    09:44   0:00 [httpd] 
root      8470  0.0  0.0 112652   984 pts/10   S+   10:43   0:00 grep --color=auto -w Z

В моем примере PID зомби процесса 2037. Теперь получим PID родительского процесса:

# ps o ppid 2037
 PPID
 2033

Когда мы знаем PID, правильнее всего определить что это за процесс командой top, а затем перезапустить его.

В крайнем случае можно убить родительский процесс:

# kill -9 2033

Добавить сервис в systemd

В этой заметке я расскажу о том, как настроить сервис Linux для автоматического запуска демона после сбоя или перезагрузки. В качестве примера я буду использовать Nginx и PHP но вы можете этот способ для любых приложений.

Уже прошло достаточно много времени с того момента, как большинство популярных дистрибутивов Linux перешли на системный менеджер systemd. Тем не менее, до сих пор старая система инициализации SysV продолжает параллельно функционировать на ряду с systemd. И как и раньше, большинство пакетов после установки добавляют стартовый скрипт в каталог init.d.

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

Для примера мы установили OpenVPN, для которого по умолчанию используется механизм запуска System V. Во время установки пакета в каталог /etc/init.d будет скопирован скрипт инициализации, который будет отвечать за автоматический запуск приложения после перезагрузки. Если в один прекрасный день OpenVPN упадет, без дополнительных настроек, нужно будет каждый раз запускать этот процесс вручную.

Теперь возьмем ситуацию, когда для запуск сервиса выполнен через systemd. Если произойдет крэш или даже если вы захотите специально убить процесс, то systemd автоматически это обнаружит и повторно запустит процесс. При этом systemd обратно совместим с командами System V и сценариями инициализации.

А теперь давайте рассмотрим, как быстро добавить сервис в systemd. Демон systemd запускает сервисы описанные в его конфигурации. Конфигурация состоит из файлов, которые называют юнитами. Для юнитов созданных вручную используется каталог:

/etc/systemd/system/

В простом варианте юнит состоит из трех секции: [Unit], [Service], [Install].

Секция [Unit]

Описание юнита:

Description=MyUnit

Далее указываем порядок загрузки, после какого сервиса systemd запустить наш юнит:

After=syslog.target
After=network.target

Секция [Service]

Задаем тип сервиса:

Type=simple

Используется по умолчанию. Служба будет запущена незамедлительно. Процесс при этом не должен разветвляться. Не используйте этот тип, если другие службы зависят от очередности при запуске данной службы.

Type=forking

Указывают если служба запускается однократно и процесс разветвляется с завершением родительского процесса.

Расположение pid-файла:

PIDFile=/run/service.pid

Рабочий каталог приложения:

WorkingDirectory=/usr/local/service

Пользователь и группа, под которым будет запускаться сервис:

User=unit
Group=unit

Задаем приоритет убийства процесса при нехватке памяти:

OOMScoreAdjust=-100

Минимальное значение -1000 — полный запрет.

Команды запуска, остановки и перезапуска сервиса:

ExecStart=/usr/sbin/service
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID

Таймаут запуска или остановки сервиса:

TimeoutSec=300

Контроль запуска сервиса и автоматический запуск в случае крєша:

Restart=always

Секция [Install]

Уровень запуска сервиса:

WantedBy=multi-user.target

В итоге мы получили следующий юнит:

[Unit]
Description=MyUnit
After=syslog.target
After=network.target

[Service]
Type=forking
PIDFile=/run/service.pid
WorkingDirectory=/usr/local/service
User=munit
Group=unit
OOMScoreAdjust=-100

ExecStart=/usr/sbin/service
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
TimeoutSec=300

[Install]
WantedBy=multi-user.target

Добавляем юнит в каталог:

nano /etc/systemd/system/unit_name

Включаем, запускаем и смотрим статус юнита:

systemctl enable unit_name
systemctl start unit_name
systemctl -l status unit_name

А теперь привожу примеры работающих юнитов.

Юнит systemd для запуска Nginx:

[Unit]
Description=The NGINX HTTP and reverse proxy server
After=syslog.target network.target remote-fs.target nss-lookup.target

[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t
ExecStart=/usr/sbin/nginx
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target

Юнит systemd для запуска php-fpm:

[Unit]
Description=The PHP FastCGI Process Manager
After=syslog.target network.target

[Service]
Type=simple
PIDFile=/usr/local/php-fpm/var/run/php-fpm.pid
ExecStart=/usr/local/php-fpm/sbin/php-fpm --nodaemonize --fpm-config /usr/local$
ExecReload=/bin/kill -USR2 $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target

Access denied for user ‘debian-sys-maint’@’localhost’

С проблемой можно столкнуться в результате некорректной установки MySQL. Полностью ошибка выглядит следующим образом:

mysql_upgrade: Got error: 1045: Access denied for user 'debian-sys-maint'@'localhost' (using password: YES) while connecting to the MySQL server

Суть проблемы состоит в том, что mysql_upgrade не может получить доступ к нашей базе под указанным в файле debian.cnf паролем. Если выполнить команду:

cat /etc/mysql/debian.cnf

Мы получить результат следующего вида:

# Automatically generated for Debian scripts. DO NOT TOUCH!
[client]
host     = localhost
user     = debian-sys-maint
password = n4aSHUP04s1J32X5
socket   = /var/run/mysqld/mysqld.sock
[mysql_upgrade]
user     = debian-sys-maint
password = n4aSHUP04s1J32X5
socket   = /var/run/mysqld/mysqld.sock
basedir  = /usr

Нас интересует параметр password. Копируем это пароль, после чего в MySQL необходимо выполнить запрос:

mysql> GRANT ALL PRIVILEGES ON *.* TO 'debian-sys-maint'@'localhost' IDENTIFIED BY 'n4aSHUP04s1J32X5';

С первого раза не сработало, MySQL ругался на то, что пароль не соответствует правилам безопасности. Тогда просто можно добавить к паролю любой символ и повторить команду. При этом не забудьте указать новый пароль в фале debian.cnf.

Для того чтобы имения вступили в силу, необходимо перезапустить MySQL.

Сброс пароля MySQL

В интернете достаточно много материалов про сброс пароля root в MySQL 5.7. Процесс достаточно простой и уже много раз описывался в сети, но связи с тем, что метод опробован на практике, решил записать последовательность своих действий. Все действия выполнялись в Ubuntu 16.04 и MySQL 5.7.

Проверим версию MySQL:

mysql --version
mysql  Ver 14.14 Distrib 5.7.16, for Linux (x86_64) using  EditLine wrapper

От версии пакета MySQL будет зависть SQL-запрос, который необходимо выполнить для изменения пароля. Далее останавливаем MySQL:

service mysql stop

Создаем каталог, устанавливаем на него права:

mkdir -p /var/run/mysqld
chown -R mysql /var/run/mysqld

Запускаем MySQL без загрузки grant tables, в целях безопасности отключаем сеть:

mysqld_safe --skip-grant-tables --skip-networking &

Теперь мы можем подключится к MySQL без использования пароля:

mysql -u root

Перезагрузим таблицы привилегий:

mysql> FLUSH PRIVILEGES;

Теперь осталось только изменить пароль root. Если у вас MySQL 5.7.6 или новее, MariaDB 10.1.20 или новее, используем следующую команду:

ALTER USER 'root'@'localhost' IDENTIFIED BY 'new_password';

Для версий MySQL 5.7.5 или старее, MariaDB 10.1.20 или старее, используем команду:

SET PASSWORD FOR 'root'@'localhost' = PASSWORD('new_password');

Результат выполнения команды должен выглядеть следующим образом:

Output
Query OK, 0 rows affected (0.00 sec)

Перезапустим сервер сервер и подключимся к MySQL с использованием нового пароля:

mysql -u root -p

Таким образом, мы спросили пароль root и получили доступ к MySQL с правами администратора.

Очистить журнал systemd

На сегодняшний день systemd стал де-факто стандартом для современных Linux-систем. Его компонент journal cобирает все системные сообщения: сообщения ядра, служб и приложений. После чего происходит их запись в виде бинарных файлов. Для просмотра и управления файлами логов используется утилита journalctl.

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

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

journalctl --disk-usage

Что бы ограничить бесконтрольное хранение журнала, нам необходимо выполнить настройку ротации логов. С помощью опций −−vacuum-size и −−vacuum-time мы можем соответственно установить предельно допустимый размер и срок хранения для хранимых на диске логов.

Команда ниже устанавливает предельно допустимый размер для хранимых на диске логов в 1 ГБ, после его превышения лишние файлы будут автоматические удалены.

journalctl --vacuum-size=1G

Подобным образом образом работает опция −−vacuum-time. Команда установит для логов срок хранения, по истечении которого они будут удалены автоматически:

journalctl --vacuum-time=1years

Дополнительно нужно прописать настройки ротации логов в конфигурационный файл:

vi /etc/systemd/journald.conf

Настройки ротации логов включают следующие параметры:

# Максимальный объём логов на диске
SystemMaxUse=
# Объём свободного места на диске после сохранения логов
SystemKeepFree=
# Объём файла лога, по достижении которого он должен быть удален
SystemMaxFileSize=
# Максимальный объём, который логи могут занимать в /run
RuntimeMaxUse=
# Объём свободного места, которое должно оставаться в /run после сохранения логов
RuntimeKeepFree=
# Объём файла лога, по достижении которого он должен быть удален из /run.
RuntimeMaxFileSize=

Применяем настройки без перезапуска системы:

systemctl restart systemd-journald

Автоматическая установка Debian

Установку Debian можно полностью автоматизировать путем использования специального файла с заранее указанным сценарием ответов на все вопросы инсталлятора. Данный способ автоматической установки называется Debian Preseed. Далее созданный файл-сценарий ответов preseed.cfg запаковывается в initrd установочного iso-образа Debian.

Информацию с описанием параметров сценария Preseed можно можно найти на официальном сайте. И я не вижу смысла дублировать эту информацию в своем блоге. Вместо этого я сразу размещу содержимое используемого мной файла preseed.cfg, а если у вас возникнут вопросы, то описание параметров вы всегда сможете найти по указанной выше ссылке.

В файле сценариев для своих нужд я использовал такие переменные параметры, как: ($IP), ($NETMASK), ($GATEWAY). Ниже привожу пример моего файла сценария Debian Preseed. Внимание! Особенно обратите внимание на блок ### Partitioning, вначале которого я полностью забиваю нулями содержимое диска. Данные будут полностью стерты со всех дисков в системе, будьте крайне осторожны.

### Localization
d-i debian-installer/locale string en_US
d-i console-setup/ask_detect boolean false
d-i keyboard-configuration/xkb-keymap select us

### Network configuration
d-i netcfg/choose_interface select auto
d-i netcfg/disable_autoconfig boolean true
# IPv4
d-i netcfg/get_ipaddress string ($IP)
d-i netcfg/get_netmask string ($NETMASK)
d-i netcfg/get_gateway string ($GATEWAY)
d-i netcfg/get_nameservers string ($NAMESERVER)
d-i netcfg/confirm_static boolean true
# Hostname
d-i netcfg/get_hostname string ($HOSTNAME)

### Mirrors
d-i mirror/country string manual
d-i mirror/http/hostname string ($MIRROR)
d-i mirror/http/directory string /debian
d-i mirror/http/proxy string ($HTTPPROXYv4)
d-i mirror/suite string stable

### Account setup
d-i passwd/root-login boolean true
d-i passwd/make-user boolean false
d-i passwd/root-password-crypted password ($PASS_CRYPT)
d-i user-setup/allow-password-weak boolean true
d-i user-setup/encrypt-home boolean false

### Clock
d-i clock-setup/utc boolean true
d-i time/zone string ($TIMEZONE)
d-i clock-setup/ntp boolean false

### Partitioning
d-i partman/early_command string \
for DISK in $(list-devices disk); do \
    parted -s ${DISK} mklabel gpt; \
    parted -s ${DISK} mklabel msdos; \
        dd if=/dev/zero of=${DISK} bs=512 count=1; \
done;

d-i partman-auto/method string regular
d-i partman-basicfilesystems/no_swap boolean false
d-i partman-auto/expert_recipe string                         \
      root ::                                                 \
              5000 5000 -1 ext4                               \
                      method{ format } format{ }              \
                      use_filesystem{ } filesystem{ ext4 }    \
                      mountpoint{ / }                         \
              .                                               \
d-i partman-partitioning/confirm_write_new_label boolean true
d-i partman/choose_partition select finish
d-i partman/confirm boolean true
d-i partman/confirm_nooverwrite boolean true
d-i partman/mount_style select uuid

### Apt setup
d-i apt-setup/contrib boolean true
d-i apt-setup/services-select multiselect security, volatile
tasksel tasksel/first multiselect minimal
d-i pkgsel/include string openssh-server
popularity-contest popularity-contest/participate boolean false

### Grub
d-i grub-installer/only_debian boolean true
d-i grub-installer/with_other_os boolean true
d-i grub-installer/bootdev string default

### Finish install
d-i finish-install/keep-consoles boolean true
d-i finish-install/reboot_in_progress note

d-i preseed/late_command string \
        sed -i '/^PermitRootLogin/c PermitRootLogin yes' /target/etc/ssh/sshd_config ;\
        if [ -n "($SSHPUBKEYS)" ]; then \
                mkdir -p /target/root/.ssh ;\
                chmod 700 /target/root/.ssh ;\
                echo "($SSHPUBKEYS)" > /target/tmp/keyfile ;\
                cat /target/tmp/keyfile | /target/usr/bin/base64 -d > /target/root/.ssh/authorized_keys ;\
                in-target rm -f /tmp/keyfile ;\
        fi ;\
        sed -i '/^GRUB_CMDLINE_LINUX_DEFAULT/c GRUB_CMDLINE_LINUX_DEFAULT="quiet"' /target/etc/default/grub ;\
        sed -i '/^GRUB_TIMEOUT/c GRUB_TIMEOUT=0' /target/etc/default/grub && in-target update-grub