Фильтр анонсируемых адресов

Продолжаю делать записи для себя. На этот раз потребовалось отфильтровать роуты, отдаваемые другим. OSPF для этого не подходит: стандарт такого попросту не предусматривает. Значит, переключаемся на BGP. Минимальный конфиг для FRR

!
router bgp 65000
 bgp router-id 10.0.0.2
 no bgp ebgp-requires-policy
 neighbor 10.0.0.3 remote-as 65000
 !
 address-family ipv4 unicast
  redistribute connected
  redistribute static
  neighbor 10.0.0.3 next-hop-self
 exit-address-family
exit
!

На другой стороне есть адрес 6.7.8.9, котрый не должен попасть на этот роутер.

!
ip route 6.7.8.9/32 10.1.0.254
!
router bgp 65000
 bgp router-id 10.0.0.3
 no bgp ebgp-requires-policy
 neighbor 10.0.0.2 remote-as 65000
 !
 address-family ipv4 unicast
  redistribute connected
  redistribute static
  neighbor 10.0.0.2 next-hop-self
  neighbor 10.0.0.2 prefix-list nobad-out out
 exit-address-family
exit
!
ip prefix-list nobad-out seq 5 deny 6.7.8.9/32 le 32
ip prefix-list nobad-out seq 10 permit 10.1.0.0/24 le 32
!

Проверяем

router2# show ip route bgp
Codes: K - kernel route, C - connected, S - static, R - RIP,
       O - OSPF, I - IS-IS, B - BGP, E - EIGRP, N - NHRP,
       T - Table, v - VNC, V - VNC-Direct, A - Babel, F - PBR,
       f - OpenFabric,
       > - selected route, * - FIB route, q - queued, r - rejected, b - backup
       t - trapped, o - offload failure

B>* 10.1.0.0/24 [200/0] via 10.0.0.3, ens19, weight 1, 00:16:06

ВНИМАНИЕ: правка “на живую” permit-list не приведет к перечитыванию правил на другой стороне. Надо вручную в router bgp прицепить-отцепить их. Почему так – хз.

Вариант лечения: не важно где сказать clear ip bgp 10.0.0.3 (ессно, на передатчике адрес приемника и наоборот), это сбросит сессию bgp и заставит перечитать роуты.

MultiWAN без боли и шума

О, сколько боли в слове MultiWAN! Стоит погуглить что-нибудь типа “есть два провайдера, как сделать так, чтобы можно было пользоваться одновременно или использовать второй как бекапный”, так сразу высыпается куча советов про метрики, маркировку трафика с помощью iptables и так далее. Правда, в последнее время все поутихло, но это потому, что в большинстве прошивок для домашних роутеров наконец-то доделали этот функционал.

Но я-то другой! У меня закидоныпросы! Вот прямо сейчас мне надо разрулить аж трех “провайдеров” на одной точке. Плюс пустить один из адресов через одного провайдера… В общем, попробовал я сначала натыкать галочек в любимом кинетике, потом сдался в сторону {pf|opn}sense, но и так не преуспел… Нет, наверняка можно было допинать, но я устал и сдался.

Итак, первоначальные условия. Есть три провайдера: через сотовую связь, ADSL и GPON. Работают одновременно. Рядом стоит сервер, который легко потянет кучу виртуалок. Необходимо клиентов (то есть меня) пускать в интернет, при этом приоритет gpon, adsl, сотик. Но один из служебных маршрутов должен уходить через ADSL. Вроде бы просто, да?

Для начала я вообще решил проверить, а возможно ли это без боли. Для этого я сделал стенд из трех виртуалок. Две я обозвал router1 и router2, а клиента – естественно client.

internet - (ens18)router1(ens19) - 10.0.0.1 - network
internet - (ens18)router2(ens19) - 10.0.0.2 - network
                  client (ens19) - 10.0.0.3 - network

Я опускаю настройку роутеров и клиента. Все друг друга видят, на роутерах включен форвард пакетов и SNAT, в общем, все работает на ручном приводе хорошо. Теперь необходима автоматика.

Ставлю на каждый хост FRR. Редактирую /etc/frr/daemons на предмет включения ospfd и запускаю. Далее скармливаю на всех хостах одну и ту же конструкцию, только меняю router id

!
interface ens18
 ip ospf passive
exit
!
interface ens19
 ip ospf dead-interval 30
exit
!
router ospf
 ospf router-id 10.0.0.1
 network 10.0.0.0/24 area 0.0.0.0
exit
!

Никакой авторизации и прочих заморочек. Поднимаю OSPF, запрещаю ему спамить в сторону провайдера и говорю, что все в сети 10/24 – наше. Проверяю, что роутеры видят друг друга.

client# show ip ospf neighbor 

Neighbor ID     Pri State           Dead Time Address         Interface                        RXmtL RqstL DBsmL
10.0.0.1          1 Full/DR           27.376s 10.0.0.1        ens19:10.0.0.3                       0     0     0
10.0.0.2          1 Full/Backup       28.362s 10.0.0.2        ens19:10.0.0.3                       0     0     0

В принципе, теперь можно расставлять роуты куда надо и радоваться жизни. Но мне-то надо рулить default роутом. И тут засада: по-умолчанию, чтобы не расхреначить все, роутеры по умолчанию дропают роуты на 0.0.0.0/0. Можно, конечно, воспользоваться хаком имени OpenVPN и анонсировать роуты 0.0.0.0/1 и 128.0.0.0/1, но это не наш метод. Немного погуглив, выянил, что достаточно добавить default-information originate always в секцию router ospf и все получится. Дескать, я edge/border/ваще_крутой роутер и ходи через меня.

client# show ip ospf route 
============ OSPF network routing table ============
N    10.0.0.0/24           [1] area: 0.0.0.0
                           directly attached to ens19

============ OSPF router routing table =============
R    10.0.0.1              [1] area: 0.0.0.0, ASBR
                           via 10.0.0.1, ens19

============ OSPF external routing table ===========
N E2 0.0.0.0/0             [1/1] tag: 0
                           via 10.0.0.1, ens19

И действительно, стоило мне такое сказать, как client тут же все увидел и обновил. Добавляю ту же строку в конфиг второго. Вуаля!

root@client:~# ip r
default nhid 22 proto ospf metric 20 
	nexthop via 10.0.0.1 dev ens19 weight 1 
	nexthop via 10.0.0.2 dev ens19 weight 1 
10.0.0.0/24 dev ens19 proto kernel scope link src 10.0.0.3 

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

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

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

client# show ip ospf neighbor 

Neighbor ID     Pri State           Dead Time Address         Interface                        RXmtL RqstL DBsmL
10.0.0.1          1 Full/DR           24.884s 10.0.0.1        ens19:10.0.0.3                       0     0     0
10.0.0.2          1 Full/Backup       25.013s 10.0.0.2        ens19:10.0.0.3                       0     0     0
10.1.0.4          1 Full/Backup       22.051s 10.1.0.4        ens20:10.1.0.3                       0     0     0

client# 

client# show ip ospf border-routers 
============ OSPF router routing table =============
R    10.0.0.1              [1] area: 0.0.0.0, ASBR
                           via 10.0.0.1, ens19
R    10.0.0.2              [1] area: 0.0.0.0, ASBR
                           via 10.0.0.2, ens19
R    10.1.0.4              [1] area: 0.0.0.0, ASBR
                           via 10.1.0.4, ens20

root@client:~# ip r
default nhid 79 proto ospf metric 20 
	nexthop via 10.0.0.1 dev ens19 weight 1 
	nexthop via 10.0.0.2 dev ens19 weight 1 
	nexthop via 10.1.0.4 dev ens20 weight 1 
10.0.0.0/24 dev ens19 proto kernel scope link src 10.0.0.3 
10.1.0.0/24 dev ens20 proto kernel scope link src 10.1.0.3 

Как видно, теперь у меня аж три некстхопа. Ходи – не хочу.

Проверяю, как OSPF на client видит интерфейсы

client# show ip ospf interface 
ens19 is up
  ifindex 3, MTU 1500 bytes, BW 4294967295 Mbit <UP,BROADCAST,RUNNING,MULTICAST>
  Internet Address 10.0.0.3/24, Broadcast 10.0.0.255, Area 0.0.0.0
  MTU mismatch detection: enabled
  Router ID 10.0.0.3, Network Type BROADCAST, Cost: 1
  Transmit Delay is 1 sec, State DROther, Priority 1
  Designated Router (ID) 10.0.0.1 Interface Address 10.0.0.1/24
  Backup Designated Router (ID) 10.0.0.2, Interface Address 10.0.0.2
  Saved Network-LSA sequence number 0x80000004
  Multicast group memberships: OSPFAllRouters
  Timer intervals configured, Hello 10s, Dead 30s, Wait 30s, Retransmit 5
    Hello due in 6.368s
  Neighbor Count is 2, Adjacent neighbor count is 2
ens20 is up
  ifindex 4, MTU 1500 bytes, BW 4294967295 Mbit <UP,BROADCAST,RUNNING,MULTICAST>
  Internet Address 10.1.0.3/24, Broadcast 10.1.0.255, Area 0.0.0.0
  MTU mismatch detection: enabled
  Router ID 10.0.0.3, Network Type BROADCAST, Cost: 1
  Transmit Delay is 1 sec, State DR, Priority 1
  Designated Router (ID) 10.0.0.3 Interface Address 10.1.0.3/24
  Backup Designated Router (ID) 10.1.0.4, Interface Address 10.1.0.4
  Multicast group memberships: OSPFAllRouters OSPFDesignatedRouters
  Timer intervals configured, Hello 10s, Dead 30s, Wait 30s, Retransmit 5
    Hello due in 0.712s
  Neighbor Count is 1, Adjacent neighbor count is 1

Все правильно, по умолчанию для ethernet cost 1.

Добавляю на клиенте на интерфейс ens19, который смотрит на первые два роутера, опцию ip ospf cost 100. Согласно мануалам, это должно сказать, что туда надо трафик отправлять в последнюю очередь (ведь 100>1)

Проверяю. Вот это было до.

root@client:~# ip r
default nhid 87 proto ospf metric 20 
	nexthop via 10.0.0.1 dev ens19 weight 1 
	nexthop via 10.0.0.2 dev ens19 weight 1 
        nexthop via 10.1.0.4 dev ens20 weight 1
10.0.0.0/24 dev ens19 proto kernel scope link src 10.0.0.3 
10.1.0.0/24 dev ens20 proto kernel scope link src 10.1.0.3 

Включаю ip ospf cost

root@client:~# ip r
default nhid 91 via 10.1.0.4 dev ens20 proto ospf metric 20 
10.0.0.0/24 dev ens19 proto kernel scope link src 10.0.0.3 
10.1.0.0/24 dev ens20 proto kernel scope link src 10.1.0.3 

Проверяю, что вообще-то роуты вернутся, если что-то случится с router3

root@client:~# ip link set ens20 down
root@client:~# ip r
default nhid 95 proto ospf metric 20 
	nexthop via 10.0.0.1 dev ens19 weight 1 
	nexthop via 10.0.0.2 dev ens19 weight 1 
10.0.0.0/24 dev ens19 proto kernel scope link src 10.0.0.3 

Работает, прямо как я и задумал. Теперь садим на каждый роутер по скрипту, который будет проверять доступность интернета. Таких скриптов уйма на любой вкус, цвет и запах. Можно вообще какой-нибудь zabbix присобачить. Но главное, чтобы они шли и пускали два скриптика

root@router3:~# cat enable.sh 
#!/bin/bash
cat << EOF | /usr/bin/vtysh
conf t
router ospf
default-information originate always
exit
exit
exit
EOF

root@router3:~# cat disable.sh 
#!/bin/bash
cat << EOF | /usr/bin/vtysh
conf t
router ospf
no default-information originate always
exit
exit
exit
EOF

Их названия говорят сами за себя, как и то, что они делают. Позапускал их, проверил, что таблица роутинга перестраивается, как и положено, согласно правилам.

Теперь последнее: пустить определенный маршрут через определенный роутер. Пусть это будет 1.2.3.4/32 на router2. Это вообще просто. Просто создаем статический роут и просим распростанить статику.

root@router2:~# vtysh

Hello, this is FRRouting (version 8.1).
Copyright 1996-2005 Kunihiro Ishiguro, et al.

router2# conf t
router2(config)# ip route 1.2.3.4/32 192.168.1.1
router2(config)# router ospf
router2(config-router)# redistribute static

И все получается согласно заветам лучших сетевиков.

root@client:~# ip r
default nhid 113 via 10.1.0.4 dev ens20 proto ospf metric 20 
1.2.3.4 nhid 115 via 10.0.0.2 dev ens19 proto ospf metric 20 
10.0.0.0/24 dev ens19 proto kernel scope link src 10.0.0.3 
10.1.0.0/24 dev ens20 proto kernel scope link src 10.1.0.3 

Теперь осталось развести router1 и router2 по разным подсетям, донастроить точно так же, как и router3, забекапить все и забыть до появления новых вводных.

А, да. Ну и всех жаждущих интернета отправить на client. Теперь точно все.

Видеонаблюдение, часть 1

В целях покупки домика загорелось мне устроить видеонаблюдение. И чтобы потом как в фильмах “вот преступник, увеличь кадр. еще! ага, попался!”. У меня преступников не ожидается, но хотелку осуществить очень охота.

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

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

Но у аналогов есть и минус: от каждой камеры нужен прямой кабель до регистратора. 16 камер? 16 линков на коаксиале. В общем, не мой размерчик от слова совсем.

Значит, цифровая. Тем более, что ethernet с wifi у меня точно будет в каждом угле.

Открываю браузер, иду в магазин и немного охреневаю от изобилия камер со всякими аббревиатурами. А цены скачут на три порядка. Что делать, куда бежать?

Первое условие “никаких облаков” сразу отсеивает большую половину камер. Эти камеры потому и дешевы, что сливают китайцам и корейцам все, что можно слить. Новомодным системам надо же на чем-то обучаться? Да и безопасность таких камер хромает на обе ноги – достаточно погуглить и вы обнаружите кучу кредов на любой вкус: хоть напрямую, хоть через “облако”.

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

Пока камеры ехали, я принялся изучать, чем же их обслуживать. Или, говоря другими словами, DVR, NVR или ip-видеорегистратор. Тут я тоже сразу задрал планку “работает под linux, управление через браузер”. Казалось бы, софта на рынке дофига и больше – бери и пробуй.

Ан, нет. 99% софта – это откровенные поделки, лишь бы было. Особенно этим отличаются отечественные производители. Ни инструкций по настройке, ни поддержки чего-то более-менее распространенного… ничего вообще. Я ставил, смотрел, плевался и удалял. “серверные” версии, которые требуют qtgui? да легко!

Где-то тут приехали и сами камеры, сразу же добавив много интересных аспектов. Например, некоторые камеры сами по себе видеорегистраторы. Можно вставить sd карту, обрисовать зоны для детектора движений и вуаля – все заработает.

Но в итоге самым важным оказалось совершенно другое. Формат, в котором передается видео. MJPEG, H264 и самый моднявый, H265 с плюсиками и буквами на конце. Внезапно для меня это стало могильным камнем для 99,99% видеорегистраторов. Дело в том, что чем моднее формат, тем меньше он требует диска для хранения. В проспектах буржуев я встречал, что H265 S+ требует до 70% меньше места, чем H264.

В итоге с банальной задачей “смотри в камеры, если обнаружишь движение, пиши на диск” справился только один видеорегистратор – AgentDVR. Все остальные, включая широко известный ZoneMinder – тупо крешились, жрали проц и память и так далее и тому подобное. Отдавать им такую важную задачу – глупость высшего уровня.

Выше – скриншот с тестовой инсталляции AgentDVR. Три камеры – 4МП, 2МП и 2МП. Пишется по детектору движения. Как видите, за 11 дней сожрало 120 гигов. И процессор совершенно не нагружен. Было бы прелестно, если бы была хоть какая-то интеграция со сторонними системами. Нет, она вроде заявлена, но за помесячную денежку. Не подходит – я готов заплатить за софт, но единоразово.

Увидев подобные цифры потребления, я воспрял духом. Никаких петабайт на глубину в неделю не нужно – хватит и обычных объемов! А их, как вы понимаете, есть у меня!

Следующий шаг – это полностью “аппаратные” решения. Специализированные, заточенные на работу с камерами и только с камерами. Ну и за кучу (или не очень) денежек.

И тут очередная засада. Как выбрать видеорегистратор? Ни одна собака не пишет, сколько камер он может понятнуть. Вместо этого вовсю рекламируют, сколько потоков с каким разрешением он может одновременно отображать. Но мне-то это не надо…

Когда ко мне приехала очередная пачка железок, то тайна оказалась не такой-то уж и тайной. У каждого приличного видеорегистратора есть параметр “максимальный входной поток”. Он измеряется в мегабитах. И чтобы подсчитать, сколько камер потянет регистратор, надо просто принять “1 мегапиксел камеры = 1 мегабит на регистраторе”. И не смотрите, что 4МП камера выдает по сети всего 600-800 килобайт в секунду – это не те потоки, не итшные. В итоге для трех камер на 4, 2 и 2 мегапикселя нужен регистратор, умеющий переваривать поток в 9+ мегабит. Почему не 8? Оставьте небольшой процент на всякие просадки, усушки и утруски.

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

Вот мой, купленный на пробу от tiandy. Два диска, 200МБ входного. Усб справа для ориентира в размерах.

Сами регистраторы тоже разные. Самые простые умеют просто писать поток с камеры на диск. Платы таких на али можно найти от 1500 рублей. У регистраторов покруче добавляется возможность писать на внешние диски, в случае каких-то событий отправлять письма, включать что-то внешнее и прочее подобное.

И тут меня настигла первая засада: у “дешевых” регистратов вся аналитика (распознование человек-машина, границы и так далее) ложится исключительно на камеры. Если камера не умеет (или железка не знает про возможности камеры) – то регистратор напишет “ой, и я тоже не могу”. Вот мой, к примеру, не может.

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

Значит что? Оставляем текущую мешанину работать, набираться опыта и приступаю к тестированию регистраторов под windows. Причем, раз аппетит пришел, с особым упором на аналитику, ибо задача простой записи по движению решена аж двумя способами.

Даешь звук в vmware

Я как-то привык к заикающемуся и запинающемуся звуку внутри виртуалки. Ну сколько не пробовал разных рецептов, помогали … не очень. А тут внезапно выдался свободный часик и я решил немного погуглить. Оказывается, проблема известная, только не все знают про ее решение.

Скопировал файлик, заменил значения … и вуаля! Звук снова в наличии.

Привожу медиатеку к единому стилю

В предидущем посте я прошелся по библиотеке и расставил везде теги и обложки. Но теперь надо решить другую проблему: у меня есть mp3, m4a и flac файлы. Причем я точно знаю, что некоторые flac и m4a получены из mp3. Да, источники музыки были не всегда адекватными …

Поэтому шаг номер раз: приводим всю библиотеку в mp3 формат. То, что из m4a и flac перезапишутся mp3 – пофиг

find . -type f -name "*.m4a" | parallel 'ffmpeg -y -i {}  -map_metadata 0 -codec:a libmp3lame -qscale:a 1 {.}.mp3'

find . -type f -name "*.flac" | parallel 'ffmpeg -y -i {}  -map_metadata 0 -codec:a libmp3lame -qscale:a 1 {.}.mp3'

Теперь можно грохнуть “оригиналы”. И совсем маленький шаг: необходимо избавиться от дубликатов. Так как источников было много, то и для некоторых популярных песен было аж до 5 вариантов. Пишу маленький скрипт, который удаляет все дубликаты меньшего размера (да, буду считать, что бОльший размер означает лучшее качество).

#!/bin/bash

IFS=$(echo -en "\n\b")

function traverse() { 
    # lets find copies in directory and i dont want use .m?? in search
    for file in $(ls -1 ${1} |grep -e \(1\).mp3 -e \(2\).mp3 -e \(3\).mp3 -e \(4\).mp3 -e \(5\).mp3) 
    do
        l="${1}/${file%????????}*" # cut (X).mp3 from file name
        # delete all files except first and biggest
        ls -1S ${l} | tail -n +2 | xargs -d '\n' rm -f
        # Now rename file. Leave mv alone for errors "the file is same" - its working indicator for me :)
        ffin="${l%?}.mp3"
        mv  $l $ffin        
    done
    # now lets find another directory to deep in
    for file in $(ls "$1")
    do
        if [[ -d ${1}/${file} ]]; then
            traverse "${1}/${file}"
        fi
    done
}

function main() {
    traverse "$1"
}

main "$1"

И натравливаем его на библиотеку. Я специально не стал обрамлять вокруг mv, что бы хоть что-то выводило во время работы, поэтому сообщения типа mv: '/mnt/swamp/Reloaded//松居慶子/Dream Walk/04 Fire in the Desert.mp3' and '/mnt/swamp/Reloaded//松居慶子/Dream Walk/04 Fire in the Desert.mp3' are the same file стали для меня индикатором, что скрипт работает.

В результате у меня теперь есть библиотека музыки, где все разложено по полочкам (иногда полупустым), протегано и с обложками. Но (ох уж это но) есть куча песен с битрейтом 96 и 128 кбит/с. Но как к этому подобраться автоматически – я пока не представляю.

Навожу порядок в музыке

Довольно долго я таскаю с собой архив музыки. Первые треки в нем датируются аж 1995 годом. Естественно, что за все это время внутри образовалась каша. Я много раз пытался подойти к этой свалке, что бы разгрести ее содержимое и разложить все по полочкам. Однако регулярно обламывался из-за банальной лени: все-таки раскидать почти 100 тысяч файлов нужно адское терпение.

Ситуацию усложняло то, что куча файлов имело очень говорящие названия типа 3.mp3. Естественно, каких-либо тегов тоже ожидать не приходилось. Ну вот просто как-то понравилась мне песенка, я ее и забросил в архив …

Наконец в очередной раз я подошел к этой свалке. Тыкался, тыкался, смотрел на всякие редакторы тегов и горестно вздыхал. Пока не решил погуглить, как можно для всего этого поиспользовать всякие шазамы и прочие новомодные (почти) штучки. И вуаля!

https://picard.musicbrainz.org/

Оставлю эту ссылку отдельной строкой. Жрет mp3, ищет по аудиотпечатку что же это за трек и заполняет теги, добавляет обложку и раскладывает получившееся красиво. Я в совершеннейшем восторге. На тестовой выборке в 7 тыщ файлов не нашла соответствие только для 3. Но их никто не знает, ни яндекс, ни яблоки. Мелочь …

Bitwarden -> KeePassXC

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

Не то, что бы мне жалко денег, но заводить какой-то странный bitcoin кошелек на не менее странных биржах мне стремно. А оплата карточкой через paypal не доступна, ибо они поддерживают нацистов и типа санкции.

Сначала я подумал поднять свой сервер для него, благо есть аж два варианта. Но реально, как-то стало очень лень. Решил попробовать KeePassXC. Нашел репу https://github.com/davidnemec/bitwarden-to-keepass, сделал три шага по мануалу и вуаля – оно все заработало и заработало подозрительно легко. И даже клиент не такой вырвиглазный, каким я его помню …

OS X странный   login item

Пробегал мимо настроек, решил на всякий случай заглянуть в то, что у меня пускается автоматом.  Settings-General-Login Items, а там такое

Что за ln? При нажатии на значок информации честно перекидывает на /bin/ln. Троян?

$ codesign -dvvv /bin/ln
Executable=/bin/ln
Identifier=com.apple.ln
Format=Mach-O universal (x86_64 arm64e)
CodeDirectory v=20400 size=453 flags=0x0(none) hashes=9+2 location=embedded
Platform identifier=14
Hash type=sha256 size=32
CandidateCDHash sha256=da717c2e8dc49817f70f208029b3533b4c9cdb2c
CandidateCDHashFull sha256=da717c2e8dc49817f70f208029b3533b4c9cdb2ceacfa90a43b19009331880fb
Hash choices=sha256
CMSDigest=da717c2e8dc49817f70f208029b3533b4c9cdb2ceacfa90a43b19009331880fb
CMSDigestType=2
Launch Constraints:
	None
CDHash=da717c2e8dc49817f70f208029b3533b4c9cdb2c
Signature size=4442
Authority=Software Signing
Authority=Apple Code Signing Certification Authority
Authority=Apple Root CA
Signed Time=5 Nov 2022, 04:23:08
Info.plist=not bound
TeamIdentifier=not set
Sealed Resources=none
Internal requirements count=1 size=60

Нет, не троян. Уже лучше. Значит ищем в {~}/Library/LaunchAgents то, что под это маскируется. Оказывается, это Docker Desktop такой херней развлекается

$ cat com.docker.socket.plist 
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict><key>KeepAlive</key><false/><key>Label</key><string>com.docker.socket</string><key>ProcessType</key><string>Background</string><key>Program</key><string>/bin/ln</string><key>ProgramArguments</key><array><string>/bin/ln</string><string>-s</string><string>-f</string><string>/Users/kiltum/.docker/run/docker.sock</string><string>/var/run/docker.sock</string></array><key>RunAtLoad</key><true/></dict></plist>

Что делать? Да ничего. При запуске Docker Desktop он просто сругается и предложит сделать эту ссылку заново. Ну или оставьте “сервис ln” включенным

Обходим DNS hi-jack

Не так давно некоторые (я не буду показывать пальцем) провайдеры начали заниматься херней. А именно блокировать или подменять DNS ответы. И ладно бы блочили какой-нибудь абстрактный facebook.com, так они ломают к примеру резолвинг обратных записей, что например для почтовых серверов больно и печально. Если обратиться к техподдержку, то там начинают лепетать про защиту от угроз и предлагают пользоваться серверами провайдера, ведь они защищены и вообще лучше всех.

В общем, на данный момент это все лечится очень просто. Берем любой клиент DNS over HTTPS (DoH), ставим его и направляем весь днс трафик через него. Для ubuntu рецепт следующий:

apt install dnscrypt-proxy
systemctl enable dnscrypt-proxy
systemctl start dnscrypt-proxy

В результате на 127.0.2.1:53 появляется DNS сервер, который кладет всевозможные причиндалы на провайдерские потуги. Ну а дальше во всех клиентах просто указываем этот сервер. Лично я просто добавил одну строчку на главном домашнем кеширующем bind

cat /etc/bind/named.conf.options | grep fo
        forwarders { 127.0.2.1 ; };

Ну и попутно подобным шагом вы провайдеру статистику “куда вы ходите” поломаете. Ибо “кто с чем к нам, тот тем и получит”

PS Да, это все лечится VPN, но кто даст гарантию, что и провайдер VPN не займется тем же самым?

Zimbra lets encrypt

Давным-давно выкладывал скрипт для обновления SSL у зимбры. С тех пор прошло много времени, поэтому капельку обновил

#!/bin/bash
cp -f /etc/letsencrypt/live/mail.ka12.co/* /opt/zimbra/ssl/letsencrypt/
wget -O /tmp/ISRG-X1.pem https://letsencrypt.org/certs/isrgrootx1.pem.txt
cat /tmp/ISRG-X1.pem > /opt/zimbra/ssl/letsencrypt/fullchain.pem 
cat /etc/letsencrypt/live/mail.ka12.co/fullchain.pem >> /opt/zimbra/ssl/letsencrypt/fullchain.pem 

chown zimbra:zimbra /opt/zimbra/ssl/letsencrypt/*
runuser -u zimbra /opt/zimbra/bin/zmcertmgr verifycrt comm /opt/zimbra/ssl/letsencrypt/privkey.pem /opt/zimbra/ssl/letsencrypt/cert.pem /opt/zimbra/ssl/letsencrypt/fullchain.pem
runuser -l zimbra -c 'zmcontrol stop'
cp -a /opt/zimbra/ssl/zimbra /opt/zimbra/ssl/zimbra.`date "+%Y%m%d"`

runuser -u zimbra cp /opt/zimbra/ssl/letsencrypt/privkey.pem /opt/zimbra/ssl/zimbra/commercial/commercial.key
runuser -u zimbra /opt/zimbra/bin/zmcertmgr deploycrt comm /opt/zimbra/ssl/letsencrypt/cert.pem /opt/zimbra/ssl/letsencrypt/fullchain.pem
runuser -l zimbra -c '/opt/zimbra/bin/zmcertmgr deploycrt comm /opt/zimbra/ssl/letsencrypt/cert.pem /opt/zimbra/ssl/letsencrypt/fullchain.pem'
runuser -l zimbra -c 'zmcontrol start'

mail.ka12.co замените на свой домен. Принцип простой – сначала любым привычным способом получаете сертификат через LE, а потом запуская скрипт выше засовываете сертификат в zimbra

k8s namespace stuck in terminating stage

При тестировании всяких штук очень часто получается так, что при удалении неймспейсов они замерзают в Terminating. Могут на 5 минут, могут на сутки. Нашел рецепт

for ns in $(kubectl get ns --field-selector status.phase=Terminating -o jsonpath='{.items[*].metadata.name}')
do
  kubectl get ns $ns -ojson | jq '.spec.finalizers = []' | kubectl replace --raw "/api/v1/namespaces/$ns/finalize" -f -
done

for ns in $(kubectl get ns --field-selector status.phase=Terminating -o jsonpath='{.items[*].metadata.name}')
do
  kubectl get ns $ns -ojson | jq '.metadata.finalizers = []' | kubectl replace --raw "/api/v1/namespaces/$ns/finalize" -f -
done

Нашел тут https://stackoverflow.com/questions/65667846/namespace-stuck-as-terminating

Работает

VMWare 16 и Ubuntu 22.04

А вот магическая последовательность команд, что бы завести это чудо под ubuntu

VMWARE_VERSION=workstation-16.2.3
TMP_FOLDER=/tmp/patch-vmware
rm -fdr $TMP_FOLDER
mkdir -p $TMP_FOLDER
cd $TMP_FOLDER
sudo apt install git -y
git clone https://github.com/mkubecek/vmware-host-modules.git
cd $TMP_FOLDER/vmware-host-modules
git checkout $VMWARE_VERSION
git fetch
make
sudo make install
sudo rm /usr/lib/vmware/lib/libz.so.1/libz.so.1
sudo ln -s /lib/x86_64-linux-gnu/libz.so.1 /usr/lib/vmware/lib/libz.so.1/libz.so.1

Честно спер отсюда https://www.itzgeek.com/how-tos/linux/ubuntu-how-tos/how-to-install-vmware-workstation-16-pro-on-ubuntu-22-04-ubuntu-20-04.html

Vmware 16 и Fedora 35

Исторически сложилось, что vmware не может скомпилировать свои модули. Лечится простым тыканьем носом в правильные места

CPATH=/usr/src/kernels/5.16.18-200.fc35.x86_64/include/linux vmware-modconfig --console --install-all

Версию ядра заменить на текущую.

Как остановить скроллинг в firefox

Зачем-то в firefox сделали такую фичу: когда ты скроллишь и “бросаешь”, то скроллинг некоторое время продолжается. Особенно это часто проявляется с тачпадами. В итоге часто начинается дерганье туда-сюда. Бесит неимеверно. Лечится просто: в about:config этот параметр надо поставить в false

apz.gtk.kinetic_scroll.enabled

Звук Huawei Matebook X 2021 под linux

Есть ноутбук Huawei Matebook X Pro 2021 (MACHD-WXX9). Под линуксом у него крайне отвратный звук. Когда играет тихо еще туда-сюда, а вот когда добавляешь громкости …

Внезапно выяснилось, что у этой машинки аж 4 динамика, которые почему-то подключены раздельно к “Наушникам” и “Колонкам”. Если поставить громкость одинаковую, то играет точно так же, как и под windows.

Все попытки собрать это в кучу пока завершились ничем. Поэтому написал маленький однострочник, который ставит громкость “Наушников” равной громкости “Колонок”.

amixer --quiet sset Headphone `amixer sget Speaker|grep 'Left:' | awk -F'[][]' '{ print $2 }'`

Да, сначала ставишь громкость, потом запускаешь … Костыль, но пока рецептов нет.

Для некоторых дистрибутивов может потребоваться добавить -c 0

#!/bin/bash
while :
do
	amixer -c 0 --quiet sset Headphone `amixer -c 0 sget Speaker|grep 'Left:' | awk -F'[][]' '{ print $2 }'`
	echo "Sound changed. Press [CTRL+C] to stop.."
	sleep 1
done

Вот такой вот скрипт можно засунуть в screen и временно забыть о регулировке звука.

Скорость работы VCP у STM32

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

Но разрабатывая очередное устройство у меня возникли смутные ощущения, что есть некие тормоза про общении с устройством. Решил проверить.

Для начала сгенерировал в STM32CubeMX пустой проект. В котором есть только USB и он определен как CDC.

Потом прямо в коде приема блока тут же его отправляю его назад. Кусок из usbd_cdc_if.c

static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
  /* USER CODE BEGIN 6 */
  USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
  CDC_Transmit_FS(&Buf[0], *Len);
  USBD_CDC_ReceivePacket(&hUsbDeviceFS);
  return (USBD_OK);
  /* USER CODE END 6 */
}

И написал маленькую программку на питоне, которая тупо спамит в порт увеличивающимися блоками и замеряет скорость. Можно взять тут https://github.com/kiltum/usb-rs485/blob/master/test/test/test.py

import time
import threading
import serial
# f042
#ser = serial.Serial(port='/dev/cu.usbmodem2058335047481')
# f303
ser = serial.Serial(port='/dev/cu.usbmodem2057385756311')

ser.isOpen()
# For windows
#ser.set_buffer_size(rx_size=262144, tx_size=262144)

bytesReceived = 0
minimalSpeed = 10000000
maximumSpeed = 0
counterStep = 0
blockSize = 1
shallExit = 0


def res():
    global bytesReceived
    global minimalSpeed
    global maximumSpeed
    global counterStep
    global blockSize
    global ser
    global shallExit

    if minimalSpeed > bytesReceived:
        if bytesReceived > 0:
            minimalSpeed = bytesReceived
    if maximumSpeed < bytesReceived:
        maximumSpeed = bytesReceived
    bytesReceived = 0

    counterStep = counterStep + 1
    if counterStep > 60:
        print("BlockSize:", blockSize, "Minimal:", minimalSpeed, "Maximum:", maximumSpeed,
              "Average:", round((minimalSpeed+maximumSpeed)/2048), "kb/s")
        with open("result.csv", "a") as myfile:
            myfile.write(str(blockSize) + "," + str(minimalSpeed) + "," + str(maximumSpeed) + "\n")
        ser.read(ser.inWaiting())
        counterStep = 0
        minimalSpeed = 100000000
        maximumSpeed = 0
        blockSize = blockSize * 2

    if shallExit == 0:
        threading.Timer(1, res).start()


with open("result.csv", "w") as myfile:
    myfile.close()

res()


while 1:
    if blockSize > 65536:
        shallExit = 1
        exit(0)
    s = "A" * blockSize
    b = s.encode()
    ser.write(b)
    bytesReceived = bytesReceived + ser.inWaiting()
    ser.read(ser.inWaiting())

Сильно я не заморачивался, поэтому указать нужный порт придется вам самим прямо в коде. “Человекочитаемые” программа пишет в консоль и попутно генерирует result.csv для импорта в excel или другую подобную программу

Под рукой у меня оказалось только два stm32 с usb: F042 и F303. Оранжевая линия это максимальная скорость, синяя – минимальная. Такие прыжки максимальной скорости вызваны буферизацией у всех участников процесса. Ну по крайней мере я сейчас так думаю.

Результаты довольно показательные. Как найду еще процессоров – попробую повторить. Но пока можно сказать, что не стоит использовать блоки больше 128-256 байт и можно надеяться на скорость не менее 200 килобайт в секунду.

UPDATE1: Добавил график от F779. Суть та же. Видимо, где-то прямо в коде CDC у stm большие проблемы

Windows 11 – альтернативная смена раскладки

Внезапно оказалось, что в Windows 11 (подозреваю, что и в windows 10) поддерживается два варианта смены раскладок. Первый и основной – это Win+Space комбинация. Корявая, но тем не менее.

Но нечаянно тут нажал и оказалось, что “старая” комбинация по умолчанию Alt+Shift тоже работает. Причем без дебильного всплывающего окна.

Что бы сменить на более каноничное (для меня) Ctlr+Shift надо сходить Settings – Time & language – Typing – Advanced keyboard settings – Input language hot keys. Откроется привычное по старым windows окно

Пока не понятно зачем это, но прикольно.

gitlab-runner lookup docker no such host

Жила-была репа в gitlab. Был в ней CI, был в ней и CD. Пользовалась репа шаренными раннерами от gitlab и успешно тратила кучу минут на сборку. Все было хорошо, пока число этих минут не стало расти угрожающими темпами. В общем, надо поднимать свой раннер и не один

Поначалу ничего этакого, все идет по инструкции

docker run --rm -it -v /srv/gitlab-runner/config:/etc/gitlab-runner gitlab/gitlab-runner register

docker run -d --name gitlab-runner --restart always -v /srv/gitlab-runner/config:/etc/gitlab-runner      -v /var/run/docker.sock:/var/run/docker.sock      gitlab/gitlab-runner:latest

Все пошло хорошо, пока не возникла ошибка

error during connect: Post http://docker:2375/v1.40/auth: dial tcp: lookup docker on x.x.x.x:53: no such host

Первым, что выдают рецепты из интернета, так это запустить докер в привелигированном режиме. Другие советы типа “пробрось docker.sock” уже учтены в инструкции гитлаба. Но даже будучи включенными – не помогают. Все равно докер ломится по tcp, полностью игнорируя сокет.

Погуглив еще немного, обнаружил, что если стоит переменная DOCKER_HOST, то все остальное игнорируется. Ок, значит надо сказать unset DOCKER_HOST перед выполнением. И вуаля! Вот пример рабочего config.toml. Значимые изменения от дефолтного я выделил

concurrent = 2
check_interval = 0

[session_server]
  session_timeout = 1800

[[runners]]
  name = "gitlab-runner-docker"
  url = "https://gitlab.com/"
  token = "TOKEN"
  executor = "docker"
  pre_build_script = "unset DOCKER_HOST"
  [runners.custom_build_dir]
  [runners.cache]
    [runners.cache.s3]
    [runners.cache.gcs]
    [runners.cache.azure]
  [runners.docker]
    tls_verify = false
    image = "docker:latest"
    privileged = true
    disable_entrypoint_overwrite = false
    oom_kill_disable = false
    disable_cache = false
    volumes = ["/cache", "/var/run/docker.sock:/var/run/docker.sock"]
    shm_size = 0

Как убрать лишние раскладки в Windows

Для установки Windows я использую International версию isoшки. С одной стороны привык, что все на английском, а с другой стороны если взять русскую, то потом замумукаешься русский выковыривать отовсюду. И с какой-то версии эта “интернациональная” версия по умолчанию ставит все от United Kingdom. В результате получается так

Лечится это двумя способами: либо в региональных настройках добавляем language pack от United Kingdom, в нем добавляем клавиатуры и потом их удаляем. Либо запуском regedt32 и открытием следующей ветки реестра

Computer\HKEY_CURRENT_USER\Keyboard Layout\Preload

Там видно такое

Последовательность записей совпадает с тем, что показывается при переключении языка. В моем случае грохаем последние две и перелогиниваемся. Результат

Знай свой cgroup

Давеча столкнулся с непонятным (для меня до сегодня) поведением cgroup. Изначально описание проблемы было очень информативным “сервер тормозит”.

Захожу я на сервер и вижу картину маслом:

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

Так как эта нода кубернетеса, то я посмотрел и на ограничения подов в /sys/fs/cgroup/memory/. Тоже все согласно описанному, везде memory.limit_in_bytes соответствуют нужному.

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

SUM=0
OVERALL=0
for DIR in `find /proc/ -maxdepth 1 -type d -regex "^/proc/[0-9]+"`
do
    PID=`echo $DIR | cut -d / -f 3`
    PROGNAME=`ps -p $PID -o comm --no-headers`
    for SWAP in `grep VmSwap $DIR/status 2>/dev/null | awk '{ print $2 }'`
    do
        let SUM=$SUM+$SWAP
    done
    if (( $SUM > 0 )); then
        echo "PID=$PID swapped $SUM KB ($PROGNAME)"
    fi
    let OVERALL=$OVERALL+$SUM
    SUM=0
done
echo "Overall swap used: $OVERALL KB"

Но скрипт выдал совершенно не совпадающие с системными утилитами значение. Согласно его выводу, своп использовался на 6 гигов. А я вижу на скриншоте выше – 12. Проверил выборочно значения из /proc – совпадают с высчитанными …

Ок, проверю вообще работу подсистему памяти. Набросал быстренько микропрограммку на С, которая раз в секунду сжирала гиг памяти. top честно показал сначала исчерпание free, потом окончание свопа. После пришел OOM и убил программку. Всё правильно, всё так и должно быть.

Долго я ломал голову и пробовал разные варианты. Пока в процессе очередного созерцания top внезапно глаз не зацепился за главного пожирателя памяти. Вернее за его показатель VIRT в 32 гига памяти. Так-то я смотрел на %MEM и RES. В общем, появился резонный вопрос “какого?”

Забрезжила идея, что что-то не так с cgroup. Ок, делаю группу с лимитом памяти в 10 гигов, проверяю, что memory.limit_in_bytes стоят, запускаю снова программку-пожиратель памяти … и вуаля! Через 10 секунд сожралось ровно 10 гигов RAM, и начал жраться своп. Вопрос “какого?” стал более актуальным

Начал гуглить. https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt говорит скромно

memory.memsw.usage_in_bytes # show current usage for memory+Swap (See 5.5 for details)

memory.limit_in_bytes # set/show limit of memory usage

memory.memsw.limit_in_bytes # set/show limit of memory+Swap usage

Про memsw я специально добавил. Но на этой машине Ubuntu 20.04 с cgroup V2 и параметра с memsw нет. Нахожу дальнейшим гуглежом https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html

The main argument for a combined memory+swap facility in the original cgroup design was that global or parental pressure would always be able to swap all anonymous memory of a child group, regardless of the child’s own (possibly untrusted) configuration. However, untrusted groups can sabotage swapping by other means – such as referencing its anonymous memory in a tight loop – and an admin can not assume full swappability when overcommitting untrusted jobs.

Особенно понравились слова про саботаж. То есть, докер ограничивал использование только RAM, но не SWAP. Теперь, когда проблема стала понятной, стало понятно, что и надо гуглить.

https://docs.docker.com/engine/install/linux-postinstall/#your-kernel-does-not-support-cgroup-swap-limit-capabilities

Грубо говоря, надо добавить в конфиг grub

GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount=1"

Но тут нода “боевая”, надо дать доработать сервису, поэтому просто увеличили лимиты для пода.

Как говорится, все “побежало и поскакало”

PS Про то, что на ноде с k8s не должно быть свопа я в курсе. Как и то, что начиная с версии 1.21 он поддерживается.