侵入されても「詰まない」ための要塞術:SELinux/AppArmorによる強制アクセス制御(MAC)の実践
エンジニア諸君、日々お疲れ様。
「うちはFWで守っているから大丈夫」「AWSのセキュリティグループでIP制限しているから安心」……そんな言葉を耳にするたび、私は現場で背筋が凍る思いをする。侵入者は、君たちが必死に築いた防波堤をいとも簡単に乗り越えてくる。それが現実だ。
今日話したいのは、侵入された後の話だ。「侵入されても、何もさせない(あるいは、何もできない)」状態を作るための最後の砦、強制アクセス制御(MAC)について語る。
なぜ「ユーザー権限」だけでは不十分なのか
Linuxの標準的なパーミッション(DAC: 任意アクセス制御)は、ファイルを所有している「ユーザー」の権限に依存する。もし、君たちが大切に育てたWebアプリ(例えばwww-data権限で動くPHP)に脆弱性があり、リモートコード実行(RCE)を許したとしよう。
攻撃者は、www-data権限で動くシェルを手に入れる。その瞬間、www-dataがアクセスできる全てのディレクトリ、設定ファイル、そしてデータベースの認証情報が彼らの掌中に入る。DACの世界では、プロセスは「ユーザーの権限」そのものだからだ。
ここで登場するのが、SELinuxやAppArmorによるMACだ。これは「ユーザーが誰か」ではなく、「プロセスは何を許可されているか」をカーネルレベルで定義する。たとえRCEを許しても、そのプロセスが「Webルート以外に触るな」「/etc/shadowなんて知るな」と鎖で繋がれていれば、被害は最小限に留まる。
AppArmorによる「隔離」の実装(Ubuntu環境)
今回は、導入のハードルが比較的低く、管理が現実的なAppArmorを例に挙げよう。特定のPythonスクリプトが、本来触るべきでないファイルにアクセスしようとした場合を想定する。
1. プロファイルの作成
/etc/apparmor.d/ 配下にプロファイルを作成する。例えば、特定のWebアプリ用プロファイル usr.bin.python3.app を作る。
# /etc/apparmor.d/usr.bin.python3.app
# 特定のディレクトリのみにアクセスを制限するプロファイル
profile my-web-app /usr/bin/python3.9 {
# 基本的なライブラリへの読み取り許可
/usr/lib/** r,
# 特定のWebアプリディレクトリへのアクセス許可
/var/www/html/app/** r,
# ログへの書き込み許可(重要:ここ以外には書き込ませない)
/var/log/app/app.log w,
# デフォルトですべての拒否(deny)が働くため、明示的な許可のみを記述する
# これにより、攻撃者が /etc/passwd を読もうとしてもブロックされる
}
2. プロファイルの適用とデバッグ
プロファイルを読み込み、強制モード(Enforce)にする。
# プロファイルの読み込み
sudo apparmor_parser -r /etc/apparmor.d/usr.bin.python3.app
# 強制モードへの切り替え
sudo aa-enforce /usr/bin/python3.9
もしアプリが動かなくなったら、迷わず dmesg を見ろ。audit メッセージが、何がブロックされたかを雄弁に語ってくれる。
# ブロックされたイベントを確認
dmesg | grep apparmor | grep "DENIED"
脆弱性悪用時(RCE)のシナリオ:攻撃者の視点
例えば、君たちのWebアプリに以下のような、入力をバリデーションしない脆弱なコードがあったとする。
import os
import subprocess
# ユーザーからの入力をそのまま実行してしまう致命的な脆弱性
def execute_command(user_input):
# 攻撃者はここで "; cat /etc/shadow" などを注入する
return subprocess.check_output(user_input, shell=True)
この状態で攻撃者が os.system('cat /etc/shadow') を実行しようとした時、AppArmorが有効であればどうなるか? カーネルは cat プロセスが /etc/shadow を開こうとした瞬間に、「お前のプロファイルにはそんな許可はない」と突き返す。結果、攻撃者の端末には Permission denied と表示され、機密情報は守られる。
インフラ構築のプロとしてのアドバイス
MACを導入する上で最も重要なのは、「最初から100%を目指さないこと」だ。
1. Complainモードで運用開始: まずは aa-complain モードで運用し、アプリの正規の挙動をログにすべて蓄積させる。
2. プロファイルの自動生成: aa-genprof や aa-logprof を活用して、ログから必要なアクセス許可を自動抽出させる。
3. 徐々にEnforceへ: 確信が持てるまで強制モードにはしない。これはCI/CDパイプラインに組み込む際も同様だ。
セキュリティは「設定して終わり」ではない。OSのアップデートやアプリの構成変更に伴い、ポリシーも進化させる必要がある。
面倒か? ああ、確かに面倒だ。だが、深夜3時に「顧客の個人情報が流出しました」という電話を受ける恐怖と、このポリシー設定の手間、どちらが重いかは言うまでもないだろう。
エンジニアとして、システムに「檻」を作ること。それが君たちの設計者としての誇りになるはずだ。明日からの運用に、ぜひこの「鎖」を組み込んでみてくれ。健闘を祈る。
コメント