Kubernetes に Falco を展開してアプリケーションの挙動をモニタリングする

enabling container security with Falco

about Falco

公式 HPContainer Native Runtime Securityと記述されている通り、 Cloud Native な環境におけるセキュリティのモニタリングを実現してくれるソフトウェアです。
Falco を導入すると、可動しているアプリケーションが通常とは違った挙動をしていないか等モニタリングすることができます。
また、検知した際の通知も Slack はもちろん Fluentd や NATS にも対応していて非常に使いやすいです。

deploy Falco to kubernetes cluster

Falco の repository を clone します。

$ git clone https://github.com/falcosecurity/falco

kubernetes の yaml は falco/integrations/k8s-using-daemonsetの中に配置されています。

# 先程 clone した repository
$ cd falco/integrations/k8s-using-daemonset/
$ ls -F
falco-event-generator-deployment.yaml k8s-without-rbac/ k8s-with-rbac/ README.md

deploy 対象の kubernetes cluster が RBAC を使っている場合は k8s-with-rbac/ 配下を使うとよいでしょう。

今回は k8s-with-rbac/ 配下の manifest を使って deploy していきます。

ServiceAccount and ClusterRoleBinding

Falco が使用する ServiceAccount を作成し、ClusterRole を割り当てます。

$ kubectl apply -f falco-account.yaml
serviceaccount/falco-account created
clusterrole.rbac.authorization.k8s.io/falco-cluster-role created
clusterrolebinding.rbac.authorization.k8s.io/falco-cluster-role-binding created

ConfigMap

Falco が使う config と rule を ConfigMap で作成します。
後に Pod 側で file として mount して参照することになります。

falco.yaml

config の内容は repository に配置されている falco.yamlを参考にします。

# ConfigMap の内容を配置する directory を作成する(別にどこでもよい)
$ mkdir falco-config
# config の雛形を配置する
$ cp ../../../falco.yaml falco-config/.
# 編集する
$ vi falco-config/falco.yaml

編集する箇所は下記の部分です。

# json での output を有効にする
json_output: true

# program_output で slack の webhook を設定する
program_output:
enabled: true
keep_alive: false
program: "jq '{text: .output}' | curl -d @- -X POST https://hooks.slack.com/services/foo/bar/baz"

slack の webhook の URL はあらかじめ取得しておいて下さい。

各種 rule

Falco が参照する rule もあらかじめ用意されているのでそれを利用します。
もちろん自作しても構いません。

$ cp ../../../rules/falco_rules.* falco-config/.

あらかじめ用意されているのは falco_rules.local.yaml及び falco_rules.yamlですが、 falco_rules.yamlが base となる rule で、必要に応じて falco_rules.local.yamlで上書きして運用するのが良さそうです。

config と rule の編集が終わったら configmap を作成します。

$ kubectl create configmap falco-config --from-file=falco-config/
configmap/falco-config created

DaemonSet

DaemonSet を作成します。
falco-daemonset-configmap.yamlが用意されているので、これを使います。
そのまま使ってもいいのですが、後ほど DaemonSet の reload をする時に便利なのでちょっと updateStrategyをいじっておきます。

# updateStrategy の type を RollingUpdate にしておく
$ vi falco-daemonset-configmap.yaml
---
spec:
updateStrategy: # 追記
type: RollingUpdate # 追記
---
# DaemonSet 作成
$ kubectl apply -f falco-daemonset-configmap.yaml
daemonset.extensions/falco created
# Pod が正常に起動しているか確認しておく
$ kubectl get pods -l name=falco

Verify Falco behavior

Falco が正常に動いているか確認してみます。

# Falco の pod で bash を起動する
$ kubectl exec -ti falco-4xfp9 bash
root@falco-4xfp9:/#
# exit する
root@falco-4xfp9:/# exit
exit
# Falco の log を確認する
$ kubectl logs --tail 1 falco-4xfp9
{"output":"12:16:40.439277259: Notice A shell was spawned in a container with an attached terminal (user=root k8s.pod=falco-4xfp9 container=5fdb0a349c31 shell=bash parent=<NA> cmdline=bash terminal=34817)","priority":"Notice","rule":"Terminal shell in container","time":"2018-10-16T12:16:40.439277259Z", "output_fields": {"container.id":"5fdb0a349c31","evt.time":1539692200439277259,"k8s.pod.name":"falco-4xfp9","proc.cmdline":"bash ","proc.name":"bash","proc.pname":null,"proc.tty":34817,"user.name":"root"}}

Notice A shell was spawned ... と出ています。

これは先程の falco_rules.yaml の定義にあった、

- rule: Terminal shell in container
desc: A shell was used as the entrypoint/exec point into a container with an attached terminal.
condition: >
spawned_process and container
and shell_procs and proc.tty != 0
and container_entrypoint
output: >
A shell was spawned in a container with an attached terminal (user=%user.name %container.info
shell=%proc.name parent=%proc.pname cmdline=%proc.cmdline terminal=%proc.tty)
priority: NOTICE
tags: [container, shell]

こちらの rule に match した為に検知されたことになります。

slack webhook

slack 側には

このように通知されます。 falco.yamlprogram_output.program の部分は自由に書けますので、頑張ればもう少しリッチな通知にすることができそうですね。

【追記】がんばってリッチにしてみた

頑張った結果

ちなみにこのように設定しています

program_output:
enabled: true
keep_alive: false
program: "jq '{username: \"falco\", icon_emoji: \":eagle:\", attachments: [{fallback: \"Falco Alert\", footer: .time, color: \"danger\", fields: [{title: \"Rule\", value: .rule, short: true}, {title: \"Priority\", value: .priority, short: true}, {title: \"Pod\", value: .output_fields[\"k8s.pod.name\"], short: \"true\"}, {title: \"Command\", value: .output_fields[\"proc.cmdline\"], short: \"true\"}, {title: \"Output\", value: .output, short: false}]}]}' | curl -s -d @- -X POST https://hooks.slack.com/services/foo/bar/baz"

tag による rule の絞り込み

Falco の rule は tag を付けることができます。

この tag を使って指定した tag が付いている rule だけを有効にしたり、その逆で指定した tag が付いている rule 以外を有効にすることができます。

先程検知した rule: Terminal shell in containerには containershellの tag が付いています。
試しに shellの rule を無効にしてみます。

# DaemonSet を更新する
$ kubectl edit daemonsets falco
---
spec:
containers:
- args:
- /usr/bin/falco
- -K
- /var/run/secrets/kubernetes.io/serviceaccount/token
- -k
- https://kubernetes.default
- -pk
- -T # 追記
- shell # 追記
---
# falco の args に -T shell を追加する
# 先程 updateStrategy を RollingUpdate にしていれば自動的に pod は再作成される
$ kubectl get pods -l name=falco

先程と同じように kubectl exec -ti falco-xxxx bashとしても検知されないはずです。(slack の通知も当然飛ばない)
どうやら定義された tag のどれかを無効化( -T)するだけでよさそうです。

$ kubectl logs --tail 1 falco-6krnp
Tue Oct 16 12:42:37 2018: Disabling rules with tag: shell

falco の pod 側にも無効化されたと log が出してくれるようですね。

逆に shell の rule のみを有効化する際には -t を使用します。その場合には

$ kubectl logs --tail 1 falco-dbm9f
Tue Oct 16 12:59:41 2018: Enabling rules with tag: shell

このように表示されます。

How to update falco config

ConfigMap の内容を更新しても falco 自体は config を再度読み込んでくれないようです。
つまり ConfigMap を更新した場合には Pod が再作成されないと有効になりません。
ですので DaemonSet を再作成するという運用でもいいのですが、ここではちょっとしたテクニックを使って Pod を再作成する方法を紹介しておきます。

先程 DaemonSet を作成する際に spec.updateStrategy.typeRollingUpdate にしました。これにより下記の command で DaemonSet の Pod を restart させることができます。

$ kubectl set env daemonset falco RELOAD_DATE="$(date)"

関係ない env を set しているだけなのですが、こうすることで各 Pod は再作成されます。更新する env は何でもいいので HOGEHOGE=${RANDOM} とかでも問題ありません。

この仕組を使えば ConfigMap を更新した後に簡単に Falco の設定を反映させることができます。
updateStrategyについては各自の運用ルールがあると思うのでそれに従うべきですが、今回の Falco の DaemonSet の場合は RollingUpdateにしておいたほうが運用しやすいんじゃないかなと思いました。

Host 側のセキュリティもチェックできる

Falco のチェックは falco_probeというkernel module を load させて行っています。

# kubernetes の node 側で確認できる
$ lsmod | grep falco_probe
falco_probe 618496 4

つまりチェックの対象は可動している Pod だけに留まらないということです。

ですので node 側に login し、 /root/test_file という file を作ると下記のように検知されます。

まとめ

Falco について実際に触れてみましたが、とてもおもしろいソフトウェアだと思いました。

Kubernetes の環境に簡単に導入することができるのも魅力ですね。

kubernetes で可動させる場合には falco-operator も作られているようですので、operator で導入してもいいかもしれませんね。

セキュリティ担当の方は是非触ってみてください。
rule の管理とかとても大変そうですが、あらかじめ用意されている rule がすごく豊富なので少しカスタマイズするだけで好みの rule を作ることができると思います。

それではよい Falco 生活を!