'sudo -E'がエクスポート-fとしてエクスポートした関数環境変数を保存しないのはなぜですか?

'sudo -E'がエクスポート-fとしてエクスポートした関数環境変数を保存しないのはなぜですか?

script.shから:

#!/bin/bash

func(){
  echo "I am here a func."
}

export -f func
export variable="I am here a variable"

sudo -E bash -c "func ; echo $variable"

出力は次のとおりです。

bash: func: command not found
I am here a variable
  1. なぜ?
  2. 初期シェルでsudoを介して[script.shで宣言された]関数を呼び出すことができるソリューションはありますか?

実際、私は次の方法を知っています。

#!/bin/bash

func(){
  echo "I am here a func."
}

FUNC=$(declare -f func)
export variable="I am here a variable"

sudo -E bash -c "$FUNC; func ; echo $variable"

ところで、このようにすべての機能を登録しなければならないので重すぎると思います。

答え1

export -f func私の環境で以下を確認した後:

$ env | grep func
BASH_FUNC_func%%=() …

Bashは、同じ名前BASH_FUNC_foo%%で始まる値を持つ変数として関数をエクスポートします()シェルショック名前にはプレフィックス()がありませんfoo=() …。 Appleプレフィックスは__BASH_FUNC<。とにかく値は()

sudoこれらの名前と環境全体に非常に注意を払ってください。sudoBashスクリプトを実行するためにスクリプトを使用できると想像してください。スクリプトが合法的に使用されていると想像してくださいcat。あなたという名前のシェル関数をエクスポートすると、catスクリプトを効果的に変更し、任意のコードを実行できます。

自分のものを使うことができます$PATH(そしてこれを行う方法はありますか?;ただし、管理者はこれを防ぐ方法もあります。または利用可能な場合$LD_PRELOAD(それは簡単ではありません。参照)man 5 sudoers)を言及したLD_

エクスポートしたシェル関数に戻りましょう。まずsudo -Vroot(sudo sudo -V一般ユーザー)として実行すると、出力の一部は次のようになります。

Environment variables to remove:
        *=()*

または:

Environment variables to remove:
        __BASH_FUNC<*
        BASH_FUNC_*

バージョンによって異なると思いますsudo

次の行を使用して、リストからこれらのパターンを削除することができますsudoers(ただし、そうしないで最初に完全な答えを読んでください)。

Defaults       env_delete -= "*=()* BASH_FUNC_* __BASH_FUNC<*"

「メンテナンス」リストに追加することもできます(追加する必要はありません)。

Defaults        env_keep += "*=()* BASH_FUNC_* __BASH_FUNC<*"

ただし、エクスポートされた関数が必ずしも一緒に動作するわけではありませんsudo。これは、パターンがハード*=()*コーディングされ、sudo拒否変数の一部のバージョン値がで始まるためです()。この行動は進化しました。利用可能なソリューションは、sudo使用しているバージョンによって異なります。

~からman 5 sudoers:

コマンド環境

環境変数はプログラムの動作に影響を与える可能性があるため、sudoers実行中のコマンドによって継承されるユーザー環境の変数を制限する方法が提供されます。 sudoersには、環境変数を処理する2つの方法があります。

デフォルトでは、このenv_resetオプションは有効になっています。 [...] HOME、、環境変数はターゲットユーザーに基づいて初期化され、MAIL変数は呼び出し元ユーザーに基づいて設定されます。またはオプションが許可されている場合、などの他の変数は、呼び出すユーザー環境で保存されます。これは実際に環境変数のホワイトリストです。 [...] 値が or で始まる環境変数は、名前と値の部分が or と一致しない限り削除されます。これはbashシェルの関数として解釈できるからです。バージョン 1.8.11 以前では、これらの変数は常に削除されました。SHELLLOGNAMEUSERSUDO_*DISPLAYPATHTERMenv_checkenv_keep()env_keepenv_check

ただしenv_reset、このオプションが無効になっている場合、およびenv_checkオプションenv_deleteによって明示的に拒否されていないすべての変数は、呼び出しプロセスによって継承されます。この場合のenv_check動作はenv_deleteブラックリストと同じです。バージョン 1.8.21 以前では、()値が で始まる環境変数は常に削除されました。バージョン 1.8.21 以降、パターンを使用してenv_deletebash シェル機能を一致させます。潜在的に危険な環境変数をすべてブラックリストに登録することは不可能であるため、デフォルトの動作をenv_resetお勧めします。

env_check、またはでenv_delete指定された環境変数には、ゼロ個以上の文字に一致するenv_keep1つ以上の文字を含めることができます。*他のワイルドカード文字はサポートされていません。

デフォルトでは、環境変数は名前で一致します。ただし、パターンに等号(=)が含まれている場合は、変数名と値の両方が一致する必要があります。たとえば、bashシェル関数は次のように一致することができます。

env_keep += "BASH_FUNC_my_func%%=()*"

=()*サフィックスがない場合、bash シェル機能はデフォルトで維持されないため、一致しません。

ご覧のとおり、env_resetこれは重要です。おそらく活性化されているでしょう。-Eoption() の要点は、要求に応じてsudo -E無効にすることです。env_reset


私のKubuntu 18.04.3 LTSでは、私のsudoバージョンは1.8.21です。デフォルトでは、*=()*削除する変数のリストに配置されます。次の行はsudoersこれを停止します。

Defaults       env_delete -= "*=()*"

これにより、sudo -Eエクスポートされたシェル機能はすべて保存されます。これが私の主な答えです。


私のDebian 9sudoバージョンは1.8.19です。デフォルトでは、削除する変数のリストにBASH_FUNC_*およびが配置されます。__BASH_FUNC<*これらの項目を削除しても効果はありません()。これはハードコードされているように見える値が何であれ、このツールはほとんど常に変数を拒否するためです。

このバージョンのマニュアルはいくつかの手がかりを提供します。解決策が見つかりました。

Defaults       env_keep += "BASH_FUNC_func%%=()*"

それからsudo いいえ -Efuncエクスポートすると、この特定のシェル機能()が維持されます。欠点(場合によっては利点かもしれません):

  • これは適用されないため、sudo -E必要に応じて残りの環境を単純に維持することはできません。
  • ワイルドカードなしで変数のフルネームを明示的に指定する必要があります。BASH_FUNC_*%%=()*それ以外の場合はBASH_FUNC_*動作*=()*しません。

この方法は1.8.21でも動作します。


マニュアルによると、バージョン1.8.11より前は、で始まる変数は()常に削除されました。あなたがsudoその年齢であれば、質問に使用したトリック(または同様のトリック)以外の解決策はありません。


メモ:

  • sudoers他のファイルのように編集しないでくださいvisudo。万一の場合に備えたその他の予防措置:
    • 別の「代替」シェルをルート()で始めますsudo -isudoersメインシェルで愚かなことをした場合は、このファイルに頼らずにrootとして実行できます。
    • SSH経由で接続する場合は、昇格シェルを作成する前にまたはを使用してくださいtmuxscreen愚かなことをしてsudoers(偶然に)接続が失敗した場合、管理者権限シェルは通常のユーザーが終了するのではなく再接続するのを待ちます。
    • cp -a特に実験している場合は、まずファイルのバックアップコピーを作成します()。
    • 作業が完了したと思われる場合は、「代替」昇格シェルを終了する前に、root アクセス権を再度取得できることを確認してください。
  • sudoあなたが唯一のユーザーであり、sudoerが任意のコードを実行することを許可するトリックに対する防御を強化することにあまり意味がない場合でも(あなたは任意のトリックなしで任意のsudoコードを自由に実行でき、それを望むので)あまりにも多くを許可するする前に2回考えてください。 。シェル機能をそのまま維持すればsudo今はすぐではありませんが、将来は簡単に攻撃を受けることができます。

関連情報