Линукс и DevOps Дни
2.04K subscribers
108 photos
8 videos
194 links
Самобытно про разработку, devops, linux, скрипты, тестирование, сисадминство, техдирство, пиэмство и за айтишную жизу.
Download Telegram
Про интересный факап и траблшутинг.

Обратился клиент — у нас mysql реплика не работает. Вернее работает, но отстала всего лишь на 3 месяца.

На вопрос — а хули вы 3 месяца сидели, у вас же мониторинг есть?

Очевидный ответ — да, есть, но оно молчит.

Ладно, полез разбираться. SHOW SLAVE STATUS\G;

А там ошибки брынчат, как хуи в бидоне.

Error 'Cannot delete or update a parent row: a foreign key constraint fails'


Ну это ладно, ошибка и ошибка, тут понятно что делать — НИЧЕГО.

Так как реплика используется чисто аналитиками на потыкать, игнорим эту ошибку через my.cfg. Быстрофикс.

Для продуктовой реплики такое делать - НЕЛЬЗЯ!

Самый важный вопрос — какого хера мониторинг 3 месяца молчал?

Тут уже интереснее. Реплика заведена в prometheus, экспортеры есть, все дела.

Но из графаны сервер пропал, хотя год назад я ее точно там видел.

Думаем… Думаем… Смотрю графану, мониторится поле: replica_seconds_behind

Хм, лезу обратно на реплику, а там сроду нет mysql_exporter. Что же это тогда такое?

Копаем вглубь и видим, что node_exporter мониторит папку /tmp на наличие файликов .prom.

Ага… То есть метрики с mysql реплики собирает какой-то bash скрипт по крону, генерит текстовичок и отдает в prometheus.

Типа такого:

replica_slave_io_running{host="replica"} 1
replica_seconds_behind{host="replica"} 1234567


Да, оно прекрасно работало, до момента пока не вылезла ошибка: Cannot delete or update a parent row

Соответственно текстовый файл replica.prom получился в таком формате:

replica_slave_io_running{host="replica"} 1
replica_seconds_behind{host="replica"} Null


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

Прикол в том, что во время возникновения ошибки, поле Seconds_Behind_Master в mysql принимает значение Null, а не продолжает дальше считать на сколько отстала реплика.

А вот и bash скрипт, который собирал метрики:

#!/bin/bash

MAINDIR=/tmp
METRICS=rstatus.prom
HOST="replica"

SLAVE_IO_RUNNING=$(mysql -e 'SHOW SLAVE STATUS \G' | grep 'Slave_IO_Running'| awk '{print $2}')
SLAVE_SECONDS_BEHIND=$(mysql -e 'SHOW SLAVE STATUS \G' | grep 'Seconds_Behind_Master'| awk '{print $2}')

if [[ "$SLAVE_IO_RUNNING" == "Yes" ]]; then
J=1
echo 'replica_slave_io_running{host="'$HOST'"}' $J > $MAINDIR/$METRICS
else
J=0
echo 'replica_slave_io_running{host="'$HOST'"}' $J > $MAINDIR/$METRICS
fi

echo 'replica_seconds_behind{host="'$HOST'"}' $SLAVE_SECONDS_BEHIND >> $MAINDIR/$METRICS


Работа над ошибками проведена, инцидент разобран. Ни одна жопа на ретроспективе пока не пострадала, но возможно дело времени.

Как писать подобные экспортеры я накидывал в этом посте.


И всегда помни — если изобретаешь велосипед, всегда обрабатывай эксепшены!

tags: #devops #debug #bash #monitoring

🔔
Тут персонаж один пристал, говорит что у него в Java есть какой-то «мусожор», а вот в Bash такого нет и поэтому от наших скриптов одни проблемы.

Термин «Мусожор» видимо профессиональный и является пресловутым «Garbage Collector».

К Java я отношусь ровно, оно меня не трогает и я его тоже.

Как говорится — Одно гавно другому не равно. Поэтому хуисосить не буду.

Давай проведем эксперимент:

1. ps -o rss -p $$
2. var=$(printf "%s\n" {1..100000})
3. ps -o rss -p $$
4. var="bashdays"
5. ps -o rss -p $$


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

Во второй строчке записываем в переменную var содержимое от 1-100000 с переносом на новую строку.

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

Ага, оно стало больше. Было 4348 стало 5276.

В четвертой строчке, перезаписываем var на строку «bashdays» и снова проверяем память.

На этот раз пятая строчка вновь показывает: 4348.

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

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

Bash не работает с памятью и объектами, так же как обычные языки программирования.

tags: #bash

🔔
И снова здравствуйте. Давай сегодня немного в кишках покопаемся.

Запускаем бессмысленную команду:

dd if=/dev/zero of=/dev/null status=progress


И наблюдаем как строка статистики постоянно меняется.

В простонародье — рефрешится.

4254480896 bytes (4.3 GB, 4.0 GiB) copied, 4.77966 s, 890 MB/s


Бесполезная? Ну не скажи, с её помощью можно протестировать скорость чтения и записи.

Эта команда копирует блоками нули из спец-файла в другой. Записывая статистику на стандартный вывод ошибок stderr.

Строка статистики постоянно меняется. Но как dd это делает? Давай разберемся ниже.

Запускаем страшную кишку и немного ждем

dd if=/dev/zero of=/dev/null status=progress |& { read -t 4; echo "${REPLY}"; }


По итогу получаем, что-то подобное:

$'\r927888896 bytes (928 MB, 885 MiB) copied, 1 s, 928 MB/s\r1851585024 bytes (1.9 GB, 1.7 GiB) copied, 2 s, 926 MB/s\r2749514752 bytes (2.7 GB, 2.6 GiB) copied, 3 s, 917 MB/s'


Портянка с read означает — ждем 4 секунды, считываем первую строчку ввода и сохраняем ее в переменную REPLY, а далее выводим содержимое переменной на экран.

Не так уж и страшно, правда?

Ааа, херовина |& нужна, чтобы объединить stdout и stderr и затем направить его в read.

Ну дак вот.

Получается утилита dd, пишет статистику за секунду в стандартный поток ошибок и затем суёт символ «\r».

Который означает - возврат каретки.

Вот и вся магия.

Делаем хак, чтобы избавиться от возврата каретки:

dd if=/dev/zero of=/dev/null status=progress |& tr '\r' '\n'


Теперь статистика выводится в столбик:

1836032000 copied, 2 s, 918 MB/s
2793336832 copied, 3 s, 931 MB/s
3726170624 copied, 4 s, 932 MB/s
4655933440 copied, 5 s, 931 MB/s


Нагляднее? Конечно!

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

Херачим Bash скрипт:


#!/bin/bash

for i in {9..20}
do
bs=$((2**i))
dd bs="$bs" if=/dev/zero of=/dev/null status=progress |& {
mapfile -d$'\r' -n 2; printf '%-8s : %s\n' "$bs" "${MAPFILE[1]}"; }
done

exit


Запускаем и получаем:

512 : 878647296 copied, 1 s, 879 MB/s
1024 : 1618295808 copied, 1 s, 1.6 GB/s
2048 : 3096260608 copied, 1 s, 3.1 GB/s
4096 : 5056294912 copied, 1 s, 5.1 GB/s
8192 : 8047247360 copied, 1 s, 8.0 GB/s


Круто? Да охуеть!

Ну а если нужна статистика за пять секунд. Делаем 5+1 к количеству записей и индексу массива. Массив начинается с нуля. Но можешь это изменить с помощью опции -O.

#!/bin/bash

for i in {9..20}
do
bs=$((2**i))
dd bs="$bs" if=/dev/zero of=/dev/null status=progress |& {
mapfile -d$'\r' -n 6; printf '%-8s : %s\n' "$bs" "${MAPFILE[5]}"; }
done

exit


По итогу имеем статистику за 5 секунд:

512 : 4667945472 copied, 5 s, 934 MB/s
1024 : 8693190656 copied, 5 s, 1.7 GB/s
2048 : 15988561920 copied, 5 s, 3.2 GB/s
4096 : 27510878208 copied, 5 s, 5.5 GB/s
8192 : 39937343488 copied, 5 s, 8.0 GB/s


Вот такие пироги. Теперь можешь тестировать VPSки и писать статью на хабру с бенчмарками.

Давай, увидимся завтра!

tags: #linux #bash

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

Сегодня разбираем проблемный Bash скрипт.

#!/bin/bash  

i=0
for name in `ls *.txt`
do
i=$[i+1]
mv $name `printf "%02d" $i`.txt
done


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

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

Для начала, давай создадим вводные, чтобы было с чем работать.

mkdir /tmp/test
cd /tmp/test
touch {001..003}.txt {01..03}.txt


Так, создали 6 файлов с разными именами:

01.txt
02.txt
03.txt
001.txt
002.txt
003.txt


Модифицируем проблемный скрипт, чтобы он выводил строку с командой mv перед её выполнением:

#!/bin/bash  
i=0
for name in `ls *.txt`
do
i=$[i+1]
echo 'DEBUG: mv' $name `printf "%02d" $i`.txt
mv $name `printf "%02d" $i`.txt
done

exit 0


Запускаем и получаем красивое:

DEBUG: mv 001.txt 01.txt
DEBUG: mv 002.txt 02.txt
DEBUG: mv 003.txt 03.txt
DEBUG: mv 01.txt 04.txt
DEBUG: mv 02.txt 05.txt
DEBUG: mv 03.txt 06.txt


Смотрим что у нас осталось, хуяк и вместо 6 файлов, у нас осталось всего лишь 3 штук.

ls

04.txt
05.txt
06.txt


Куда блядь делось остальное? Вокруг да около ходить не будем.

Дело в том, что в одном каталоге не может быть двух файлов с одинаковым названием. Очевидно? Да!

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

mv <старое имя> <новое имя>


Если <новое имя> это существующее имя файла, тогда оно будет удалено, а <старое имя> получит имя <новое имя>.

Если взять нашу ситуацию, то произошло следующее:

1. DEBUG: mv 001.txt 01.txt 
2. DEBUG: mv 002.txt 02.txt
3. DEBUG: mv 003.txt 03.txt
4. DEBUG: mv 01.txt 04.txt
5. DEBUG: mv 02.txt 05.txt
6. DEBUG: mv 03.txt 06.txt


1. Удалено 01.txt. и 001.txt -> 01.txt
2. Удалено 02.txt. и 002.txt -> 02.txt
3. Удалено 03.txt. и 003.txt -> 03.txt

Итого потеряли 3 файла, печаль.

Теперь давай более наглядно. Удаляем подопытные файлы и создаем новые. Плюс запишем в каждый файл его имя.

for i in {001..003}.txt {01..03}.txt; do echo $i > $i;done


Получилось 6 файлов, в каждом файле содержится имя самого файла.

Смотрим что получилось:

ls -la

8 Jul 10 08:05 001.txt
8 Jul 10 08:05 002.txt
8 Jul 10 08:05 003.txt
7 Jul 10 08:05 01.txt
7 Jul 10 08:05 02.txt
7 Jul 10 08:05 03.txt


Снова модифицируем скрипт и задаем опцию -b для утилиты mv. Теперь для существующего файла будет создаваться резервная копия. Бекап ёпта!

#!/bin/bash  
i=0
for name in `ls *.txt`
do
i=$[i+1]
echo 'DEBUG: mv' $name `printf "%02d" $i`.txt
mv -b $name `printf "%02d" $i`.txt
done
exit 0


Выполняем скрипт и смотрим что у нас осталось от файлов.

ls -la

7 Jul 10 08:05 01.txt~
7 Jul 10 08:05 02.txt~
7 Jul 10 08:05 03.txt~
8 Jul 10 08:05 04.txt
8 Jul 10 08:05 05.txt
8 Jul 10 08:05 06.txt


Теперь смотрим содержимое самих файлов и преисполняемся.

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

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

Изучай, всего тебе самого наилучшего!

tags: #linux #bash

🔔
Привет. Сегодня нассым в чайник.

А если конкретнее, рассмотрим более подробно команду man

Это с виду она дохуя скучная, на деле там всё по полочкам.

Man содержит разделы от 1 до 9 + дополнительные (n, l, p). В дополнительные разделы входят всё то, что по каким-то причинам не попало в первые 9.

Все это дело сконфигурировано в файле: /etc/manpath.config, в него можно не смотреть, там хуйня какая-то непонятная для не подготовленного человека.

Ну дак вот. В каждом man разделе, есть страница intro. То бишь «вступление», которое описывает что содержит раздел.

Давай тыкать палкой.

Ставим пакет:

apt install manpages-dev


Запускаем:

man 1 intro


Выведется список с содержимым 1го раздела:

- Login
- The shell
- Pathnames and the current directory
- Directories
- Disks and filesystems

и т.п.


Теперь пройдемся по разделам:

1. Справка по стандартным командам (программы и утилиты). Это самый огромный раздел в справочной системе. Глаза разбегутся. Но есть лайкфак, можно воспользоваться поиском.

man -k copy


Выведется ограниченный список команд и утилит, которые содержат слово «copy»

cp (1) - copy files and directories
cpgr (8) - copy with locking the given file
cpio (1) - copy files to and from archives
cppw (8) - copy with locking the given file
dd (1) - convert and copy a file


В скобках выводится цифра раздела, в котором была найдена справка. А дальше можно сделать например так:

man 8 cppw


Херак и ты получил желаемое. С поиском намного удобнее и приятнее работать.

2. Раздел содержит описание системных вызовов. В этом разделе можно найти понимание этих ваших линуксов. Например, смотрим справку по mkdir.

man 2 mkdir


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

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

4. Описание файлов-устройств расположенные в /dev

5. Описание форматов файлов. Конфигурационные файлы приложений и их синтаксис. Например, синтаксис файла теневых паролей.

man 5 login.defs


Чтиво весьма занятное. Именно в этом файле задаются «SYS_UID_MAX», «SYS_UID_MIN». Это диапазон UID, который будет присвоен пользователю, при использовании утилит «useradd» или «newusers» когда, ты добавляешь нового пользователя.

6. Раздел игр.

7. Описание стандартов и соглашений. Например, последовательность загрузки ОС.

man 7 boot


или параметры ядра при загрузке:

man 7 bootparam


или шаблоны имён файлов:

man 7 glob


8. Команды для системного администрирования. Например, информация о портах/сокетах:

man 8 netstat


монтирование устройств:

man 8 mount


отображение информации о дисках и их настройка:

man 8 hdparm


9. Функции ядра. В большинстве случаев этот раздел отсутствует в современных дистрибутивах. Раздел не является стандартом. Если нужно узнать про функции ядра, тут только поиск через man -k <ключевое слово>.

Так же ты можешь встретить разделы n, l, p, но опять-же они не всегда присутствуют, все зависит от того, какой софт ты ставил себе на машину.

Ну и закончим эту простыню вишенкой. Для этой команды нужны Иксы.

apt install man-db groff
man -H bash


Страница со справкой Bash будет отформатирована в «html» формате и открыта в браузере по умолчанию.

Без Иксов просто сформируется «html» файл в папке tmp, у меня он появился тут: /tmp/hmanU3yORv/bash.html

Ладно, изучай. Увидимся завтра!

PS: Все кто подал заявки на вписку в Linux factory, все ок. Как только решу вопросы с эквайрингом Тибанка (бекенд у них отвалился), всем напишу. Терпения вам и мне дорогие подписчики. Всех обнял!


tags: #linux #bash

🔔
Ёпта! Работа над Linux Factory идет полным ходом, всем кто принес мне свой емейл в бота, все котики и записаны.

Кому я еще не ответил — отвечу, ничо не потерялось. Я тут в одну харю ща всё разгребаю и отбиваюсь от менеджеров тибанка с помощью их же разработки — автоответчика Олега.

В начале следующей неделе буду рассылать инвайты в закрытый канал, ну и инструкции чо каво делать.

✔️ Так, теперь по сегодняшней теме — Awk и пасхальное яйцо.

Если запустить:

awk --nostalgia


Всё нахуй упадет с ошибкой — awk: bailing out near line 1 Aborted

nostalgia это не документированная опция, которая была добавлена в версии 2.11 beta и сопровождалось странным комментарием:

undocumented feature, inspired by nostalgia, and a T-shirt


Чо за херня?

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

Еще из интересного. Gawk использует GNUтое расширение предоставляемое функциями разбора аргументов командной строки. При активации этого расширения доступен специальный синтаксис -W long_option.

awk -Wnos
awk -Wcopy


Эти команды аналогичны:

awk --nostalgia
awk --copyright


Странное конечно поведение учитывая содержимое этого поста.

Ну да ладно, хуй с ним. Чо только не придумают…

Хороших тебе предстоящих выходных и береги себя.

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


Ааа еще забыл, Tagd Tagd написал нам две пиздатых статьи, на днях выложу. Спасибо Tagd Tagd с меня бонусы!

tags: #linux #bash

🔔
Сегодня мы рассмотрим второй способ, как запилить SOCKS через ssh.

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

Как-то видел анимацию сисек при долгом копировании, но сейчас не нашел. Может кто подскажет... Варианты анимации здесь.

sudo vim /usr/local/bin/socksshell

#!/bin/bash
trap 'echo -e "\\nWaiting session end...";exit' SIGINT SIGTERM SIGHUP SIGQUIT
printf "VPN on. Press CTRL-C for BREAK "
while :;do
for i in '> ' '>>' ' <' '<<';do
printf "\b\b$i"
sleep 3
done
done

sudo chmod 755 /usr/local/bin/socksshell


Скрипт не дает пользователю ничего делать, но своей анимацией поддерживает соединение в рабочем состоянии. Если не нравится shell на скрипте - поищите в сети Sleep Dummy Shell он на си.

В файле /etc/ssh/sshd_config

Match User bashdays_tagd
MaxAuthTries 3
MaxSessions 10
PasswordAuthentication no
PermitEmptyPasswords no
PubkeyAuthentication yes
allowtcpforwarding yes
ForceCommand /usr/local/bin/socksshell
#--


Опция ForceCommand, фактически, заменяет оболочку для ssh, если захотите заменить оболочку пользователя в глобально системе (не обязательно):

sudo usermod -s /usr/local/bin/socksshell bashdays_tagd
sudo service sshd restart


А на клиенте:

ssh -D 5000 bashdays_tagd.host.name


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

В firefox Настройки -> Настройка сети -> Параметры соединения: Ручная настройка прокси. SOCKS5. 127.0.0.1 порт 5000.

+ галка Отправлять DNS-запросы через прокси при использовании SOCKS5.

или

ssh -D 192.168.1.57:5000 bashdays_tagd.host.name


Если ваш адрес 192.168.1.57 и вы человек еще более добрый чем я и решили поделиться вашем счастьем со всей своей локалкой.

В настройка firefox нужно будет заменить 127.0.0.1 на 192.168.1.57

Продолжение следует... © by Tagd Tagd

tags: #linux #networks #bash

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

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

Итак: Шёл Хер по херу, встретил Хера на херу, взял Хер Хера за хер, и к херам закинул нахер.

Вот, а сейчас говорят, что пионеров ничему хорошему не учили... exec.

Есть два варианта использования:

1. exec command — билет в один конец. Для чего это нужно я так и не понял, может умные в комментах напишут. Перед выполнением команды, bash фактически завершается, управление передаётся команде. И все...

пример:

exec echo 123 — результат очень красочный. Такой же, как и с любой другой командой.

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

В Basic open "filename.txt" for input as #3
В bash exec 3<fileneme.txt
В Basic open "filename.txt" for output as #4
В bash exec 4>filename.txt
В Basic open "filename.txt" for append as #5
В bash exec 5>>filename.txt
В Basic open "filename.txt" for random as #6
В bash exec 6<>filename.txt

Приведу несколько полезных примеров

exec 2>/dev/null — все сообщения об ошибках отправить по пути, указанному в считалочке.

На код ошибки (типа errorlevel) это не влияет.

exec 1>filename.txt все сообщения, кроме ошибок отправить в файл.
exec 1>filename.txt 2>&1 все сообщения, включая ошибки отправить в файл.

Стоп-стоп. Откуда взялся &? Легко запомнить. Это костыль. Если вы не укажите &, сообщения попадут в файл с именем "1".

Все помнят, что 0=/dev/stdin 1=/dev/stdout 2=/dev/stderr. Остальные 3-9 можем использовать по собственному усмотрению.

Пример:

#!/bin/bash
seq 1 3|tee 1.txt > 2.txt
exec 3<1.txt
while read -u3 L1;do
exec 4<2.txt
while read -u4 L2;do
echo $L1 $L2
done
done


seq — создаем два файла, по три строки в каждом
exec 3<1.txt - открываем файл для чтения с дескриптором 3
read -u3 L1 читаем из файла с дескриптором 3. Дальше все аналогично.

Если нужно закрыть 4, например, дескриптор за ненадобностью exec 4>&-

man tee seq
help while read exec


tags: #bash © by Tagd Tagd

🔔 ➡️
Привет. В дополнение к предыдущему посту про свистоперделки.

Чтобы каждый раз не вводить такую дичь:

ssh -i ~/.ssh/id_rsa user.xxx.xxx.xxx


Ключик можно автоматически добавлять в ssh-agent и тогда подключение будет выглядеть так:

ssh user.xxx.xxx.xxx


Чтобы это провернуть, прописываем в ~/.profile

if [ -z "$SSH_AUTH_SOCK" ]; then
eval $(ssh-agent -s) > ~/.ssh/ssh-agent
fi

ssh-add ~/.ssh/gitlab_rsa
ssh-add ~/.ssh/production_rsa
ssh-add ~/.ssh/home_rsa


Первый блок проверяет запущен ли SSH агент, а если нет — запускает его.

Второй блок добавляет нужные ключи в агент. Посмотреть все ключи в агенте можешь командой ssh-add -L, а удалить всё из агента можно командой ssh-add -D.

Вот и вся наука. Ну а теперь интерактивная часть.

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

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

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

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

🔔 ➡️
Здрасти. К делу!

Гиту откровенно насрать на права доступа файлов. И после клонирования репы, эти права будут установлены по umask пользователя.

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

На помощь приходит костыль, всё в лучших традициях!

#!/bin/bash

find . -type f -exec stat --format='%a %n' {} \; > permissions.txt
git add permissions.txt
git commit -m "на залупе лешего вертели"
git push


Пробегаемся по всем файлам и каталогам, записываем текущие права в файл permissions.txt, коммитим, пушим.

Ну и скрипт для восстановления прав:

#!/bin/bash

if [ -f permissions.txt ]; then
while read perm file; do
if [ -f "$file" ]; then
chmod "$perm" "$file"
fi
done < permissions.txt
fi


Вот и вся наука. По желанию вешаешь это на Git Hook (post-checkout, post-merge) и автоматизируешь.

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


А еще есть git-restore-mtime, для восстановления метаданных времени модификации файлов.

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

tags: #linux #bash

🔔 ➡️
Аутентификация, авторизация. Навеяло.

Есть два вида запоминания паролей - хранение и связанная генерация. Это когда ты вводишь логин, а программа на основании его и модификатора (соли) вычисляет пароль.

Хранить лучше в программах типа keepass и аналогах. Сейчас затронем тему генерации.

Есть такая известная программа как pwgen. Просто чудесная. Столько всего умеет. В том числе и связывать логин и пароль. Проблема только в том, что при генерации командная строка может попасть в history.

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

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

Модификатор - это что-то типа парольной фразы при использовании ключа.

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

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

Модификатор может набираться с клавиатуры видимом и скрытом режимах. Все зависит от агрессивности коллектива.

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

Да, совсем забыл, параметр PWGEN_OPT - дополнительные опции генерации пароля НЕЛЬЗЯ менять после начала эксплуатации программы.

Программа pwgen должна быть установлена.

Для debian sudo apt install pwgen

declare -r PWGEN_OPT='-1 --symbols --ambiguous --capitalize'
declare -r PASSLEN=15
declare -r PASSNUM=5
declare -r KEYFILE=${0%/*}/'1.jpg'
declare -l HIDDENMOD=0
declare -r PWGEN=$(which pwgen)
declare LOGIN
declare MOD MOD1

if [[ ! "$PWGEN" ]];then
echo pwgen not installed
exit 1
fi
if [[ ! -s "$KEYFILE" ]];then
echo keyfile "$KEYFILE" not found
exit 2
fi
read -p "Input login:" LOGIN
if [[ "1y" =~ "$HIDDENMOD" ]];then
read -s -p "Input modificator:" MOD;echo
read -s -p "Confirm modificator:" MOD1;echo
if [[ "$MOD" != "$MOD1" ]];then
echo "Modificator not confirmed"
exit 3
fi
else
read -p "Input modificator:" MOD;echo
fi
echo pwgen options: $PWGEN_OPT
echo "Login: $LOGIN"
$PWGEN $PWGEN_OPT -H"${KEYFILE}#${LOGIN}${MOD}" $PASSLEN $PASSNUM


сохраняем в файл passgen.sh


chmod +x passgen.sh
./passgen.sh


По умолчанию ввод модификатора отображаемый. Для параноиков - установить HIDDENMOD=1 результат работы примерно такой:

Input login:tagd.ru
Input modificator:BashDaysTheBest

pwgen options: -1 --symbols --ambiguous --capitalize
Login: tagd.ru
aig3ohkie.Wah4X
AiguW~u7vohphae
eiJa7ahxei.die!
FaeNa=phah9voh3
Kih]ahca3Hie7ke


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

➡️ Тыкни сюда, чтобы посмотреть описание работы программы.

Всё, пароли хранить не нужно - ввели логин и модификатор - получили пароли. При компрометации просто используете следующий. Если увеличить число генерируемых паролей - начальные пароли будут те же.

Почитать:

help declare read
man pwgen
https://cheatsheets.zip


tags: #bash #linux © by Tagd Tagd

🔔 ➡️
Порой бывает необходимо прям из bash-скрипта добавить/удалить какое-нибудь задание в cron.

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

{ crontab -l; echo '# echo BashDays >/tmp/BashDays.txt' ; }| crontab -


ИЛИ:

{ crontab -l; echo '#*/10 * * * 5 echo BashDays >>/tmp/BashDays.txt' ; }| crontab -


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

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


#!/bin/bash

function ADD_CRON_TASK(){
local CRON_TASK=${1:-'#'}
local COMMENT=${2:-$(date +%s)}
COMMENT=${COMMENT// /_}
{ crontab -l 2>/dev/null; echo "$CRON_TASK #$COMMENT"; }| crontab -
}

function REMOVE_CRON_TASK(){
if [[ $# -eq 0 ]];then
echo No comment to remove cron task >&2
return
fi
COMMENT=${1// /_}
crontab -l 2>/dev/null|sed '/#'$COMMENT'$/d'|crontab -
}

CRON_TASK='# reboot'
COMMENT="My litle joke"
ADD_CRON_TASK "$CRON_TASK" "$COMMENT"

crontab -l|tail -3

echo '###################################################'
REMOVE_CRON_TASK "$COMMENT"

crontab -l |tail -3


Если запустить программу, увидите что-то типа:

# 
# m h dom mon dow command
# reboot #My_litle_joke
#############################
# For more information see the
#
# m h dom mon dow command


Видно, что программа добавила задание, вывела последние три строки crontab'a, потом разделитель.

Затем задание было удалено. Это видно по последним трем строкам.

В комментариях задания пробелы заменяются на "_" для упрощения работы c sed.

Ну, и на всякий пожарный напомню, что для корректной работы с cron нужно задать переменную PATH либо в crontab, либо в скрипте.

tags: #bash #linux © by Tagd Tagd

🔔 ➡️
У меня возникла проблема. Мне нужно переконфигурировать сеть на железном сервере, который находится в другом городе. И командировка туда займет пару дней.

Проблема усугубляется тем, что на сервере нет ipmi или аналогов, кроме того, в окружении нет админа. Поэтому конфигурировать сеть нужно по ssh.

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

Скрипт поможет в случае чего восстановить конфигурацию, в случае моего косяка.

Алгоритм работы следующий:

0. Есть работающий сервер.

1. При запуске создается резервная копия защищаемого каталога конфигурации /etc

2. В crontab добавляется задание, которое через определенное время ПОСЛЕ ПЕРЕЗАГРУЗКИ восстановит каталог из копии.

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

4.Если пользователь не смог войти в систему - сначала создается вторая резервная копия защищаемого каталога (на тот случай, если сконфигурировали все правильно, но забыли/не смогли войти из-за проблем на вашей стороне, ну и для анализа), после этого производится восстановление резервной копии из пункта 1, убирается задание из crontab и производится перезагрузка сервера. Сервер приведен к пункту 0.


Пользоваться этой цацой просто:

1. ПЕРЕД НАЧАЛОМ КОНФИГУРИРОВАНИЯ запускаем скрипт, Он запрашивает имя пользователя и время ожидания до восстановления ПОСЛЕ ПЕРЕЗАГРУЗКИ.

2. Конфигурируем сервак.

3. Отправляем сервак в перезагрузку. (Да, не лучший вариант, но по сравнению с командировкой фигня).

4. Пытаемся войти по ssh.


По ssh мы войдем в любом случае. Или быстро (если сервак сконфигурировали правильно) или по окончании времени ожидания и перезагрузки.

Обращаю внимание - если пользователь смог войти в систему - Сервер считается работоспособным, задание из cron убирается.

И если вы решите что-то снова поконфигурировать - нужно снова запускать скрипт и один-два раза перезагружать сервак. Лучше составьте план действий.

Скрипт тестировался на debian путем удаления каталога /etc/network

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

Скрипт не сможет восстановить конфигурацию, если машина не грузится совсем (ну, например грохнули fstab)

На Alpine точно не работает по двум причинам:

1. crontab не понимает опцию (проблема решаемая через /etc/inid.d)

2. по умолчанию не работают команды last, who, w. Поэтому несколько проблематично определить наличие пользователя в системе.

➡️ Скрипт в комментариях к этому посту, сюда не влез сука!

tags: #bash #linux © by Tagd Tagd

🔔 ➡️
Принцесса для снятия стресса

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

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


<прилагательное><фамилия> и всегда написаны в нижнем регистре.

Например:

quirky_pare
agitated_turing
furious_heisenberg
romantic_curie


Окей. После некоторого ресёрча оказалось, что 90% докера написано на Golang. Вот это поворот!

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

➡️ Посмотреть можешь тут.

Мотаем этот код в самый низ и видим забавную хуйню:

if name == "boring_wozniak" /* Steve Wozniak is not boring */ {
goto begin
}


Этакая пасхалочка. Но зачем все эти имена?

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

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

#!/bin/bash

adjectives=(
"adoring" "agitated" "amazing" "angry" "awesome" "blissful" "bold"
"boring" "brave" "clever" "cool" "compassionate" "competent" "crazy"
"dazzling" "determined" "ecstatic" "elegant" "eloquent" "epic"
"exciting" "fervent" "focused" "furious" "gallant" "gifted" "goofy"
"gracious" "happy" "hardcore" "hopeful" "hungry" "infallible" "inspiring"
"jolly" "jovial" "keen" "kind" "laughing" "loving" "magical"
"mystifying" "naughty" "nervous" "nostalgic" "optimistic" "peaceful"
"practical" "priceless" "quirky" "recursing" "relaxed" "reverent"
"sad" "serene" "sharp" "silly" "sleepy" "stoic" "strange" "stupefied"
"suspicious" "tender" "thirsty" "trusting" "unruffled" "vibrant"
"vigilant" "wizardly" "wonderful" "youthful" "zealous" "zen"
)

scientists=(
"albattani" "allen" "archimedes" "ardinghelli" "babbage" "bashdays" "banach"
"banzai" "bardeen" "bartik" "bassi" "bell" "benz" "bhabha" "bhaskara"
"blackwell" "bohr" "booth" "borg" "bose" "boyd" "brahmagupta" "brattain"
"brown" "carson" "chandrasekhar" "colden" "cori" "cray" "curie"
"darwin" "davinci" "dijkstra" "dubinsky" "easley" "einstein"
"elion" "engelbart" "euclid" "euler" "fermat" "fermi" "feynman"
"franklin" "galileo" "galois" "goldberg" "goodall" "hawking"
"heisenberg" "hermann" "herschel" "hertz" "hodgkin" "hoover" "hopper"
"hypatia" "joliot" "kalam" "keller" "kowalevski" "lalande" "leavitt"
"lichterman" "liskov" "lovelace" "mayer" "mccarthy" "mcclintock"
"mclean" "mcnulty" "meitner" "mendel" "mendeleev" "minsky" "mirzakhani"
"moore" "napier" "newton" "nobel" "noether" "panini" "pare"
"pasteur" "payne" "perlman" "pike" "poincare" "ptolemy" "raman"
"ramanujan" "ride" "ritchie" "roentgen" "rosalind" "saha"
"sammet" "shockley" "sinoussi" "stallman" "stonebraker" "swanson"
"tesla" "thompson" "torvalds" "turing" "varahamihira" "visvesvaraya"
"wilbur" "wiles" "williams" "wilson" "wing" "wozniak" "wright"
"yonath"
)

generate_container_name() {
local adjective="${adjectives[RANDOM % ${#adjectives[@]}]}"
local scientist="${scientists[RANDOM % ${#scientists[@]}]}"
echo "${adjective}_${scientist}"
}

count=${1:-10}
for ((i=1; i<=count; i++)); do
generate_container_name
done


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

Теперь ты в праве заполнить массивы своими данными и генерировать уникальные имена, например: pizdatiy_huy или ohuennaya_pizda.

А потом использовать сразу в командах:

docker run --name $(bash script.sh) -d nginx


Есть еще такая штука:

alias dn='curl -s https://frightanic.com/goodies_content/docker-names.php'


Это выплюнет тебе подобное имя прям в консоли без скриптов и приседаний.

А еще есть библиотека на питоне — codenamize, которая проворачивает тоже самое.

Короче есть из чего выбрать. В общем пользуйся, прикручивай, изучай!

tags: #linux #bash #docker

🔔 ➡️
Bash генератор OTP для 2FA

Я давненько пользуюсь Authy. Это генератор OTP кодов для 2FA. Большим плюсом было — что этот генератор работал на десктопе.

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

TOTP (Time-based One-Time Password) — это алгоритм, используемый для генерации одноразовых паролей (OTP), которые действуют в течение ограниченного времени.


Короче в мою зону комфорта беспощадно насрали.

Каждое случившиеся гавно это повод изобрести велосипед что-то своё.

Пошло оно всё нахуй! Будем генерить коды Bash скриптом!

Для начала нам понадобится утилита zbar, она позволит из терминала распознать qr код, этот код выдают сервисы где включается 2FA.

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

sudo apt install zbar-tools


Далее пиздуем например в gitlab и включаем 2FA, в ответ оно тебе выплюнет QR код. Делаем скриншот QR и сохраняем.

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

zbarimg gitlab_qr.png


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

QR-Code:otpauth://totp/gitlab.com:gitlab.com_hello%40devopsina.ru?secret=1234567890ABCDEFG&issuer=gitlab.com

scanned 1 barcode symbols from 1 images in 0.02 seconds


Замечательно. Из этой кишки нам нужен только secret=1234567890ABCDEFG.

Устанавливаем вторую утилиту:

sudo apt install oathtool


Накидываем bash скрипт:

#!/bin/bash

SECRET="1234567890ABCDEFG"
echo "Gitlab 2FA code: $(oathtool --totp -b "$SECRET")"


Запускаем и получаем желаемый OTP код, вводим его в Gitlab и охуеваем. Мы успешно прошли 2FA. Всё работает!

Базу расковыряли. Теперь нужно какое-то человеческое решение. Накидываем еще один bash скрипт:

#!/bin/bash

secrets=(
"GitLab:1234567890ABCDEFG"
"Google:1234567890ABCDEFG"
"Bashdays:1234567890ABCDEFG"
)

for i in "${!secrets[@]}"; do
IFS=":" read -r service secret <<< "${secrets[i]}"
echo "$((i + 1)). $service"
done

read -p "Введите номер сервиса: " choice

if [[ "$choice" -gt 0 && "$choice" -le "${#secrets[@]}" ]]; then
IFS=":" read -r selected_service selected_secret <<< "${secrets[choice - 1]}"
TOTPCODE=$(oathtool --totp -b "$selected_secret")
echo "TOTP код для $selected_service: $TOTPCODE"
else
echo "Неверный выбор."
fi

read -p ""


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

Что-то вроде:

secrets_file="secrets.txt"

while IFS= read -r line; do
secrets+=("$line")
done < "$secrets_file"


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

1. Gitlab
2. Google
3. Bashdays

Введите номер сервиса:


Вводим нужный номер сервиса и получаем актуальный OTP код.

Всю эту хуйню можно легко запилить в телеграм бота и генерить OTP коды прям в там не отходя от кассы. А также сделать автоматическую распознавалку QR с добавлением новых сервисов в список.

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

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

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

Вот такой хуйнёй переодически приходится заниматься.

Ладно, чо. Хорошего тебе дня, увидимся!

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

🔔 ➡️