はじめに
Kubernetesの監査ログといえばKubernetes Audit Loggingを利用した、Kubernetes API に対してのリクエストログの事を指します。
しかし、監査ログとして「いつ」「だれが」「何をしたか」を確認するためにはpod内で実行されたコマンドもログとして取得する必要があると感じています。
そこで、今回は kubectl exec
等でpod内で実行されたコマンドをログ取得する方法としてFalcoを検証してみました。
Falcoとは?
Falcoの公式ページにて下記の紹介があります。
Falcoは、アプリケーションの異常なアクティビティを検出するように設計されたビヘイビアアクティビティモニタです。もともとSysdigによって構築されたパワフルなシステムコールキャプチャテクノロジーを使用します。Falcoでは、1組の[ルール]を使用して、コンテナ、アプリケーション、ホスト、およびネットワークアクティビティを1か所で、1つのデータソースから継続的に監視および検出できます。
詳細な説明は公式ページを見てもらうのが確実です。
今回は「kubectl execにてpod内にて実行されたコマンドをログとして出力する事ができるツール」として利用します。
検証環境
- EKS
- Kubernetes v1.16
今回は下記の様な環境を作ってpod内にて実行されたコマンドをlogとして取得できるかを検証します。
Falcoのデプロイ
FalcoのInstall方法は公式ページのドキュメントに日本語で記載されています。
今回は DaemonSet マニフェスト
の箇所を参考にデプロイをしました。
※注意:公式ページの紹介しているリポジトリと実際のリポジトリが異なります。2020年05月31日現在
デプロイに関するファイルが別リポジトリに移動されています。正しいリポジトリはこちら
また、公式マニュアルではconfigmapの作成を kubectl create
にて行っていますが、CI/CDを考慮してコード管理ができるようにします。
マニフェストファイルでは複数の外部ファイルを参照してconfigmapを作成する事ができないのでkustomizeのconfigMapGeneratorを使って実現しました。
resources:
- rbac.yaml
- daemonset.yaml
configMapGenerator:
- name: falco-config
files:
- falco-config/falco.yaml
- falco-config/falco_rules.yaml
- falco-config/falco_rules.local.yaml
ドキュメント通りに進めてkustomization.yamlを設置すると下記のようなディレクトリ構成になります。
.
├── daemonset.yaml
├── falco-config
│ ├── falco.yaml
│ ├── falco_rules.local.yaml
│ ├── falco_rules.yaml
│ └── k8s_audit_rules.yaml
├── kustomization.yaml
└── rbac.yaml
上記ディレクトリにてapplyする事でKubernetes上にDaemonSetとしてFalcoをDeployする事ができます。
$ kubectl get pod
NAMESPACE NAME READY STATUS RESTARTS AGE
default falco-daemonset-z9xh6 1/1 Running 0 37s
default falco-daemonset-t4dr9 1/1 Running 0 38s
Falcoのルール作成
Falcoではlogの出力条件を『ルール』として定義します。
ルールの定義箇所はデフォルトのルールファイルとローカルルールファイルがそれぞれあります。
- falco_rules.yaml (デフォルトのルールファイル)
- 最初に呼び出されるファイル
- 事前定義された基礎的なルールが含まれている
- このルールファイルは変更されず、新しいソフトウェアバージョンごとに置き換えられることが意図されている
- falco_rules.local.yaml(ローカルルールファイル)
- falco_rules.yamlの後に呼び出されるファイル
- falco_rules.yaml に対する追加、オーバーライド、変更等を定義する
- このルールファイルはソフトウェアバージョンごとに置き換わる事は無い
falco_rules.yaml
はfalcoのバージョンが変わった際に置換する可能性があるのでカスタムルールを作成する際は falco_rules.local.yaml
に記載しましょう。
falcoのカスタムルール作成は公式マニュアルが例を記載してあるので大変参考になります。
あらかじめ定義されているマクロや各種フィールドを組み合わせて condition
として定義する事でログの出力条件とする事ができます。
フィールドを組み合わせて任意のマクロを作成する事も可能です。
今回はpod内のshellで実行した全てのログを取得したいので新規プロセスを検知するマクロを使用しました。
- macro: spawned_process
condition: evt.type = execve and evt.dir=<
上記マクロをconditionに追加した下記ルールを falco_rules.local.yaml
に追記します。
- rule: Unexpected activity
desc: Detected Unexpected activity
condition: spawned_process
output: >
Unexpected process spawned in container
(command=%proc.cmdline pid=%proc.pid user=%user.name %container.info image=%container.image)
priority: INFO
falco_rules.local.yaml
の編集が完了したところで再度falcoのDaemonSetをapplyしてください。
注意点
falco_rules.yamlの中にはKubernetes APIのlog取得に関するルールも記述されています。
EKS等のマネージドなKubernetes環境を利用している場合、Kubernetes APIのlogがクラスターのオプションとして取得できる事が多いのでどちらを利用するかは運用次第かと思います。
Falco以外でKubernetes APIのlogを取得する際は下記部分をコメントアウトすると良さそうです。
2020/06/14 更新
falco_rules.yaml
の内容を変更したい場合は falco_rules.local.yaml
にて上書きするべきでした。
※ falco_rules.yaml
は 『このルールファイルは変更されず、新しいソフトウェアバージョンごとに置き換えられることが意図されている』 為です。
下記ruleを falco_rules.local.yaml
に追記する事でAPIのlog取得ルールをdisableに上書きする事ができました。
- rule: Contact K8S API Server From Container
desc: Detect attempts to contact the K8S API Server from a container
condition: evt.type=connect and evt.dir=< and (fd.typechar=4 or fd.typechar=6) and container and not k8s_containers and k8s_api_server
output: Unexpected connection to K8s API Server from container (command=%proc.cmdline %container.info image=%container.image.repository:%container.image.tag connection=%fd.name)
priority: NOTICE
tags: [network, k8s, container, mitre_discovery]
enabled: false
Falcoで取得したlogの確認
実際にpod内のshellでコマンドを実行してlogを取得できているか確認してみます。
$ kubectl exec -it falco-daemonset-7sxx6 -- bash
root@falco-daemonset-7sxx6:/#
root@falco-daemonset-7sxx6:/# whoami
root
root@falco-daemonset-7sxx6:/# ping 8.8.8.8
falcoのlogにて上記コマンド実行のlogが残っているか確認します。
podのlogとして記録されるのでで下記コマンドにてlogを確認する事ができます。
$ kubectl logs falco-daemonset-7sxx6
"09:00:59.029886506: Notice Unexpected process spawned in container (command=whoami pid=28214 user=root k8s.ns=defalut k8s.pod=falco-daemonset-7sxx6 container=afa3528dd4a0 image=falcosecurity/falco@sha256:70e3e3c46a37a98642f9cd8490235c414ee1c7d670cde65345fa20187971c0a0)\n"
"09:01:11.611216799: Notice Unexpected process spawned in container (command=ping 8.8.8.8 pid=28486 user=root k8s.ns=defalut k8s.pod=falco-daemonset-7sxx6 container=afa3528dd4a0 image=falcosecurity/falco@sha256:70e3e3c46a37a98642f9cd8490235c414ee1c7d670cde65345fa20187971c0a0)\n"
コマンドの実行日時やどのpodで実行されたかもlogにあるので監査ログとして利用できそうです。
念の為、同じNodeにのっている別のpodでもコマンドを実行してみました。
$ kubectl exec -it example-pod -- bash
root@example-pod:/#
root@example-pod:/# cat /etc/hostname
example-pod
root@example-pod:/#
falcoのlogにて上記コマンド実行のlogが残っているか確認します。
"09:21:30.694701115: Notice Unexpected process spawned in container (command=cat /etc/hostname pid=24018 user=root k8s.ns=defalut k8s.pod=example-pod container=d5158de43f2d image=hogehoge@sha256:34cf6e68a67f1dc80958824af97e16c1fd78e21d1e8313f56f62628816cc85c9)\n",
別のpodでもlogが取得できているので問題なさそうです。
総評
情報収集コマンドも含めて全てfalcoのpodにlogとして出力されていました。
取得内容は実行されたpod名、実行コマンド、日時と揃っているので監査logとして利用する事ができそうです。
kubectl exec
実行ユーザーについてはKubernetes API側のlogにて出てるはずなのでログを比較する事で、「いつ」「だれが」「何をしたか」を調査する事はできると思います。
podのlogとして出力されるのでEKSであればCloudWatchlogsを使って取得してS3に保存するといった運用が可能な点も使いやすいと感じました。
気になった点としてexeternal-secretsやexternal-dns等カスタムコントローラを利用している際に該当のpodからKubernetes APIへの通信が不正通信扱いになり、大量のlogが流れていました。
これらの利用する際は該当のlogを除外するruleを定義する等の対応が必要になりそうです。
ruleがとても柔軟に定義できるため、今回の用途以外でもFalcoを活用できる場面は多々あるかと思います。
皆様も是非色々とお試しください。