Sudo を使ってシェルスクリプトを実行するときに、環境変数も引き渡したい

プロビジョニングスクリプトを書いています。そのプロビジョニングスクリプトはRootで実行されるのですが、そのスクリプトから、他のユーザとして、別のスクリプトを実行したいというケースがありました。その子スクリプトは、サーバーを起動します。

* [setup.sh](https://github.com/TsuyoshiUshio/volley/blob/master/template/template/azure/vm/setup.sh)

ところが、そのサーバーの挙動がおかしく、原因は、親スクリプトで設定した環境変数が、子スクリプトに渡っていない様子です。そのサーバーをプロビジョニングしたVMで手動で起動するとすべての環境変数設定が出来て正しく起動しますので、一瞬不思議にみえますが、単純にプロビジョニングスクリプトでの実行時に環境変数がわかっていないのです。

# 他のユーザとして、シェルを実行する方法

他のユーザとしてシェルの実行は`sudo`を使います。現在のユーザが、そのユーザに対して権限がなければ、パスワードを求められますが、例えばRootユーザから、他のユーザになる場合は、パスワードは求められません。

```bash
$ sudo -u ushio ./child.sh
```

ところが、この方法では、親スクリプトで設定した環境変数が渡りません。試してみましょう。

_parent.sh_

```bash
#!/bin/bash

export FOO=BAR

sudo -u ushio ./child.sh 
```

_child.sh_

```bash
#!/bin/bash

# source ~/.bashrc

echo "FOO: "${FOO}
```

## 実行結果

```bash
sudo su 
[sudo] password for ushio: 
# ./parent.sh 
FOO: 
```

何も表示されません。ちなみに、`source ~/.bashrc` の部分を有効にすると、`./child.sh: line 3: /root/.bashrc: Permission denied` のエラーになります。これは、別のユーザで実行しても、root としてみなされている雰囲気のようです。実際に、env コマンドを`child.sh`で実行してみると、パスの構成などからみて、ushioユーザの.bashrcは実行されていないようです。これが、envを`child.sh` で実行してみた結果ですが、なんとも中途半端な感じになっていて、パスは、Rootのものだけど、環境変数は特にわたっていません。

```bash
LANG=C.UTF-8
SUDO_GID=0
DISPLAY=127.0.0.1:0.0
COLORTERM=truecolor
USERNAME=ushio
SUDO_COMMAND=./child.sh
USER=ushio
PWD=/home/ushio/Codes/DevSecOps/volley/playground/environmentvariables
HOME=/root
SUDO_USER=root
SUDO_UID=0
MAIL=/var/mail/ushio
SHELL=/usr/bin/zsh
TERM=xterm-256color
SHLVL=1
LOGNAME=ushio
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin
_=/usr/bin/env
```

#　解決策

とても簡単で、`-E` オプションを使います。

_parent.sh_

```bash
#!/bin/bash

export FOO=BAR

sudo -u ushio -E ./child.sh 
```

## 実行結果

```bash
# ./parent.sh 
FOO: BAR
```

## よりより解決策

今回は、`-E` で解決ですが、`--preserve-env=`で特定の環境変数も渡せるようですね。

`sudo -u ushio --preserve-env=FOO ./child.sh` に変更しても結果は同じでうまくいきました。

```bash
     -E, --preserve-env
                 Indicates to the security policy that the user wishes to preserve their existing environment vari‐
                 ables.  The security policy may return an error if the user does not have permission to preserve the
                 environment.

     --preserve-env=list
                 Indicates to the security policy that the user wishes to add the comma-separated list of environment
                 variables to those preserved from the user's environment.  The security policy may return an error if
                 the user does not have permission to preserve the environment.
```

複数のものも試してみましょう。カンマ区切りと書いてあります。

_parent.sh_

```bash
#!/bin/bash

export FOO=BAR
export BUZ=QUUX

sudo -u ushio --preserve-env=FOO,BUZ ./child.sh 
```

## 実行結果

```bash
# ./parent.sh 
FOO: BAR
BUZ: QUUX
``


# 追記 $HOME 環境変数にご注意

`-E` を追加したところ、スクリプトが思わぬところで失敗しました。`$HOME` 環境変数が、`-E`なしの時はターゲットのユーザとして動作するので例えば `/home/ushio` になるのですが、`/root`になってしまうようです。先ほどのenv
を`--preserve-env=list` でやってみましょう。ああ！なるほど、だから`--preserve-env=list`オプションがあるのか！意図したもののみ入れ替えるわけですね。

```bash
LANG=C.UTF-8
SUDO_GID=0
DISPLAY=127.0.0.1:0.0
COLORTERM=truecolor
USERNAME=ushio
SUDO_COMMAND=./child.sh
USER=ushio
PWD=/home/ushio/Codes/DevSecOps/volley/playground/environmentvariables
HOME=/root
SUDO_USER=root
FOO=BAR
SUDO_UID=0
MAIL=/var/mail/ushio
SHELL=/usr/bin/zsh
TERM=xterm-256color
SHLVL=1
LOGNAME=ushio
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin
_=/usr/bin/env
FOO: BAR
```