【入門編】 コンテナランタイムのセキュリティ:SeccompとAppArmorによるシステムコール制限 – 暗号理論・認証基盤 & エンドポイントセキュリティ防御ガイド

こんにちは!インフラや開発の現場で、日々コンテナ技術と向き合っている皆さん。

「Dockerを使ってアプリをサクッと動かせるようになった!」
「Kubernetesでオートスケーリングもばっちり!」

最高ですね。現代の開発において、コンテナはなくてはならない強力な相棒です。でも、ふとこんな不安が頭をよぎったことはありませんか?

「もし、コンテナの中で動いているアプリに脆弱性があって、悪いやつに侵入されたら……ホストOSまで乗っ取られちゃうの?」

結論から言うと、対策をしていなければ、その不安は現実になります。
コンテナはホストOSのカーネルを共有しているため、一度突破されると、マンションの隣の部屋(ホストOSや他のコンテナ)に簡単に侵入できてしまう合鍵のような状態になりかねません。

そこで今回は、コンテナの「最後の砦」であるSeccomp(セックスト・コンポーネントじゃないですよ、セキュア・コンピューティングです!)とAppArmorを使ったシステムコール制限について、身近な防犯の仕組みに例えながら、一緒に優しく紐解いていきましょう。一歩ずつ、確実にマスターしていきましょうね!

—

1. なぜコンテナに「制限」が必要なのか?(家の鍵の例え)

まずは、コンテナとホストOSの関係を私たちの生活に置き換えて考えてみましょう。

想像してみてください。あなたは、頑丈な一軒家(ホストOS)の中に、便利な「レンタルルーム(コンテナ)」を作りました。その部屋には、誰でも使えるように色々な道具(Linuxのシステムコール)が置いてあります。

システムコールとは、アプリが「ハードディスクに書き込みたい!」「ネットワーク通信をしたい!」と、OSの核心部分(カーネル)にお願いするための連絡用紙のようなものです。

もし、このレンタルルームを借りた人が「泥棒」だったらどうでしょう?
「部屋の合鍵を作りたい」「隣の部屋の金庫を開けたい」という危険なお願い(危険なシステムコール)を、連絡用紙に書いてOSに送りまくったら、家全体が乗っ取られてしまいますよね。

Dockerのデフォルト状態は、いわば「ほとんど何でも頼めちゃう便利な連絡用紙が置きっぱなしの状態」です。これを、「この道具は使っちゃダメ!」「この用紙にはこの内容しか書いちゃダメ!」と厳しく制限するのが、今回学ぶSeccompとAppArmorなんです。

—

2. Seccomp(セキュア・コンピューティング)の正体と使い方

まずは Seccomp から見ていきましょう。
Seccompは、Linuxカーネルに備わっている機能で、「プロセスが使えるシステムコール(OSへのお願い)をホワイトリスト方式で制限する」仕組みです。

Dockerでは、デフォルトでも実は約300個あるシステムコールのうち、危険な約44個をブロックするプロファイルが適用されています。でも、本番環境やよりセキュアにしたい環境では、さらにこれを絞り込む(あるいは独自のカスタムプロファイルを作る)ことが重要になります。

Seccompのカスタムプロファイルを作ってみよう

例えば、「このコンテナからは、ファイルシステムの書き込みに関するシステムコールを一切させたくない!」という場合のJSONプロファイルの例を見てみましょう。

{
  "defaultAction": "SCMP_ACT_ALLOW",
  "architectures": [
    "SCMP_ARCH_X86_64",
    "SCMP_ARCH_ARM64"
  ],
  "syscalls": [
    {
      "names": [
        "chmod",
        "fchmod",
        "fchmodat"
      ],
      "action": "SCMP_ACT_ERRNO",
      "args": []
    }
  ]
}

【コードの解説】

  • defaultAction: 基本的な動きの設定です。「許可 (SCMP_ACT_ALLOW)」を基本としています。
  • architectures: 対象となるCPUアーキテクチャを指定しています。
  • syscalls: ここで名指ししたシステムコール(今回はパーミッションを変更する chmod 系)に対して、SCMP_ACT_ERRNO(エラーを返す)というアクションを指定しています。つまり、「そんな命令は受け付けません!」と突っ返すわけですね。

これを seccomp-profile.json という名前で保存したら、Dockerを起動する際に以下のように読み込ませます。

# カスタムのSeccompプロファイル鉄壁のガードを適用してコンテナを起動
docker run --security-opt seccomp=./seccomp-profile.json -d my-web-app:latest

これで、万が一コンテナ内から不正なコードが実行されても、ファイル権限の改ざんを狙った chmod は即座にブロックされます!

—

3. AppArmorによるファイルアクセスの「行動制限」

お次は AppArmor です。
Seccompが「どんなシステムコール(命令)を出せるか」を制限するのに対し、AppArmorは「どのファイルに触っていいか、どのネットワークにアクセスしていいか」という、いわば行動範囲そのものを制限する「MAC(強制アクセス制御)」の仕組みです。

学校やオフィスの入館証をイメージしてください。
一般の社員証(コンテナ内の一般プロセス)では、社長室(ホストの重要システムファイル)のドアノブをガチャガチャしても開きませんよね。AppArmorは、まさにその「入館証の権限ルール」を定義するものです。

AppArmorプロファイルの書き方と適用

Ubuntuなどの環境では、AppArmorは標準で有効になっています。簡単なプロファイルの書き方を見てみましょう。

# /etc/apparmor.d/docker-custom-restrict
# 独自のカスタムAppArmorプロファイル
# 「#include <tunables/global>」などの基本設定は省略しています

profile docker-custom-restrict flags=(attach_disconnected, mediated_стейate) {
  # ネットワークの基本的な読み書きは許可
  network,

  # /etc/passwd などの機密ファイルへの読み取りを厳禁とする
  deny /etc/passwd r,

  # アプリケーションがログを出力する専用ディレクトリ以外への書き込みを禁止
  /var/log/my-app/ rw,
  deny /var/log/** w,
}

【設定のポイント】

  • deny: 「絶対にダメ!」と強力に拒否するルールです。たとえコンテナ内でroot権限を奪われたとしても、このAppArmorが有効であれば /etc/passwd を覗き見することはできません。
  • 逆に rw で許可された場所以外への書き込みもガチガチにブロックします。

これを適用してDockerコンテナを動かすときは、次のように指定します。

# AppArmorのプロファイルを指定してコンテナを実行する
docker run --security-opt apparmor=docker-custom-restrict -d my-web-app:latest

これで、アプリケーションが予期せぬ挙動を示したり、マルウェアに汚染されたりしても、被害をそのコンテナの狭いsandbox(砂場)の中に完全に閉じ込めることができます。

—

4. Kubernetes(K8s)での実用的な設定方法

「Docker単体ならわかったけど、実務ではKubernetes(K8s)を使っているよ!」という方も多いはず。ご安心ください。K8sでも、Podのセキュリティコンテキスト(SecurityContext)やアノテーションを使って、これらを簡単に適用できます。

KubernetesでSeccompやAppArmorを適用するマニフェストファイルのサンプルは以下の通りです。

apiVersion: v1
kind: Pod
metadata:
  name: secure-pod-example
  labels:
    app: secured-app
  annotations:
    # AppArmorプロファイルを指定(Node側に事前にプロファイルがロードされている前提です)
    container.apparmor.security.beta.kubernetes.io/my-container: "localhost/docker-custom-restrict"
spec:
  containers:
  - name: my-container
    image: my-web-app:latest
    securityContext:
      # 特権コンテナとして動かさない(絶対にtrueにしてはいけません!)
      privileged: false
      # ルートファイルシステムを読み取り専用にする(必要に応じて)
      readOnlyRootFilesystem: true
      # Seccompのデフォルトプロファイル(RuntimeDefault)を明示的に適用
      seccompProfile:
        type: RuntimeDefault

【プロフェッショナルからのアドバイス】
近年のKubernetes(v1.19以降など)では、RuntimeDefault というSeccompプロファイルが標準でサポートされるようになっています。まずはマニフェストに上記の seccompProfile 設定を記述し、不要なシステムコールを最初からはじく習慣をつけましょう。これだけでもセキュリティレベルが劇的に向上します。

—

5. おわりに:セキュリティは「面倒くさい」の先にある安心

今回は、SeccompとAppArmorを使ったコンテナのシステムコール・行動制限について解説しました。

「設定ファイルを書くのがちょっと面倒だな……」
「動かない機能が出てきたらどうしよう……」

そう感じることもあるかもしれません。実際、開発初期の段階では、厳しすぎる制限が原因でアプリがエラーを起こすこともあります(いわゆる「動かないトラブルのデバッグ」ですね)。

ですが、それは言い換えれば、「サイバー攻撃者にとっても、同じくらい面倒で突破不可能な壁ができている」ということです。

家の鍵をしっかり閉めるのと同じように、コンテナの鍵も二重・三重にかけておくこと。それが、私たちエンジニアがユーザーの信頼を守るための大切な第一歩です。
焦らず、まずはデフォルトのプロファイルの有効化から、一歩ずつ安全なインフラストラクチャを作っていきましょう!

それでは、また次回のセキュリティ解説でお会いしましょう。ハッピー・セキュア・コーディング!

コメント

タイトルとURLをコピーしました