サーバー保護:ファイアウォールとSSHだけではない
システムに携わるエンジニアなら、サーバーの要塞化はよく知られた話でしょう。私たちは通常、ufwでのファイアウォール設定、SSHポートの変更、パスワードログインの禁止、Fail2Banの導入といった基本的なことから始めます。しかし、それはあくまで外側の防御です。もしサーバー上の古いWordPressプラグインにRCE(リモートコード実行)の脆弱性が見つかったらどうなるでしょうか?攻撃者はすでに内部に侵入しています。彼らは次に何をするでしょうか?
私には、真夜中に忘れられない出来事があります。個人サーバーがSSHブルートフォースボットによって毎分何千回もアクセスされたのです。幸い、すぐに気づき、深刻な被害はありませんでした。その一件以来、私は常に内側からのセキュリティに執着するようになりました。攻撃者がwww-dataのような制限されたユーザー権限であっても内部に侵入できたなら、彼らはそこで止まりません。彼らの目標は、権限昇格(privilege escalation)を果たし、root権限を奪取することです。ここからが本当の戦いの始まりです。
では、乗っ取られたプロセスが「悪さ」をするのをどう防ぐか?ここで、強制アクセス制御(Mandatory Access Control – MAC)メカニズムが役立ちます。
DACとMAC:プロセスを制限する2つのアプローチ
Linuxでは、プログラムのアクセス権を制御するための主要な哲学が2つあります。
- 任意アクセス制御(Discretionary Access Control – DAC): これは、ユーザー、グループ、その他に対しておなじみの
rwx(読み取り、書き込み、実行)権限を割り当てるメカニズムです。その問題は「任意」であることです。プログラムがapp_userの権限で実行されると、app_userが持つすべての権限を継承します。プログラムがハッキングされると、攻撃者も同じ権限を手に入れてしまいます。 - 強制アクセス制御(Mandatory Access Control – MAC): これは「強制的」なメカニズムです。カーネルが、あらかじめ定義されたセキュリティポリシーを実行し、プロセスが何をしてよいかを決定します。重要なのは、プロセスがroot権限で実行されていても、このポリシーによって制限されるという点です。
MACの世界における二大巨頭:AppArmorとSELinux
LinuxのMACと言えば、最も有名な2つの名前がSELinuxとAppArmorです。
- SELinux (Security-Enhanced Linux): NSAによって開発され、非常に強力で詳細、そして…複雑です。システム上のすべてのオブジェクト(ファイル、プロセス、ポート)に「ラベル」を付け、ラベル間の相互作用ルールを定義します。SELinuxはRed Hat系のディストリビューション(CentOS, Fedora, RHEL)で標準となっています。
- AppArmor (Application Armor): より親しみやすく、ファイルパスベースで動作します。ラベリングの代わりに、アプリケーションごとに「プロファイル」を定義し、アクセスを許可または不許可にするパスをリストアップします。AppArmorはDebian系のディストリビューション(Ubuntu, Debian)でデフォルトの選択肢です。
AppArmorかSELinuxか?どちらが「扱いやすい」か?
これは古典的な問いです。セキュリティの専門家に尋ねれば、非常に詳細な制御が可能なためSELinuxの方が優れていると言うかもしれません。しかし、何十台ものサーバーを管理するDevOpsやSysAdminにとっては、「管理のしやすさ」と「デバッグのしやすさ」が最優先されます。
なぜAppArmorは好まれるのか?
- 学習とポリシー作成が容易: AppArmorのプロファイルの構文は非常に直感的です。パスと
r,w,xのような権限をリストアップするだけです。 - Ubuntuに標準搭載: 何も追加でインストールする必要はありません。ほとんどのコアサービスにはすでにプロファイルが用意されています。
- 充実した支援ツール:
aa-genprofやaa-logprofのようなユーティリティが、新しいプロファイルの作成を非常に簡単にします。 - エラー診断が容易: プロファイルがパスに基づいているため、アプリケーションがなぜアクセスをブロックされたのかを簡単に推測できます。
AppArmorの欠点は?
- SELinuxほど詳細ではありません。例えば、ファイルの動的なセキュリティコンテキストやラベルに基づいて権限を割り当てることはできません。
実際、「SELinuxのエラーをどう修正するか? -> setenforce 0」(つまり、無効にしてしまう)というミームが存在するのには理由があります。SELinuxはデバッグが難しく、予期せぬエラーを引き起こしやすいことで有名です。対照的に、AppArmorは安全性と可用性の素晴らしいバランスを実現しています。一般的な権限昇格攻撃の大部分をブロックするのに十分強力です。さらに重要なのは、新しいアプリケーションをデプロイするたびに頭を悩ませることがない点です。
UbuntuでのAppArmor設定手順ガイド
理論はここまでにして、実践に入りましょう。ここではAppArmorプロファイルをゼロから作成する方法を説明します。例として、Nginxのプロファイルを作成してみましょう(実際にはNginxにはすでにプロファイルが用意されていますが)。
ステップ1:AppArmorの状態を確認する
まず、サーバー上でAppArmorがどのように動作しているかを見てみましょう。
sudo aa-status
このコマンドは、ロードされているプロファイルをリストアップします。どのプロファイルがenforceモード(違反を強制的にブロック)で、どのプロファイルがcomplainモード(ログに記録するだけでブロックはしない)であるかを確認できます。
ステップ2:EnforceモードとComplainモードを理解する
- Enforceモード: 本番の動作モード。ポリシーに違反するすべてのアクションはブロックされ、システムログに記録されます。
- Complainモード: 「学習」およびデバッグ用のモード。AppArmorはどのアクションもブロックしません。代わりに、すべての違反をログに記録するだけです。このモードは、新しいアプリケーションのプロファイルを構築する際に非常に役立ちます。
プロファイルをcomplainモードに切り替えるには(例:Nginxの場合):
sudo aa-complain /usr/sbin/nginx
そしてenforceモードに戻すには:
sudo aa-enforce /usr/sbin/nginx
ステップ3:実践編:aa-genprofでプロファイルを自動生成する
ここが最も興味深い部分です。AppArmorはアプリケーションの振る舞いを「学習」し、基本的なプロファイルを自動的に生成できます。
1. アプリケーションをComplainモードに設定: 「学習」プロセス中にAppArmorがNginxをブロックしないようにします。
sudo aa-complain /usr/sbin/nginx
2. プロファイル生成ツールを実行: ターミナルを開き、以下のコマンドを実行します。これにより、システムログの監視が開始されます。
sudo aa-genprof /usr/sbin/nginx
3. アプリケーションを操作する: 別のターミナルを開きます。ここで、Nginxに必要な操作を実行させる必要があります。操作を丁寧に行うほど、生成されるプロファイルはより正確になります。
# サービスを再起動
sudo systemctl restart nginx
# ウェブサイトにアクセス
curl http://localhost
# エラーログを生成するために存在しないページにアクセスしてみる
curl http://localhost/non-existent-page
4. ログをスキャンしてルールを作成: aa-genprofを実行しているターミナルに戻り、Sキー(Scan)を押してログをスキャンさせ、Nginxが実行したアクションを見つけさせます。
ツールはアクション一つひとつについて尋ねてきます。例えば:
Profile: /usr/sbin/nginx
Path: /var/log/nginx/access.log
Mode: w
(A)llow / (D)eny / (I)gnore / (N)ew / (G)lob / Glob with (E)xt / (S)kip / (F)inish
ここで、Nginxがaccess.logファイルに書き込む(w – write)のを許可するか尋ねられます。これは正当な操作なので、A(Allow)を押します。各要求を慎重に検討してください。よくわからない場合は、I(Ignore)を押してスキップできます。すべての操作を確認し終えたら、S(Save)で新しいルールを保存し、F`(Finish)で終了します。</p>
<h3>ステップ4:プロファイルを手動で読み解き、調整する</h3>
<p>作成したプロファイルは<code>/etc/apparmor.d/usr.sbin.nginxに保存されます。このファイルを開いて見てみましょう。内容は以下のようになります。
#include <tunables/global>
/usr/sbin/nginx flags=(complain) {
#include <abstractions/base>
#include <abstractions/nginx>
capability dac_override,
capability net_bind_service,
capability setgid,
capability setuid,
network tcp,
/etc/nginx/nginx.conf r,
/var/log/nginx/access.log w,
/var/log/nginx/error.log w,
/var/www/html/** r,
}
構文は非常に読みやすいです:
r: 読み取り(read)を許可。w: 書き込み(write)を許可。x: 実行(execute)を許可。*: ファイル名の一部を表すワイルドカード。**: サブディレクトリ内のすべてを表すワイルドカード。
このファイルを手動で編集して権限を追加・削除し、その後プロファイルをリロードするだけで構いません。
ステップ5:有効化と監視
プロファイルに満足したら、enforceモードに切り替えましょう。
sudo aa-enforce /usr/sbin/nginx
プロファイルファイルを手動で編集した場合は、次のコマンドでリロードしてください。
sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.nginx
どのアクションがブロックされたかを知るにはどうすればよいでしょうか?システムログをリアルタイムで監視しましょう。
sudo journalctl -f | grep 'apparmor="DENIED"'
Nginxに関連するDENIEDの行が表示された場合、それはプロファイルに必要な権限が不足していることを意味します。aa-logprofを使用して更新するか、手動でプロファイルファイルを編集してからリロードする必要があります。
結論:見過ごされがちな保護レイヤー
AppArmorは非常に価値のある保護レイヤーですが、見過ごされがちです。すべてのセキュリティ問題を解決する万能薬ではありません。しかし、最も一般的な権限昇格技術に対する強固な障壁を作り出します。一度慣れてしまえば、自作のアプリケーション(Python, Node.js, Go)のプロファイルを作成することも非常に迅速になります。
少し時間を取って、重要なサービスをAppArmorで「ロック」することを検討してみてください。夜、ずっと安心して眠れるようになりますよ。それは、外側の防御が突破された場合に備える、静かな救世主であり、第二の防衛線なのです。
