相棒の気持ちを理解することで、トラブルを作れないエンジニアになろう（手順書から始めるトラブル防止）

この記事は [NTTコムウェア Advent Calendar 2023](https://qiita.com/advent-calendar/2023/nttcomware) 12日目の記事です。


エンジニアの方が手順書をつくり、それをもとに別の人が環境を構築する場面があります。
その中には、実行する人やインフラの気持ちを理解せず、わざわざトラブルの原因を混ぜ込む手順になっているものも多く見受けられます。

コマンドやコードを実行する側の気持ちや設計思想を汲むことによって避けられるトラブルがあります。
本記事では事例と解決方法をいくつか挙げます。
人やインフラの気持ちを理解していただき、トラブルの原因を作れないエンジニアが増えてくれればと願っています。


# 本記事を書いた人について
普段は社内の開発プロジェクト向けに Microsoft Azure や AWS の技術支援を提供しています。
以前は OS やコンパイラを作ったり Apache Spark を使った分散処理基盤の技術検証をしたりしていました。

プライベートでは最近また 98 を引っ張りだしてきてリアルモード縛りのプログラミングで遊んでいます。
うちの 98 は自身が小学生の時に買ってもらった PC-9821Ce2。メモリを 13 MB（オンボード 5 MB に加えて拡張メモリ 4 MB を 2 枚）も積んでいます。
CPU は QFP の 486SX なので下駄を履くことはできませんが、メモリが沢山あるので DOS エクステンダだって快適に動きます。
電源ユニットのコンデンサを交換するなど自分でハードウェアを保守しながら大事にしています。


# 環境に関する前提条件
本記事で紹介しているツール群と設定例は以下の環境で確認したものを掲載しています。
記事中では Linux での考え方を紹介していますが、Windows 等でもツールが異なるのみで考え方は通用します。

* Debian/GNU Linux 12(Bookworm) 
* tar (GNU tar) 1.34
* xz (XZ Utils) 5.4.1
* liblzma 5.4.1
* sudo 1.9.13p3
    * Sudoers policy plugin version 1.9.13p
    * Sudoers file grammar version 50
    * Sudoers I/O plugin version 1.9.13p3
    * Sudoers audit plugin version 1.9.13p3
* GCC 13.2.0


# 取り上げる事例
挙げ始めると枚挙に暇が無いので本記事では 2 つ例を紹介します。
気が向いたら AdventCalendar ではないところで書いていこうと思っています。

* case 1： 余計なものを見せないことを徹底する
* case 2： 人がセキュリティホールになることを防ぐ

# case 1： 余計なものを見せないことを徹底する
触れる情報量が増えれば増えるほど、作業を行う人の負担が増えて疲労がたまり、トラブルを誘発します。

それに CO2 排出量削減、グリーン IT と叫んでおきながら計算機やネットワークに余計な仕事をさせて消費電力を増やしている場面を多く見ています。


## 1.1 そのログ、見たいですか？ 見せる必要ありますか？
システムを操作する上で確認しなければならないことは、以下の 2 点です。

* 何をする（した）のか
* 結果どうなったか

とりわけ、事前に検証も済んでいて正常に終わるであろう手順が仕上がっている場合は、作業の際に触れる情報量（入力・出力とも）は極力少なくすべきです。

そこでみんな大好き verbose オプション。verbose = 多弁・冗長。
tarball を展開するときに `tar xvf ～` と打たせていませんか？

```console
$ tar xvf gcc-13.2.0.tar.xz
gcc-13.2.0/
gcc-13.2.0/libbacktrace/
gcc-13.2.0/libbacktrace/Makefile.am
gcc-13.2.0/libbacktrace/allocfail.c
gcc-13.2.0/libbacktrace/stest.c
...
...
gcc-13.2.0/gotools/aclocal.m4
gcc-13.2.0/gotools/configure.ac
gcc-13.2.0/gotools/ChangeLog
```

## 1.2 verbose を捨てることの効果と、捨てる代わりにやっておいた方が良いこと
主に疲労の低減です。
後述しますが、出力される情報の量が絞られるのでコマンドの進捗が分からず不安になるかもしれません。
それに備えてコマンドの実行完了までどの程度の時間を要するのかを事前に調べておき、伝えておくと親切ですね。

* 効果
    * 作業者の負荷低減による作業ミスを低減できる
    * マシンやネットワークの負荷を低減できる
    * 作業時間を短縮できる
* 代わりにやっておいた方がよいこと
    * そのコマンドが完了するまでの、おおよその所要時間を調べ明示しておく

## 1.3 tarball 展開の時に verbose オプションを付けることによる影響
例えば [GCC](https://gcc.gnu.org/) 13.2.0 の tarball を展開する場合は

* 機器類への影響
    * 129,835 行、7,368,246 文字がターミナルに流れる（実際にはターミナル制御文字も混じるのでもっと多くなる）
    * リモートのマシンに SSH 等で繋いでいるのであれば、8 MB ほど余計な通信が発生する
        * 2HD DOS/V フォーマット 1.44 MB のフロッピーディスク 6 枚分にもなる
        * スーパーファミコンの FINAL FANTASY VI のカセットですら 3 MB らしい（箱の裏に 24 メガビットと書いてある）
        * V.90 や ISDN なら必死で削る量
        * 音響カプラなら発狂するレベル
    * 手元のターミナルエミュレータに文字を描画するために、エミュレータを実行している端末の CPU やメモリや GPU を動かすための電力も消費する
* 作業者への影響
    * 展開されるファイルのリストが豪速で流れ、疲労が蓄積する
        * 仕事している感は出せるでしょうが、そこではないところで頑張りたい
    * プロンプトに戻るまで時間がかかるため時間を浪費する
    * 自分が叩いたコマンドが見えなくなり、誤りに気付きにくい
        * 特に商用環境の作業では手順に書かれたコマンド以外は打ってはならない。直前に実行したコマンドを確認するために「Ctrl-p」や「↑」キーを押すことさえ許されない

なお、実行時間の差は以下の通りです。`real` の行が全体の実行時間です。

```console
$ time tar xf gcc-13.2.0.tar.xz

real    0m6.275s
user    0m4.325s
sys     0m3.114s

$ time tar xvf gcc-13.2.0.tar.xz
...(snip)...

real    0m11.434s
user    0m5.441s
sys     0m8.555s
```
ここでは実行時間の詳細な内訳には触れませんが時間差のほとんどは標準出力への書き込みです。
ターミナルに余計な情報が流れない分、実行時間は約半分です。

## 1.4 沈黙は金、雄弁は銀： verbose オプションはいらない
読みもしない文字列を画面に沢山流しても成果は上がりません。
そればかりか、スクロールバッファが流れてしまって直前に打ったコマンドを確認できず不安を駆ってしまいます。

こうしましょう。

```console
$ tar xf gcc-13.2.0.tar.xz
$                             ← 正常に終われば何も表示されずにプロンプトに戻ってくる
```

正常に終わったかをどうしても目で見たければ、コマンドの戻り値を確認しましょう。
実行直後に直前のコマンドの戻り値を保持する環境変数を調べれば分かります。
`tar` コマンドであれば、`0` であれば正常に終わっています。

```console
$ tar xf gcc-13.2.0.tar.xz
$ echo ${?}
0
```

コマンドの戻り値と意味は man ページに書いてあります。`tar` コマンドならこのような感じです。

```console
$ man tar
...(snip)...
RETURN VALUE
       Tar exit code indicates whether it was able to successfully perform the requested operation, and if not, what kind of error occurred.

       0      Successful termination.

       1      Some files differ.  If tar was invoked with the --compare (--diff, -d) command line option, this means that some files in the ar‐
              chive  differ  from  their disk counterparts.  If tar was given one of the --create, --append or --update options, this exit code
              means that some files were changed while being archived and so the resulting archive does not contain the exact copy of the  file
              set.

       2      Fatal error.  This means that some fatal, unrecoverable error occurred.
...(snip)...
```

## 1.5 Small is Beautiful
`tar` コマンドに限らず、UNIX ライクなシステムで使うことを考えられたコマンド類は可能な限り寡黙に作られています。
「UNIX という考え方」（Mike Gancarz 著、芳尾 桂 監訳、オーム社）という名著に、いくつかの定理や考え方が書かれています。
今回の verbose の例では、「過度の対話的インタフェースを避ける」ですね。

https://www.ohmsha.co.jp/book/9784274064067/

----

# case 2： 最小の権限を最短時間だけ許容する
手順書でこんなもの見たことはないでしょうか。

```console
root ユーザに昇格する

$ su -
```

亜種

```console
root ユーザでシェルを起動する

$ sudo bash
```

**このような手順を作った方や違和感なく実行に移せる方には今すぐ現場から退場いただき出禁にしましょう。その人がセキュリティホールです。**

システムを構築・運用する人は、このような極端な権限を持ち続けたい訳ではありません。
操作に必要なコマンドを実行できれば満足であり、不要な強力な権限を持たされた上で問題が起きたときの責任を負いたくありません。これが実際に操作をする人の気持ちです。

特権が必要な作業を要する際は、以下を事細かに実施すべきです。

1. ユーザに実行させたい操作を考える
1. そのために必要最小限の権限を調べる
1. ユーザに実行させたい操作に必要な最小限の権限を設定する

:::note info
ここ 25 年ほど、root で実行しなければならないコマンドを叩く機会は数え切れない程あったものの、root セッションを使わなければならないような事態にはあまり遭遇したことがありません。
Slackware・Gentoo あたりはインストーラ時にブートディスクから立ち上げると自動で root のシェルが上がってくるほか、トラブルシュートのためにシングルモードで上げた時くらいでしょうか。
最近では Ubuntu は root セッションを使うことすら想定していないようです。（一般ユーザからの sudo が基本であり、インストール中に root のパスワードを設定しない）
:::


## 2.1 sudo はそのまま使ってはならない

「分かってるよ sudo でしょ？」

そうです。改めて、正しい知識をここで身につけて帰りましょう。


## 2.2 sudo を適切に運用することの効果
`sudo` は正しく使うことで作業者の生産性やセキュリティレベルを高めることができる、非常に有用なツールです。だからこそ 1980 年の初版リリースから 40 年以上経過した今でもメンテナンスされ続け、使われ続けているのです。

* 作業者の実行ミスを防ぐことができる（ミスへの恐怖心の低減）
* 不要な権限を渡さないことでシステムのセキュリティレベルを向上できる
* 操作の目的と必要な権限を理解できるので適度な緊張感で効率よく作業できる

### 2.2.1 毎回パスワードを打たせる

毎回パスワードを打たせるなんて効率悪いと思われるかもしれませんが、毎回面倒な手順を踏ませることに意味があるのです。
もし間違ったコマンドラインを打ち込んでしまった場合に、実行するまでの間に Ctrl-c を押せる猶予ができます。異常に気づける機会が多い方が作業をする者は安心できるでしょう。

`/etc/sudoers` で `NOPASSWD` の設定をしていない限り、大体の Linux ディストリビューションではデフォルトの 15 分間は再度 sudo する際にパスワードを再入力せずに済むようになっています。
これをやめて、毎回パスワードを打たせるようにします。`/etc/sudoers` に `timestamp_timeout` の設定を入れます。`0` を設定すると毎回パスワードを入力させることになります。

・・・ここで `sudo vi /etc/sudoers` なんて打ってはダメですよ。`sudo visudo` を使ってください。
ツールごとの作法を守るのもエンジニアに必要な資質のひとつです。


```console:/etc/sudoers
Defaults        timestamp_timeout = 0
```

:::note info
`visudo` コマンドはシェル変数 `EDITOR` で指定したエディタで `/etc/sudoers` を開きます。
保存しエディタを終了した際にファイル中に書式ミスがある場合は警告してくれますので、「設定したつもりなのに効いていない」という状況を防ぐことができます。

```console
$ sudo visudo
... （間違った内容で編集・保存しエディタを終了） ...
/etc/sudoers:62:31: unknown defaults entry "timestamp_timeouts"
What now?
```
ここで Ctrl-C すると今回編集した内容を破棄してくれます。
:::


### 2.2.2 sudo できるユーザと、実行できるコマンドを絞る

全ユーザに sudo を許すのではなく、sudo できるユーザと実行を許容するコマンドを定義しましょう。

このように書きます。
`Cmnd_Alias` でコマンドのリストを定義し、ユーザ個別に許可するコマンドリストを指定します。
ホスト条件などを加えると更に良いのですがここでは省略し、`ALL` としています。

```console:/etc/sudoers
...(snip)...

Cmnd_Alias SYS_OPERATIONS = /sbin/shutdown, /sbin/reboot, /bin/systemctl
Cmnd_Alias DISK_OPERATIONS = /usr/sbin/fdisk, /usr/sbin/pvcreate, /usr/sbin/vgcreate, /usr/sbin/lvcreate, ... 
Cmnd_Alias PKGMANAGER = /usr/bin/apt

hoge ALL=(ALL) SYS_OPERATIONS, DISK_OPERATIONS, PKGMANAGER

...(snip)...
```

これでユーザ `hoge` はシステムのシャットダウン・ディスクパーティションに関する操作・`apt` コマンドの実行しかできなくなりました。似た名前の他のコマンドを誤って実行することもできません。`ls` コマンドすら打てません。

```console
$ sudo ls
[sudo] password for hoge:
Sorry, user hoge is not allowed to execute '/usr/bin/ls' as root on demohost.
```

実行を許されていないコマンドが実行されるとログに残ります。

```console
$ journalctl
...(snip)...
Nov 26 11:39:57 demohost sudo[6440]:  hoge : command not allowed ; TTY=pts/2 ; PWD=/home/hoge ; USER=root ; COMMAND=/usr/bin/ls
...(snip)...

```

# 今後に向けて
僕は家族のお気持ちを察し、そろそろジンジャークッキーを焼き、クリスマスに合わせてブッシュドノエルを準備します。
トラブルを作らないエンジニアを目指して。


※ 記載されている会社名、製品名、サービス名は、各社の商標または登録商標です。