Unixの権限が許可していても、ポリシーによってサービスが拒否される可能性がある。また、特定のcapabilityを持っているからといって、関連するすべての操作が許可されるわけではない。アクセス制御では、主体、対象、操作、およびポリシーの組み合わせ を一体的に解釈する必要がある。
DACにはowner/group/otherとACLが含まれ、通常はオブジェクトの所有者が権限を付与できる。強制ポリシーは、サービスドメインをさらに制約できる。
参照対象 該当セクション
LSMとラベル/プロファイル 第1~3節
capabilityとBPFポリシー 第4~5節
キーリング、整合性、実行状態 第6~7節
1. LSMはチェックの位置を提供し、モジュールがポリシーを提供する
ファイルアクセスには複数の独立した条件を満たす必要がある
呼び出しとパスのチェック
オブジェクトと操作の有効性
基本権限チェック
DAC / ACL / capability
有効化されたLSMのチェック
ラベル、プロファイルなどのポリシー
ファイル操作の実行
I/Oエラーなどが発生する可能性あり
図は制約関係を示している;実際のカーネルチェックは複数のパスに分散されている
あるレイヤーが許可しても、他のレイヤーを自動的にバイパスするわけではない;LSMはsyscallエントリでのみ動作するわけではない。
LSMフックは、ファイル、資格情報、プロセス、ネットワークなどのカーネルオブジェクトの操作パス内に位置する。1つのシステムコールで複数のチェックがトリガーされる場合があり、カーネル内部の操作もこれらの位置を通ることがある。フックには、判断を返す関数のほか、安全状態の割り当て、解放、または更新を通知するものも含まれる。「すべてのフックがエラーを返せば拒否される」と一概には言えない。
例えば security_file_open() は、戻り値を持つ file_open フックを使用する。これを call_void_hook() として記述し、エラーチェックを主張するのは正しくない。ターゲットバージョンの security/security.c と include/linux/lsm_hook_defs.h を読む方が、古いバージョンのフックリスト構造をコピーするよりも信頼性が高い。
一般的な拒否型の権限フックでは、有効化されたモジュールが定義された組み合わせルールに従ってチェックに参加する。1つのモジュールによる拒否を、別のモジュールの許可で上書きすることはできない。SELinuxとAppArmorがすべてのシステムで固定された並列動作を行うと描くのは誤りである。実際の有効化の組み合わせと順序は、カーネルバージョン、構成、および起動パラメータに依存する。securityfsが見える場合、/sys/kernel/security/lsm に現在アクティブなモジュールがリストされる。
LSMという名前だからといって、それらが自由に insmod/rmmod できる通常のモジュールであるわけではない。フレームワークの概念についてはLSMドキュメント を参照のこと。ここで言及されている歴史的構造体は、現在のソースコードと合わせて読むべきである。
2. SELinux:主体ドメインとオブジェクトタイプ
SELinuxのセキュリティコンテキストには通常、user、role、type、およびレベル/範囲が含まれる。例えば、ファイルラベルの httpd_sys_content_t はオブジェクトタイプである。サービスプロセスは特定のドメインで実行される。
type enforcementの拒否レコードを読む際には、以下の4項目を一致させること。
フィールド 確認すべき事項
source context どの主体ドメインがアクセスを要求したか?
target context オブジェクトは実際にはどのタイプか?
object class ファイル、ディレクトリ、またはその他のオブジェクトか?
permission 拒否されたのは read、search、または他の操作か?
パスだけを見ると、ラベル、ドメイン変換、またはディレクトリのsearch権限を見逃す可能性がある。ルールは、ソースドメインが対象タイプのある種のオブジェクトに対して行う操作の許可を表現している。
AVCはクエリコストを削減するためにアクセス判断をキャッシュする。ラベルのソースには、ファイルシステムの拡張属性や、ポリシーが特定のオブジェクトに対して生成したラベルが含まれる。procfs、tmpfsなどのマーキング方法がすべて genfscon によって生成されると一概には言えない。
Enforcingモードではポリシーが拒否を実行する。permissiveモードでは、本来拒否されるべきアクセスを記録するだけで、SELinuxによる拒否は適用されない。他のDAC、LSM、およびシステムエラーにより操作が失敗する可能性があるため、permissiveであってもすべての操作が成功するわけではない。監査はポリシーとログ設定の影響を受けるため、すべての判断に新しいログエントリが作成されるとは限らない。
3. AppArmorとSmack:異なるポリシー表現
AppArmorはプログラムプロファイルを中核とし、ファイルアクセスルールはパスに基づいて表現されることが多く、capabilityやネットワーク操作などを制限できる。同じUnixユーザー内の異なるプログラムは、異なる制約を受けることができる。
パスベースのポリシーを理解するには、プロセスのマウントビュー、エイリアス、リネーム、およびリンクルールを知る必要がある。しかし、「ハードリンクを作成したりbind mountを行ったりすればAppArmorを回避できる」と結論付けてはならない。リンクとマウント自体もチェックの対象であり、プロファイル、ポリシーネームスペース、および具体的なルールが結果を決定する。以下はルールの構文断片であり、デプロイ可能な完全なサービスプロファイルではない。
/var/www/** r,
/var/log/example/*.log w,
capability net_bind_service,
これには、プログラムの読み込み、ダイナミックライブラリ、ディレクトリアクセス、および完全な実行依存関係が含まれていない。理論的に1つのルールを理解することと、利用可能で十分に収束されたポリシーを持つことは、別の作業である。AppArmorドキュメント を参照。
Smackも主体/オブジェクトラベルとアクセスルールを使用するが、独自のルールと特殊なラベルセマンティクスを持つ。それは「SELinuxから特定の機能の固定パーセントを削除したバージョン」ではない。選択メカニズムは、実際の表現力とデプロイ環境を比較して決定すべきである。Smackドキュメント を参照。
4. Capabilities:権限を分割し、完全なサンドボックスは生成しない
カーネルのcapabilityセマンティクスはスレッド資格情報に基づき、permitted、effective、inheritable、およびboundingおよびambientセットを含む。通常の権限チェックでは、effectiveセット内の関連能力が関与する。exec時にどのように再計算されるかは、ファイルcapability、set-ID、securebitsなどの条件に依存する。
CAP_NET_RAW は、raw/packetソケットなどの権限に関連する。
CAP_SYS_PTRACE は、一部のptrace資格制限を回避するために使用できるが、他のチェックも存在する。
CAP_NET_BIND_SERVICE は保護された低ポートに関連する。ネットワークネームスペースのポート閾値設定も、これを必要とするかどうかに影響する。
CAP_SYS_ADMIN は範囲が非常に広いため、名前を狭い操作として理解すべきではない。
userネームスペース内のcapabilityは、適用されるネームスペースとオブジェクト関係内でのみ有効であり、コンテナ内のrootがホストrootのすべての能力を自動的に持つわけではない。逆に、capabilityを削除しても、通常ユーザーが本来使用できるすべてのインターフェースが削除されるわけではない。詳細はcapabilities(7) を参照。
DAC、capabilityの範囲、LSM、および読み取り専用ファイルシステム条件をそれぞれ分離して考える。以下に示すのは、これらの制約の組み合わせであり、すべてのシステムが固定された関数呼び出し順序に従うことを偽装するものではない。
図を準備しています 再読み込み
1つの許可が、別の拒否を上書きできるか? 適用されるcapabilityは特定のDACバイパス条件を満たすことができるが、LSMの拒否や読み取り専用ファイルシステムを許可に変更することはできない。userネームスペース内のrootも、ホスト内のすべてのオブジェクトへのパスポートではない。
{"id":"access-control-gates","title":"1つの許可が、別の拒否を上書きできるか?","summary":"適用されるcapabilityは特定のDACバイパス条件を満たすことができるが、LSMの拒否や読み取り専用ファイルシステムを許可に変更することはできない。userネームスペース内のrootも、ホスト内のすべてのオブジェクトへのパスポートではない。","height":1000,"html":"<h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p><label class=\"check\"><input type=\"checkbox\" id=\"dac\"><span data-i18n=\"dac\"></span></label><label class=\"check\"><input type=\"checkbox\" id=\"cap\" checked><span data-i18n=\"cap\"></span></label><label class=\"check\"><input type=\"checkbox\" id=\"scopeOK\"><span data-i18n=\"scopeOK\"></span></label><label class=\"check\"><input type=\"checkbox\" id=\"lsm\" checked><span data-i18n=\"lsm\"></span></label><label class=\"check\"><input type=\"checkbox\" id=\"rw\" checked><span data-i18n=\"rw\"></span></label><div class=\"track-row\"><span data-i18n=\"dacGate\"></span><div id=\"dacGate\" class=\"track\"></div></div><div class=\"track-row\"><span data-i18n=\"lsmGate\"></span><div id=\"lsmGate\" class=\"track\"></div></div><div class=\"track-row\"><span data-i18n=\"fsGate\"></span><div id=\"fsGate\" class=\"track\"></div></div><div class=\"formula\" id=\"formula\"></div><div class=\"card\"><header data-i18n=\"result\"></header><div id=\"result\" class=\"readout\"></div></div><div class=\"copy\"><p id=\"explanation\" role=\"status\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"note\"></p></div><details class=\"scope\"><summary data-i18n=\"scopeLabel\"></summary><p data-i18n=\"scope\"></p></details>","css":".intro{margin:8px 0 18px;color:var(--muted);font-size:14px}.caption,.hint{font-size:12px;color:var(--muted);margin-top:12px}.control{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px 12px;align-items:center;margin:14px 0;font-size:13px}.control label{font-size:13px}.control output{color:var(--accent);min-width:5em;text-align:right;font-variant-numeric:tabular-nums}.control input{grid-column:1/-1;width:100%;margin:0}.presets{display:flex;gap:4px;align-items:stretch}.presets button{flex:1 1 0;min-width:0;overflow-wrap:anywhere;font-size:13px}.actions{display:flex;gap:8px;flex-wrap:wrap;margin:14px 0}.actions button{flex:1;min-width:0;font-size:13px;min-height:42px}.copy{display:grid;font-size:14px;margin:16px 0}.copy>*{grid-area:1/1;margin:0}.reserve{visibility:hidden}.scope{margin-top:14px;color:var(--muted);font-size:12px}.scope summary{padding:8px 0;cursor:pointer}.scope p{margin-top:10px}.two{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.card{border:1px solid var(--rule);border-radius:9px;padding:14px;min-width:0}.card header{font-size:14px;font-weight:600;margin-bottom:10px}.card button{font-size:12px;width:100%;min-width:0}.card small{display:block;font-size:12px;color:var(--muted)}.card code{font-size:12px;overflow-wrap:anywhere}.metrics{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.metrics>div{border-left:2px solid var(--accent);padding-left:10px;min-width:0}.metrics span{display:block;min-height:3.2em;color:var(--muted);font-size:12px}.metrics strong{font-size:23px;display:block;font-weight:500;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.formula{border-top:1px solid var(--rule);padding-top:12px;margin-top:14px;font-size:13px;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.value{font:500 20px var(--viz-font-ui)}.legend{display:flex;flex-wrap:wrap;gap:6px 14px;font-size:12px;color:var(--muted);margin:12px 0}.legend span:before{content:'';display:inline-block;width:8px;height:8px;border-radius:2px;background:var(--accent);margin-right:6px}.legend .second:before{background:var(--second)}.check{display:flex;align-items:center;gap:8px;font-size:13px;margin:12px 0}.check input{width:16px;height:16px;flex-shrink:0;accent-color:var(--accent)}select,input[type=text],input[type=number]{background:var(--paper);color:var(--ink);font:inherit;font-size:13px;padding:10px;border:1px solid var(--rule);border-radius:7px;max-width:100%;min-width:0}select{width:100%;min-height:42px}.band{height:20px;display:flex;background:var(--surface);border-radius:4px;overflow:hidden}.band i{height:100%;display:block;background:var(--accent);transition:width .25s}.band i.second{background:var(--second)}button:disabled{cursor:default}.status{font-size:13px;min-height:4.8em}.mono{font-family:var(--viz-font-mono);font-variant-numeric:tabular-nums}.muted{color:var(--muted)}\n@media(max-width:480px){.two{gap:8px}.card{padding:12px 9px}.actions button,.presets button{font-size:12px}.metrics strong{font-size:20px}.metrics{gap:8px}}\n@media(prefers-reduced-motion:reduce){*,*::before,*::after{transition:none!important;animation:none!important}}\n.track-row{margin:16px 0}.track-row>span{display:block;font-size:12px;color:var(--muted);margin-bottom:7px}.track{display:flex;gap:5px;min-width:0}.cell{flex:1;min-width:0;min-height:48px;border:1px solid var(--rule);border-radius:5px;background:var(--surface);display:flex;align-items:center;justify-content:center;text-align:center;padding:5px 3px;font-size:12px;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.cell.on{background:var(--accent-soft);border-color:var(--accent)}.cell.gold{background:var(--second-soft);border-color:var(--second)}.cell.empty{border-style:dashed;color:var(--muted)}.cell.error{border-color:var(--second);text-decoration:line-through}.choices{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;margin:14px 0}.choices button{min-width:0;padding:10px 4px;font-size:12px;overflow-wrap:anywhere}.choices button[aria-current=true]{border-color:var(--accent);background:var(--accent-soft)}.relation{display:grid;grid-template-columns:minmax(0,1fr) 28px minmax(0,1fr);align-items:center;gap:6px;margin:12px 0}.relation .cell{min-height:72px}.relation>i{text-align:center;font-style:normal;color:var(--accent)}.readout{font-size:13px;white-space:pre-line;min-height:6.4em;overflow-wrap:anywhere}.plot{position:relative;border-left:1px solid var(--rule);border-bottom:1px solid var(--rule);padding:8px 0;margin:18px 0}.plot-row{height:28px;position:relative;margin:4px 0;background:var(--surface)}.plot-row i{position:absolute;inset-block:5px;background:var(--accent);min-width:2px;transition:width .2s,left .2s}.plot-row b{position:relative;padding:4px 6px;font:11px/28px var(--viz-font-ui);background:var(--paper);border-radius:3px}.axis{display:flex;justify-content:space-between;font-size:11px;color:var(--muted)}\n\n.formula{min-height:4.8em}.readout{min-height:3.2em}","js":"const $=s=>document.querySelector(s);const set=(id,v)=>$('#'+id).textContent=v;const t=k=>viz.t(k);function cells(id,values){$('#'+id).replaceChildren(...values.map(v=>{const e=document.createElement('div');e.className='cell '+(v.cls||'');e.textContent=v.text;e.title=v.title||v.text;return e;}));}function pressed(mode){document.querySelectorAll('[data-mode]').forEach(e=>e.setAttribute('aria-pressed',String(e.dataset.mode===mode)));}function draw(){const dac=$('#dac').checked,cap=$('#cap').checked,scope=$('#scopeOK').checked,l=$('#lsm').checked,r=$('#rw').checked,d=dac||(cap&&scope);for(const[k,v]of [['dacGate',d],['lsmGate',l],['fsGate',r]])cells(k,[{text:t(v?'satisfied':'fail'),cls:v?'on':'gold'}]);set('formula','('+Number(dac)+' ∨ ('+Number(cap)+' ∧ '+Number(scope)+')) ∧ '+Number(l)+' ∧ '+Number(r)+' = '+Number(d&&l&&r));set('result',t(d&&l&&r?'allow':'deny'));set('explanation',t('note'));}document.querySelectorAll('input').forEach(e=>e.onchange=draw);draw();","audio":false,"strings":{"allow":"これらの条件が許可","cap":"effective CAP_DAC_OVERRIDEを保持","dac":"DAC自体は書き込みを許可","dacGate":"DACまたは適用されるバイパス条件","deny":"少なくとも1つが拒否","fail":"満たさない","fsGate":"ファイルシステム書き込み条件","heading":"1つの許可が、別の拒否を上書きできるか?","intro":"通常ファイルの書き込みに対して、DAC、capabilityの範囲、LSM、および読み取り専用マウント条件を設定する。各制約を同時に保持し、最終的な判断を観察する。","lsm":"LSMポリシーが許可","lsmGate":"LSM制約","note":"capabilityの適用範囲を解除し、サブuserネームスペース内でのみ権限がある場合をシミュレートする。LSMまたは書き込み可能条件を個別に無効にして、capabilityを追加してもなぜすべての条件を補完できないか観察する。","reset":"リセット","result":"上記の必須条件の論理積","rw":"マウントが書き込み可能","satisfied":"満たす","scope":"通常ファイルの書き込みのみを分析し、inodeのアイデンティティマッピングが有効で、ACLの例外やその他のエラーがないと仮定する。capabilityはeffective CAP_DAC_OVERRIDEであり、範囲条件は該当inodeに対して適用可能とする。3つの図でsyscallの固定チェック順序を主張するものではない。完全な認可シミュレータではない。","scopeLabel":"モデルの範囲と規約","scopeOK":"そのcapabilityが対象inodeに対して適用可能"}}
5. BPF LSM:検証されたプログラムでもポリシーを誤って記述する可能性がある
BPF LSMは、権限で制御されたBPFプログラムを使用して、サポートされるフックを拡張することを許可する。libbpfは読み込みとリンクを処理でき、CO-RE/BTFはカーネルタイプの適応を支援する。
システム全体のMACフックの場合、複数のBPFプログラムがチェーンされる際、プログラムはインターフェースを通じて以前の拒否結果を保持する必要があり、渡された非ゼロの ret を無条件に0に変更してはならない。異なるattachタイプには異なる戻り値のセマンティクスがあり、1つの例の戻り値ルールをすべてのBPF LSMプログラムに直接適用することはできない。
verifierは、受け入れ可能なメモリアクセス、制御フローなどの条件をチェックし、プログラムがカーネルを破壊するリスクを低減する。しかし、ビジネスロジックの認可が正しいことを証明するものではなく、カーネル/verifierにバグがないことを約束するものでもない。誤ったポリシーをデプロイすると、通常の作業がブロックされる可能性がある。BPF LSMドキュメント を参照。
6. Keyring、IMA、およびEVM
カーネルのkeyringは、タイプ、権限、および寿命を持つkeyオブジェクトを管理し、keyをkeyringにリンクする機能をサポートする。これはすべてのユーザーがすべての秘密鍵を読み取れるグローバル辞書ではなく、各keyペイロードがハードウェアによって保護されることも保証されない。trusted、encryptedなどのkeyタイプには、それぞれ独自のラップとルートトラスト方式がある。
測定記録と使用時の拒否は異なる目標
IMAのmeasurement はポリシーに基づいてファイルなどのオブジェクトを測定し、測定ログを保持し、TPM PCRを拡張できる。それは1バイト読み込むたびにファイル全体を再計算するものではなく、各ファイルのハッシュをTPMに個別に保存するものでもない。リモート検証には、ログ、PCR、信頼できる証明、および期待される状態の両方を理解する必要がある。
IMAのappraisal は、ポリシーに基づいてファイルの整合性/真正性を検証し、使用を拒否するかどうかを決定する。EVMは選択されたセキュリティ拡張属性および関連メタデータの整合性を保護し、HMACまたは署名を使用できる。測定、実行拒否、およびディスクコンテンツの暗号化は3つの異なる目標である。測定ログだけでは、改ざんされたプログラムの実行を自動的に阻止できない。
7. 現在の環境を只読で観察する
from pathlib import Path
for name in ( ' /sys/kernel/security/lsm' , ' /sys/fs/selinux/enforce' ,
' /proc/self/attr/current' ) :
try :
print ( name , Path ( name ) . read_text ( ) . strip ( ) )
except OSError as exc :
print ( name , type ( exc ) . __name__ )
for line in Path ( ' /proc/self/status' ) . read_text ( ) . splitlines ( ) :
if line . startswith ( ( ' Cap' , ' NoNewPrivs:' , ' Seccomp:' ) ) :
print ( line )
ファイルが見えない場合は、securityfsのマウント漏れ、構成、権限、またはコンテナビューに起因する可能性がある。読み取り失敗だけで、システムにアクセス制御が何もないと断言してはならない。実際の認可問題は、サービスプロセスの資格情報、ネームスペース、ラベル/プロファイル、および拒否ログと組み合わせて検討すべきであり、現在のシェルをチェックするだけでは不十分である。
参考:カーネルキー管理 、整合性メカニズムのソース 。