Content
一般に、Kubernetes はセキュリティ監視や脅威検知の機能を備えていません。そのため、クラスター管理者自身がセキュリティの問題を監視して対応することが求められます。
しかし Kubernetes は、監査ログという形で潜在的なセキュリティイベントの検出に役立つ非常に重要なツールを提供しています。監査ログは、Kubernetes API に対して発行されたアクセスリクエストの詳細を体系的に記録し、クラスター全体の疑わしいアクティビティを検出するための一元的なリソースとして機能します。
この記事では、Kubernetes 監査ログの役割と操作方法について説明し、監査ログを使用してセキュリティイベントを追跡する場合の例をご紹介します。
Kubernetes 監査ログとは
簡単に言えば、Kubernetes 監査ログとは、Kubernetes の監査サービスから収集した情報を記録したログのことです。
監査ログの目的は、開始されたリクエスト、開始したユーザー、影響を受けたリソース、そして各リクエストの結果をクラスター管理者が追跡できるようにすることです。
このように監査データを記録して分析すれば、リソースへの不正アクセスのリクエストや、不明なユーザーやサービスによって開始されたリクエストなど、クラスター内の潜在的なセキュリティの問題を早期に可視化することができます。監査ログは、既存のセキュリティ侵害を調査する際にも極めて有用です。とはいえ、その段階に至る前に問題を発見することが望まれます。
Kubernetes で作成した場合のみ存在する監査ログ
「Kubernetes 監査ログ」という言葉はよく聞きますが、Kubernetes 自体が特定の監査ログを作成するわけではないため、この呼び方は誤解を招く恐れがあります。つまり、Kubernetes がすべての監査イベントを自動的に特定のファイルに記録し、それを開いて追跡するだけでセキュリティイベントを把握できるというわけではありません。
そうではなく、Kubernetes では管理者が必要に応じてセキュリティイベントを記録し、任意のバックエンドにそれらをストリーミングするための機能が提供されます。このように、Kubernetes ではさまざまな種類の監査ログを作成できますが、その性質は設定した構成によって異なります。また、デフォルトの監査ログは存在しないため、最初に設定する必要があります。
Kubernetes の監査イベントとステージ
Kubernetes は、ステージと監査イベントという 2 つの重要な概念に基づいて監査データを登録します。
監査イベントとは API サーバーへのあらゆるリクエストを指し、ステージとはサーバーが各リクエストを処理する際の段階を意味します。
各イベントには、考えられる 4 つの「ステージ」があります。
- RequestReceived: このステージでは、API サーバーがリクエストを受信していますが、処理はまだ開始していません。
- ResponseStarted: サーバーがリクエストの処理を開始しましたが、レスポンスはまだ送信していません。
- ResponseComplete: サーバーがリクエストの処理を終了し、レスポンスを送信しました。
- Panic: このステージは、リクエストへのレスポンスで、API サーバーに「重大なエラー」が生じた場合に発生します。
クラスター内で発生するすべての監査イベントのすべてのステージについて、監査ログに情報を記録するように Kubernetes を設定できます。これにより、セキュリティ関連のリクエストがいつ発生したかだけでなく、どのように処理されたかも把握できます。
このような詳細情報は、セキュリティインシデントの重大度の評価に役立ちます。たとえば、ResponseStarted のステージでは悪意がある可能性のあるリクエストがブロックされるため、API サーバーがそのリクエストを許可するレスポンスを返した場合よりも不安要素は少なくなります。どちらのタイプのイベントも把握しておく必要がありますが、後者のほうが優先されるでしょう。Kubernetes の監査を利用すると、その違いを簡単に見分けられます。
監査ログの形式
デフォルトでは、Kubernetes は各監査イベントに関するデータを JSON 形式で生成します。ログファイルに保存できるイベントの例は次のようになります。
{
"kind": "Event",
"apiVersion": "audit.k8s.io/v1beta1",
"metadata": {
"creationTimestamp": "2018-10-08T08:26:55Z"
},
"level": "Request",
"timestamp": "2018-10-08T08:26:55Z",
"auditID": "288ace59-97ba-4121-b06e-f648f72c3122",
"stage": "ResponseComplete",
"requestURI": "/api/v1/pods?limit=500",
"verb": "list",
"user": {
"username": "admin",
"groups": ["system:authenticated"]
},
"sourceIPs": ["10.0.138.91"],
"objectRef": {
"resource": "pods",
"apiVersion": "v1"
},
"responseStatus": {
"metadata": {},
"code": 200
},
"requestReceivedTimestamp": "2018-10-08T08:26:55.466934Z",
"stageTimestamp": "2018-10-08T08:26:55.471137Z",
"annotations": {
"authorization.k8s.io/decision": "allow",
"authorization.k8s.io/reason": "RBAC: allowed by
ClusterRoleBinding "admin-cluster-binding" of ClusterRole "cluster-
admin" to User "admin""
}
}
Code language: PHP (php)
Kubernetes API サーバーにおける監査の有効化
先ほども述べたとおり、Kubernetes は監査イベントを記録するための機能を提供しますが、デフォルトでは実際の記録は行われません。監査ログを生成するには、この機能を有効にする必要があります。
そのためには、API サーバー構成で 2 つのファイルの場所を指定する必要があります。
--audit-policy-file=/etc/kubernetes/audit-policy.yaml \
--audit-log-path=/var/log/audit.log
Code language: JavaScript (javascript)
ここで、audit-policy.yaml は記録する監査イベントとその方法を定義するファイルを示し、audit.log はログデータが実際に保存される(マスターノード上の)場所を示しています。
監査ポリシーファイルの定義
通常、クラスター内で発生する API リクエストをすべて記録する必要はありません。すべて記録するとデータが増えすぎ、ノイズの除去が難しくなります。
監査ポリシーファイルを作成するのはそのためです。監査ポリシーファイルとは、どのイベントを記録し、各イベントについてどの程度のデータを記録するかを指定する YAML 形式のファイルです。
たとえば、次のようになります。
apiVersion: audit.k8s.io/v1 # This is required.
kind: Policy
# Don't generate audit events for all requests in RequestReceived stage.
omitStages:
- "RequestReceived"
rules:
# Log pod changes at RequestResponse level
- level: RequestResponse
resources:
- group: ""
# Resource "pods" doesn't match requests to any subresource of pods,
# which is consistent with the RBAC policy.
resources: ["pods"]
# Only check access to resource "pods"
- level: Metadata
resources:
- group: ""
resources: ["pods/log", "pods/status"]
# Don't log watch requests by the "system:kube-proxy" on endpoints or services
- level: None
users: ["system:kube-proxy"]
verbs: ["watch"]
resources:
- group: "" # core API group
resources: ["endpoints", "services"]
# Don't log authenticated requests to certain non-resource URL paths.
- level: None
userGroups: ["system:authenticated"]
nonResourceURLs:
- "/api*" # Wildcard matching.
- "/version"
# Log the request body of configmap changes in kube-system.
- level: Request
resources:
- group: "" # core API group
resources: ["configmaps"]
# This rule only applies to resources in the "kube-system" namespace.
# The empty string "" can be used to select non-namespaced resources.
namespaces: ["kube-system"]
# Log configmap and secret changes in all other namespaces at the Metadata level.
- level: Metadata
resources:
- group: "" # core API group
resources: ["secrets", "configmaps"]
# A catch-all rule to log all other requests at the Metadata level.
- level: Metadata
# Long-running requests like watches that fall under this rule will not
# generate an audit event in RequestReceived.
omitStages:
- "RequestReceived"
Code language: PHP (php)
上記のコメントからわかるように、このポリシーファイルは記録対象の監査イベントの種類を絞り込んでいます。たとえば、RequestReceived ステージのイベントは無視し、ポッドへのアクセスだけを追跡します。
監査ログを利用してセキュリティイベントを検出する
Kubernetes 監査の簡単な使用例として、次のような監査ポリシーファイルを作成したとします。
apiVersion: audit.k8s.io/v1beta1
kind: Policy
omitStages:
- "RequestReceived"
Rules:
- level: Request
users: ["admin"]
Resources:
- group: ""
resources: ["*"]
- level: Request
user: ["system:anonymous"]
resources:
- group: ""
resources: ["*"]
Code language: JavaScript (javascript)
この監査設定を使用すると、既存の Role または ClusterRole に関連付けられていない新しいユーザーがリクエストを発行したことを検出できます。
たとえば、そのユーザーが次のコマンドでポッドを一覧表示しようとしたとします。
kubectl get pods
Code language: JavaScript (javascript)
このユーザーにはポッドを一覧表示する権限がないため、API サーバーはリクエストを拒否します(kubectl は「Error from server (Forbidden): pods is forbidden: User」を返します)。
同時に、API サーバーは次のような監査イベントを記録します。
{
"kind": "Event",
"apiVersion": "audit.k8s.io/v1beta1",
"metadata": {
"creationTimestamp": "2018-10-08T10:00:20Z"
},
"level": "Request",
"timestamp": "2018-10-08T10:00:20Z",
"auditID": "5fc5eab3-82f5-480f-93d2-79bfb47789f1",
"stage": "ResponseComplete",
"requestURI": "/api/v1/namespaces/default/pods?limit=500",
"verb": "list",
"user": {
"username": "system:anonymous",
"groups": ["system:unauthenticated"]
},
"sourceIPs": ["10.0.141.137"],
"objectRef": {
"resource": "pods",
"namespace": "default",
"apiVersion": "v1"
},
"responseStatus": {
"metadata": {},
"status": "Failure",
"reason": "Forbidden",
"code": 403
},
"requestReceivedTimestamp": "2018-10-08T10:00:20.605009Z",
"stageTimestamp": "2018-10-08T10:00:20.605191Z",
"annotations": {
"authorization.k8s.io/decision": "forbid",
"authorization.k8s.io/reason": ""
}
}
Code language: JSON / JSON with Comments (json)
管理者は、監査ログを追跡することによってこのリクエストを検出し、存在しないはずのユーザーアカウントが存在することを通知できます。
Kubernetes 監査ログを最大限に活用する
API サーバーが 1 時間あたり数百件、あるいは数千件のリクエストを処理するような大規模なクラスターでは、監査ログを手作業で追跡し、潜在的なリスクを検出することは現実的に不可能です。
代わりに、監査イベントを自動的に監視し、異常があればアラートを生成できる検出ツールにイベントのログデータをストリーミングするとよいでしょう。
これには 2 つの方法があります。
監査ログファイルを監視する: マスターノード上で監査ログファイルを直接監視するように侵入検知ツールを設定します。ただし、通常これを行うには、ツールをマスターノード上で実行している必要がありますが、マスターへの負荷が増加するため望ましくない場合があります。(ローカルで実行しているログエージェントを使用してマスターノードからログファイルを収集し、外部ツールに送信することもできますが、その場合、マスター上で余分なソフトウェア、つまりログエージェントを実行しなければならないため、問題の根本的な解決にはなりません。)
HTTP 経由でイベントを送信する: Webhook を使用すれば、イベントデータを外部のセキュリティツールに HTTP 経由で送信できます。これによってセキュリティツールをクラスターから完全に分離して実行することができます。
たとえば、オープンソースのランタイムセキュリティツールである Falco に監査イベントデータをストリーミングするには、kube-apiserver ファイルで Falco をバックエンド webhook として構成します。
apiVersion: v1
kind: Config
clusters:
- name: falco
cluster:
server: http://$FALCO_SERVICE_CLUSTERIP:8765/k8s_audit
contexts:
- context:
cluster: falco
user: ""
name: default-context
current-context: default-context
preferences: {}
users: []
Code language: JavaScript (javascript)
この設定では、(任意のサーバーにホストされている)Falco を使用して、セキュリティイベントが発生した場合に通知を受け取ることができます。Kubernetes の監査ファイルを直接監視したり、セキュリティソフトウェアをクラスター内で直接実行したりする必要はありません。
監査は Kubernetes のセキュリティ戦略に不可欠な要素です。監査ログは、Kubernetes の他のものと同様、設定と管理が少し複雑ですが、高度な構成が可能でもあります。Kubernetes を使用すれば、記録する監査データの種類、保存場所、そしてその処理方法を正確に制御できます。
ノイズを回避しながら、記録する監査イベントの種類を戦略的に決定すると同時に、潜在的なリスクをリアルタイムで通知できる(Falco などの)侵入検知ツールを使用して監査データを統合することによって、Kubernetes のセキュリティ脅威が大きな問題へと発展する前にそれを発見し、修正する能力を最大限に高めることができます。