Настройка v3-пользователя на агенте сервера для штатного линуксового snmpd (пакет net-snmp). Out of scope: snmp traps и пользователи с правом запиcи (rw).

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

Напоминалка как настраивается v1/v2c

В версиях v1/v2c использовалось понятие community. Это что-то типа парольной фразы, которую агент получает от клиента. При совпадении community с настройками доступа (например, IP-адресом клиента из разрешённого диапазона), агент отдаёт телеметрию. Всё передаётся открытым методом, как сам community, так и запрашиваемая телеметрия.

В tcpdump запросы выглядят так (community comminity_name):

tcpdump: listening on br0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
20:28:09.382435 IP (tos 0x48, ttl 44, id 25108, offset 0, flags [DF], proto UDP (17), length 74)
    192.158.1.38.46214 > 198.51.100.1.161:  { SNMPv2c C="comminity_name" { GetNextRequest(25) R=1193096294  .1.3.6.1.2.1 } } 
20:28:09.414940 IP (tos 0x48, ttl 44, id 25112, offset 0, flags [DF], proto UDP (17), length 77)
    192.158.1.38.46214 > 198.51.100.1.161:  { SNMPv2c C="comminity_name" { GetNextRequest(28) R=1193096295  .1.3.6.1.2.1.1.1.0 } } 
20:28:09.447385 IP (tos 0x48, ttl 44, id 25117, offset 0, flags [DF], proto UDP (17), length 77)
    192.158.1.38.46214 > 198.51.100.1.161:  { SNMPv2c C="comminity_name" { GetNextRequest(28) R=1193096296  .1.3.6.1.2.1.1.2.0 } } 
20:28:09.479880 IP (tos 0x48, ttl 44, id 25125, offset 0, flags [DF], proto UDP (17), length 77)
    192.158.1.38.46214 > 198.51.100.1.161:  { SNMPv2c C="comminity_name" { GetNextRequest(28) R=1193096297  .1.3.6.1.2.1.1.3.0 } } 
20:28:09.512357 IP (tos 0x48, ttl 44, id 25128, offset 0, flags [DF], proto UDP (17), length 77)
...

NB! В tcpdump здесь и дальше только запросы, ответов нет.

Настройка v2 community производится добавлением строки в файле /etc/snmp/snmpd.conf, со следующим синтаксисом:

rocommunity[6] communityname [access] [-V view]

где:

  • 6 (опционально) - признак ipv6 (он традиционно отдельно от ipv4)

  • communityname - название community, фактически общий пароль

  • access (опционально) - опциональные настройки доступа клиента, варианты: default, hostname, network/bits

  • -V view (опционально) - действующее ограничение по видимости древа oid для данного community, настраивается отдельно, out of scope

Примитивный пример:

rocommunity  comminity_name
rocommunity6 comminity_name

Тут настроено одно community comminity_name только для чтения для обоих версий протокола IP без дополнительных настроек ограничения.

Этого достаточно для понимания.

Аутентификация в SNMPv3

В версии v3 отказались от понятия community. Ну, как отказались, переработали, но термин такой исключён. Появилось шифрование потока и аутентификационной информации. Что взамен?

  • имя пользователя

  • пароль для аутентификации

  • пароль для шифрования

То есть, против типичной схемы пары "логин/пароль" появилась не очень очевидная "логин/два пароля", что изначально вводит в ступор. Для каждого из паролей ещё надо задать алгоритм, с которым они будут использоваться:

  • пароль для аутентификации — нужно указать алгоритм хэширования, традиционно MD5 или SHA, в более поздних версиях появились вариации SHA

  • пароль для шифрования — нужно указать алгоритм симметричного шифрования, традиционно DES или AES, в более поздних версиях появились вариации AES

В tcpdump запросы по версии v3 выглядит примерно так:

tcpdump: listening on br0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
23:28:39.753314 IP (tos 0x48, ttl 44, id 62294, offset 0, flags [DF], proto UDP (17), length 92)
    192.158.1.38.48116 > 198.51.100.1.161:  { SNMPv3 { F=r } { USM B=0 T=0 U="" } { ScopedPDU E= C="" { GetRequest(14) R=30306933  } } } 
23:28:39.785927 IP (tos 0x48, ttl 44, id 62301, offset 0, flags [DF], proto UDP (17), length 171)
    192.158.1.38.48116 > 198.51.100.1.161:  { SNMPv3 { F=apr } { USM B=9 T=330 U="user_name" } { ScopedPDU [!scoped PDU]8f_6f_9f_0a_aa_3b_69_8c_02_30_66_48_52_7d_52_2b_9e_b0_a2_84_cc_60_8a_9e_d5_67_30_52_26_88_0e_68_a7_89_5d_df_78_a4_9b_4e_62_dc_f6_54_f8_16_00_02_ba_f7} } 
23:28:39.818831 IP (tos 0x48, ttl 44, id 62308, offset 0, flags [DF], proto UDP (17), length 174)
    192.158.1.38.48116 > 198.51.100.1.161:  { SNMPv3 { F=apr } { USM B=9 T=330 U="user_name" } { ScopedPDU [!scoped PDU]f1_46_7e_ec_3a_d4_38_94_69_38_14_73_20_71_de_1a_24_b2_61_56_d9_a2_c3_20_b2_6b_6e_11_5f_23_25_f7_56_ba_09_df_09_82_79_54_ec_22_7a_f5_81_60_52_59_83_65_40_09_ac} } 
23:28:39.851664 IP (tos 0x48, ttl 44, id 62310, offset 0, flags [DF], proto UDP (17), length 174)
    192.158.1.38.48116 > 198.51.100.1.161:  { SNMPv3 { F=apr } { USM B=9 T=330 U="user_name" } { ScopedPDU [!scoped PDU]43_30_e9_c9_7c_e2_07_9e_41_74_53_98_51_79_ff_7b_26_51_93_b2_fe_35_db_8e_a4_67_58_87_3e_de_8c_f6_5a_3e_1d_a4_47_d7_3d_f9_c8_b1_ac_2f_d2_48_3a_57_d0_f4_a8_45_9c} } 
23:28:39.884478 IP (tos 0x48, ttl 44, id 62316, offset 0, flags [DF], proto UDP (17), length 174)
    192.158.1.38.48116 > 198.51.100.1.161:  { SNMPv3 { F=apr } { USM B=9 T=330 U="user_name" } { ScopedPDU [!scoped PDU]a0_35_d4_83_cb_6a_82_f1_bd_d7_16_58_00_ff_d7_5e_03_3a_01_c8_be_2e_6d_e5_bf_eb_7c_ce_07_12_76_fc_ee_6e_61_dc_18_d3_4b_7e_dd_f7_bc_f1_3e_de_ad_52_a2_2e_22_ea_74} } 
...

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

Настройка производится чуть сложнее для понимания. Дело в том, что недостаточно закинуть в конфиг заранее подготовленную срочку (с некоторыми оговорками), так как аутентификационная информация генерируется в момент создания пользователя. Это, конечно, заметно осложняет деплой snmpd конфига с помощью типичных средств развёртывания, например, ansible.

Суммарно, и агент и его клиент должны обладать общей информацией (помимо знания клиента о хосте агента):

  • имя пользователя

  • пароль для аутентификации (типовая cli-опция для net-snmp -a)

  • алгоритм аутентификации (типовая cli-опция для net-snmp -A)

  • пароль для шифрования (типовая cli-опция для net-snmp -x)

  • алгоритм шифрования (типовая cli-опция для net-snmp -X)

Помимо уже названного конфига /etc/snmp/snmpd.conf, существует ещё по крайней мере два для динамического создания и хранения аутентификационной информации:

  • /var/lib/snmp/snmpd.conf

  • /usr/share/snmp/snmpd.conf

Даемон snmpd формирует необходимые строчки при запуске, поэтому перед созданием юзера надо погасить процесс. Специальный встроенный скрипт сообщит, что процесс запущен и не даст создать пользователя без его остановки, так как при остановке процесса один из этих файлов перезатирается in-memory значениями.

Создание пользователя на агенте производится командой (опции расписаны выше):

net-snmp-create-v3-user -ro -a 'auth_pass' -A SHA -x 'priv_pass' -X AES user_name

Так создаётся пользователь с именем user_name с паролем аутентификации auth_pass, алгоритмом аутентификации SHA, паролем шифрования priv_pass и алгоритмом шифрования AES. Технически по результату выполнения этого скрипта, добавляется по строке в два вышеназванных конфига:

  • в /var/lib/snmp/snmpd.conf строка createUser user_name SHA "auth_pass" AES "priv_pass"

  • в /usr/share/snmp/snmpd.conf строка rouser user_name

Но это не всё. При запуске даемона snmpd происходит модификация файла /var/lib/snmp/snmpd.conf: все строчки с началом createUser исчезают и появляются уже рабочие строки вида:

usmUser 1 3 0x80001f888052e226409c42d06300000000 "user_name" "user_name" NULL .1.3.6.1.6.3.10.1.1.3 0x018a3bb9c17caafed74347e50d918963e37e70d7 .1.3.6.1.6.3.10.1.2.4 0xcfdecd0e1bc6bf08eb7ae90e68bdb414 ""

Файл /usr/share/snmp/snmpd.conf остаётся нетронутым. В дальнейшем возможно манипулировать аутентификационной информацией в строке usmUser с помощью cli-утилиты snmpusm (см. man).

SNMPv3 security level

Пару слов на счёт уровня безопасности v3. Традиционно выглядит как:

  • noAuthNoPriv — передавать данные без аутентификации и шифрования

  • authNoPriv — передавать данные с успешной аутентификацией, но не шифруя их

  • authPriv — передавать данные с успешной аутентификацией и с использованием шифрования

Первый из них — малополезная экзотика, остановимся на остальных двух authNoPriv и authPriv.

Нагляднее будет показать пару строк tcpdump. Вот так выглядит обмен для authNoPriv:

10:50:58.534839 IP (tos 0x48, ttl 44, id 54935, offset 0, flags [DF], proto UDP (17), length 92)
    192.158.1.38.20723 > 198.51.100.1.161:  { SNMPv3 { F=r } { USM B=0 T=0 U="" } { ScopedPDU E= C="" { GetRequest(14) R=1808634162  } } } 
10:50:58.567563 IP (tos 0x48, ttl 44, id 54937, offset 0, flags [DF], proto UDP (17), length 162)
    192.158.1.38.20723 > 198.51.100.1.161:  { SNMPv3 { F=ar } { USM B=10 T=39805 U="user_name" } { ScopedPDU E=_80_00_1f_88_80_52_e2_26_40_9c_42_d0_63_00_00_00_00 C="" { GetNextRequest(25) R=1808634161  .1.3.6.1.2.1 } } } 
10:50:58.600457 IP (tos 0x48, ttl 44, id 54945, offset 0, flags [DF], proto UDP (17), length 165)
    192.158.1.38.20723 > 198.51.100.1.161:  { SNMPv3 { F=ar } { USM B=10 T=39805 U="user_name" } { ScopedPDU E=_80_00_1f_88_80_52_e2_26_40_9c_42_d0_63_00_00_00_00 C="" { GetNextRequest(28) R=1808634163  .1.3.6.1.2.1.1.1.0 } } } 
...

Вот так для authPriv:

10:51:47.463848 IP (tos 0x48, ttl 44, id 61280, offset 0, flags [DF], proto UDP (17), length 92)
    192.158.1.38.54277 > 198.51.100.1.161:  { SNMPv3 { F=r } { USM B=0 T=0 U="" } { ScopedPDU E= C="" { GetRequest(14) R=1901099838  } } } 
10:51:47.496541 IP (tos 0x48, ttl 44, id 61282, offset 0, flags [DF], proto UDP (17), length 172)
    192.158.1.38.54277 > 198.51.100.1.161:  { SNMPv3 { F=apr } { USM B=10 T=39854 U="user_name" } { ScopedPDU [!scoped PDU]f2_87_ea_15_87_25_a7_b8_44_b4_38_62_15_86_d4_4e_1a_99_34_9e_6c_4d_6a_39_ea_1c_d7_1c_6e_f6_1f_76_c0_e2_ba_91_4d_8e_d5_9c_e7_06_29_c1_47_6e_a2_9a_2b_5c} } 
10:51:47.529402 IP (tos 0x48, ttl 44, id 61290, offset 0, flags [DF], proto UDP (17), length 175)
    192.158.1.38.54277 > 198.51.100.1.161:  { SNMPv3 { F=apr } { USM B=10 T=39854 U="user_name" } { ScopedPDU [!scoped PDU]da_e4_d6_48_f1_ea_28_7f_bf_ce_d6_e0_ef_f7_95_06_0c_d7_79_7c_f1_4b_78_75_4f_1a_e2_00_a6_83_d5_e6_c2_82_55_0f_09_a4_7d_f1_e1_f2_a0_64_d3_c3_d7_13_c3_ae_95_8b_db} } 
...

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

Настройка клиента

Традиционно, проверяется, работает ли snmp вообще с помощью встроенной утилиты snmpwalk. Вот так можно проверить работу клиента v2ccommunity):

snmpwalk -v 2c -c communityname hostname

где:

  • communityname — имя community

  • hostname — имя хоста агента

Вот так можно проверить работу клиента v3 (с шифрованием authPriv):

snmpwalk -v 3 -a SHA -A auth_pass -x AES -X priv_pass -l authPriv -u user_name hostname

где:

  • SHA — алгоритм аутентификации

  • auth_pass — пароль для аутентификации

  • AES — алгоритм шифрования

  • priv_pass — пароль для шифрования

  • authPriv — уровень безопасности "с аутентификацией и шифрованием"

  • user_name — имя пользователя

  • hostname — имя хоста агента

Если всё так работает, осталось заполнить аналогичные строки в софте мониторинга:

CC BY-SA, Vadim Rybalko. Настройка SNMPv3 в LibreNMS.
CC BY-SA, Vadim Rybalko. Настройка SNMPv3 в LibreNMS.

Если что-то не работает, посмотреть в правила фаерволла на обоих концах и попробовать "поволкать" с хоста системы мониторинга, посмотреть в tcpdump, попробовать как работает клиент с локалхоста агента как по внешнему IP-адресу, так и по loopback адресу, например, 127.0.0.1 или ::1.

Комментарии (0)