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
Всем привет.

Я тут с удивлением обнаружил, что в 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
Всем привет. Что мы все о 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
Свой S3 или пошаговая настройка Garage

Здрасти, здрасти. Теперь по выходным у меня полнейший «цифровой детокс», а причина этому — дача. Круче всякого спортзала прокачивает, а заодно прочищает голову. Физический труд всегда в почёте.

Ладно, это детали… сегодня будем поднимать собственный S3 и да, без хуйни вроде minio и ceph.

А на помощь к нам приходит «Гараж», в котором мы и будем хранить наши «Вёдра». Garage ориентирован на маленький и средние кластера, домашние сервера и распределенные ноды. И да, он полностью в opensource и не просит денег. Для self-host пиздатейшее решений, да и морда есть из коробки.

Я возьму 3 своих рандомных сервера и сделаю из них кластер. Сервера не пустые, на них что-то крутится и вертится, просто рядышком впендюрю еще одну хуёвинку.

Почему именно 3 сервера? Кластерная классика, если один из серверов пойдёт по пизде, то 2 других соберут кворум и отдадут тебе твои данные. То есть тебе не нужно иметь заранее подготовленные машины, можно взять какойнить хлам и из него запилить кластер под бекапы. И всё это дело будет работать нативно через API S3.

Да, когда ты будешь заливать файлы в своё облако, данные будут реплицироваться на все 3 сервера. Поэтому сервера рекомендую держать в разных регионах или вообще у разных провайдеров. Например, если сгорит один дата центр, то ты не проебешь свои данные. Удобно и надёжно. Ну и смотри в сторону дискового места, если на одном сервере выделил 50 гигов под бакет, то на остальных серверах выдели столько же, чтобы не возникало коллизий.


Прицепом воткнем балансировщик нагрузки, чтобы по домену всё работало. И да, запускать будем в докере.

Поехали настроим это безобразие.

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

Файл compose.yaml:

services:
garage:
image: dxflrs/garage:v2.3.0
ports:
- "3900:3900" # S3 API
- "3901:3901" # RPC
- "3902:3902" # Web (optional)
- "3903:3903" # Admin API
volumes:
- ./garage.toml:/etc/garage.toml:ro
- ./meta:/var/lib/garage/meta
- ./data:/var/lib/garage/data


Думаю, вопросов не должно возникнуть. Банальный композник, порты я тебе все подписал. Версию гаража можешь глянуть в интернете, на момент написания статьи последняя v2.3.0. Да буква «v» тут важна и latest тут не канает.

Читать продолжение: https://two.su/3qjfl

🛠 #devops #linux

💬 Bashdays 📲 MAX 🌐 LF 🔵 Blog
Please open Telegram to view this post
VIEW IN TELEGRAM
54
Такс, S3 кластер на Garage в предыдущем посте мы с тобой сообразили, самое время накрутить обвесов и сделать всё по взрослому. Как ты любишь — без хуйни.

Нежных сразу — нахуй, остальным велком.


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

Проверяем наш кластер:

docker exec -it garage-garage-1 /garage status


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

Устанавливаем haproxy:

apt install haproxy


Домен для кластера я буду использовать s3.linuxfactory.ru, соответственно во всяких rclone нужно будет прописать его в параметре endpoint.

Правим конфиг /etc/haproxy/haproxy.cfg:

global
log /dev/log local0
maxconn 4096

defaults
mode tcp
timeout connect 5s
timeout client 1m
timeout server 1m
option http-server-close
timeout http-request 10s
timeout queue 1m

frontend garage_front
bind *:443 ssl crt /etc/ssl/garage.pem
option http-buffer-request
default_backend garage_back

backend garage_back
balance roundrobin

server node1 95.123.123.116:3900 check
server node2 152.44.71.205:3900 check
server node3 145.83.116.20:3900 check


Проверяем валидность конфиги:

haproxy -c -f /etc/haproxy/haproxy.cfg


Приучи себя всегда это делать, будь то nginx либо что-то другое. Однажды это спасет твою жопу от пенетрации.


Ага, хуй там плавал. Давай создадим SSL сертификат. Я конечно предпочитаю angie, чтобы он сам это сделал, но у нас haproxy, поэтому придется немного пострадать...

Читать продолжение: https://two.su/aaahd

🛠 #devops #linux

💬 Bashdays 📲 MAX 🌐 LF 🔵 Blog
Please open Telegram to view this post
VIEW IN TELEGRAM
26
Относительная нумерация

Частенько народ интересуется, а почему у меня в виме такая странная нумерация. Да, этим вопросом и я в своё время задавался. Называется это «Относительная нумерация».

Главная идея здесь — быстрое перемещение по коду с клавиатуры.

Например, курсор стоит у меня в 27 строке. Если я сейчас выполню комбинацию: 6j, то курсор у меня встанет на строку: Не забудь подставить своё мыло. Такая нумерация показывает расстояние до строки от курсора, а не её абсолютный номер.

Аналогично работает и в других направлениях. В редакторе ZED это включается через конфиг:

relative_line_numbers": "enabled"


В виме же по классике в конфиге:

set number  
set relativenumber


Честно, если ты пользуешься мышкой, это бесполезная настройка. Для новичков это совсем неочевидная фича и возможно даже вредная. Короче у этой фичи целая философия:

➡️ Мышление расстояниями. В заурядной жизни, ты будешь думать — ага, мне бы на строку 15 попасть. А если ты адепт вима, твоё мышление такое — мне бы на 6 строчек ниже.

➡️ Ускорение всех count-команд. Тут не только клавиши hjkl задействованы, ты можешь выполнять ряд других операций. Например:

5dd — удалить 5 строк
3yy — скопировать 3 строки
4> — сдвинуть 4 строки вправо
2} — прыгнуть на 2 абзаца


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

➡️ Сильно уменьшает мысленную математику. Без — так… я на 120 строке, мне нужно на 133… это +13. С ней — вижу 13 → 13j.

Мозги по сути вообще ничего не считают. Это плюс. Думаешь не головой, а жопой.

➡️ Работает лучше с большими файлами. Когда у тебя файл на 1000+ строк, абсолютные номера создают шум, а относительные прям в тему. Ты ориентируешься вокруг курсора, а не во всём файле.

Минусы? Да конечно!

➡️ Поначалу выламывает тебя, перестраивает, больно
➡️ Плохо подходит для дебага с конкретными строками

В общем относительная нумерация, это не про цифры, она про то, чтобы перестать искать строки и моментально перепрыгивать в нужное место.

Если есть чё добавить, камон в комменты, будет интересно почитать.

🛠 #devops #linux

💬 Bashdays 📲 MAX 🌐 LF 🔵 Blog
Please open Telegram to view this post
VIEW IN TELEGRAM
44
Тайна вызова Less в Linux утилитах

Привет ребят. Ща покопаемся в неочевидных вещах, залезем так сказать под капот.

Знаешь же такую команду:

git log


Тут всё просто, она выводит историю коммитов. И что примечательно, в конце будет пагинация (pager). Смотри картинку 1.

А теперь пойдем неочевидной дорожкой и выполним:

sudo mv /usr/bin/less /usr/bin/less.bak 


Ну и снова запустим git log: Смотри картинку 2.

Хуяк и нет больше пагинации. Сразу вывалилась простыня. О чем это говорит? А говорит это о том, что git использует внешнюю утилиту less при выводе истории коммитов, причем делает это по умолчанию.

Но если less накрылся пиздой, git сразу вывалит простыню без пагинации. Забавно! Давай подключим strace и подтвердим этот факт, запускаем:

strace -f -e execve git log


И видим картину маслом: Смотри картинку 3

Ага, запускается less, а теперь снова перемещаем less и смотрим выхлоп strace: Смотри картинку 4

Вот и прекрасно, словили ошибку error: cannot run less: No such file or directory. Но ожидаемый результат все равно получили.

Если выполнить команду echo $PAGER, то увидим, какая утилита будет использоваться для пагинации. По умолчанию less, но раз есть переменная, её можно переопределить. Давай попробуем это сделать.

apt install lolcat
export PAGER="lolcat"
git log


Иииии ебать копать: Смотри картинку 5.

Кстати не только git завязан на переменную $PAGER, такое же поведение ты можешь увидеть у утилит: man, systemctl, journalctl и т.п. Всё что работает с пагинацией, использует переменную $PAGER.

Ну и на закуску:

export PAGER="vim -"
man ls


Команда man ls будет выполнена в vim. То есть получается что любая программа, доверяющая $PAGER, фактически запускает произвольную команду пользователя.

А это уже еще один вектор атаки из арсенала пентестеров, о котором никто не рассказывает. Так что бери на вооружение, глядишь сгодится при прохождении очередного HackBox’а.

🛠 #security #linux

💬 Bashdays 📲 MAX 🌐 LF 🔵 Blog
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
344
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
Здрасти мои хорошие, заранее прошу прощение за рекламные интеграции, был в отпуске, сейчас дача началась, короче время хватает только на проверку домашек. Но вкусных тем я уже понабрал, черновиков накидал, так что потихоньку буду возвращаться в рабочий ритм и радовать тебя всякой полезной хуйнёй.


Ну так вот. В очередной раз решил я с винды на линукс пересесть, потыкать Niri, да и винде уже 5 лет. Знатно засрана дерьмом, срок её подошел к логическому концу. Ну и вопрос встал ребром.

Все наработки и данные у меня в WSL на убунте. Логично что можно зайти в убунту и скопировать всё нужное в /mnt/c/backup, но это костыль, потому что будет потеря скорости, лишняя прокладка. Да, я честно пытался 40 гигов перетащить через copy и rsync, но прошло 2 часа и чет особо нихуя не продвинулось.

Ну не может же такого быть, должно всё нативненько бекапиться. Так и оказалось.

Заходим в powershell и выполняем:

wsl -l -v


Выведет список дистрибутивов, я работаю на Ubuntu 24.04, дальше делаю так:

wsl --shutdown
wsl --export "Ubuntu-24.04" D:\backup\wsl-ubuntu.tar


Жду 10 минут и получаю полноценный tar с полной копией файловой системы.

Ну великолепно же! Ну а дальше всё это я потом распакую и раскидаю уже по местам. Очередной пример того, что если не интересоваться новым и работать только с привычными инструментами, можно нехило деградировать, сидеть и ждать у моря погоды.

Знаю, будешь пытать меня в комментах насчет — а какой линукс ты поставил? Скажу так — пидорский. Но я только посмотреть, один раз не пидарас )

🛠 #linux #wsl #windows

💬 Bashdays 📲 MAX 🌐 LF 🔵 Blog
Please open Telegram to view this post
VIEW IN TELEGRAM
254
В прошлом посте мы с тобой перелопалити приоритеты чтения sshd_config, сегодня добъем нюансы. Меня вчера опередили в комментариях с Match, но не страшно.

Сюда по классике не влезло, много буков, поэтому всё в блоге.

Читать продолжение: https://two.su/atcbh

🛠 #linux #devops

💬 Bashdays 📲 MAX 🌐 LF 🔵 Blog
Please open Telegram to view this post
VIEW IN TELEGRAM
23
Всем привет. Вчера решал задачу фильтрации 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