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

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

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

MAX: https://max.ru/bashdays

Курс: @tormozilla_bot
Блог: https://bashdays.ru
Download Telegram
Почему 99% девопсов жирные?

Ну ясно понятно, из-за стресса, а не из-за того, что много жрут и ведут сидячий образ жизни.

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

✔️ Утилита называется — debsums

Она предназначена для проверки целостности файлов, которые установлены пакетным менеджером. Вроде хуйня, а НЕТ! Инструмент — мастхэв.

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

Откуда берутся эталонные хэши?

При установке пакетов и т.п. все эталонные, контрольные суммы попадают в «базу данных», которая находится тут: /var/lib/dpkg/info.

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

Например, nginx-common.md5sums

dba41b  system/nginx.service
17d6d7 package-hooks/source_nginx.py
9e33ba nginx-common/NEWS.Debian.gz
6c278a nginx-common/README.Debian


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

У меня в Селектел на убунте 22 уже была предустановлена, хотя локально её не было. Имей это ввиду.

Запускаем так

debsums -a


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

Если нужно проверить только измененные:

debsums -ac


Опять же очень полезно при миграциях с сервера на сервер.

А для проверки только конфигов:

debsums -ae


Например, я в душе не ебал что файл php.ini кто-то правил, а debsum мне это подсказал. И я не зафакапил.

После отработки команды, имеем список измененных файлов:

/etc/crontab
/sbin/start-stop-daemon
/etc/modprobe.d/blacklist.conf
/etc/nginx/sites-available/default
/etc/php/8.1/fpm/pool.d/www.conf
/etc/sysctl.conf
/etc/sudoers
/etc/pam.d/sudo
/etc/systemd/zram-generator.conf


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

Указав ключ -x весь её высер можно сразу выгрузить в файл. В отчет попадут только измененные файлы.

Из минусов — пиздец долго всё ищет (при полной проверке). Запускаем и идем пить кофе/смотреть мультики. Но это приятное ожидание, плюсы покрывают минусы.

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

Подобных утилит много, из тех что я знаю: AIDE, Tripwire, rkhunter, chkrootkit, ossec, Lynis. Присмотрись и к ним, возможно найдешь среди них что-то интересное под свои задачи.

Ну вот и всё. Пойдука я чайку хапну. Хорошего тебе дня!

tags: #linux #utils

@ВАSНDАYS | BАSHDАYS.CОM
Please open Telegram to view this post
VIEW IN TELEGRAM
135
Привет друзья. Готовы поработать в субботу и потом отдохнуть 4 дня?

Жаль что подобные графики справедливы для рядовых айтишников. А нам инженерам приходится держать оборону 24/7/365. Но в этом есть и свои плюсы.

Теперь по делу. Иногда клиент очень хочет оставить авторизацию на Linux сервера по ssh по паролю.

Я таких никогда не уговариваю использовать ключи, ну хочет человек, ради бога.

Возможно когда его забрутфорсят, мозги встанут на место.

Ладно. Как жить с паролями? Обычно! Я выполняю 3 действия:

1. При первом логине запрашиваем смену пароля
2. Пароль должен отвечать заданному шаблону
3. Ну и естественно втыкаю fail2ban

Давай теперь по каждому пункту.

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

adduser newuser
chage -d0 newuser


Сначала заводим пользователя, а потом ставим на него черную метку. Через ключ -d говорим через сколько дней запросить смену пароля. У меня это 0, то есть поменять при первом логине.

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

Теперь как сделать шаблон пароля.

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

Тут посложнее. Будем использовать модуль libpam-pwquality.

apt install libpam-pwquality


Открываем файл /etc/security/pwquality.conf и настраиваем под свои хотелки:

minlen  = 16
dcredit = -1
ucredit = -1
lcredit = -1
ocredit = -1


Длина пароля, маленькие и большие буковки, спецсимволы и т.п.

Да, нужно включить этот модуль в файле /etc/pam.d/common-password

password requisite pam_pwquality.so retry=3


Теперь при смене паролей, будут ошибки:

The password contains less than 1 uppercase letters
The password contains less than 1 non-alphanumeric characters
The password is shorter than 16 characters


Пока пароль не будет соответствовать заданному шаблону.

Ну а дальше настраиваешь fail2ban по мануалам из гугла или ютуба и радуешься.

Но вообще не рекомендую подобной херней заниматься и делать сразу всё на ключах + по возможности настраивать iptables.

✔️ Стоит ли менять 22 порт на другой?

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

Как отключить логи я писал в этом посте.

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


Ну и для прикола можешь Port knocking настроить, если уж совсем делать нехуй.

Всем котиков и бородатых мужиков! 🥳😲

tags: #linux

@ВАSНDАYS | BАSHDАYS.CОM
Please open Telegram to view this post
VIEW IN TELEGRAM
1292
Про вербальный секс

Здрасти. Вот вам еще одна задачка из моего загашника, которую я скидываю на собесах, размять так сказать мозги кандидату.

Задачка придумана инженерами NASA, специально для будущих разработчиков. Которым в будущем предстоит писать прошивки для их невзъебенных спейс шаттлов и AI гавноходов.


Хм, а ты думал они ни СИська пишут? Нет конечно, там чистый Bash!

Ладно, это пена, задача основана на реальных кейсах.

Сейчас будет что-то из оперы Javascript, но касаемо Bash. Устраиваемся поудобнее, наливаем чаёк/кофеёк. Поехали!

true
echo $?

false
echo $?

echo $((true == false))


Первый блок возвращает 0, второй соответственно 1. А что вернет третий? Очевидно 0? А вот и хуй там плавал! Вернет 1.

Знаешь почему? Потому что ты мыслишь шаблонами, да true = 0, false = 1, но ты упустил саму суть.

Операция == внутри $(()) не является равенством возвращаемых СТАТУСОВ в Bash. Она выполняет проверку равенства двух чисел.

True и False обрабатываются как переменные, но этим переменным не присвоены никакие значения. Поэтому они интерпретируются как 0.

echo $((true))
echo $((false))


В обоих случаях ты получишь 0. Получается так:

echo $((0 == 0))


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

✔️ А как сделать правильно?

Ну например так:

true
TRUE=$?
false
FALSE=$?
if (( $TRUE == $FALSE )); then echo TRUE; else echo FALSE; fi


Теперь всё встало на свои места. Мы объявили переменные и задали им значения. А потом уже сравниваем СТАТУСЫ и не РАВЕНСТВО двух чисел.

Такие дела. Вроде безобидная штука, а на логику пиздец как влияет.

Поэтому всегда будь внимателен. И заранее проверяй такие штуки перед деплоем в продакшен. Хотя совет дурацкий, согласен. Проще раздеплоиться и потом уже на проде пофиксить. Бест практики ёпта!


Ладно, не болей, увидимся!

tags: #bash

@ВАSНDАYS | BАSHDАYS.CОM
Please open Telegram to view this post
VIEW IN TELEGRAM
131
Возможно ты когда-нибудь замечал, что при вводе команды history, иногда рядом с командой отображается какая-то звездочка. Выглядит это примерно так:

 1990 htop
1991* ls -la
1992 uptime


Смущает? Неа! Но доебаться стоит, откуда она вообще взялась и что она означает. Не просто же так она там появилась.

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

gavno
free
bashdays
history


Видим такое:

 1997  gavno
1998 free
1999 bashdays
2000 history


✔️ Делаем финт ушами:

1. Жмем в консоли ctrl+r
2. Вводим команду bashdays
3. Нажимаем стрелку вверх
4. Отображается предыдущая команда free
5. Удаляем через backspace команду free
6. Нажимаем стрелку вниз
7. Видим появилась строка bashdays
8. Выполняем history

 1997  gavno
1998*
1999 bashdays
2000 history


Видим что команда free пропала и вместо нее появилась какая-то всратая звездочка. Это всё потому что мы отредактировали команду free через backspace.

Если backspace заменить и не стирать free, а добавить несколько букв: freezer, то выхлоп будет таким:

 1997  gavno
1998* freezer
1999 bashdays
2000 history


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

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

А еще есть хак. Если нажать ctrl + r, ввести команду, которая помечена звездочкой, а затем нажать ctrl + shift + -. То редактирование откатится и звездочка пропадёт.

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


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

В официальной документации по Bash по этому поводу есть лишь такое упоминание: Lines prefixed with a * have been modified. Скудненько.

Такие дела.

tags: #bash #linux

@ВАSНDАYS | BАSHDАYS.CОM
Please open Telegram to view this post
VIEW IN TELEGRAM
1021
Алиасы штука удобная, со временем они становятся зоной комфорта, экономят время, особенно на локальной машине. Но как использовать готовые алиасы в своих bash скриптах?

Сегодня про это и поговорим. Смотри, есть у тебя алиас в .bashrc:

alias glist="ls -la"


Тут понятно, вводим glist а оно по умолчанию интерпретируется как ls -la. А теперь напишем простой bash скрипт:

#!/bin/bash

alias
echo "hello bashdays"
glist


И вот незадача. Получаем ошибку: glist: command not found

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

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

И как быть?

Городить в самом скрипте всё необходимое? Ну можно и так, но есть способ изящнее.

Изменяем скрипт:

#!/bin/bash -i

alias
echo "hello bashdays"
glist


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

✔️ Теперь при запуске скрипта:

1. Выведется список всех алиасов
2. Выведется строка из echo
3. Отработает алиас glist == ls -la

Можно конечно еще и через source, shopt -s expand_aliases и прочие велосипеды подобное сделать, но через ключик -i более нативнее что-ли получается.

Если же нет возможности поправить скрипт, как вариант, прописываем в bashrc такую строчку:

function glist () { command ls -la "$@"; }; export -f glist


Теперь даже без ключа -i функция glist будет работать в любом скрипте без вмешательства в него.

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

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

tags: #bash #linux

@ВАSНDАYS | BАSHDАYS.CОM
Please open Telegram to view this post
VIEW IN TELEGRAM
1381
⚡️⚡️⚡️⚡️⚡️⚡️

Linux умер! Все из вас хоть раз в жизни запускали fork бомбу. Но если ты, тот единственный и неповторимый, то советую взять и прям сейчас это сделать. Это как впервые поссать в море.

Открывай консоль и запускай:

:(){ :|:& };:


После запуска, перезагружай свой компьютер и продолжай читать этот пост.

Концепция, лежащая в основе форк-бомбы — процессы постоянно воспроизводят сами себя, потенциально вызывая отказ в обслуживании.


✔️ Что это за странная регулярка?

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

forkbomb()
{
forkbomb | forkbomb &
};
forkbomb


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

От этой заразы можно защитится с помощью ulimit, ограничив количество запускаемых процессов. Делается так:

ulimit -u 30


Ну либо прописать эти ограничения /etc/security/limits.conf

* hard nproc 30


hard = максимальное количество процессов nproc
* = для всех пользователей равным 30

Жесткое ограничение позволяет зафиксировать лимиты, чтобы пользователи ненароком через ulimit их не сняли.

Еще был вопрос: а почему вместо | не использовать && ? Ну логично же, && выполняет команды последовательно. А вертикальная палка выполняет команды одновременно, при этом дескрипторы файлов перенаправляются в канал.

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

Ладно, чо, вроде пятница, а организм уже бунтует против субботнего рабочего дня. Ну ничего, зато потом 4 дня можно будет отдохнуть. Давай, увидимся!

tags: #bash #linux

@ВАSНDАYS | BАSHDАYS.CОM
Please open Telegram to view this post
VIEW IN TELEGRAM
217
Как в 20 выглядеть на 40?

Правильно! Установить Linux!

Я всегда обходил стороной команду find, наверное потому что я никогда не умел ее готовить. Мне всегда было проще находить нужное в midnight commander.

У меня кстати даже постов про find не было в этом канале.


Но для bash скриптов mc это не панацея, поэтому изредка приходилось гуглить про find и что-то лепить.

Сегодня затрём как раз про find. Но не за Базу, а за его скорость и бест-практики.

С помощью find мы ищем по разным паттернам и условиям. Порой необходимо совершить какие-то действия с найденными результатами. И find не может нас этим порадовать. Приходится передавать данные внешним утилитам.

У find есть пару опций:

exec = запускает утилиту и передает в неё аргументы
execdir = перед запуском утилиты, переходит в каталог с найденным

Синтаксис этих штук такой:

-exec команда {} суффикс


команда = утилита, которую запускаем
{} = развернется в найденные файлы
; = суффикс, на каждый файл запускается команда
+ = суффикс, команда запускается с группой файлов

Символ ; обязательно нужно экранировать либо поместить в кавычки.


Хорош с теорией, погнали в практику:

find ~/ -type f -exec sh -c 'echo $$' {} \;


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

По итогу на экран я получил 100500 строчек такого вида:

26719
26720
26721


Это список процессов, которые были задействованы в результате поиска. Чёто прям дохуя. Если надоело ждать результата, жми CTRL+C.

✔️ А теперь давай оптимизируем!

find ~/ -type f -exec sh -c 'echo $$' {} +


По итогу я получаю одну цифру 27413, один идентификатор процесса, в отличие от предыдущего варианта. Круто? Да охуеть не встать!

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

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

Проще ведь сразу скормить утилите группу найденных файлов и никому не ебать мозги.

Стопэ. Это еще не всё, можно еще больше разогнать 👇

Включаем режим оверклоакинга!

Будем использовать утилиту xargs, она как раз входит в пакет с find. Утилита xargs запускает процессы параллельно. Уже понимаешь чо щас будет?

find ~/ -type f -print0 | xargs -0 -n4 -P2 команда


Xargs читает стандартный ввод и разбивает его на группы аргументов. А после этого запускает команду с данной группой аргументов.

По ключам:

0 = аргументы разделяются символами NUL ASCII. А спецсимволы косой черты \ и кавычками, воспринимаются как обычные.

n = максимально количество аргументов переданных за один раз

P = максимально количество процессов запущенных одновременно. Если указать 0, запустится как можно больше таких процессов.

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

А еще если во время работы xargs, ей послать сигнал SIGUSR1 (kill), то количество одновременно запускаемых процессов будет увеличено. А для SIGUSR2, будет уменьшено.

SIGUSR1 и SIGUSR2 могут быть использованы программой для произвольных нужд, например для межпроцессной (или межпоточной) синхронизации, управления фоновыми процессами (демонами) и т. п. По умолчанию, сигналы SIGUSR1 и SIGUSR2 завершают выполнение процесса.


Это базовая команда для правильной работы find + xargs:

find -print0 | xargs -0 command


Но учти, если command у тебя будет оболочкой sh/bash, тогда нужно правильно обработать аргументы и учесть всякие splitting, globbing и т.п.

Ладно чо. Всем хорошего отдыха и заебатых шашлычков. На 01.05 я возьму отгул, а там дальше уже всё по плану: за кишочки подергаем, да на страусе (strace) покатаемся. Будет охуенно!

tags: #bash #linux

@ВАSНDАYS | BАSHDАYS.CОM
Please open Telegram to view this post
VIEW IN TELEGRAM
102
Привет. Сегодня шифруем пиксели!

Если кратко, берем png картинку и шифруем ее с помощью парольной фразы.

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

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


А поможет нам в этом старый и добрый ImageMagic

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

convert wizard: bashdays.png


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

Хорошо. Теперь нужно зашифровать это изображение. Выполняем команду:

echo 'пароль' | convert bashdays.png -encipher - -depth 8 png24:hidden.png


Получаем файл hidden.png с пикселями. Файл прекрасно открывается, но волшебника мы уже не видим. Лишь какой-то цветной шум. Выглядит это так.

Шифрование делаем через опцию encipher, в качестве аргумента эта опция принимает имя файла с парольной фразой. Дефис после encipher означает - стандартный ввод, который в данном случае указывает на канал «|».

Ну и дешифровка:

echo 'пароль' | convert hidden.png -decipher - restored.png


Если проебал пароль или ввел неправильно, получишь тот же набор цветных пикселей. Пароль восстановить увы не получится. Так что запиши на бумажку и повесь на монитор, как в лучших практиках ИБ.

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

convert bashdays.png -encipher key.gif result.png


В этом примере, key.gif это ключ для шифрования. Без этого файла расшифровать изображение уже не получится. Заебись? Заебись!

✔️ А какие нюансы?

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

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

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

Если далёк от криптографии, то забей хуй, работает да и ладно.


Где можно применить?

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

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

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

Изучай!

tags: #security

@ВАSНDАYS | BАSHDАYS.CОM
Please open Telegram to view this post
VIEW IN TELEGRAM
71
Сегодня про RANDOM и револьвер

В бородатые 2000е была такая консольная игруля «Русская рулетка», состояла она из одной строчки:

[ $[ $RANDOM % 6 ] == 0 ] && rm -rf / || echo "You live"


Для начала давай сделаем её более безопасной:

[ $[ $RANDOM % 6 ] == 0 ] && echo 'You died' || echo 'You live'


Суть такая: Если деление по модулю псевдослучайного числа переменной RANDOM вернёт - 0, значит тебе песда и пистолет выстрелил. Соответственно если > 0, то ты пока еще жив и тебе повезло.

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

Число отличное от 0, указанное в качестве переменной RANDOM используется как seed (семя) для генератора псевдослучайных чисел.

Если посеять число 2, то при первом обращении к переменной RANDOM получим значение, которое делится на 6 без остатка.

RANDOM=2  
[ $[ $RANDOM % 6 ] == 0 ] && echo 'You died' || echo 'You live'


Этот посев даст нужное в разных версиях интерпретатора. Все дело в том, что в версии bash 5.1 были внесены изменения связанные с этим. Короче если у тебя версия баша > 5.0, то можно изменить поведение генератора с помощью переменной BASH_COMPAT.

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

Например, если вы установите BASH_COMPAT=3.2, то это означает, что текущая оболочка Bash будет работать совместимо с версией 3.2. Это может быть полезно, если у вас есть скрипты, которые были написаны для старых версий Bash и вы хотите, чтобы они продолжали работать в новых версиях без изменений поведения.


Так, давай теперь это проверим:

( BASH_COMPAT=51 ; RANDOM=0 ; echo $RANDOM )


Получаем значение: 20814

( BASH_COMPAT=50 ; RANDOM=0 ; echo $RANDOM )


А тут уже другое: 20034

Если мы задаем 0 в переменную RANDOM, тогда семя (seed) инициализируется жестко заданным значением. При котором генерируется требуемое число, то есть делится на 6 без остатка.

RANDOM=0  
[ $[ $RANDOM % 6 ] == 0 ] && echo 'You died' || echo 'You live'


Тут пистолет гарантированно выстрелит. Аналогично предыдущему примеру, если начало строки не число:

RANDOM='bashdays'  
[ $[ $RANDOM % 6 ] == 0 ] && echo 'You died' || echo 'You live'


Снова получим 100% выстрел.

✔️ Как работает генератор:

- Начальное значение seed, оболочка получает совершая манипуляции над значениями pid, ppid, uid + текущие время (секунды, микросекунды).

- Далее полученное значение пропускается через алгоритм, а результат сохраняется. При следующих обращениях также пропускается через алгоритм.

- Потом полученное число усекается. В документации указано максимальное возможное значение 32367.

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

Такие дела, исследуйте!

tags: #bash #linux

@ВАSНDАYS | BАSHDАYS.CОM
Please open Telegram to view this post
VIEW IN TELEGRAM
48
🔤🔤🔤🔤🔤🔤🔤🔤🔤

shopt -s extglob; : "${PS1@P}"; : "${_//$'\x01'+([^$'\x02'])$'\x02'}"; : "${#_}"; printf '%b' "\e#8\e[$(((LINES-1)/2));$(((COLUMNS-_)/2))H"


tags:
#bash

@ВАSНDАYS | BАSHDАYS.CОM
Please open Telegram to view this post
VIEW IN TELEGRAM
60
Всем привет. Записал сегодня пилотный выпуск на ютубину, изредка буду заходить с подобным контентом. Над звуком еще поработаем, есть нюансы. Завтра отдельным постом продолжу эту тему, так сказать вывернем наизнанку.

Поддержи подпиской и лайком! Спасибо!👇

https://youtu.be/gTwpoAaB_dY
155
Media is too big
VIEW IN TELEGRAM
Привет отдыхающим. Продолжим тему с повышением прав. Искать suid биты через find конечно мастхев, но есть пиздатый инструмент под названием LinPEAS.

LinPEAS - Linux Privilege Escalation Awesome Script


Изобрел его некий пентестер - Карлсон (Carlson P), который держит проект HackTricks, возможно ты про него когда-нибудь слышал.

Короче штука написана на sh. Чувствуешь уровень? То есть будет работать хоть на чайнике.

При запуске LinPEAS сканирует Linux машину, на всякие дырки и криворукость админов.

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

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

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

Запускается одной командой и не требует установки:

curl -L https://github.com/peass-ng/PEASS-ng/releases/latest/download/linpeas.sh | sh


Но можно и через змеюку:

python -c "import urllib.request; urllib.request.urlretrieve('https://github.com/peass-ng/PEASS-ng/releases/latest/download/linpeas.sh', 'linpeas.sh')"

python3 -c "import urllib.request; urllib.request.urlretrieve('https://github.com/peass-ng/PEASS-ng/releases/latest/download/linpeas.sh', 'linpeas.sh')"


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

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

Короче достойная пепяка! Рекомендую потыкать, по крайней мере на своих сервачках с пет проектами. Найдешь много интересного.

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

tags: #linux #security #utils

@ВАSНDАYS | BАSHDАYS.CОM
Please open Telegram to view this post
VIEW IN TELEGRAM
121
Ты — caмый счaстливый чeловек нa плaнeте!

Почему? Потому, что сейчас узнаешь про ONLY.

Частенько возникала необходимость разрешить пользователю запускать только определенные команды по ssh. То есть явно указать ему белый список таких команд.

Городили мы конечно знатные велосипеды с помощью костылей, rbash, симлинков, chroot и т.п.

Кстати про chroot писал в этом посте, почитай на досуге, мож где применишь.


Все эти костыли были неудобные и какие-то пиздец сложно поддерживаемые. Но решение было найдено и вполне элегантное. Всё уже украдено придумано за нас.

Для затравки: /home/user/.ssh/authorized_keys

command="only rsync ls cat" ssh-rsa AAAAB3Nza


Думаю идею ты понял. Пользователь подключается по ssh и может выполнить только 3 команды rsync, ls, cat.

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

ssh user@bashdays.ru ls /home/user/


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

Нехер ему на сервере делать. Есть список команд, вот пусть ими и довольствуется без интерактива.

✔️ Теперь подробности

ONLY это НЕ какая-то встроенная команда в Linux, это Bash скрипт, который аккуратно лежит в /usr/local/sbin, положить его туда ты должен самостоятельно.

1. Берем only из репы
2. Кидаем его в /usr/local/sbin/only
3. Ставим chmod +x чтобы запускался
4. Делаем пару ключей для работы

ssh-keygen -P "" -f only


P = пустая парольная фраза
f = задаем имя ключа

Содержимое ключа only pub добавляем в authorized_keys нужному пользователю, а private key отдаем, тому кто будет подключаться к серверу.

Да, файл authorized_keys у меня выглядит примерно так:

command="only rsync cat ls", no-agent-forwarding, no-port-forwarding, no-pty, no-user-rc, no-X11-forwarding ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAA


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

Важная штука, потребуется файл с правилами, называется .onlyrules. Забирай тут.

Этот файл нужно также скопировать в папку, того пользователя, которого ограничиваем, например в /home/user/.onlyrules

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

\:^ls$:{p;q}
\:^who$:{p;q}


Короче это фильтры на аргументы, ключи и т.п. Можно не заморачиваться и просто обернуть нужные команды между \:^ и $:{p;q}.

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


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

Лично мне эта штука понравилась, раздал тестировщикам ключики, теперь они сами могут своих вонючих демонов перезапускать, без блуждания по серверу и фраз — да блядь, я что-то нажал и всё сломалось!

Такие дела. Увидимся!

tags: #bash #linux #utils

@ВАSНDАYS | BАSHDАYS.CОM
Please open Telegram to view this post
VIEW IN TELEGRAM
146
Тут у безопасников жопы сгорели после предыдущего поста.

Нужно ли на приватный ssh ключ устанавливать пароль?


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

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

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

Удобно? Охуенно удобно! Главное практично.

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

Добавь тогда уж туда еще один фактор авторизации через QR код и Яндекс.Ключ. Лишним не будет 😲

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

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

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


Потому что у человека в приоритете две основных потребности - личная безопасность и секс. Привет пирамиде Маслоу.

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

Если считаешь по-другому, велком в комментарии и хорошего дня!

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

@ВАSНDАYS | BАSHDАYS.CОM
Please open Telegram to view this post
VIEW IN TELEGRAM
105
Привет, сегодня коротко, очередная внеплановая пятница. Короче в продолжение к постам про повышение привилегий.

Если звезды сошлись (suid/guid и прочая хуйня), то через vim ты можешь получить интерактивный root shell.

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

vim -c ':!/bin/sh'


Или так:

vim --cmd ':set shell=/bin/sh|:shell'


На выходе тебе выплюнет доллар ($) ну и там уже бесоёбишь.

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

И если в этом списке есть vi/vim, то ты ничем не ограничен. Запускаешь полноценный shell через vim и развлекаешься. А порой можно дотянутся и до папки /root и всего остального. Было бы желание 🥳

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

Давай, хороших выходных! Я пожалуй тоже устрою себе внеплановый отпуск на пару дней. Увидимся!

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

tags: #linux #security #utils

@ВАSНDАYS | BАSHDАYS.CОM
Please open Telegram to view this post
VIEW IN TELEGRAM
99
This media is not supported in your browser
VIEW IN TELEGRAM
Майские праздники равносильны новогодним, уже на третий день начинаешь деградировать и превращаться в обезьяну. После таких затяжных выходных, нужно еще пару недель, чтобы вернуться к нормальной жизни.

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

✔️ Называется Atuin — Magical shell history.

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

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

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

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

Все в лучших традициях selfhosted. Мануал по установке сервера. Через docker-compose за пару минут все расчехляется.

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


Дополнительно:

- написана на rust + sqlite
- удобный поиск и фильтр
- история команд шифруется
- совместимо с Bash, ZSH, Fish, NuShell

Установка одной командой:

/bin/bash -c "$(curl --proto '=https' --tlsv1.2 -sSf https://setup.atuin.sh)"


Короче рекомендую потыкать.

Сайт проекта: https://atuin.sh/
Страница на github: https://github.com/atuinsh/atuin

tags: #linux #utils

@ВАSНDАYS | BАSHDАYS.CОM
Please open Telegram to view this post
VIEW IN TELEGRAM
78
Сегодня за девопс, gitlab и как сделать комфортный pipeline с выпадающими списками Variables + Бонусы.

Из коробки, когда ты жмешь кнопку Run Pipeline, тебе нужно руками вбивать нужные Variables, например, чтобы указать окружение, в которое будет раскатан релиз.

Каждый раз вбивать руками Variable ENVIRONMENT=stage / production ну пиздец тяжко и неудобно. А если таких переменных овердохуя, то всё желание пропадает что-то делать.

Хочется чтобы был выпадающий список со всеми параметрами. Накликал мышкой и в продакшен.

Выпадающие списки можно делать не только с окружениями, но и добавить банальные вопросы YES/NO и прочее. А затем в секции rules уже все это разруливать и обрабатывать.

✔️Ниже базовая болванка

Создаем файл .gitlab-ci.yml в корне проекта и добавляем в него:

variables:
ENVIRONMENT:
value: "N/A"
options:
- "N/A"
- "STAGE"
- "PRODUCTION"
description: "Deployment environment"

.stage_server:
&stage_server
WEB_NODE: "xx.xx.xx.xx"
DB_NODE: "xx.xx.xx.xx"

.production_server:
&production_server
WEB_NODE: "xx.xx.xx.xx"
DB_NODE: "xx.xx.xx.xx"

deploy:
tags:
- common-runner
script:
- echo $ENVIRONMENT
- echo "build & deploy here"
rules:
- if: '$ENVIRONMENT == "STAGE"'
variables:
<<: *stage_server
- if: '$ENVIRONMENT == "PRODUCTION"'
variables:
<<: *production_server


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

N/A (выбран по умолчанию) добавлено для дураков, которые ничего не выбирают и сразу тыкают Run Pipeline. В таком случае пайплайн зафейлится и ничего не пострадает.

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

Ну и основная секция deploy, у меня указан раннер с тэгом common-runner, а потом полезная нагрузка которая будет выполнена.

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

Собственно всё. Если нужно добавить еще каких-то выпадающих списков, добавляем в первую секцию всё необходимое, аналогично ENVIRONMENT. Ну и потом в rules все это обрабатываем. Например, так:

- if: '$JOB == "YES" && $ENVIRONMENT == "STAGE"'


В общем всё элементарно. По итогу получаем незамысловатый пайплайн, всё в одном месте и ничего не раскидано по самому gitlab.

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

Создаем репу dev/pipelines, кидаем туда файл .gitlab-ci.yml, настраиваем в нем всё под свои реалии.

Дальше в нужных проектах добавляем файл .gitlab-ci.yml с таким содержимым:

include:
- project: "dev/pipelines"
file: ".gitlab-ci.yml"


Всё, теперь не надо плодить кучу .gitlab-ci.yml с одинаковым содержимым, все инклудится из одного места. Ну и если нужно что-то поменять, меняем один раз и все проекты автоматически это подхватывают.

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

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

tags: #devops

@ВАSНDАYS | BАSHDАYS.CОM
Please open Telegram to view this post
VIEW IN TELEGRAM
153
Чипи-чипи, чапа-чапа. Привет, на скрине 🚫некий Linux бедолага очень обрадовался, когда нашел пасхалку в команде date. Этакий нигде недокументированный аргумент.

date --iso=seconds


Ну да, если запросить help либо глянуть в гуглах, такая конструкция нигде не упоминается.

Молодец чо. НО, если бы он знал про функцию getopt_long, то вряд ли сделал открытие. Ща расскажу в чем прикол.

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

Функция getopt_long входит в состав пакета GNU C (glibc)


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

date --iso-8601=seconds


Но если пропустить её через getopt_long, получим ту самую «пасхалку»:

date --iso=seconds


✔️ Еще примеры для понимания:

1. nano --smart --book
2. ls --hyper --show
3. du --one --human --sum


1. --smarthome --bookstyle
2. --hyperlink --show-control-chars
3. --one-file-system --human-readable --summarize


Понял в чем прикол? Но это еще не всё!

Утилиты из состава пакета coreutils, поддерживают сокращения аргументов и опций. Смотри:

for i in d h m s n; do date -I"$i"; done

2024-05-14
2024-05-14T09+00:00
2024-05-14T09:59+00:00
2024-05-14T09:59:37+00:00
2024-05-14T09:59:37,850707267+00:00


-Id == -Idate
-Ih == -Ihours
-Im == -Iminutes
-Is == -Iseconds
-In == -Ins


Аналогично с длинными опциями:

for i in d h m s n; do date --iso="$i"; done

2024-05-14
2024-05-14T10+00:00
2024-05-14T10:01+00:00
2024-05-14T10:01:57+00:00
2024-05-14T10:01:57,460027853+00:00


--iso=d == --iso-8601=date
--iso=h == --iso-8601=hours
--iso=m == --iso-8601=minutes
--iso=s == --iso-8601=seconds
--iso=n == --iso-8601=ns


Ну и на примере ls и sort:

ls --col=al | cat


--col=al == --color=always


printf '%s\n' {20..1} | sort --so=n


--so=n == --sort=numeric


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

Что имеем? Не нужно искать пасхалки, там где их нет.

Такие дела!

tags: #linux

🔔 @bashdays
Please open Telegram to view this post
VIEW IN TELEGRAM
91
Упоримся? А давай!

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

while :; do cat /etc/hosts ; done | grep -m1 'a'


По идее тут что-то вроде бесконечного цикла, который будет cat’ить на экран файл hosts, предварительно грепнув содержимое по символу «a».

Аргумент -m1 = выход после первого вхождения искомой подстроки. После этого, конец канала закрывается для чтения.


Запустил? И всё зависло! По крайней мере у меня вывелась только одна строчка вида:

127.0.0.1 localhost bashdays


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

✔️ Давай убедимся:

set -o pipefail
cat /etc/hosts | exit 0
echo $(( $? > 128 ? $? - 128 : $? ))


Получаем цифру 13. Откуда она взялась? Смотри! Если команда завершается сигналом, тогда статус ошибки ($?) == 128 + Номер сигнала.

А дальше вычитаем, делим, умножаем, корень квадратный вычисляем, заводим в дискриминант по синусу. Пеню. Короче получили цифру 13.

Более подробно про сигналы мы с тобой разбирали в этом посте.

Давай посмотрим, что такое 13:

kill -l 13


Вывелось PIPE, что равносильно SIGPIPE (Broken Pipe). Ага!

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

Раньше эта ситуация была очевидна и на экран выводилась ошибка. А вот с версии Bash 3.1-alpha1 это пофиксили, вот выжимка из спеков:

This document details the changes between this version, bash-3.1-alpha1,
and the previous version, bash-3.0-release.

By default, the shell no longer reports processes dying from SIGPIPE.


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

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

Ну либо так можно глянуть:

ps --no-headers --sort=pid -eo pid


Увидишь большую разницу между значениями PID.

Теперь как это пофиксить. Если уж захотелось упороться, нужно упарываться правильно.

Вот несколько вариантов:

while : ; do cat /etc/hosts || { echo $? >&2 ; exit; } ;done | grep -m1 'a'
while : ; do cat /etc/hosts || break ; done | grep -m1 'a'
while : ; do cat /etc/hosts || exit ; done | grep -m1 'a'


Если присмотреться, может возникнуть вопрос — а откуда выходит exit? Цикл выполняется в подоболочке, потому что является участником канала.

Давай проверим:

while : ; do cat ; done | grep -m1 'a' &


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

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

Проверяем, что оно действительно приостановлено:

jobs -l

[1]+251913 Stopped (tty input) while :; do
cat;
done
251921 | grep --color=auto -m1 'a'


Ну и теперь смотрим, кто есть кто:

ps --forest -o pid,ppid,stat,comm

PID PPID STAT COMMAND
251893 251854 Ss bash
251913 251893 T \_bash
251924 251913 T | \_cat
251921 251893 T \_grep
251962 251893 R+ \_ps


Процесс 251913/251893 и есть подоболочка, в котором крутится наш цикл. Вот с этой подоболочки и выходит exit.

Сложно? Да не… азы с некоторыми нюансами. Изучай!

tags: #linux #bash

🔔 @bashdays
Please open Telegram to view this post
VIEW IN TELEGRAM
63
Привет. В Linux есть утилита basenc, которая входит в состав пакета coreutils. С помощью неё можно конвертировать данные в двоичный формат. Это который 00110010.

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

А всё просто, давай закодируем название деревни:

echo 'пиздачосово' | basenc -w16 --base2msbf


По итогу получаем набор 0 и 1. Ключ -w16 я указываю, чтобы выводить по 16 символов в строке. Для удобства. Так как русские символы кодируются двумя байтами.

✔️ Вывод получился таким:

1101000010111111
1101000010111000
1101000010110111
1101000010110100
1101000010110000
1101000110000111
1101000010111110
1101000110000001
1101000010111110
1101000010110010
1101000010111110
00001010


А почему в последней строке какой-то обрубок?

Это не обрубок, это 8 бит! И эти 8 бит == символу новой строки, который закодирован одним байтом, о чем нам говорит левый бит первого и единственного октета (00001010).

А декодируется всё это безобразие так:

str="1101000010111111110100001011100011010000101101111101000010110100110100001011000011010001100001111101000010111110110100011000000111010000101111101101000010110010110100001011111000001010"

echo $str | basenc -d --base2msbf


Просто указываем ключ -d и вуаля!

Если тема интересна, можешь почитать более подробно про кодирование UTF-8 здесь.

Вага-вага!

Сегодня чуть позже (16:00 МСК) залетит еще один партнерский пост, но это будет не просто рекламная интеграция.

Ниже я сделаю кнопку (в 16:02 МСК). Нажав на нее ты сможешь выиграть 1 бесплатный купон на неплохую онлайн конфу. Всего будет 3 таких купона. Всю эту кухню контролирую лично я, поэтому наёба тут нет.

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

Короче если интересно, не пропусти. Давай, хорошего дня!

tags: #linux #bash

🔔 @bashdays
Please open Telegram to view this post
VIEW IN TELEGRAM
91
Здрасти. Утренний мини пост, вечером еще закину чтива.

Как ты знаешь, утилита false возвращает всегда ошибку (код завершения 1). Давай запустим и убедимся:

false
echo $?
1


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

whereis false
false: /usr/bin/false


Теперь сделаем такое:

env false --help


Используя env, мы запустили альтернативную версию false. И что мы видим? Вывалилась страница help. Очевидно? Ага!

А теперь давай посмотрим код выхода, после вывода help.

echo $?
1


Чо блядь? Почему единица? Команда по выводу справки отработала корректно? Корректно! Ожидаем ноль? Ага! Но вот хуюшки! Получите единицу!

Баг? Не думаю. Давай проверим на выводе версии:

env false --version
echo $?
1


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

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

Специально ли это сделали, никто не знает. Но факт остается фактом.

✔️ Посмотреть чем является команда, можно с помощью команды type:

type -a false

false is a shell builtin
false is /usr/bin/false
false is /bin/false


Вот такие вот перлы порой случаются. Ошибка и точка!

tags: #bash #linux

🔔 @bashdays
Please open Telegram to view this post
VIEW IN TELEGRAM
87