su と sudo の環境変数に関連するオプションを完全に理解するためのまとめ

Written by @ryysud

Oct 19, 2018 19:08 · 2618 words · 6 minutes read #linux

TL;DR

環境変数周りで困ったら、基本的には login shell で実行するオプションを使っておくのが間違いなさそう。

  • su -
  • sudo -i

su コマンドの環境変数に関連するオプション

  • -m, -p, –preserve-environment は切り替え先のユーザーの ~/.bashrc を読まないオプション
  • - (hyphen), -l, –login は login shell で実行するオプション

sudo コマンドの環境変数に関連するオプション

  • -E, –preserve-env はコマンドを実行する際に切り替え元のユーザーの環境変数を保持しておくオプション
  • -i, –login は login shell で実行するオプション

検証環境

  • OS は CentOS 7.5(ローカルで Docker Container として起動)
  • 検証ユーザーとして anonymous ユーザーを用意(sudoers にも追加)
  • su のバージョンは 2.23.2
  • sudo のバージョンは 1.8.19p2
[root@dev /]# cat /etc/redhat-release
CentOS Linux release 7.5.1804 (Core)

[root@dev /]# uname -a
Linux dev 4.9.93-linuxkit-aufs #1 SMP Wed Jun 6 16:55:56 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux

[root@dev /]# su --version
su from util-linux 2.23.2

[root@dev /]# sudo --version
Sudo version 1.8.19p2

基本方針

su –help と sudo –help で表示された options を眺めて、それっぽいものを洗い出す方針で進めていきます。

bash での login shell が読むファイルの順番をおさらい

ここから下で、login shell で実行するオプションが出てくるので軽くおさらいしておくと、login shell で実行するオプションが指定された際には ① から読まれ、オプションが指定されずに interactive shell で実行する際には ③ から読まれる形となります。

〜 login shell で実行するオプションが “指定された” ときはここから 〜

① /etc/profile
② ~/.bash_profile(なかったら ~/.bash_login、~/.profile の順で読まれる)

〜 login shell で実行するオプションが “指定されてない” ときはここから 〜

③ ~/.bashrc
④ /etc/bashrc
⑤ bash 実行

bash での login shell が読むファイルも用意しておく

以下のファイルに確認用の環境変数を仕込んでおく。

  • /root/.bash_profile( export READ_ROOT_BASH_PROFILE=true を末尾に追加 )
  • /root/.bashrc( export READ_ROOT_BASHRC=true を末尾に追加 )
  • /home/anonymous/.bash_profile( export READ_ANONYMOUS_BASH_PROFILE=true を末尾に追加 )
  • /home/anonymous/.bashrc( export READ_ANONYMOUS_BASHRC=true を末尾に追加 )

su コマンドの環境変数に関連するオプション

オプションなし

初めに1つもオプションをつけない状態の挙動を確認しておきます。オプションなしで root ユーザーから anonymous ユーザーに切り替えてみると、切り替え先のユーザーである anonymous ユーザーの ~/.bashrc が読まれて、環境変数が追加されていることがわかります。

# root ユーザー時点での状態
[root@dev /]# env | grep -i read
READ_ROOT_BASH_PROFILE=true
READ_ROOT_BASHRC=true

# anonymous ユーザーに切り替え
[root@dev /]# su anonymous

# anonymous ユーザーに切り替え後の状態
[anonymous@dev /]$ env | grep -i read
READ_ROOT_BASH_PROFILE=true
READ_ROOT_BASHRC=true
READ_ANONYMOUS_BASHRC=true # <--- 追加された環境変数

-m, -p, –preserve-environment

説明

切り替え先のユーザーの ~/.bashrc を読まないオプション。このオプションを指定することで、切り替え先のユーザーの ~/.bashrc を読まずに、切り替え元のユーザーの ~/.bashrc を再度読ませることができます。

[root@dev /]# su --help | grep '\-m'
 -m, -p, --preserve-environment  do not reset environment variables

実際に挙動をみていく

-m オプションありで root ユーザーから anonymous ユーザーに切り替えてみると、anonymous ユーザーの ~/.bashrc が読まれずに、root ユーザーの ~/.bashrc が読まれ、結果的に切り替え先のユーザーの環境変数は追加されていないことがわかります。しかも、切り替え先のユーザーで切り替え元のユーザーの ~/.bashrc を読みにいくので、大体は Permission denied になりそうですし、このオプションがあって嬉しいユースケースが浮かびません…。まあ、覚えておいて損はなさそうな感じですね〜!

# root ユーザー時点での状態
[root@dev /]# env | grep -i read
READ_ROOT_BASH_PROFILE=true
READ_ROOT_BASHRC=true

# anonymous ユーザーに切り替え(同一の挙動なので -m だけ試す)
[root@dev /]# su -m anonymous
bash: /root/.bashrc: Permission denied

# anonymous ユーザーに切り替え後の状態
bash-4.2$ env | grep -i read
READ_ROOT_BASH_PROFILE=true
READ_ROOT_BASHRC=true

- (hyphen), -l, –login

説明

login shell で実行するオプション。

[root@dev /]# su --help | grep '\--login'
 -, -l, --login                  make the shell a login shell

実際に挙動をみていく

- (hyphen) オプションありで root ユーザーから anonymous ユーザーに切り替えてみると、login shell での実行となり、anonymous ユーザーの ~/.bash_profile と ~/.bashrc が読まれて、結果的に切り替え元のユーザーが保持していた環境変数は破棄されて、切り替え先のユーザーの環境変数のみになることがわかります。切り替え先のユーザーの初期設定が適用されるので、クリーンな状態でユーザー切り替えをやりたいときには、このオプション一択となります!(自分もよっぽどのことがない限りは基本的にこれを使ってます)

# root ユーザー時点での状態
[root@dev /]# env | grep -i read
READ_ROOT_BASH_PROFILE=true
READ_ROOT_BASHRC=true

# anonymous ユーザーに切り替え(同一の挙動なので - だけ試す)
[root@dev /]# su - anonymous
Last login: Fri Oct 19 11:23:58 UTC 2018 on pts/2

# anonymous ユーザーに切り替え後の状態
[anonymous@dev ~]$ env | grep -i read
READ_ANONYMOUS_BASHRC=true # <--- 切り替え先のユーザーの環境変数のみになる
READ_ANONYMOUS_BASH_PROFILE=true # <--- 切り替え先のユーザーの環境変数のみになる

sudo コマンドの環境変数に関連するオプション

オプションなし

初めに1つもオプションをつけない状態の挙動を確認しておきます。オプションなしで root ユーザーから anonymous ユーザーに切り替えてコマンドを実行してみると、切り替え元のユーザーの環境変数は渡らないことがわかります。

# root ユーザー時点での状態
[root@dev /]# env | grep -i read
READ_ROOT_BASH_PROFILE=true
READ_ROOT_BASHRC=true

# anonymous ユーザーに切り替えてコマンドを実行
[root@dev /]# sudo -u anonymous env | grep -i read

-E, –preserve-env

説明

コマンドを実行する際に、切り替え元のユーザーの環境変数を保持しておくオプション。

[root@dev /]# sudo --help | grep '\-E'
  -E, --preserve-env          preserve user environment when running command

実際に挙動をみていく

-E オプションありで root ユーザーから anonymous ユーザーに切り替えてコマンドを実行してみると、コマンド実行時に切り替え元のユーザーの環境変数が渡っていることがわかります。

# root ユーザー時点での状態
[root@dev /]# env | grep -i read
READ_ROOT_BASH_PROFILE=true
READ_ROOT_BASHRC=true

# anonymous ユーザーに切り替えてコマンドを実行
[root@dev /]# sudo -E -u anonymous env | grep -i read
READ_ROOT_BASH_PROFILE=true # <--- 切り替え元のユーザーの環境変数が渡っている
READ_ROOT_BASHRC=true # <--- 切り替え元のユーザーの環境変数が渡っている

-i, –login

説明

[root@dev /]# sudo --help | grep '\--login'
  -i, --login                 run login shell as the target user; a command may also be

実際に挙動をみていく

-i オプションありで root ユーザーから anonymous ユーザーに切り替えてコマンドを実行してみると、login shell での実行となり、su - と同じ挙動となります。クリーンな状態でやりたいときは、このオプションが良さそうですね!(いつも sudo -E しか使ってなかったので今後はこれを使っていく所存)

なお、-E と -i の両方を指定した際には、-i の login shell 実行に -E が負ける形となります。参考までに。

# root ユーザー時点での状態
[root@dev /]# env | grep -i read
READ_ROOT_BASH_PROFILE=true
READ_ROOT_BASHRC=true

# anonymous ユーザーに切り替えてコマンドを実行
[root@dev /]# sudo -i -u anonymous env | grep -i read
READ_ANONYMOUS_BASHRC=true
READ_ANONYMOUS_BASH_PROFILE=true

まとめ

冒頭の TL;DR がまとめ的な役割なので、そちらを参照ください。基本的には su - と sudo -i でやっていくのが1番良さそうということがわかったので、今後はググることはなさそうです。