sudoなしで他のユーザーでコマンドリストを実行できないのはなぜですか?

sudoなしで他のユーザーでコマンドリストを実行できないのはなぜですか?

この質問は、他のフォーラムでさまざまな方法で質問されました。ただし、bashで次のことができない理由の適切な説明はありません。

#!/bin/bash
command1
SWITCH_USER_TO rag
command2
command3

一般的に推奨される方法は

#!/bin/bash
command1
sudo -u rag command2
sudo -u rag command3

しかし、bashスクリプトの実行中にある時点で別のbashユーザーに変更し、残りのコマンドを他のユーザーとして実行できないのはなぜですか?

答え1

情報はすでに出ています。私の他の答え、しかしそこに埋もれています。だからここに追加する必要があると思いました。

bashユーザーの変更に関する規定はありませんがzsh

zsh次の変数に値を割り当ててユーザーを変更できます。

  • $EUID:有効なユーザーIDを変更します(それ以上ではありません)。通常、実際のユーザーIDと保存されたセットユーザーID(setuid実行可能ファイルから呼び出された場合)の間、またはeuidが0の場合は、別のIDにeuidを変更できます。
  • $UID:有効ユーザーID、実ユーザーID、保存ユーザーIDを新しい値に変更します。新しい値がゼロ以外の場合、3つの値がすべて同じ値に設定されている場合は、他の値に変更できないため、戻りません。
  • $EGID$GID:同じですが、グループIDのものです。
  • $USERNAMEsudoまたはを使用するのと同じですsu。 euid、ruid、ssuidをそれぞれのユーザーのuidに設定します。また、ユーザーデータベースで定義されているグループメンバーシップに基づいて、egid、rgid、ssgid、および補足グループを設定します。 forと同様に$UID設定しないと何も返されませんが、forと同様にサブシェルのユーザーだけを変更できます。$USERNAMEroot$UID

このスクリプトを「root」として実行する場合:

#! /bin/zsh -
UID=0 # make sure all our uids are 0

id -u # run a command as root

EUID=1000

id -u # run a command as uid 1000 (but the real user id is still 0
      # so that command would be able to change its euid to that.
      # As for the gids, we only have those we had initially, so 
      # if started as "sudo the-script", only the groups root is a
      # member of.

EUID=0 # we're allowed to do that because our ruid is 0. We need to do
       # that because as a non-priviledged user, we can't set our euid
       # to anything else.

EUID=1001 # now we can change our euid since we're superuser again.

id -u # same as above

これで、ユーザーを変更するにはsudoサブシェルsuを使用する必要があります。それ以外の場合は一度だけ実行できます。

#! /bin/zsh -

id -u # run as root

(
  USERNAME=rag
  # that's a subshell running as "rag"

  id # see all relevant group memberships are applied
)
# now back to the parent shell process running as root

(
  USERNAME=stephane
  # another subshell this time running as "stephane"

  id
)

答え2

コアman 2 setuid販売中です。

これで呼び出しプロセスで動作します。もっと重要なのはあなたです。できないあなたの特権を高めなさい。そのため、常に最高の権限()で実行され、それに応じて必要なユーザーにドロップダウンするようにSETUIDビットがsu設定されます。sudoroot

この組み合わせは、他のプログラムを実行してシェルUIDを変更することはできません(これが他のsudoプログラムでこれを期待できない、または他のプログラムで実行できない理由です)、ユーザーが直接(シェルに)変更できないことを意味します。ルートとして実行するか、ユーザーと一緒に実行するかを意図しています。ユーザーとして実行するように切り替えたかったのですが、これは意味がありません。そして、一度特権を放棄すれば戻せません。

答え3

まあ、いつでもこれを行うことができます:

#! /bin/bash -
{ shopt -s expand_aliases;SWITCH_TO_USER(){ { _u=$*;_x="$(declare;alias
shopt -p;set +o);"'set -- "${_a[@]}";unset _x _a';set +x;} 2>/dev/null
exec sudo -u "$1" env "_x=$_x" bash -c 'eval "$_x" 2> /dev/null;. "$0"
' "$0";};alias skip=":||:<<'SWITCH_TO_USER $_u'"
alias SWITCH_TO_USER="{ eval '"'_a=("$@")'"';} 2>/dev/null;SWITCH_TO_USER"
${_u+:} alias skip=:;} 2>/dev/null
skip

echo test
a=foo
set a b

SWITCH_TO_USER root

echo "$a and $1 as $(id -un)"
set -x
foo() { echo "bar as $(id -un)"; }

SWITCH_TO_USER rag

foo
set +x

SWITCH_TO_USER root again

echo "hi again from $(id -un)"

(‿ʘ)

これはそもそも冗談です。要求した内容を達成するためです。予想したものとまったく一致しない場合も、実際には機能しない場合もあります。しかし、ある程度効果があり、いくつかのクールなトリックが含まれているにつれて、次のようないくつかの説明があります。

〜のようにミロスラフ、Linuxスタイルを取り除く能力(とにかくここでは役に立ちません。)権限のないプロセスがuidを変更する唯一の方法は、setuid実行可能ファイルを実行することです。

スーパーユーザー権限を取得したら(たとえば、rootが所有するsetuid実行可能ファイルを実行して)、保存されたセットユーザーIDを削除しない限り、元のユーザーIDである0と異なるIDの間で有効ユーザーIDを前後に切り替えることができます。 (sudo似ているか一般的に行われる作業のようにsu)。

たとえば、

$ sudo cp /usr/bin/env .
$ sudo chmod 4755 ./env

これで、env有効なユーザーIDと保存された設定ユーザーID 0(マイ本物ユーザーIDはまだ1000です):

$ ./env id -u
0
$ ./env id -ru
1000
$ ./env -u PATH =perl -e '$>=1; system("id -u"); $>=0;$>=2; system("id -u");
   $>=0; $>=$<=3; system("id -ru; id -u"); $>=0;$<=$>=4; system("id -ru; id -u")'
1
2
3
3
4
4

perlsetuid/ seteuid(対応する変数$>と変数)ラッパーがあります$<

zshも同様です。

$ sudo zsh -c 'EUID=1; id -u; EUID=0; EUID=2; id -u'
1
2

上記のコマンドは実際のユーザーIDとセーブセットユーザーID 0として呼び出されますが(セーブセットユーザーIDの代わりにidmyを使用するとセーブセットユーザーIDのみになり、実際のユーザーIDはまだ1000になります)が、信頼できないコマンドの場合はまだ多少のダメージを与える可能性があるため、次のように書く必要があります。./envsudo

$ sudo zsh -c 'UID=1 id -u; UID=2 id -u'

(つまり、これらのコマンドを実行するためにすべてのuid(有効、実際、および保存されたセット)を設定します。

bashユーザーIDを変更する方法はありません。したがって、スクリプトを呼び出すsetuid実行可能ファイルがあってもbashこれは役に立ちません。

を使用すると、bashuidを変更するたびにsetuid実行可能ファイルを実行する必要があります。

上記のスクリプトのアイデアは、SWITCH_TO_USERを呼び出して新しいbashインスタンスを実行して残りのスクリプトを実行することです。

SWITCH_TO_USER someusersudo他のユーザーとしてスクリプトを再実行しますが(使用して)まで、スクリプトの起動をスキップする関数ですSWITCH_TO_USER someuser

難しいのは、別のユーザーで新しいbashを起動した後、現在のbashの状態を保存したいということです。

分析してみましょう。

{ shopt -s expand_aliases;

エイリアスが必要です。このスクリプトのトリックの1つは、次SWITCH_TO_USER someuserのようにスクリプトの一部をスキップすることです。

:||: << 'SWITCH_TO_USER someuser'
part to skip
SWITCH_TO_USER

この形式は#if 0Cで使用されているものと似ており、いくつかのコードを完全にコメントアウトする方法です。

:true を返す無動作です。したがって、: || :2番目のものは:実行されません。しかし、解析されます。これは(引用されているように)拡張または解釈が行われない<< 'xxx'文書形式です。xxx

私たちはこれをすることができました:

: << 'SWITCH_TO_USER someuser'
part to skip
SWITCH_TO_USER

ただし、これはここに文書を作成して標準入力として渡す必要があることを意味します::||:

今、難しい部分は、bash解析プロセスの非常に早い段階で拡張エイリアスを使用するという事実です。このセクションのskipエイリアスになる:||: << 'SWITCH_TO_USER someuther'コメントする構造。

続けましょう:

SWITCH_TO_USER(){ { _u=$*;_x="$(declare;alias
shopt -p;set +o);"'set -- "${_a[@]}";unset _x _a';set +x;} 2>/dev/null
exec sudo -u "$1" env "_x=$_x" bash -c 'eval "$_x" 2> /dev/null;. "$0"
' "$0";}

SWITCH_TO_USER の定義です。機能。以下では、SWITCH_TO_USERが最終的にこの関数のエイリアスになるのがわかります。

この関数は、スクリプトを再実行するほとんどのタスクを担当します。最後に、環境内の変数を使用して(同じプロセスにあるため)再実行されるのを見ることができます(exec通常は環境をクリーンアップし、任意の環境変数を渡すことができないため、ここで使用します)。その変数の内容をbashコードで評価し、スクリプト自体を取得します。bash_xenvsudobash$_x

_x以前は、次のように定義されていました。

_x="$(declare;alias;shopt -p;set +o);"'set -- "${_a[@]}";unset _x _a'

すべてdeclare、、、出力はシェルの内部状態のダンプを構成しますaliasshopt -p set +oつまり、すべての変数、関数、エイリアス、およびオプションの定義を評価できるシェルコードにダンプします。その上に$1配列値(以下を参照)に基づいて位置パラメータ(、...)設定を追加し、スクリプトの残りの部分で大きな変数が環境に残らないようにいくつかのクリーンアップ操作を実行します。時間。$2$_a$_x

最初の部分は、stderrが()set +xにリダイレクトされるコマンドグループ内に含まれていることに気づくでしょう。これは、スクリプト(または)の特定の時点で実行されている場合は、その序文がトレースを生成したくないため、できるだけ邪魔を最小限に抑えることを望んでいるためです。したがって、(事前ダンプオプションが設定されていることを確認してから)、実行してトレースを/ dev / nullに送信します。/dev/null{...} 2> /dev/nullset -xset -o xtraceset +xxtrace

stderrも同様の理由で/ dev / nullにリダイレクトされますが、特別なeval "$_X"読み取り専用変数に書き込もうとしたときにエラーを防ぐためです。

引き続きスクリプトを実行しましょう。

alias skip=":||:<<'SWITCH_TO_USER $_u'"

これが上記の技術です。初期スクリプト呼び出し時にキャンセルされます(下記参照)。

alias SWITCH_TO_USER="{ eval '"'_a=("$@")'"';} 2>/dev/null;SWITCH_TO_USER"

これで、SWITCH_TO_USERの周りのエイリアスラッパーです。主な理由は、$1スクリプトの残りの部分を解釈する$2新しいパラメータに位置パラメータ(、...)を渡すことができるためです。bash私たちはこれを行うことはできませんSWITCH_TO_USER 機能関数の内部には、"$@"スクリプトのパラメータではなく関数のパラメータがあるためです。 stderrを/ dev / nullにリダイレクトすることもxtracesを隠すことです。これはeval解決するためのものですbashSWITCH_TO_USER 機能

${_u+:} alias skip=:

このセクションでは、変数が設定されていない限り、エイリアスskipを解放します(:no-opコマンドで置き換えます)。$_u

skip

これが私たちのskipエイリアスです。初めて呼び出されると、ただ:(何もしません)だけです。サブシーケンスが呼び出されると、次のように表示されます:||: << 'SWITCH_TO_USER root'

echo test
a=foo
set a b

SWITCH_TO_USER root

たとえば、この時点で私たちはユーザーとしてスクリプトを再呼び出しし、rootスクリプトは保存された状態を復元し、その行にSWITCH_TO_USER rootジャンプして続行します。

つまり、SWITCH_TO_USER行の先頭に引数の間にスペースを1つだけ入れ、statと同じように書く必要があります。

ほとんどの状態、stdin、stdout、およびstderrは保存されますが、他のファイル記述子は明示的にそうしないようsudoに設定しない限り、通常は閉じられるため保存されません。たとえば、

exec 3> some-file
SWITCH_TO_USER bob
echo test >&3

通常は動作しません。

また、これを行う場合は、次の点に注意してください。

SWITCH_TO_USER alice
SWITCH_TO_USER bob
SWITCH_TO_USER root

sudoこれはasaliceとasaliceに対する権限がある場合にのみ機能します。sudobobbobroot

したがって、実際にはあまり役に立ちません。su代わりに(またはsudo呼び出し元の代わりにターゲットユーザーを認証するsudo構成)を使用する方が合理的ですが、sudoこれはまだすべてのユーザーのパスワードを知る必要があることを意味します。

答え4

「sh」またはシェルコマンドを実行して、「sudo -u ram sh」コマンドを使用して「ram」ユーザーに変更できます。 「終了」コマンドを使用すると、元のユーザーに戻ります。

関連情報