2016-09-19 (Mon) [長年日記]

_ [obsd]doas mastery和訳

あまり見直していないけど、こんなものかな。自分用の覚え書きということで。

doas を世に出して一年になりますので、一冊本を書く時期ですな。あるいは薄い本程度でもいいかもしれませんが。

UNIX システムには二種類のユーザがいます。スーパーユーザと一般ユーザです。スーパーユーザはスーパーですが、あとは誰もスーパーじゃありません。これほど権力が独占されていると、物事は単純になりますが、権力が過剰に付与されることもよく起こります。たいていは一つの作業をするためにだけスーパーユーザの権力が必要になるものです。常日頃それほどの権力を持っていたいとは思いませんね。そこに付随する責任を考えてみてください! そこで doas は、sudo コマンドと同様、スーパーユーザの特権を小分けにして、特定の作業のためだけに付与するものです。

doas コマンド自体にも少しばかりのオプションがあり、それも後のほうで扱いますが、いちばん面白いのは設定ファイルです。ここがキモなんです。

doas.conf

可能な限りシンプルな doas.conf ファイルは実にまったくシンプルです。まばたきすると見逃しますよ。いいですか。





この空ファイルは何もしませんが、それのおかげで我々はデフォルトのルールセットを説明できます。すなわち、そんなものはないということです。doas は DENY 状態からルール評価を始めるのです。どのルールにもマッチしなければ、その動作は拒否されます。そうです。root でさえ permission denied エラーを食らいます。このファイル自体に関していえば実用性は皆無ですが、これで、システムの doas.conf ファイルを読みさえすれば挙動をすべて完全に理解できることが保証されるわけです。

ただ、もし設定ファイルが存在すらしない場合 doas は、有効でない旨のメッセージを出して終了します。

実用性のある範囲で最もシンプルな設定は、おそらくこんな感じの見た目になるでしょう。

permit :wheel

これは wheel グループ内のあらゆるユーザが、あらゆるコマンドを root として実行できるようにします。おおまかに言えば su コマンドと同じようなことです。もっと似せるには、root がパスワードなしでコマンドを実行できるようにしたいですね。

permit :wheel
permit nopass keepenv root

ここで root のルールを二番目にしたのは、doas が最後のマッチ式にルールを評価するからです。root は wheel グループに属しているので最初のルールがマッチし、それを二番目のルールで上書きする必要があるのです。いつでも一般的なルールから始めて、あとから具体的にしていくようにしてください。

root なのに doas を実行するのはなぜでしょう。それは、権限の少ないユーザに切り換えたいときがあるからです。あるいは手元のスクリプトが、重要な操作で権限を上昇するために doas を使っていて、しかし自分は既に root になっている場合があるからです。

パスワード

デフォルトで doas は、毎度ユーザがコマンドを実行するたびに認証を要求します。通常これはパスワードの入力を意味します。複数のコマンドを実行する場合にはウンザリしてしまうかもしれませんね。これを改変するため doas.conf に追加できるキーワードが二つあります。

nopass キーワードを追加すると、そのユーザは常にパスワード入力なしでコマンドの実行を許可されます。これは上述の root 用ルールにありました。既に root であり、好きなことができるのですから、パスワードを要求する理由はありませんね。

persist キーワードを追加すると、doas はそのユーザが以前に認証したことを記憶し、その後の五分間は確認を求めなくなります。

permit persist :wheel

このルールは、よくある sudo 設定のとおり、wheel のユーザが初めてコマンドを実行するときにパスワードを要求します。

doas の使う認証情報はカーネル内に記録され、そのセッションに紐付けられます。ファイルシステム情報とは異なり、他のユーザからアクセスすることができませんから、偽造が困難です。タイムアウトはコンピュータ時間ではなくリアル時間で起こるので、システム時計を巻き戻しても、時間切れを無かったことにはできません。時間内に再実行すると残り時間はリセットされますが、それはルールが persist な場合だけです。一つのルールが persist と nopass の両方であることもできませんし、nopass ルールが残り時間を増やすこともできません。

一つのマシンに複数ログインしている場合には、それぞれのログインで認証が必要になります。加えて、認証情報には親シェルのプロセス ID が含まれていますから、何らかのシェルスクリプト内で doas を実行するときには再認証が必要ということになります。すなわち、同じことを別の言い方で表現するなら、品質の不確かなスクリプトやプログラムを実行する場合でも、知らないうちに権限を上昇されることはない、ということです。(まあ理論上の話です。実際のところは、このチェックにはかなりゴニョゴニョする余地が残っています。)

permit persist :wheel
permit :wheel cmd reboot

認証チェックが省略されるのは persist の付いたルールだけです。ミスを防ぐため、重要なコマンドは persist なしで再び載せておけば、常にパスワードを要求することができます。

コマンドラインの -L オプションを doas に渡すと、ログアウトしたのと同じように、そのターミナルにおける persist の認証情報を消してくれます。時間切れまで待つ必要はありません。

環境変数

実行するコマンドに情報を渡す方法は二つあります。第一の、明白な方はコマンドラインです。第二の、見えにくい方法は環境変数を経由するものです。ほとんど目に見えないにもかかわらず、環境変数はプログラムの挙動を劇的に左右することがあります。そういうわけで、不測の結果を避けるために doas が何らかのフィルタリングを提供するのは大切なことです。デフォルトでは、ほんのわずかな変数だけが渡されます。

環境変数に関わる設定キーワードが二つあります。keepenv と setenv です。前者はとても単純。前に、root のルールで見たことがあります。環境変数をすべてそのままコマンドに渡すだけです。信頼できるユーザには便利な手抜き方法です。

setenv を使うのはもう少し複雑です。というのも、環境変数の追加、変更、削除が可能だからです。例を見てみましょう。

permit setenv { PKG_PATH ENV=/root/.kshrc PS1=$DOAS_PS1 } zoltan

このルールは zoltan に root としてコマンドを実行できるようにします。PKG_PATH 環境変数は保持されます。ENV という、ksh の設定ファイルを指定する環境変数は、root の所有するものに変更されます。最後に、シェルのプロンプト表示を制御する PS1 の設定を上書きしています。この設定ファイルでは具体的な値を指定せず、DOAS_PS1 の値を使います。これにより、zoltan が root 時のプロンプトを自在に調整できます。

ユーザと権限

doas.conf に書かれた各ルールは、permit や他のオプションの後に指定された個人やグループに適用されます。ユーザ名かグループ名かを識別するキーワードはありません。代わりに chown に似た書式が使われ、ユーザ名ならそのまま書き、グループ名は頭にコロンを付けます (:group)。このあたりで、ルールは最後にマッチしたもの勝ちで評価されるということを思い出しておきましょう。個別ユーザのルールが自動的にグループのルールより優先されるわけではありませんから、個別ユーザは後のほうに書かなければいけません。

ほとんどの状況で doas は root としてコマンドを実行するために使われます。その場合、特別な書式は必要ありません。しかしながら、特定のユーザになるようルールを制限したいということもありえます。

permit nopass zoltan as dbadmin

このルールは zoltan に、パスワード入力なしでデータベース管理者としてコマンドを実行できるよう許可しますが、しかしこれだけでは何も root として実行できません。

コマンド

doas.conf 文法の旅も、終わりが近づいてきました。doas は、ルールが特定のコマンドにしか適用されないように制限することもできます。さらには、特定のコマンドに特定の引数を与えた場合のみにも制限できます。

permit nopass :operator cmd reboot

通常、reboot には root 権限が必要ですが、直接実行されるのではなく、setuid なプログラムである shutdown から実行されます。shutdown を実行できるのは operator グループだけです。上のルールは、このグループのユーザが reboot を直接実行できるようにします。しかしながら operators が root として他のコマンドを実行したりシェルを取得したりすることはできません。

permit zoltan cmd sh args /etc/netstart

こちらでは zoltan に、netstart という (ネットワークインタフェースを設定する) スクリプトの再実行を許可します。他のシェルコマンド実行は許可しません。netstart スクリプトだけです。

この例ではどちらも、cmd にファイル名しか指定しませんでした。このような場合、doas はシステムの PATH (/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin) のコマンドだけに実行を限定します。zoltan が自分のホームディレクトリに sh というバイナリを設置して PATH を ~/bin にしても、我々の裏をかくことはできません。cmd には絶対パスを指定することもできますが、その場合、ユーザは毎回フルで打ち込む必要があります。

コマンド引数は残さず指定しなければなりません。

permit zoltan cmd ifconfig args iwm0 up
permit zoltan cmd ifconfig args iwm0 down

この二つのルールでは、zoltan に wifi インタフェースのオンオフを許可していますが、他のパラメータは一切変更させません。

カーネルから情報を集めるユーティリティの中には、一般ユーザとして実行すると情報の一部しか出してくれないものがあります。フルで出すには root として実行する必要があります。たとえば fstat は、unix ドメインソケットに関して最小限の情報しか表示しません。

tedu     Xorg       94581   16* unix stream 0x0
tedu     Xorg       94581   17* unix stream 0x0
tedu     Xorg       94581   18* unix stream 0x0
tedu     Xorg       94581   19* unix stream 0x0
tedu     Xorg       94581   20* unix stream 0x0

しかし root として実行しなおすと、ずっと多くの情報が得られます。

tedu     Xorg       94581   16* unix stream 0xffff800000b45980 <-> 0xffff800000b3d700
tedu     Xorg       94581   17* unix stream 0xffff800000b3d880 <-> 0xffff800000b45500
tedu     Xorg       94581   18* unix stream 0xffff800000b45480 <-> 0xffff800000b45300
tedu     Xorg       94581   19* unix stream 0xffff800000b45080 <-> 0xffff800000128e80
tedu     Xorg       94581   20* unix stream 0xffff800000b60280 <-> 0xffff800000b60780

こうなっていれば、ソケットを向こう側のプロセスと対応づけることができます。

tedu     xterm      33159    3* unix stream 0xffff800000b45300 <-> 0xffff800000b45480
tedu     Xorg       94581   18* unix stream 0xffff800000b45480 <-> 0xffff800000b45300

これらのカーネルアドレスは、カーネルのメモリ配置について情報を漏らしてしまい、攻撃を容易にしかねないので、通常は隠されています。しかし tedu を信頼している場合 (というか信頼しない人なんていないでしょマジで) なら、簡単な設定ルールで変更できます。

permit nopass tedu cmd fstat args -u tedu

fstat を使うと他のユーザのプロセスが開いているファイルも見えてしまうのですが、ここでは引数を指定して、tedu が接続とプロセスを対応づけられないようにしています。

deny

ここまで見てきた permit ルールとは対照的に、特定のコマンド実行を拒否する deny ルールを作ることも可能です。この機能が真価を発揮するのは、信頼している操作者がうっかり typo してしまうのを防ぐ安全装置としてです。セキュリティ機能としては使わないほうが賢明です。というのも、漏れのないブラックリストを作ると、漏れなく疲労困憊するからです。それよりはまず (訳注: permit を絞って) 意図せず権限を与えることのないようなルールセットを工夫すべきです。

permit :wheel
deny zoltan cmd reboot

zoltan が wheel グループに入っているとすれば、ここで彼はあらゆるコマンドを実行できます。ただし reboot を除きます。zoltan には、少しトリガーハッピーになって、違うターミナルに打ち込む癖があるのかもしれません。このルールセットは実際のところ、zoltan がマシンを再起動させることを防いでなどいません。最初のルールがあれば、doas.conf を編集して次のルールを消すなど、色々やるだけの権限があるのです。全員が仲間だという前提になっています。

doas

doas コマンド自体には、少しばかりのオプションがあります。

ちょうど設定ファイルの文法を見終えたわけですから、新しいファイルをインストールする前に -C オプションを使って文法チェックすることができますね。特定のコマンドと引数について、実際に実行せずにルールセット評価の結果をチェックすることもできます。さきほどの fstat の例を続けて、そのコマンドに引数があるときだけマッチすることをチェックできます。

$ doas -C doas.conf fstat
deny
$ doas -C doas.conf fstat -u tedu
permit nopass

入力を受け付けないようなスクリプトに doas を組み入れるときは、-n オプションを付けると失敗を未然に防ぐ役に立ちます。nopass ルールしか実行せず、パスワードの必要なルールは即座に失敗を返すのです。これには persist なルールも含まれており、直前に認証していようがいまいが関係なく失敗にします。

謝辞

たくさんの OpenBSD 開発者およびユーザの支援なしに doas を完成させることはできなかったでしょう。特に Vadim Zhukov は設定パーザと後退テストスイートに甚大なる貢献をしてくれました; Todd Miller, Damien Miller, および Martijn van Duren は、アイディアやインスピレーションを提供してくれました; Theo de Raadt は追加機能を却下するうえで支えになってくれました; Henning Brauer はターミナルに認証維持を結びつけるアイディアをくれました; そして Michael Lucas には、キャッチーなタイトルをパクったことで借りができました。


«前の日記(2016-04-04 (Mon)) 最新