Bash Days | Linux | DevOps
23.7K subscribers
225 photos
26 videos
776 links
Авторский блог от действующего девопса

Самобытно про разработку, devops, linux, скрипты, сисадминство, техдирство и за айтишную жизу.

Автор: Роман Шубин
Реклама: @maxgrue

MAX: https://max.ru/bashdays

Курс: @tormozilla_bot
Блог: https://bashdays.ru
Download Telegram
Синхронизируем настройки Pi-Hole между инстансами.

У меня в сети живет несколько нод с pi-hole, которые раскиданы по разным устройствам (proxmox, raspberry pi и т.п.). И сразу встала необходимость, чтобы все ноды с pi-hole имели одинаковые настройки.

Pi-hole — это сетевой DNS‑фильтр и блокировщик рекламы с открытым исходным кодом. Он работает как свой DNS‑сервер, перехватывает DNS‑запросы от устройств в локальной сети и блокирует домены из списков рекламы, трекеров и вредоносных сайтов, возвращая «пустой» ответ вместо IP‑адреса рекламного ресурса.


Стратегия такая, одна нода будет master, где производятся все основное настройки, затем все эти настройки раскатываются на другие ноды (slave).

Раньше такой кейс разруливали с помощью Orbital Sync, Nebula Sync и т.п. Но одно сдохло, другое работает через хуй-пизда-копыто. Короче нужно рабочее решение.

Пишем свой велосипед

#!/usr/bin/env bash

set -euo pipefail

PRIMARY_PIH_DIR="/etc/pihole"

SECONDARY_USER="root"
SECONDARY_PIH_DIR="/etc/pihole"
SECONDARY_HOSTS=(
"192.168.10.97"
"192.168.10.98"
"192.168.10.99"
)

RSYNC_EXCLUDES=(
"--exclude=pihole-FTL.db"
"--exclude=macvendor.db"
"--exclude=*.log"
)

echo "[pihole-sync] $(date): start"

for host in "${SECONDARY_HOSTS[@]}"; do
echo "[pihole-sync] ---- host ${host} ----"

rsync -az \
"${RSYNC_EXCLUDES[@]}" \
"${PRIMARY_PIH_DIR}/" \
"${SECONDARY_USER}@${host}:${SECONDARY_PIH_DIR}/"

echo "[pihole-sync] ${host}: restart dns"
ssh "${SECONDARY_USER}@${host}" "pihole restartdns >/dev/null 2>&1" || \
echo "[pihole-sync] ${host}: FAILED to restart dns"

done

echo "[pihole-sync] $(date): done"


В SECONDARY_HOSTS забиваем айпишники slave инстансов, этакий массив. На этом настройка скрипта закончена. Весь лишний мусор вроде логов и статистики синхронизироваться не будет.

Сохраняем, чмодим, кидаем в крон (а лучше в systemd с таймерами):

Про таймеры подробно писал тут и тут.


crontab -e
*/5 * * * * /usr/local/sbin/pihole-sync.sh >> /var/log/pihole-sync.log 2>&1


Не забываем прокинуть ssh ключи с master на slave, чтобы скрипт не уперся рогом в логин и пароль.

Вроде мелочь, а полезная, да еще и на bash. Хороших тебе предстоящих выходных и береги себя!

🛠 #bash #linux #devops #selfhosting

💬 Bashdays 📲 MAX 🌐 LF 🔵 Blog
Please open Telegram to view this post
VIEW IN TELEGRAM
532
В последних двух моих статьях я рассказывал о том, как я перешел с pfSense на opnSense и попытался заменить arpwatchd самописным скриптом. Здесь я расскажу, с какими «проблемами» пришлось столкнуться.

🔤🔤🔤🔤🔤🔤🔤

Первая статья: https://xn--r1a.website/bashdays/1356
Вторая статья: https://xn--r1a.website/bashdays/1360


Началось с того, что я тупанул. Я знал, что pfSense и opnSense основаны на BSD, и знал, что обычно там отсутствует bash.

И почему-то подумал, что придется писать скрипт на csh (tsch). Я не знаю csh, но принципиальных отличий нет.

Ну, скрипт и скрипт. Небольшие отличия в синтаксисе. if и while присутствуют, значит проблем нет. Это действительно так. Самой большой проблемой стал перенос длинных строк даже в константах:

#!/bin/bash
echo '1
2
3'

#!/bin/csh
echo '1 \
2 \
3'


Это делает однострочники (в моем случае awk) крайне уродливыми. Когда скрипт уже работал, я решил проверить, какие там есть оболочки cat /etc/shells , и с изумлением обнаружил /bin/sh.

Ладно, потратил лишних пару дней, но приобрел опыт. Винить некого. Но плюс в том, что если знаешь bash - в случае крайней необходимости можешь написать скрипт и для другой оболочки.

Были небольшие затыки с программами. Вроде date, как date, но ключики немного не совпадают. Я думал там gawk, но нет, awk. (не нашел разделы BEGINFILE{...} ENDFILE{...}). Но, при большом желании можно обойтись и без них.

Что порадовало, так это наличие man в opnSense. В pfSense его не было. И это очень круто.

Очень жаль, что не нашел ramdisk /dev/shm (в Linux он обычно есть и под него выделено половину оперативки).

Ну, и еще одной проблемой стал редактор vi, который оказался единственным в системе!!!

ed, red, sed не в счет, хотя, если припрет... В pfSense был еще nano.

Я в последнее время использую vim (который начал изучать после рекомендаций Дмитрия Малинина здесь, на BashDays. За что ему спасибо.) Кто еще не начал - ставьте vim, и запускайте vimtutor. Этого будет достаточно, чтобы как-то работать в vi и vim.

Вывод из всего этого - nix это круто. И не важно - Linux или BSD. Они очень похожи, по крайней мере, если знаешь что-то одно - разобраться не проблема. Принципы одинаковые.

Холиваров прошу не устраивать. Просто решил поделиться опытом. Если где-то ошибся - поправьте.

🛠 #networks #linux #bash

💬 Bashdays 📲 MAX 🌐 LF 🔵 Blog
Please open Telegram to view this post
VIEW IN TELEGRAM
42
Фарш обратно не провернёшь

Порой в Bash поделках требуется создать какой-нибудь секрет, для дальнейшей его передачи в CI/CD или куда-то еще. Ежу понятно, это можно сделать «в лоб», но мыж с тобой не волки позорные, поэтому давай сделаем это по всем правилам DevSecOps Best Practice. (* лучшие практики безопасной разработки).

Задача: Создать безопасно секрет, чтобы его не спиздили (например из свапа при форензике).

Форензика — это направление информационной безопасности, связанное с анализом и восстановлением данных для расследования инцидентов


Кто-то извращается со shred и т.п. утилитами, но это избыточно, да и на SSD, Btrfs, ZFS и journal FS оно будет работать хуева и не гарантирует физическое уничтожение данных. Лучше на диск вообще ничего не писать.

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

#!/usr/bin/env bash

set -euo pipefail

SECRET="$(openssl rand -base64 32)"

printf '%s' "$SECRET" | \
openssl enc -aes-256-cbc -salt -pbkdf2 -iter 200000 \
-in input.txt \
-out output.enc \
-pass stdin

unset SECRET


Что тут происходит?

Ничего необычного, генерим 32 случайных байта, кодируем в base64, получаем пароль высокой энтропии, затем через printf передаем пароль (без \n) через pipe в stdin.

Ну и в конце скармливаем какой-нибудь утилите или в CI/CD, куда нужно передать секрет, в моем случае я передал его в openssl.

По итогу секрет, не попадает в history, не лежит на диске и временно находится в памяти. Но важно понимать, что после unset SECRET переменная удаляется только из таблицы переменных, в памяти эта переменная может по-прежнему храниться и быть уязвима к форензике. Поэтому носи это в голове и по возможности перезатирай память например тем же stress.

А еще оно может попасть в swap и это еще хуже, swap это прям как неочищенная «корзина» с удаленными файлами.

На bash этот момент описать наверное не получится, поэтому покажу как сделать на Сиськах. Будем использовать mlock().

#include <sys/mman.h>
#include <string.h>
#include <stdio.h>

int main() {
char secret[32] = "super_secret_password";

if (mlock(secret, sizeof(secret)) != 0) {
perror("mlock failed");
return 1;
}

printf("Secret in locked memory\n");

// Используем секрет...

memset(secret, 0, sizeof(secret)); // затираем
munlock(secret, sizeof(secret));

return 0;
}


Когда этот код будет вызывать mlock(), указанный диапазон памяти закрепляется в RAM и ядро не имеет права выгружать его в swap.

Получается что сначала mlock() блокирует участок памяти, затирает memset() её перед освобождением и с помощью munlock() снимает блокировку.

Кстати mlock() используют GnuPG, OpenSSH, HashiVault, KeePassXX. Так что вариант надежный, можешь не сомневаться. А еще иногда используется mlockall():

mlockall(MCL_CURRENT | MCL_FUTURE);


Блокируется вся текущая и будущая память процесса, активно применяется демонами в HashiVault.

Такие дела.

Накидай еще своих вариантов в комменты, будет интересно ознакомиться.

🛠 #dev #security #bash

💬 Bashdays 📲 MAX 🌐 LF 🔵 Blog
Please open Telegram to view this post
VIEW IN TELEGRAM
155
Всем привет. Здесь я уже останавливался на sftp. Немного заострю внимание на особенностях применения в скриптах.

🔤🔤🔤🔤🔤🔤🔤

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

Обычно для обработки в скрипте используют heredoc, но есть и другой путь, через конвейер.

echo -e "ls\\ncd /tmp\\nget 123.txt\\nbye"|sftp -i keyfile user@host:/path/to/dir


Здесь \\n - это код перевода строки. Не красиво, но работает.

Вот тут есть небольшая тонкость, которую хотелось бы пояснить.

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

Для решения проблемы есть специальный ключ -b (batch mode) который позволяет читать sftp-скрипт из файла. Чтобы (как в нашем случае) читать stdin, нужно указать -b- , или совсем конкретно -b "/dev/stdin".

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

Но и здесь есть исключения. Если команду указать с префиксом "-", то она переходит в разряд опциональных, и в случае ошибки, sftp-скрипт будет продолжен. Например -get 123.txt. Кроме этого префикса есть еще префикс "@", который подавляет печать(вывод) команды при выполнении. Префиксы равнозначны, могут использоваться в любой последовательности.

Знания почерпнуты из man, при попытке перевести обмен между 1c с ftp на sftp. Если тема интересна, могу привести рабочий скрипт обмена, и рассказать, почему я от него в итоге отказался.

man sftp


Всем кода без багов.

🛠 #linux #bash

💬 Bashdays 📲 MAX 🌐 LF 🔵 Blog
Please open Telegram to view this post
VIEW IN TELEGRAM
40
15 функций Bash, которые стоит добавить в .bashrc

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

Создать директорию и сразу в неё перейти

mkcd() {
mkdir -p "$1" && cd "$1"
}


Подняться на несколько уровней вверх

up() {
local d=""
for ((i=1;i<=$1;i++)); do
d+="../"
done
cd "$d"
}


Быстро найти файл

ff() {
find . -type f -iname "*$1*"
}


Найти директорию

fd() {
find . -type d -iname "*$1*"
}


Найти процесс

psg() {
ps aux | grep -i "$1" | grep -v grep
}


Посмотреть последние команды

h() {
history | tail -n "$1"
}


Поиск по истории

hg() {
history | grep "$1"
}


Узнать размер директории

dirsize() {
du -sh "$1"
}


Универсальная распаковка архивов

extract() {
if [ -f "$1" ]; then
case "$1" in
*.tar.bz2) tar xjf "$1" ;;
*.tar.gz) tar xzf "$1" ;;
*.bz2) bunzip2 "$1" ;;
*.rar) unrar x "$1" ;;
*.gz) gunzip "$1" ;;
*.tar) tar xf "$1" ;;
*.tbz2) tar xjf "$1" ;;
*.tgz) tar xzf "$1" ;;
*.zip) unzip "$1" ;;
*.7z) 7z x "$1" ;;
*) echo "unknown archive" ;;
esac
fi
}


Быстрый HTTP-сервер из текущей папки

serve() {
python3 -m http.server "${1:-8000}"
}


Узнать свой внешний IP

myip() {
curl -s ifconfig.me
}


Узнать IP домена

ipinfo() {
dig +short "$1"
}


Показать открытые порты

ports() {
ss -tuln
}


Полная очистка терминала

cls() {
clear && printf '\e[3J'
}


Безопасный rm

rm() {
ls -FCsd -- "$@"
read -p 'Delete? [y/N] ' ans
if [ "$ans" = "y" ]; then
command rm -rf -- "$@"
fi
}


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

The end. Кидай в комменты, какие функции используешь ты, будет полезно.

Источник: https://boreal.social/post/15-practical-bash-functions-i-use-in-my-bashrc (обсуждения на реддите)

🛠 #bash

💬 Bashdays 📲 MAX 🌐 LF 🔵 Blog
Please open Telegram to view this post
VIEW IN TELEGRAM
8121
Всем привет.

Я тут с удивлением обнаружил, что в debian 13 исчезли команды last и lastb которые позволяли смотреть удачные/неудачные попытки ssh/sftp

🔤🔤🔤🔤🔤🔤🔤

А я к ним так привык... journalctl — прекрасен, но лазить по логам — так себе занятие.

Решил немного упростить, чтобы вывод был в виде таблички: timestamp ip login

Иногда мальчиши-плохиши подсовывают «пустого» пользователя " ", и чтобы число полей было всегда три, я заменяю его на ":", используется в качестве разделителя /etc/passwd, и поэтому такого логина не должно быть.

journalctl --output=short-unix --unit=ssh |
gawk '{match($0,/.*for( invalid user)? +(.*?) +from (([0-9]{1,3}[.]){3}[0-9]{1,3})/,a)
#^ это одна длинная строка
if(a[2]==""){a[2]=":"} # заменили "пробельного usera" на ":"
if(a[3]!="") print $1, a[3],a[2]}'



#Отображение только неудачных попыток
journalctl --output=short-unix --unit=ssh |
gawk '/error: maximum/{match($0,/.*for( invalid user)? +(.*?) +from (([0-9]{1,3}[.]){3}[0-9]{1,3})/,a)
#^это одна длинная строка
if(a[2]==""){a[2]=":"} # заменили "пробельного usera" на ":"
if(a[3]!="") print $1, a[3],a[2]}'


Если нужно ограничить диапазон дат можно использовать ключ

--since="@TIMESTAMP"

где TIMESTAMP дата вида 1773848322.767923 или 1773848322 или просто --since="-1 day"

Если не нравится дата в timestamp замените последнюю строчку на:

if(a[3]!="") print strftime("%Y%m%d_%H%M%S",$1), a[3],a[2]}'


Или какой-нибудь свой формат. Я, вообще не спец по regexp, поэтому, если кто-нибудь поможет ускорить выражение — буду благодарен.

Всем кода без багов.

🛠 #bash #linux

💬 Bashdays 📲 MAX 🌐 LF 🔵 Blog
Please open Telegram to view this post
VIEW IN TELEGRAM
844
А какие способы хитрой подмены файлов помимо mount --bind ты знаешь?

echo first >first.txt
echo second >second.txt
ln -s first.txt second.txt

# Выведет: ln: cannot create symbolic link from 'first.txt' to 'second.txt': File exists

mount --bind first.txt second.txt
# А так работает.

cat second.txt
#Выведет first.


Наверное можно еще через cgroups замутить, или через eBPF понаделать хуки на системный вызов open, openat, туда же mount overlay...

🛠 #shitcode #bash

💬 Bashdays 📲 MAX 🌐 LF 🔵 Blog
Please open Telegram to view this post
VIEW IN TELEGRAM
24
Всем привет. Что мы все о bash да и bash. Надоело. Сегодня поговорим про awk.

🔤🔤🔤🔤🔤🔤🔤

Не много осталось админов, которые парсят текстовые логи. Вся молодежь и даже пОдростки перешли на JSON. Ну да, это удобно. Но иногда awk быстрее. Особенно на больших файлах и файлах и простых фильтрах.

Но я сегодня не в настроении устраивать холивар.

При использовании логов очень часто возникает проблема — очень большое количество полей. А awk зачастую использует именно номер поля.

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

Сам скрипт:

#!/bin/awk -f
BEGIN{printf "INPUT field separator "
getline
FS=$0
printf "INPUT test line\n"
getline
printf "\n\n"
for (i=1;i<=NF;i++)print i, $i
}


Как обычно сохраняем fields.awk и делаем исполняемым:

chmod +x fields.awk
./fields.awk


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

1. Сначала вводим разделитель.
2. Затем строчку лога и получаем разбивку по полям.

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

Рассмотрим такой пример:

<134>1 2026-04-17T16:51:47+03:00 OPNsense.internal filterlog 18074 - [meta sequenceId="2054702"] 111,,,4b75111111111111111111111111cb1d,eno1,match,block,in,4,0x0,,64,27367,0,DF,6,tcp,60,192.168.2.125,192.168.7.14,60248,22,0,S,1539242113,,65535,,mss;sackOK;TS;nop;wscale


В этом случае, если нужно одновременно вытащить и дату и IP с портами, проще в качестве основного разделителя использовать ",", а потом первое поле:

<134>1 2026-04-17T16:51:47+03:00 OPNsense.internal filterlog 18074 - [meta sequenceId="2054702"] 111


Здесь, если мы разбить по пробелам (split($1,f," ")), то f[2] будет содержать дату. Которую в свою очередь можно путем нехитрых манипуляций превратить в unuxtime, для удобства машинной обработки.

Всем кода без багов.

🛠 #bash #linux

💬 Bashdays 📲 MAX 🌐 LF 🔵 Blog
Please open Telegram to view this post
VIEW IN TELEGRAM
142
Less и секретный режим мониторинга

Очередные консольные приколы о которых ты не знаешь. Есть такая красота:

tail -f /var/log/nginx/access.log


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

НО есть аналог:

less +F /var/log/nginx/access.log


А если кто не знал, то в less можно пользоваться клавишами vim. Например, навигация hjkl.

Сразу видно, что утилиты затачивали под одну экосистему и unixway. Ну а теперь вкуснятина.

После выполнения команды less +F нажимаем CTRL+C и теперь можно передвигаться по выводу как раз теме же клавишами hjkl, что-то поискать /127.0.0.1, g/G, n/N.

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


Ну а чтобы вернуться в режим наблюдения, нажимаем SHIFT+F и less возвращает режим мониторинга, строчки с логами продолжают бежать в реальном времени.

Так что less это не просто про пагинацию, а немного больше.

🛠 #bash #linux

💬 Bashdays 📲 MAX 🌐 LF 🔵 Blog
Please open Telegram to view this post
VIEW IN TELEGRAM
94
Всем привет. Сегодня хочу поговорить о культуре написания условий bash. Точнее, об использовании отрицаний (НЕ).

🔤🔤🔤🔤🔤🔤🔤

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

if [[ ! -f "$file" || ! -r "$file" || ! -w "$file" ]];then
echo "Ошибка: Файл отсутствует или нет прав на чтение/запись"
# exit 1
fi


Расшифруем:

! -f "$file" - НЕ регулярный файл (возможно каталог, устройство, или вообще ничего).
! -r "$file" - у файла НЕт прав на чтение.
! -w "$file" - у файла НЕт прав на запись.

Условия объединены по ИЛИ, значит если выполнится какое-либо - будет ошибка.

С точки зрения логики - все правильно, но у меня лично в глазах рябит от количества палок.

👆На мой взгляд, отрицания сильно осложняют понимание.

Вспоминаем, что:

! A || ! B эквивалентно ! [ A && B ]
! A && ! B эквивалентно ! [ A || B ]

👆И помним, что пробелы в условиях bash критически важны.

Таким образом, конструкцию можно переписать, с сохранением функциональности:

if ! [[ -f "$file" && -r "$file" && -w "$file" ]];then
echo "Ошибка: Файл отсутствует или нет прав на чтение/запись"
# exit 1
fi


Расшифруем:

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

if [[ -f "$file" && -r "$file" && -w "$file" ]];then
:
else
echo "Ошибка: Файл отсутствует или нет прав на чтение/запись"
# exit 1
fi


Здесь отрицание заменено переносом тела if в тело оператора else.

":" необходимо, потому что между if и else должен быть хотя бы один оператор (в данном случае ":" эквивалент оператора true или nop.

Можно записать и:

if [[ -f "$file" && -r "$file" && -w "$file" ]];then :
else
echo "Ошибка: Файл отсутствует или нет прав на чтение/запись"
# exit 1
fi


👆 Обратите внимание на ":" после then

Но на мой взгляд, в данном случае, читаемость несколько ухудшится.

Хотя первая конструкции и остальные конструкции эквивалентны, но отличие все же есть - скорость работы (Особенно, если в качестве операндов будут использованы функции) И зависит эта скорость будет от сценария работы.

Если при объединении по ИЛИ первый операнд ИСТИНА, то остальные даже проверяться не будут.

При объединении по И все наоборот: остальные проверяться не будут - если первый операнд ЛОЖЬ.

Всем работы без багов.

🛠 #bash #linux

💬 Bashdays 📲 MAX 🌐 LF 🔵 Blog
Please open Telegram to view this post
VIEW IN TELEGRAM
250
Тут ёбнутая тема пошла, чтобы вы могли ходить на нашу апиху, нужно установить сертификаты от Минцифры.

Я долго это игнорил, пока всё к херам не сломалось.

Ошибка: cURL: SSL certificate problem: self-signed certificate in certificate chain


Да, в голову сразу пришло решение — ёбнуть SSL Ignore. Но это костыль и безопасники в жопу выебут.

Хули, пришлось разбираться. Как оказалось Angie и nginx трогать не надо и всё решается на уровне дистрибутива. У меня убунты старенькие торчат в продах, поэтому затачивал под них. Но смысл не меняется и можешь подкрутить под свои дистрибутивы.

Чтобы 100500 команд не вводить, сделал всё одной командой, запускаешь из консольки и всё сразу работает, без костылей и нападок на твой задний проход со стороны ИБешкников. Даже в скрипт сохранять не нужно.

Установка РФ-сертификатов:

(
set -e

cert_tmp="$(mktemp -d)"
trap 'rm -rf "$cert_tmp"' EXIT

curl -fsSL \
https://gu-st.ru/content/Other/doc/russian_trusted_root_ca.cer \
-o "$cert_tmp/root.crt"

curl -fsSL \
https://gu-st.ru/content/Other/doc/russian_trusted_sub_ca.cer \
-o "$cert_tmp/sub.crt"

openssl x509 -in "$cert_tmp/root.crt" -noout -subject |
grep -Fq "Russian Trusted Root CA"

openssl x509 -in "$cert_tmp/sub.crt" -noout -subject |
grep -Fq "Russian Trusted Sub CA"

root_fingerprint="$(
openssl x509 -in "$cert_tmp/root.crt" -noout -fingerprint -sha1 |
cut -d= -f2 |
tr -d ':'
)"

test "$root_fingerprint" = "8FF915CCAB7BC16F8C5C8099D53E0E115B3AEC2F"

openssl verify \
-CAfile "$cert_tmp/root.crt" \
"$cert_tmp/sub.crt"

install -m 0644 "$cert_tmp/root.crt" \
/usr/local/share/ca-certificates/russian_trusted_root_ca.crt

install -m 0644 "$cert_tmp/sub.crt" \
/usr/local/share/ca-certificates/russian_trusted_sub_ca.crt

update-ca-certificates

curl -fsSvo /dev/null https://business.t-bank-app.ru/
)


Ну и всё, если в куче мусора увидел — SSL certificate verify ok, значит всё прошло успешно. Перезапускаешь свои пыхи или чё там у тебя и можно дальше не париться.

Такие дела. Изучай.

🛠 #devops #security #bash

💬 Bashdays 📲 MAX 🌐 LF 🔵 Blog
Please open Telegram to view this post
VIEW IN TELEGRAM
388
Всем привет. Вчера решал задачу фильтрации txt-файла и нашёл конструкцию на awk, от которой испытал эстетический оргазм. Решил с вами ею поделиться.

Итак, задача: есть _data.txt_ с данными и _filter.txt_ со списком фильтрации.

Строки data.txt обрабатываются, если есть совпадение в filter.txt. Это классическая задача фильтрации (например, обработка белых и чёрных списков).

И да, я знаю про программы comm и join, с помощью которых можно решать подобные вещи, но мне нравится awk, потому что делает это гибче благодаря наличию языка программирования и не требует предсортировки.

👍 Итак, сама конструкция:


awk 'NR == FNR { filter[$0] = 1; next } $0 in filter { print $0, "in filter" }' filter.txt data.txt


Обратите внимание: здесь два блока обработки строк, каждый со своим условием

1. NR == FNR { filter[$0] = 1; next }
2. $0 in filter { print $0, "in filter" }

Обычно выполняются оба блока: сначала первый, потом второй, но здесь это не так. Первый блок только считывает данные filter.txt в массив filter.

Второй обрабатывает только те строки, которые есть в массиве.

NR — встроенная переменная, номер записи ДЛЯ ВСЕХ файлов.
FNR — аналогична предыдущей, но начинается с 1 для каждого нового файла.
filter[$0] = 1 — сохраняем всю строку первого файла как ключ массива.
next — сразу переходит к обработке следующей строки.

Таким образом, связка условия NR == FNR и next будет работать только для первого файла (filter.txt).

Для второго файла условие NR == FNR уже не соблюдается, поэтому первый блок не выполняется, а второй выполняется, если строка данных присутствует в массиве filter.

Ну и пример из реальной жизни — filter.txt:

RU
BY


data.txt

RU 127.0.0.1
BY 127.0.0.2
US 127.0.0.3
KZ 127.0.0.4


awk 'NR == FNR { filter[$1] = 1; next } $1 in filter { print $2, "in filter" }' filter.txt data.txt

127.0.0.1 in filter
127.0.0.2 in filter


Обратите внимание: на выходе только нужные строки и только нужный столбец. Как же это круто!

Маленькое ограничение: содержимое filter.txt, загруженное в массив filter, должно помещаться в оперативную память. Иначе система может начать активно использовать swap, а процесс — завершиться из-за нехватки памяти.

man awk
man comm
man join


Всем работы без багов.

© Tagd Tagd

🛠 #devops #bash #linux

💬 Bashdays 📲 MAX 🌐 LF 🔵 Blog
Please open Telegram to view this post
VIEW IN TELEGRAM
67