Компы и автомобили

CIS Benchmark

15 декабря 2017, 22:13

Всю ночь читал регламент по безопасной настройке CentOS 7 за авторством организации «Center for internet security». «Много думал». Называется дока «CIS CentOS Linux 7 Benchmark». *Hint*: На сайте конторы в разделе «CIS benchmarks» есть целый набор таких рекомендаций, под разные платформы разных версий, включая серверные и клиентские Windows, и различные дистрибутивы Linux.

У нас до сих пор в конторе нет внутренних регламентов, которые бы коротко, чётко и по делу предписывали, как надо конфигурировать сервера в базе. Понятно, что под каждый сервис настройки могут отличаться, но некий первоначальный вариант нужно всё-таки прописать. Поэтому, как говорится, «за неимением горничной» нужно пользоваться CIS-овским.

В целом, он неплох. За платную подписку они (CIS) также дают доступ к утилитам аудита настроек и remediation-скриптам, с помощью которых можно по-быстрому привести настройки к рекомендуемым.

Также у них есть регламенты общего характера, по части контроля за инфраструктурой вообще. Добрые люди даже перевели парочку и выложили на Хабре, настоятельно рекомендую ознакомиться:
https://habrahabr.ru/company/pentestit/blog/338532/
https://habrahabr.ru/company/pentestit/blog/339206/

IPsec между Linux и Windows 2012 R2

31 августа 2017, 11:58

Вводная часть

Есть такая клёвая штука, называется Seafile. Это «облачное хранилище», с возможностью развёртывания на своих серверах. Реально функциональная и удобная вещь. Позволяет шарить папки/файлы, как на определённый, так и неопределённый круг пользователей. Позволяет расшарить папку для аплоада, причём содержимое пользователям видно не будет, можно только заливать файлы. Есть клиенты Seafile под винду, линукс и макось. И самое клёвое — серверная часть работает под FreeBSD :-) Домашнее облачко перевёл с Owncloud/Netxtcloud на Seafile.

Развернули в конторе для внутренних нужд Seafile, правда, не на фре, а на CentOS. Я начал настраивать авторизацию пользователей Seafile в AD через LDAP, и напоролся на граблю. По LDAP авторизация идёт, а по LDAPS — нет. А это неправильно, когда пароли по сетке ходят в открытом виде — всё-таки у нас тут инфобез, и всё такое, и по локальной сетке наши пентестеры шарятся тоже только в путь. Начал копать, как же дальше жить, в свете открывшихся фактов. Оказалось, я в этой проблеме не одинок, и существует она (применительно к версии Seafile 6 и CentOS 7) ажно с марта месяца. Нашёл на профильном форуме обсуждение. Если резюмировать, то суть там сводится к тому, что «да, LDAPS сломан, потому что несовместимы либы, которые есть в системе, и которыми пользуется Seafile. Можете попробовать закостылить путём переноса части сиафайловских библиотек в отдельный каталожек и перезапуска сервиса, чтобы Seafile стал использовать системные библиотеки». Но в моём случае это не прокатило. И у народа с форума тоже. По итогам вялого обсуждения разработчики обещали пересобрать Seafile под центось. 23-го июня, на вопрос: «чуваки, ну когда же?!» от разработчиков был получен ответ: «Сорян, пацаны, времени ваще нет!».

В общем, стало понятно, что милостей от природы ждать не стоит, и нужно думать, что делать. Основная задача — зашифровать трафик между сервером Seafile и контроллерами домена. Т. е. нужен туннель. Мы же серьёзная контора, поэтому и туннель у нас должен быть серьёзный. Не какой-нибудь жалкий PPTP/L2TP, или OpenVPN, прости-господи, а самый что ни на есть IPsec. Для разнообразия — не в туннельном, а в транспортном режиме. Так как скрывать IP-адреса нам не нужно, и достаточно только шифрования data payload, то транспортный.

Процесс настройки.

Итак, в красном углу ринга у нас CentOS 7. В синем — её соперник, Windows 2012 R2. Если с настройкой IPsec в винде вопросов особо не возникло — там это делается с помощью Windows Firewall, то с линуксом пришлось пое... эээ... поекспериментировать :-) И после каких-то жалких 4-х дней непрерывной возни, с помощью гугла, профильных сообществ и такой-то матери удалось поднять IPsec транспорт между CentOS (IP 192.168.100.15) и Windows 2012 R2 (192.168.170.10).

Изначально проблема была в том, что не устанавливалась фаза 1. Несмотря на совпадающие параметры, соединение не поднималось и SA не создавались. В дампе это выглядело так:

12:09:36.117723 IP (tos 0x0, ttl 64, id 29646, offset 0, flags [DF], proto UDP (17), length 204)
    192.168.100.15.isakmp > 192.168.170.10.isakmp: [udp sum ok] isakmp 1.0 msgid 00000000
     cookie e6d44de9c8641db7->0000000000000000: phase 1 I ident:
    (sa: doi=ipsec situation=identity
        (p: #0 protoid=isakmp transform=1
            (t: #1 id=ike (type=enc value=3des)(type=hash value=sha1)(type=group desc value=modp2048)(type=auth
             value=preshared)(type=lifetype value=sec)(type=lifeduration value=2a30))))
    (vid: len=8)
    (vid: len=16)
    (vid: len=20)
    (vid: len=16)
    (vid: len=16)
12:09:36.118423 IP (tos 0x0, ttl 128, id 19867, offset 0, flags [none], proto UDP (17), length 236)
    192.168.170.10.isakmp > 192.168.100.15.isakmp: [udp sum ok] isakmp 1.0 msgid 00000000
    cookie e6d44de9c8641db7->b2c0c64ce5211438: phase 1 R ident:
    (sa: doi=ipsec situation=identity
        (p: #0 protoid=isakmp transform=1
            (t: #1 id=ike (type=enc value=3des)(type=hash value=sha1)(type=group desc value=modp2048)(type=auth
            value=preshared)(type=lifetype value=sec)(type=lifeduration len=4 value=00002a30))))
    (vid: len=20)
    (vid: len=16)
    (vid: len=16)
    (vid: len=16)
    (vid: len=16)
    (vid: len=16)

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

Aug 10 17:31:29 03[MGR] checkin IKE_SA dc01[3]
Aug 10 17:31:29 03[MGR] checkin of IKE_SA successful
Aug 10 17:31:29 16[NET] sending packet: from 192.168.100.15[500] to 192.168.170.10[500]
Aug 10 17:31:33 09[MGR] checkout IKEv1 SA with SPIs bf2f04e4730454f7_i 0000000000000000_r
Aug 10 17:31:33 09[MGR] IKE_SA dc01[3] successfully checked out
Aug 10 17:31:33 09[IKE] sending retransmit 1 of request message ID 0, seq 1
Aug 10 17:31:33 09[NET] sending packet: from 192.168.100.15[500] to 192.168.170.10[500] (176 bytes)
Aug 10 17:31:33 09[MGR] checkin IKE_SA dc01[3]
Aug 10 17:31:33 09[MGR] checkin of IKE_SA successful
Aug 10 17:31:33 16[NET] sending packet: from 192.168.100.15[500] to 192.168.170.10[500]
Aug 10 17:31:40 10[MGR] checkout IKEv1 SA with SPIs bf2f04e4730454f7_i 0000000000000000_r
Aug 10 17:31:40 10[MGR] IKE_SA dc01[3] successfully checked out
Aug 10 17:31:40 10[IKE] sending retransmit 2 of request message ID 0, seq 1
Aug 10 17:31:40 10[NET] sending packet: from 192.168.100.15[500] to 192.168.170.10[500] (176 bytes)
Aug 10 17:31:40 10[MGR] checkin IKE_SA dc01[3]
Aug 10 17:31:40 10[MGR] checkin of IKE_SA successful
Aug 10 17:31:40 16[NET] sending packet: from 192.168.100.15[500] to 192.168.170.10[500]
Aug 10 17:31:53 03[MGR] checkout IKEv1 SA with SPIs bf2f04e4730454f7_i 0000000000000000_r
Aug 10 17:31:53 03[MGR] IKE_SA dc01[3] successfully checked out
Aug 10 17:31:53 03[IKE] sending retransmit 3 of request message ID 0, seq 1
Aug 10 17:31:53 03[NET] sending packet: from 192.168.100.15[500] to 192.168.170.10[500] (176 bytes)
Aug 10 17:31:53 03[MGR] checkin IKE_SA dc01[3]
Aug 10 17:31:53 03[MGR] checkin of IKE_SA successful
Aug 10 17:31:53 16[NET] sending packet: from 192.168.100.15[500] to 192.168.170.10[500]
Aug 10 17:32:17 07[MGR] checkout IKEv1 SA with SPIs bf2f04e4730454f7_i 0000000000000000_r
Aug 10 17:32:17 07[MGR] IKE_SA dc01[3] successfully checked out
Aug 10 17:32:17 07[IKE] sending retransmit 4 of request message ID 0, seq 1
Aug 10 17:32:17 07[NET] sending packet: from 192.168.100.15[500] to 192.168.170.10[500] (176 bytes)
Aug 10 17:32:17 07[MGR] checkin IKE_SA dc01[3]
Aug 10 17:32:17 07[MGR] checkin of IKE_SA successful
Aug 10 17:32:17 16[NET] sending packet: from 192.168.100.15[500] to 192.168.170.10[500]
Aug 10 17:32:59 07[MGR] checkout IKEv1 SA with SPIs bf2f04e4730454f7_i 0000000000000000_r
Aug 10 17:32:59 07[MGR] IKE_SA dc01[3] successfully checked out
Aug 10 17:32:59 07[IKE] sending retransmit 5 of request message ID 0, seq 1
Aug 10 17:32:59 07[NET] sending packet: from 192.168.100.15[500] to 192.168.170.10[500] (176 bytes)
Aug 10 17:32:59 07[MGR] checkin IKE_SA dc01[3]
Aug 10 17:32:59 07[MGR] checkin of IKE_SA successful

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

В процессе чтения мудрых указаний партии документации с сайта Strongswan сниошло озарение, что теперь, оказывается, есть две схемы конфигурации и запуска IPsec. Первый — это по старинке, через демон strongswan и настройками в файле ipsec.conf. Именно этот способ и был изначально использован. Но суровая практическая реальность показала, что при этом как минимум не идёт логирование событий IPsec, настраиваемое в файле /etc/strongswan/strongswand.d/charon-logging.conf.

И есть более стильный-модный-молодёжный способ — это настройка через конфиг /etc/strongswan/swanctl/swanctl.conf и последующий запуск с помощью сервиса strongswan-swanctl. Это два взаимоисключающих способа настройки и использования Strongswan. В итоге после долгих и бесплодных попыток запустить Strongswan с конфигом из ipsec.conf было принято волевое решение перенести настройки в swanctl.conf.

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

connections {
    dc01 {
        version = 1
        local_addrs = 192.168.100.15
        remote_addrs = 192.168.170.10
        aggressive = no
        unique = no
        proposals = 3des-sha1-modp2048
        reauth_time = 30m
        local {
            auth = psk
            id = 192.168.100.15
        }
        remote {
            auth = psk
            id = 192.168.170.10
        }
        children {
            dc01 {
                mode = transport
                esp_proposals = aes256-sha1
                start_action = trap
            }
        }
    }
}
secrets {
    ike01 {
        id = 192.168.170.10
        secret = 
    }
}

Перезапускаем сервис strongswan-swanctl, иии... Ничего. Туннель не поднимается. Начинаем запускать его руками: swanctl —load-all:

loaded ike secret 'ike01'
no authorities found, 0 unloaded
no pools found, 0 unloaded
loaded connection 'dc01'
successfully loaded 1 connections, 0 unloaded

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

# swanctl —list-conns
dc01: IKEv1, reauthentication every 1800s
  local:  192.168.100.15
  remote: 192.168.170.10
  local pre-shared key authentication:
    id: 192.168.100.15
  remote pre-shared key authentication:
    id: 192.168.170.10
  dc01: TRANSPORT, rekeying every 3600s
    local:  dynamic
    remote: dynamic

Есть контакт. Проверяем, создались ли SA-шки (в фоне постоянно висит пинг на соседа по туннелю):

[root@msk01-seafile01 ~]# swanctl —list-conns
[root@msk01-seafile01 ~]#

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

# swanctl -i —child dc01
[IKE] initiating Main Mode IKE_SA dc01[16] to 192.168.170.10
[ENC] generating ID_PROT request 0 [ SA V V V V V ]
[NET] sending packet: from 192.168.100.15[500] to 192.168.170.10[500] (176 bytes)
[NET] received packet: from 192.168.170.10[500] to 192.168.100.15[500] (208 bytes)
[ENC] parsed ID_PROT response 0 [ SA V V V V V V ]
[IKE] received MS NT5 ISAKMPOAKLEY vendor ID
[IKE] received NAT-T (RFC 3947) vendor ID
[IKE] received draft-ietf-ipsec-nat-t-ike-02\n vendor ID
[IKE] received FRAGMENTATION vendor ID
[ENC] received unknown vendor ID: fb:1d:e3:cd:f3:41:b7:ea:16:b7:e5:be:08:55:f1:20
[ENC] received unknown vendor ID: e3:a5:96:6a:76:37:9f:e7:07:22:82:31:e5:ce:86:52
[ENC] generating ID_PROT request 0 [ KE No NAT-D NAT-D ]
[NET] sending packet: from 192.168.100.15[500] to 192.168.170.10[500] (372 bytes)
[NET] received packet: from 192.168.170.10[500] to 192.168.100.15[500] (388 bytes)
[ENC] parsed ID_PROT response 0 [ KE No NAT-D NAT-D ]
[ENC] generating ID_PROT request 0 [ ID HASH ]
[NET] sending packet: from 192.168.100.15[500] to 192.168.170.10[500] (68 bytes)
[NET] received packet: from 192.168.170.10[500] to 192.168.100.15[500] (68 bytes)
[ENC] parsed ID_PROT response 0 [ ID HASH ]
[IKE] IKE_SA dc01[16] established between 192.168.100.15[192.168.100.15]...192.168.170.10[192.168.170.10]
[IKE] scheduling reauthentication in 1753s
[IKE] maximum IKE_SA lifetime 1933s
[ENC] generating QUICK_MODE request 4130174163 [ HASH SA No ID ID ]
[NET] sending packet: from 192.168.100.15[500] to 192.168.170.10[500] (172 bytes)
[NET] received packet: from 192.168.170.10[500] to 192.168.100.15[500] (220 bytes)
[ENC] parsed QUICK_MODE response 4130174163 [ HASH SA No ID ID N(24576) ]
[IKE] CHILD_SA dc01{28} established with SPIs c856809b_i b619b0e4_o and TS 192.168.100.15/32 === 192.168.170.10/32
[ENC] generating QUICK_MODE request 4130174163 [ HASH ]
initiate completed successfully

Проверяем, установились ли SA-шки:

# swanctl —list-sas
dc01: #1, ESTABLISHED, IKEv1, c95a166b86ec3d0b_i* 6df2b856fdeff612_r
  local  '192.168.100.15' @ 192.168.100.15[500]
  remote '192.168.170.10' @ 192.168.170.10[500]
  3DES_CBC/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_2048
  established 1189s ago, reauth in 604s
[root@msk01-seafile01 ~]# swanctl -i —child dc01
[ENC] generating QUICK_MODE request 1540311929 [ HASH SA No ID ID ]
[NET] sending packet: from 192.168.100.15[500] to 192.168.170.10[500] (172 bytes)
[NET] received packet: from 192.168.170.10[500] to 192.168.100.15[500] (220 bytes)
[ENC] parsed QUICK_MODE response 1540311929 [ HASH SA No ID ID N(24576) ]
[IKE] CHILD_SA dc01{3} established with SPIs c587449e_i a113d280_o and TS 192.168.100.15/32 == 192.168.170.10/32
[ENC] generating QUICK_MODE request 1540311929 [ HASH ]
[NET] sending packet: from 192.168.100.15[500] to 192.168.170.10[500] (60 bytes)
initiate completed successfully

Работает. Т. е. руками туннель запускается, а при перезапуске сервиса он не грузится, и не активируется. Начинаем копать, в чём тут дело. Перезапуск сервиса с последующим анализом /var/log/messages приносит следующие новости:

Aug 11 16:26:06 msk01-seafile01 systemd: Starting strongSwan IPsec IKEv1/IKEv2 daemon using swanctl...
Aug 11 16:26:06 msk01-seafile01 charon-systemd: loading ca certificates from '/etc/strongswan/ipsec.d/cacerts'
Aug 11 16:26:06 msk01-seafile01 charon-systemd: loading aa certificates from '/etc/strongswan/ipsec.d/aacerts'
Aug 11 16:26:06 msk01-seafile01 charon-systemd: loading ocsp signer certificates from '/etc/strongswan/ipsec.d/ocspcerts'
Aug 11 16:26:06 msk01-seafile01 charon-systemd: loading attribute certificates from '/etc/strongswan/ipsec.d/acerts'
Aug 11 16:26:06 msk01-seafile01 charon-systemd: loading crls from '/etc/strongswan/ipsec.d/crls'
Aug 11 16:26:06 msk01-seafile01 charon-systemd: loading secrets from '/etc/strongswan/ipsec.secrets'
Aug 11 16:26:06 msk01-seafile01 charon-systemd: loaded plugins: charon-systemd charon-systemd aes des rc2 sha2
                               sha1 md4 md5 random nonce x509 revocation constraints acert pubkey pk
                               cs1 pkcs8 pkcs12 pgp dnskey sshkey pem openssl gcrypt fips-prf gmp
                               curve25519 xcbc cmac hmac ctr ccm gcm curl attr kernel-netlink
                               resolve socket-default farp stroke vici updown eap
                               -identity eap-md5 eap-gtc eap-mschapv2 eap-tls eap-ttls eap-peap
                               xauth-generic xauth-eap xauth-pam xauth-noauth dhcp unity
Aug 11 16:26:06 msk01-seafile01 charon-systemd: spawning 16 worker threads
Aug 11 16:26:06 msk01-seafile01 swanctl: no files found matching '/etc/strongswan/strongswan.conf'
Aug 11 16:26:06 msk01-seafile01 swanctl: no files found matching '/etc/strongswan/swanctl/swanctl.conf'
Aug 11 16:26:06 msk01-seafile01 swanctl: failed to open config file '/etc/strongswan/swanctl/swanctl.conf'
Aug 11 16:26:06 msk01-seafile01 swanctl: opening directory '/etc/strongswan/swanctl/x509' failed: Permission denied
Aug 11 16:26:06 msk01-seafile01 swanctl: opening directory '/etc/strongswan/swanctl/x509ca' failed: Permission denied
Aug 11 16:26:06 msk01-seafile01 swanctl: opening directory '/etc/strongswan/swanctl/x509ocsp' failed: Permission denied
Aug 11 16:26:06 msk01-seafile01 swanctl: opening directory '/etc/strongswan/swanctl/x509aa' failed: Permission denied
Aug 11 16:26:06 msk01-seafile01 swanctl: opening directory '/etc/strongswan/swanctl/x509ac' failed: Permission denied
Aug 11 16:26:06 msk01-seafile01 swanctl: opening directory '/etc/strongswan/swanctl/x509crl' failed: Permission denied
Aug 11 16:26:06 msk01-seafile01 swanctl: opening directory '/etc/strongswan/swanctl/pubkey' failed: Permission denied
Aug 11 16:26:06 msk01-seafile01 swanctl: opening directory '/etc/strongswan/swanctl/private' failed: Permission denied
Aug 11 16:26:06 msk01-seafile01 swanctl: opening directory '/etc/strongswan/swanctl/rsa' failed: Permission denied
Aug 11 16:26:06 msk01-seafile01 swanctl: opening directory '/etc/strongswan/swanctl/ecdsa' failed: Permission denied
Aug 11 16:26:06 msk01-seafile01 swanctl: opening directory '/etc/strongswan/swanctl/bliss' failed: Permission denied
Aug 11 16:26:06 msk01-seafile01 swanctl: opening directory '/etc/strongswan/swanctl/pkcs8' failed: Permission denied
Aug 11 16:26:06 msk01-seafile01 swanctl: opening directory '/etc/strongswan/swanctl/pkcs12' failed: Permission denied
Aug 11 16:26:06 msk01-seafile01 swanctl: no authorities found, 0 unloaded
Aug 11 16:26:06 msk01-seafile01 swanctl: no pools found, 0 unloaded
Aug 11 16:26:06 msk01-seafile01 swanctl: no connections found, 0 unloaded
Aug 11 16:26:06 msk01-seafile01 systemd: Started strongSwan IPsec IKEv1/IKEv2 daemon using swanctl.

То есть, swanctl, запускаемый с правами рута из-под systemd не получает доступа к каталогам и конфигу swanctl.conf. Лихо, что сказать... Вот за такие вещи я и «люблю» Linux... :-)

Права на каталоги и файлы выставлены следующим образом:

drwxr-xr-x. 15 root root   204 Aug 11 15:31 .
drwx———.  6 root root   132 Aug 11 15:46 ..
drwxr-xr-x.  2 root root     6 Jun 19 22:15 bliss
drwxr-x—-.  2 root root     6 Jun 19 22:15 ecdsa
drwxr-x—-.  2 root root     6 Jun 19 22:15 pkcs12
drwxr-x—-.  2 root root     6 Jun 19 22:15 pkcs8
drwxr-x—-.  2 root root     6 Jun 19 22:15 private
drwxr-xr-x.  2 root root     6 Jun 19 22:15 pubkey
drwxr-x—-.  2 root root     6 Jun 19 22:15 rsa
-rw-r——-.  1 root root 14539 Aug 11 15:31 swanctl.conf
drwxr-xr-x.  2 root root     6 Jun 19 22:15 x509
drwxr-xr-x.  2 root root     6 Jun 19 22:15 x509aa
drwxr-xr-x.  2 root root     6 Jun 19 22:15 x509ac
drwxr-xr-x.  2 root root     6 Jun 19 22:15 x509ca
drwxr-xr-x.  2 root root     6 Jun 19 22:15 x509crl
drwxr-xr-x.  2 root root     6 Jun 19 22:15 x509ocsp

Добавил права на чтение swanctl.conf для others. Не помогло — всё равно жалуется, что нет доступа. Полез смотреть логи SELinux. В них тоже чисто. Опять было достал бубен, но потом вспомнил, что есть категории событий, которые блокируются по-тихому, и в /var/log/audit/audit.log не попадают. Включаем логирование всех событий:

# semodule -DB

После перезапуска сервиса смотрим в логи. Ну точно! Опять проклятый SELinux:

type=AVC msg=audit(1502458060.323:15123): avc: denied { search } for  pid=99426 comm="swanctl" name="strongswan" dev="dm-0" ino=33604377 scontext=system_u:system_r:ipsec_mgmt_t:s 0 tcontext=system_u:object_r:ipsec_conf_file_t:s0 tclass=dir

И начинается кропотливый процесс докидывания разрешений в политиках SELinux. Итоговый файл политик получился такой:


module swanctl2conf 1.0;

require {
        type ipsec_conf_file_t;
        type ipsec_mgmt_t;
        class dir { open read search };
}

#============= ipsec_mgmt_t ==============
allow ipsec_mgmt_t ipsec_conf_file_t:dir open;

#!!!! This avc is allowed in the current policy
allow ipsec_mgmt_t ipsec_conf_file_t:dir { read search };

Применяем всю эту красоту:

# checkmodule -m -M -o swanctl2conf.mod swanctl2conf.te 
# semodule_package -o swanctl2conf.pp -m swanctl2conf.mod
# semodule -i swanctl2conf.pp

После этого перезапускаем сервис и смотрим в логи:

Aug 11 17:16:45 msk01-seafile01 systemd: Stopping strongSwan IPsec IKEv1/IKEv2 daemon using swanctl...
Aug 11 17:16:45 msk01-seafile01 charon-systemd: SIGTERM received, shutting down
Aug 11 17:16:45 msk01-seafile01 systemd: Starting strongSwan IPsec IKEv1/IKEv2 daemon using swanctl...
Aug 11 17:16:45 msk01-seafile01 charon-systemd: loading ca certificates from '/etc/strongswan/ipsec.d/cacerts'
Aug 11 17:16:45 msk01-seafile01 charon-systemd: loading aa certificates from '/etc/strongswan/ipsec.d/aacerts'
Aug 11 17:16:45 msk01-seafile01 charon-systemd: loading ocsp signer certificates from '/etc/strongswan/ipsec.d/ocspcerts'
Aug 11 17:16:45 msk01-seafile01 charon-systemd: loading attribute certificates from '/etc/strongswan/ipsec.d/acerts'
Aug 11 17:16:45 msk01-seafile01 charon-systemd: loading crls from '/etc/strongswan/ipsec.d/crls'
Aug 11 17:16:45 msk01-seafile01 charon-systemd: loading secrets from '/etc/strongswan/ipsec.secrets'
Aug 11 17:16:45 msk01-seafile01 charon-systemd: loaded plugins: charon-systemd charon-systemd aes des rc2 sha2
                               sha1 md4 md5 random nonce x509 revocation constraints acert pubkey pk
                               cs1 pkcs8 pkcs12 pgp dnskey sshkey pem openssl gcrypt fips-prf gmp
                               curve25519 xcbc cmac hmac ctr ccm gcm curl attr kernel-netlink
                               resolve socket-default farp stroke vici updown eap
                               -identity eap-md5 eap-gtc eap-mschapv2 eap-tls eap-ttls eap-peap
                               xauth-generic xauth-eap xauth-pam xauth-noauth dhcp unity
Aug 11 17:16:45 msk01-seafile01 charon-systemd: spawning 16 worker threads
Aug 11 17:16:45 msk01-seafile01 charon-systemd: loaded IKE shared key with id 'ike01' for: '192.168.170.10'
Aug 11 17:16:45 msk01-seafile01 swanctl: no authorities found, 0 unloaded
Aug 11 17:16:45 msk01-seafile01 swanctl: no pools found, 0 unloaded
Aug 11 17:16:45 msk01-seafile01 charon-systemd: added vici connection: dc01
Aug 11 17:16:45 msk01-seafile01 charon-systemd: installing 'dc01'
Aug 11 17:16:45 msk01-seafile01 swanctl: loaded ike secret 'ike01'
Aug 11 17:16:45 msk01-seafile01 swanctl: loaded connection 'dc01'
Aug 11 17:16:45 msk01-seafile01 swanctl: successfully loaded 1 connections, 0 unloaded
Aug 11 17:16:45 msk01-seafile01 systemd: Started strongSwan IPsec IKEv1/IKEv2 daemon using swanctl.

Иными словами — кррррасота! Всё работает.

Zabbix agent + SElinux = hardcore...

28 июля 2017, 12:14

Вынужден тут приобщаться к странному и алогичному миру линуксовых ОС. И поимел сегодня исключительно волнующий опыт настройки мониторинга контрольной суммы файла /etc/shadow в CentOS посредством Zabbix. Казалось бы, всё просто — файл доступен по чтению только руту, пропиши в sudoers что-то вроде:

zabbix ALL=(ALL) NOPASSWD: /usr/bin/sha256sum /etc/shadow

, а в userparameters:

UserParameter=shadow.checksum,sudo sha256sum /etc/shadow|cut -d « » -f 1

и будет тебе щастье. Но щастья что-то не наступило... Параметр всё равно оставался в статусе «Unsupported», с мотивацией:

sh: /usr/bin/sudo: Permission denied

Разборки показали, что sudo жалуется, что у пользователя zabbix не установлен shell. Точнее, установлена оболочка /sbin/nologin. ОК, патчим sudoers:

Defaults:zabbix !requiretty
zabbix ALL=(ALL) NOPASSWD: /usr/bin/sha256sum /etc/shadow

Лучше стало, но не сильно: «sudo: unable to open audit system: Permission denied». «Эге!», — подумал я, «стопудово дело в SELinux». И пошёл читать grep AVC /var/log/audit/log. И действительно, там было понаписано всякое. Например:

type=AVC msg=audit(1501160287.479:190259): avc: denied { execute } for  pid=21472 comm=«sh» name=«sudo» dev=«dm-0» ino=50709281 scontext=system_u:system_r:zabbix_agent_t:s0 tcontext=system_u:object_r:sudo_exec_t:s0 tclass=file

И вот тут началось бесконечное порево с правкой политик SELinux для того, чтобы в конечном итоге параметр заработал. Заняло это чистого времени часа 4. Текст модуля политики разросся раза в два :-)

На каком-то этапе упёрся, так как параметр всё ещё не работал, но в /var/log/audit/audit.log никаких новых надписей о запрещённых операциях не появилось. Исследование показало, что в SELinux определённые сообщения подавляются. Чтобы это временно отключить, необходимо выполнить команду:

# semodule -DB

Тогда будут выводиться все сообщения, но это временно — до следующего ребилда политики. Каковая происходит, в частности, при установке исправленного модуля в системе.

Вообще, на CentOS 7 довольно сильно пришлось докручивать политики SELinux под zabbix-агента. Началось с того, что агент просто не запускался, так как SELinux не давал ему права на установку лимитов. Пришлось строить кастомную политику.

Потом выяснилось, что у агента нет прав на запись в каталог /tmp, а у нас вызывается скрипт мониторинга nginx, который создаёт кэш-файл в /tmp. С одной стороны, проще было бы класть временный файл куда-нибудь в каталоги самого заббикса. Но с другой — файл-то временный, и самое ему место в /tmp.

В итоге получился вот такой модуль политики:

# cat zabbix-agent.te

module zabbix-agent 1.0;

require {
        type user_tmp_t;
        type tmp_t;
        type zabbix_agent_t;
        type sudo_exec_t;
        type http_cache_port_t;
        type shadow_t;
        type devlog_t;
        type kernel_t;

        class tcp_socket { name_connect };
        class unix_dgram_socket { create connect sendto write };
        class netlink_audit_socket { create read write nlmsg_relay };
        class sock_file { write };
        class capability { sys_resource dac_override dac_read_search audit_write };
        class dir { add_name write };
        class file { create open setattr write execute execute_no_trans read };
        class process { setrlimit };
}

#============= zabbix_agent_t ==============
allow zabbix_agent_t self:process { setrlimit };
allow zabbix_agent_t tmp_t:dir { add_name write };
allow zabbix_agent_t http_cache_port_t:tcp_socket name_connect;
allow zabbix_agent_t sudo_exec_t:file { execute execute_no_trans };
allow zabbix_agent_t self:netlink_audit_socket { create read write nlmsg_relay };
allow zabbix_agent_t self:unix_dgram_socket { create connect write };
allow zabbix_agent_t kernel_t:unix_dgram_socket { sendto };
allow zabbix_agent_t self:capability { sys_resource dac_override dac_read_search audit_write };
allow zabbix_agent_t shadow_t:file { read open };
allow zabbix_agent_t devlog_t:sock_file write;


#!!!! WARNING: 'tmp_t' is a base type.
allow zabbix_agent_t tmp_t:file { create open setattr write };
allow zabbix_agent_t user_tmp_t:file open;

Там прописаны разрешения на доступ к http-порту (требуется для мониторинга nginx), доступ на запись файлов в /tmp, и использование sudo.

Затем нужно скомпилировать модуль и установить его в системе:

# checkmodule -M -m -o zabbix-agent.mod zabbix-agent.te
# semodule_package -o zabbix-agent.pp -m zabbix-agent.mod
# semodule -i zabbix-agent.pp

И только после этого заббикс стал нормально считать sha256 для файла /etc/shadow .

awk oneliners

14 февраля 2017, 16:58

Постоянно забываю синтаксисы sed и awk. Нашёл для awk очень актуальный список однострочников:

HANDY ONE-LINE SCRIPTS FOR AWK                               30 April 2008
Compiled by Eric Pement — eric [at] pement.org               version 0.27

Latest version of this file (in English) is usually at:
   http://www.pement.org/awk/awk1line.txt

This file will also be available in other languages:
   Chinese  — http://ximix.org/translation/awk1line_zh-CN.txt   

USAGE:

   Unix: awk '/pattern/ {print «$1»}'    # standard Unix shells
DOS/Win: awk '/pattern/ {print «$1»}'    # compiled with DJGPP, Cygwin
         awk «/pattern/ {print \»$1\«}»  # GnuWin32, UnxUtils, Mingw

Note that the DJGPP compilation (for DOS or Windows-32) permits an awk
script to follow Unix quoting syntax '/like/ {«this»}'. HOWEVER, if the
command interpreter is CMD.EXE or COMMAND.COM, single quotes will not
protect the redirection arrows (<, >) nor do they protect pipes (|).
These are special symbols which require «double quotes» to protect them
from interpretation as operating system directives. If the command
interpreter is bash, ksh or another Unix shell, then single and double
quotes will follow the standard Unix usage.

Users of MS-DOS or Microsoft Windows must remember that the percent
sign (%) is used to indicate environment variables, so this symbol must
be doubled (%%) to yield a single percent sign visible to awk.

If a script will not need to be quoted in Unix, DOS, or CMD, then I
normally omit the quote marks. If an example is peculiar to GNU awk,
the command 'gawk' will be used. Please notify me if you find errors or
new commands to add to this list (total length under 65 characters). I
usually try to put the shortest script first. To conserve space, I
normally use '1' instead of '{print}' to print each line. Either one
will work.

FILE SPACING:

 # double space a file
 awk '1;{print «»}'
 awk 'BEGIN{ORS=«\n\n»};1'

 # double space a file which already has blank lines in it. Output file
 # should contain no more than one blank line between lines of text.
 # NOTE: On Unix systems, DOS lines which have only CRLF (\r\n) are
 # often treated as non-blank, and thus 'NF' alone will return TRUE.
 awk 'NF{print $0 «\n»}'

 # triple space a file
 awk '1;{print «\n»}'

NUMBERING AND CALCULATIONS:

 # precede each line by its line number FOR THAT FILE (left alignment).
 # Using a tab (\t) instead of space will preserve margins.
 awk '{print FNR «\t» $0}' files*

 # precede each line by its line number FOR ALL FILES TOGETHER, with tab.
 awk '{print NR «\t» $0}' files*

 # number each line of a file (number on left, right-aligned)
 # Double the percent signs if typing from the DOS command prompt.
 awk '{printf(«%5d : %s\n», NR,$0)}'

 # number each line of file, but only print numbers if line is not blank
 # Remember caveats about Unix treatment of \r (mentioned above)
 awk 'NF{$0=++a « :» $0};1'
 awk '{print (NF? ++a « :» :«») $0}'

 # count lines (emulates «wc -l»)
 awk 'END{print NR}'

 # print the sums of the fields of every line
 awk '{s=0; for (i=1; i<=NF; i++) s=s+$i; print s}'

 # add all fields in all lines and print the sum
 awk '{for (i=1; i<=NF; i++) s=s+$i}; END{print s}'

 # print every line after replacing each field with its absolute value
 awk '{for (i=1; i<=NF; i++) if ($i < 0) $i = -$i; print }'
 awk '{for (i=1; i<=NF; i++) $i = ($i < 0) ? -$i : $i; print }'

 # print the total number of fields ("words") in all lines
 awk '{ total = total + NF }; END {print total}' file

 # print the total number of lines that contain "Beth"
 awk '/Beth/{n++}; END {print n+0}' file

 # print the largest first field and the line that contains it
 # Intended for finding the longest string in field #1
 awk '$1 > max {max=$1; maxline=$0}; END{ print max, maxline}'

 # print the number of fields in each line, followed by the line
 awk '{ print NF «:» $0 } '

 # print the last field of each line
 awk '{ print $NF }'

 # print the last field of the last line
 awk '{ field = $NF }; END{ print field }'

 # print every line with more than 4 fields
 awk 'NF > 4'

 # print every line where the value of the last field is > 4
 awk '$NF > 4'

STRING CREATION:

 # create a string of a specific length (e.g., generate 513 spaces)
 awk 'BEGIN{while (a++<513) s=s " "; print s}'

 # insert a string of specific length at a certain character position
 # Example: insert 49 spaces after column #6 of each input line.
 gawk --re-interval 'BEGIN{while(a++<49)s=s " "};{sub(/^.{6}/,"&" s)};1'

ARRAY CREATION:

 # These next 2 entries are not one-line scripts, but the technique
 # is so handy that it merits inclusion here.
 
 # create an array named "month", indexed by numbers, so that month[1]
 # is 'Jan', month[2] is 'Feb', month[3] is 'Mar' and so on.
 split("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec", month, " ")

 # create an array named "mdigit", indexed by strings, so that
 # mdigit["Jan"] is 1, mdigit["Feb"] is 2, etc. Requires "month" array
 for (i=1; i<=12; i++) mdigit[month[i]] = i

TEXT CONVERSION AND SUBSTITUTION:

 # IN UNIX ENVIRONMENT: convert DOS newlines (CR/LF) to Unix format
 awk '{sub(/\r$/,"")};1'   # assumes EACH line ends with Ctrl-M

 # IN UNIX ENVIRONMENT: convert Unix newlines (LF) to DOS format
 awk '{sub(/$/,"\r")};1'

 # IN DOS ENVIRONMENT: convert Unix newlines (LF) to DOS format
 awk 1

 # IN DOS ENVIRONMENT: convert DOS newlines (CR/LF) to Unix format
 # Cannot be done with DOS versions of awk, other than gawk:
 gawk -v BINMODE="w" '1' infile >outfile

 # Use «tr» instead.
 tr -d \r outfile            # GNU tr version 1.22 or higher

 # delete leading whitespace (spaces, tabs) from front of each line
 # aligns all text flush left
 awk '{sub(/^[ \t]+/, «»)};1'

 # delete trailing whitespace (spaces, tabs) from end of each line
 awk '{sub(/[ \t]+$/, «»)};1'

 # delete BOTH leading and trailing whitespace from each line
 awk '{gsub(/^[ \t]+|[ \t]+$/,«»)};1'
 awk '{$1=$1};1'           # also removes extra space between fields

 # insert 5 blank spaces at beginning of each line (make page offset)
 awk '{sub(/^/, «     „)};1'

 # align all text flush right on a 79-column width
 awk '{printf „%79s\n“, $0}' file*

 # center all text on a 79-character width
 awk '{l=length();s=int((79-l)/2); printf „%“(s+l)“s\n»,$0}' file*

 # substitute (find and replace) «foo» with «bar» on each line
 awk '{sub(/foo/,«bar»)}; 1'           # replace only 1st instance
 gawk '{$0=gensub(/foo/,«bar»,4)}; 1'  # replace only 4th instance
 awk '{gsub(/foo/,«bar»)}; 1'          # replace ALL instances in a line

 # substitute «foo» with «bar» ONLY for lines which contain «baz»
 awk '/baz/{gsub(/foo/, «bar»)}; 1'

 # substitute «foo» with «bar» EXCEPT for lines which contain «baz»
 awk '!/baz/{gsub(/foo/, «bar»)}; 1'

 # change «scarlet» or «ruby» or «puce» to «red»
 awk '{gsub(/scarlet|ruby|puce/, «red»)}; 1'

 # reverse order of lines (emulates «tac»)
 awk '{a[i++]=$0} END {for (j=i-1; j>=0;) print a[j—] }' file*

 # if a line ends with a backslash, append the next line to it (fails if
 # there are multiple lines ending with backslash...)
 awk '/\\$/ {sub(/\\$/,«»); getline t; print $0 t; next}; 1' file*

 # print and sort the login names of all users
 awk -F «:» '{print $1 | «sort» }' /etc/passwd

 # print the first 2 fields, in opposite order, of every line
 awk '{print $2, $1}' file

 # switch the first 2 fields of every line
 awk '{temp = $1; $1 = $2; $2 = temp}' file

 # print every line, deleting the second field of that line
 awk '{ $2 = «»; print }'

 # print in reverse order the fields of every line
 awk '{for (i=NF; i>0; i—) printf(«%s „,$i);print „“}' file

 # concatenate every 5 lines of input, using a comma separator
 # between fields
 awk 'ORS=NR%5?“,»:«\n»' file

SELECTIVE PRINTING OF CERTAIN LINES:

 # print first 10 lines of file (emulates behavior of «head»)
 awk 'NR < 11'

 # print first line of file (emulates "head -1")
 awk 'NR>1{exit};1'

  # print the last 2 lines of a file (emulates «tail -2»)
 awk '{y=x «\n» $0; x=$0};END{print y}'

 # print the last line of a file (emulates «tail -1»)
 awk 'END{print}'

 # print only lines which match regular expression (emulates «grep»)
 awk '/regex/'

 # print only lines which do NOT match regex (emulates «grep -v»)
 awk '!/regex/'

 # print any line where field #5 is equal to «abc123»
 awk '$5 == «abc123»'

 # print only those lines where field #5 is NOT equal to «abc123»
 # This will also print lines which have less than 5 fields.
 awk '$5 != «abc123»'
 awk '!($5 == «abc123»)'

 # matching a field against a regular expression
 awk '$7  ~ /^[a-f]/'    # print line if field #7 matches regex
 awk '$7 !~ /^[a-f]/'    # print line if field #7 does NOT match regex

 # print the line immediately before a regex, but not the line
 # containing the regex
 awk '/regex/{print x};{x=$0}'
 awk '/regex/{print (NR==1 ? «match on line 1» : x)};{x=$0}'

 # print the line immediately after a regex, but not the line
 # containing the regex
 awk '/regex/{getline;print}'

 # grep for AAA and BBB and CCC (in any order on the same line)
 awk '/AAA/ && /BBB/ && /CCC/'

 # grep for AAA and BBB and CCC (in that order)
 awk '/AAA.*BBB.*CCC/'

 # print only lines of 65 characters or longer
 awk 'length > 64'

 # print only lines of less than 65 characters
 awk 'length < 64'

 # print section of file from regular expression to end of file
 awk '/regex/,0'
 awk '/regex/,EOF'

 # print section of file based on line numbers (lines 8-12, inclusive)
 awk 'NR==8,NR==12'

 # print line number 52
 awk 'NR==52'
 awk 'NR==52 {print;exit}'          # more efficient on large files

 # print section of file between two regular expressions (inclusive)
 awk '/Iowa/,/Montana/'             # case sensitive

SELECTIVE DELETION OF CERTAIN LINES:

 # delete ALL blank lines from a file (same as "grep '.' ")
 awk NF
 awk '/./'

 # remove duplicate, consecutive lines (emulates "uniq")
 awk 'a !~ $0; {a=$0}'

 # remove duplicate, nonconsecutive lines
 awk '!a[$0]++'                     # most concise script
 awk '!($0 in a){a[$0];print}'      # most efficient script

CREDITS AND THANKS:

Special thanks to the late Peter S. Tillier (U.K.) for helping me with
the first release of this FAQ file, and to Daniel Jana, Yisu Dong, and
others for their suggestions and corrections.

For additional syntax instructions, including the way to apply editing
commands from a disk file instead of the command line, consult:

  "sed & awk, 2nd Edition," by Dale Dougherty and Arnold Robbins
  (O'Reilly, 1997)

  "UNIX Text Processing," by Dale Dougherty and Tim O'Reilly (Hayden
  Books, 1987)

  "GAWK: Effective awk Programming," 3d edition, by Arnold D. Robbins
  (O'Reilly, 2003) or at http://www.gnu.org/software/gawk/manual/

To fully exploit the power of awk, one must understand "regular
expressions." For detailed discussion of regular expressions, see
"Mastering Regular Expressions, 3d edition" by Jeffrey Friedl (O'Reilly,
2006).

The info and manual ("man") pages on Unix systems may be helpful (try
"man awk", "man nawk", "man gawk", "man regexp", or the section on
regular expressions in "man ed").

USE OF '\t' IN awk SCRIPTS: For clarity in documentation, I have used
'\t' to indicate a tab character (0x09) in the scripts.  All versions of
awk should recognize this abbreviation.

#---end of file---

Пофиксили баг в pf на FreeBSD

24 мая 2016, 12:28

На выходных пришло оповещение, что баг, про который я писал в прошлом году, починили: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=201519

Пока только для releng/10.3

ScaleIO и отвал обоих MDM

13 апреля 2016, 17:08

Проблема

Я в конторе развернул ScaleIO на тестовом кластере. 4 гипервизора esxi, по 4 жёстких диска в каждом. И сегодня случился какой-то внезапный отвал двух нод последовательно. Машина с Primary MDM по сети отвечает, но находится в несознанке — по SSH зайти нельзя, локальная консоль тоже мёртвая. А secondary MDM выпал вместе с гипервизором. При этом непосредственно в момента вылета я внимательно втыкал в GUI ScaleIO, и, как говорится, ничто не предвещало.

Ребутнул сбойнувший гипервизор, виртуалка с secondary MDM поднялась и на вид даже как живая. Зашёл в консоль, говорю там:

scli −-login −-user admin
Enter password: Error: Failed to connect to MDM 127.0.0.1:6611

Беглый осмотр слушаемых на машины портов показал, что реально, на порту 6611 никого нет:

# ss -l
Recv-Q Send-Q           Local Address:Port               Peer Address:Port
0      128                          *:9099                          *:*
0      128                          *:6611                          *:*
0      128                          *:9011                          *:*
0      128                          *:25620                         *:*
0      128                         :::ssh                          :::*
0      128                          *:ssh                           *:*
0      100                  127.0.0.1:smtp                          *:*
0      128                          *:25660                         *:*
0      128                          *:7072                          *:*
0      128                          *:25640                         *:*

А это нам намекает, что MDM на не стартовал. Не делает он этого в двух случаях — либо когда primary MDM в кластере поднят и работает, либо если что-то сломалось. Ситуация довольно идиотская, и напоминает пресловутую шутку про /pkunzip.zip/. Чтобы починить MDM, нужно сначала залогиниться в MDM, так как все команды scli выполняются через активный MDM. А чтобы залогиниться, нужен рабочий MDM.

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

MDM rescue mode capability

In MDM cluster mode, if a Primary MDM fails after the Secondary MDM has already failed, concern for lack of repository synchronization will cause the Secondary MDM not to automatically take over the Primary MDM role. This leaves the system non-operational.

The MDM rescue mode feature enables you to force a Secondary MDM to take on the role of Primary MDM, thus enabling the system to continue to function, albeit in a not-fully-synchronized state.

Implementing MDM rescue mode

This section describes how to run the MDM rescue mode feature. Any user can run rescue mode, using a combination of a file written to the MDM, and the rescue_mode command. You must have write privileges on the MDM to complete this task.
Command
rescue_mode Syntax
scli —rescue_mode
To run rescue mode, perform the following steps:

  1. Create a text file called MDM_SERVICE_MODE on the Secondary MDM, in the location corresponding to your operating system:
    • Windows: C:\Program Files\emc\scaleio\MDM\logs
    • Linux: /opt/emc/scaleio/mdm/logs/
  2. In the body of the file, type the text Rescue Mode, and save the file.
  3. In the CLI, type the command scli —rescue_mode.

The Secondary MDM is forced to take over the Primary MDM role. You can verify this
using the query_cluster command.

Я проделал описанное и реально, это помогло — смог подключиться к MDM и проверить, что кластер хоть в каком-то виде жив. После этого удалось подключиться с помощью GUI, и увидеть, что не всё так плохо, как могло бы быть на самом деле:

Похоже, потерь данных не будет, но пока дальше разбираться с причинами проблем и пытаться ребутать машину, где работал primary MDM, лучше не торопиться. Дождёмся сначала полного ребилда томов.

Но радость была преждевременной

Тома ребилдились, и я решил разобраться с бывшим primary MDM. Ребутнул зависший хост, соответственно, виртуалка тоже ребутнулась, и все настроенные пулы выпали в состояние Failed. В общем, долго ли, коротко ли, но мораль сей басни такова — мало войти в rescue mode для MDM, нужно грамотно из него ещё и выйти. В моём случае нужно было бы делать так: оставить работать только secondary MDM в rescue mode, дождаться ребилда томов и эвакуировать все виртуалки оттуда. После этого попытаться переключить ScaleIO из cluster mode в single mode, снести на бывшем первом MDM пакет с MDM и переустановить его заново. Затем добавить его как secondary MDM и попытаться включить обратно cluster mode.

Мой дисковый пул рассыпался, так как после введения в rescue mode того MDM, который был secondary, он в авторитарном режиме стал primary. И пока он был один — всё работало. А как только я ребутнул зависший primary MDM, он отказался становиться secondary после ребута, и в итоге получилась ситуация split brain — два MDM в кластере, и оба primary, и с разной версией БД с метаданными. Ну в итоге они там и наребилдили выпавшие дисковые пулы, перемешав все блоки в кучу. Из-за этого всё рассыпалось.

Сбор статистики для выбора параметров vFRC в VMware

21 марта 2016, 17:01

Как известно, для эффективного использования vFRC (virtual flash read cache) у VMware, нужно правильным образом выбрать два параметра — размер кэша, и размер блока кэша. Если с размером ещё можно попытаться разобраться эмпирически (читай — методом научного тыка), то с размером блока это сделать как-то сложнее. Чтобы правильно выбрать размер блока, нужна статистика по каждой отдельной виртуальной машине, для которой предполагается использовать vFRC, на предмет того, блоки какого размера превалируют в дисковом обмене. И именно такой размер блока нужно выставлять в vFRC. Оказывается, в VMware есть механизм, который такую позволяет собрать статистику по размерам блоков обмена.

Для этого нужно подключиться к хосту, на котором работает искомая ВМ, по SSH. И выполнить следующие команды:

vscsiStats -l

Эта команда выведет нам список виртуалок на хосте, а также виртуальных дисков у каждой виртуалки:

[root@esx-1:~] vscsiStats -l
Virtual Machine worldGroupID: 35676, Virtual Machine Display Name: TEST-IO, Virtual Machine Config File:
/vmfs/volumes/55f97ce4-d51833a0-3617-0007430759d0/TEST-IO/TEST-IO.vmx, {
Virtual SCSI Disk handleID: 8214 (scsi0:0)
Virtual SCSI Disk handleID: 8215 (scsi0:1)
}

У меня на хосте одна машина, поэтому вот.

Из этого чарующего списка нас интересуют два идентификатора: Virtual Machine worldGroupID и Virtual SCSI Disk handleID.

Первый является идентификатором ВМ, второй — идентификатором того виртуального диска этой ВМ, для которой вам нужна статистика. В моём случае worldGroupID равен 35676, а Virtual SCSI Disk handleID равен 8215 — меня интересует именно второй диск. На этой виртуалке у меня сейчас запущен Microsoft JetStress 2013 и тестовые базы лежать именно на втором диске.

Затем, нужно запустить сбор статистики. Делается это командой:

[root@esx-1:~] vscsiStats -s -w 53290 -i 8202
vscsiStats: Starting Vscsi stats collection for worldGroup 53290, handleID 8202 (scsi0:0)
Success.

Ключ /-w/ задаёт worldGroupID, ключ /-i/ задаёт disk handleID. Хост нам отвечает, что сбор статистики начат.

Через какое-то время можно посмотреть, что там мы насобирали:

[root@esx-1:~] vscsiStats -p ioLength -c -w 35676 -i 8215
Histogram: IO lengths of commands,virtual machine worldGroupID,35676,virtual disk handleID,8215 (scsi0:1)
min,4096
max,524288
mean,41803
count,1308632
Frequency,Histogram Bucket Limit
0,512
0,1024
0,2048
0,4095
72510,4096
0,8191
17429,8192
14999,16383
4949,16384
1107407,32768
4878,49152
1839,65535
18921,65536
754,81920
4768,131072
60065,262144
113,524288
0,524288
Histogram: IO lengths of Read commands,virtual machine worldGroupID,35676,virtual disk handleID,8215 (scsi0:1)
min,4096
max,262144
mean,50433
count,724878
Frequency,Histogram Bucket Limit
0,512
0,1024
0,2048
0,4095
910,4096
0,8191
0,8192
2,16383
0,16384
667157,32768
0,49152
0,65535
428,65536
1,81920
269,131072
56111,262144
0,524288
0,524288
Histogram: IO lengths of Write commands,virtual machine worldGroupID,35676,virtual disk handleID,8215 (scsi0:1)
min,4096
max,524288
mean,31087
count,583754
Frequency,Histogram Bucket Limit
0,512
0,1024
0,2048
0,4095
71600,4096
0,8191
17429,8192
14997,16383
4949,16384
440250,32768
4878,49152
1839,65535
18493,65536
753,81920
4499,131072
3954,262144
113,524288
0,524288

Формат таблички очень простой. Три блока диаграмм, первая — всего количество команд ввода-вывода с блоками соответствующего размера, затем отдельно команды только чтения, и только записи. Цифры идут в формате: «<кол-во_команд>, <размер_блока>».

Как вы понимаете из названия технологии «Virtual Flash READ cache», нас интересует только второй раздел, который «IO lengths of Read commands». Если эти данные сохранить в формат CSV, а затем творчески обработать в Excel, то получим вот такую красивую картинку:

Из каковой следует, что размер блока кэша нам для этой нагрузки следует делать именно 32 Kb.

После того, как мы осознали эту простую истину, нужно выключить сбор статистики на гипервизоре:

vscsiStats -x

И идти настраивать vFRC :-)

Вообще же, vscsiStat — довольно интересная утиля. Она позволяет не только выяснить размер блоков, которыми виртуалка обменивается с хранилищем, но и оценить тип нагрузки в терминах «случайная/последовательная». Для этого нужно собрать инфу командой:

vscsiStats -p seekdistance -w

Она покажет, насколько далеко находятся запрашиваемые виртуалкой блоки данных. Чем дальше цифры от 0, тем нагрузка «случайнее». Есть ещё возможность оценить статистику по задержкам выполнения команд, и есть ключ /-p all/, который выводит вообще все типы данных.

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

Вот ссылка: http://www.virten.net/vscsistats/
Скажем автору большое человеческое спасибо за такой сервис :-)

Зависание инсталлятора VMware tools

6 ноября 2015, 12:51

Попытался сейчас на одной виртуалке с Ubuntu заапргейдить VMware tools в «non-interactive mode». Это когда оно, по идее, должно само подключиться к виртуалке, и само всё сделать. Но операция «Initiated VMware tools install or upgrade» зависла на 0%, и больше ничего нельзя было с виртуалкой сделать — ни отмонтировать диск с VMware tools, ни мигрировать виртуалку, ни отменить операцию установки. При попытке выбрать «End Install/upgrade VMware tools» выскакивала ошибка «Call „VirtualMachine.UnmountToolsInstaller“ for object „Usergate-Webfilter“ on vCenter Server „vcenter.vsphere.local“ failed», а в логах появлялось сообщение: «The operation is not allowed in the current state».

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

В итоге на просторах отыскался рецепт:

  1. Включить SSH на том хосте, на котором работает виртуалка;
  2. Зайти в консоль хоста и выполнить там команду vim-cmd vmsvc/getallvms. Эта команда покажет список работающих виртуалок. В левой колонке будет ID соответствующей виртуалки. Ищем в списке виртуалку, на которой завис инсталлятор VMware tools, и определяем её ID;
  3. Выполняем команду vim-cmd vmsvc/tools.cancelinstall , где VM_ID — идентификатор виртуалки, определённый на шаге 2. После этого операция установки VMware tools завершается с ошибкой и освобождает виртуалку;
  4. Не забываем снова отключить службу SSH на хосте.

pf и ipfw одновременно — последовательность обработки пакетов

29 октября 2015, 15:01

Вводная информация. Зачем нужны два файрвола.

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

Например, на pf гораздо проще организовать NAT в несколько внешних IP, причём разбрасывать клиентов по IP он может разными алгоритмами. Мы используем source hash — когда файрвол выбирает IP, в который будет NATиться конкретный пользователь, исходя из IP-адреса этого пользователя. Т. е. в рамках одной PPPoE-сессии конкретный клиент всегда будет ходить с одного и того же IP.

Или, например, у pf есть возможность ограничивать количество соединений в секунду. Так мы блокируем активность ботов по рассылке спама на пользовательских компах — число соединений с одного пользовательского IP на любой 25-ый порт в интернете ограничено не более чем 10 соединений в 30 секунд. Если пользователь превышает этот лимит — его IP заносится в табличку и ему доступ к любому 25-му порту блокируется на 1 час. По истечении часа скрипт очищает эту табличку, и всё начинается заново.

Но есть у pf и проблемы. Например, у него до сих пор нет connection tracker'a для протокола PPTP. Что делает невозможным использование пользователями PPTP-туннелей. Точнее, не так. Пользователь может юзать PPTP-туннель, но это доступно будет только одному пользователю на конкретном BRAS'e. Соответственно — кто раньше туннель установил, того и тапки. У остальных туннели работать уже не будут — файрвол не в состоянии отличить идущие внутри туннеля пакеты и правильно раскидать их по пользователям. Поэтому задачу NAT'a PPTP и GRE трафика мы решаем с помощью IPFW.

Также в процессе эксплуатации в файрволе pf выявилось несколько багов. Про один я уже писал, про второй написать пока только планирую. Коротко — суть бага в том, что при использовании в pf правила route-to для пакетов, пришедших через PPPoE и прошедших NAT криво просчитывается контрольная сумма. Старший и младший байт контрольной суммы меняются местами (т. е. если сумма должна быть 0xABDC, то в результирующем пакете после обработки его PF контрольная сумма проставлялась 0xCDAB), и получатель такого пакета его просто молча дропает. Поэтому этот функционал также пришлось запилить на IPFW.

К чему я это всё?

А вот к чему. Когда используешь два файрвола одновременно, то возникает вопрос последовательности обработки пакетов файрволами, чтобы правила одного файрвола не мешали работать правилам другого файрвола. Поэтому в какой-то момент пришлось разбираться с тем, как всё это работает.

Как это работает

Было бы логично предположить, что последовательность обработки пакетов двумя файрволами будет зависеть от последовательности загрузки соответствующих модулей ядра. Но, как говорит наш школьный физик: «Мысль хорошая. Но неправильная». На самом деле, последовательность обработки будет зависеть от того, в какой последовательности файрволы встраивают свои обработчики (hooks) в цепочки обработчиков фреймворка pfil (подробности можно почитать в man 9 pfil). Эти цепочки представляют собой очереди, отдельно для обработки входящих пакетов, и отдельно — для исходящих.

Причём каждый файрвол вставляет свой обработчик в цепочку в разные моменты своей загрузки. IPFW вставляет сразу при загрузке модуля, а pf при загрузке модуля ничего не делает, и встраивает обработчик только когда мы говорим pfctl -e, т. е. собственно включаем файрвол.

Соответственно, если посмотреть на rcoderd, то файрволы грузятся в следующем порядке:

# rcorder /etc/rc.d/* | grep -nE '/i?pfw?$'
41:/etc/rc.d/pf
57:/etc/rc.d/ipfw

Соответственно, в этом случае обработка будет происходить так:

Как следует из картинки, при стандартной схеме загрузки файрволов для входящих пакетов первым будет отрабатывать IPFW, а для исходящих первым будет работать PF.

Также нужно понимать, что если вы перезапустите файрвол pf, то последовательность обработки поменяется, так как в момент останова pf уберёт обработчики из очереди, а при запуске опять вставит их, но уже вот так:

Соответственно, входящие пакеты первым будут попадать в PF, а исходящие — сначала обрабатываться IPFW.

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

При желании, можно изменить порядок начальной инициализации файрволов таким образом, чтобы IPFW стартовал первым. Для этого не обязательно править rcorder, достаточно в /boot/loader.conf вписать загрузку модуля IPFW. Пользуясь тем, что IPFW ставит свои обработчики при загрузке модуля, получится, что IPFW будет стартовать первым.

Время, вперёд!

23 октября 2015, 21:31

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

Полезли смотреть всей толпой. Выявили, что в логах факт отработки крона логируется с указанием времени, на час опережающем текущее. Т. е. если, например, выполнить date, то получаем 17 часов, а крон в лог пишет, что запустился в 18 часов. Вспомнили, что мы этот сервак не перезагружали после обновления time zone data. Я сделал service syslogd restart. Смотрю в логи — ну, вроде как факт очередного редактирования crontab записался с правильным временем . Должно задание отработать. Однако нифига :-) В логах стало вот так:

Oct 14 17:41:13 vpn2-lesnoy crontab[63631]: (root) BEGIN EDIT (root)
Oct 14 17:41:34 vpn2 crontab[63631]: (root) REPLACE (root)
Oct 14 17:41:35 vpn2 crontab[63631]: (root) END EDIT (root)
Oct 14 17:41:46 vpn2 crontab[63646]: (root) BEGIN EDIT (root)
Oct 14 17:41:52 vpn2 crontab[63646]: (root) END EDIT (root)
Oct 14 18:42:00 vpn2 /usr/sbin/cron[992]: (root) RELOAD (tabs/root)
Oct 14 17:42:34 vpn2 crontab[63677]: (root) LIST (root)
Oct 14 18:44:00 vpn2 /usr/sbin/cron[63721]: (operator) CMD (/usr/libexec/save-entropy)
Oct 14 18:45:00 vpn2 /usr/sbin/cron[63763]: (root) CMD (/usr/libexec/atrun)

Т. е. время скачет очень внезапно :-) Перезапуск крона этот вопрос решил, и время везде синхронизировалось. Т. е. задание не отрабатывало потому, что коллега устанавливал срабатывание задания на 17 часов, а крон считал, что уже 18 часов.

Ctrl + ↓ Ранее