Falcoを利用したPod内実行コマンドのログ取得

# はじめに
Kubernetesの監査ログといえば[Kubernetes Audit Logging](https://kubernetes.io/docs/tasks/debug-application-cluster/audit/)を利用した、Kubernetes API に対してのリクエストログの事を指します。
しかし、監査ログとして「いつ」「だれが」「何をしたか」を確認するためにはpod内で実行されたコマンドもログとして取得する必要があると感じています。

そこで、今回は `kubectl exec` 等でpod内で実行されたコマンドをログ取得する方法としてFalcoを検証してみました。

# Falcoとは？
Falcoの[公式ページ](https://falco.org/jp/docs/)にて下記の紹介があります。
>Falcoは、アプリケーションの異常なアクティビティを検出するように設計されたビヘイビアアクティビティモニタです。もともとSysdigによって構築されたパワフルなシステムコールキャプチャテクノロジーを使用します。Falcoでは、1組の[ルール]を使用して、コンテナ、アプリケーション、ホスト、およびネットワークアクティビティを1か所で、1つのデータソースから継続的に監視および検出できます。

詳細な説明は公式ページを見てもらうのが確実です。
今回は<font color="Red">「kubectl execにてpod内にて実行されたコマンドをログとして出力する事ができるツール」</font>として利用します。

# 検証環境
- EKS
- Kubernetes v1.16

今回は下記の様な環境を作ってpod内にて実行されたコマンドをlogとして取得できるかを検証します。

![falco_qiita_image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528118/aba979a4-ec9e-ccd5-cc25-32ebd70f6b7f.png)

# Falcoのデプロイ
FalcoのInstall方法は[公式ページのドキュメント](https://falco.org/jp/docs/installation/)に日本語で記載されています。
今回は `DaemonSet マニフェスト` の箇所を参考にデプロイをしました。

<font color="Red">※注意：公式ページの紹介しているリポジトリと実際のリポジトリが異なります。2020年05月31日現在</font>
デプロイに関するファイルが別リポジトリに移動されています。正しいリポジトリは[こちら](https://github.com/falcosecurity/evolution)

また、公式マニュアルではconfigmapの作成を `kubectl create` にて行っていますが、CI/CDを考慮してコード管理ができるようにします。
マニフェストファイルでは複数の外部ファイルを参照してconfigmapを作成する事ができないのでkustomizeのconfigMapGeneratorを使って実現しました。

```yaml:kustomization.yaml
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のカスタムルール作成は[公式マニュアル](https://falco.org/jp/docs/rules/)が例を記載してあるので大変参考になります。
あらかじめ定義されている[マクロ](https://falco.org/jp/docs/rules/default-macros/)や[各種フィールド](https://falco.org/jp/docs/rules/supported-fields/)を組み合わせて `condition` として定義する事でログの出力条件とする事ができます。
フィールドを組み合わせて任意のマクロを作成する事も可能です。

今回はpod内のshellで実行した全てのログを取得したいので新規プロセスを検知するマクロを使用しました。

```yaml:利用するマクロ
- macro: spawned_process
  condition: evt.type = execve and evt.dir=<
```

上記マクロをconditionに追加した下記ルールを `falco_rules.local.yaml` に追記します。

```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してください。

<font color="Red">注意点</font>
falco_rules.yamlの中にはKubernetes APIのlog取得に関するルールも記述されています。
EKS等のマネージドなKubernetes環境を利用している場合、Kubernetes APIのlogがクラスターのオプションとして取得できる事が多いのでどちらを利用するかは運用次第かと思います。
~~Falco以外でKubernetes APIのlogを取得する際は下記部分をコメントアウトすると良さそうです。~~
<font color="Red">2020/06/14 更新</font>
`falco_rules.yaml` の内容を変更したい場合は `falco_rules.local.yaml` にて上書きするべきでした。
※ `falco_rules.yaml` は 『このルールファイルは変更されず、新しいソフトウェアバージョンごとに置き換えられることが意図されている』 為です。

下記ruleを `falco_rules.local.yaml` に追記する事でAPIのlog取得ルールをdisableに上書きする事ができました。

```yaml
- 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を取得できているか確認してみます。

```shell:falcoのpod内でコマンドを実行
$ 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を確認する事ができます。

```shell
$ kubectl logs falco-daemonset-7sxx6
```

```shell:falcoの出力log
"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でもコマンドを実行してみました。

```shell:同じNode上のexample-podにてコマンドを実行
$ kubectl exec -it example-pod -- bash 
root@example-pod:/#
root@example-pod:/# cat /etc/hostname
example-pod
root@example-pod:/#
```

falcoのlogにて上記コマンド実行のlogが残っているか確認します。

```shell:falcoの出力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を活用できる場面は多々あるかと思います。
皆様も是非色々とお試しください。