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


8

この質問は他のフォーラムで別の方法で尋ねられました。しかし、なぜbashで以下を実行できないのか、きちんとした説明はありません。

#!/bin/bash
command1
SWITCH_USER_TO rag
command2
command3

通常、推奨される方法は

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

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


この質問は、superuserのsuperuser.com/questions/93385/…でも尋ねられました。この回答superuser.com/a/573882/76251は、スクリプト内でユーザーを変更するためのHERE DOCのような方法を提供します。
Tim Kennedy

回答:


4

情報は既に他の回答に含まれていますが、少し埋まっています。それで、ここに追加したいと思いました。

bashユーザーを変更するための準備はありませんが、zshあります。

ではzsh、これらの変数に値を割り当ててユーザーを変更します。

  • $EUID:実効ユーザーIDを変更します(それ以外は何も変更しません)。通常、実際のユーザーIDと保存されたセットのユーザーID(setuid実行可能ファイルから呼び出された場合)の間でeuidを変更したり、euidが0の場合は何かに変更したりできます。
  • $UID:実効ユーザーID、実ユーザーID、および保存されたセットユーザーIDを新しい値に変更します。その新しい値が0でない限り、戻ってくることはありません。3つすべてが同じ値に設定されると、それを他の値に変更する方法はありません。
  • $EGIDand $GID:同じことですが、グループIDが対象です。
  • $USERNAME。それは使用してのようなものですsudosu。euid、ruid、ssuidをそのユーザーのuidに設定します。また、ユーザーデータベースで定義されているグループメンバーシップに基づいて、egid、rgid、ssgid、および補足グループを設定します。forのように$UID、に設定$USERNAMEしない限りroot、戻ってくることはありませんが、forのように$UID、サブシェルの場合のみユーザーを変更できます。

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

#! /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
)

8

カーネルのオファーman 2 setuidと友達。

今、それは呼び出しプロセスで動作します。さらに重要なことに、特権を昇格することはできません。これが理由でsuありsudo、SETUIDビットが設定されているため、常に最高の特権(root)で実行され、それに応じて目的のユーザーにドロップされます。

その組み合わせは、他のプログラムを実行してシェルUIDを変更することはできません(そのためsudo、他のプログラムがそれを期待したり、他のプログラムでそれを実行したりすることはできません)。 rootまたは切り替えたいユーザーと同じユーザーとして実行してもかまわない。さらに、特権を削除すると、元に戻すことはできません。


6

まあ、あなたはいつでも行うことができます:

#! /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を元のユーザーID 0と他のIDの間で切り替えることができます(のようなもの、sudoまたはsu通常そうするもの)。

例えば:

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

これenvで、有効なユーザーIDと0の保存されたセットユーザーID(実際のユーザー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を使用して呼び出されます(ただし、./env代わりに自分を使用した場合、保存ユーザーID sudoのみで、実際のユーザーIDは1000のままでした)。つまり、信頼されていないコマンドであったとしても、いくつかのダメージを与える可能性があるため、代わりに次のように記述します。

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

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

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

ではbash、uidを変更するたびにsetuid実行可能ファイルを実行する必要があります。

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

SWITCH_TO_USER someuser多かれ少なかれ、別のユーザーとして(を使用してsudo)スクリプトを再度実行する関数ですが、スクリプトの開始はまでスキップし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

しかし、それはヒアドキュメントがstdinとしてに書かれて渡されなければならなかったことを意味するでしょう::||:それを避けます。

ここでハックになるのは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)再実行することがわかります(通常は環境をサニタイズし、任意のenv変数を渡せないため、ここで使用します)。これにより、その変数の内容がbashコードとして評価され、スクリプト自体が提供されます。bash_xenvsudobash$_x

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

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

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

までの最初の部分set +xは、stderrが/dev/null{...} 2> /dev/null)にリダイレクトされるコマンドグループにラップされていることがわかります。これは、スクリプトset -x(またはset -o xtrace)のある時点で実行される場合、そのプリアンブルがトレースを生成しないようにするためです。したがって、トレースを/ dev / nullに送信するset +xxtrace事前にオプション(を含む)設定をダンプすることを確認した後)を実行します。

eval "$_X"標準エラー出力にも同様の理由では/ dev / nullにリダイレクトされるだけでなく、特別な読み取り専用の変数への試みを書くことについてエラーを回避します。

スクリプトを続けましょう。

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

それが上記のトリックです。最初のスクリプト呼び出しでは、キャンセルされます(以下を参照)。

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

SWITCH_TO_USERのエイリアスラッパーになりました。主な理由は、残りのスクリプトを解釈するnewに位置パラメータ($1$2...)を渡すことができるようにするためですbashSWITCH_TO_USER 関数"$@"ではスクリプトの引数ではなく、関数の引数であるため、関数でそれを行うことはできませんでした。/ dev / nullへのstderrリダイレクトは、xtraceを非表示にすることであり、これevalはのバグを回避することですbash。次に、SWITCH_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行までスキップして続行します。

つまり、statとまったく同じようSWITCH_TO_USERに、行の先頭に、引数の間に1つのスペースを入れて記述する必要があります。

状態、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ようにalicealiceする権利を有するsudoなどbob、およびbobなどroot

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


あなたのスクリプトが何をしているのか説明してもらえますか?
2013

@ragは、あなたが...それを求め
ステファンChazelas

1
それはあなたのエントリーioshccですか?あなたはたった1行で入力するべきでした。
ott-- 2013

-2

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

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