Bash Days | Linux | DevOps
23.2K subscribers
126 photos
22 videos
593 links
Авторский канал от действующего девопса

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

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

Курс: @tormozilla_bot

РКН: https://two.su/bashdays
Download Telegram
Во времена динозавров, мне удалось поработать в профильных компаниях, которые занимаются информационной безопасностью и тестированием инфраструктуры клиентов на проникновение.

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

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

И мне стало интересно, а как это провернуть? А если появился интерес, надо тыкать палкой.

Сейчас будем решать эту задачу. Как именно, понятия не имею. Будем экспериментировать.

Отключаем логирование

Цель - папка var/log всегда должна быть пустая и чтобы Linux машина не встала раком. Дополнительно обуздаем journald и history от bash.

Первое, что я проверил, удалил все и запретил вообще любую запись в var/log через chmod 000, но после перезагрузки сервера, всё вернулось на свои места. Права на папку стали 755. Однако…

Теперь попробуем установить на папку специальный атрибут:

sudo chattr +i /var/log


Перезагружаемся. Хе! Сработало. В папке пустота! На этом можно двигать задачу в done.

После установки этого атрибута, у тебя начнет сыпать ошибками apt, но пакеты оно будет ставить, так что имей это ввиду. Это фиксится добавлением строчки Dir::Log "/путь"; в apt.conf.d.

НО, не все так просто. Если ввести:

jorunalctl -f


Мы снова видим логи. Но теперь уже на экране. А раз они на экране, значит они где-то лежат и на диске. Давай искать.

Зачищаем этот системный журнал:

sudo journalctl --rotate && sudo journalctl --vacuum-time=1s


Ага, на экране видим путь, то что оно зачистило, это /run/log/journal/.

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

Открываем файл /etc/systemd/journald.conf и добавляем:

Storage=none


Перезагружаем службу и удаляем остатки:

sudo systemctl restart systemd-journald
rm -R /run/log/journal/*


Теперь если ввести:

jorunalctl -f


Получим такое сообщение: No journal files were found. Прекрасно!

Так, если в системе установлен rsyslog или подобное, то дизейблим и его:

systemctl stop rsyslog
systemctl disable rsyslog


Основное сделали. Осталось логирование в .bash_history. Зачищаем и накидываем атрибут на запрет записи:

cat /dev/null > ~/.bash_history
sudo chattr +i ~/.bash_history
history -c && history -w``

Либо добавляем в .bashrc строчку: HISTSIZE=0, с ней будет нативнее и без костылей с атрибутом.

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

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

Всех обнимаю, хорошего дня!

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

tags: #bash #linux

💩 @bashdays
Please open Telegram to view this post
VIEW IN TELEGRAM
134
Занятный факап приключился у клиента. На скрине думаю понятно, что произошло. Добро пожаловать в Linux!

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

Если кратко, я глянул в логи /var/log/cron, взял рандомную таску которая выполнялась ранее и вбил:

grep -a -C 500 -F 'console import:reports' /dev/sda1


Соответственно console import:reports это часть таски, которая когда-то выполнялась. Команда отработала и нашла мне удаленный cron файл. Копипаста и все рады. Но тут опять же повезло, что данные не перезаписались на диске.

В любом случае можно было бы восстановить по хронологии из файла /var/log/cron, но уже с геморроем. Либо грепнуть CRON в syslog и journald.

На будущее сделал клиенту алиас:

alias crontab="crontab -i"


Этакая защита от дурака и толстых пальцев. Теперь если перепутать «e» и «r», то оно выругается и запросит подтверждение:

crontab: really delete root's crontab? (y/n)

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

Бэкап крона:

@daily crontab -l > $HOME/.crontab


Восстановить:

crontab < $HOME/.crontab


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

Всех с пятницей, хороших предстоящих выходных. Увидимся!

tags: #bash #linux

💩 @bashdays
Please open Telegram to view this post
VIEW IN TELEGRAM
2211
Привет. Сегодня про хитрости с пайпами и кодами завершения. Максимально упрощаю, чтобы стало понятнее.

Собираем такую команду:

false | true


Если после её выполнения сделать: echo $?, то получим статус выхода 0. То есть у нас есть статус завершения именно команды true. НО теперь узнать с каким кодом завершилась первая команда false?

А вот так, модифицируем команду:

false|true ; ( exit ${PIPESTATUS[0]} )


В этом случае будет получен код завершения 1, который справедлив для команды false. Как ты можешь заметить, если в PIPESTATUS изменить [0] на [1], мы снова получим статус завершения 0 (то есть для true).

PIPESTATUS - это специальный массив переменных в оболочке Bash, который содержит статус завершения каждой команды в цепочке команд, выполняемой через конвейер (pipeline). Когда в вашем скрипте используется конструкция конвейера с несколькими командами, PIPESTATUS сохраняет статус завершения каждой из этих команд.


Аналогично можно манипулировать любой частью пайпа и получать нужные тебе статусы.

command1 | command2 | command3
echo "${PIPESTATUS[0]}"
echo "${PIPESTATUS[1]}"
echo "${PIPESTATUS[2]}"


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

Больше про коды завершения, можешь найти в этом посте.

tags: #bash

💩 @bashdays
Please open Telegram to view this post
VIEW IN TELEGRAM
139
Тадам! Сегодня научимся определять, отправляется ли стандартный вывод скрипта в терминал либо передается с помощью pipe в другой скрипт/утилиту. Чтобы стало понятнее, нужно читать дальше.

Есть у меня некий Bash скрипт, пусть будет таким:

echo "Hello Bashdays Welcome"


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

Давай по порядку, смотри:

bold=$(tput bold)
normal=$(tput sgr0)

echo "Hello ${bold}BashDays${normal} Welcome"


Этот скрипт выведет строку Hello Bashdays Welcome, где слово Bashdays будет жирным (bold).

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

./script.sh
Hello Bashdays Welcome

./script.sh | cat
Hello Bashdays Welcome


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

Давай мутить

В POSIX оболочках существует флаг -t, с помощью которого можно задетектить файловый дескриптор. Если файловый дескриптор fd = True, то скрипт запущен самостоятельно в терминале. Дескриптор fd может принимать значения:

0 - стандартный ввод
1 - стандартный вывод
2 - стандартная ошибка


Пишем скрипт с логикой:

bold=$(tput bold)
normal=$(tput sgr0)

if [ -t 1 ] ; then
echo "Hello ${bold}BashDays${normal} Welcome"
else.
echo "Hello BashDays Welcome"
fi


Запускаем:

./script.sh
Hello BashDays Welcome

./script | cat
Hello BashDays Welcome


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

Флаг -t справедлив не только для Bash, он будет работать во всех POSIX оболочках.

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

Хорошего дня, увидимся!

tags: #bash

💩 @bashdays
Please open Telegram to view this post
VIEW IN TELEGRAM
99
А спорим ты не знаешь чем отличается:

var=$(command)


от

var=`command`


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

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


Если на чистоту, то эти два примера ничем не отличаются, логика работы будет одинакова. Выполнится какая-либо команда (command) и ее результат запишется в переменную var.

Эти штуки, кривые кавычки, называются backticks = обратные палки/знаки или gravemarks = надгробия.


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

А в чём, тогда подвох?

Да всё просто - gravemarks (надгробия) морально устарели и в бест-практиках нынче $(command).

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

list=$(ls -l $(cat bashdays.txt))


либо

list=`ls -l \`cat bashdays.txt\``


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

Ну и порой использование gravemarks влияет на результаты:

A="A_VAR"
echo $(echo "\$A")
echo `echo "\$A"`


Вроде две одинаковые команды, а выведут разный результат:

$A
A_VAR


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

echo `echo "\\$A"`


В результате получим вывод на экран: $A как и в первом варианте с $().

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

Ладно. Давай краба, увидимся!

tags: #bash

💩 @bashdays
Please open Telegram to view this post
VIEW IN TELEGRAM
155
Помнишь я писал про shellcheck, который проверяет Bash скрипты на синтаксические и подобные ошибки. Ну так вот, у интерпретатора Bash есть подобный функционал прям из коробки.

Суть - проверить скрипт на ошибки, до запуска.

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

Конечно коробочный вариант не такой гибкий как shellchek, но зато ничего не нужно устанавливать лишнего. Это как nginx -t перед его ребутом. Зачем ставить что-то внешнее, если это уже предусмотрено в самой программе. Ну только если расширить функционал и получить больше плюшек.

А если работаешь с гитом, можешь сделать pre-hook и гавно в репу уже не улетит (но если постараешься, то улетит):

bashErr=$(find src/ -type f -name '*.sh' -exec bash -n {} \;  2>&1 > /dev/null)
if [ -n "$bashErr" ]; then
echo "$bashErr"
exit 1
fi


После запуска этого хука проверяются все * .sh скрипты в папке src. Пример скрипта с ошибкой:

echo "Hello Bashdays
exit 0


Хуй Хук выдаст тебе сообщение src/script.sh: 7: Syntax error: Unterminated quoted string и запретит (коммитить, пушить в мастер).

Нечто подобное было прикручено к puppet очень давно в одной студии, был какой-то внутренний линтер на хуке гита, но хлебнул я знатно с ним. Мне пушить, а он на какие-то фантомные пробелы орет падла. Короче как только встал у руля, избавился и от этого вонючего паппета, от хуков и от команды.

Ну или как вариант можно включать set -u, например если переменную не объявил и попытался где-то её использовать.

set -u включает режим обработки неустановленных переменных (также известный как режим nounset). Когда этот режим включен, обращение к неустановленной переменной приведет к ошибке выполнения скрипта.


set -u

echo "Переменная VAR: $VAR"


./script.sh: line 3: VAR: unbound variable


Или так:

set -o noexec


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

А завтра кстати внеплановая пятница! Ждем ждем…

tags: #bash

💩 @bashdays
Please open Telegram to view this post
VIEW IN TELEGRAM
93
С наступающим! Давным-давно я избавился от OpenVPN и сейчас активно продолжаю использовать WireGuard. Даже порой связываю продакшен сервера wg тоннелями в разных регионах, где нет возможности это сделать из коробки. Стабильно, быстро, бесплатно.

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

Клиент в рандомный момент показывал средний палец и работал через хуй копыто.

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

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

И да, я много гуглил про эти проблемы, они имеются и фиксятся именно, так как я их и фиксил, перезагрузками, службами и другими костылями.

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

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

Чо понравилось:

1. Не глючит, шустрый, не виснет
2. Поддержка импорта тоннелей из официального wg клиента
3. При загрузки ОС можно автоматом подключать/не подключать VPN
4. Разделение приложений, кто идет через VPN, а кто нет
5. Ну и конечно другие фичи, какие хз, мне 4х хватает

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

💻 Страница проекта на github

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

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

tags: #utils #windows #networks

💩 @bashdays
Please open Telegram to view this post
VIEW IN TELEGRAM
112
Доброе утро! Все выходные разгребал бухгалтерию, да готовил рекламную кампанию на завтра. Даже баг мимолётом поправил у клиента. После подключения ddos-guard, мониторинг сильно засопливил. Ну тут очевидно, docker контейнеры с экспортерами снимают показатели по домену, а домен-то теперь за ddos-guard, пришлось прокинуть hosts файл и ходить напрямую за метриками.

Ладно. Сегодня на повестке дня: Зачем добавлять ./ перед исполняемым файлом или скриптом.

Когда я только начал изучение linux, я столкнулся с проблемой, что не могу выполнить bash скрипт. Вроде и атрибут +x указан, но после запуска получал идинахуй:

script.sh: command not found


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

Но почему так? Это же не логично! Нет, всё логично. Это сделано для безопасности и от кривых рук. Например:

Работаешь ты под рутом, заходишь в домашнюю папку какого-нибудь пользователя, делаешь ls и хуяк, нет у тебя больше сервера. А что произошло?

А произошло то, что в папке пользователя лежал бинарник под названием ls. Который содержал руткит, он то и выполнился. Хотя ты ожидал что выполнится нативная утилита ls.

Вот поэтому если запускать какую-то дичь или скрипт БЕЗ ./ то оболочка будет искать эту дичь/скрипт в указанных каталогах. Которые определены в PATH. Соответственно если домашний каталог пользователя не определен в PATH, то и возникает ошибка command not found.

А вот когда указываешь ./script.sh ты говоришь оболочке - Эй бля! Игнорируй PATH и запускай мне эту пепяку! Да, вот прям отсюда!

Аналогично можно запускать указывая полный путь к программе/скрипту:

cd ~
./script.sh

# или

/home/user/script.sh


Здесь так же PATH будет игнорироваться. Но вопрос остается: Как это может быть мерой безопасности, если эта мера обходится банальным ./ ?

Нууу… это больше для предотвращения несчастных случаев. Указав явно ./ ты говоришь оболочке - запусти конкретно этот файл из текущего каталога, а не тот который может лежать в PATH.

Как вариант, можешь добавить точку в PATH и тогда можешь запускать скрипты без ./ например:

export PATH=$PATH:.
script.sh


Добавляем текущий каталог в PATH. Теперь script.sh выполнится, без указания этого хитрого ./ Но так делать не рекомендуется, наступишь на грабли или даже на граблища!

Так, теперь про cron.
Если хочешь чтобы твои скрипты гарантировано запускались в cron, всегда указывай полные пути до программ/утилит. Так как cron может смотреть в какой-то свой PATH или вообще через sh запускаться. Встречал/встречаю я такие случаи. Поэтому рекомендуется писать скрипты в таком духе:

CAT=$(which cat)
$CAT script.sh


Определяем полный путь до утилиты cat, а потом уже используем как переменные. Даже если cat не найдется через PATH, скрипт в кроне будет выполнен, так как указаны полные пути. Заметки про PATH можешь глянуть в этом посте.

Вот так. Хорошей тебе рабочей недели. Вечерком после интеграции закину еще чтива. На связи!


tags: #bash #linux

💩 @bashdays
Please open Telegram to view this post
VIEW IN TELEGRAM
125
Сегодня на собесе задал кандидату вопрос, а расскажи чем отличается && и «;» при склейке консольных команд. В ответ получил — а мне это зачем знать? Если с && не заработало, то поменяю на «;».

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

Давай разбираться чем отличается:

command && command


от:

command ; command


&& (AND) это логический оператор, а вот «;» обычная последовательность действий.

В первом примере с &&, вторая команда запустится, только в том случае, если первая завершится успешным кодом возврата. Соответственно если первая накроется пиздой, произойдет короткое замыкание и вторая команда даже не соизволит запуститься.

Про статусы и коды возврата можешь почитать в этом посте.


А вот в случае с «;», вторая команда запустится при любых обстоятельствах, конечно если это не kernel panic и при условии если в оболочке не включен set -e (завершение при любых сбоях).

Да, есть еще конструкция:

false || echo "hello bashdays"


Тут у нас логический оператор OR (или). Вторая команда будет запущена, только если первая сыграет в ящик. В примере выше выполнится вторая команда, так как первая всегда завершается с ошибкой. Если false заменить на true, соответственно на экране будет пусто.

Иногда применяю это в скриптах, когда одна утилита не смогла справиться, подключается вторая.

База. Тут хочешь не хочешь, оно должно на подкорке быть. Интересно конечно, как людей на вайтишных курсах обучают. Человек отучился 6 месяцев, администратор Linux, диплом. А в голове обезьяна тарелками стучит, администрирует. Хотя тут всё от человека зависит, если ему это не интересно, то никакие курсы ситуацию не изменят.

Ладно, всякое бывает. Не смею больше задерживать. Спокойной ночи. Увидимся!

tags: #bash #linux

💩 @bashdays
Please open Telegram to view this post
VIEW IN TELEGRAM
227
Внимание, сейчас будем упарываться. Давно не упарывались. Камон в нашу ламповую лабораторию. Сразу не сбегай, возможно узнаешь что-то новенькое или окончательно засвистит фляга.

Для начала давай создадим две подопытные папки:

mkdir /tmp/{.libs,.bashdays}


Теперь скопируем в них нативную утилиту cp (copy):

cp "$(which cp)" /tmp/.libs/lt-test
cp "$(which cp)" /tmp/.bashdays/lt-test


Скопировали, файлы назвали lt-test.

Далее запускаем:

/tmp/.libs/lt-test example.txt .


В параметрах указываем файл, которого нет. На экран вывелось:

test: cannot stat 'example.txt': No such file or directory


А теперь запускаем:

/tmp/.bashdays/lt-test example.txt .


На экран получаем:

/tmp/.bashdays/lt-test: cannot stat 'example.txt': No such file or directory


Ну и внимательно сравниваем результаты:

1. test: cannot stat 'example.txt'
2. /tmp/.bashdays/lt-test: cannot stat 'example.txt'


Хм, всё же идентично, почему тогда результаты разные? В первом случае кусок названия запускаемого файла lt-test вообще обрезан.

В первом случае путь и прёфикс lt был обрезан, потому что запуск утилиты был выполнен из директории .libs.

Всё дело в функции set_program_name которую пихают в ГНУтые утилиты и программы. Эта функция модифицирует путь и имя утилиты если запуск был произведен из папки .libs, а затем присваивает результат переменным.

А потом эти переменные используются для вывода при ошибках или при выводе страницы хелпа.

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


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

/tmp/.libs/lt-test --help | head  -1
/tmp/bashdays/lt-test --help | head -1


Результат тот же:

1. bash: /tmp/.libs/lt-test --help
2. bash: /tmp/bashdays/lt-test --help


Вся эта байда в программах нужна для корректного отображения имени, при использовании ГНУТого инструмента libtool.

Как это обрабатывается на самом деле, можешь глянуть в нативном исходнике на СИ здесь, там и про префикс lt и про .libs всё прекрасно структурировано. А здесь можешь ознакомиться с развёрнутым комментарием на эту же тему.

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

tags: #linux

💩 @bashdays
Please open Telegram to view this post
VIEW IN TELEGRAM
111
Привет друзья. Частый вопрос: Можно ли в bash скрипте, каким-то образом временно изменить текущий рабочий каталог для выполнения команды, но остаться в каталоге запуска?

Да всё можно, а если говорят что нельзя, то это пиздёшь. Формулировка вопроса конечно замысловатая. Сходу в голову приходит вариант использовать cd (Change Directory), тип такого:

cd /tmp/test && ./exec_command


После запуска команды, переходим в каталог /tmp/test и выполняем exec_command. Логично? Логично! А что если после запуска этой команды, я хочу остаться в том же каталоге из которого запускал эту команду.

Про символы &&/;/|| и т.п. можешь почитать в этой статье.


Вот наглядно что происходит:

1. > pwd
2. /root
3. > cd /tmp/test && ./exec_command
4. > pwd
5. /tmp/test


В самой последней строке видим, что каталог изменился. А я хочу оставаться в root!

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

Способ первый:

Добавим в скрипт exec_command такой код:

ls


Запускаем то же самое в круглых скобках:

1. > pwd
2. /root
3. > (cd /tmp/test && ./exec_command)
4. a.txt b.txt c.txt exec_command
5. > pwd
6. /root
7. > ls
8. profit


Что мы видим: после выполнения команды в скобках, отработала команда ls из скрипта и вывела на экран содержимое каталога /tmp/test. Как только произошла магия, всё вернулось обратно в каталог root и вывело на экран содержимое каталога root.

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


Способ второй pushd/popd:

1. > pwd
2. /root
3. > pushd /tmp/test
4. /tmp/test ~
5. > ./exec_command
6. a.txt b.txt c.txt exec_command
7. > popd
8. ~
9. > pwd
10. /root


Механика аналогична первому способу. Но используются древние, инопланетные технологии pushd и popd, это коробочные аналоги cd из 80x годов. Местоположение предыдущих каталогов сохраняются в стек (массив). А потом уже из этого стека можно вытаскивать необходимое. Погугли если интересно, есть статейки.

Ну и бонусом третий способ:

1. > pwd
2. /root
3. cd /tmp/test && ./exec_command
4. a.txt b.txt c.txt exec_command
5. > pwd
6. /tmp/test
7. > cd -
8. /root


Здесь используем чистый cd, но для возврата в предыдущий каталог используем cd с параметром «-».

В 99% случаев никто этим не пользуется, все привыкли в скриптах использовать cd туда-сюда с указанием полных/относительных путей. Но если посмотреть в скрипты коммерческих продуктов, в них активно используются методы с pushd и popd.

Легаси? Возможно. Тут наверное дело больше в элегантности. У каждого свои бест-практики, нет неправильных решений. Если задача приносит ожидаемый результат, значит ты всё сделал правильно.

Ладно, изучай, а я пошёл писать следующий пост про обработку сигналов. И нас тут уже 13к, идем дальше!

tags: #bash

💩 @bashdays
Please open Telegram to view this post
VIEW IN TELEGRAM
170
Время неумолимо. Кажется ещё сегодня пятница, а уже на горизонте маячит понедельник.

Привет. Сегодня про сигналы и как с ними взаимодействовать через bash скрипты. Да те самые SIGINT, SIGTERM, SIGKILL.

Вероятно ты про них слышал. Ладно, скажу больше, ты даже ими пользуешься. Банально нажимая CTRL+C, ты посылаешь сигнал SIGINT. Этот сигнал позволяет скрипту корректно завершить работу.

Если прервать скрипт по нажатию CTRL+C, то вернется код ошибки 130 (echo $?). А откуда взялось 130?

Потому что если процесс завершается из-за полученного сигнала, он возвращает значение, которое является суммой 128 и кода сигнала. SIGINT = 2, итого получаем 128+2=130.

Цифра 128 выбрана не случайно, сделано это для того, чтобы избежать конфликтов с другими возвращаемыми значениями процесса. Linux использует значения от 0-127 для обозначения различных ошибок и успешного завершения процесса. Поэтому значения от 128 и выше можно использовать для обозначения сигналов, приведших к завершению процесса, не создавая путаницы с кодами завершения процесса.


Для обработки сигналов, в Bash можно использовать обработчики. Спасибо, капитан!

Пример обработки сигналов:

#!/bin/bash

# Функция для обработки SIGINT (Ctrl+C)
sigint_handler() {
echo "Caught SIGINT (Ctrl+C). Exiting..."
exit 1
}

# Функция для обработки SIGTERM
sigterm_handler() {
echo "Caught SIGTERM. Exiting..."
exit 1
}

# Установка обработчиков сигналов
trap sigint_handler SIGINT
trap sigterm_handler SIGTERM

# Главный цикл
while true; do
echo "Script is running..."
sleep 1
done


Скрипт запустит бесконечный цикл. Если нажать CTRL+C, сработает хендлер sigint_handler и скрипт завершится с кодом 1. Видишь, мы поменяли дефолтный код 130 на 1.

А хендлер sigterm_handler сработает в том случае если скрипт убить через kill -TERM PID. Процесс так же завершится с кодом 1.

Таким образом в хендлерах мы можем построить любую логику, например вывести слово «хуй». А если выпилить exit 1, то по нажатию CTRL+C скрипт вообще никак не отреагирует на завершение и продолжит выполняться дальше. БЕСКОНЕЧНО!

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

SIGTERM
= Этот сигнал обычно отправляется операционной системой при попытке завершения процесса. Обработка SIGTERM позволяет скрипту завершить работу безопасно и корректно освободить ресурсы.


Да, есть еще SIGKILL, а вот его обработать не получится. Он нужен для крайних мер. Например, если из скрипта выше убрать exit 1, то бесконечный цикл удастся завершить только через SIGKILL (kill PID). Ну ту тоже логично, всё продумано.

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

Существуют и другие сигналы SIGHUP / SIGUSR1 / SIGPIPE / SIGCHLD и т.п. но сути это не меняет, пост про перехват сигналов.

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

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

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

tags: #bash #linux

💩 @bashdays
Please open Telegram to view this post
VIEW IN TELEGRAM
104
О чо на почту сегодня прилетело. Вчера в чатике видел, ссылка уже пролетала от Константина, но чет значения не придал. А после этого письма придал.

Идем по этой ссылке и тыкаем какой сервак запустить в браузере, с виндой или линуксом. Проходим капчу если необходимо. Если не запускается, значит мешают всякие adguard и т.п. плагины. У меня в яндекс браузере сразу не заработало, а вот в чистом фоксе норм взлетело.

Короче под капотом Debian 12 или Windows Server 2016 (14393.6709) Длительность онлайн сессии 15 минут.

С докой по сервакам, можешь ознакомиться здесь, там с картинками и всеми подробностями.

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

Ради интереса проверил лицензию winrar, на момент закралась мысль - а что если... Но нет, всего лишь пробная версия на 40 дней. Ну да ладно.

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

Выжимка из доки:

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

- дополнительном рабочем столе
- виртуальном браузере для открытия любых веб-сайтов вне личного ПК
- сервере для тестирования той или иной сомнительной программы
- проверке собственного ПО и скриптов в виртуальной среде
- тестировании консольных команд
- проверки доступности вашего сайта или приложения
- тестировании сайтов, приложений, программ в виртуальной среде
- запуске социальной сети, мессенджера или сайта в режиме полного инкогнито
- способе удалить папку system32 без риска для здоровья

tags: #services

💩 @bashdays
Please open Telegram to view this post
VIEW IN TELEGRAM
87
Зима меня что-то прям знатно так подзаебала, тысяча одёжек, реанимация автомобиля в -40, то дождь за шиворот, то метель в морду лица. Всё же летом как-то попроще мне кажется, хотя и жара сильно изматывает. Нежные мы стали с этой удалёнкой, жёсткая зависимость от зоны комфорта. Ладно…

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

Можно ли после запуска скрипта, перенаправить STDOUT и STDERR в другое место, не останавливая сам скрипт/процесс? Ответ ты и сам знаешь, конечно можно! Ща покажу как.

Чтобы все стало понятным, берем для эксперимента такую команду и запускаем ее в терминале.

tail -f /var/log/auth.log &


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

Как только в файле auth.log появляются новые записи, оно начинает нам срать в терминал, мешая дальнейшей работе. Хоть задача и запущена в фоне, но активно работает с текущим tty и stdout.

Чтобы нам все это дело отправить в dev/null не останавливая фоновую задачу/скрипт. Проделываем следующий трюк:

ps aux | grep tail


Узнаём PID процесса, в моём случае это 1327692, далее смотрим файловые дескрипторы этого процесса:

ls -l /proc/1327692/fd


Получаем нечто подобное:

0 -> /dev/pts/1
1 -> /dev/pts/1
2 -> /dev/pts/1
3 -> /var/log/auth.log


Знакомо? stdout = 1 (поток вывода), stderr = 2 (поток с ошибками) и т.п. Уже наталкивает на мысль чо будем делать? Нет? Поехали дальше.

Теперь каким-то образом, надо зареврайтить это говнище в dev/null. Для этого будем использовать утилиту gdb, она обычно идет в коробке либо ставится через апт-инсталл.

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


Запускаем кракена:

gdb -p 1327692 /usr/bin/tail


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

Всё, теперь ты находишься в «хакерской» оболочке (gdb), начинаем реврайтить говнище. Вводим такие команды:

p (int) dup2((int) open("/dev/null", 0), 1)
p (int) dup2((int) open("/dev/null", 0), 2)


На экран получишь нечто подобное:

$1 = 1
$2 = 2


После этого закрываем gdb:

detach
quit


Вот и всё. Говнище из auth.log перестало выводиться на экран, хотя фоновый процесс продолжает работать. Смотрим что у нас получилось:

ls -l /proc/1327692/fd


И видим что наш реврайт сработал:

0 -> /dev/pts/1
1 -> /dev/null
2 -> /dev/null
3 -> /var/log/auth.log
4 -> anon_inode:inotify
5 -> /dev/null
6 -> /dev/null


Теперь stdout(1) и stderr(2) смотрят в dev/null, собственно этого мы и желали добиться.

Разберем команды gdb

«p» (print), выводим результат команды на экран.

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

Все это дело приводим к типу Int, хотя раньше и без этого всё отлично работало, видимо что-то поменялось.

И получается такое: Команда сначала открывает файл dev/null, затем дублирует его файловый дескриптор, а затем перенаправляет stdout в этот файл. Прозрачно? Прозрачно!

Вот такие пироги. Надеюсь было интересно.

Кстати чтобы не упарываться с gdb, существует софтина под названием reredirect, делает то же самое, если интересно, можешь потыкать, ссылка git на репозиторий.


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

tags: #bash #linux

💩 @bashdays
Please open Telegram to view this post
VIEW IN TELEGRAM
158
🅰️🅰️🅰️
7 причин, почему коты делают «тыгыдык»

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

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

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

Сейчас покажу очередной велосипед, который сэкономит мне еще пару секунд прибывания в консоли. А может быть и ты куда-нибудь себе это присобачишь. Погнали!

Есть у меня сервер, на нем крутится самописная bastion оболочка. Как это работает:

1. Цепляюсь в bastion
2. В консоль вываливает список серверов
3. Коннект к серверу ssh <index>

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

Bastion я написал, впечатлившись этим решением от ITSumma, которое называется isolate.


Как я подключался раньше

> ssh user@bastion.bashdays.com


Как я подключался потом

> export alias bas="ssh user@bastion.bashdays.com"
> bas


Как я подключаюсь сейчас

> alt+b


Прям эволюционный роадмэп. Интересно, какая стадия будет следующей. Итак, повесил я подключение на хоткей, стало довольно удобно. Только открылась консоль, бряк alt+b и я уже внутри.

Хоткей вешал так, добавив это в .bahsrc:

bastion() {
echo "connect to bastion server"
ssh -t qa@bastion.bashdays.com 'reset;bash'
}

bind -x '"\eb": bastion'


«ёб» в последней строчке, это как раз клавиша alt+b в кодах ANSI escape sequences. Если нужны другие кнопки, го в гугол, там этого полно.

«-t» создаёт псевдотерминал, чтобы команды вводились на сервере, а не в текущей оболочке.

А reset;bash понадобился, чтобы победить проблему с отображением команд, которые вводишь. Если убрать это хак, то когда будешь что-то вводить в консоли, это не будет отображаться на экране.

В zsh я на быструю руку попытался подобное провернуть, оно даже подключается, но терминал впадает в бэдтрип Read Only, какая-то абстракция с псевдотерминалами. Пока руки не дошли докопаться до сути. Но если у тебя получится это завести, пиши в комменты, сделаешь ОЧЕНЬ нужное и доброе дело.

Вот мои наработки для .zshrc:

bastion() {
echo "connect to bastion server"
ssh -tt user@bastion.bashdays.com
}

zle -N bastion
bindkey '\eb' bastion


Так, совсем забыл про котов, эту тему обязательно нужно обсудить. И дело тут не в блуждающем нерве.

На хоткеи ты можешь повесить любую команду, любой комбайн, который только заблагорассудится, например можешь запускать vim или редактирование файла /etc/hosts. Короче изучай.

И давай краба, жду тебя в комментах!

tags: #bash #linux

@BASHDAYS
Please open Telegram to view this post
VIEW IN TELEGRAM
146
Что случится, если отключить буферизацию

Привет. Чтобы результаты вывода софта/скриптов отображались без задержек в реальном времени — отключи буферизацию.

На практике, эффект буферизации незаметен, либо софт/скрипты и так работают в реальном времени.

Распространенный случай.

Ты всяко с таким сталкивался: запустил программу, которая пишет данные в файл. И пока софтина не отработает, файл с результатами, визуально пустой. Грыжа!

Но хочется же в реальном времени наблюдать, как файл заполняется и делать выводы. А что если софтина работает 10 часов? А выхлоп нужен вчера?

/usr/local/sbin/script.sh &> bashdays.log


Проблема этого условного скрипта, в том, что файл bashdays.log создаётся только, после выполнения скрипта. Как отслеживать прогресс?

Воспользуйся отключением буферизации!

Всё делается с помощью утилиты stdbuf. Утилита изменяет способ буферизации программ.

stdbuf -oL /usr/local/sbin/script.sh &> bashdays.log


Ключ «o» взаимодействует со стандартным выводом (stdout). А аргумент «L» означает Line buffered, то есть, буферизация по строкам.

По умолчанию при перенаправлении в файл, стандартный вывод (stdout) полностью буферизуется и записывается в файл после сброса.


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

Одно из применений stdbuf:

stdbuf -oL nohup /usr/local/sbin/script.sh &> bashdays.log


Если закрыть вкладку с сессией, скрипт продолжит работу.

Либо можешь воспользоваться утилитой script:

script -c <PROGRAM> -f bashdays.txt


Ключ «f» выполняет аналогичную функцию, после каждой операции, запускается flush, запись на диск.

На этом смею откланяться, хорошего дня/вечера!

tags: #bash #linux

@BASHDAYS
121
Охохо, привет! Размазывать бытовуху не буду, сразу к делу. В bash есть офигительная команда, называется она «readonly». С помощью неё ты сможешь защитить любую переменную от изменения. Получается этакая константа, которую довольно сложно перезаписать.

И не дай бог кто-то решит ее перезаписать, получит в лоб ошибкой:

bash: VAR: readonly variable


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

Оказалось, всё очень даже очевидно. Этот скуфяра мало того что применил ООП в скриптах (это пиздец), так еще и воспользовался «readonly». Спасибо тебе дорогой друг. Надеюсь на новом месте, тебе такого с кубернейтсами не позволяют делать. Диверсант блядь.

Еще раз, НЕ НУЖНО УСЛОЖНЯТЬ, ООП и выкрутасы оставь для программистов.


Ладно. Теперь про «readonly». Если не можем победить, значит нужно изучить и победить. Это как в анекдоте 18+:

Я ебу все что движется, а всё что не движется, я двигаю и ебу.

Поехали двигать!

Переменная защищается так:

readonly VAR=1234


Если её попытаться переопределить, то моментально отправляешься в пешее эротическое. Возникает логичный вопрос — как её откатить обратно и сделать нормальной переменной?

В голову приходит логичный ответ, сделай так:

unset VAR


А вот и нет! Сразу получишь хуем ошибкой по башке: bash: unset: VAR: cannot unset: readonly variable.

Хм… интересное кино. А чо делать? Не использовать «readonly»! Ну а если пришлось с этим столкнутся, придется использовать древнюю магию. Мы её кстати недавно использовали в этом посте.

Чтобы заансетить константу, открываем «Ящик Пандоры»:

gdb -ex 'call (int) unbind_variable("VAR")' --pid=$$ --batch


Если получил $1 = 0, значит всё прошло успешно.

Такими консервированными переменными хорошо манипулировать во всяких конфигах, например в .bashrc и т.п. Если кто-то захочет что-то переопределить, у него ничего не получится. Защита от дурака. Но помни, что древняя магия gdb может не сработать на этом проклятии, если ты не root.

Например, можно защитить переменную PATH от вмешательств и ни одна собака его не зареврайтит. И порой даже ты сам.

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

tags: #bash #linux

@BASHDAYS
139
Любишь унижения? Рекомендую!

Здрасти. В Linux есть возможность включить «оскорбительный режим», который работает из коробки без установки дополнительного шлака.

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

Для включения, добавь в файл /etc/sudoers (либо в /etc/sudoers.d/fuck.conf) такую строчку:

Defaults insults


А после этого попробуй выполнить от пользователя:

sudo apt update


И введи неправильный пароль. Результаты бывают разные:

Take a stress pill and think things over.
You fucking stupid shit!
My mind is going. I can feel it.
Realy? Are you on drugs?


Все эти фразочки зашиты в библиотеке /usr/libexec/sudo/sudoers.so. Но на английском не интересно. Поэтому можно выводить своё сообщение через такой хак:

Делается так:

Defaults  badpass_message="Еще одна попытка и я тебя выебу!"


Предварительно закомментируй Defaults insults. Можно конечно пересобрать sudoers.so со своими фразами, но геморно. Ну либо пропатчить прям этот файл через HEX редактор.

Уверен есть способ запустить такой Bash скрипт по триггеру:

#!/bin/bash

messages=("Неверный блядь пароль! У тебя палец сломан?"
"Еще одна попытка и я тебя выебу"
"Может, тебе стоит попробовать свою девичью фамилию?")

echo "${messages[$RANDOM % ${#messages[@]}]}"


Изучай!

tags: #bash #linux

@BASHDAYS
213
Кто сеет ветер, тот пожнет бурю

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

Не спеши переключать канал, будет ОХУЕННО интересно.

Bash теперь не менее опасен чем perl.


ctypes - это не просто очередная скриптовая библиотека, это штука позволяет вызывать функции из shared библиотек на СИ, прямо из bash скриптов. Охуеть да?

Можно сказать это внештатный модуль для Bash с огромным функционалом. Где не справился Bash, справится Bash + ctypes.

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

Давай попрактикуемся. Собираем модуль ctypes.

git clone https://github.com/taviso/ctypes.sh.git
cd ctypes.sh
./autogen.sh
./configure
make
sudo make install


По умолчанию вся эта байда ставится в папку /usr/local/bin и /usr/local/lib. Но при сборке путь можно изменить через параметр PREFIX=$HOME make install.

Так собрали, установили. Чо дальше? А дальше пишем самую простую so библиотеку:

#include <stdio.h>

void hello_bashdays() {
printf("Hello, Bashdays!\n");
}


Компилируем:

gcc -shared -o /tmp/bashdays.so bashdays.c


На выходе получаем готовый файл bashdays.so с функцией hello_bashdays. Проверяем какие символы доступны в скомпилированной библиотеке:

nm -g /tmp/bashdays.so


Команда nm покажет, все глобальные функции и переменные, которые доступны в библиотеке bashdays.so. Мне выдало такое:

w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
w __cxa_finalize@GLIBC_2.2.5
w __gmon_start__
0000000000001119 T hello_bashdays
U puts@GLIBC_2.2.5


Ок. Всё верно! Символ T перед названием функции, означает, что функция глобальная. А символ U (puts) = что эту функцию можно вызвать из вне.

Ну и теперь с помощью ctypes мы можем вызвать эту функцию из Bash скрипта.

#!/bin/bash

source /usr/local/bin/ctypes.sh

dlopen "/tmp/bashdays.so"
dlcall "hello_bashdays"


Опа нихуя! На экран получаем результат выполнения СИ функции hello_bashdays. Собственно Hello, Bashdays!

И это всего лишь верхушка айсберга, если копнуть глубже…

Вот несколько отзывов на ctypes:

- Это отвратительно
- Это должно прекратиться
- Вы зашли слишком далеко
- Это шутка?

Вот такие интересные штуки существуют. Надеюсь тебе понравилось. Теперь ты знаешь как собирать shared so файлы и легко вызывать от туда функции.

Аа забыл, как заансетить переменную:

readonly VAR=123
source /usr/local/bin/ctypes.sh
dlcall unbind_variable string:VAR


Вот и все. И никаких тебе танцев с gdb. Переменная VAR обнулена.

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

🌐 Страница проекта на github


tags: #bash #linux

@BASHDAYS
Please open Telegram to view this post
VIEW IN TELEGRAM
138
Кот насрал в макбук

Причем не «На», а конкретно «Вв». Сегодня снова про гавно и котов, которые выставляют айтишников на 200к рублей.

Сторис

С утра настраивал примитивный kubernetes кластер для клиента, пилил таску с ноута, сидя на унитазе. Ну а как, удалёнка, могу себе позволить. Жил бы один, вообщеб обратно в обезьяну эволюционировал.

Короче время дейлика с другим клиентом, бросаю открытый ноут в сортире на коврик, бегу к стационарному компу где установлен zoom. 15 минут слушаю очередную хуиту от лосей, проблема не у нас, а на серверах. Классика. По привычке — выгораю, шлю нахуй, реву.

Ну и вот!

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

Ноут который я бросил на коврик… Ему был произведен основательный апгрейд +100500 к бренду.

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

А новое и тёплое всегда безумно манит. Тем более присутствует интерактив — закрыть крышку. Зона комфорта. Ну ты и сам это прекрасно понимаешь.

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

Напомнил знакомого, который, любил гадить в лифтах. Сейчас кстати работает JAVA разработчиком. Тоже сеньор, лифты заменил на GIT репозитории. Забавно, но ситуация страшная.

Сначала комбик за 50к, потом стена и диван в зале, а сегодня макбук за 200к. Хотя сам кот стоил всего 5к. Сомнительно конечно вложение было. Что дальше, хуй знает. Снова выгорел, поревел…

Такое происходит только в фильмах, но увы, порой я думаю, что живу в каком-то сценарии Тарантино.


Проанализировав произошедшее, в очередной раз убедился, что от apple и кубернейтсов одно зло. Linux / Windows / Android / Bash / Ctypes наше всё.

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

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

И всегда помни — даже если кот кастрирован, с его жопой ты ничего не сделаешь. Максимум сможешь провести ретроспективу по методологии «Scrum».

Говорят в айти охуенно весело. Справедливо!

tags: #рабочиебудни

@BASHDAYS
227