Виконання функції сценарію Баша із судо


22

У мене є сценарій, який робить багато різних речей, більшість з яких не потребує особливих привілеїв. Однак для одного конкретного розділу, який я містив у собі функції, потрібні кореневі привілеї.

Я не хочу вимагати, щоб весь скрипт запускався як корінь, і я хочу мати можливість викликати цю функцію з привілеями 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 -cрядок.

Якщо ви хочете експортувати всі функції з зовнішнього екземпляра 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"'
Gilles 'SO- перестаньте бути зла'

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процес, але пропонує комфорт гередока. Коротше кажучи, це так:

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

Якщо припустити, що ваш скрипт або (a) є автономним, або (b) може джерелом своїх компонентів вибирати його місце розташування (а не пам'ятати, де знаходиться ваш домашній каталог), ви можете зробити щось подібне:

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

Сценарії можуть повторюватися самостійно з різних причин: зміна привілеїв - одна з таких.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.