alt text

Tulisan ini dibuat sebagai catatan saat mencoba Falco untuk memantau container secara runtime di Amazon EKS.

Apa itu Falco?

Falco adalah aplikasi runtime security yang berlisensi open-source dan gratis dikembangkan Sysdic, Inc. Saat tulisan ini dibuat, Falco masuk ke dalam CNCF project dengan status inkubasi.


Manfaat Falco

Falco dapat mendeteksi dan mengirimkan notifikasi apabila ada aktifitas di dalam container yang dianggap mencurigakan sesuai dengan aturan atau rule yang sudah dibuat sebelumnya. Falco dapat memantau system call Linux dengan menggunakan kernel module atau eBPF probe dari kernel secara runtime.

Falco memiliki beberapa rule bawaan, diantaranya:

  • Privilege escalation menggunakan privileged containers
  • Perubahan namespace menggunakan aplikasi seperti setns
  • Aktivitas baca atau tulis ke direktori terkenal seperti /etc, /usr/bin, /usr/sbin, dan lain-lain
  • Pembuatan symlinks
  • Perubahan ownership dan mode
  • Koneksi jaringan yang tidak terduga atau perubahan socket
  • Memunculkan process menggunakan execve
  • Mengeksekusi shell binaries seperti sh, bash, csh, zsh, dan lain-lain
  • Mengeksekusi SSH binaries seperti ssh, scp, sftp, dan lain-lain
  • Perubahan Linux coreutils executables
  • Perubahan login binaries
  • Perubahan shadowutil or passwd executables seperti shadowconfig, pwck, chpasswd, getpasswd, change, useradd, dan lain-lain.

Penjelasan singkat untuk Falco sudah cukup, untuk ingin tahu lebih detil bisa lihat dokumentasi Falco di https://falco.org/docs/ dan daftar lengkap rule bisa lihat di https://github.com/falcosecurity/falco/tree/master/rules.


Pemasangan Falco

Berikut adalah gambaran umum arsitektur yang akan dideploy pada tulisan ini.

alt text

Kubernetes Cluster

Sebelum dapat memasang Falco, terlebih dahulu perlu mendeploy sebuah kluster Kubernetes. Di tulisan ini, saya memilih Amazon EKS dengan bantuan aplikasi eksctl dengan CloudFormation. Berikut adalah manifest yang digunakan untuk mendeploy Kubernetes kluster.

falco-cluster.yml

apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: falco-cluster
  region: ap-southeast-1
  version: "1.19"

managedNodeGroups:
  - name: falco-node
    minSize: 1
    maxSize: 2
    instanceType: t3a.small
    amiFamily: AmazonLinux2
    volumeSize: 30
    volumeEncrypted: true
    desiredCapacity: 1

Jalankan perintah berikut untuk membuat kluster Kubernetes.

fadhil@thomas:~$ eksctl create cluster -f falco-cluster.yml

Setelah proses pembuatan kluster berhasil, maka akan tersimpan file kube config di path ~/.kube/config

Log Forwading

Log yang dihasilkan oleh Falco akan diteruskan ke Amazon CloudWatch agar terpusat dan nantinya akan memudahkan apabila ingin meneruskannya lagi ke SIEM atau membuat alerting.

IAM Permission

Untuk dapat meneruskan log ke Amazon CloudWatch dibutuhkan perizinan, maka perlu membuat IAM Policy.

iam_role_policy.json

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents",
                "logs:DescribeLogStreams"
            ],
            "Resource": [
                "arn:aws:logs:*:*:*"
            ]
        }
    ]
}

Jalankan perintah berikut untuk membuat policy dengan nama EKS-CloudWatchLogs.

fadhil@thomas:~$ aws iam create-policy --policy-name EKS-CloudWatchLogs --policy-document file://iam_role_policy.json

Kemudian tempelkan policy EKS-CloudWatchLogs ke role EKS NodeGroup. Nama role dari EKS NodeGroup dapat dilihat dengan cara berikut:

  • Buka halaman Amazon Elastic Kubernetes Service.
  • Pilih falco-cluster di daftar Clusters.
  • Pilih falco-node di Node groups pada tab Compute.
  • Akan terlihat nama role di bagian Node IAM role ARN.

Jalankan perintah berikut untuk menambahkan policy EKS-CloudWatchLogs ke EKS NodeGroup role.

fadhil@thomas:~$ aws iam attach-role-policy --role-name EKS-NODE-ROLE-NAME --policy-arn `aws iam list-policies | jq -r '.[][] | select(.PolicyName == "EKS-CloudWatchLogs") | .Arn'`

Fluent Bit DaemonSet

Setelah menyiapkan IAM Permission, kemudian dapat mendeploy Fluent Bit. Berikut adalah manifest dari Fluent Bit.

fluent-bit-configmap.yml

apiVersion: v1
kind: ConfigMap
metadata:
  name: fluent-bit-config
  namespace: logging
  labels:
    app.kubernetes.io/name: fluent-bit
data:
  fluent-bit.conf: |
    [SERVICE]
        Parsers_File  parsers.conf
    [INPUT]
        Name              tail
        Tag               falco.*
        Path              /var/log/containers/falco*.log
        Parser            falco
        DB                /var/log/flb_falco.db
        Mem_Buf_Limit     5MB
        Skip_Long_Lines   On
        Refresh_Interval  10
    [OUTPUT]
        Name cloudwatch
        Match falco.**
        region ap-southeast-1
        log_group_name falco
        log_stream_name alerts
        auto_create_group true    
  parsers.conf: |
    [PARSER]
        Name        falco
        Format      json
        Time_Key    time
        Time_Format %Y-%m-%dT%H:%M:%S.%L
        Time_Keep   Off
        # Command      |  Decoder | Field | Optional Action
        # =============|==================|=================
        Decode_Field_As   json    log    

fluent-bit-daemonset.yml

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluent-bit
  namespace: logging
  labels:
    app.kubernetes.io/name: fluent-bit
spec:
  selector:
    matchLabels:
      name: fluent-bit
  template:
    metadata:
      labels:
        name: fluent-bit
    spec:
      serviceAccountName: fluent-bit
      containers:
      - name: aws-for-fluent-bit
        image: amazon/aws-for-fluent-bit:1.2.2
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
        - name: fluent-bit-config
          mountPath: /fluent-bit/etc/
        - name: mnt
          mountPath: /mnt
          readOnly: true
        resources:
          limits:
            memory: 500Mi
          requests:
            cpu: 500m
            memory: 100Mi
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers
      - name: fluent-bit-config
        configMap:
          name: fluent-bit-config
      - name: mnt
        hostPath:
          path: /mnt

fluent-bit-service-account.yml

apiVersion: v1
kind: Namespace
metadata:
  name: logging
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: fluent-bit
  namespace: logging
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: pod-log-reader
rules:
- apiGroups: [""]
  resources:
  - namespaces
  - pods
  verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: pod-log-crb
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: pod-log-reader
subjects:
- kind: ServiceAccount
  name: fluent-bit
  namespace: logging

Siapkan semua manifest fluent-bit ke dalam satu folder fluent-bit. Jalankan perintah berikut untuk mendeploy fluent-bit.

fadhil@thomas:~$ kubectl apply -f fluent-bit/

Falco DaemonSet

Ada beberapa cara untuk memasang Falco. Pada tulisan ini saya akan mencoba menggunakan Helm Chart. Terlebih dahulu unduh file values.yaml dari https://github.com/falcosecurity/charts/blob/master/falco/values.yaml. Ubah json_output: false menjadi json_output: true untuk menjadikan format output log Falco menjadi json.

Jalan perintah berikut untuk memasang Falco di kluster Kubernetes yang sudah dibuat sebelumnya.

fadhil@thomas:~$ helm install falco -f values.yaml falcosecurity/falco --namespace falco --create-namespace

atau bisa juga dengan command --set

fadhil@thomas:~$ helm install falco --set falco.json_output=true falcosecurity/falco --namespace falco --create-namespace

Jalankan perintah berikut untuk memeriksa apakah Falco sudah berjalan dengan sukses.

fadhil@thomas:~$ kubectl get pod -n falco
NAME          READY   STATUS    RESTARTS   AGE
falco-9q9hc   1/1     Running   0          26m

Monitored App

Untuk uji coba, saya akan mendeploy dvwa [https://github.com/digininja/DVWA] yang nantinya akan dipantau oleh Falco.

Kenapa memilih dvwa? dvwa merupakan aplikasi yang memiliki kerentanan terhadap beberapa jenis serangan seperti command injection. Jadi yang ingin dicoba adalah apakah Falco dapat mendeteksi apabila ada seseorang yang memanfaatkan kerentanan seperti command injection dari koneksi luar. Jadi jangan mencobanya pada server publik mana pun.

dvwa-deployment.yml

apiVersion: v1
kind: Namespace
metadata:
  name: dvwa
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: dvwa-app
  namespace: dvwa
spec:
  replicas: 1
  selector:
    matchLabels:
      app: dvwa-app
  template:
    metadata:
      labels:
        app: dvwa-app
    spec:
      containers:
      - image: vulnerables/web-dvwa
        imagePullPolicy: IfNotPresent
        name: dvwa-app
        ports:
        - containerPort: 80
        env:
        - name: PORT
          value: "80"
---
apiVersion: v1
kind: Service
metadata:
  name: dvwa-app
  namespace: dvwa
spec:
  ports:
    - port: 80
      targetPort: 80
      protocol: TCP
  type: ClusterIP
  selector:
    app: dvwa-app
---

Jalankan perintah berikut untuk mendeploy dvwa.

fadhil@thomas:~$ kubectl apply -f dvwa-deployment.yml

Simulasi dan Pengujian

Setelah berhasil memasang Falco pada kluster Kubernetes, maka sekarang saatnya uji coba dan simulasi untuk melihat apakah Falco berhasil mendeteksi aktivitas yang mencurigakan.

Memunculkan Shell

Jalankan perintah berikut untuk memunculkan shell di dalam pod dvwa.

fadhil@thomas:~$ kubectl get pod -n dvwa
NAME                        READY   STATUS    RESTARTS   AGE
dvwa-app-54f998c8c5-b85m2   1/1     Running   0          10m

fadhil@thomas:~$ kubectl exec -it dvwa-app-54f998c8c5-b85m2 -- /bin/bash
root@dvwa-app-54f998c8c5-b85m2:/#

Berikut adalah log yang muncul pada Amazon CloudWatch.

{
    "log": {
        "output": "21:36:54.888738975: Notice A shell was spawned in a container with an attached terminal (user=root user_loginuid=-1 k8s.ns=dvwa k8s.pod=dvwa-app-54f998c8c5-b85m2 container=bf4d401d1c5e shell=bash parent=runc cmdline=bash terminal=34816 container_id=bf4d401d1c5e image=vulnerables/web-dvwa)",
        "output_fields": {
            "container.id": "bf4d401d1c5e",
            "container.image.repository": "vulnerables/web-dvwa",
            "evt.time": 1660426614888738975,
            "k8s.ns.name": "dvwa",
            "k8s.pod.name": "dvwa-app-54f998c8c5-b85m2",
            "proc.cmdline": "bash",
            "proc.name": "bash",
            "proc.pname": "runc",
            "proc.tty": 34816,
            "user.loginuid": -1,
            "user.name": "root"
        },
        "priority": "Notice",
        "rule": "Terminal shell in container",
        "source": "syscall",
        "tags": [
            "container",
            "mitre_execution",
            "shell"
        ],
        "time": "2022-08-13T21:36:54.888738975Z"
    },
    "stream": "stdout"
}

Membaca File Sensitif

Jalankan perintah berikut untuk membaca file shadow di dalam pod dvwa.

fadhil@thomas:~$ kubectl exec -it dvwa-app-54f998c8c5-b85m2 -- /bin/bash
root@dvwa-app-54f998c8c5-b85m2:/# cat /etc/shadow > /dev/null 2>&1
root@dvwa-app-54f998c8c5-b85m2:/#

Berikut adalah log yang muncul pada Amazon CloudWatch.

{
    "log": {
        "output": "21:43:22.271495665: Warning Sensitive file opened for reading by non-trusted program (user=root user_loginuid=-1 program=cat command=cat /etc/shadow file=/etc/shadow parent=bash gparent=<NA> ggparent=<NA> gggparent=<NA> container_id=bf4d401d1c5e image=vulnerables/web-dvwa) k8s.ns=dvwa k8s.pod=dvwa-app-54f998c8c5-b85m2 container=bf4d401d1c5e",
        "output_fields": {
            "container.id": "bf4d401d1c5e",
            "container.image.repository": "vulnerables/web-dvwa",
            "evt.time": 1660427002271495665,
            "fd.name": "/etc/shadow",
            "k8s.ns.name": "dvwa",
            "k8s.pod.name": "dvwa-app-54f998c8c5-b85m2",
            "proc.aname[2]": null,
            "proc.aname[3]": null,
            "proc.aname[4]": null,
            "proc.cmdline": "cat /etc/shadow",
            "proc.name": "cat",
            "proc.pname": "bash",
            "user.loginuid": -1,
            "user.name": "root"
        },
        "priority": "Warning",
        "rule": "Read sensitive file untrusted",
        "source": "syscall",
        "tags": [
            "filesystem",
            "mitre_credential_access",
            "mitre_discovery"
        ],
        "time": "2022-08-13T21:43:22.271495665Z"
    },
    "stream": "stdout"
}

Memanfaatkan Kerentanan Command Injection di DVWA

Submit google.com; cat /etc/passwd pada text box di halaman DVWA. alt text

Berikut adalah log yang muncul pada Amazon CloudWatch.

{
    "log": {
        "output": "21:57:08.286599298: Debug Shell spawned by untrusted binary (user=www-data user_loginuid=-1 shell=sh parent=apache2 cmdline=sh -c ping  -c 4 google.com; cat /etc/passwd pcmdline=apache2 -k start gparent=apache2 ggparent=main.sh aname[4]=<NA> aname[5]=<NA> aname[6]=<NA> aname[7]=<NA> container_id=bf4d401d1c5e image=vulnerables/web-dvwa) k8s.ns=dvwa k8s.pod=dvwa-app-54f998c8c5-b85m2 container=bf4d401d1c5e",
        "output_fields": {
            "container.id": "bf4d401d1c5e",
            "container.image.repository": "vulnerables/web-dvwa",
            "evt.time": 1660427828286599298,
            "k8s.ns.name": "dvwa",
            "k8s.pod.name": "dvwa-app-54f998c8c5-b85m2",
            "proc.aname[2]": "apache2",
            "proc.aname[3]": "main.sh",
            "proc.aname[4]": null,
            "proc.aname[5]": null,
            "proc.aname[6]": null,
            "proc.aname[7]": null,
            "proc.cmdline": "sh -c ping  -c 4 google.com; cat /etc/passwd",
            "proc.name": "sh",
            "proc.pcmdline": "apache2 -k start",
            "proc.pname": "apache2",
            "user.loginuid": -1,
            "user.name": "www-data"
        },
        "priority": "Debug",
        "rule": "Run shell untrusted",
        "source": "syscall",
        "tags": [
            "mitre_execution",
            "shell"
        ],
        "time": "2022-08-13T21:57:08.286599298Z"
    },
    "stream": "stdout"
}

Kesimpulan

Dalam tulisan ini, saya telah menunjukkan bagaimana memantau aktivitas mencurigakan secara runtime di dalam container di kluster Amazon EKS menggunakan Falco, dan meneruskan lognya ke Amazon CloudWatch.


Referensi

  1. https://falco.org/docs/getting-started/
  2. https://eksctl.io/
  3. https://github.com/digininja/DVWA