Bash Days | Linux | DevOps
23.3K subscribers
156 photos
24 videos
678 links
Авторский канал от действующего девопса

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

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

MAX: https://max.ru/bashdays

Курс: @tormozilla_bot
Блог: https://bashdays.ru
Download Telegram
Если ты выполняешь план, то тебе повысят план, а не зарплату!

Пытался вчера подружить taskwarrior и s3 для синхронизации, из коробки оно вроде как только с AWS работает.

Подкинул в конфиг параметры подключение к кастомному хранилищу. Ну думаю, какая в хуй разница.

sync.backend=s3
sync.aws.region=ru-7
sync.aws.bucket=taskwarrior
sync.aws.access_key_id=aed38518013b4ab
sync.aws.secret_access_key=992570bad57
sync.aws.endpoint=s3.ru-7.storage.selcloud.ru


Проверяю task sync init, хуй там плавал, ошибка:

unhandled error: dispatch failure: io error: error trying to connect: dns error: failed to lookup address information: Name or service not known: dns error: failed to lookup address information: Name or service not known: failed to lookup address information: Name or service not known


Мде… Всёж правильно, эндпоинт пингуется, курлится, телнетится. Описывать весь момент дебага не буду, но там конкретный такой - метод тыка был.

Ну раз обычный «метод тыка» не помогает, расчехляем strace!

strace -s 200 -f -e trace=network,connect,sendto,recvfrom task sync


Что делает команда:

-s 200 — печатать до 200 байт строковых аргументов (по умолчанию strace режет строки до 32 байт). Это важно, чтобы увидеть полный URL/hostname, который передаётся в syscalls.

-f — следить не только за основным процессом, но и за всеми дочерними (fork/clone)

-e trace=network, connect, sendto, recvfrom — ограничиваем вывод только сетевыми вызовами: socket, connect → создание сокетов и подключения (TCP/UDP). sendto / recvfrom → передача данных (обычно видно DNS-запросы, HTTP-заголовки и т.д.).

И в выхлопе находим строчку: taskwarrior.s3.ru-7.amazonaws.com.

😀😃😄😁😅😂🤣😊
😇🙂🙃😉😌😍🥰😘
😗😙😚😋😛😝😜🤪
🤨🧐🤓😎🤩🥳😏😒
😞😔😟😕🙁☹️😣😖
😫😩🥺😢😭😤😠😡

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

То есть настройка sync.aws.endpoint=… вообще не учитывается — клиент жёстко строит URL по схеме AWS.

Ну хоть проблему нашли. Strace все же достаточно пиздатый инструмент.

Отсюда вывод: с кастомным S3 напрямую taskwarrior работать не сможет. Даже если устроить подмену хостов или сделать хак через CNAME.

А как синхронизировать-то задачи? Ооо брат, я уже написал про это отдельный пост, чуть позже закину.

Хороших тебе выходных!

🛠 #strace #debug #taskwarrior

@bashdays @linuxfactory @blog
Please open Telegram to view this post
VIEW IN TELEGRAM
1357
Дегенеративные модели

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

ВЕЧНЫЕ СЕРВЕРА!

Как работает эта модель — ты один раз оплачиваешь условные 10-30-50-100к рублей и получаешь VPS навсегда, до конца мать его жизни.

Звучит достаточно вкусно, но в каждой бочке меда есть ложка дегтя.

Вечного ничего не бывает.

Ну так, вот:

Покупая такой сервер, сервер не становится твоей собственностью. Ты по-прежнему выполняешь условия оферты, которую ты конечно же НЕ прочитал и просто протыкал далее → далее.

Открываем оферту и читаем:

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


Первый звоночек.

Я сталкивался с такой ситуацией в молодости и по глупости купил такой «вечный сервер» за 10к рублей. А через полгода с меня начали требовать абонентскую плату, а на мой ахуй мне было сказано — а вы оферту читали? Какие вопросы?

Пошел, почитал, а там черным по белому — извините, теперь ваш «вечный сервер» требует ежемесячной оплаты, потому что мы охуели и хотим больше денег.

Ладно, опыт получен. Прежде чем что-то бездумно покупать, вопрос нужно хотя бы немного заресерчить. И в 99% ты найдешь очень много подводных камней, о которых ты и не подозревал.

Второй звоночек.

Ладно, ты купил «вечный сервер» и потом хуяк! Скорость стала никакой. Идем в саппорт и требуем объяснений, в ответ получаем — ну дак в оферте же прописано.

При превышении 3ТБ трафика в месяц, для «вечных серверов», скорость пропускной полосы будет снижена до dialup’а. Вы превысили допустимы трафик, поэтому идете — нахуй и сосите жопу!

ВОТ ЭТО ПОВОРОТ!

😀😃😄😁😅😂🤣😊
😇🙂🙃😉😌😍🥰😘
😗😙😚😋😛😝😜🤪
🤨🧐🤓😎🤩🥳😏😒
😞😔😟😕🙁☹️😣😖
😫😩🥺😢😭😤😠😡

Третий звоночек.

Компания просто перестает существовать, либо ее банят за махинации и черные схемы. Всякое бывает. В этом случае ты просто теряешь доступ к своему «вечному серверу». Истории такие были, по крайней мере знаю две и оба раза я лишался доступа к серверам, благо были бэкапы.

Четвертый звоночек.

Для саппорта ты не приоритетный клиент, ты лох ебаный, которого уже облапошили, хуль на тебя время тратить, сам ебись со своими проблемами и «вечным сервером».

Выводы:

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

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

Всех под одну гребенку не ровняю, возможно на рынке и есть добросовестные поставщики услуг, респект им и уважуха! Но в любом случае «третий звоночек» никто не отменяет. Фактически «вечность» держится только на репутации и доброй воле провайдера.

Предложение о «вечных серверах» живёт только в маркетинговых материалах, но юридической фиксации в оферте нет. Получается, что «вечность» основана исключительно на доверии к провайдеру.

Если сервер нужен надолго, то «окупается» он через 12-36 месяцев. Ресурсы у сервера фиксированные, например 1Гб RAM сегодня ещё куда-нибудь сгодится, но через 3–5 лет такой сервер может быть слишком слабым. А апгрейд невозможен.


Гарантий нет, это инвестиция в надежность чужого бизнеса.

Так что если тебе кто-то предлагает «вечный сервер» — шли его нахуй!

🛠 #рабочиебудни #hosting #server

@bashdays @linuxfactory @blog
Please open Telegram to view this post
VIEW IN TELEGRAM
1094
Я тут порой пишу для одного интернет журнала (не для хабра, в пизду его). Ну и встал вопрос, как передавать материалы заказчику, чтобы не в голом markdown, а уже сверстано, стили, картинки и т.п.

Вариантов было много, hugo, mkdocs и т.п. По итогу выбрал Writefreely, оно отлично подошло. Оупенсорц, написан на GO, ставится на свои сервера, работает шустро.

Аналог Телеграфа и Телетайпа, но на стероидах. Можно делать разные блоги под разные задачи. Есть черновики, доступ по паролю и т.п.

Короче доволен, базовые потребности закрыты, поднимается так:

services:
write-freely:
image: nephatrine/write-freely:latest
container_name: write-freely
environment:
TZ: America/New_York
PUID: 1000
PGID: 1000
ports:
- "127.0.0.1:8085:8080/tcp"
volumes:
- ./write-freely:/mnt/config


Способ не официальный. Потому что если делать, как в официальной документации, то как обычно — нихуя не работает из коробки. Классика!

Следующая проблема — картинки по ctrl+c/ctrl+v в writefreely не вставляются. Их нужно предварительно закончать и потом через ссылку вставлять. Дела…

По FTP я ебал это делать, поэтому был поднят Picsur.

Отличнейшее решение для любителей self-hosting. Аналог Imgur, но свой!

Запускается так:

services:
picsur:
image: ghcr.io/caramelfur/picsur:latest
container_name: picsur
ports:
- '127.0.0.1:8080:8080'
environment:
PICSUR_DB_HOST: picsur_postgres
volumes:
- ./storage:/picsur/storage
restart: unless-stopped
picsur_postgres:
image: postgres:17-alpine
container_name: picsur_postgres
environment:
POSTGRES_DB: picsur
POSTGRES_PASSWORD: picsur
POSTGRES_USER: picsur
restart: unless-stopped
volumes:
- picsur-data:/var/lib/postgresql/data
volumes:
picsur-data:


Тем более эта хератень отлично дружит со скриншотилкой ShareX которую я совсем недавно начал использовать.

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

В настройках Picsur генеришь json и двойным кликом интегрируешь с ShareX, ничего руками прописывать не нужно.


Как работает связка:


Делаешь скриншот, оно автоматически заливается в Picsur и в буфере обмена у тебя уже готовая ссылка для вставки в markdown. Удобно просто пиздец!

Такие дела. Вот и еще один полезный сервис в моей домашней лаборатории.

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

Кстати нашел ShareX под Linux, если кому надо.


🛠 #services #selfhosting #workflow

@bashdays @linuxfactory @blog
Please open Telegram to view this post
VIEW IN TELEGRAM
672
⚡️⚡️⚡️⚡️⚡️⚡️⚡️⚡️

У девопса нет стейджа, только прод!


Продолжаем тему с taskwarrior, сегодня настраиваем единый сервер синхронизации на движке Taskchampion.

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

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

➡️https://two.su/b9wmx


🛠 #рабочиебудни #gtd

@bashdays @linuxfactory @blog
Please open Telegram to view this post
VIEW IN TELEGRAM
230
Всех с пятницей!

Меня частенько и «косвенно» связывали с компанией Selectel, я молча читал, слушал... И однажды подумал — а хули нет? Мало мне 3х работ, возьму еще одну.

И теперь я их официальный амбассадор.

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

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

Еще раз всех с пятницей, береги себя и своих близких. Ну и хороших тебе предстоящих выходных! Ура блядь, дождались!

🍻🍻🍻🍻🍻
1594
Вчера пишу сотруднику — Алёша, у нас прод какого-то хера 500ку каждые 3 минуты выплёвывает, чё у вас там происходит?

В ответ я получаю — даже не «нихуя», а реакцию (эмоджи) на мой вопрос, тупо ебать большой палец вверх. Типа — заебись!

А че заебись? Вы взяли это в работу, или заебись и вы очень рады, или что нахуй вообще происходит? Как это блядь интерпретировать?

Немного охуев от такого, я понимаю что я делаю так же, даже не задумываясь.

А делаю я по причине — нахуя ты мне 40 сообщений написал подряд? Ты думаешь я это читать буду? Нет! Я просто поставлю тебе сука лайк и дальше пойду делами своими заниматься.

😀😃😄😁😅😂🤣😊
😇🙂🙃😉😌😍🥰😘
😗😙😚😋😛😝😜🤪
🤨🧐🤓😎🤩🥳😏😒
😞😔😟😕🙁☹️😣😖
😫😩🥺😢😭😤😠😡

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


Выдели главное и напиши в 140 символов.

Про «приветы» уже много раз обсуждалось, для них отдельный котел.


Давай разберемся когда «лайк» уместен.

Короткий ответ без флуда. Если человек пишет: «Встреча завтра в 11», и ты ставишь 👍 — это знак, что ты прочитал и согласен.

Эмоциональный отклик. «Спасибо, что помог!» — сердечко или смайлик даёт обратную связь, но не перегружает чат.

Командная культура. В некоторых компаниях реакциями заменяют лишние «ок», «понял», «согласен», и это экономит время.

Когда не уместен:

Личное обращение. Если коллега задаёт вопрос «Можешь помочь с этим?» и получает только реакцию, это может восприниматься как уход от диалога (послали нахуй).

Неясный контекст. Если сообщение неоднозначное («Нужно срочно переделать отчёт»), то реакция в виде 👍 может вызвать вопросы: «Это согласие? Или сарказм?»

Разные ожидания. Для кого-то реакция = полноценный ответ, а для кого-то — «отписался, лишь бы ничего не сказать» (это мой случай).

Ну так вот:

Если реакция однозначно отвечает на запрос (например, подтверждение), то это вполне корректно.

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

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

Можно сформулировать простое правило:

- Если сообщение требует только «понял/согласен/спасибо» — реакция окей;

- Если есть риск недопонимания или человек вложил усилие — лучше ответить словами.

Ну и повторюсь — не пиши 10-20-30 сообщений подряд, никто их читать не будет, сформулируй мысль одним коротким предложением и напиши.

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


Ладно, теперь твои мысли давай в комменты.

Как ты относишься к таким лайкам?

🛠 #рабочиебудни

@bashdays @linuxfactory @blog
Please open Telegram to view this post
VIEW IN TELEGRAM
579
Смотри какая лялька: ExplainShell

Сервис помогает понять, что делает shell команда и все её параметры и ключи. Вставляешь например команду из прошлого поста:

strace -s 200 -f -e trace=network,recvfrom task sync


И получаешь по ней полный разбор.

Работает достаточно просто, под капотом овер-дохуя ≈30к-man страниц. Штука оупенсорцная и лежит тут.

Логика работы:

1. Ман-страницы (разделы 1 и 8) загружаются и преобразуются в HTML.

2. Параграфы классифицируются – разделяются те, где описаны опции/флаги, и те, где нет.

3. Из отобранных параграфов извлекаются конкретные параметры и их описания.

4. Когда ты вводишь команду, она разбирается на синтаксическое дерево (AST) с помощью библиотеки bashlex.

5. Компоненты команды («узлы» AST) сопоставляются с параметрами, найденными в ман-страницах.

6. Отображаем на фронте.

Вообще штука довольно бесполезная, но в образовательных целях очень даже вменяемая. Как раз для персонажей которые нихуя не понимают как ориентироваться в man страницах.

Хотя, кого я обманываю, сейчас каждый первый загоняет непонятную команду в GPT и оно тебе всё по полочкам раскладывает. Да еще и на русском языке.


Ладно, глядишь сгодиться в хозяйстве.

🛠 #services #bash

@bashdays @linuxfactory @blog
Please open Telegram to view this post
VIEW IN TELEGRAM
270
Всегда делай хорошо, плохо само получится!

Сегодня я припёр тебе Vim Master. Херабора для тех, кто хочет приручить Vim. Это не скучный тутор, а что-то вроде игрульки.

16 уровней, челледжи на скорость, бейджики, всё по красоте. Устанавливать ничего не нужно, работает прям в браузере. Но если очень хочется, то оно с открытым исходным кодом, можно и себе воткнуть при желании…

Есть два режима: Cheat Mode для новичков и Challenge Mode для тех, у кого писька уже подросла.

Потыкай на досуге, освежи мышечную память.

🛠 #games #vim

@bashdays @linuxfactory @blog
Please open Telegram to view this post
VIEW IN TELEGRAM
159
Ищем баги с помощью strace

Предположим, крутится у тебя в проде какое-то приложение, это приложение было разработано криворукими обезьянами — на отъебись.

По итогу продакшен начинает троить и выжирать процессорное время. Хуита!

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

Запускаем:

strace -c python3 app.py


Через несколько секунд жмём Ctrl-C и получаем статистику:

% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ------
99.82 0.413251 8 49431 write
0.07 0.000291 32 9 mmap
0.05 0.000207 25 8 mprotect
0.03 0.000129 21 6 openat
0.02 0.000090 30 3 close
......


Хм… эта падла активно пользуется системным вызовом write().

time — процент процессорного времени, потраченного на вызов.
usecs/call — среднее время на один вызов (в микросекундах).
calls — сколько раз вызов был сделан.

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

Важно понимать: strace показывает только то время, которое ядро тратит на обработку системных вызовов. Поэтому значения могут отличаться от того, что покажет команда time:

$ time python3 app.py

real 0m7.412s
user 0m1.102s
sys 0m6.184s


Здесь sys совпадёт с тем, что мы видели через strace -c.

Ну и теперь даже без доступа к исходникам можно быстро понять, где «утекают» ресурсы.


Исходники у нас есть, давай посмотрим:

with open("tmp.txt", "w") as f:
while True:
f.write("Привет супчики! Привет от BashDays!")
f.flush()


Что тут не так:

Из-за flush() Python гонит строку сразу в файловую систему, без буферизации.

Как пофиксить:

# fixed.py
with open("tmp.txt", "w", buffering=1024*1024) as f:
while True:
f.write("Привет супчики! Привет от BashDays!\n")


Теперь данные будут сбрасывать пачками, так как мы указали буферизацию, которая равна 1MB.

Проверяем до фикса:

$ strace -c python3 app.py
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ------
99.8 0.413251 8 49431 write


Проверяем после фикса:

$ strace -c python3 app-fixed.py
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ------
98.9 0.072111 450 160 write


Количество вызовов write() резко сократилось, нагрузка на ядро упала.

Как костыль и быстрофикс — сойдёт! Повторюсь — мы с тобой не обезьяны, чтобы вникать в код разработчиков и что-то в нем «правильно» править.

В большинстве случаев, ты просто находишь проблемы и уже с этими данными создаешь задачу на разработчика. Сам в код не лезь, целее будешь.

Ну и на закуску фикс, который сделали разработчики:

import io

buffer = io.StringIO()

with open("tmp.txt", "w") as f:
while True:
buffer.write("Привет супчики! Привет от BashDays\n")
if buffer.tell() > 1024 * 1024:
f.write(buffer.getvalue())
f.flush()
buffer.seek(0)
buffer.truncate(0)


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

1. StringIO хранит текст в оперативной памяти.
2. Цикл гонит туда строки.
3. Когда накопится, например, 1 MB, содержимое сбрасывается в файл одной большой порцией (write + flush).
4. Буфер очищается и цикл продолжается.

Хуй знает на сколько это всё правильно, ну раз сделали через внутреннию буферизацию StringIO, значит так правильно.

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

🛠 #debug

@bashdays @linuxfactory @blog
Please open Telegram to view this post
VIEW IN TELEGRAM
10877
Интровертизм в голове.

Сегодня подкормленный HR поделился формулой успеха.

Формула: На собесе хотят услышать, а на работе хотят увидеть.

Не стоит забивать на софт-скилы, хоть ты там ебать 100500 овердохуя умный лоб. Учись пиздеть (разговаривать). Даже если ты там прожженный интроверт — учись пиздеть, даже если тебе это не нравится.

Без пиздежа, ничего не получится. Ладно, получится, но ты сразу упрешься в потолок без перспектив.

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

Прошел стадию с пиздежом, дальше у тебя будет — та самая, единственная задача на испытательном сроке. Которую никто не хочет делать. Которую не знают как сделать. А тебе придется это сделать!

А как её сделать? А ты придумай! Это твоя киллер-фича! Это твоё наследие (легаси). Если не можешь придумать — значит рановато пришел к этому боссу, откатись и качни свою жопку еще посильнее, откопай аммулет, высри бусину.

Короче как бы ты не хотел избегать людей, с ними придется общаться. Хотя бы для того, чтобы пройти HRа и задобрить его своим пиздежом.

Лично я терпеть не могу лично общаться, для меня целое испытание поехать поменять летнюю резину на зимнюю, либо кому-то позвонить и что-то узнать.

Со звонками ладно, делегировал, но есть вещи и дела где без тебя вопрос не решится. А если решится, то обязательно наебут.

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

Интровертизм это в голове.

🛠 #рабочиебудни

@bashdays @linuxfactory @blog
Please open Telegram to view this post
VIEW IN TELEGRAM
2685
Довольно часто нужно быстро загрузить файл построчно в массив.

Как делает мальчик:

lines=()
while IFS= read -r line; do
lines+=("$line")
done < file.txt

echo "Первая строка: ${lines[0]}"
echo "Всего строк: ${#lines[@]}"


Как делает мужчина:

mapfile -t lines < file.txt

echo "Первая строка: ${lines[0]}"
echo "Всего строк: ${#lines[@]}"


В первом варианте много кода, НО, работает везде. Во втором варианте, работает только в bash ≥4.0, но кода в разы меньше и не жрет CPU.

Совет: если пишешь скрипт под bash — всегда используй mapfile. Если нужен кросс-шелл (sh,dash,ash) — оставайся на цикле.


Либо расширь второй вариант и укажи:

#!/usr/bin/env bash


Это гарантирует, что твой скрипт выполнится именно через bash, а не через системный sh (который может быть dash, ash, ksh и т.п.).

env ищет bash в $PATH, так что это более переносимо, чем жёстко указывать #!/bin/bash

Ну и прицепом можешь добавить: set -euo pipefail

Это включение «строгого режима» в баше:

-e — выход из скрипта при любой ошибке (не игнорировать exit code ≠ 0).

-u — ошибка при обращении к неинициализированной переменной (не будет пустых значений «по-тихому»).

-o pipefail — пайплайны возвращают код ошибки первой упавшей команды, а не последней.

По итогу:

- Скрипт точно запустится под bash
- Ошибки не будут замалчиваться
- Сразу ловишь косяки

Удобно в CI/CD, где всё должно падать быстро и без хуйни.

grep foo file.txt | wc -l
echo $? # 0, даже если grep ничего не нашёл


set -o pipefail
grep foo file.txt | wc -l
echo $? # 1, потому что grep ушел по пизде


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

🛠 #bash

@bashdays @linuxfactory @blog
Please open Telegram to view this post
VIEW IN TELEGRAM
170
Ща темку тебе интересную покажу…

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

То есть для каждого нового файла, оно считает MD5 и ищет его в базе, если такого хеша нет, значит файл новый.


Звучит пиздец банально. Ну дак вот.

В какой-то момент ты обнаруживаешь 2 файла, с разным содержимым, но с одним и тем же хешем. Да ёб твоб мать! А как такое возможно?

Возможно!

MD5 ― это криптографическая хеш-функция, придуманная в 1991 году Рональдом Ривестом. Её задача — брать произвольные данные и сжимать их в 128-битное число (32 hex-символа).


Как эта поебота работает описывать не буду, если интересно погугли, там это уже на 100500 разжевали и в рот положили.

Вернемся к баранам:

Создаём пару текстовых файлов:

printf 'TEXTCOLLBYfGiJUETHQ4hAcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak' > a.txt

printf 'TEXTCOLLBYfGiJUETHQ4hEcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak' > b.txt


Содержимое отличается одним символом (22 символ): В a.txt это A (ASCII 65, 0x41), в b.txtE (ASCII 69, 0x45).

Проверим, что так оно и есть:

xxd -g 1 a.txt | sed -n '1,2p'
xxd -g 1 b.txt | sed -n '1,2p'


xxd создаёт представление указанного файла или данных, прочитанных из потока стандартного ввода, в виде шестнадцатеричных кодов.


Либо сразу выводим различия:

cmp -l a.txt b.txt

22 101 105


- 22 → позиция (22-й байт в файле)
- 101 (восьмеричное) → 0o101 = 65 (десятичное) = 0x41 = 'A'
- 105 (восьмеричное) → 0o105 = 69 (десятичное) = 0x45 = 'E'

cmp — утилита, которая сравнивает два файла побайтово и сообщает о первом отличии, если оно есть.


Ага, видим разницу.

Едем дальше, считаем MD5:

md5sum a.txt b.txt


И видим:

faad49866e9498fc1719f5289e7a0269  a.txt
faad49866e9498fc1719f5289e7a0269 b.txt


Вот это нихуясебе! Наверное md5sum гличнулся…

Проверяем по другому:

openssl dgst -md5 a.txt b.txt


MD5(a.txt)= faad49866e9498fc1719f5289e7a0269
MD5(b.txt)= faad49866e9498fc1719f5289e7a0269


Да нет, все правильно… Хеш одинаковый для разных файлов.

И что это всё значит?

Это наглядная коллизия и практическая атака на MD5 алгоритм.

На первый взгляд кажется, что «достаточно поменять один байт и можно случайно попасть в коллизию». Но на деле случайно это практически невозможно провернуть (вероятность ≈ 1 к 2^128).

То, что я показал в примере с AE — это синтетическая пара, созданная криптографами специально с использованием дифференциального криптоанализа MD5.

То есть кто-то заранее просчитал, какой именно байт нужно поменять, чтобы скомпенсировать все внутренние изменения в состояниях MD5.

Практическая атака (2004–2008)

- В 2004 году Ван Сяоюнь с коллегами показали первую реальную коллизию для MD5 за считанные часы.
- В 2008 году был создан поддельный сертификат SSL с использованием MD5-коллизии (огромный скандал).

Вот с тех пор MD5 окончательно считается небезопасным.

Коллизии можно находить и создавать целенаправленно, поэтому не рекомендую использовать MD5 для проверки целостности, подписей, сертификатов и т.п.

Для этих задач лучше бери проверенный SHA-256 или что-то посильнее.

Вот такие пироги. Изучай!

🛠 #crypt #md5 #glitch

@bashdays @linuxfactory @blog
Please open Telegram to view this post
VIEW IN TELEGRAM
1795
Как оказалось многие не знают, как нативным cron (без systemd timers) запускать скрипты с интервалом в 30 секунд, без модификации самого скрипта.

Все довольно просто и очевидно. Нужно сделать бутерброд.

Cron исторически работает только с минутной точностью. В crontab нельзя написать «каждые 10 секунд» или «раз в 30 секунд». Для этого обычно использую systemd timers или отдельный демонический скрипт с while true; sleep ...


В crontab строка запускается раз в минуту. Но внутри можно поставить sleep — задержку перед запуском. Таким образом мы получим несколько запусков в рамках одной минуты.

* * * * * /usr/local/sbin/bashdays.sh
* * * * * sleep 30; /usr/local/sbin/bashdays.sh


1. Первая строка запускает скрипт в начале минуты (00:00, 00:01, 00:02…)

2. Вторая строка — ждёт 30 секунд и запускает скрипт (00:00:30, 00:01:30, 00:02:30…).

Тут и получаем шаг в 30 секунд, именно через 2 вызова.

Костыльно? Ага! Но порой не хочется ебаться с таймерами и сделать все по-быстрому. Как вариант, вполне годный. Аналогично можно городить и другие интервалы.

Минусы подхода

Нет гарантии точности. Если первый запуск скрипта будет работать дольше, чем пауза (sleep), запуски могут наложиться.
Мусор в crontab. Для мелкого интервала надо плодить много строк.
Нет гибкой логики.

Где это полезно

Лёгкие скрипты мониторинга (ping, проверка статуса).
Хаотизация нагрузки (например, sleep $((RANDOM % 60)) для рассинхрона).
Если systemd timers или другие планировщики недоступны (например, в ограниченных окружениях или старых системах).

А как работать с таймерами ищи по тегу #systemd, много про это писал.

🛠 #linux #cron #systemd

@bashdays @linuxfactory @blog
Please open Telegram to view this post
VIEW IN TELEGRAM
52
В качестве идеи.

🔤🔤🔥🔤🔤🔤🔤

Кто давно работает с техникой, знает, что бывают дни, когда все работает как часы. И делать абсолютно ничего не нужно.

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

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

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

А после того, как без тебя все начинает ломаться - можно смело требовать повышения зарплаты, так, чисто за присутствие, потому что именно твое присутствие аки Атлант поддерживает всю инфраструктуру. :-)

Вот на что нужно ИИ натаскивать. А не код на баше писать. :-) и работу у людей отнимать.

Письните в комментах, сталкивались с такими ситуациями? Пользовались бы такой языковой моделькой?

🛠 #рабочиебудни

@bashdays @linuxfactory @blog
Please open Telegram to view this post
VIEW IN TELEGRAM
58
Чем ЦОД отличается от дачи? Да ничем!

В ЦОДе заботятся о стойках, охлаждении и аптайме. А на даче об углях, мангале и шашлыке. И там и там важно ничего не сжечь.

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

Что входит в набор дачника: кепка, металлическая кружка, ланч-бокс с приборами и шоппер. Самый необходимый минимум.

Кепка: Защищает лысину от выгорания.
Кружка: Для холодного пива.
Ланч-бокс: Для шашлыка.
Шоппер: Замена рюкзака, под девайсы.

Этот «старт-пак» я получил от Selectel — они не только про дата-центры, но и про экологию.

Все элементарно: в ЦОДах используются чиллеры и кондиционеры, а в природе роль «системы охлаждения» берут на себя деревья. Если ты ходил в школу, то должен быть в курсе.

Деревья испаряют влагу, дают тень и снижают температуру вокруг, получается что-то вроде природного «free cooling/халявное охлаждение».


😀😃😄😁😅😂🤣😊
😇🙂🙃😉😌😍🥰😘
😗😙😚😋😛😝😜🤪
🤨🧐🤓😎🤩🥳😏😒
😞😔😟😕🙁😣😖
😫😩🥺😢😭😤😠😡

Формула простая: 1 сервер = 1 дерево. Это основа акции «Зеленый Selectel». За всё время было засажено 38 гектаров или 160к деревьев. Сильно!

Как вариант — если у тебя дома греется self-hosted сервак или подгорает пятая точка от бесполезных созвонов — посади в каждой комнате по 3 дерева и не трать деньги на оверпрайс решения для охлаждения.

Ладно, всех с пятницей, хороших тебе предстоящих выходных и береги себя!

Selectel для охлаждения высаживает лес, а я сегодня для охлаждения высажу пару банок холодного пива.


🛠 #рабочиебудни

@bashdays @linuxfactory @blog
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
1258
Есть такая книга — «Не работайте с мудаками». Я ее не читал, но вся суть в названии. Ща расскажу поучительную историю.

Отдел продаж разжился постоянным клиентом, успешно работают с ним 5 и более лет, сайтики там рисуют, CI/CD настраивают и т.п. Для постоянного клиента все блага, фиксированные скидки, обработка экстравагантных запросов, отлизывание жопы, где-то даже бесплатно, полное доверие.

В какой-то момент, у постоянного клиента меняется менеджер. Прилетает очередная таска на доработку. Доработки сделаны, но менеджер визжит-пищит и орет — я просил другое!

По ТЗ все гладко, перепроверяют, как просили — так и сделали. Не доебаться!

Но нет, откуда-то берутся новые вводные. Новые вводные предварительно озвучены не были. Вводные в голове нового менеджера, которые он получил из космоса. То, что их нет в ТЗ, это сугубо личные проблемы исполнителя.

Кто виноват? Конечно — исполнитель! Он должен был догадаться сам. А значит виноват — исполнитель!

Исполнитель пробует решить всё миром, объясняет, так и так, что это дополнительные человеко-часы, в договоре такого нет, нужно доплатить и вообще так не делается.

По итогу — виноват исполнитель, переделывайте за свой счет.

Классика! Ну такое себе…

Что имеем: С таким менеджером дальше работать никто не будет, искать концы до главного босса тем более (других клиентов жопой ешь). Конфликт конечно же нужно уладить, спустить на тормозах. Мы же взрослые люди. Но на этом увы всё, сотрудничество закончено!

Да, у айтишников есть свой «черный список», который гуляет в приватных чатах в телеграм. Не только HRы этим грешат.


В таких ситуациях не стоит хамить, нужно разобраться и найти какие-то общие точки соприкосновений.

Искать правду и что-то доказывать смысла нет, если человек живет в своем мире, его от туда уже не вытащить. И нужно ли вытаскивать? Ему там комфортно, он таким уродился!

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

Конфликт решается просто — ок, сделаем как вы хотите. Пусть думают что всё пошло по их правилам, но это лишь начало конца.

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

Теперь сайтики и CI/CD, бывшему клиенту обойдутся в x2-x3 дороже, потому что рыночные цены ушагали вперед семимильными шагами, даже на фрилансе.

Мосты сожжены, а реквест на расторжение договора выслан по почте. Всё рамках трудовых отношений.

Подведем итоги:

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

Для ИПешников с сотрудниками это прям критично.


Но порой и проверенные люди тоже дают сбой (оказываются пидарасами), это как продакшен. Продакшен в 99% случаев тот еще пидарас.

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

Ну а если это твой основной заказчик (который делает тебе 90% прибыли), найди выходы на главного и обсуди все вопросы с ним, благо это делается на раз-два. Менеджер уйдет сразу нахуй и будет выкинут на улицу. Проходили, знаем.

Но лично я предпочитаю чтобы овнер сам это всё просек и нашел крысу. Без подсказок и т.п. Если не просекет, то извините, выше уже не прыгнет с такими кадрами.


Большего и не скажешь. Если читал книгу «Не работайте с мудаками» расскажи в паре слов, о чем она. А то всё руки не дойдут полистать.

😀😃😄😁😅😂🤣😊
😇🙂🙃😉😌😍🥰😘
😗😙😚😋😛😝😜🤪
🤨🧐🤓😎🤩🥳😏😒
😞😔😟😕🙁☹️😣😖
😫😩🥺😢😭😤😠😡

Спасибо за внимание и до свидания!

🛠 #рабочиебудни

@bashdays @linuxfactory @blog
Please open Telegram to view this post
VIEW IN TELEGRAM
572
This media is not supported in your browser
VIEW IN TELEGRAM
Про яйца и балансировщик нагрузки

Когда нужно распределить запросы между серверами, возникает проблема — а как блядь понять, какой сервер сейчас менее нагружен?

Постоянно опрашивать сервера, это хуйня, информация будет не актуальной, все это медленно, пока мы собирали данные, ситуация уже поменялась.

На этом фоне возникает эффект «стада», когда много запросов дружно бегут туда, где вроде бы свободно и по итогу перегружают сервер еще больше.

Это можно решить проще — например перенаправлять запросы случайно.

Работает это вроде неплохо, но опять же всегда есть шанс, что нагрузка будет неравномерной. И снова получаем, что какой-то из серверов будет перегружен запросами.

Какой-то замкнутый круг, печаль и говнище.

А чё делать?

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

Вроде мелочь, но работает этот подход прям заебись! И что интересно у этого подхода есть официальная формулировка — Power of Two Choices или «Сила двух выборов».

Почему это заебись?

На просторах есть задачка «яйца и корзины», в оригинале там конечно не «яйца», а шары.

Короче. Мы случайно кидаем яйца в корзины. Если класть каждое яйцо в случайную корзину, то в итоге некоторые корзины будут переполнены.

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

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

Все просто! Тоже самое и с серверами:

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

Давай на котиках:

Представь, что у тебя есть куча серверов, и на четверти из них пришло по 4 запроса.

- Если я выбираю сервер случайно, есть 25% шанс попасть именно на сервер, которому уже пезда.

- Если я выбираю два сервера и беру менее загруженный — вероятность того, что оба окажутся с нагрузкой 4, уже всего 6,25% (1/16).

Получается, что вероятность перегрузки падает очень быстро. А дальше — ещё быстрее: чтобы нагрузка выросла до 5, 6 и так далее, нужно каждый раз «удачно» попасть в редкие перегруженные сервера, и шанс становится ничтожным.

Видишь, математика нужна не только бекендерам, но и в девопсе можно применить.

Что получаем на практике:

На практике балансировка «выбором из двух» делает распределение нагрузки по серверам почти равномерной. При этом мы не тратим кучу ресурсов на мониторинг и не пытаемся каждый раз точно узнать состояние всех серверов.

Такой подход - простой и элегантный. Как говорится — без хуйни!

Формулы и расчеты визуально можешь глянуть тут. Там как раз разбирается способ с яйцами, но уже с углубленной математикой и формулами.

Этот же принцип используют, например, в cuckoo hashing (структура данных для хранения ключей).

Cuckoo hashing (кукушкино хеширование) — алгоритм разрешения коллизий значений хеш-функций в таблице с постоянным временем выборки в худшем случае.


Как реализовать в nginx

upstream backend {
random two; # Power of 2 Choices
server app1.bashdays.ru;
server app2.bashdays.ru;
server app3.bashdays.ru;
}


Вместо случайного выбора nginx берёт два случайных сервера и кидает запрос на тот, где меньше соединений.

Этот же принцип используют, например, в cuckoo hashing (структура данных для хранения ключей).


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

🛠 #devops #nginx

@bashdays @linuxfactory @blog
Please open Telegram to view this post
VIEW IN TELEGRAM
789
Как не стремись к автоматизации, всегда найдется какой-нибудь легаси сервис, который требует ручного обслуживания.

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

Доступ к сервису имели многие, поэтому люди порой троили и запускали команды в каталоге сервиса от root. Сервису было на это поебать, но до момента его перезапуска.

Обычно это чинилось очень легко, через chown -R. Все это знали и никого это не смущало. Короче костыль ебаный.

Казалось бы, есть масса способов предотвратить такие ошибки: правильные права на файлы, ACL’ы, SELinux.

Но веселья в этом мало! Поэтому я решил заебенить свой собственный мониторинг файловой системы. Скиллов то предостаточно, хули нет.

Спойлер: Я залез в кроличью нору и знатно так хлебнул гавна.

Попытка намбер 1

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

Всё просто: инициализируем fanotify_init, указываем каталоги через fanotify_mark и читаем события из дескриптора.

Но тут же вылез огромный хуй:

- нельзя отслеживать каталоги рекурсивно (только целый маунт)
- anotify даёт только PID процесса, который что-то сделал. А чтобы узнать UID/GID — нужно лезть в /proc/<pid>/status. То есть на каждое событие приходится открывать и парсить файлы в /proc.

Решение вполне рабочее, но громоздкое. Я такие не люблю, этож думать надо, вайбкодингом не решается.

Попытка намбер 2

Вспоминаем что есть eBPF. Это штука позволяет запускать программы прямо в ядре Linux. Они компилируются в байткод, проходят проверку, а потом гоняются через JIT почти с нативной скоростью.

Что такое eBPF можешь почитать тут и тут.


В eBPF заебись то, что можно цепляться за разные функции ядра. Например, можно подцепиться к vfs_mkdir или vfs_create — это общий слой для работы с файлами.

То есть единая точка входа. Там можно отлавливать события и фильтровать их, не гоняя лишние переключения контекста.

Но и тут вылез хуй:

- kprobes на функции VFS нестабильны, в новых ядрах сигнатуры могут меняться или функции вообще исчезнуть.

- фильтрацию приходится писать прямо в eBPF, а там свои ограничения. Нет бесконечных циклов, стек всего ~512 байт.

Да блядь…

Как я победил рекурсивный обход

Чтобы понять, что именно меняется в каталоге сервиса, пришлось использовать структуру dentry и подниматься по дереву до родителя.

Но так как в eBPF нельзя сделать «бесконечный» цикл, я ограничил глубину с помощью MAX_DEPTH.

На практике этого вполне достаточно. Глубина каталогов мне известна. Ну и конечно, пришлось аккуратно работать с RCU-локами, чтобы дерево не поменялось в момент обхода.

➡️ Кусок кода в первом комментарии, сюда не влез.


Как можно улучшить

В идеале использовать не VFS-хуки, а LSM hooks (Linux Security Module).

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

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

Итоги

Эта поделка как и предполагалась, погрузила меня в печаль, душевные страдания, НО стала отличным тренажером для прокачки:

- Внутренностей Linux ядра
- Работы с eBPF
- И кучу другого с kernel-space

eBPF — мощнейший инструмент, но очень тонкий. Ошибёшься — будешь выебан в жопу.


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

Мораль?

Иногда самое простое решение — это chown -R. Но куда интереснее — написать свой велосипед и заглянуть в кроличью нору Linux ядра.

🛠 #linux #debug #dev

@bashdays @linuxfactory @blog
Please open Telegram to view this post
VIEW IN TELEGRAM
949
Настройка core dump в Docker

Цель этого поста — дать тебе общее руководство по включению и сбору core dumps для приложений, работающих в docker контейнере.

Настраиваем хост для сохранения дампов

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

Универсальный способ — задать шаблон core pattern. Шаблон определяет путь и информацию о процессе который наебнулся.

echo '/tmp/core.%e.%p' | sudo tee /proc/sys/kernel/core_pattern


Кратенько:

%e — имя процесса
%p — pid процесса

Более подробно о конфигурации core pattern можешь почитать в man-странице ядра Linux.


Как вариант, можно настроить host изнутри контейнера через CMD или ENTRYPOINT. Но контейнер в этом случае должен запускаться в privileged mode, что хуева с точки зрения безопасности.

Пример хуёвого приложения

#include <cstdlib>
void foo() {
std::abort();
}

int main() {
foo();
return 0;
}


После компиляции и запуска, приложение наебнется с ошибкой.

Пишем Dockerfile для этого приложения

FROM ubuntu:22.04

# Install tools
RUN apt update \
&& apt -y install \
build-essential \
gdb \
&& rm -rf /var/lib/apt/lists/*

# Build the application
COPY ./ /src/
WORKDIR /src/
RUN g++ main.cpp -o app

CMD ["/src/app"]


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


Запускаем контейнер с нужными опциями:

docker run \
--init \
--ulimit core=-1 \
--mount type=bind,source=/tmp/,target=/tmp/ \
application:latest


Разбираем опции:

--init — гарантирует корректную обработку сигналов внутри контейнера

--ulimit — устанавливает неограниченный размер core dump для процессов внутри контейнера

--mount — монтирует /tmp из хоста в контейнер, чтобы дампы, создаваемые внутри контейнера, были доступны после его остановки или удаления

Здесь важно: путь source на хосте должен совпадать с тем, который задан в шаблоне core pattern.

После того как приложение наебнется, core dump будет сохранён на хостовой машине в директории /tmp.

ls /tmp/core*
# /tmp/core.app.5


Анализируем дамп с помощью gdb

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

Это поможет убедиться, что все зависимости (библиотеки и прочая хуитень) доступны.

docker run \
-it \
--mount type=bind,source=/tmp/,target=/tmp/ \
application:latest \
bash


Если в образе нет исходного кода, можно допом смаунтить исходники:

docker run \
-it \
--mount type=bind,source=/tmp/,target=/tmp/ \
--mount type=bind,source=<путь_к_исходникам_на_хосте>,target=/src/ \
application:latest \
bash


Теперь внутри контейнера запускаем:

gdb app /tmp/core.app.5


После загрузки дампа можно выполнить команду bt (backtrace), чтобы увидеть трассировку стека:

(gdb) bt
#0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
#1 0x00007f263f378921 in __GI_abort () at abort.c:79
#2 0x000055f9a9d16653 in foo() ()
#3 0x000055f9a9d1665c in main ()


Команда поможет определить, где именно произошел факап.

Давай быстренько разберем ошибку.

#0 и #1 показывают, что процесс получил сигнал 6 (SIGABRT) и завершился через abort()

#2 — вызов произошёл внутри функции foo()

#3main() вызвал foo()

В исходнике было:

void foo() {
std::abort();
}


То есть ошибка здесь не баг компиляции или рантайма, а намеренно вставленный вызов std::abort(), который и приводит к аварийному завершению и генерации core dump.

Если у тебя docker-compose, то все флаги (--init, --ulimit, --mount и т.д.) применимы и для него. То есть отладку можно легко адаптировать.

Хуй знает чё еще написать, завтра тему дебага продолжим, чет в конце года много траблшутинга навалило разнообразного.

🛠 #linux #debug #dev

@bashdays @linuxfactory @blog
Please open Telegram to view this post
VIEW IN TELEGRAM
545