著者:コーリー・ミニヤード、モンタビスタ・ソフトウェア
第3章:OpenWRTポリシーの実践
前の2つの章( 第1章と第2章)では、SELinuxとOpenWRTポリシーの基本について説明しました。今回は、ポリシーを実際に使用する方法について詳しく説明します。このポリシーは、多くのものが実行する一般的な操作にヘルパーマクロが用意されているように設計されています。ヘルパーを探す場所を知るには、これらのものがどのように定義され、使用されているかを理解する必要があります。
CILの基本的な使用方法
まず、制限対象となる各プログラム(または関連するプログラム群)はsrc/agentディレクトリで定義されています。例えば、 lxcのルールセットを確認したい場合は、 src/agent/lxc.cilを参照してください。この例では、 newgidmapコマンドとnewuidmapコマンドのポリシーを作成する手順を説明します。
監査ログ
まず、これに関する監査ログを見てみましょう。
type=AVC msg=audit(02/17/21 06:26:07.530:64)<br /> avc: denied { execute } for pid=927 comm=lxc-usernsexec<br /> name=newuidmap dev="rootfs" ino=1167<br /> scontext=u:r:lxc.subj tcontext=u:r:file.execfile<br /> tclass=file permissive=1<br /> ----<br /> type=AVC msg=audit(02/17/21 06:26:07.530:65)<br /> avc: denied { getattr } for pid=927 comm=lxc-usernsexec<br /> path=/usr/bin/newuidmap dev="rootfs" ino=1167<br /> scontext=u:r:lxc.subj tcontext=u:r:file.execfile<br /> tclass=file permissive=1<br /> .<br /> .<br /> .<br /> ----<br /> type=AVC msg=audit(02/17/21 06:26:07.540:75)<br /> avc: denied { open } for pid=929 comm=newuidmap<br /> path=/etc/subuid dev="rootfs" ino=2114<br /> scontext=u:r:lxc.subj tcontext=u:r:file.conffile<br /> tclass=file permissive=1<br /> ----<br /> type=AVC msg=audit(02/17/21 06:26:07.540:76)<br /> avc: denied { getattr } for pid=929 comm=newuidmap<br /> path=/etc/subuid dev="rootfs" ino=2114<br /> scontext=u:r:lxc.subj tcontext=u:r:file.conffile<br /> tclass=file permissive=1
他にもたくさんありますが、ここではこれに焦点を当てましょう。これらには以下が含まれます。
- 拒否されました - これらはあなたが関心を持つログです。他にもログが存在する可能性があります。
- { xxx } これは拒否された特定の操作です。その意味は、拒否された内容によって異なります。ファイルの場合は、open または getattr が該当します。機能の場合は、その機能自体が該当します。
comm - 実行されていたプログラム名。 - path/name/ino = オブジェクトのファイルのパス/名前/inode(多くの場合、ファイルシステムのベースからの相対パス)、またはオブジェクトの名前。これらの情報を持たないオブジェクト(例えば、Unixソケット)には、これらの情報は存在しない場合があります。
- scontext - 操作を実行しようとしているソースコンテキスト。ほとんどの場合、実行中のプログラムのサブジェクトコンテキストです。
- tcontext - ターゲットコンテキスト。操作によっては、ファイルへのアクセス時などのファイルオブジェクトコンテキスト、またはシグナル送信時などのプログラムサブジェクトコンテキストとなる場合があります。
- tclass - これは確認すべき重要なフィールドです。ターゲットのクラスを表します。ターゲットのクラスによって、実行すべき処理が異なります。ディレクトリ (dir) クラスはファイル クラスとは異なります。機能エラーが発生した場合、cap_userns は機能とは異なります。
以上の説明を踏まえて、実際にこの件に関する政策を作成する手順に移りましょう。
初期ポリシーの作成
監査ログで最初にすべきことは、「このプログラムはこのような動作をすべきなのか?」と自問することです。すべてを許可するのではなく、プログラムが本来行うべき動作について考えてください。これは、プログラムを実行するだけでポリシーを自動生成できるAppArmorのようなツールを使う場合と比べて大きな利点です。本来許可すべきでないプログラムのバグを許可してしまう可能性があります。これは非常に重要です。プログラムに問題がある場合は修正してください。それでは、実際にポリシーを作成する作業に進みましょう。
上記の最初の部分からわかるように、プログラム ` lxc-usernsexecはプログラム ` /usr/bin/newuidmapを実行します。これに関連するログは他にもたくさんあります。ファイルを開いて読み取る必要があるためですが、この時点で何が起こっているかはわかっています。`newuidmap` newuidmap lxcのようなものを見ることができるようにしたくありません。設計されたとおりに動作するようにしたいだけです。また、 lxc newuidmapできることを実行できるようにしたくありません。そのため、` src/agent/newidmap.cilに newuidmap 専用のファイルを作成することで、 newuidmap独自のサブジェクト内に制限します。
; Allow transition from sys.subj (more or less unconfined)<br /> ; to newidmap context<br /> (in .sys<br /> (call .newidmap.subj_type_transition (subj)))
; Allow unconfined to access newidmap files<br /> (in .file<br /> (call .newidmap.obj_type_transition_conffile<br /> (unconfined.subj_typeattr)))<br /><br /> (block newidmap<br /> ;;<br /> ;; Contexts<br /> ;;<br /><br /> ; Our configuration files<br /> (filecon<br /> "/etc/sub[gu]id"<br /> file<br /> conffile_file_context)<br /> ; Our executable files<br /> (filecon<br /> "/usr/bin/new[gu]idmap"<br /> file<br /> execfile_file_context)<br /><br /> ;;<br /> ;; Macros<br /> ;;<br /><br /> ; Allow access to our conf files<br /> (macro obj_type_transition_conffile ((type ARG1))<br /> (call .file.conffile_obj_type_transition<br /> (ARG1 conffile file "subgid"))<br /> (call .file.conffile_obj_type_transition<br /> (ARG1 conffile file "subuid")))<br /><br /> ; Allow access to execute our programs<br /> (macro obj_type_transition_execfile ((type ARG1))<br /> (call .file.execfile_obj_type_transition<br /> (ARG1 execfile file "newgidmap"))<br /> (call .file.execfile_obj_type_transition<br /> (ARG1 execfile file "newuidmap")))<br /><br /> ;;<br /> ;; Policy<br /> ;;<br /><br /> ; Create the newidmap types and basic macros<br /> (blockinherit .agent.base_template)<br /><br /> ; Create the types and macros for configuration file access<br /> (blockinherit .file.conf.obj_template)<br /><br /> ; Give ourselves read access to our configuration files.<br /> (call read_conffile_files (subj))<br /> )
最初の数行は、ほとんどのプログラムで標準的なものです。` in ` は、それぞれ sys ブロックと files ブロックに項目を追加することを意味します。まるでそれらを追記しているようなものです。filecon filecon使用して新しいオブジェクト コンテキストで項目を作成する場合、ファイルが作成されるときに適切なラベルが付けられるように、これらの ` (in .file) ` 行のいずれかを実行する必要があります。
` filecon行は、 newuidmap (および、この処理の一部として制約も行うnewgidmap )で使用されるファイルのファイルコンテキストを設定します。また、実行可能ファイルのコンテキストも設定します。この実行可能ファイルは、新しいサブジェクト(プロセス)コンテキストを設定するために使用します。ファイル名が正規表現になっていることに注意してください。
` (blockinherit .agent.base_template) ` は、subject typeattributes のセットにnewidmap.subj追加し、その他いくつかの要素を追加します。このテンプレートは、先ほど使用したサブジェクトタイプ ` newidmap.subjと実行可能コンテキスト ` execfile_file_contextを作成します。このテンプレートが行うすべての処理を確認したい場合は、` src/agent.cilの ` agentブロックと ` base_templateブロックを参照してください。
` (blockinherit .file.conf.obj_template) ` チャンクはnewidmap.conffile newidmap.conffile_file_contextを作成し、そのタイプを ` fileおよび `conffile` タイプ属性に追加し、サブジェクトがさまざまな方法でこれらの設定ファイルにアクセスできるようにするマクロを作成します。タイプ属性の追加は、クラスにタイプを追加するものであり、この場合は ` conffileおよび `file` タイプ属性に追加されます。
最後に、` (call read_conffile_files (subj)) ` というコマンドを実行すると、対象ユーザーがファイルにアクセスできるようになります。ただし、これはデフォルトでは有効になっていません。
` src/agent/lxc.cilに、newidmap プログラムにアクセスできるようにするためのコードをいくつか追加する必要があります。
; We can call newxidmap executables.<br /> (call .newidmap.subj_type_transition (subj))
これは、 newidmapで作成したマクロを使用して、 newidmapサブジェクトへの遷移とファイルの実行を可能にします。次に、ポリシーをビルドしてインストールします。
監査ファイルでnewuidmapを検索すると、実行時の問題やnewuidmap設定ファイルにアクセスする際の問題がすべて解消されていることがわかります。ただし、まだ問題が残っています(項目をまとめるために順序を少し変更しています)。
type=AVC msg=audit(02/17/21 19:42:49.570:80)<br /> avc: denied { use } for pid=892 comm=newuidmap<br /> path=pipe:[2178] dev="pipefs" ino=2178<br /> scontext=u:r:newidmap.subj tcontext=u:r:lxc.subj<br /> tclass=fd permissive=1<br /> ----<br /> type=AVC msg=audit(02/17/21 19:42:49.570:81)<br /> avc: denied { write } for pid=892 comm=newuidmap<br /> path=pipe:[2178] dev="pipefs" ino=2178<br /> scontext=u:r:newidmap.subj tcontext=u:r:lxc.subj<br /> tclass=fifo_file permissive=1
これらは、 lxc newidmapの出力を取得できるようにパイプを開いたことに関係しています。tcontext tcontext lxc.subjであることから、それがlxcに属していることがわかります。ログの別の箇所では、 readが必要な別のパイプも渡されていたため、 readアクセスも必要ですlxc.cilでこれへのアクセスを許可する必要があります。
; We pass a pipe to newxidmap, let it have access<br /> (in .newidmap<br /> (call .lxc.readwriteinherited_fifo_file (subj))<br /> )
それでは、 to /proc移りましょう。
type=AVC msg=audit(02/17/21 19:42:49.580:85)<br /> avc: denied { read } for pid=892 comm=newuidmap<br /> name=891 dev="proc" ino=2179<br /> scontext=u:r:newidmap.subj tcontext=u:r:lxc.subj<br /> tclass=dir permissive=1<br /> ----<br /> type=AVC msg=audit(02/17/21 19:42:49.580:86)<br /> avc: denied { open } for pid=892 comm=newuidmap<br /> path=/proc/891 dev="proc" ino=2179<br /> scontext=u:r:newidmap.subj tcontext=u:r:lxc.subj<br /> tclass=dir permissive=1<br /> ----<br /> type=AVC msg=audit(02/17/21 19:42:49.580:94)<br /> avc: denied { write } for pid=892 comm=newuidmap<br /> name=uid_map dev="proc" ino=2183<br /> scontext=u:r:newidmap.subj tcontext=u:r:lxc.subj<br /> tclass=file permissive=1<br /> ----<br /> type=AVC msg=audit(02/17/21 19:42:49.580:93)<br /> avc: denied { getattr } for pid=892 comm=newuidmap<br /> path=/proc/891 dev="proc" ino=2179<br /> scontext=u:r:newidmap.subj tcontext=u:r:lxc.subj<br /> tclass=dir permissive=1<br /> ----<br /> type=AVC msg=audit(02/17/21 19:42:49.580:95)<br /> avc: denied { open } for pid=892 comm=newuidmap<br /> path=/proc/891/uid_map dev="proc" ino=2183<br /> scontext=u:r:newidmap.subj tcontext=u:r:lxc.subj<br /> tclass=file permissive=1
これは、 lxc uid マップを設定するように要求しているプログラムです。これはセキュリティ上のジレンマを引き起こします。newuidmap プログラムが ` /proc内の ` uid_mapファイルに書き込むようにしていますが、これはおそらくそのプログラムの役割です。しかし、これはlxcプログラムに対して行っているため、newidmap はlxcファイルに対してこれを行う権限を持つことになります。
` lxc.subjコンテキストでnewuidmapプログラムを実行させることは可能です。しかし、その場合、` lxc.subjに ` newidmapファイルや、` newuidmapが必要とするその他のものへのアクセス権を与える必要があります。現在の分析では、コンテナ内部にアクセスできる ` lxc.subjとして外部のプログラムを実行しないことが重要であるとされています。コンテナ内部は保護すべき最も重要な部分だからです。したがって、` newidmap.cilファイルの作成を進めますが、これが最善の策ではない可能性があり、このアプリケーションには適していても、一般的には適さない可能性があります。
そのため、 lxc.cilでこれへのアクセスを許可する必要があります。newidmap newidmapそれがどこから来ているのかを知りません。アクセスを許可するには、 lxcブロックに何かを追加する必要があります。標準の procfile マクロは使用できません。なぜなら、それらは標準のファイル コンテキストlxc.fsに対して機能し、この場合は実行可能コンテキストlxc.subjに対して機能しないからです。
; This allows us to pass proc object to newidmap because the /proc/nnn<br /> ; files for things I execute will be owned by lxc.subj.<br /> (macro list_procsubj_dirs ((type ARG1))<br /> (allow ARG1 subj list_dir))<br /> (macro readwrite_procsubj_files ((type ARG1))<br /> (allow ARG1 subj readwrite_file))
そして今度は、 lxc.cilでnewidmapアクセス権があることを伝えます。
; Let newxidmap access our /proc/nnn files to set the uid map<br /> (in .newidmap<br /> (call .lxc.list_procsubj_dirs (subj))<br /> (call .lxc.readwrite_procsubj_files (subj))<br /> )
最後に、 /proc自体が procfile なので、 newidmap.cilでそれへのアクセス権を与えます。
; Allow access to list files in /proc<br /> (call .fs.list_procfile_dirs (subj))
残りはnewidmap.cilへの変更です。
type=AVC msg=audit(02/17/21 19:42:49.580:87)<br /> avc: denied { create } for pid=892 comm=newuidmap<br /> scontext=u:r:newidmap.subj tcontext=u:r:newidmap.subj<br /> tclass=unix_stream_socket permissive=1<br /> ----<br /> type=AVC msg=audit(02/17/21 19:42:49.580:88)<br /> avc: denied { connect } for pid=892 comm=newuidmap<br /> scontext=u:r:newidmap.subj tcontext=u:r:newidmap.subj<br /> tclass=unix_stream_socket permissive=1
Unixソケットへのアクセスを許可する必要があります。
; Allow us to create and connect to a unix socket<br /> (allow subj self create_unix_stream_socket)
さらに、以下の問題も挙げられます。
type=AVC msg=audit(02/17/21 19:42:49.580:89)<br /> avc: denied { search } for pid=892 comm=newuidmap<br /> name=run dev="rootfs" ino=1263<br /> scontext=u:r:newidmap.subj tcontext=u:r:varfile.runtimevarfile<br /> tclass=dir permissive=1
これは少し厄介です。しかし、` runtimevarfileから、これが/varにあり、名前が run であることがわかります。つまり/var/runを検索しようとしているということです。簡単ですvarfile.runtimeに検索権限を与えればよいのです。
; Allow search on /var/run<br /> (call var.search_fs_dirs (subj))
さらに続きがあります。
type=AVC msg=audit(02/17/21 19:42:49.580:90)<br /> avc: denied { read } for pid=892 comm=newuidmap<br /> name=passwd dev="rootfs" ino=1239<br /> scontext=u:r:newidmap.subj tcontext=u:r:nameservice.miscfile<br /> tclass=file permissive=1<br /> ----<br /> type=AVC msg=audit(02/17/21 19:42:49.580:91)<br /> avc: denied { open } for pid=892 comm=newuidmap<br /> path=/etc/passwd dev="rootfs" ino=1239<br /> scontext=u:r:newidmap.subj tcontext=u:r:nameservice.miscfile<br /> tclass=file permissive=1<br /> ----<br /> type=AVC msg=audit(02/17/21 19:42:49.580:92)<br /> avc: denied { getattr } for pid=892 comm=newuidmap<br /> path=/etc/passwd dev="rootfs" ino=1239<br /> scontext=u:r:newidmap.subj tcontext=u:r:nameservice.miscfile<br /> tclass=file permissive=1
newuidmap /etc/passwdから読み込む必要があります。他の処理でも同様のことが行われているため、おそらく既にこの処理のためのマクロが存在するでしょう。調べてみると、` file/miscfile/nameservicesmiscfile.cilの ` nameserviceブロック内にいくつかのマクロが見つかるはずです。これを使用できます。
; Allow access to /etc/passwd<br /> (call nameservice.read_miscfile_files (subj))
そして、いくつかの機能上の問題点:
type=AVC msg=audit(02/17/21 19:42:49.580:96)<br /> avc: denied { sys_admin } for pid=892 comm=newuidmap<br /> capability=sys_admin<br /> scontext=u:r:newidmap.subj tcontext=u:r:newidmap.subj<br /> tclass=cap_userns permissive=1<br /> ----<br /> type=AVC msg=audit(02/17/21 19:42:49.580:97)<br /> avc: denied { setuid } for pid=892 comm=newuidmap capability=setuid<br /> scontext=u:r:newidmap.subj tcontext=u:r:newidmap.subj<br /> tclass=capability permissive=1<br /> ----<br /> type=AVC msg=audit(02/17/21 19:42:49.590:98)<br /> avc: denied { setgid } for pid=893 comm=newgidmap capability=setgid<br /> scontext=u:r:newidmap.subj tcontext=u:r:newidmap.subj<br /> tclass=capability permissive=1
これらは基本的な許可だけで済みます。
; Capabilities we need to set the uids and guids<br /> (allow subj self (cap_userns (sys_admin)))<br /> (allow subj self (capability (setuid setgid)))
というわけで、私たちはこれらすべての手順を踏み、すべてが正常に動作することを確認し、もう一度確認します。
type=AVC msg=audit(02/17/21 22:58:46.460:139)<br /> avc: denied { read write } for pid=929 comm=newuidmap<br /> path=socket:[2241] dev="sockfs" ino=2241<br /> scontext=u:r:newidmap.subj tcontext=u:r:lxc.subj<br /> tclass=unix_stream_socket permissive=1
これはファイルのさらに下の方にあり、おそらく別のnewuidmapの呼び出しによるものです。何らかの理由で、 lxcから Unix ストリーム ソケットを継承しています。これはおそらくlxcのバグで、exec の前に fork でソケットを閉じていないためですが、警告を抑制することができます。newuidmap newuidmapこれを使用しない場合は問題ありません。` in .newidmap ` に以下を追加してください。
; This appears to be due to a bug in lxc, it's not closing a socket<br /> ; that gets passed when newxidmap is called. Suppress the log<br /> ; as we know about this.<br /> (dontaudit subj .lxc.subj readwrite_unix_stream_socket)
これに対応するマクロはないので、手動でコーディングする必要があります。
そんなに簡単じゃない
私がこれを簡単にやっているように見せていますが、実際はそうではありません。私はこれに丸一日費やしました。その後、OpenWRTのメンテナーがこれを検証し、私が犯したあらゆる間違いを指摘してくれました。セキュリティを理解する必要があります。掘り下げて、物事を調べ、理解しなければなりません。物事はしばしば予想通りには動作せず、試行錯誤する必要があります。コンパイラのメッセージはもっと役に立つはずです(とはいえ、以前のポリシーコンパイラよりははるかに優れています)。少しずつ作業を進め、再コンパイルして、何が問題を引き起こしたのかを把握するのが最善です。
各パーツとその仕組み
ここでほとんどの処理を行う基本的なメカニズムは、` macro 、` call 、` block操作です。さらに、` inと` blockinherit操作もあります。
これらの関数は位置に依存しないことに注意してください。ファイル内で使用する前にマクロを宣言する必要はありません。一般的に、順序は関係ありません。ただし、マクロをオーバーライドする場合など、例外的なケースもあります。
マクロと呼び出し
` macro `は、C言語のマクロと同様に、期待どおりに動作します。マクロ内のコードは、引数を置き換えた上で、` call文の場所に挿入されます。非常に分かりやすい仕組みです。
ブロックとブロック継承
ブロックは名前空間を作成し、その名前空間内での操作を可能にします。この名前空間は、コンテキストに応じて複数の機能を実行します。
名前空間ブロック
上記の ` newidmapブロックのような一部のブロックは、サブジェクトとオブジェクトのコンテキストを定義するための名前空間を作成するために使用されます。`subj` subj頻繁に使用されていることに気づくでしょう。これは、
(blockinherit agent.base_template)
そして、これはブロック内の主題コンテキストです。この特定のブロックinhertは非常に多くの処理を実行しており、これが私たちがOpenWrtポリシーを起点とした理由の1つです。そうでなければ、膨大な量のコードを記述する必要があったでしょう。`src src/agent.cilを参照すれば、そのすべての処理を確認できます。
次のようなことをすると:
(blockinherit .file.conf.obj_template)
` fileブロック内の ` confブロック内にある ` obj_templateブロックを取り込んでいます。これにより、ブロック名に基づいて ` newidmapオブジェクトが作成されます。この例では ` newidmap.conffile 。上記のステートメントは、ブロックの名前空間に ` conffile_file_contextを作成します。ブロックで定義されたマクロは、作成されたすべての型とともに、そのブロック名で使用できます。たとえば、newidmap conffile コンテキストを参照する必要がある場合は、` .newidmap.conffile_file_contextを使用できます。
テンプレートブロック
これらは ` blockinheritで使用されるブロックで、基本的には挿入されるテンプレートであり、現在のブロック名を使用して挿入場所に合わせてカスタマイズされます。これはほぼ直接挿入です。例えば、次のようなブロックがある場合です。
(block asdf<br /> (blockabstract asdf)<br /> (macro1 m1 ....)<br /> (macro2 m2 ....))
そして、私はそれを別のブロックで使用します。
(block jkl<br /> (blockinherit asdf))
それはまるで次のようである。
(block jkl<br /> (macro1 m1 ....)<br /> (macro2 m2 ....))
` blockabstractステートメントは、ブロックがblockinherited目的として設計されていることを宣言します。ブロックから直接コードを生成することはありません。
で
in文は既存のブロックに追加します。例えば、以下のような場合です。
(in .newidmap<br /> (call .lxc.readwriteinherited_fifo_file (subj))<br /> )
` lxc.cilファイル内で記述します。これは、` newidmap.cilを使用するたびにそのファイルに追加するのは非効率的だからです。代わりに、使用するファイルに直接追加すれば、そのファイルは自分がそれを使用していることを認識できます。
` in ` ステートメントは、内容をそのままブロックに挿入します。つまり、` subj ` はターゲットブロック内のサブジェクトとなり、使いやすくなります。
次回のブログ記事もお楽しみに!
MVSecureサービスとMVXpertサービスの詳細については、こちらをご覧ください。
Please reach out to discuss your particular scenario today.