Как стать автором
Обновить

Комментарии 60

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

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

Не совсем понимаю в чём проблема. Это задокументированное поведение bash.

И если не валидировать ввод получается фигня. Во всех языках.

Проблемы безопасности тут нет как и самой безопасности в bash. Если уж у злоумышленника есть возможность запустить скрипт - то скопировать и исправить его и подавно. Незачем тут мудрить с пейлодом в [ ] или ( ) .

Полностью согласен. Такое ощущение, что люди официальную документацию никогда не читали, и все поведение давно уже описано. Делают непонятные открытия туалетной бумаги в своих статьях вместо газеты, и всем пытаются о ней рассказать. Что все давно уже используют туалетную бумагу их не волнует…
Ну нет. Проблема тут есть и гигантская. Когда я работал в одной из FAANG компаний, часть пользовательских действий приводила к вызову баш скриптов, написанных типичными линуксоидами. В качестве простого примера можно представить себе batch job, генерирующий thumbnail для загруженных картинок. Типичная ситуация такова, что ошибки подобного рода там встречались через строчку.

bash и shell вообще — очень плохие языки для написания чего-либо в принципе. Даже локальных скриптов. Всё из-за того, что там полно способов выстрелить себе в ногу и заметить кровотечение сильно позже того, как злоумышленник получил контроль над вашей системой. Если вообще можно заметить.
bash и shell вообще — плохие языки для написания чего-либо в принципе

Всё, что есть прекрасного в этой жизни, либо аморально, либо незаконно, либо приводит к ожирению.
Вполне приличный скриптовый язык сценариев, ни больше ни меньше. Автоматизировать работу вполне удобно, хоть мне знакомы с десяток языков программирования и опыт программирования около 25 лет, для своих целей shell скрипт хорошо подходит. Вам никто не мешает переписать скрипт например на perl или любой другой язык. Вы всегда шуроповертом гвозди забиваете и утверждаете, что это плохой инструмент? Выстрелить в ногу можно в любом языке, все претензии не языку, а к автору скрипта, что он мудак и не смог нормально написать. С дуру даже коего чего можно сломать… Гавнокодеров в любом языке программирования полно.
а к автору скрипта, что он мудак и не смог нормально написать

и да, и нет. Может Вы знаете про VestaCP, например. https://vestacp.com Весьма неплохая панель для администрирования серверов и написана полностью на баше. Но при этом поддерживать такое количество кода попросту нереально. Отсюда и огромное количество ишьюс (в т.ч. и по безопасности) в трекере VestaCP и отказ от дальнейшей разработки продукта.
Второе пример — инит скрипты из SysV. В конечном счете, мы перешли все на системди и слава Богу. Потому что скрипты каждый писал как мог, каких-то общих подходов не было.


Выстрелить в ногу можно в любом языке, все претензии не языку,

претензии к языку, что он не предоставляет никаких средств типизации, проверок. Другой вопрос, что Вы абсолютно правы, что это и накладывает ограничения на применение шелл скриптов. Условный скрипт на 10 строчек на нем писать ок, а делать какую-то хитрую автоматизацию — ей-Богу, лучше python взять...

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

Вам 18 лет? Очевидно, что чем больше кода — тем сложнее поддерживать. Чем больше кода — тем более необходима модульность. Тем нужнее всякие namespace, наличие ООП на уровне языка и прочее. Это все есть в баше? Нет!


Такие вещи предусматриваются на этапе проектирования

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


У меня есть скрипты в тысячи строк.

и что? Еще раз — если есть потребность в скрипте в тысячи строк — это ОЧЕНЬ хороший аргумент бросить баш и взять что-то более вменяемое, например, полноценный язык программирования с нормальными модулями для выполнения рутинных задач. Например, тот же python.

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

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

Насчёт гита — может быть, но для большинства это execute only код, без необходимости влезать в код, а тем более править его. К тому же и на Си можно было что-то подобное реализовать через макросы препроцессора. Но никто так не делает ))) Почему? Да как раз потому что надо обеспечить возможность изменения для программистов любого уровня )

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

Удобно, да. Но совершенно не безопасно.

Покажите любой ваш bash скрипт на 10+ строк, посмотрим за сколько в нём найдут уязвимость.
Если уж у злоумышленника есть возможность запустить скрипт — то скопировать и исправить его и подавно.

Вы неправы от слова совсем. Типичный пример. Есть сервер. На нем есть непривилегированные пользователи. Скрипт запускается либо со sticky bit/suid или чем-то подобным, либо прописан в sudo у пользователя на исполнение. Редактирование скрипта, понятное дело, только руту доступно. Так реализует скрипты для администрирования системы. Какая-нибудь задача self-service. И в случае описанном в статье это будет как раз уязвимость повышения привилегий.


Незачем тут мудрить с пейлодом в [ ] или ( ) .

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

Есть сервер. На нем есть непривилегированные пользователи. Скрипт запускается либо со sticky bit/suid или чем-то подобным, либо прописан в sudo у пользователя на исполнение.

Это называется архитектор мудак. Можно уволнять. Запускать шелл скрипты через вебсервер это сверх извращение. Для выполнения задач от вебсервера где-то видел используют perl скрипт, но самое правильное пишется сервер с FIFO очередью(команды могут выполняться долго, вебсервер не может ждать выполнения, и должен быстро вернуть страницу на запрос), который будет принимать, валидировать и выполнять сообщения от вебсервера. bash это обычный язык сценариев, не надо молотком шурупы закручивать!
Запускать шелл скрипты через вебсервер это сверх извращение

это вы сами себе придумали ) Я ничего не говорил про веб-сервер.
Речь про некие оболочки для админских задач, возможно, интерактивных из консоли. Хотя сейчас — да, почти что угодно можно реализовать на веб-сервере на Flask'е, а дальше под капотом пускай будут exec() и прочие полезные и хорошие вызовы )

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

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

с того, что иногда это необходимо. Система прав линукса не разделяет пользователей на категории — грубо говоря есть рут, и есть все остальное. Конечно, есть система прав на файлах, есть дополнительные уровни вроде setfacl/getfacl, есть CAPs у приложений… Но по факту сделать простую вещь — дать пользователю привилегию на ХХХ, при этом не давая ему остальных полномочий и не переделывая настройки в еще половине системы — весьма затруднительно.


Как простой пример. Вот представьте себе — сервер. На нем крутится докер. В докере какая-то программа. Есть условный админ — у него полные права. И есть разраб. Ему нужно дать возможность обновлять приложение, перезапускать, может быть что-то еще. Но не более. Чтоб не дай Бог не заруинил систему.
Как будете решать? Одно из наивных решений — как раз засунуть это все в скрипт, не притаскивая какие-нибудь сложные штуки вроде https://www.portainer.io (у которых, между прочим, свои проблемы есть).

Как простой пример. Вот представьте себе — сервер. На нем крутится докер. В докере какая-то программа. Есть условный админ — у него полные права. И есть разраб. Ему нужно дать возможность обновлять приложение, перезапускать, может быть что-то еще. Но не более. Чтоб не дай Бог не заруинил систему.
Как будете решать?

docs.docker.com/engine/security/rootless

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


И ещё добавлю. Ну, Окей. Будет рутлес. Но это не позволит пользователю публиковать сервис на непривилегированных портах. И, напротив, даст слишком широкие права в том ключе, что пользователь сможет запустить любой недоваренный код. А речь шла в моем изначальном вопросе о закрытом (=ограниченном) перечне действий с конкретным контейнером или подмножеством контейнеров, запущенных на конкретном сервере

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

Можно поподробнее? Возможно, я чего-то не знаю, но у меня несколько компонентов небольшого проектика уже полгода как крутятся в docker rootless в продакшне.

Если ходите на сервер по ssh то лучше без пароля и по ключу, это уже стандарт, верно? Разраб дает свой key.pub, вы прописываете его в на сервере, и перед ключем указываете ограничения или привилегии. В man sshd дивные 15 строк решат 90% проблем. Например даете рута на что-то.

Если ходите на сервер по ssh то лучше без пароля и по ключу, это уже стандарт, верно?

к делу не относится, но верно


Разраб дает свой key.pub, вы прописываете его в на сервере, и перед ключем указываете ограничения или привилегии.

точно так же можно навесить ограничения на юзера, причем тут ключ? :-)


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

Если что, suid бит действует только на исполняемые бинарные файлы, ты хоть 100 раз его на Шелл скрипт поставь, ядро будет его игнорировать пока ты в нем не поправишь нужный вызов в нужной функции)

Спасибо за комментарий ))) главное — идея )))

Не совсем понимаю в чём проблема. Это задокументированное поведение bash.
Не совсем, ввиду:
$ bash --posix x.sh
Enter guess: a[$(date >&2)]+42
вторник, 18 мая 2021 г. 21:25:35 (MSK)
Correct

Хотя `man bash' описывает
--posix
Change the behavior of bash where the default operation differs
from the POSIX standard to match the standard (posix mode). See
SEE ALSO below for a reference to a document that details how
posix mode affects bash's behavior.
о которой говорят очень и очень мало

Да на каждом шагу слышно "не пиши на bash", ну и собственный опыт.


И если не валидировать ввод получается фигня

Все не завалидируешь (и это тупиковый путь). Тут в другом проблема — многие воспринимают bash/yoursh скрипты как язык программирования, а это не совсем так. Ну по факту ты программируешь команды, но делает это та же самая оболочка где ты водишь команды. Со всеми своими ньюансами типа разворачивание звездочки в список файлов в текущей директории и т.п.


Ну а про заключение в двойные кавычки переменной при сравнении кто только не говорил (говорят очень и очень мало, да. Я не слышал значит не говорят)

Да на каждом шагу слышно «не пиши на bash», ну и собственный опыт


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

Согласен. Но тут надо оценивать перспективы. Баш — он, как оказывается, не везде одинаковый (мы это уже выяснили). А большие скрипты на нем трудно поддерживать.
К тому же Python тоже есть в практически любом линуксе, потому что, например, от него зависит пакетный менеджер yum. В убунту Python тоже в 90% случаев есть из коробки.

Как же у вас всё усложнено… bash… валидировать… без лыж в гамаке никак нельзя?


[: a[$(date: unexpected operator
Wrong
$ uname -srm
FreeBSD 13.0-RELEASE amd64
echo $0
?
Довольно интересно, почему продвижение нормальной альтернативы почти никого не интересует? Всё же доступность оболочки — это довольно существенный аргумент, и в оболочке доступной на локалхосте смысла довольно мало.
Альтернатив достаточно много. Да и сам баш это альтернатива. Другое дело что стандартному sh уже много десятков лет, он везде и просто так извести его не выйдет.

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

Единственный способ достичь нирваны — постепенное улучшение языка sh.
Я говорю про то, что для запуска альтернативного кода нужно будет ещё откуда-то взять оболочку: либо просить сисадмина, либо собирать портабельную версию, либо взять другой скриптовый язык.
Единственный способ достичь нирваны — постепенное улучшение языка sh.
Сомневаюсь, что это можно сделать, не сломав обратную совместимость.
Нормальная альтернатива — POSIX shell. Но башизмы и кошизмы прут из всех щелей, и конца и края им нет.

Вон в `bash' даже ключ специальный придумали `--posix' и что? И ничего, `bash --posix ./myscript' это пример исполняет во всей красе, а нормальный POSIX shell отвергает его.
Нормальная альтернатива — POSIX shell.
В posix shell есть проблема с экранированием пробелов? Кроме этого, существуют и другие вопросы, как отсутствие протокола для взаимодействия программ(весь вывод в виде текста), довольно большая специфичность как синтаксиса, так и приёмов работы, например эмуляция хеш массивов, использование текстовых переменных вместо массивов(например, $PATH).

Вместо тонны статей, просто дайте людям ссылку на shellcheck и пусть им валидируют свои скрипты.

Скрипты которые крутят в банках на легаси системах, просто так копипастить в чьи-то веб формочки — ну такая идея. На любителя. Могут не оценить.

Я думаю подразумевалась вебформочка на https://www.shellcheck.net
А не утилита командной строки. Хотя тоже — aptitude? Серьезно? В банках? Там допотопный RHEL6, хорошо если 7. Но не везде ) Банки разные бывают :-)

Я думаю подразумевалась вебформочка на www.shellcheck.net


Это просто вебморда к тому же скрипту на Хаскеле

Там допотопный RHEL6

Ну не такой уж он и допотопный. Впрочем Хаскель есть и для RHEL5
Что значит «просто»? Просто достаточно много какой инфы не должны покидать определенные организации в никаком виде. Ни в виде разговоров, ни в виде «просто формочка». Ни в виде — ну я домой на почту перешлю что б героически поработать ночью… Ну вот вообще никак нельзя. Даже если чуть чуть и очень хоцца.

КГ\АМ

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

хороший совет. Это первый шаг. Но вообще shellcheck недостаточно. А лучше всего — вообще на шелле НИКОГДА и НИЧЕГО не писать. Та же проблема с тем, что у тебя Шелл работает только поверх кодов возврата и текстовых потоков. Никакой типизации, никаких юнит-тестов. Это хорошо работает только для ад-хок задач

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

В гитлаб ci как раз норм. В терминальных случаях — там вообще что-то типа Bazel вызывается (в одну команду с параметрами)
Кстати, у гитлаба просто огромная расширяемость за счёт возможности каждый этап пайплайна реализовывать поверх отдельного докер контейнера...

Не понимаю, почему не писать простое


if [ "$num" -eq 42 ]

./r.sh: строка 5: [: a[$(date >&2)]+42: ожидается целочисленное выражение

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

После того как на работе решили все скрипты писать начиная с #!/bin/sh -eu и судя по тому, что полёт пока нормальный, тоже начинаю подозревать, что баш переоценён как скриптовый язык.

Без echo ${A%%.png}.jpg и echo item{1..10} не грустно?

Как бы, и `-e' полезный ключ, и `-u' хороший ключ, и '#!/bin/sh' разумное начало, но против кошизмов/башизмов на системах, где /bin/sh форма bash, ksh или zsh (многие Linux, macOS, ...), это всё не очень помогает. Даже башовский ключ `-o posix' не помогает. Да:
$ sh -eu myscript
Enter guess: a[$(date >&2)]+42
myscript: line 3: a: unbound variable
Но:
$ PATH=/usr/bin:/bin: sh -eu -o posix myscript
Enter guess: PATH[$(date >&2)]+42
среда, 19 мая 2021 г. 00:33:01 (MSK)
myscript: line 3: /usr/bin:/bin:: syntax error: operand expected (error token is "/usr/bin:/bin:")
Wrong
Ошибка, конечно, выдаётся, но, увы, уже после выполнения произвольного кода.

P.P.S.
Да! Можно ж и безошибочный вариант пользовательского ввода предложить:
$ sh -eu -o posix myscript
Enter guess: IFS[$(date >&2)]+42
среда, 19 мая 2021 г. 00:52:57 (MSK)
Correct

P.S.
А вот во FreeBSD, где /bin/sh реальный POSIX shell… Но это другая тема.

а в контейнерах любят нонче alpine, в котором sh aka ash… Поэтому минимальное рабочее подмножество для написания скриптов — это /bin/sh без всяких наворотов типа башизмов.

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

Ну, «#!/bin/sh -eu» не самый лучший способ, скажем:
#!/bin/dash
set -eu
или «#!/bin/ash», и давали бы лучшие гарантии, и обеспечивали бы лучшее документирование принятых решений. Но, конечно, Вам виднее.
Строго говоря, это не башизм, а кошизм, т.к. ksh появился гораздо раньше bash.

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

Честно говоря, я вижу в примере из статьи проблему не в соответствии стандарту, а в очень странном дизайне оператора [[. Он разворачивает переменную дважды, сначала из variable expansion $num, а потом то, что внутри — (command substitution $(...)). Обычно (при вызове команд) правила другие — переменные раскрываются один раз и потом разбиваются на слова (или нет, если в кавычках). Двойное раскрытие (применение command substitution к результатам variable expansion) — это сюрприз. Так не должно быть.

Ну, этому оператору ksh уж скоро 40 лет исполнится, так что это весьма и весьма пролонгированный сюрприз.

Кроме того, и в POSIX shell, скажем, в Bourne (Thomson) shell FreeBSD, арифметическая подстановка работает похожим образом, к примеру:
$ x=1+1 ; echo $x ; echo $(($x))
1+1
2
Другое дело, что в POSIX shell, она более прозрачна и её область применения несколько более ограничена, чем в bash или ksh.
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.