Sudoを使用したBashスクリプト関数の実行


22

さまざまな処理を行うスクリプトがありますが、そのほとんどは特別な特権を必要としません。ただし、関数内に含まれている特定のセクションには、ルート権限が必要です。

スクリプト全体をルートとして実行する必要はありません。スクリプト内からルート権限でこの関数を呼び出すことができるようにしたいと考えています。とにかくインタラクティブであるため、必要に応じてパスワードを要求することは問題ではありません。ただし、を使用しようとするとsudo functionx、次のようになります。

sudo: functionx: command not found

予想通り、export違いはありませんでした。いくつかの理由で、関数を分割して個別のスクリプトとして実行するのではなく、スクリプト内で関数を直接実行できるようにしたいと思います。

関数を抽出せずにsudoに「表示」し、適切なディレクトリを見つけて、それをスタンドアロンスクリプトとして実行する方法はありますか。

この関数はそれ自体が1ページの長さであり、二重引用符と単一引用符で囲まれた複数の文字列を含んでいます。また、メインスクリプトの他の場所で定義されているメニュー関数にも依存しています。

sudo ANYを持っている人だけが関数を実行できると期待します。それは、パスワードを変更することの1つです。


いくつかの機能が関係しているという事実は、それをさらに複雑にし、失敗しやすくします。ここで、メニュー関数が呼び出す可能性のある他の関数と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!(つまり、二重引用符はこの特定のechoコマンドには影響しません)。他の多く/ほとんどの状況では、関数内の二重引用符がbash -cコマンドを中断する可能性があります。
cas

1
これは元の質問に答えているので、より良い解決策が得られない場合は受け入れます。ただし、スクリプト内の別の場所で定義されている関数に依存しているため、特定の関数(編集を参照)が破損します。
BryKKan

1
私は今日の午後にいくつかのテストを行いました(bash -xcただではなくbash -c)を使用bashして、二重引用符を単一引用符に置き換えて必要に応じて変更'する程度まで、この状況で物事を再引用するのに十分賢いようです'\''。処理できない場合もあると思いますが、少なくとも単純で中程度に複雑な場合には間違いなく機能します。たとえば、tryfunction 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 -c "$(declare -f)" 作業は(外殻もバッシュであると仮定して)正確。あなたはそれが正しく動作ショーを掲載例-引用符が変更された場所はあるトレースにはbashシェル構文でトレースを出力し、例えばしようとするため、bash -xc 'echo "hello world"'
ジル「SO-停止されて悪」

3
素晴らしい答え。ソリューションを実装しました-スクリプト自体をスクリプト内からインポートできることに注意してください。条件付きでネストするとsudo yourFunction、検出されたかどうかをチェックします(そうでない場合、再帰からセグメンテーションエラーが発生します)
GrayedFox

5

「問題」は、sudoセキュリティリスクから保護するために、環境(いくつかの許可された変数を除く)をクリアし、いくつかの変数を事前定義された安全な値に設定することです。つまり、これは実際には問題ではありません。これは機能です。

たとえば、PATHを事前定義された値に設定しPATH="/path/to/myevildirectory:$PATH"、設定しsudoなかった場合、実行するすべてのコマンドのフルパス名を指定しなかったスクリプト(つまり、ほとんどのスクリプト)は/path/to/myevildirectory、他のディレクトリの前を調べます。以下のようなコマンドを置くlsか、grepそこに他の一般的なツールや、あなたは簡単にシステム上のようなものは何でもあなたを行うことができます。

最も簡単/最良の方法は、関数をスクリプトとして書き直してパスのどこかに保存することです(またはsudoコマンドラインでスクリプトへのフルパスを指定します- sudo許可するように構成されていない限り、これを行う必要があります)ルートとして任意のコマンドを実行します)、それを実行可能にしますchmod +x /path/to/scriptname.sh

シェル関数をスクリプトとして書き換えるのは、関数定義内のコマンドをファイルに保存するのと同じくらい簡単です(function ...{および}行なしで)。


これは、質問には一切答えません。彼は特にスクリプトに入れないようにしたいと考えています。
ウィル

1
またsudo -E、環境のクリアを回避します。
ウィル

なぜそれが起こっているのかある程度理解しています。この動作を一時的に無効にする何らかの手段があることを望んでいました。他のどこかで-Eオプションが言及されましたが、この場合は機能しませんでした。残念ながら、スタンドアロンスクリプトにする方法の説明はありますが、それを回避する手段が必要だったので、質問に答えることはできません。エンドユーザーがスクリプトを配置する場所を制御することはできません。ハードコードされたディレクトリと、メインスクリプトの実行元を正確に判断しようとする歌と踊りの両方を避けたいと思います。
-BryKKan

OPがそれを要求したかどうかは関係ありません。彼が望んでいることがうまくいかない場合、または非常に安全でない何かをすることによってのみ動作させることができる場合、彼らはそれを伝えられ、代替案を提供する必要があります-代替案が彼らが明らかに望まないと述べたものであっても時にはそれが唯一または安全にそれを行う最善の方法です)。誰かに足で銃を向けて引き金を引いた場合に起こりうる結果について警告せずに、自分の足を撃つ方法を教えるのは無責任でしょう。
cas

1
@casそれは本当です。状況によっては、安全に実行できないというのは受け入れられる答えです。私の最後の編集を見てください。セキュリティへの影響についてのあなたの意見がそれと同じであるかどうか知りたいです。
BryKKan

3

そのために独自のSudobash関数を作成しました。関数とエイリアスを呼び出すように機能します。

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

スクリプトが(a)自己完結型であるか、(b)(ホームディレクトリの場所を覚えるのではなく)場所に基づいてコンポーネントを取得できると仮定すると、次のようなことができます。

  • コマンドで$0パス名を使用してスクリプトのパス名を使用しsudo、スクリプトがチェックするオプションを渡して、パスワードの更新を呼び出します。(単に実行するのではなく./myscript)パスでスクリプトを見つけることに依存している限り、で絶対パス名を取得する必要があります$0
  • 以来、sudoスクリプトを実行し、それが持っているアクセス、スクリプトに必要な機能にします。
  • スクリプトの上部(関数宣言を過ぎた部分)で、スクリプトはそれを確認しuidrootユーザーとして実行されたことを認識し、パスワードの更新、実行、実行を指示するオプションが設定されていることを確認します。それ。

スクリプトはさまざまな理由で繰り返されます。特権の変更はその1つです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.