Выполнение функции Bash Script с Sudo


22

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

Я не хочу требовать, чтобы весь сценарий выполнялся от имени пользователя root, и я хочу иметь возможность вызывать эту функцию с правами root изнутри сценария. Запрос пароля в случае необходимости не является проблемой, так как в любом случае он в основном интерактивный. Однако, когда я пытаюсь использовать sudo functionx, я получаю:

sudo: functionx: command not found

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

Есть ли способ, которым я могу сделать свою функцию "видимой" для sudo, не распаковывая ее, не находя соответствующий каталог, а затем выполняя его как отдельный скрипт?

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

Я бы только ожидал, что кто-то с sudo ЛЮБОЙ сможет запустить функцию, так как одна из вещей, которую он делает - это смена паролей.


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

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

Ответы:


19

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

function hello()
{
    echo "Hello!"
}

# Test that it works.
hello

FUNC=$(declare -f hello)
sudo bash -c "$FUNC; hello"

Или проще:

sudo bash -c "$(declare -f hello); hello"

Меня устраивает:

$ bash --version
GNU bash, version 4.3.42(1)-release (x86_64-apple-darwin14.5.0)
$ hello
Hello!
$
$ FUNC=$(declare -f hello)
$ sudo bash -c "$FUNC; hello"
Hello!

По сути, declare -fвернет содержимое функции, которую вы затем передадите bash -cinline.

Если вы хотите экспортировать все функции из внешнего экземпляра bash, измените FUNC=$(declare -f hello)на FUNC=$(declare -f).

редактировать

Чтобы обратиться к комментариям о цитировании, посмотрите этот пример:

$ hello()
> {
> echo "This 'is a' test."
> }
$ declare -f hello
hello ()
{
    echo "This 'is a' test."
}
$ FUNC=$(declare -f hello)
$ sudo bash -c "$FUNC; hello"
Password:
This 'is a' test.

1
Это работает только случайно, потому что echo "Hello!"фактически совпадает с echo Hello!(т.е. двойные кавычки не имеют значения для этой конкретной команды эха). Во многих / большинстве других случаев двойные кавычки в функции могут нарушить bash -cкоманду.
Cas

1
Это действительно отвечает на первоначальный вопрос, поэтому, если я не найду лучшего решения, я приму его. Тем не менее, это нарушает мою конкретную функцию (см. Мое редактирование), поскольку зависит от функций, определенных в другом месте скрипта.
BryKKan

1
я сделал некоторые испытания в начале второй половине дня ( с использованием , bash -xcа не просто bash -c) и, похоже , bashявляется достаточно умны , чтобы повторно цитатой вещи в этой ситуации, даже в той степени , заменив двойные кавычки одинарные кавычки и изменения 'в '\''случае необходимости. Я уверен, что будут некоторые случаи, которые он не может обработать, но он определенно работает по крайней мере для простых и умеренно сложных случаев - например, попробуйтеfunction hello() { filename="this is a 'filename' with single quotes and spaces" ; echo "$filename" ; } ; FUNC=$(declare -f hello) ; bash -xc "$FUNC ; hello"
cas

4
@cas declare -fвыводит определение функции способом, который может быть повторно проанализирован bash, поэтому bash -c "$(declare -f)" он работает правильно (при условии, что внешняя оболочка также является bash). Пример, который вы опубликовали, показывает, что он работает правильно - там, где были изменены кавычки, находится след , потому что bash выводит следы в синтаксисе оболочки, например, попытайтесьbash -xc 'echo "hello world"'
Жиль "ТАК - перестать быть злым"

3
Отличный ответ. Я реализовал ваше решение - я хотел бы отметить, что вы можете импортировать сам скрипт из скрипта, при условии, что вы вложите его в условное выражение, которое проверяет, sudo yourFunctionне найдены ли (в противном случае вы получите ошибку сегментации из рекурсии)
GrayedFox

5

«Проблема» заключается в том, что sudoочищает среду (за исключением нескольких допустимых переменных) и устанавливает для некоторых переменных заранее определенные безопасные значения для защиты от угроз безопасности. другими словами, это на самом деле не проблема. Это особенность.

Например, если вы установили PATH="/path/to/myevildirectory:$PATH"и sudoне установили PATH в предварительно определенное значение, то любой сценарий, в котором не указано полное имя пути для ВСЕХ команд, которые он выполняет (т. Е. Большинство сценариев), будет смотреть /path/to/myevildirectoryперед любым другим каталогом. Поместите туда такие команды, как lsили grepили другие распространенные инструменты, и вы можете легко делать все, что вам нравится в системе.

Самый простой / лучший способ - переписать функцию в виде скрипта и сохранить ее где-нибудь в пути (или указать полный путь к скрипту в sudoкомандной строке - что вам нужно будет сделать в любом случае, если sudoне настроено, чтобы позволить вам запустить ЛЮБУЮ команду как root) и сделать ее исполняемой сchmod +x /path/to/scriptname.sh

Переписать функцию оболочки как скрипт так же просто, как просто сохранить команды внутри определения функции в файл (без строк и function ..., {и }).


Это никак не отвечает на вопрос. Он специально хочет не включать это в сценарий.
Уилл

1
Также sudo -Eизбегает очистки окружающей среды.
Уилл

Я до некоторой степени понимаю, почему это происходит. Я надеялся, что есть какие-то средства, чтобы временно отменить это поведение. Где-то еще была упомянута опция -E, хотя в этом случае это не сработало. К сожалению, хотя я ценю объяснение того, как сделать его автономным сценарием, это определенно не отвечает на вопрос, потому что я хотел средства, чтобы избежать этого. Я не контролирую, где конечный пользователь размещает скрипт, и я хотел бы избежать как жестко закодированных каталогов, так и песни и танца, пытаясь точно определить, откуда был запущен основной скрипт.
BryKKan

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

1
@ CAS Это правда. Это не может быть сделано безопасно, является приемлемым ответом в некоторых обстоятельствах. Смотрите мое последнее изменение, хотя. Мне было бы любопытно узнать, является ли ваше мнение о последствиях безопасности таким же.
BryKKan

3

Я написал свою собственную Sudoфункцию bash для этого, она работает для вызова функций и псевдонимов:

function Sudo {
        local firstArg=$1
        if [ $(type -t $firstArg) = function ]
        then
                shift && command sudo bash -c "$(declare -f $firstArg);$firstArg $*"
        elif [ $(type -t $firstArg) = alias ]
        then
                alias sudo='\sudo '
                eval "sudo $@"
        else
                command sudo "$@"
        fi
}

2

Вы можете комбинировать функции и псевдонимы

Пример:

function hello_fn() {
    echo "Hello!" 
}

alias hello='bash -c "$(declare -f hello_fn); hello_fn"' 
alias sudo='sudo '

потом sudo helloработает


1

Вот вариант ответа Уилла . Это включает в себя дополнительный catпроцесс, но предлагает комфорт heredoc. В двух словах это выглядит так:

f () 
{
    echo ok;
}

cat <<EOS | sudo bash
$(declare -f f)
f
EOS

Если вы хотите больше пищи для размышлений, попробуйте это:

#!/bin/bash

f () 
{ 
    x="a b"; 
    menu "$x"; 
    y="difficult thing"; 
    echo "a $y to parse"; 
}

menu () 
{
    [ "$1" == "a b" ] && 
    echo "here's the menu"; 
}

cat <<EOS | sudo bash
$(declare -f f)
$(declare -f menu)
f
EOS

Выход:

here's the menu
a difficult thing to pass

Здесь у нас есть menuфункция, соответствующая функции в вопросе, которая «определена в другом месте в основном скрипте». Если «в другом месте» означает, что его определение уже прочитано на данном этапе, когда выполняется функция, требующая sudoвыполнения, то ситуация аналогична. Но это, возможно, еще не было прочитано. Может быть другая функция, которая все же вызовет ее определение. В этом случае declare -f menuего необходимо заменить чем-то более сложным, либо весь скрипт должен быть исправлен так, чтобы menuфункция уже была объявлена.


Очень интересно. Мне придется попробовать это в какой-то момент. И да, menuфункция была бы объявлена ​​до этого момента, как fвызывается из меню.
BryKKan

0

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

  • используйте $0путь к сценарию, используя его в sudoкоманде, и передайте параметр, который будет проверяться сценарием, для вызова обновления пароля. Пока вы полагаетесь на поиск сценария по пути (а не просто при запуске ./myscript), вы должны получить абсолютный путь в $0.
  • поскольку он sudoзапускает скрипт, он имеет доступ к функциям, которые ему необходимы в скрипте.
  • в верхней части скрипта (точнее, после объявления функций), скрипт проверит его uidи поймет, что он был запущен от имени пользователя root , и увидит, что у него есть опция, позволяющая ему обновить пароль, перейти и сделать тот.

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.