psqlからPreparedStatementを実行する

PreparedStatementを実行した際の監査ログを確認したく、試したメモ。パラメータ化されたクエリと、PreparedStatementは一緒ではないっぽいので注意。

参考リンク

準備

PostgreSQLに接続する。

DBをリストする。

\l

DBを作成する。

CREATE DATABASE kenko;

DBに接続する。

\c kenko

テーブルをリストする。

\d

テーブルを作成する。

CREATE TABLE kenko_record(
    id char(4) not null,
    name text not null,
    food text not null,
    PRIMARY KEY(id)
);

データを投入する。

INSERT INTO kenko_record(id,name,food) VALUES (1,'hoge','おにぎり');
INSERT INTO kenko_record(id,name,food) VALUES (2,'fuga','サンドイッチ');
INSERT INTO kenko_record(id,name,food) VALUES (3,'piyo','ラーメン');

データをセレクトする。

kenko=> select * from kenko_record;
  id  | name |     food     
------+------+--------------
 1    | hoge | おにぎり
 2    | fuga | サンドイッチ
 3    | piyo | ラーメン
(3 rows)

kenko=> 

PreparedStatement

Prepareする。

PREPARE test (char) as
  select * from kenko_record where id = $1;

実行する。

kenko=> EXECUTE test('1');
  id  | name |   food   
------+------+----------
 1    | hoge | おにぎり
(1 row)

kenko=> 

shd-notifier

shd-notifierをセットアップしようとしたメモ。

手順

クローンする。2行目のコマンドはサブディレクトリを新たなルートにしている。

git clone https://github.com/aws/aws-health-tools.git && cd aws-health-tools
git filter-branch --subdirectory-filter shd-notifier/ HEAD

マネコンから東京リージョンにSNSトピックを作成し、Eメールでサブスクライブしておく。

東京リージョンにCloudFormationでデプロイする。

ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text)
aws cloudformation deploy \
    --stack-name ShdNotifier \
    --template-file shd-notifier.yml \
    --parameter-overrides ChatClient="sns" \
        EndpointArray="[arn:aws:sns:ap-northeast-1:${ACCOUNT_ID}:shd-notifier-topic]]" \
        Bail=0 \
        DEBUG=1 \
    --capabilities CAPABILITY_NAMED_IAM

この時点で各Lambda関数は中身がない。

f:id:sotoiwa:20210729173333p:plain

deploy.shを実行する

deploy.sh Health-Event ap-northeast-1

Lambda関数がデプロイされる。

f:id:sotoiwa:20210729173355p:plain

Bail=0としたので15分でメールが来るはず。

しかしメールが来ない。

関数のログを見ると、以下のエラーが発生しており、どうやらHealth APIを実行しているため、ビジネス以上のサポートがないとダメな模様。

[ERROR] ClientError: An error occurred (SubscriptionRequiredException) when calling the DescribeEvents operation: 
Traceback (most recent call last):
  File "/var/task/index.py", line 77, in lambda_handler
    maxResults=maxEvents
  File "/var/runtime/botocore/client.py", line 386, in _api_call
    return self._make_api_call(operation_name, kwargs)
  File "/var/runtime/botocore/client.py", line 705, in _make_api_call
    raise error_class(parsed_response, operation_name)

Git操作のメモ

Git操作についてのメモ。

やりたいことは以下。

  • mainブランチから、backup-20210729ブランチを作成した
  • その後mainブランチにはいくつかのコミットが追加された
  • mainブランチをbackup-20210729ブランチと同じ状態に戻したい
  • mainブランチの変更はバックアップしておきたい

たぶんいろいろやり方はあるが教えてもらった一つの手順。

前準備

GitHubでtestリポジトリを作成し、ローカルリポジトリをセットアップする。で作業する。

mkdir test
cd test
echo "# test" >> README.md
git init
git add README.md
git commit -m "first commit"
git branch -M main
git remote add origin git@github.com:sotoiwa/test.git
git push -u origin main

ブランチを作成。

git branch backup-20210729

メインブランチにいくつかコミット。

echo hello > test1.txt
git add .
git commit -m "add test1.txt"
echo hello > test2.txt
git add .
git commit -m "add test2.txt"
echo hello > test3.txt
git add .
git commit -m "add test3.txt"

手順

ローカルのmainで作業する。

戻したいコミットを確認する。aacedb6が戻したいコミット。

$ git log --all --oneline --graph
* a525f7a (HEAD -> main) add test3.txt
* b4607ca add test2.txt
* d794b74 add test1.txt
* aacedb6 (origin/main, backup-20210729) first commit

ローカルのmainのHEADをaacedb6に戻す。--softなのでファイルは最新のままになる。

git reset --soft aacedb6
# ブランチ名指定でもOK
# git reset --soft backup-20210729

状態を確認する。ファイルの変更が差分として出ている。

$ git status
On branch main
Your branch is up to date with 'origin/main'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   test1.txt
        new file:   test2.txt
        new file:   test3.txt

mainのHEADは20210729と同じになっている。

$ git log --all --oneline --graph
* aacedb6 (HEAD -> main, origin/main, backup-20210729) first commit

バックアップのために差分をまとめた新しいコミットを作る。

git add .
git commit -m "add some files"

状態を確認する。

$ git log --all --oneline --graph
* 0970403 (HEAD -> main) add some files
* aacedb6 (origin/main, backup-20210729) first commit

ローカルのmainをリモートのorigin/somefilesとしてpushしておく。

git push origin main:somefiles

あるいはここからローカルにブランチを切る。

git branch somefiles

状態としてはこうなる。

$ git branch -a
  backup-20210729
* main
  somefiles
  remotes/origin/main
  remotes/origin/somefiles
$ git log --all --oneline --graph
* 0970403 (HEAD -> main, origin/somefiles, somefiles) add some files
* aacedb6 (origin/main, backup-20210729) first commit

ローカルのmainのHEADを再びaacedb6に戻す。今度は--hardでファイルも戻す。

git reset --hard aacedb6

こうなる。

$ git log --all --oneline --graph
* 0970403 (origin/somefiles, somefiles) add some files
* aacedb6 (HEAD -> main, origin/main, backup-20210729) first commit

備考

  • バックアップ用にブランチを切っていたかどうかはあまり関係ない
  • reset --softを使っているのはコミットログをまとめるためなので、ブランチを切っておいてから--reset hardでもよい

Falcoルールのカスタマイズ

HelmでのFalco環境のセットアップをクラスターのデプロイからやり直し、Falcoルールをカスタマイズして実用に耐えそうな環境を目指すメモ。

コンポーネント バージョン
EKS 1.20
プラットフォームバージョン eks.2
Falco 0.29.1
Falcoチャート 1.15.4

クラスターの作成

1.20でクラスターを作成する。ノードなしで作成する。

cat << EOF > cluster.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: falco
  region: ap-northeast-1
  version: "1.20"
vpc:
  cidr: "10.0.0.0/16"

availabilityZones:
  - ap-northeast-1a
  - ap-northeast-1c

cloudWatch:
  clusterLogging:
    enableTypes: ["*"]

iam:
  withOIDC: true
EOF
eksctl create cluster -f cluster.yaml

ノードを作成する。

cat << "EOF" > managed-ng-1.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: falco
  region: ap-northeast-1

managedNodeGroups:
  - name: managed-ng-1
    minSize: 2
    maxSize: 2
    desiredCapacity: 2
    privateNetworking: true
    iam:
      attachPolicyARNs:
        - arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy
        - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
    preBootstrapCommands:
      - |
        #!/bin/bash

        set -o errexit
        set -o pipefail
        set -o nounset

        yum -y install kernel-devel-$(uname -r)
EOF
eksctl create nodegroup -f managed-ng-1.yaml

Datadogエージェントを入れる(オプション)

helm repo add datadog https://helm.datadoghq.com
helm repo update

APIキーを渡してインストールする。

helm upgrade --install datadog datadog/datadog \
  -n datadog --create-namespace \
  --set datadog.apiKey=hogehoge

Falcoのデプロイ

helm repo add falcosecurity https://falcosecurity.github.io/charts
helm repo update

チャートを確認する。

$ helm search repo falco
NAME                            CHART VERSION   APP VERSION     DESCRIPTION                                       
falcosecurity/falco             1.15.4          0.29.1          Falco                                             
falcosecurity/falco-exporter    0.5.2           0.5.0           Prometheus Metrics Exporter for Falco output ev...
falcosecurity/falcosidekick     0.3.11          2.23.1          A simple daemon to help you with falco's outputs  
stable/falco                    1.1.8           0.0.1           DEPRECATED - incubator/falco

valuesファイルを作成する。初期化コンテナを使う方法でFalcoをインストールする。auditLog.enabled=trueとすることでServiceが作られる。falcosidekickとの連携のための設定を入れるが、一緒には入れずに別に入れる。

cat << EOF > falco-values.yaml
image:
  repository: falcosecurity/falco-no-driver

auditLog:
  enabled: true

falco:
  jsonOutput: true
  jsonIncludeOutputProperty: true
  httpOutput:
    enabled: true
    url: "http://falcosidekick:2801/"

extraInitContainers:
  - name: driver-loader
    image: docker.io/falcosecurity/falco-driver-loader:0.29.1
    imagePullPolicy: Always
    securityContext:
      privileged: true
    volumeMounts:
      - mountPath: /host/proc
        name: proc-fs
        readOnly: true
      - mountPath: /host/boot
        name: boot-fs
        readOnly: true
      - mountPath: /host/lib/modules
        name: lib-modules
      - mountPath: /host/usr
        name: usr-fs
        readOnly: true
      - mountPath: /host/etc
        name: etc-fs
        readOnly: true
EOF

インストールする。

helm upgrade --install falco falcosecurity/falco \
  -n falco --create-namespace \
  -f falco-values.yaml

falcosidekickの導入

valuesファイルを作成する。

cat << EOF > falcosidekick-values.yaml
config:
  slack:
    webhookurl: "https://hooks.slack.com/hogehoge"
  datadog:
    apikey: "hogehoge"
  aws:
    sns:
      topicarn: "arn:aws:sns:ap-northeast-1:XXXXXXXXXXXX:Falco_Alarms_Topic"
webui:
  enabled: true
EOF

インストールする。

helm upgrade --install falcosidekick falcosecurity/falcosidekick \
  -n falco --create-namespace \
  -f falcosidekick-values.yaml

IRSAで権限を与えるためのポリシーを作成する。

ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account)
AWS_REGION=$(aws configure get region)
cat <<EOF >eks-falcosidekick-policy.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "sns:Publish",
            "Resource": "arn:aws:sns:${AWS_REGION}:${ACCOUNT_ID}:Falco_Alarms_Topic"
        }
    ]
}
EOF
aws iam create-policy \
    --policy-name eks-falcosidekick-policy \
    --policy-document file://eks-falcosidekick-policy.json

IRSAでロールをアタッチする。

eksctl create iamserviceaccount \
    --name falcosidekick \
    --namespace falco \
    --cluster falco \
    --attach-policy-arn arn:aws:iam::${ACCOUNT_ID}:policy/eks-falcosidekick-policy \
    --override-existing-serviceaccounts \
    --approve

ポートフォワードでfalcosidekick-uiにアクセスする。

k -n falco port-forward svc/falcosidekick-ui 2802

ekscloudwatchの導入

ConfigMapを作成する。

cat << "EOF" > ekscloudwatch-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: ekscloudwatch-config
  namespace: falco
data:
  # Required: Endpoint to forward audit events to, such as Sysdig Secure agent
  # The agent must expose a k8s audit server (k8s_audit_server_port must be configured in the agent as well)
  endpoint: "http://falco:8765/k8s-audit"

  # Required: Cloudwatch polling interval
  cw_polling: "1m"

  # Required: CloudWatch query filter
  cw_filter: '{ $.sourceIPs[0] != "::1" && $.sourceIPs[0] != "127.0.0.1" }'

  # Optional: both the EKS cluster name and region must be set
  # This can be omitted if the EC2 instance can perform the ec2metadata and ec2:DescribeInstances action
  cluster_name: "falco"
  aws_region: "ap-northeast-1"
EOF

ConfiMapを作成する。

k apply -f ekscloudwatch-config.yaml
CLUSTER_NAME="falco"
ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account)
AWS_REGION=$(aws configure get region)
cat <<EOF >eks-ekscloudwatch-policy.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:DescribeQueries",
                "logs:DescribeExportTasks",
                "logs:GetLogRecord",
                "logs:GetQueryResults",
                "logs:StopQuery",
                "logs:TestMetricFilter",
                "logs:DescribeQueryDefinitions",
                "logs:DescribeResourcePolicies",
                "logs:GetLogDelivery",
                "logs:DescribeDestinations",
                "logs:ListLogDeliveries"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:Describe*",
                "logs:List*",
                "logs:StartQuery",
                "logs:FilterLogEvents",
                "logs:Get*"
            ],
            "Resource": [
                "arn:aws:logs:${AWS_REGION}:${ACCOUNT_ID}:log-group:/aws/eks/${CLUSTER_NAME}/cluster:log-stream:*",
                "arn:aws:logs:${AWS_REGION}:${ACCOUNT_ID}:log-group:/aws/eks/${CLUSTER_NAME}/cluster"
            ]
        }
    ]
}
EOF
aws iam create-policy \
    --policy-name eks-ekscloudwatch-policy \
    --policy-document file://eks-ekscloudwatch-policy.json

IRSAで必要な権限を与える。

eksctl create iamserviceaccount \
    --name eks-cloudwatch \
    --namespace falco \
    --cluster falco \
    --attach-policy-arn arn:aws:iam::${ACCOUNT_ID}:policy/eks-ekscloudwatch-policy \
    --override-existing-serviceaccounts \
    --approve

Deploymentを作成する。

cat << EOF > ekscloudwatch-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: eks-cloudwatch
  namespace: falco
spec:
  minReadySeconds: 5
  replicas: 1
  selector:
    matchLabels:
      app: eks-cloudwatch
  template:
    metadata:
      labels:
        app: eks-cloudwatch
    spec:
      serviceAccountName: eks-cloudwatch
      containers:
        - image: sysdiglabs/k8sauditlogforwarder:ekscloudwatch-0.3
          imagePullPolicy: Always
          name: eks-cloudwatch-container
          env:
            - name: ENDPOINT
              valueFrom:
                configMapKeyRef:
                  name: ekscloudwatch-config
                  key: endpoint
            - name: CLUSTER_NAME
              valueFrom:
                configMapKeyRef:
                  name: ekscloudwatch-config
                  key: cluster_name
            - name: AWS_REGION
              valueFrom:
                configMapKeyRef:
                  name: ekscloudwatch-config
                  key: aws_region
            - name: CW_POLLING
              valueFrom:
                configMapKeyRef:
                  name: ekscloudwatch-config
                  key: cw_polling
            - name: CW_FILTER
              valueFrom:
                configMapKeyRef:
                  name: ekscloudwatch-config
                  key: cw_filter
EOF

デプロイする。

k apply -f ekscloudwatch-deployment.yaml

Podが動いていることを確認する。

$ k -n falco get po
NAME                               READY   STATUS    RESTARTS   AGE
eks-cloudwatch-66f84688d9-q8nb2    1/1     Running   0          50s
falco-ckpfx                        1/1     Running   0          32m
falco-sbfm8                        1/1     Running   0          31m
falcosidekick-96876945c-cftkw      1/1     Running   0          26m
falcosidekick-96876945c-k5mtd      1/1     Running   0          26m
falcosidekick-ui-db9fdf886-j2xrq   1/1     Running   0          26m

EKS 1.20だからか、この通知がたくさん出る。

f:id:sotoiwa:20210728090808p:plain

このリストを上書きするカスタムルールを追加する必要がある。

1.20から追加されたEKS独自のユーザーはここに記載がある。以下を追加すれば出まくることは防げそうだったが、念のため全部追加する。

  • "eks:certificate-controller"
  • "eks:k8s-metrics"
  • "eks:cluster-event-watcher"
  • "eks:pod-identity-mutating-webhook"
cat << EOF > falco-values.yaml
image:
  repository: falcosecurity/falco-no-driver

auditLog:
  enabled: true

falco:
  jsonOutput: true
  jsonIncludeOutputProperty: true
  httpOutput:
    enabled: true
    url: "http://falcosidekick:2801/"

extraInitContainers:
  - name: driver-loader
    image: docker.io/falcosecurity/falco-driver-loader:0.29.1
    imagePullPolicy: Always
    securityContext:
      privileged: true
    volumeMounts:
      - mountPath: /host/proc
        name: proc-fs
        readOnly: true
      - mountPath: /host/boot
        name: boot-fs
        readOnly: true
      - mountPath: /host/lib/modules
        name: lib-modules
      - mountPath: /host/usr
        name: usr-fs
        readOnly: true
      - mountPath: /host/etc
        name: etc-fs
        readOnly: true

customRules:
  k8s_audit_rules.local.yaml: |-
    - list: allowed_k8s_users
      items: [
        "minikube", "minikube-user", "kubelet", "kops", "admin", "kube", "kube-proxy", "kube-apiserver-healthcheck",
        "kubernetes-admin",
        vertical_pod_autoscaler_users,
        cluster-autoscaler,
        "system:addon-manager",
        "cloud-controller-manager",
        "eks:node-manager",
        "system:kube-controller-manager",
        "eks:authenticator",
        "eks:certificate-controller",
        "eks:cluster-event-watcher",
        "eks:fargate-scheduler",
        "eks:k8s-metrics",
        "eks:nodewatcher",
        "eks:pod-identity-mutating-webhook",
        "eks:cluster-bootstrap"
        ]
EOF

アップデートする。

helm upgrade --install falco falcosecurity/falco \
  -n falco --create-namespace \
  -f falco-values.yaml

これである程度収まったが、K8s Serviceaccount Createdが繰り返し出る

ルールのカスタマイズ

通知がいろいろ出るので、ホワイトリストへの追加などを行い、通知を抑制する。またEKS Best PracticeにLogs Insightsのサンプルクエリとして載っている以下の内容をFalcoルールとして実装してみる。

  • Lists updates to the aws-auth ConfigMap
  • Lists creation of new or changes to validation webhooks
  • Lists create, update, delete operations to Roles
  • Lists create, update, delete operations to RoleBindings
  • Lists create, update, delete operations to ClusterRoles
  • Lists create, update, delete operations to ClusterRoleBindings
  • Plots unauthorized read operations against Secrets
  • List of failed anonymous requests

こんな感じになった。

cat << EOF > falco-values.yaml
image:
  repository: falcosecurity/falco-no-driver

auditLog:
  enabled: true

falco:
  jsonOutput: true
  jsonIncludeOutputProperty: true
  httpOutput:
    enabled: true
    url: "http://falcosidekick:2801/"

extraInitContainers:
  - name: driver-loader
    image: docker.io/falcosecurity/falco-driver-loader:0.29.1
    imagePullPolicy: Always
    securityContext:
      privileged: true
    volumeMounts:
      - mountPath: /host/proc
        name: proc-fs
        readOnly: true
      - mountPath: /host/boot
        name: boot-fs
        readOnly: true
      - mountPath: /host/lib/modules
        name: lib-modules
      - mountPath: /host/usr
        name: usr-fs
        readOnly: true
      - mountPath: /host/etc
        name: etc-fs
        readOnly: true

customRules:
  falco_rules.local.yaml: |-
    - list: falco_privileged_images
      items: [
        docker.io/calico/node,
        calico/node,
        docker.io/cloudnativelabs/kube-router,
        docker.io/docker/ucp-agent,
        docker.io/falcosecurity/falco,
        docker.io/mesosphere/mesos-slave,
        docker.io/rook/toolbox,
        docker.io/sysdig/sysdig,
        falcosecurity/falco,
        gcr.io/google_containers/kube-proxy,
        gcr.io/google-containers/startup-script,
        gcr.io/projectcalico-org/node,
        gke.gcr.io/kube-proxy,
        gke.gcr.io/gke-metadata-server,
        gke.gcr.io/netd-amd64,
        gcr.io/google-containers/prometheus-to-sd,
        k8s.gcr.io/ip-masq-agent-amd64,
        k8s.gcr.io/kube-proxy,
        k8s.gcr.io/prometheus-to-sd,
        quay.io/calico/node,
        sysdig/sysdig,
        sematext_images,
        docker.io/falcosecurity/falco-no-driver,
        falcosecurity/falco-no-driver,
        602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/eks/kube-proxy,
        602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/amazon-k8s-cni-init
        ]
    - list: falco_sensitive_mount_images
      items: [
        docker.io/sysdig/sysdig, sysdig/sysdig,
        docker.io/falcosecurity/falco, falcosecurity/falco,
        gcr.io/google_containers/hyperkube,
        gcr.io/google_containers/kube-proxy, docker.io/calico/node,
        docker.io/rook/toolbox, docker.io/cloudnativelabs/kube-router, docker.io/consul,
        docker.io/datadog/docker-dd-agent, docker.io/datadog/agent, docker.io/docker/ucp-agent, docker.io/gliderlabs/logspout,
        docker.io/netdata/netdata, docker.io/google/cadvisor, docker.io/prom/node-exporter,
        amazon/amazon-ecs-agent, prom/node-exporter, amazon/cloudwatch-agent,
        docker.io/falcosecurity/falco-no-driver, falcosecurity/falco-no-driver,
        gcr.io/datadoghq/agent
        ]
    - list: falco_hostnetwork_images
      items: [
        gcr.io/google-containers/prometheus-to-sd,
        gcr.io/projectcalico-org/typha,
        gcr.io/projectcalico-org/node,
        gke.gcr.io/gke-metadata-server,
        gke.gcr.io/kube-proxy,
        gke.gcr.io/netd-amd64,
        k8s.gcr.io/ip-masq-agent-amd64
        k8s.gcr.io/prometheus-to-sd,
        602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/amazon-k8s-cni,
        602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/eks/kube-proxy
        ]
  k8s_audit_rules.local.yaml: |-
    - list: allowed_k8s_users
      items: [
        "minikube", "minikube-user", "kubelet", "kops", "admin", "kube", "kube-proxy", "kube-apiserver-healthcheck",
        "kubernetes-admin",
        vertical_pod_autoscaler_users,
        cluster-autoscaler,
        "system:addon-manager",
        "cloud-controller-manager",
        "eks:node-manager",
        "system:kube-controller-manager",
        "eks:authenticator",
        "eks:certificate-controller",
        "eks:cluster-event-watcher",
        "eks:fargate-scheduler",
        "eks:k8s-metrics",
        "eks:nodewatcher",
        "eks:pod-identity-mutating-webhook",
        "eks:cluster-bootstrap"
        ]
    - rule: K8s Serviceaccount Created
      desc: Detect any attempt to create a service account
      condition: (kactivity and kcreate and serviceaccount and response_successful and non_system_user)
      output: K8s Serviceaccount Created (user=%ka.user.name user=%ka.target.name ns=%ka.target.namespace resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
      priority: INFO
      source: k8s_audit
      tags: [k8s]
    - list: user_allowed_kube_namespace_image_list
      items: [
        602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/amazon-k8s-cni,
        602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/eks/kube-proxy,
        602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/eks/coredns,
        user_trusted_image_list
        ]
    - rule: K8s aws-auth ConfigMap Edited
      desc: Detect any attempt to edit the aws-auth configmap.
      condition: (kactivity and kmodify and configmap and ka.target.namespace=kube-system and ka.target.name=aws-auth and response_successful)
      output: K8s aws-auth ConfigMap Edited (sourceip=%jevt.value[/sourceIPs/0] user=%ka.user.name verb=%ka.verb configmap=%ka.target.name ns=%ka.target.namespace resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
      priority: ERROR
      source: k8s_audit
      tags: [k8s]
    - macro: validatingwebhookconfiguration
      condition: ka.target.resource=validatingwebhookconfigurations
    - rule: K8s ValidatingWebhookConfiguration Created or Edited.
      desc: Detect any attempt to create or edit a validatingwebhookconfiguration.
      condition: (kactivity and (kcreate or kmodify) and validatingwebhookconfiguration and response_successful)
      output: K8s ValidatingWebhookConfiguration Created or Edited (user=%ka.user.name verb=%ka.verb validatingwebhookconfiguration=%ka.target.name resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
      priority: ERROR
      source: k8s_audit
      tags: [k8s]
    - rule: K8s Role/Clusterrole Created
      desc: Detect any attempt to create a cluster role/role
      enabled: false
      condition: (kactivity and kcreate and (clusterrole or role) and response_successful)
      output: K8s Cluster Role Created (user=%ka.user.name role=%ka.target.name rules=%ka.req.role.rules resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
      priority: INFO
      source: k8s_audit
      tags: [k8s]
    - rule: K8s Role/Clusterrole Deleted
      desc: Detect any attempt to delete a cluster role/role
      enabled: false
      condition: (kactivity and kdelete and (clusterrole or role) and response_successful)
      output: K8s Cluster Role Deleted (user=%ka.user.name role=%ka.target.name resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
      priority: INFO
      source: k8s_audit
      tags: [k8s]
    - rule: K8s Role/Clusterrolebinding Created
      desc: Detect any attempt to create a clusterrolebinding
      enabled: false
      condition: (kactivity and kcreate and clusterrolebinding and response_successful)
      output: K8s Cluster Role Binding Created (user=%ka.user.name binding=%ka.target.name subjects=%ka.req.binding.subjects role=%ka.req.binding.role resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
      priority: INFO
      source: k8s_audit
      tags: [k8s]
    - rule: K8s Role/Clusterrolebinding Deleted
      desc: Detect any attempt to delete a clusterrolebinding
      enabled: false
      condition: (kactivity and kdelete and clusterrolebinding and response_successful)
      output: K8s Cluster Role Binding Deleted (user=%ka.user.name binding=%ka.target.name resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
      priority: INFO
      source: k8s_audit
      tags: [k8s]
    - rule: K8s Role Created, Edited or Deleted
      desc: Detect any attempt to create, edit or delete a role.
      condition: (kactivity and (kcreate or kmodify or kdelete) and role and response_successful)
      output: K8s Role Created, Edited or Deleted (user=%ka.user.name verb=%ka.verb role=%ka.target.name ns=%ka.target.namespace resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
      priority: ERROR
      source: k8s_audit
      tags: [k8s]
    - macro: rolebinding
      condition: ka.target.resource=rolebindings
    - rule: K8s RoleBinding Create, Edited or Deleted
      desc: Detect any attempt to create, edit or delete a rolebinding.
      condition: (kactivity and (kcreate or kmodify or kdelete) and rolebinding and response_successful)
      output: K8s RoleBinding Created, Edited or Deleted (user=%ka.user.name verb=%ka.verb rolebinding=%ka.target.name ns=%ka.target.namespace resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
      priority: ERROR
      source: k8s_audit
      tags: [k8s]
    - rule: K8s ClusterRole Created, Edited or Deleted
      desc: Detect any attempt to create, edit or delete a clusterrole.
      condition: (kactivity and (kcreate or kmodify or kdelete) and clusterrole and response_successful)
      output: K8s ClusterRole Created, Edited or Deleted (user=%ka.user.name verb=%ka.verb clusterrole=%ka.target.name ns=%ka.target.namespace resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
      priority: ERROR
      source: k8s_audit
    - rule: K8s ClusterRoleBinding Create, Edited or Deleted
      desc: Detect any attempt to create, edit or delete a clusterrolebinding.
      condition: (kactivity and (kcreate or kmodify or kdelete) and clusterrolebinding and response_successful)
      output: K8s ClusterRoleBinding Created, Edited or Deleted (user=%ka.user.name verb=%ka.verb clusterrolebinding=%ka.target.name ns=%ka.target.namespace resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
      priority: ERROR
      source: k8s_audit
      tags: [k8s]
    - macro: kread
      condition: (ka.verb in (get,watch,list))
    - rule: K8s Unauthorized read operations against Secret
      desc: Detect unauthorized read operations against secret.
      condition: (kactivity and kread and secret and not response_successful)
      output: K8s Unauthorized read operations against Secret (user=%ka.user.name secret=%ka.target.name ns=%ka.target.namespace resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
      priority: ERROR
      source: k8s_audit
      tags: [k8s]
    - macro: anonymous
      condition: ka.user.name=system:anonymous
    - rule: K8s Failed anonymous requests
      desc: Detect failed anonymous requests.
      enabled: false
      condition: (kactivity and anonymous and not response_successful)
      output: K8s Failed anonymous requests (user=%ka.user.name verb=%ka.verb uri=%ka.uri sourceip=%jevt.value[/sourceIPs/0] resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
      priority: ERROR
      source: k8s_audit
      tags: [k8s]
EOF

アップデートする。

helm upgrade --install falco falcosecurity/falco \
  -n falco --create-namespace \
  -f falco-values.yaml

Falcoのルールは複数のルールがマッチする場合に複数のアラートが出るわけではなく、1回しかアラートが出ないようである。したがって、カスタムしたルールを入れていても、デフォルトのルールにマッチしてしまうとそちらの方でアラートが出てしまう。デフォルトのルールのほうのPriorityが低かったりする場合は注意が必要そう。

Falcoでプレビルドのカーネルモジュールがダウンロードできない場合

以下のIssueにあるように、Falcoのプレビルドのカーネルモジュールがダウンロードできない場合について試したメモ。

コンポーネント バージョン
EKS 1.21
プラットフォームバージョン eks.1
Falco 0.29.1
Falcoチャート 1.15.3

クラスターの準備

1.21でクラスターを作成する。

cat << EOF > cluster.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: falco3
  region: ap-northeast-1
  version: "1.21"
vpc:
  cidr: "10.0.0.0/16"

availabilityZones:
  - ap-northeast-1a
  - ap-northeast-1c

managedNodeGroups:
  - name: managed-ng-1
    minSize: 2
    maxSize: 2
    desiredCapacity: 2
    ssh:
      allow: true
      publicKeyName: default
      enableSsm: true
    privateNetworking: true

cloudWatch:
  clusterLogging:
    enableTypes: ["*"]

iam:
  withOIDC: true
EOF
eksctl create cluster -f cluster.yaml

ノードのバージョンを確認する。カーネルバージョンが新しくまだサポートされていないはず。

$ k get node -o wide
NAME                                              STATUS   ROLES    AGE   VERSION                     INTERNAL-IP    EXTERNAL-IP   OS-IMAGE         KERNEL-VERSION                CONTAINER-RUNTIME
ip-10-0-101-224.ap-northeast-1.compute.internal   Ready    <none>   22m   v1.21.2-13+d2965f0db10712   10.0.101.224   <none>        Amazon Linux 2   5.4.129-62.227.amzn2.x86_64   docker://19.3.13
ip-10-0-91-102.ap-northeast-1.compute.internal    Ready    <none>   22m   v1.21.2-13+d2965f0db10712   10.0.91.102    <none>        Amazon Linux 2   5.4.129-62.227.amzn2.x86_64   docker://19.3.13

Falcoのデプロイ

Helm

FalcoのHelmチャートリポジトリを追加する。

helm repo add falcosecurity https://falcosecurity.github.io/charts
helm repo update

チャートを確認する。

$ helm search repo falco
NAME                            CHART VERSION   APP VERSION     DESCRIPTION                                       
falcosecurity/falco             1.15.3          0.29.1          Falco                                             
falcosecurity/falco-exporter    0.5.2           0.5.0           Prometheus Metrics Exporter for Falco output ev...
falcosecurity/falcosidekick     0.3.11          2.23.1          A simple daemon to help you with falco's outputs  
stable/falco                    1.1.8           0.0.1           DEPRECATED - incubator/falco

デフォルト設定で導入する。

helm upgrade --install falco falcosecurity/falco -n falco --create-namespace

エラーになることを確認。

$ k -n falco get po
NAME          READY   STATUS   RESTARTS   AGE
falco-r8lpf   0/1     Error    1          23s
falco-rw95v   0/1     Error    1          23s

プレビルドモジュールのダウンロードに失敗している。

$ k -n falco logs falco-r8lpf
* Setting up /usr/src links from host
* Running falco-driver-loader for: falco version=0.29.1, driver version=17f5df52a7d9ed6bb12d3b1768460def8439936d
* Running falco-driver-loader with: driver=module, compile=yes, download=yes
* Unloading falco module, if present
* Trying to load a system falco module, if present
* Looking for a falco module locally (kernel 5.4.129-62.227.amzn2.x86_64)
* Trying to download a prebuilt falco module from https://download.falco.org/driver/17f5df52a7d9ed6bb12d3b1768460def8439936d/falco_amazonlinux2_5.4.129-62.227.amzn2.x86_64_1.ko
curl: (22) The requested URL returned error: 404
Unable to find a prebuilt falco module
* Trying to dkms install falco module with GCC /usr/bin/gcc
DIRECTIVE: MAKE="'/tmp/falco-dkms-make'"
* Running dkms build failed, couldn't find /var/lib/dkms/falco/17f5df52a7d9ed6bb12d3b1768460def8439936d/build/make.log (with GCC /usr/bin/gcc)
* Trying to dkms install falco module with GCC /usr/bin/gcc-8
DIRECTIVE: MAKE="'/tmp/falco-dkms-make'"
* Running dkms build failed, couldn't find /var/lib/dkms/falco/17f5df52a7d9ed6bb12d3b1768460def8439936d/build/make.log (with GCC /usr/bin/gcc-8)
* Trying to dkms install falco module with GCC /usr/bin/gcc-6
DIRECTIVE: MAKE="'/tmp/falco-dkms-make'"
* Running dkms build failed, couldn't find /var/lib/dkms/falco/17f5df52a7d9ed6bb12d3b1768460def8439936d/build/make.log (with GCC /usr/bin/gcc-6)
* Trying to dkms install falco module with GCC /usr/bin/gcc-5
DIRECTIVE: MAKE="'/tmp/falco-dkms-make'"
* Running dkms build failed, couldn't find /var/lib/dkms/falco/17f5df52a7d9ed6bb12d3b1768460def8439936d/build/make.log (with GCC /usr/bin/gcc-5)
Consider compiling your own falco driver and loading it or getting in touch with the Falco community
Tue Jul 20 22:29:24 2021: Falco version 0.29.1 (driver version 17f5df52a7d9ed6bb12d3b1768460def8439936d)
Tue Jul 20 22:29:24 2021: Falco initialized with configuration file /etc/falco/falco.yaml
Tue Jul 20 22:29:24 2021: Loading rules from file /etc/falco/falco_rules.yaml:
Tue Jul 20 22:29:24 2021: Loading rules from file /etc/falco/falco_rules.local.yaml:
Tue Jul 20 22:29:25 2021: Unable to load the driver.
Tue Jul 20 22:29:25 2021: Runtime error: error opening device /host/dev/falco0. Make sure you have root credentials and that the falco module is loaded.. Exiting.

ホスト側にカーネルヘッダーを入れればよいのか確認する。

ノードグループを作成する。

cat << "EOF" > managed-ng-2.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: falco3
  region: ap-northeast-1

managedNodeGroups:
  - name: managed-ng-2
    minSize: 2
    maxSize: 2
    desiredCapacity: 2
    privateNetworking: true
    iam:
      attachPolicyARNs:
        - arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy
        - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly
        - arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
    preBootstrapCommands:
      - |
        #!/bin/bash

        set -o errexit
        set -o pipefail
        set -o nounset

        yum -y install kernel-devel-$(uname -r)
EOF
eksctl create nodegroup -f managed-ng-2.yaml

ノードを確認する。

$ k get node -o wide
NAME                                             STATUS   ROLES    AGE    VERSION                     INTERNAL-IP   EXTERNAL-IP   OS-IMAGE         KERNEL-VERSION                CONTAINER-RUNTIME
ip-10-0-67-203.ap-northeast-1.compute.internal   Ready    <none>   2m6s   v1.21.2-13+d2965f0db10712   10.0.67.203   <none>        Amazon Linux 2   5.4.129-62.227.amzn2.x86_64   docker://19.3.13
ip-10-0-72-194.ap-northeast-1.compute.internal   Ready    <none>   14m    v1.21.2-13+d2965f0db10712   10.0.72.194   <none>        Amazon Linux 2   5.4.129-62.227.amzn2.x86_64   docker://19.3.13
ip-10-0-98-194.ap-northeast-1.compute.internal   Ready    <none>   17m    v1.21.2-13+d2965f0db10712   10.0.98.194   <none>        Amazon Linux 2   5.4.129-62.227.amzn2.x86_64   docker://19.3.13
ip-10-0-99-60.ap-northeast-1.compute.internal    Ready    <none>   2m5s   v1.21.2-13+d2965f0db10712   10.0.99.60    <none>        Amazon Linux 2   5.4.129-62.227.amzn2.x86_64   docker://19.3.13

新しいカーネルヘッダーが入っているはずのノードでは成功する。

$ k -n falco get po
NAME          READY   STATUS             RESTARTS   AGE
falco-4xfzb   1/1     Running            0          113s
falco-s5g6m   0/1     CrashLoopBackOff   7          14m
falco-svvtj   0/1     CrashLoopBackOff   8          16m
falco-xbf8h   1/1     Running            0          113s

カーネルモジュールがビルドされてインストールされている。

$ k -n falco logs falco-4xfzb
* Setting up /usr/src links from host
* Running falco-driver-loader for: falco version=0.29.1, driver version=17f5df52a7d9ed6bb12d3b1768460def8439936d
* Running falco-driver-loader with: driver=module, compile=yes, download=yes
* Unloading falco module, if present
* Trying to load a system falco module, if present
* Looking for a falco module locally (kernel 5.4.129-62.227.amzn2.x86_64)
* Trying to download a prebuilt falco module from https://download.falco.org/driver/17f5df52a7d9ed6bb12d3b1768460def8439936d/falco_amazonlinux2_5.4.129-62.227.amzn2.x86_64_1.ko
curl: (22) The requested URL returned error: 404 
Unable to find a prebuilt falco module
* Trying to dkms install falco module with GCC /usr/bin/gcc
DIRECTIVE: MAKE="'/tmp/falco-dkms-make'"

Kernel preparation unnecessary for this kernel.  Skipping...

Building module:
cleaning build area...
'/tmp/falco-dkms-make'....
cleaning build area...

DKMS: build completed.

falco.ko:
Running module version sanity check.
 - Original module
   - No original module exists within this kernel
 - Installation
   - Installing to /lib/modules/5.4.129-62.227.amzn2.x86_64/kernel/extra/

depmod....

DKMS: install completed.
* falco module installed in dkms, trying to insmod
* Success: falco module found and loaded in dkms
Tue Jul 20 23:21:29 2021: Falco version 0.29.1 (driver version 17f5df52a7d9ed6bb12d3b1768460def8439936d)
Tue Jul 20 23:21:29 2021: Falco initialized with configuration file /etc/falco/falco.yaml
Tue Jul 20 23:21:29 2021: Loading rules from file /etc/falco/falco_rules.yaml:
Tue Jul 20 23:21:30 2021: Loading rules from file /etc/falco/falco_rules.local.yaml:
Tue Jul 20 23:21:30 2021: Starting internal webserver, listening on port 8765
23:21:30.276773000: Notice Privileged container started (user=root user_loginuid=0 command=container:a0a78572add8 k8s.ns=kube-system k8s.pod=kube-proxy-4tpvz container=a0a78572add8 image=602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/eks/pause:3.1-eksbuild.1) k8s.ns=kube-system k8s.pod=kube-proxy-4tpvz container=a0a78572add8
23:21:30.278237000: Notice Privileged container started (user=root user_loginuid=0 command=container:8b6c4513d244 k8s.ns=kube-system k8s.pod=aws-node-wq455 container=8b6c4513d244 image=602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/eks/pause:3.1-eksbuild.1) k8s.ns=kube-system k8s.pod=aws-node-wq455 container=8b6c4513d244
23:21:30.286672000: Notice Privileged container started (user=<NA> user_loginuid=0 command=container:bc14e1c8f69f k8s.ns=falco k8s.pod=falco-4xfzb container=bc14e1c8f69f image=602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/eks/pause:3.1-eksbuild.1) k8s.ns=falco k8s.pod=falco-4xfzb container=bc14e1c8f69f

念のため、Init Container方式でも問題ないか確認する。

まずFalcoを削除する。

helm delete -n falco falco

既にカーネルモジュールがインストールされてしまったので、マネコンから全ノードを削除してASGに再作成させる。

ノードが新しくなったことを確認する。

$ k get node
NAME                                              STATUS   ROLES    AGE     VERSION
ip-10-0-109-245.ap-northeast-1.compute.internal   Ready    <none>   95s     v1.21.2-13+d2965f0db10712
ip-10-0-69-39.ap-northeast-1.compute.internal     Ready    <none>   3m16s   v1.21.2-13+d2965f0db10712
ip-10-0-75-251.ap-northeast-1.compute.internal    Ready    <none>   48s     v1.21.2-13+d2965f0db10712
ip-10-0-99-198.ap-northeast-1.compute.internal    Ready    <none>   3m8s    v1.21.2-13+d2965f0db10712

Init Containerを使う方法でFalcoをインストールする。

cat << EOF > values.yaml
image:
  repository: falcosecurity/falco-no-driver

extraInitContainers:
  - name: driver-loader
    image: docker.io/falcosecurity/falco-driver-loader:0.29.1
    imagePullPolicy: Always
    securityContext:
      privileged: true
    volumeMounts:
      - mountPath: /host/proc
        name: proc-fs
        readOnly: true
      - mountPath: /host/boot
        name: boot-fs
        readOnly: true
      - mountPath: /host/lib/modules
        name: lib-modules
      - mountPath: /host/usr
        name: usr-fs
        readOnly: true
      - mountPath: /host/etc
        name: etc-fs
        readOnly: true
EOF
helm upgrade --install falco falcosecurity/falco -n falco --create-namespace -f values.yaml

この方法でも問題ないことを確認。

$ k -n falco get po
NAME          READY   STATUS                  RESTARTS   AGE
falco-9f6l5   1/1     Running                 0          3m28s
falco-9kpkm   0/1     Init:CrashLoopBackOff   4          3m28s
falco-l4kgj   0/1     Init:Error              5          3m28s
falco-s4vr8   1/1     Running                 0          3m28s

Falcoをyumで入れる場合のPodとの連携

Falcoをyumで入れた場合に、Helmで入れたekscloudwatchfalcosidekickとの連携を確認したメモ。

クラスターの作成

コンポーネント バージョン
EKS 1.19
プラットフォームバージョン eks.5
Falco 0.29.1
falcosidekick 2.23.1
falcosidekickチャート 0.3.10
ekscloudwatch ekscloudwatch-0.3

クラスターの準備

1.19でクラスターを作成する。ノードなしで作成する。

cat << EOF > cluster.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: falco
  region: ap-northeast-1
  version: "1.19"
vpc:
  cidr: "10.2.0.0/16"

availabilityZones:
  - ap-northeast-1a
  - ap-northeast-1c

cloudWatch:
  clusterLogging:
    enableTypes: ["*"]

iam:
  withOIDC: true
EOF
eksctl create cluster -f cluster.yaml

ノードグループを作成する。

cat << "EOF" > managed-ng-2.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: falco
  region: ap-northeast-1

managedNodeGroups:
  - name: managed-ng-2
    minSize: 2
    maxSize: 2
    desiredCapacity: 2
    privateNetworking: true
    preBootstrapCommands:
      - |
        #!/bin/bash

        set -o errexit
        set -o pipefail
        set -o nounset

        rpm --import https://falco.org/repo/falcosecurity-3672BA8F.asc
        curl -s -o /etc/yum.repos.d/falcosecurity.repo https://falco.org/repo/falcosecurity-rpm.repo
        yum -y install kernel-devel-$(uname -r)
        yum -y install falco
        sed -i -e '/syslog_output/ { N; s/enabled: true/enabled: false/ }' /etc/falco/falco.yaml
        systemctl enable falco
        systemctl start falco
EOF
eksctl create nodegroup -f managed-ng-2.yaml

falcosidekickの導入

ノードポートを指定してfalcosidekickを導入する。

$ helm search repo falco
NAME                            CHART VERSION   APP VERSION DESCRIPTION
falcosecurity/falco             1.15.3          0.29.1      Falco
falcosecurity/falco-exporter    0.5.1           0.5.0       Prometheus Metrics Exporter for Falco output ev...
falcosecurity/falcosidekick     0.3.10          2.23.1      A simple daemon to help you with falco's outputs
stable/falco                    1.1.8           0.0.1       DEPRECATED - incubator/falco
helm upgrade --install falcosidekick falcosecurity/falcosidekick -n falco --create-namespace \
  --set webui.enabled=true \
  --set service.type=NodePort

NodePortの番号を確認する。

$ k -n falco get svc
NAME               TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
falcosidekick      NodePort    172.20.139.21    <none>        2801:31490/TCP   5d1h
falcosidekick-ui   ClusterIP   172.20.168.108   <none>        2802/TCP         5d1h

ノードにSSMでログインし、/etc/falco/falco.yamlを修正する。

# Whether to output events in json or text
json_output: true

http_output:
  enabled: true
  url: "http://localhost:31490/"

なお、このようにNodePort経由ではなく、ClusterIPを直接指定してもいいかも知れない。

この修正は新しいノードに反映されないので、恒久化するための対応が必要。

  • ユーザーデータでsedで頑張る
  • S3に置いて、ユーザーデータでs3からダウンロードするようにする

ekscloudwatchの導入

ConfigMapを作成する。

cat << "EOF" > ekscloudwatch-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: ekscloudwatch-config
  namespace: falco
data:
  # Required: Endpoint to forward audit events to, such as Sysdig Secure agent
  # The agent must expose a k8s audit server (k8s_audit_server_port must be configured in the agent as well)
  endpoint: "http://falco:8765/k8s-audit"

  # Required: Cloudwatch polling interval
  cw_polling: "1m"

  # Required: CloudWatch query filter
  cw_filter: '{ $.sourceIPs[0] != "::1" && $.sourceIPs[0] != "127.0.0.1" }'

  # Optional: both the EKS cluster name and region must be set
  # This can be omitted if the EC2 instance can perform the ec2metadata and ec2:DescribeInstances action
  cluster_name: "falco"
  aws_region: "ap-northeast-1"
EOF

ここのendpoint: "http://falco:8765/k8s-audit"はDeploymentでENDPOINT環境変数を定義するので使わない。

ConfiMapを作成する。

k apply -f ekscloudwatch-config.yaml

ServiceAccountを作成する。

k -n falco create sa eks-cloudwatch

IRSAで必要な権限を与える。

eksctl create iamserviceaccount \
    --name eks-cloudwatch \
    --namespace falco \
    --cluster ekscloudwatch \
    --attach-policy-arn arn:aws:iam::aws:policy/CloudWatchReadOnlyAccess \
    --override-existing-serviceaccounts \
    --approve

deployment.yamlもNamespaceを修正し、ServiceAccoutNameを指定する。ノードのIPを取得して環境変数でエンドポイントで設定するようにする。

cat << EOF > ekscloudwatch-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: eks-cloudwatch
  namespace: falco
spec:
  minReadySeconds: 5
  replicas: 1
  selector:
    matchLabels:
      app: eks-cloudwatch
  template:
    metadata:
      labels:
        app: eks-cloudwatch
    spec:
      serviceAccountName: eks-cloudwatch
      containers:
        - image: sysdiglabs/k8sauditlogforwarder:ekscloudwatch-0.3
          imagePullPolicy: Always
          name: eks-cloudwatch-container
          env:
            - name: NODEIP
              valueFrom:
                fieldRef:
                  fieldPath: status.hostIP
            - name: ENDPOINT
              value: "http://$(NODEIP):8765/k8s-audit"
            - name: CLUSTER_NAME
              valueFrom:
                configMapKeyRef:
                  name: ekscloudwatch-config
                  key: cluster_name
            - name: AWS_REGION
              valueFrom:
                configMapKeyRef:
                  name: ekscloudwatch-config
                  key: aws_region
            - name: CW_POLLING
              valueFrom:
                configMapKeyRef:
                  name: ekscloudwatch-config
                  key: cw_polling
            - name: CW_FILTER
              valueFrom:
                configMapKeyRef:
                  name: ekscloudwatch-config
                  key: cw_filter
EOF

デプロイする。

k apply -f ekscloudwatch-deployment.yaml

Podが動いていることを確認する。

$ k -n falco get po
NAME                                READY   STATUS    RESTARTS   AGE
eks-cloudwatch-74c984f494-ztkrq     1/1     Running   0          40s
falcosidekick-586c44fc5c-8n89g      1/1     Running   0          40s
falcosidekick-586c44fc5c-p254p      1/1     Running   0          40s
falcosidekick-ui-5d6b4bd557-s4k7t   1/1     Running   0          40s

確認

ポートフォワードでfalcosidekick-uiにアクセスする。

k -n falco port-forward svc/falcosidekick-ui 2802

aws-auth ConfigMapを適当に修正してみる。こちらを参照。

k -n kube-system edit cm aws-auth

通知されない!これはFalcoルール作る必要がありそう。

ConfigMapを作成してみる。

cat << "EOF" > my-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: my-config
  namespace: default
data:
  ui.properties: |
    color.good=purple
    color.bad=yellow
    allow.textmode=true
  access.properties: |
    aws_access_key_id = MY-ID
    aws_secret_access_key = MY-KEY
EOF
k apply -f my-config.yaml

これは通知された。

f:id:sotoiwa:20210719094159p:plain

Falco設定の永続化

ノードが再起動してもいいように、Falcoの設定をS3に置く。

バケットを作成する。

ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account)
BUCKET_NAME="eks-falco-config-${ACCOUNT_ID}"
AWS_REGION=$(aws configure get region)
aws s3api create-bucket \
    --bucket ${BUCKET_NAME} \
    --region ${AWS_REGION} \
    --create-bucket-configuration LocationConstraint=${AWS_REGION}

バージョニングとSSE-S3でのバケットのデフォルト暗号化を有効にする。要件に応じて設定する。

aws s3api put-bucket-versioning \
  --bucket ${BUCKET_NAME} \
  --versioning-configuration Status=Enabled
aws s3api put-bucket-encryption \
    --bucket ${BUCKET_NAME} \
    --server-side-encryption-configuration '{"Rules":[{"ApplyServerSideEncryptionByDefault":{"SSEAlgorithm":"AES256"}}]}'

このバケットへのアクセスを許可するポリシーを作成する。

cat <<EOF > eks-falco-config-policy.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:GetObject",
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::${BUCKET_NAME}",
                "arn:aws:s3:::${BUCKET_NAME}/*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": "s3:ListAllMyBuckets",
            "Resource": "*"
        }
    ]
}
EOF
aws iam create-policy \
    --policy-name eks-falco-config-policy \
    --policy-document file://eks-falco-config-policy.json

このポリシーをインスタンスロールにアタッチする。

また、一時的にAmazonS3FullAccessもアタッチする。

インスタンスからS3にファイルをアップロードする。

ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account)
BUCKET_NAME="eks-falco-config-${ACCOUNT_ID}"
aws s3 cp falco.yaml s3://${BUCKET_NAME}/

アップロードできたらAmazonS3FullAccessをデタッチする。

ダウンロードできることを確認する。

aws s3 cp s3://${BUCKET_NAME}/falco.yaml /tmp/

ノードグループを作成する。

ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account)
cat << EOF > managed-ng-1.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: falco
  region: ap-northeast-1

managedNodeGroups:
  - name: managed-ng-1
    minSize: 2
    maxSize: 2
    desiredCapacity: 2
    privateNetworking: true
    iam:
      attachPolicyARNs:
        - arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy
        - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly
        - arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
        - arn:aws:iam::${ACCOUNT_ID}:policy/eks-falco-config-policy
EOF
cat << "EOF" >> managed-ng-1.yaml
    preBootstrapCommands:
      - |
        #!/bin/bash

        set -o errexit
        set -o pipefail
        set -o nounset

        rpm --import https://falco.org/repo/falcosecurity-3672BA8F.asc
        curl -s -o /etc/yum.repos.d/falcosecurity.repo https://falco.org/repo/falcosecurity-rpm.repo
        yum -y install kernel-devel-$(uname -r)
        yum -y install falco
        ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account)
        BUCKET_NAME="eks-falco-config-${ACCOUNT_ID}"
        aws s3 cp s3://${BUCKET_NAME}/falco.yaml /etc/falco/falco.yaml
        systemctl enable falco
        systemctl start falco
EOF
eksctl create nodegroup -f managed-ng-1.yaml

ノードが作成できたら古いノードグループを削除する。

eksctl delete nodegroup --cluster falco managed-ng-2

新しい−ノードを確認する。

$ k get node
NAME                                             STATUS   ROLES    AGE     VERSION
ip-10-2-112-95.ap-northeast-1.compute.internal   Ready    <none>   5m58s   v1.19.6-eks-49a6c0
ip-10-2-75-246.ap-northeast-1.compute.internal   Ready    <none>   5m59s   v1.19.6-eks-49a6c0

ポートフォワードでfalcosidekick-uiにアクセスする。

k -n falco port-forward svc/falcosidekick-ui 2802

ConfigMapを作ってみる。

kubectl create configmap my-config-2 --from-literal=key1=config1 --from-literal=key2=config2

しばし待ち、だいたい1分位で通知されることを確認する。

f:id:sotoiwa:20210720184141p:plain

audit-bridgeを試す

ekscloudwatchと比較のためfalco-eks-audit-bridgeを試したメモ。

コンポーネント バージョン
EKS 1.19
プラットフォームバージョン eks.5
Falco 0.29.1
Falcoチャート 1.15.3
audit-bridge v1.0.3

参考リンク

クラスターの準備

1.19でクラスターを作成する。

cat << EOF > cluster.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: audit-bridge
  region: ap-northeast-1
  version: "1.19"
vpc:
  cidr: "10.0.0.0/16"

availabilityZones:
  - ap-northeast-1a
  - ap-northeast-1c

managedNodeGroups:
  - name: managed-ng-1
    minSize: 2
    maxSize: 2
    desiredCapacity: 2
    privateNetworking: true

cloudWatch:
  clusterLogging:
    enableTypes: ["*"]

iam:
  withOIDC: true
EOF
eksctl create cluster -f cluster.yaml

Falcoのデプロイ

初期化コンテナを使う方法でFalcoをインストールする。auditLog.enabled=trueとすることでServiceが作られる。

cat << EOF > values.yaml
image:
  repository: falcosecurity/falco-no-driver

auditLog:
  enabled: true

extraInitContainers:
  - name: driver-loader
    image: docker.io/falcosecurity/falco-driver-loader:0.29.1
    imagePullPolicy: Always
    securityContext:
      privileged: true
    volumeMounts:
      - mountPath: /host/proc
        name: proc-fs
        readOnly: true
      - mountPath: /host/boot
        name: boot-fs
        readOnly: true
      - mountPath: /host/lib/modules
        name: lib-modules
      - mountPath: /host/usr
        name: usr-fs
        readOnly: true
      - mountPath: /host/etc
        name: etc-fs
        readOnly: true

falcosidekick:
  enabled: true
  webui:
    enabled: true
EOF
$ helm upgrade --install falco falcosecurity/falco -n falco --create-namespace -f values.yaml
Release "falco" does not exist. Installing it now.
NAME: falco
LAST DEPLOYED: Wed Jul 14 04:32:51 2021
NAMESPACE: falco
STATUS: deployed
REVISION: 1
NOTES:
Falco agents are spinning up on each node in your cluster. After a few
seconds, they are going to start monitoring your containers looking for
security issues.


No further action should be required.

PodとServiceを確認する。

$ k -n falco get po
NAME                                      READY   STATUS    RESTARTS   AGE
falco-falcosidekick-5cbc97b7d9-l7flk      1/1     Running   0          3m3s
falco-falcosidekick-5cbc97b7d9-nshb2      1/1     Running   0          3m3s
falco-falcosidekick-ui-79c4d8b546-zz62f   1/1     Running   0          3m3s
falco-m4n5s                               1/1     Running   0          3m3s
falco-vd55b                               1/1     Running   0          3m3s
$ k -n falco get svc
NAME                     TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
falco                    ClusterIP   172.20.101.21    <none>        8765/TCP   3m13s
falco-falcosidekick      ClusterIP   172.20.103.152   <none>        2801/TCP   3m13s
falco-falcosidekick-ui   ClusterIP   172.20.30.23     <none>        2802/TCP   3m13s

ポートフォワードでfalcosidekick-uiにアクセスする。

k -n falco port-forward svc/falco-falcosidekick-ui 2802

ログのS3への転送

コントロールプレーンのログをS3に転送するように設定する。

バケットを作成する。

ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account)
BUCKET_NAME="eks-audit-bridge-bucket-${ACCOUNT_ID}"
AWS_REGION=$(aws configure get region)
aws s3api create-bucket \
    --bucket ${BUCKET_NAME} \
    --region ${AWS_REGION} \
    --create-bucket-configuration LocationConstraint=${AWS_REGION}

SSE-S3でのバケットのデフォルト暗号化を有効にする。要件に応じて設定する。

aws s3api put-bucket-encryption \
    --bucket ${BUCKET_NAME} \
    --server-side-encryption-configuration '{"Rules":[{"ApplyServerSideEncryptionByDefault":{"SSEAlgorithm":"AES256"}}]}'

Kinesisのコンソールでデリバリーストリームを作成する。

項目
Delivery stream name eks-audit-bridge-stream
Source Direct PUT or other sources
Enable server-side encryption for source records in delivery stream チェックしない
Data transformation Disabled
Record format conversion Disabled
Destination Amazon S3
S3 bucket 上で作成したバケットを選択
S3 prefix 指定しない
S3 error prefix 指定しない
Buffer size 5 MiB
Buffer interval 300 seconds
S3 compression GZIP
S3 encryption Disabled
Error logging Enabled
IAM role Create or update IAM role KinesisFirehoseServiceRole-...

サブスクリプションフィルターで使用するIAMロールを作成する。マネコンだと信頼されたエンティティにCloudWatch Logsが指定できないので、CLIで作成する。

ポリシーを作成する。

STREAM_NAME="eks-audit-bridge-stream"
cat <<EOF >eks-audit-bridge-firehose-policy.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "firehose:PutRecord",
                "firehose:PutRecordBatch"
            ],
            "Resource": [
                "arn:aws:firehose:${AWS_REGION}:${ACCOUNT_ID}:deliverystream/${STREAM_NAME}"
            ]
        }
    ]
}
EOF
aws iam create-policy \
    --policy-name eks-audit-bridge-firehose-policy \
    --policy-document file://eks-audit-bridge-firehose-policy.json

ロールを作成する。

cat <<EOF > eks-audit-bridge-firehose-role-trust.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "logs.ap-northeast-1.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
EOF
aws iam create-role \
    --role-name eks-audit-bridge-firehose-role \
    --assume-role-policy-document file://eks-audit-bridge-firehose-role-trust.json

ロールにポリシーをアタッチする。

aws iam attach-role-policy \
    --role-name eks-audit-bridge-firehose-role \
    --policy-arn arn:aws:iam::${ACCOUNT_ID}:policy/eks-audit-bridge-firehose-policy

マネコンからだとLambdaとElasticsearchのサブスクリプションフィルターしか作成できないので、CLIで作成する必要がある。

aws logs put-subscription-filter \
    --log-group-name "/aws/eks/audit-bridge/cluster" \
    --filter-name "eks-audit-bridge-firehose" \
    --filter-pattern "" \
    --destination-arn "arn:aws:firehose:${AWS_REGION}:${ACCOUNT_ID}:deliverystream/${STREAM_NAME}" \
    --role-arn "arn:aws:iam::${ACCOUNT_ID}:role/eks-audit-bridge-firehose-role"

しばらく待ってログが送られることを確認する。

イメージのビルド

イメージのビルド日が古いので、ビルドし直す。

リポジトリをクローンする。

git clone https://github.com/xebia/falco-eks-audit-bridge.git
cd falco-eks-audit-bridge

ビルドエラーになったので、Dockerfileにgo env -w GOPROXY=directを追加した。

RUN GOPROXY=direct go mod download && \
    CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-extldflags "-static"' -o falco-eks-audit-bridge -v
docker build -t sotoiwa540/falco-eks-audit-bridge:0.0.1 .
docker push sotoiwa540/falco-eks-audit-bridge:0.0.1

イメージを確認する。大きくなってるが、ベースイメージが大きくなったのか。

$ docker images | grep falco-eks-audit-bridge
sotoiwa540/falco-eks-audit-bridge                          0.0.1               fca66666b7a0   2 minutes ago   35.1MB
xebia/falco-eks-audit-bridge                               v1.0.3              378b55c00b1b   18 months ago   17.7MB

audit-bridgeのデプロイ

ServiceAccountを作成する。

k -n falco create sa audit-bridge

IRSAで必要な権限を与える。本来はバケットを限定するべき。

eksctl create iamserviceaccount \
    --name audit-bridge \
    --namespace falco \
    --cluster audit-bridge \
    --attach-policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess \
    --override-existing-serviceaccounts \
    --approve

READMEによると、環境変数を2つ渡すだけ。

cat << EOF > audit-bridge-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: audit-bridge
  name: audit-bridge
  namespace: falco
spec:
  replicas: 1
  selector:
    matchLabels:
      app: audit-bridge
  template:
    metadata:
      labels:
        app: audit-bridge
    spec:
      serviceAccountName: audit-bridge
      containers:
      - image: sotoiwa540/falco-eks-audit-bridge:0.0.1
        name: falco-eks-audit-bridge
        env:
        - name: BUCKET
          value: ${BUCKET_NAME}
        - name: FALCO_ENDPOINT
          value: http://falco:8765/k8s-audit
EOF

デプロイする。

k apply -f audit-bridge-deployment.yaml

Podが動いていることを確認する。

$ k -n falco get po
NAME                                      READY   STATUS    RESTARTS   AGE
audit-bridge-8467568967-f5h5w             1/1     Running   0          15s
falco-falcosidekick-5cbc97b7d9-l7flk      1/1     Running   0          58m
falco-falcosidekick-5cbc97b7d9-nshb2      1/1     Running   0          58m
falco-falcosidekick-ui-79c4d8b546-zz62f   1/1     Running   0          58m
falco-m4n5s                               1/1     Running   0          58m
falco-vd55b                               1/1     Running   0          58m

しかしこれだと権限が不足している。

$ k -n falco logs audit-bridge-8467568967-f5h5w
Started monitoring services
Processing: 2021/07/13/20/eks-audit-bridge-stream-1-2021-07-13-20-18-01-bca35eb8-eabc-456e-9250-382653c8de2a.gz
Could not copy file to processed folder:
AccessDenied: Access Denied
        status code: 403, request id: W8F84855ZX6DCQBM, host id: 14NfekBhqqWdfG7HNf5ermqDiVlFlVeEd2efRVoJH0aOxdxu0iUTn5FI/MHPsKuMfFp8efAMaOI=
Processing: 2021/07/13/20/eks-audit-bridge-stream-1-2021-07-13-20-23-05-25e24f0c-864e-4fde-95d1-26552b0c0566.gz

試しにAmazonS3FullAccessを与えると動作した。権限の精査が必要。

$ k -n falco logs audit-bridge-8467568967-mrctc
Started monitoring services
Processing: 2021/07/13/20/eks-audit-bridge-stream-1-2021-07-13-20-23-05-25e24f0c-864e-4fde-95d1-26552b0c0566.gz
Processing: 2021/07/13/20/eks-audit-bridge-stream-1-2021-07-13-20-43-11-585ffcfd-ae65-4776-9ac5-dc7d7a78d9e3.gz
No new Firehose events found, waiting for next check interval.

処理済みのログをprocessed/フォルダに移動するという処理をやっているようだ。そういえば、ekscloudwatchはどの範囲のログを送っているのかよくわからなかった。

f:id:sotoiwa:20210714060200p:plain

以下を参考にテストする。

テストのため、AWSアクセスキーを含むConfigMapを作成する。

cat << "EOF" > my-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: my-config
  namespace: default
data:
  ui.properties: |
    color.good=purple
    color.bad=yellow
    allow.textmode=true
  access.properties: |
    aws_access_key_id = MY-ID
    aws_secret_access_key = MY-KEY
EOF
k apply -f my-config.yaml

しばらくログを見ておく。

以下のログが出た。falcosidekickを有効にしたのでjson形式になっている。

{"output":"20:52:49.501943040: Notice K8s ConfigMap Created (user=kubernetes-admin configmap=my-config ns=default resp=201 decision=allow reason=)","priority":"Notice","rule":"K8s ConfigMap Created","time":"2021-07-13T20:52:49.501943040Z", "output_fields": {"jevt.time":"20:52:49.501943040","ka.auth.decision":"allow","ka.auth.reason":"","ka.response.code":"201","ka.target.name":"my-config","ka.target.namespace":"default","ka.user.name":"kubernetes-admin"}}

f:id:sotoiwa:20210714060246p:plain