WEBアクセスでSUDO実行

# はじめに

会社のHPをWordPressで構築していますが、「最近（忘れたころに）」DB接続エラーが起きてしまって困ることがありました(サーバー: SakuraVPS/AlmaLinuxベース)。１年ぶりに接続エラーが起きると『何事だ』と焦りますよね。

<img width="80%" alt="NEOKI" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294946/d36fcebf-a52f-43d3-d1a1-1dd90cefdcaf.png">

お休み中で油断して自宅でまったりとしているとき（お昼寝中）に『閲覧できないんですけど...』とトラブル連絡が来てびっくり仰天、ページ閲覧不可の状態を長引かせるわけにもいかずで焦る、とイヤな感じがしますよね。

<img width="30%" alt="NEOKI" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294946/14eb8502-fb7f-87b1-ab60-a60daacb07da.png">&nbsp;&nbsp;<img width="23.5%" alt="NEOKI" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294946/ddf539d1-636c-2223-d1d6-08beed709c84.png">&nbsp;&nbsp;<img width="21%" alt="NEOKI" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294946/0066a35b-59be-01b4-fb85-96fe945c3137.png">


本来は DB接続エラーにつながる原因を排除していきトラブル可能性を低くしてことがが王道だと思いますが、それをしたからといってトラブルは起こるときは起こるものです。

とりあえずHP(WP)を復旧させたい、「急ぐ、どうしよう」と思うわけです。

復旧させるにはサーバーにログインしてコンソールで`SUDO`でDBサービス再起動させるのが本来の手段ですが(予想外の理由でmysqlが落ちている、とりあえず起動だけさせておきたい！の前提)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294946/2f4a7ca8-389b-42f6-18dc-7f53a1f7b689.png)

自宅にいるときなどパスワード(機密情報)を入手できずであせったりしますね。しかも作業を頼めるようなLinuxに詳しい人にも連絡つかないしの状態。これから仕事場に向かわないといけないの？いつもなら職場にVPN接続してなんとかするけど今は自宅にパソコンがないようぅぅぅ（泣）

サーバーログインをしないで気軽にDBサービス再起動できたらいいな、Linuxコマンド不慣れな人へも「DB再起動ぐらい」は頼める状態にあってほしい！が切実な願いなのです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294946/712b1d62-94a5-333f-6b87-4e00640e6dab.png)



# 思い

ブラウザで特定のURLをたたくだけで、DBサービス再起動させたい、つまりブラウザ経由で`systemctl restart mariadb`を実行したい。ブラウザでたたくだけだから誰でもできるよね（お休みのときは他の人に任せてゆっくりと過ごしたい）。

# 今回のサーバー構成

- SakuraVPS
- OS: Almalinux 8.6 (Sky Tiger)
- WEBサーバ: Apache/2.4.37 (AlmaLinux)
- PHP: PHP 8.1.6 (cli) (built: May 11 2022 01:14:18)
- DB: mysql  Ver 15.1 Distrib 10.3.32-MariaDB

# 考慮するべき点

APACHEを通してシェルコマンドを実行することはできるのですが、そのときのシェルコマンドの実行ユーザは APACHEユーザになります。今回の場合、APACHEユーザは `apache` と設定されていました。`ubuntu系`OSでよく見ますが`www-data`の場合もあります。APACHEユーザは設定により異なりますので念のために事前確認しておきましょう。

ここで問題となることがあります。APACHEユーザにはROOT権限がありませんしSUDOもできません（標準ではのお話しですが）。

そのため（そのままでは）APACHEユーザではROOT権限を必要とする『システム管理』コマンドなどを実行することができません。

ではどうしましょうか？

#### APACHEユーザを `root`にする案
APACHEの設定でAPACHEユーザに ROOTを指定する方法です。
危険すぎるので候補として考えたくもないですね。

#### APACHEユーザが普通にSUDOできるようにしてあげる案
visudo で設定を与える方法です。
```
apache    ALL=(ALL:ALL) ALL
```
これでsudoをつけたら何でも実行できてしまいます。
`root`と同じく危険すぎて候補から除外ですね。
それとsudo をすると 実行ユーザのパスワードを求められますが、apacheユーザにはパスワードがないので実行できないかもしれません（試してないからわからない）。

#### 特定の管理CMD限定で SUDOできるようにする案
特定の管理CMD(例： systemctl ) 限定で、sudoできるようにする方法です。
```
apache ALL=(ALL) NOPASSWD: systemctl
```
`NOPASSWORD`をつけているので SUDOをしたときにパスワードを求めてこないですが、WEB経由で`systemctl`を実行できてしまいます。何でもできてしまう状態からは脱しているのですが、`systemctl`という強力なコマンド(システムに与える影響が広すぎるコマンド)を直接に実行できてしまうことが『気持ちわるい』です。
できればこの方法は採用したくないです。

#### 特定のシェルスクリプト限定でSUDOできる案
```
apache ALL=(ALL) NOPASSWD: /home/hoge/fuga.sh
```
この設定をして、`fuga.sh`のなかで、`sudo で 管理CMDを実行する！`を書いてみたらよさそうなのかな？

```
それでは実験で apacheユーザになってみましょう

hoge@pc:~$ sudo su - apache --shell=/bin/bash
apache@pc:~$ 

apacheユーザになった状態で sudo fuga.shを実行してみます
apache@pc:~$ sudo /home/hoge/fuga.sh
```
でもエラーになります。/home/hoge の参照権限が `apache`にはないからです。場所が悪かった。

`apache`ユーザの状態から、ユーザ=`hoge`として /home/hoge/fuga.sh　を実行してみましょう
```
apache@pc:~$ sudo -u hoge /home/hoge/fuga.sh
```
はい。これで、`/home/hoge/fuga.sh` が実行できました(Apacheユーザで SUDO が実行されました)。

# apache→hoge→SUDO実行

`hoge`ユーザで SUDOをすると、パスワードを求められます。
```:/home/hoge/fuga.sh
#!/bin/bash
sudo systemctl restart mariadb
```
WEBページ経由で fuga.shを起動しているのですが、パスワードを自動的に付与してみましょう。SUDOにオプション`-S`をつけてみます。

```:/home/hoge/fuga.sh
#!/bin/bash
echo "PASSWORD" | sudo -S systemctl restart mariadb
```

スクリプトのなかにパスワードを書くことにためらいがあるのであれば、こうしましょう
```:/home/hoge/passCode.txt
PASSWORD
```

```:/home/hoge/fuga.sh
#!/bin/bash
cat /home/hoge/passCode.txt | sudo -S systemctl restart mariadb
```

パスワード文字列とスクリプトを分離できました。多分、気休程度ですけどこれがよさそう。


# まとめ

## PHPスクリプト

```:/var/www/html/dbRestarter.php
<?php
exec('/var/www/kick.sh'); // USER( apache )
```
PHPスクリプトのなかに USER=`hoge`を書くことに抵抗があるので、kick.shをはさみます。`kick.sh` は apacheユーザに参照権限があるところにおきましょう。

## kick.sh
ここでユーザ`hoge`に化かします。
```:/var/www/kick.sh
#!/bin/bash
sudo -u hoge /home/hoge/fuga.sh     # USER = apache ⇒ hoge
```
## fuga.sh ( 目的である systemctlを実行する )
```:/home/hoge/fuga.sh
#!/bin/bash
cat ~/passCode.txt | sudo -S systemctl restart mariadb   # USER(hoge)
```
## passCode.txt
```:/home/hoge/passCode.txt
PASSWORD
```

# 最後に

念の為にいっておきますが、WEBブラウザからApache経由でDB再起動はすこし危険です。
危険を承知のうえで「もしものときに備えたい」気持ちが強いのであれば「メリットとリスク」をよく考えてみてから採用してみてください。会社のサーバー運用体制陣充実しているのであれば本ページで説明した方法を検討する必要性は全くありませんからね。

ブラウザでたたくURLは特定の人だけで共有するべきです。社内の誰でも勝手にいつでもDB再起動ができる状態というのは「ゼッタイニダメ」です。社外に公開するのは論外です。

キャッシュされたURLで「間違えてたたいた」も回避したいですね。
例えば、GETパラメータで 現在「日時分」を与えて、「現在日時分」でないときは無視するように、PHPスクリプトを工夫したらよいでしょう。

```: ?code=2404211320 
<?php
$params = $_GET['code']; // 2404211320 が入っている
if( isset($params) ) {
    if( $params == date('ymdHi') ) {
        exec("/var/www/kick.sh");
    }
}
```