Защита плоскости управления на оборудовании B4COM

Всем привет!
В этой небольшой статье, хочу продолжить делиться теми инструкциями, которые появляются при использовании оборудования
от B4COM.
До этого уже писал что-то общее про вендора - Базовое описание
И была одна статейка по настройке Radius - Настройка RADIUS на B4COM 41хх в связке с Cisco ISE
P.S. если вам кажется данное полезным и интересным, то буду рад обратной связи с идеями на тему статей.
Плоскость управления - что это и зачем её защищать?
Как вы все прекрасно знаете, у нас существует три плоскости взаимодействия - Data Plane, Control Plane и Management Plane.
Конкретно в этой статье, хочу остановиться только на Control Plane или плоскости управления.
В интернете есть миллион статей на эту тему, поэтому не хочу особенно повторяться, напишу коротко.
В общем, Control Plane, или плоскость управления - это некоторый уровень, слой или абстракция внутри любого оборудования (да, не только сетевики используют термин), которая занимается изучением и анализом разных параметров и свойств, которые в
дальнейшем требуется для корректной работы системы.
Например, всеми любимый K8s тоже имеет свой Control Plane - это собственно та часть, через которую вы управляете кластерами, мониторите их состояние и т.д.
Если говорить про сеть и оборудование вокруг, то для нас, сетевиков, Control Plane - это уровень где работают протоколы маршрутизации, механизмы заполнения таблиц, всякие служебные протоколы типа ND или ARP, ну и так далее.
Вот тут почитайте в общем:
Ок, что это примерно понятно. Остаётся вопрос зачем нам это защищать. Ну на самом деле ответ лежит на поверхности, очевидно что защита нужна во благо качественной работы того самого Control Plane.
Это может быть защита от злоумышленников, или защита от каких-то либо ошибок конфигурации других устройств, чтобы переживать аварию, либо просто мы не хотим тратить ресурс на обработку какого-то протокола, который у нас и не используется вовсе.
Исходя из особенностей работы Control Plane, возникает пара вариантов.
Защита на уровне hardware
Тут собственно подразумевается фильтрация на ASICе. Т.к. мы говорим про B4COM, то у оборудования есть сразу вложенный механизм распределения по очередям на аппаратном уровне, которым вы можете управлять.
Все очереди заранее настроены и распределены по протоколам. Посмотреть текущее состояние можно следующими командами
show cpu-queue details - все очереди и настройки
switch#show cpu-queue details
* - Can not configure the parameter
Cpu queue Rate In PPS Monitor Status Lossy Status
Name Configured Default Max Rate Allowed Configured Default Configured Default
=========== ========== ======= ================ =========== ========== =========== ==========
best-effort - 2113 2113 - * no-monitor - * lossy
ipmc-miss - 2113 2113 - * no-monitor - * lossy
l3-miss - 211 211 - * no-monitor - * lossy
sflow - 32000 100000 - monitor - * lossy
bgp - 1500 1500 - monitor - lossless
vrrp - 1024 1024 - monitor - lossless
ldp-rsvp - 500 500 - monitor - lossless
rip - 500 500 - monitor - lossless
ospf - 2000 2000 - monitor - lossless
dhcp - 100 2048 - no-monitor - lossy
nd - 6000 6000 - monitor - lossless
mpls - 500 500 - no-monitor - lossy
pim - 4000 4000 - * no-monitor - * lossy
arp - 6000 6000 - monitor - lossless
igmp - 4000 4000 - * no-monitor - * lossy
bpdu - 10000 10000 - monitor - lossless
ccm - 1000 1000 - no-monitor - lossy
bfd - 2000 2000 - no-monitor - lossy
ptp - 1000 1000 - no-monitor - lossy
isis - 500 1000 - monitor - lossless
trill-isis - 1000 1000 - monitor - lossless
acl - 200 1000 - * no-monitor - * lossy
vxlan - 500 6000 - monitor - lossy
daivm - 100 500 - no-monitor - lossy
switch#
show interface cpu counters queue-stats - счетчики пакетов по очередям в сторону CPU и дропы
switch#show interface cpu counters queue-stats
E - Egress, I - Ingress, Q-Size is in bytes
+--------------------+--------+-----------------+-------------------+-----------------+-------------------+
| Queue/Class-map | Q-Size | Tx pkts | Tx bytes | Dropped pkts | Dropped bytes |
+--------------------+--------+-----------------+-------------------+-----------------+-------------------+
best-effort (E) 0 0 0 0 0
bgp (E) 0 0 0 0 0
nd (E) 0 0 0 0 0
arp (E) 0 0 0 0 0
bpdu (E) 0 0 0 0 0
hw-bfd (E) 0 0 0 0 0
switch#
Добавить протокол, либо изменить очередь невозможно, не поддерживается оборудованием, но можно управлять следующими параметрами:
lossless - отбрасывать пакеты на входящем интерфейсе (работает механизм backpressure).
Это по сути shaper. Складывает всё в буфер входящего интерфейса и ничего не отбрасывает пока там есть место.
lossy - отбрасывать пакеты при превышении непосредственно на самой очереди.
Это policer, т.е. срезает всё что выше указанного порога.
monitor - логирование пакетов, которые отбрасываются
no-monitor - очевидно, что выключение логирования пакетов, которые отбрасываются
rate - ограничение по pps, триггер на который будут срабатывать собственно все параметры выше.
Делается вот тут, покажу на примере sflow:
switch#conf t
switch(config)#cpu-queue bgp ?
lossless Configure cpu queue as lossless
lossy Configure cpu queue as lossy
monitor monitor cpu queue usage
no-monitor do not monitor cpu queue usage
rate Set cpu queue rate
И выставляете требуемое значение для rate, либо меняете lossless (shaper) на lossy (policer).
С аппаратным разобрались, пойдём посмотрим теперь на второй вариант управления трафиком в сторону CPU - софтовый.
Защита на уровне software
Тут начинается русское поле экспериментов территория полученная опытным путём.
Ни для кого ни секрет, что под капотом у нас всё тот же Linux, а именно Debian. Отсюда можно сделать вывод, что вся фильтрация будет выполняться средствами iptables. Т.к. мы говорим про трафик, который идёт внутрь коммутатора и не является транзитным, то соответственно со стороны iptables - это будет цепочка INPUT. Выходит, если мы сможем ей управлять, то задача по фильтрации данного трафика будет решена.
Собственно, тут есть 2 способа:
- Нативно идти в Linux Shell и там править iptables, в целом способ реальный, но удобным его назвать сложно. Поэтому давайте сразу пропустим.
- Использовать вложенную фичу, управляя iptables через интерфейс loopback.
Работает это следующий образом. При использовании ACL на интерфейс loopback, мы автоматически программируем правила для iptables. То есть, для этого нам нужно:
- создать ACL
- применить его на loopback интерфейс в направлении IN (мы ведь хотим трафик идущий в голову срезать).
- всё.
Ок, давайте попробуем.
Автоматически, у нас создаётся всегда интерфейс lo, вот его мы и будем использовать:
interface lo
ip address 127.0.0.1/8
ipv6 address ::1/128
!
switch#
Предварительно, проверяем что у нас в текущих правилах iptables, для этого проваливаемся в shell и смотрим:
start-shell
su -
####
root@switch:~# iptables -L
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain FORWARD (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
root@switch:~#
Ок, пусто. Всё разрешено.
Создадим простой ACL для фильтрации telnet:
ip access-list acl_copp_in
5 permit any 127.0.0.0 0.255.255.255 any ### ЭТО ВАЖНО!!!
10 deny tcp any any eq telnet
Внимательный читатель спросит, а зачем тут permit на 127.0.0.0/8.
Ответ следующий - ТАК НАДО.
Без него ломаются многие внутренние сервисы, перестаёт работать NTP, SNMP и т.д.
Выявлено опытным путём и это обязательный к разрешению параметр, который должен стоят первым, во избежание ошибки :)
Далее повесим его на наш лупбек:
interface lo
ip address 127.0.0.1/8
ipv6 address ::1/128
ip access-group acl_copp_in in
!
switch#
Идём в шел, там смотрим что у нас с iptables и убеждаемся, что действительно наше правило попало в цепочку INPUT.
start-shell
su -
####
root@switch:~# iptables -L
Chain INPUT (policy ACCEPT)
target prot opt source destination
ACCEPT all -- anywhere switch
ACCEPT all -- loopback/8 anywhere
DROP tcp -- anywhere anywhere tcp dpt:telnet
Chain FORWARD (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
root@switch:~#
Обратите внимание, мы повесили ACL на автоматически созданный loopback, который находится в GRT (дефолтной таблице маршрутизации). Собственно фильтрация и будет проводиться ровно тут, и если у нас трафик идёт внутри какого-то VRF, то фильтрация его не коснётся и он достигнет цели. Поэтому, если мы хотим фильтровать внутри какого-то VRFа, то нужно добавить точно такой же ACL, но уже для loopback внутри этого VRF. Благо они автоматически создаются и остаётся его только туда привязать. ACL при этом можно оставить тот же самый, создавать отдельный смысла нет.
Давайте попробуем.
Для теста, создал вот такой VRF - copp_test
switch#sh run vrf test_copp
!
ip vrf test_copp
!
switch#
switch#sh run int lo.test_copp
!
interface lo.test_copp
!
Пойдём проверим в шел. Как вы знаете VRF = namespace в линукс, поэтому нужно учитывать этот момент и смотреть таблицу iptables для нужного нам. Как узнать какой именно нам нужен? Для этого сначала делаем вот такую команду:
switch#
switch#sh ip vrf
VRF management, VRF ID: 1, FIB ID 1
Router ID: 1.1.1.1 (automatic)
Interfaces:
eth0
lo.management
!
VRF test_copp, VRF ID: 2, FIB ID 2
Router ID is not set
Interfaces:
lo.test_copp
!
Total Number of configured IP VRF's: 2
Total Number of all VRF's: 3
Name Default RD
management not set
test_copp not set
switch#
Благодаря ей, мы получаем информацию о том, какой номер FIB ID, у нашего VRF.
VRF test_copp, VRF ID: 2, FIB ID 2
Теперь идём в шел и смотрим список всех неймспейсов:
start-shell
su -
####
ip netns list
root@switch:~# ip netns list
zebosfib2 (id: 1)
zebosfib1 (id: 0)
zebosfib0
Ок, видим, что тут есть namespace с именем zebosfib2, что соответствует тому номеру, который мы узнали чуть раньше. Смотрим что у него в настройках iptables:
root@switch:~# ip netns exec zebosfib2 iptables -L
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain FORWARD (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
root@switch:~#
root@switch:~#
Пусто, всё разрешено. Ну ожидаемо, мы ведь ничего и не фильтровали.
Ок, делаем аналогично предыдущему и вешаем ACL на автоматический созданный loopback, для этого VRF.
switch#sh run int lo.test_copp
!
interface lo.test_copp
ip vrf forwarding test_copp
ip access-group acl_copp_in in
!
switch#
Проверяем iptables:
root@switch:~#
root@switch:~# ip netns exec zebosfib2 iptables -L
Chain INPUT (policy ACCEPT)
target prot opt source destination
ACCEPT all -- anywhere localhost
ACCEPT all -- loopback/8 anywhere
DROP tcp -- anywhere anywhere tcp dpt:telnet
Chain FORWARD (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
root@switch:~#
root@switch:~#
Вуаля, наш фильтр применился и сюда.
А дальше остаётся только сделать корректные правила, описать их в ACL и применить. Вот и всё. CPU будет защищён.
Я бы рекомендовал тут действовать следующим путём, допустим вы хотите выставить фильтры для SSH. Вы заранее знаете список тех адресов, которым можно ходить и формируете ACL следующего вида:
ip access-list test_copp
5 permit any 127.0.0.0 0.255.255.255 any ### НЕ ЗАБЫВАЕМ!
10 permit tcp ALOWED_NETWORK/HOST any eq ssh ### разрешаем доступ отсюда
20 permit tcp ALOWED_NETWORK/HOST any eq ssh ### и отсюда
но, в конце разрешенного списка, с каким-то отступом делаете deny any any для конкретного протокола, таким образом вы его запрещаете для всех, кроме вышеописанных, получается что-то типа:
ip access-list test_copp
5 permit any 127.0.0.0 0.255.255.255 any
10 permit tcp ALOWED_NETWORK/HOST any eq ssh
20 permit tcp ALOWED_NETWORK/HOST any eq ssh
50 deny tcp any any eq ssh ### Запрещаем всё остальное для SSH
Ну и так далее для остальных протоколов, допустим используем BGP, SNMP, NTP, SSH и Syslog получается что-то типа:
ip access-list test_copp
5 permit any 127.0.0.0 0.255.255.255 any
10 permit tcp ALOWED_NETWORK/HOST any eq ssh
20 permit tcp ALOWED_NETWORK/HOST any eq ssh
50 deny tcp any any eq ssh
60 permit udp host ALOWED_NETWORK/HOST any eq snmp
70 permit udp host ALOWED_NETWORK/HOST any eq snmp
100 deny udp any any eq snmp
110 permit tcp ALOWED_NETWORK/HOST any eq bgp
120 permit tcp ALOWED_NETWORK/HOST any eq bgp
150 deny tcp any any eq bgp
160 permit udp host ALOWED_NETWORK/HOST any eq ntp
170 permit udp host ALOWED_NETWORK/HOST any eq ntp
200 deny udp any any eq ntp
210 permit udp host ALOWED_NETWORK/HOST any eq syslog
220 permit udp host ALOWED_NETWORK/HOST any eq syslog
250 deny udp any any eq syslog
т.к. мы хотим эти протоколы закрыть, а всё остальное будто бы и не мешает, то в конце повесим explicit permit, т.е. permit any any.
В итоге получаем вот такой ACL:
ip access-list test_copp
5 permit any 127.0.0.0 0.255.255.255 any
10 permit tcp ALOWED_NETWORK/HOST any eq ssh
20 permit tcp ALOWED_NETWORK/HOST any eq ssh
50 deny tcp any any eq ssh
60 permit udp host ALOWED_NETWORK/HOST any eq snmp
70 permit udp host ALOWED_NETWORK/HOST any eq snmp
100 deny udp any any eq snmp
110 permit tcp ALOWED_NETWORK/HOST any eq bgp
120 permit tcp ALOWED_NETWORK/HOST any eq bgp
150 deny tcp any any eq bgp
160 permit udp host ALOWED_NETWORK/HOST any eq ntp
170 permit udp host ALOWED_NETWORK/HOST any eq ntp
200 deny udp any any eq ntp
210 permit udp host ALOWED_NETWORK/HOST any eq syslog
220 permit udp host ALOWED_NETWORK/HOST any eq syslog
250 deny udp any any eq syslog
999 permit any any any
Таким образом, мы защищаем наш CPU от попыток взлома по тем протоколам, которыми пользуемся, но и не ограничиваем остальное. Если же вы хотите отключить всё, то тут вам придётся более тщательно поработать над своим ACL, перед тем как его применить, а то сами знаете как оно бывает :)

Заключение
Вот и всё что хотел сказать, надеюсь было полезно.
Повторюсь - хотите больше таких или других статей, пишите фидбек, с ним гораздо проще понимать что именно вам интересно.
Не забывайте поделиться с коллегами, друзьями и тем, кто по вашему мнению будет заинтересован. Таким образом вы окажете поддержку мне и выразите свою благодарность.
Спасибо за внимание!