SELinux・AppArmor入門|強制アクセス制御の仕組みと「無効化せずに」運用する方法

kento_morota 12分で読めます
社内のAI・IT活用、技術がわかる相手に無料相談。 相談 →
目次

Rocky LinuxやAlmaLinuxでNginxを立てたら、パーミッションは正しいのに403が返る。ポートを変えたら起動すらしない。検索すると「SELinuxを無効化すれば直る」と出てきて、そのとおりにしたら動いた——こうした経験を持つ方は少なくないはずです。しかし無効化は「鍵が開かないからドアを外す」のと同じで、サーバーの防御層を一枚まるごと捨てています。

この記事では、SELinuxとAppArmorが何を守っているのかをDACとMACの違いから説明し、コンテキスト・ポリシー・ブール値の読み方、Permissiveモードを使った安全な調査手順、audit2allowによるポリシー追加、そしてNginxが403になる典型事例の切り分けまでを扱います。RHEL系(SELinux)とUbuntu系(AppArmor)のどちらを使っていても、「無効化せずに動かす」ための判断ができるようになります。

DACとMAC|SELinux・AppArmorが守っているもの

パーミッションだけでは防げない攻撃

通常のLinuxのアクセス制御は、ファイルの所有者が「誰に読み書きを許すか」を決める任意アクセス制御(DAC: Discretionary Access Control)です。chmod・chownで設定するパーミッションがこれにあたります。DACの弱点は「プロセスはそれを起動したユーザーの権限をすべて持つ」ことです。Nginxのワーカープロセスが脆弱性を突かれた場合、nginxユーザーが読める全ファイル、つまり /etc/passwd やアプリの設定ファイル、他サイトのコードまで読み出せてしまいます。

MACは「プロセスの役割」で制限する

強制アクセス制御(MAC: Mandatory Access Control)は、ユーザーではなくプロセスごとに「このプログラムはこの種類のファイルにしか触れない」というルールをシステム全体で強制します。Nginxは「Webコンテンツと自分のログとポート80/443」にしかアクセスできない、と決めておけば、たとえ乗っ取られても被害はその範囲で止まります。SELinux(RHEL・Rocky・Alma・Fedora・Amazon Linuxで標準)とAppArmor(Ubuntu・Debian・SUSEで標準)はどちらもこのMACを実装したものです。

SELinuxAppArmor
主な採用ディストリRHEL系、Fedora、Amazon LinuxUbuntu、Debian、openSUSE
制御の単位ラベル(コンテキスト)ベース。ファイルやプロセスに付与された型で判定パスベース。プロファイルに書かれたファイルパスで判定
設定の粒度細かく強力だが学習コストが高い読みやすく、プロファイルを自作しやすい
調査用モードPermissivecomplain
主なコマンドgetenforce, semanage, restorecon, audit2allowaa-status, aa-complain, aa-enforce, aa-logprof

SELinuxの基本要素|コンテキスト・ポリシー・ブール値

コンテキストはすべてに付いたラベル

SELinuxではファイル・プロセス・ポートのすべてに「ユーザー:ロール:タイプ:レベル」というコンテキストが付いています。実務で見るのはほぼタイプです。ls -Zps -Z で確認できます。

$ ls -Z /usr/share/nginx/html/index.html
system_u:object_r:httpd_sys_content_t:s0 /usr/share/nginx/html/index.html

$ ps -eZ | grep nginx
system_u:system_r:httpd_t:s0   1234 ?  00:00:00 nginx

# ポートに付いたタイプ
$ sudo semanage port -l | grep http_port_t
http_port_t   tcp   80, 81, 443, 488, 8008, 8009, 8443, 9000

ポリシーは「httpd_t のプロセスは httpd_sys_content_t のファイルを読める」といった許可ルールの集合です。デフォルトのtargetedポリシーには主要なデーモン向けのルールが最初から含まれているため、ディストリ標準の配置場所を使っている限り、ほとんど何もしなくても動きます。問題が起きるのは「標準と違う場所・違うポート・違う接続先」を使ったときです。

ブール値は「よくある例外」のスイッチ

ポリシーを書き換えずに挙動を切り替えられるのがブール値です。たとえばNginxをリバースプロキシとしてバックエンドに接続させる、NFS上のコンテンツを配信する、といった典型的な要件はブール値の切り替えで済みます。

# httpd関連のブール値を一覧
getsebool -a | grep httpd

# Nginx/Apacheからバックエンド(アプリサーバー・DB)へのネットワーク接続を許可
# -P を付けると再起動後も維持される
sudo setsebool -P httpd_can_network_connect on

# 非標準ポート8080でリッスンさせたい場合はポートにタイプを追加
sudo semanage port -a -t http_port_t -p tcp 8080

semanagepolicycoreutils-python-utils パッケージに含まれます。入っていない場合はdnfでインストールしてください。

Permissiveモードで調査し、audit2allowで許可する

「無効化」ではなく「記録だけする」モードに切り替える

SELinuxには3つの状態があります。Enforcing(拒否して記録)、Permissive(拒否せず記録だけ)、Disabled(完全停止)です。トラブル調査のときは一時的にPermissiveにすると、「どのアクセスが拒否されるはずだったか」をログで確認しながらアプリを動かせます。

  1. getenforce で現在の状態を確認する。
  2. sudo setenforce 0 でPermissiveに切り替える(再起動で元に戻る一時的な変更)。
  3. 問題の操作を再現し、sudo ausearch -m AVC -ts recent で拒否ログ(AVC: Access Vector Cache)を確認する。
  4. 原因に応じてコンテキスト修正・ブール値変更・ポリシー追加を行う。
  5. sudo setenforce 1 でEnforcingに戻し、再度動作確認する。

サーバー全体をPermissiveにしたくない場合は、sudo semanage permissive -a httpd_t のように特定のドメインだけPermissiveにできます。これなら他のサービスの防御は維持したまま調査できます。

audit2allowで拒否ログからポリシーを作る

ブール値でもコンテキスト修正でも解決しない場合、拒否ログをもとにカスタムポリシーを生成します。

# 直近のAVC拒否を人が読める形で確認(setroubleshoot-serverが必要)
sudo sealert -a /var/log/audit/audit.log

# 拒否ログからポリシーモジュールを生成してインストール
sudo ausearch -m AVC -ts recent | audit2allow -M my_nginx
sudo semodule -i my_nginx.pp

# 生成された内容(.teファイル)は必ず目視で確認する
cat my_nginx.te

注意点は、audit2allowは「ログにあった拒否をすべて許可するルール」を機械的に作ることです。中身を見ずにインストールすると、攻撃の痕跡まで許可してしまう可能性があります。.te ファイルを開き、「なぜこのアクセスが必要なのか」を説明できる行だけ残すのが正しい使い方です。

典型事例|Nginxが403を返す・起動しない

症状

Rocky Linux 9でNginxを構築し、コンテンツを /srv/www/site に配置しました。chmod 755chown nginx も済ませ、curl でアクセスすると403。Nginxのエラーログには open() "/srv/www/site/index.html" failed (13: Permission denied) と出ています。DACのパーミッションは正しく、ファイアウォールも開いています。

原因

ls -Z で確認すると、ファイルのタイプが user_home_tvar_t になっていました。ホームディレクトリで展開したアーカイブを mv で移動したため、元の場所のコンテキストがそのまま引き継がれたのです(cp なら移動先のデフォルトが付きますが、mv はラベルを保持します)。httpd_t プロセスは user_home_t のファイルを読めないため、SELinuxが拒否していました。

対処

# 恒久的なルールとして「/srv/www以下はWebコンテンツ」と登録
sudo semanage fcontext -a -t httpd_sys_content_t "/srv/www(/.*)?"

# 登録したルールに従ってラベルを付け直す
sudo restorecon -Rv /srv/www

# アップロード先など書き込みが必要なディレクトリは別タイプにする
sudo semanage fcontext -a -t httpd_sys_rw_content_t "/srv/www/site/uploads(/.*)?"
sudo restorecon -Rv /srv/www/site/uploads

検索で見つかる chcon コマンドでもその場では直りますが、chcon は一時的な変更で、restorecon やシステムの再ラベル時に元に戻ります。必ず semanage fcontext でルールを登録してから restorecon を実行してください。同様に、bind() to 0.0.0.0:8080 failed (13: Permission denied) で起動しない場合はポートタイプの未登録、リバースプロキシで502になる場合は httpd_can_network_connect がoffである、というのが定番の原因です。Nginxの基本構築はLinux×Nginx入門、ファイアウォール側の切り分けはLinuxファイアウォール設定入門を参照してください。

Ubuntu系のAppArmorはどう扱うか

プロファイル単位でcomplain/enforceを切り替える

AppArmorはSELinuxと違い、プログラムごとのプロファイル/etc/apparmor.d/ 配下のテキストファイル)でアクセス可能なパスを列挙します。プロファイルがないプログラムは制限されません。UbuntuではMySQLやsnap系パッケージなどにプロファイルが同梱されていますが、Nginxには標準で付属しないため、SELinuxのような「Nginxが403」のトラブルはUbuntuではほとんど起きません。代わりにMySQLのデータディレクトリを変更したときに apparmor="DENIED" で起動できない、という事例がよくあります。

# 状態確認(apparmor-utilsパッケージ)
sudo aa-status

# 特定プロファイルを調査モード(拒否せず記録)にする
sudo aa-complain /etc/apparmor.d/usr.sbin.mysqld

# 拒否ログを確認
sudo journalctl -k | grep 'apparmor="DENIED"'

# ログをもとに対話的にプロファイルへ許可を追加
sudo aa-logprof

# 確認できたら強制モードに戻す
sudo aa-enforce /etc/apparmor.d/usr.sbin.mysqld

MySQLのデータディレクトリを /data/mysql に移す場合は、/etc/apparmor.d/local/usr.sbin.mysqld/data/mysql/ r,/data/mysql/** rwk, を追記し、sudo systemctl reload apparmor で反映します。パスベースなので、SELinuxのラベル付け直しのような概念はなく、「新しい場所を書き足す」だけで済むのがAppArmorのわかりやすさです。

まとめ

SELinuxとAppArmorは「面倒な障害の原因」ではなく、プロセスが乗っ取られたときの被害範囲を限定する防御層です。要点を振り返ります。

  1. DACは「誰が」、MACは「どのプログラムが」で制限する。両方あって初めて多層防御になる。
  2. トラブル時はDisabledではなくPermissive(AppArmorはcomplain)で調査する。
  3. 解決の順番は、コンテキスト修正(semanage fcontext + restorecon)→ ブール値 → audit2allowによるポリシー追加。
  4. chcon は一時的、semanage fcontext が恒久的。
  5. audit2allowの生成結果は必ず目視確認する。

まずは今動いているサーバーで getenforceaa-status を実行し、無効化されていないかを確認してみてください。もしDisabledになっていたら、まずPermissiveに戻してログを1週間眺めるところからが現実的な第一歩です。セキュリティ層を維持したままのサーバー構築や、既存環境の見直しでお悩みの際は、Harmonic Societyのシステム開発・インフラ支援にご相談ください。

#SELinux#AppArmor#Linux#セキュリティ

Harmonic Society

この記事の内容、自社の業務でも活かせそうですか?

ローカルLLM・AI・クラウドなどの技術導入を、要件整理からPoC・社内展開まで代表エンジニアが伴走します。オンライン対応・全国OK。まずは30分の無料相談から。売り込みはしません。

共有:
無料メルマガ

週1回、最新の技術記事をお届け

AI・クラウド・開発の最新記事を毎週月曜にメールでお届けします。登録は無料、いつでも解除できます。

プライバシーポリシーに基づき管理します

関連記事

Related / 9 articles

  1. プログラミング

    DDoS攻撃の仕組みと対策入門|レイヤー別の防御とCDN・クラウドの活用

    DDoS攻撃をボリューム型・プロトコル型・アプリ層に分けて仕組みを解説し、自前サーバーで防げない理由、CloudflareやAWS Shieldの標準防御、オリジンIPの隠し方、レートリミットとBot対策、攻撃を受けたときの初動、費用が跳ね上がるDenial of Walletへの備えまでわかります。

  2. プログラミング

    WAFとは?仕組み・導入パターン・誤検知対策|Webアプリを攻撃から守る実践ガイド

    WAFがファイアウォールやIDSと何が違うのか、シグネチャとマネージドルールの仕組み、Cloudflare WAF・AWS WAF・ModSecurityの比較、フォーム送信がブロックされる誤検知の調査と例外設定、ログ監視、WAFが代替できないことまで実践的に解説します。

  3. プログラミング

    セキュリティヘッダー入門|CSP・HSTS・X-Frame-Optionsの設定と効果を実践解説

    CSP・HSTS・X-Frame-Options・X-Content-Type-Optionsなど主要セキュリティヘッダーが防ぐ攻撃と、CSPのReport-Onlyからの段階導入、nonce/hash、HSTS preloadの不可逆リスク、Nginx・Next.js・Astroでの設定例、確認方法を解説します。

  4. プログラミング

    クラウドの通信費(Egress)入門|データ転送量課金の仕組みと転送コストを抑える設計

    クラウドの「受信無料・送信有料」の原則、AZ間・リージョン間・インターネット向けの単価差、NATゲートウェイ処理料の罠、CDNで転送量を減らす方法、バックアップやログ転送の見落とし、請求書で転送料を特定する手順を解説。想定外の請求を防げます。

  5. プログラミング

    秘密情報をGitに入れない仕組み|.gitignore・git-secrets・履歴から漏れた鍵の削除

    APIキーや.envをGitにコミットしてしまう典型経路と、.gitignore・.env.exampleの運用、pre-commitでのgitleaks検知、GitHub secret scanningの活用、漏れた鍵の無効化と履歴書き換え(git filter-repo)の手順を解説。仕組みで再発を防げます。

  6. プログラミング

    開発・ステージング・本番環境の分離設計|環境差分をなくす構成とアクセス制御

    開発・ステージング・本番それぞれの目的と、構成をコードで揃える方法、環境別の設定注入、本番データを使わないテストデータ戦略、ステージングの保護(Basic認証・IP制限・noindex)、コストを抑える運用までを解説。環境差分による本番障害を防げます。

  7. プログラミング

    ngrok・Cloudflare Tunnelでローカルを公開|Webhook開発とデモ環境の作り方

    NAT内のローカル環境にStripeやLINEのWebhookを届けるトンネリングの仕組みを解説。ngrok・Cloudflare Tunnel・localtunnelの比較、固定ドメインと認証、リクエスト検査、公開時のセキュリティ、自宅サーバー公開への応用までわかります。

  8. プログラミング

    ローカル開発環境のHTTPS化|mkcert・hostsファイル・自己署名証明書の正しい使い方

    ローカル開発をHTTPS前提にすべき理由(Secure Cookie・Service Worker・OAuth)と、mkcertでローカルCAを作りhostsで独自ドメインを割り当ててVite・Next.js・Dockerで使う手順を解説。証明書警告を無視する癖の危険も理解できます。

  9. プログラミング

    localhost・0.0.0.0・127.0.0.1の違い|ポートとUnixソケットを理解して「つながらない」を解決

    localhost・127.0.0.1・0.0.0.0の意味の違い、Dockerで外から接続できない原因、host.docker.internal、ポート競合の調べ方、Unixソケットの利点と権限、1024未満ポートの制約を解説。「つながらない」を仕組みから解決できます。

Harmonic Society

「読んで終わり」にせず、自社の業務で試してみませんか?

AI・ローカルLLM・クラウドの導入を、要件整理からPoC・社内展開まで代表エンジニアが伴走します。オンライン対応・全国OK・売り込みなし。

無料・30分・オンラインOK|1営業日以内に返信します