Глеб Гончаров

Сисадмин и девопс из Ульяновска

Приватный репозиторий образов Vagrant

Для запуска виртуальной машины в Vagrant используют публичные образы или собирают собственный с помощью Packer. Компания HashiCorp предлагает использовать сервис Vagrant Cloud для хранения и версионирования этих образов, чтобы распространять обновления в команде разработчиков. Сервис бесплатный, но артефакты публичные и доступны каждому без авторизации.

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

Для решения этой проблемы написал vgrepo — простую программу командной строки для управления образами Vagrant. В паре с HTTP-сервером утилита упрощает доставку и обновление образов пользователям. Посмотрите скринкаст с демонстрацией работы:

asciicast

При запуске команд up, reload и resume Vagrant ищет метаданные образа в формате JSON. По ним Vagrant проверяет локальную версию и доступную в каталоге на сервере. Если у пользователя версия старше, чем в каталоге, то будет предложено обновить образ.

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

Пользователю достаточно указать ссылку на JSON в переменной config.vm.box_url. Обратите внимание, имя образа в config.vm.box должно совпадать с именем в метаданных:

Vagrant.configure("2") do |config|
  config.vm.box = "centos7-x64"
  config.vm.box_url = "https://vagrant.gongled.me/r/centos7-x64/"
end

На разработку проекта меня вдохновил туториал по развёртке приватного Vagrant Cloud, где подробно описан весь процесс. Если вам нравится проект и вы хотите помочь — присылайте ишьюсы и пул-реквесты на Гитхабе. С удовольствием читаю всё, что мне присылают.

Настройка RAID-контроллера SmartArray B120i в CentOS 7

Небольшая инструкция об установке CentOS 7 на HPE ProLiant Microserver Gen8 с аппаратным RAID-контроллером HP Dynamic Smart Array B120i:

Загрузите образ дискеты для RAID-контроллера с официального сайта Hewlett Packard. Запишите образ на USB-диск с помощью утилиты dd. Не забудьте заменить путь до блочного устройства /dev/sdd и отмонтировать устройство перед запуском:

gunzip < hpvsa-1.2.10-120.rhel7u0.x86_64.dd.gz | pv | sudo dd of=/dev/sdd

Запустите установку CentOS 7, на экране приглашения нажмите TAB, укажите опцию dd и отключите поддержку AHCI для использования драйвера от HP:

linux dd blacklist=ahci

Готово. Во время загрузки ядра будет предложено выбрать источник для драйвера. Выберите USB-диск и продолжите установку на логический диск.

Принудительно завершаем TLS-соединение в NGINX

На днях поставили задачу: есть веб-балансировщик, обслуживающий несколько виртуальных хостов с доступом по HTTP и HTTPS. Требуется разрывать соединение с клиентами, подключающихся по TLS к списку сервисов, доступных только по HTTP. При этом пользователю запретить видеть предупреждения о несоответствии получаемого сертификата и FQDN.

В этом поможет расширение Server Name Indication (SNI) TLS. SNI предоставляет механизм передачи клиентом серверу информации о запрашиваемом хосте. Так веб-сервер узнаёт, какой сертификат ему следует использовать для соединения с виртуальным хостом.

Начиная с версии NGINX 1.11.5 доступен модуль ngx_stream_ssl_preread_module. Модуль извлекает имя сервера, доступное через SNI на фазе ClientHello без терминирования SSL/TLS. Значение заголовка доступно в переменной $ssl_preread_server_name. Используя её, можно динамически задавать адрес проксируемого TCP-бэкэнда:

stream {
  proxy_protocol on;

  upstream teardown {
    server 127.0.0.1:444;
  }

  upstream backend {
    server 127.0.0.1:445;
  }

  map $ssl_preread_server_name $name {
    example.com    teardown;
    default        backend;
  }

  server {
    listen 444;
    return "";
  }

  server {
    listen 443;
    ssl_preread on;
    proxy_pass $name;
  }
}

Так при обращении по HTTPS, NGINX перенаправит трафик в 445-й порт, а для example.com — в 444-й. В первом случае объявляем виртуальный хост и сертификат, во втором — веб-сервер разорвёт соединение.

Есть нюанс: используйте протокол PROXY для отображения реальных IP-адресов пользователей в логах. Например, для виртуального хоста example.com:

server {
  listen 445 http2 proxy_protocol;
  server_name example.com;

  set_real_ip_from 127.0.0.1;
  real_ip_header proxy_protocol;

  [...]
}

К сожалению, не выйдет рвать соединение для тех виртуальных хостов, где не указан SSL-сертификат. Как вариант, вместе с map включать файл со списком разрешённых хостов. Такой список придётся создать вручную, а после отправить SIGHUP мастер-процессу NGINX для применения изменений.

Определение пользователя по ETag в NGINX

Расскажу о старом и простом трюке по слежке за пользователями с помощью HTTP-заголовка ETag. В отличие от Cookies, о нём редко вспоминают, когда пытаются стереть следы присутствия.

Принцип работы

Когда клиент загружает веб-страницу, веб-сервер может передать идентификатор загружаемого ресурса в заголовке ETag:

HTTP/1.1 200 OK
Server: nginx
[...]
ETag: 5888f5e0-34d

Браузер сохранит ответ, а когда ресурс будет нужен снова, то в следующий раз он добавит к запросу заголовок If-None-Match.

GET /style.css HTTP/1.1
If-None-Match: "5888f5e0-34d"
Host: gongled.me

Сервер сверит ETag и значение из If-None-Match: если они совпадают, то в ответ придёт код 304 (Not Modified). Так браузер понимает, что ресурс не изменился и загружать его повторно не нужно. Если идентификаторы разные — браузер повторно загрузит ресурс ещё раз.

HTTP/1.1 304 Not Modified
Server: nginx
[...]
ETag: 5888f5e0-34d

Способы применения

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

$ printf "%x-%x\n" $(stat -c%Y style.css) $(stat --format="%s" style.css)
5888f5e0-34d

Проблема в том, что спецификация HTTP/1.1 не говорит как создавать ETag. На этом принципе и основана идентификация пользователя: вместо идентификатора файла можно отдавать любую строку. Например, переменную $request_id для трассировки запросов в NGINX.

Пользователь откроет страничку со стилями в CSS-файле, отправит запрос ресурсу и получит ответ с ETag и длительным временем кеширования. В следующий раз браузер отправит идентификатор обратно веб-серверу.

Пример конфигурации NGINX:

log_format ident_format '[$request_id] [$http_if_none_match] $remote_addr - $remote_user [$time_local] '
                        '"$request" $status $body_bytes_sent '
                        '"$http_x_forwarded_for" "$http_referer" $host '
                        '"$http_referer" "$http_user_agent" '
                        '$request_time $upstream_response_time';

server {
  listen 80;

  server_name example.com;

  [...]

  location = /css/track.css {
    access_log /var/log/nginx/ident.log ident_format;

    if ($http_if_none_match) {
      return 304;
      break;
    }
    return 200;
    add_header ETag $request_id;
    default_type text/css;
    expires max;
  }
}

Переменную $http_if_none_match можно залогировать или передать в приложение для обработки с помощью proxy_set_header, fastcgi_param или uwsgi_param. Пока пользователь не сбросит кеш, за ним можно следить. Например, узнать когда и с каких IP-адресов тот заходит на сайт:

$ cat /var/log/nginx/ident.log | grep c834687fa74d6b616cedad8ae407d5b5
[c834687fa74d6b616cedad8ae407d5b5] [-] 89.250.169.240 - - [28/Jan/2017:13:27:59 +0300] "GET /css/style.css HTTP/1.1" 304 0 "-" "-" example.com "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.2994.0 Safari/537.36" 0.000 -
[57594da59a7c817aca6b70910a67e331] [c834687fa74d6b616cedad8ae407d5b5] 89.250.169.240 - - [28/Jan/2017:13:28:09 +0300] "GET /css/style.css HTTP/1.1" 304 0 "-" "-" example.com "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.2994.0 Safari/537.36" 0.000 -
[9fbd5604134a4f1dd2fee25d3098314c] [c834687fa74d6b616cedad8ae407d5b5] 95.104.194.197 - - [28/Jan/2017:13:28:20 +0300] "GET /css/style.css HTTP/1.1" 304 0 "-" "-" example.com "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.2994.0 Safari/537.36" 0.000 -

Стратегии защиты

Если вы всерьёз озабочены своей приватностью, используйте режим «инкогнито» в браузере или отключайте кеш. Ознакомьтесь с продвинутыми методами идентификации пользователей в сети.

Я же советую не забивать голову: ETag не связывает ваш IP-адрес с личностью и ничем не грозит. Напротив, не нужно отключать кеширование и сводить на ноль усилия веб-разработчиков по оптимизации проектов.

За что я ненавижу панели управления

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

Дилетанты считают работу законченной, когда сайт открылся в браузере. Это не так. Работа сисадмина продолжается: нужно собирать метрики, делать резервные копии, конфигурировать программы и заменять оборудование.

Хостеры это понимают и устанавливают для неопытных пользователей панель управления — графическую надстройку для работы с сервером при помощи браузера. Таким образом управляют почтой, FTP-аккаунтами, базами данных, добавляют и удаляют домены, загружают файлы и смотрят статистику.

Сейчас я расскажу почему старательно избегаю сервера с панелями управления.

Веб-интерфейс

Начинающим нравится веб-интерфейс из-за лёгкости работы с ним: не нужно запоминать команды и разбираться в сложных настройках. Я предпочитаю командный интерфейс — CLI.

Не функционально. Linux изначально проектировали для работы в командной строке, благодаря чему любой параметр системы можно изменить из терминала. Веб-интерфейс не заменяет командную строку: сложные вещи по-прежнему делают в ней. Да и несложные тоже.

Не гибко. Дизайн CLI прост, а программы в нём делают что-то одно и делают это хорошо. По отдельности они делают простые вещи, но вместе позволяют создавать мощные выражения. Так достигается гибкость.

Например, чтобы показать десять IP-адресов, отправивших больше всего POST-запросов, достаточно выполнить команду:

grep 'POST' /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -n | head

Как такое сделать через веб-интерфейс — не ясно.

Не универсально. CLI выглядит одинаково и не изменяется, в то время как все панели выглядят по-разному. Причина тому — отсутствие спецификации. В CLI программы стандартизированы POSIX, это гарантирует одинаковую работу на разных операционных системах.

Сложно автоматизировать и кастомизировать

Зоопарк редакций. cPanel, Plesk, ISPManager, Vesta, DirectAdmin, Virtualmin, CentOS Web Panel, Ajenti, InterWorx, ZPanel и BlueOnyx — всё это панели управления. Список неполный: есть другие, но о них слышно реже. Они несовместимы друг с другом: у них разная программная архитектура, функциональность, консольные утилиты, версии ПО и пути до файлов.

Например, Plesk работает в Ubuntu и Debian, а DirectAdmin – нет. Virtualmin поддерживает работу с группой серверов, а Ajenti — нет. В ISPManager NGINX поставляют по умолчанию, а в cPanel для этого устанавливают стороннее расширение Engintron. Учесть нюансы непросто.

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

Сервер — не произведение искусства с неповторимой историей. Хороший админ это знает и старается снизить издержки на поддержку инфраструктуры.

Установка на чистый сервер. Если вы попытаетесь поставить панель на уже настроенный сервер, у вас скорее всего ничего не выйдет. Это может стать проблемой, когда вам нужно что-то донастроить или поменять. У вас не получится заменить одну панель на другую — после того, как удалите первую, придётся переустановить всю систему.

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

Панель вставляет палки в колёса: при следующем сохранении она перезапишет изменения, внесённые вручную. Вот подключаетесь вы к серверу с cPanel, открываете конфиг Apache и видите в шапке файла:

#
#   Direct modifications to the Apache configuration file WILL be lost upon subsequent
#   regeneration of this configuration file, or an Apache update.
#

Если что-то поменяете, то в следующий раз придётся настраивать заново.

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

#   To have your modifications retained, you should create/edit administrator-specific
#   include files:
#
#       /etc/apache2/conf.d/includes/pre_main_global.conf
#       /etc/apache2/conf.d/includes/pre_virtualhost_global.conf
#       /etc/apache2/conf.d/includes/post_virtualhost_global.conf

Для автоматизации действий в панелях есть API. Например, в cPanel две версии API: старая и новая. В новой версии ответы в XML или JSON, а в старой — HTML, строки и числа. Выходит, теперь нужно писать код с обратной совместимостью между версиями. Если панелей несколько — то и между ними.

Сложно поддерживать

Файлы не по полочкам. Для единообразия и совместимости приложений в Linux придерживаются стандарта структуры каталогов файловой системы. Благодаря нему ясно где искать файлы программ, их конфигурации и журналы событий. На серверах с панелями тяжело диагностировать проблемы, потому как каждая хранит файлы по-своему.

Согласно FHS, правильно хранить файлы веб-сайтов в директории /srv. Приемлимыми вариантами считаю /var/www (/var/www/html), как это делают сопровождающие пакетов в Debian или RedHat-подобных дистрибутивах. Логи привычно располагают в /var/log, а конфигурации nginx и apache — в /etc/nginx и /etc/apache2 (/etc/httpd) соответственно.

Не безопасно. Заключительный аргумент против — безопасность. Панели управления переусложнены, а простые вещи безопаснее сложных. Кроме того, панели управления редко обновляют. Причина в том, что администраторы боятся перезаписать изменения и сломать, что уже настроено и работает. Пользователям же как правило всё равно. В итоге такие сервера годами работают с уязвимым ПО.

Выводы

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

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

За безопасностью таких серверов никто не следит. По опыту, каждый второй сервер с панелью управления с уязвимостями в OpenSSL.

Панель управления не заменяет админа: случись беда — разбираться ему. Сервер — это как самолёт: салон должен быть удобен для пассажиров, а кабина пилота — для командира судна. Доверьте работу профессионалу.

Страницы: 1 2