Haproxy: ограничения для подсети

Защищая сервер иной раз возникает желание — сделать ограничение для ip-сети заданного размера. Покажем, как это делается на haproxy.

Задача №1. Разрешить не более 5 подключений с /22 сети к почтовому серверу в минуту

Мы полагаемся на то, что haproxy находится на шлюзе, а почтовый сервер имеет внутренний адрес 10.0.0.2 и слушает 25, 143 порты.

Решение:

frontend  mail
    bind [email protected]:25,[email protected]:143
    mode tcp

    stick-table type ip size 1k expire 1h store gpc0_rate(1m)

    tcp-request inspect-delay 20
    tcp-request content set-var(req.subnet) src,ipmask(22)
    tcp-request content track-sc0 var(req.subnet)
    tcp-request content sc-inc-gpc0(0)

    acl abuse1 sc0_gpc0_rate gt 5
    tcp-request content reject if abuse1

    default_backend mail

backend mail
    mode tcp

    server mail1 10.0.0.2 check port 25

Ключевая идея: в stick-table храним не адрес клиента, а IPv4 обработанный ipmask конвертером.
Разберем нетривиальные моменты:

  1. tcp-request inspect-delay 20
    

    Просим haproxy изучать входящее соединение не более 20 ms. Управление перейдет дальше или по истечению этого времени, или раньше, если будет получен весь запрос.

  2. tcp-request content set-var(req.subnet) src,ipmask(22)
    

    Создаем служебную переменную subnet видимую только на этапе запроса(префикс req.). Её значением будет адрес клиента(src) приведенный к 22 маске.
    [wpex more=»Подробнее о префиксах.»]

    1. Если префикс req — переменная видна только на этапе обработки запроса.
    2. Если префикс res — переменная видна только на этапе обработки ответа сервера.
    3. Если префикс txn — переменная видна как на этапе запроса, так и на этапе ответа.
    4. Если префикс sess — переменная видна в течении всей сессии(может содержать множество транзакций вида запрос-ответ).
      [/wpex]
    5. tcp-request content track-sc0 var(req.subnet)
      

      Haproxy имеет три(0, 1, 2) слота для отслеживания пользовательских параметров. Данной командой мы инструктируем следить за переменной req.subnet. Ньюанс в том, что обычно все счетчики обновляются после выполнения запроса, а команда track-sc* переводит заданные из них в режим реального времени.

    6. tcp-request content sc-inc-gpc0(0)
      

      Инкрементируем значение счетчика(gpc0) ассоциированого с переменной нулевого трекинг-слота.

    Обзор решения

    1. Оно только для IPv4, можно ли универсально сделать и для IPv6? Можно, но лучше создать отдельный frontend с stick-table type ipv6. Если же очень хочется, то можно поменять на type string len 40. Тогда заработает для обоих протоколов.
    2. В сравнении с обычным DNAT имеется существенный недостаток — почтовый сервер видит адрес прокси, а не клиента. Можно ли это исправить? Да. Об этом transparent режим и наш следующий пример.

    Задача №2. Пробросить ip-клиента в tcp-режиме

    Продолжаем с нашим почтовым сервером. Брутфорс нас уже не беспокоит, но понимать что это за люди — хочется. А в логах сплошные адреса haproxy — надо исправлять.

    Решение

    Обзор

    Данная техника чуть менее, чем полностью опирается на tproxy режим работы ядра. И концептуально сводится к следующему. Обращаясь к mail-серверу мы в src пакета помещем ip-клиента(как бы делая вид, что нет тут никакого прокси). Затем наша цель перехватить идущий обратно пакет(помним, что мы — роутер) и объяснить ядру, что его следует завернуть в haproxy. Теперь уже отправляя ответ клиенту мы подставим свой внешний айпишник, вместо внутреннего.

    Подготовка системы

    1. Создаем вспомогательную цепочку в iptables:
      iptables -t mangle -N DIVERT
      

      Скрытого смысла особо нет — цепочка для удобства, в ней мы будем помечать нужные нам пакеты.

    2. Создаем хитрые iptables-правила:
      iptables -t mangle -A PREROUTING -p tcp -m socket --dport 25 -j DIVERT
      iptables -t mangle -A PREROUTING -p tcp -m socket --dport 143 -j DIVERT
      iptables -t mangle -A PREROUTING -p tcp -m socket -s 10.0.0.2 -j DIVERT
      

      Вся тонкость в том, как работает этот match-модуль. Он срабатывает в двух ситуациях:

      1. Есть установленное соединение связанное с текущим пакетом.
      2. Есть какой-то локальный процесс слушающий порт назначения.

      Тем самым из третьего правила управление переходит в цепочку DIVERT, только для почтовго трафика(по п.1).

    3. Помечаем пакет:
      iptables -t mangle -A DIVERT -j MARK --set-mark 1
      

      Напомним, что в самом пакете никаких меток нет. Они видны только в пределах ядра.

    4. Создаем правило и табличку маршрутизации для пакетов с меткой:
      ip rule add fwmark 1 lookup 100
      

      Важно помнить, что RPDB(где и оказалось правило) просматривается до основной таблицы маршрутизации.

    5. Осталось пояснить ядру, что пакеты с этой меткой будет обрабатывать какой-то локальный процесс:
      ip route add local 0.0.0.0/0 dev lo table 100
      

      Прошлым шагом у нас автоматически создалась табличка с номером 100. Теперь мы в неё добавили правило с ключевым словом local — что цель находится на этом хосте.

    Перевод haproxy в transparent режим

    frontend  mail
        bind [email protected]:25,[email protected]:143 transparent
        mode tcp
    
        stick-table type ip size 1k expire 1h store gpc0_rate(1m)
    
        tcp-request inspect-delay 20
        tcp-request content set-var(req.subnet) src,ipmask(22)
        tcp-request content track-sc0 var(req.subnet)
        tcp-request content sc-inc-gpc0(0)
    
        acl abuse1 sc0_gpc0_rate gt 5
        tcp-request content reject if abuse1
    
        default_backend mail
    
    backend mail
        mode tcp
        source 0.0.0.0 usesrc clientip
    
        server mail1 10.0.0.2 check port 25
    

    Основные моменты:

    1. Ключевое слово transparent в bind.
    2. Инструктируем haproxy использовать ip-клиента в качестве src подключаясь к mail-серверу:
      source 0.0.0.0 usesrc clientip
      

      Первые 4 нуля не несут какого-либо смысла в transparent режиме. Тут влияет только clientip, который напротив работает только в нём.

Squid: Блокируем ipv4

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

В чём проблема?

Перед IPv6 прокси иной раз ставится такая задача — обойти ip-фильтры социальных сетей. С этой точки зрения единый IPv4 адрес может стать маркером того, что за всеми адресами стоит один и тот же человек.

Как узнать ipv4?

Механизм прост. Со стороны сервиса подсовываем какой-нибудь ресурс доступный только по v4, например невидимую, никому не нужную картинку. Не найдя ipv6 он произведет подключение по ipv4 адресу, тем самым раскрыв последний.

Позиция squid

Штатными средствами можно с легкостью заблокировать ipv4, но, увы, нет механизма управления резолвингом dns имён. Иными словами инструкция: для такого-то сайта использовать только ipv6 затруднительна в сквиде. Точка зрения разработчиков на этот счет — если у Вас возникла такая задача, то решайте её на уровне операционной системы.

Методы блокировки

Squid http_access deny

Первый и простейший способ — запретить весь не ipv6 трафик:

acl to_ipv6 dst ipv6
http_access deny !to_ipv6

Это хорошее решение, только если Вы готовы пожертвовать всем протоколом. Вы можете комбинировать его с:

acl domain_allow dstdomain .fastenv.ru

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

Iptables

Решение аналогично прошлому, но на уровне операционной системы:

iptables -A OUTPUT -p tcp --dport 80 -j DROP
iptables -A OUTPUT -p tcp --dport 443 -j DROP

Используя сетевой фильтр мы запрещаем обращения к 80 и 443 портам по ipv4.

Squid cache_dns_program

На этот раз рассмотрим наиболее корректное решние нашей задачи, но худшее по мнению squid. До версии 2.3 у него по техническим причинам не было встроенного днс резолвера и они использовали внешнюю программу — dnsserver. Путь до которой и определялся опцией cache_dns_program. Стоит заметить, что в современных версиях этот механизм выключен, а для включения требуется пересобрать сквид с опцией ‐‐disable-internal-dns. Разработчики рекомендуют никогда так не делать, но пойдем до конца:

  • Пересобираем squid с ‐‐disable-internal-dns.
  • Напишем скрипт, который займется у нас резолвингом:
    #!/bin/bash
    # dnsserver.sh
    
    resolve(){
    	local domain
    	local result
    
    	domain="${1}"
    	result=$(dig +short -t AAAA "${domain}")
    	if ! [[ "${result}" ]]
    	then
    		echo "\$fail"
    		return
    	fi
    	echo "\$addr 0" ${result}
    }
    
    while read domain
    do
    	resolve "${domain}"
    done
    
    • Формат успешного ответа:
      ('$addr') (время актуальности) (ответ днс сервера в порядке приоритета)
      
    • Формат неуспешного ответа:
      ('$fail')
      
  • Добавляем в конфигурационный файл:
    cache_dns_program /path/to/dnsserver.sh
    

Теперь разрешением dns имен занимается наш собственный скрипт, которым мы можем контролировать процесс. В примере выше, благодаря dig -t AAAA он отдает только ipv6 адреса. Данный метод, как и прочие — далек от идеала. Он требует не всегда тривиальной пересборки сквида, повышает нагрузку на процессор, увеличивает задержку на днс. За то он единственный, кто формально сохраняет работоспособность ipv4(открывающиеся по ip сайты продолжат открываться).

Анонимный ipv6 прокси канал

Мы делаем сервис для комфортной, распределенной работы с социальными сетями. Используя наш ipv6 канал привычные трудности(как блокировка например) — перестают существовать.

Кратко о главном

  • 3000 проксей.
  • 1000 /64 подсетей.
  • 100 рублей в час.
  • В одни руки.

Концепция

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

Гео

Территориальная принадлежность у нас не задана. Адреса могут оказаться как российские, так и голландские, так и укарианские, да и многие другие. Если Вам критична принадлженость проксей к определенному региону — пожалуйста, обозначьте это отдельно. Решим.)

Оплата

Мы сторонники тезиса понравилось — оплатил. Минимальный срок аренды — 1 час. Первый заказ по предоплате, все последующие можно по постоплате. Квант платежа — 100 рублей.

Ограничения

К совершенству следует стремиться, но идеал недостижим. Так и у нас есть свои ограничения:

  1. Не более 20 потоков — если Ваша программа многопточна, то следует установить число потоков в 20. Чаще всего этого более чем достаточно, но если Вам по каким-то причинам мало — пишите.)
  2. Что запрещено — того делать не надо.

Альтернатива

Если Вы не хотите зависеть от какой-либо компании и желаете самостоятельно все контролировать, то мы можем настроить ваш сервер аналогичным образом. Так же ознакомьтесь с статьей о принципе работы.

Подходит для

  • instagram
  • vk
  • fb
  • twitter
  • youtube
  • avtio
  • И многих других схожих сервисов

WordPress: прячемся за CloudFlare

CloudFlare — великолепный сервис, который для нас решает следующие задачи:

  1. CDN — кеширование статики, снижение трафика, снижение нагрузки на сервер.
  2. HTTPS сертификат — повышает доверие пользователей, улучшает SEO.
  3. Базовая защита от DDOS и подобных атак.

Самое приятное в cloudflare это цена. Всё вышесказанное предоставляется бесплатно. Теперь закончим с комплиментами и перейдем к делу. Особенность этой заметки в том, что мы используем haproxy и хотели сохранить доступность по http.

WordPress & HAProxy

wp-art1-4
Наш сайт располагается на двух равноправных нодах, каждая из которых работает под управлением nginx, который слушает только 80-й порт. Для корректной работы обоих протоколов нам требуется обучить вордпресс отличать https трафик от обычного. С этой целью силами haproxy все ssl запросы пометим специальным заголовком, а в WP добавим его обработку.

HAProxy & Ssl

  1. Создадим самоподписанный сертификат:
    mkdir -p /etc/haproxy/keys
    openssl req \
      -x509 \
      -newkey rsa:2048 \
      -keyout /etc/haproxy/keys/fastenv.pem \
      -out /etc/haproxy/keys/fastenv.pem \
      -days 3650 \
      -nodes \
      -subj "/C=RU/ST=Saint-Petersburg/L=SPB/O=Fastnev/CN=fastenv.ru"
    
  2. Настроим бекенд:
    backend wordpress
            server wordpress1 10.0.2.12:80 check
            server wordpress2 10.0.2.13:80 check
    
  3. Настроим фронтенд:
    frontend wordpress
            bind *:80
            bind *:443 ssl crt /etc/haproxy/ssl/fastenv.pem
            option forwardfor
            http-request add-header X-Fastenv-Https https if { ssl_fc }
    
            use_backend wordpress
    
    • bind *:443 ssl crt — включает терминирование ssl соединения.
    • { ssl_fc } — анонимная acl принимающая значение true всякий раз, как соединение установлено с использованием ssl.
    • option forwardfor — добавляем X-Forwarded-For заголовок, что бы видеть реальный ip клиента. С CF эта опция не нужна т.к. сервис сам проставляет XFF, но и не мешает.

WordPress & X-Fastenv-Https

В wp-config.php добавим:

function isHttps() {
        return (!empty($_SERVER['HTTP_X_FASTENV_HTTPS']));
}

$web_site_domain = 'fastenv.ru';

if (isHttps()) {
        define('WP_HOME',   'https://' . $web_site_domain);
        define('WP_SITEURL','https://' . $web_site_domain);
        $_SERVER['HTTPS'] = 'on';
}
else {
        define('WP_HOME',   'http://' . $web_site_domain);
        define('WP_SITEURL','http://' . $web_site_domain);
}

Настройка CloudFlare

На вкладке Crypto включаем SSL->Full. С этого момента CF будет обращаться по ssl к нашему серверу для https соединений. Тем самым защищается трафик от компрометации на всех участках сети user -> CF -> наш сервер.

Как вообще это работает?

Первым делом Вы переносите домен на CF, т.е. делегируете сервису управление dns записями. Далее сайт начинает резолвиться в их адреса и все пользователи желающие его просмотреть приходят(по anycast) на ближайший сервер CloudFlare, который по многим параметрам оценивает, является ли трафик хорошим. Если трафик покажется подозрительным, то будет запрошена капча. На практике их фильтры очень щадящие и капчей лишний раз не беспокоят. Далее запрос будет передан на Ваш сервер или же обработан локально согласно настройкам кеширования.

Бонусы

  1. Always Online — если сервер упадет, то CF будет показывать последнюю статическую версию из кеша.
  2. Гибкая настройка кеширования — на вкладке Page Rules можно настроить правила хоть под каждую страницу сайта.
  3. Оптимизация объема кода — это опции семейства Speed->Auto Minify, которые умеют удалять комментарии, пробелы, и похожие «лишние» символы не несущие полезной информации для интерпретатора. Работает для js, css и html кода.
  4. Исторические графики — сколько уников, сколько запросов, сколько трафика по ssl, сколько отдано из кеша и так далее. До яндекс метрики не тянет, но с общим представлением о ресурсе справляется отлично.
  5. Api — позволяет многое. Например, сбросить кеш при публикации нового поста.
  6. Поддержка IPv6.

Squid: Проксируем ipv4 -> ipv6

Squid — один из лучших представителей forward proxy серверов. В этой статье мы поговорим о:

  1. Авторизации.
  2. Проксировании IPv4 в IPv6.
  3. Производительности.

В чём задача?

Предположим, у нас имеется сервер с одним IPv4 адресом и уймой IPv6. Требуется так настроить squid, чтобы при изменении логина, менялся исходящий IPv6 адрес.

Концепция

Решение опирается на два механизма:

  1. Через acl proxy_auth создается условие, которое возвращает true только тогда, когда заданный пользователь авторизовался с верным паролем.
  2. Директива tcp_outgoing_address задает исходящий IPv6 адрес.

Установка

Весь необходимый функционал имеется в стандартном пакете вашего дистрибутива. В debian-based системах сводится к простой установке squid:

apt-get update
apt-get install squid3

Общие настройки

Для дальнейшего удобства все общие настройки сгруппируем по отдельным файлам:

  1. Разрешаем порты 80 и 443, разрешаем использовать метод CONNECT только с 443:
    # allow-port.conf
    acl allow_port port 80
    acl allow_port port 443
    acl https_port port 443
    acl CONNECT method CONNECT
    
    http_access deny !allow_port
    http_access deny CONNECT !https_port
    
  2. Запрещаем некоторые http заголовки:
    # deny-headers.conf
    request_header_access X-Forwarded-For deny all
    request_header_access Via deny all
    request_header_access Proxy deny all
    request_header_access Cache-Control deny all
    

    Данные правила делают http прокси анонимным. Важно отметить — при работе с https сайтами squid не имеет возможности вмешиваться в трафик, таким образом он не может ни удалить, ни добавить заголовки. Таким образом https прокси изначально являются анонимными.
    [wpex more=»Замечание про анонимность.» less=»Мне всё ясно!»]
    Мы называем прокси анонимным следуя общепринятому формальному определению — это такой прокси, который специально не добавляет информацию о клиенте. Надо понимать, что о реальной анонимности речи не идёт и анализируя различные параметры можно установить факт работы через промежуточный узел.
    [/wpex]

  3. Настройки авторизации:
    # auth.conf
    auth_param basic children 100
    auth_param basic realm Fastenv Proxy
    auth_param basic credentialsttl 2 hours
    
    acl auth_required proxy_auth REQUIRED
    http_access deny !auth_required
    
  4. Список разрешенных доменов:
    # allow-domain.conf
    acl allow_domain dstdomain .fastenv.ru
    acl allow_domain dstdomain .instagram.com
    acl allow_domain dstdomain .whatismyv6.com
    
    http_access deny !allow_domain
    

    Мы разрешаем доступ только к определенным сайтам. Если это не требуется, то просто не подключайте этот файл в основном конфиге.

  5. Прочие настройки(рабочая директория, таймауты, etc):
    # other-settings.conf
    coredump_dir /var/spool/squid
    
    connect_timeout 10 minute
    read_timeout 50 minutes
    

Авторизация

Принцип работы следующий. Из имени username, пароля password клиент формирует строку username:passowrd и конвертирует в base64. Примерно так:

echo -n 'username:password' | base64

Результат помещается в заголовок Authorization, который добавляется к запросу.
Теперь в дело вступает squid. Он принимает запрос и начинает последовательно обрабатывать правила до первого подходящего http_access (allow|deny). Если на этом пути встретилась acl вида proxy_auth, то проверяется заголовок Authorization (как именно описано ниже). Теперь у запроса два пути:

  1. Быть выполненным — если достигнут http_access allow.
  2. Быть прерванным — если достигнут http_access deny. В этом случае возвращается код ответа 407, который браузеры расценивают как запрос авторизации.

Как проверяется авторизация?

Squid декодирует base64 значение заголовка Authorization в текст. Примерно так:

echo -n 'dXNlcm5hbWU6cGFzc3dvcmQ=' | base64 -d
username:password

Имя пользователя и пароль передаются на стандартный вход программе аутентификации, которая должна дать ответ, пользователь хороший или нет. Регламент простой: OK — успешно, ERR — нет. Пример программы, которая всем доверяет:

#!/bin/bash

while read input
do
  echo "OK"
done

[wpex more=»Программа, которая доверяет только нам.» less=»Программа, которая доверяет только нам.»]

#!/bin/bash

while read username password
do
  if [[ "${username}" == "fastnev" ]] && [[ "${password}" == "best" ]]
  then
    echo "OK"
    continue
  fi
  echo "ERR"
done

Как Вы понимаете, тут может быть любой скрипт, да хоть авторизация через vk api.
[/wpex]

Что делают опции?

  1. auth_param basic program /usr/lib/squid3/basic_ncsa_auth /etc/squid/users-3128
    
    • Задает программу аутентификации basic_ncsa_auth с аргументом /etc/squid/users-3128. Используется как простейший стандартный способ управления пользователями. [wpex more=»Примеры.» less=»Примеры.»]

      touch /etc/squid/users-3128
      
      1. Добавить пользователя fastenv:
        htpasswd /etc/squid/users-3128 fastenv
        
      2. Удалить fastenv`а:
        htpasswd -D /etc/squid/users-3128 fastenv
        

      [/wpex]

  2. auth_param basic children 100
    
    • Максимальное число процессов аутентификации. Важно отметить, что это отдельные, дочерние процессы. Squid общается с ними по tcp через localhost.
  3. auth_param basic realm Fastenv Proxy
    
    • Приветствие формы авторизации.
  4. auth_param basic credentialsttl 2 hours
    
    • Срок действия успешной авторизации. По его истечению будет запрошена повторно.

Выбираем IPv6

Допустим пользователь fastenv должен выходить с 2400:cb00:2048:1::6812:3e7b IPv6 адреса, тогда:

  1. Создаем acl fastenv1 и правило использовать такой-то source адрес в случае истинности fastenv1:
    # access-rules-3128.conf
    acl fastenv1 proxy_auth fastenv
    tcp_outgoing_address 2400:cb00:2048:1::6812:3e7b fastenv1
    
    • Где fastenv — имя пользователя.
  2. Добавляем IP:
    ip -6 addr add 2400:cb00:2048:1::6812:3e7b/64 dev eth0
    
    • Где 64 маска сети, а eth0 внешний интерфейс.

Конфиг squid

# squid1.conf
include /etc/squid/allow-port.conf
include /etc/squid/deny-headers.conf
auth_param basic program /usr/lib/squid3/basic_ncsa_auth /etc/squid/users-3128
include /etc/squid/auth.conf
include /etc/squid/access-rules-3128.conf
include /etc/squid/domain-allow.conf
http_access allow all
http_port 3128
access_log /var/log/squid/squid-3128.log
cache_log /var/log/squid/cache-3128.log
pid_filename /var/log/squid/squid-3128.pid
include /etc/squid/other-settings.conf

Производительность

Squid — однопоточный прокси-сервер. В один момент его пропускная способоность упирается в частоту одного ядра процессора. Для обхода этого ограничения на многоядерных системах можно запускать несколько процессов на разных портах. По нашему опыту нагрузка также растет и от количества proxy_auth правил. Имея это в виду, все общие настройки вынесены в отдельные файлы, а создание еще одного процесса сводится к замене конфига и правки стартового скрипта.

Haproxy: настраиваем active/standby балансировку

Постановка задачи

Имеется два экземпляра http приложения. Требуется настроить работу прокси сервера так, чтобы под нагрузкой всегда находился только один. Активный экземпляр должен оставаться в работе до тех пор, пока не будет выключен или признан нерабочим.

Проблема

Haproxy предлагает множество алгоритмов балансировки, но, к сожалению, ни один из них не подходит под наши требования. Чаще всего вместо Active-Standby используют Active-Backup, когда создается бекенд с roundrobin балансировкой, в котором один из серверов помечается ключевым словом backup. Такой подход обладает следующим недостатком: трафик будет возвращаться на «упавшую» ноду всякий раз, как она будет помечена UP проверкой check. Иными словами, если сервер «сбоит» под нагрузкой, то трафик будет прыгать «туда-сюда». Для некоторых приложений это может оказаться критичным.

Решение

backend fastenvAS
        stick-table type ip size 3 store gpc0
        tcp-request content track-sc0 dst

        acl srv1_up srv_is_up(server1)
        acl srv2_up srv_is_up(server2)

        acl srv1 sc0_get_gpc0(fastenvAS) eq 1
        use-server server1 if srv1 srv1_up

        acl srv2 sc0_get_gpc0(fastenvAS) eq 2
        use-server server2 if srv2 srv2_up

        acl fake1 sc0_clr_gpc0 gt 0
        use-server fake if fake1 { always_false }

        acl fake2 sc0_inc_gpc0 gt 0
        use-server fake if fake2 { always_false }
        use-server server1 if srv1_up

        acl fake3 sc0_inc_gpc0 gt 0
        use-server fake if fake3 { always_false }
        use-server server2 if srv2_up

        server fake 127.0.0.1:1300 weight 0
        server server1 172.17.44.50:1300 check
        server server2 172.17.44.51:1300 check

Ключевая идея: в stick табличке храним число-индикатор активного сервера. Если этот сервер в рабочем состоянии, то отдаем запрос ему, если же нет — выполнение переходит к другим условиям.
Последовательно разберем используемые хаки:

  1. Первый трюк заключается в том, что мы храним в табличке dst, т.е. destination ip, а это, на tcp-уровне адрес самого haproxy. Вместе с ним хранится gpc0 — first General Purpose Counter, что не иначе как просто какой-то счетчик, который вы используете для своих целей.
    stick-table type ip size 3 store gpc0
    tcp-request content track-sc0 dst
  2. По нашему замыслу, значение gpc0=K сигнализирует о том, что в нагрузке находится K-й сервер. В соответствии с этим описываем блоки acl:
    acl srv1 sc0_get_gpc0(fastenvAS) eq 1
    use-server server1 if srv1 srv1_up
    ...
    

    Тут скрыт еще один нетривиальный момент. Для какого объекта будет выполнена sc0_get_gpc0? Для ip адреса связанного с текущим запросом, а это, как мы попросили прокси, всегда его собственный адрес.

  3. Рассмотрим следующий трюк:
    acl fake1 sc0_clr_gpc0 gt 0
    use-server fake if fake1 { always_false }
    

    Если управление перешло в эту точку, значит активный сервер у нас еще не выбран. Или же с ним что-то случилось. Так уж вышло, что haproxy опускает условия, которые нигде не используются. Мы описали fake сервер только для того, что бы вызывать aсl`ки работающие с gpc0. Смысл самой sc0_clr_gpc0 простой — обнуляем счетчик.

  4. Далее действуем аналогично:
    acl fake2 sc0_inc_gpc0 gt 0
    use-server fake if fake2 { always_false }
    use-server server1 if srv1_up
    
    1. Инкрементируем счетчик.
    2. Является ли этот сервер живым?
      1. Да — используем его, управление заканчивается.
      2. Нет — переходим к следующему аналогичному блоку.

Внимательный читатель, вероятно, обратил внимание: в нашем примере значение gpc0 < 3, тогда зачем мы используем конструкцию вида: { always_false }? Ведь достаточно немного изменить числа, и значения acl и так всегда будут False. Никакого скрытого смысла в этом нет. Это сделано для выразительности конфига.

Nagios: обоснование выбора

Сегодня существует множество разнообразных систем мониторинга и регулярно появляются новые. Мы, в fastenv, этим обстоятельством удивлены. Ведь великолепному Nagios уже более 15 лет! В рамках данной статьи хочется мотивировать читателя выбрать именно его для своих задач. А так же начать ему посвященный цикл.

Мифы

Первым делом развеем пару мифов.

Миф №1. Нагиус не развивается

Нагиус взрослый проект, который уже реализовал почти всё, что может потребоваться при решении задачи мониторинга. Им нет необходимости выпускать новые релизы раз в месяц. Стабильные релизы выходят раз в 6-12 месяцев. В тоже время коммиты в их git репозиторий идут регулярно. Последний — менее одного дня назад на момент написания этих строк. Иными словами, нагиус здоров и бодрствует!

Миф №2. Сообщество мертво

Действительно, глядя на список плагинов может сложиться впечатление, что многие из них устарели, не рабочие, а новые почти никто не пишет. Дело вот в чем. Nagios имеет очень простое api взаимодействия core<->plugin, которое совместимо между всеми его версиями. Более того, это api взяли на вооружение многочисленные его форки, что делает их плагины так же пригодными для нагиуса. В итоге, с одной стороны, некогда написанный под вторую версию плагин остается рабочим и под четвертую. С другой — множество плагинов пишется под форки. Вот их неполный список: icinga, naemon, shinken, op5.

 

Концепция

Нагиус стремится соответствовать лучшим традициям unix-style. Это инструмент, который делает только свое дело и, поверьте, справляется он очень хорошо. Нагиус — это система сбора метрик и формирования алертов. Всё. Хотите графики? Нету. Хотите хранить данные? Извините. Управление конфигурацией? Конечно нет.
Так в чём же смысл?

Конфиги

Настройка нагиуса представляет из себя большей частью описание объектов мониторинга. Ключевое свойство последних — наследование. Объект может унаследовать как один, так и несколько шаблонов, в каждом из которых можно добавить или переопределить какое-то свойство. В чём удобство? Покажем на примере. У Вас имеется три датацентра, в каждом ДЦ имеется сетевое обрудование, linux и windows сервера. Задача — лаконично настроить мониторинг.

  1. В nagios.cfg добавим:
    cfg_dir=/home/nagios/settings/datacenters
    

    С этого момента нагиус начнет рекурсивно просматривать все директории в datacenters и искать *.cfg. Каждый такой файл будет обработан.

  2. Создадим каталоги dc-1 dc-2 dc-3 dc-all:
    mkdir -pv /home/nagios/settings/datacenters/dc-{1,2,3,all}
    
  3. Общие настройки поместим в dc-all.
    1. Опишем группы хостов и сервисов:
      cat > groups.cfg <<"EOFF"
      define hostgroup{
              hostgroup_name                  Dc-all-hosts
              alias                           All servers
              hostgroup_members               dc-1-hosts,dc-2-hosts,dc-3-hosts # Эти группы будут определены для каждого dc
      }
      
      define servicegroup{
              servicegroup_name               Dc-all-services
              alias                           All services
              servicegroup_members            dc-1-services,dc-2-services,dc-3-services
      }
      EOFF
      
    2. Опишем настройки для хостов и сервисов:
      cat > templates.cfg <<"EOFF"
      define host{
              name                            dc-common-host
              use                             generic-host
              check_period                    24x7
              check_interval                  5
              check_command                   check-host-alive
              contact_groups                  fastenv-team
              register                        0
      }
      
      define host{
              name                            dc-net-host
              use                             dc-common-host
              check_interval                  0.1 # Нет жизни без сети..
              register                        0
      }
      
      define service{
              name                            dc-common-service 
      	use				generic-service
      	register                        0
      	notification_period		24x7
      	notification_interval		30
      	max_check_attempts		3
      	check_interval			1
      	contact_groups			fastenv-team
      }
      EOFF
      

      Рассмотрим определение сервиса. Ключевое слово use наследует стандартный шаблон generic-service, ключевое слово register информирует, что это всего лишь шаблон, а не итоговый объект. Далее мы добавили самые общие параметры:

      1. Круглосуточно информировать.
      2. Уведомлять повторно через 30 минут.
      3. Считать сервис упавшим после 3 провалившихся проверок.
      4. Установить интервал между проверками в одну минуту.
      5. Сообщать команде фастенва.)
  4. Поговорим теперь о том, как будем настраивать каждый dc:
    1. Нам потребуется создать группы хостов dc-*-hosts. Для удобства можно создать еще подгруппы для windows, linux и net хостов.
    2. Следующим шагом создадим шаблон dc-*-service с индивидуальными настройками для датацентра. Он унаследует dc-common-service, доопределит hostgroup_name(принадлежность к группе хостов dc) и что-нибудь еще.
  5. Концептуально все готово для создания проверок:
    1. Я хочу проверять 80-й порт на всех хостах!
        Нет проблем, определяем в dc-all:

        define service{
        	use				dc-common-service
        	host_name			localhost
        	service_description		Check 80 port
        	hostgroup_name			Dc-all-hosts
        	check_command			check_port!80
        }
        
    2. Я хочу проверять 443-й порт везде, кроме dc3!
        Нет проблем, определим в dc-all:

        define service{
        	use				dc-common-service
        	host_name			localhost
        	service_description		Check 443 port
        	hostgroup_name			Dc-all-hosts,!dc-3-hosts
        	check_command			check_port!443
        }
        
    3. В dc2 для всех windows хостов проверять порт 3389:
        Определим в dc-2:

        define service{
        	use				dc-2-service
        	host_name			win1-dc-2-host
        	service_description		Check 3389 port
        	hostgroup_name			dc-2-windows-hosts
        	check_command			check_port!3389
        }
        

    Что главное хотелось донести. Сопровождение нагиуса тем проще и быстрее, чем лучше продумана архитектура мониторинга. Ну а при работе с ним вы не почувствуете скованности, т.к. за 15 лет предусмотрели почти всё.)

Советы по ведению конфига

  1. Храните все файлы в гите.
  2. Создайте каталоги по логическому назначению. Хорошая идея делать симлинки в каталог, который подгружает nagios.
  3. Достаточно гранулируйте шаблоны.
  4. Настройте зависимости. Предположим, в рамках нашего примера, мы хотим проверять 443 порт в dc-2 только если доступен 80-й порт в dc-1.
      Нет проблем. Определим в dc-2:

      define servicedependency{
              hostgroup_name                  dc-2-hosts
              service_description             Check 443 port
              dependent_service_description   Check 80 port
              dependent_hostgroup_name        dc-1-hosts
              execution_failure_criteria      c
              notification_failure_criteria   c,w,u
      }
      

Api проверок

Апи выдержано в концепции минимализма. Существует всего 4 статуса: OK, WARNING, CRITICAL и UNKNOWN. Им соответствуют просто коды завершения плагина: 0, 1, 2 и >2. Если плагин хочет вернуть какое-то сообщение, то он должен сформировать на стандартный выход:

Some text | data=1

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

cat > check_admins.sh <<"EOFF"
#!/bin/bash

echo "Fastenv is working | status=1"
exit 0
EOFF
    Мы работаем 24x7.)

Требования

Сам сервер очень легковесный. В большинстве установок ему хватит 1 ядра и 256 мегабайт памяти. Для запуска проверок на самих хостах в общем случае потребуется установить nrpe сервер. Если же говорить о мониторинге unix хостов, то все проверки можно запускать по ssh используя стандартный "транспортный" плагин check_by_ssh. В таком случае со стороны цели вообще никаких настроек может не потребоваться.
В следующей статье мы поговорим об этом подробнее и покажем, как силами нагиуса можно настроить деплой своих плагинов.

Но как можно жить без графиков?

Действительно, в наш век рюшек и свистелок больших данных, сложно представить систему наблюдения за инфраструктурой без визуализации и хранения истории.
В этом вопросе разработчики строго следуют своей концепции. На стороне сервера нет никакой системы складирования данных, но сделаны все необходимые верёвочки для интеграции. Но давайте обо всём по порядку.
В первую очередь отметим, что нагиус все же хранит историю алертов. Она пишется в специальный status-лог, по которому из веб интерфейса можно сформировать отчет.
Теперь займемся интеграцией. Как уже говорилось, плагин вправе вернуть сообщение специального формата:

| var1=1 .. varN=k

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

  1. Определяем команду, которая будет обрабатывать perfdata:
    define command{
            command_name    fastenv-simple-perfdata
            command_line    /usr/bin/printf "%b" "$HOSTNAME$:$SERVICEDESC$:$SERVICESTATE$:$SERVICEPERFDATA$\n" >> /home/nagios/perfdata.log
    }
    

    Как Вы понимаете, тут можно сразу писть в mysql, постить в твиттер или делать что-то похожее. Мы же просто пишем в лог.

  2. Инструктируем нагиус, какой именно командой требуется обрабатывать данные сервиса(nagios.cfg):
    service_perfdata_file_processing_command=fastenv-simple-perfdata
    
  3. Наконец включаем этот функционал(nagios.cfg):
    process_performance_data=1
    

С хранением, полагаю, разобрались.

Полон простоты и механизм отображения. Для этого в настрйках сервиса задается параметр action_url:

action_url                      http://graph/render?host=$HOST$..

Иными словами Вы описываете ссылку в которой нагиус подставит связанные с сервисом переменные. Пройдя по ней Ваш любимый бекенд отрисует нужный график.

Вывод

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

  1. Rrd + pnp4nagios.
  2. Graphite + graphios.
  3. InfluxDB + nagflux.

Web интерфейс

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

Настройка proxy

Прокси сервера делятся главным образом на два типа forward proxy и reverse proxy. Для каких целей они служат?

Forward proxy

Работая в таком режиме сервер «исполняет» запросы клиента. Нужно запросить some page? Клиент запрашивает её у сервера, сервер исполняет запрос и возвращает ответ клиенту. Чаще всего такой прокси используется для следующих задач:

  1. Анонимизация в сети.
  2. Фильтрация:
    1. С целью ограничения доступа.
    2. С целью модификации страниц(например вырезания рекламы).
  3. Прослушка/аудит трафика.
  4. Разграничение доступа по пользователям.
    • Например разным пользователям доступны разные сегменты интернета.

Типичный представитель — squid.

Reverse proxy

Работая в таком режиме сервер «пропускает» запросы клиента на одну из фиксированных нод. Чаще всего такой прокси используется для следующих задач:

  1. Распределение нагрузки.
  2. Обеспечение бесперебойной работы.
    • Прокси контролирует живость нод и снимает нагрузку, если одна из них не справляется.
  3. Защита от атак.
  4. Управление внутреним трафиком.

Типичный представитель — haproxy.

 

Мы предлагаем:

  1. Настройка ipv6 прокси (на базе squid):
    1. Создание 5000 сочетаний login:password:ipv6.
    2. Инструмент по управлению(добавление, удаление, генерация случайнх) сочетаний.

      Стоимость — 5000 рублей.

  2. Настройка прозрачного(forward) прокси:
    1. Для фильтрации.
    2. Для аудита.
    3. Для контроля доступа.

      Стоимость — 4000 рублей, если не требуется фильтовать/логировать https. C поддержкой https стоимость составит 7000 рублей.

  3. Настройка обратного(reverse) прокси:
    1. Для защиты от атак.
    2. Для балансировки нагрузки.
    3. Для отказоустойчивости.

      Стоимость — 5000 рублей.


 

[contact-form-7 id=»489″ title=»Contact proxy 1_bootstrap»]

Разработка bash скриптов

Bash — современный и развитый язык программирования ориентированный на системное администрирование. Вместе с unix экосистемой на нём можно быстро и эффективно решать разнообразные задачи:

  1. Автоматизация процессов.
  2. Разбор логов.
  3. Построение отчетов.

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

Настройка вебсервера

Мы предлагаем:

  1. Настройка web сервера на базе nginx:
    1. Установка и настройка nginx из репозитория;
    2. Удобный инструмент по созданию своей сборки nginx с выбранными вами модулями (большое количество модулей под самые разные нужды — от java до postgresql);
    3. Настройка любой сложности конфигураций web сервера (http2, geo, редиректы, nginx+php-fpm).

       

  2. Настройка web сервера на базе apache:
    1. Установка и настройка apache из репозитория;
    2. Удобный инструмент по созданию своей сборки apache с выбранными вами модулями;
    3. Настройка apache как бэкенд.

 

Стоимость — Минимальная стоимость 700 рублей — корректная настройка редиректов, настройка параметров кэширования, добавление новых модулей и т.д.

Более сложные проекты (разворачивание web сервера «с нуля», frontend-backend) от 1000 руб и выше.