Ssoon
[6주차] EKS Security - Real Time Threat Detection with Falco 본문
AWS EKS Workshop Study
[6주차] EKS Security - Real Time Threat Detection with Falco
구구달스 2023. 6. 2. 06:08CloudNet@ 팀의 AWS EKS Workshop Study 6주차 정리입니다.
#EKS Immersion Workshop 의 내용입니다.
Falco는 오픈 소스 컨테이너 보안 도구로, 컨테이너 및 오케스트레이션 환경에서 악의적인 활동을 탐지하고 예방하는 데 사용됩니다. Falco는 컨테이너 환경에서 이벤트를 실시간으로 모니터링하고 시스템, 네트워크 및 애플리케이션 수준에서의 보안 위반을 식별합니다.
Falco는 이벤트 기반 규칙 엔진을 사용하여 행위 기반 보안 감사 및 탐지를 제공합니다. 사용자는 시스템 및 애플리케이션의 정상 동작에 대한 규칙을 정의할 수 있으며, Falco는 이러한 규칙에 따라 비정상적인 행위를 탐지하고 경고를 생성합니다. 예를 들어, 파일 시스템 접근, 프로세스 실행, 네트워크 통신 등의 이벤트를 모니터링하고, 악성 소프트웨어 감염, 권한 위반, 보안 설정 위반 등을 식별할 수 있습니다.
- Falco 알림을 CloudWatch로 전송할 수 있도록 Fluentbit 설치
- Helm를 사용한 Falco 설치
- 위협 시뮬레이션 및 실시간 탐지
Installing Fluentbit
- Fluentbit 는 오픈 소스 로그 수집 도구로, Falco alerts 를 수집하여 Cloudwatch와 같은 AWS 서비스로 전송합니다.
Fluentbit 를 설정하기 전에 먼저 EKS 노드에 로그를 CloudWatch로 전송할 수 있는 올바른 IAM 권한이 있는지 확인합니다. - 역할 정책을 생성하여 EKS 노드 역할에 첨부합니다:
TEMPOUT=$(mktemp)
cat << EoF > $TEMPOUT
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:DescribeLogStreams"
],
"Resource": [
"arn:aws:logs:*:*:*"
]
}
]
}
EoF
export CLUSTER_NAME=$(eksctl get clusters -o json | jq -r '.[0].Name')
export AWS_REGION=$(curl -s 169.254.169.254/latest/dynamic/instance-identity/document | jq -r '.region')
export NODE_ROLE_ARN=$(eksctl get iamidentitymapping --cluster ${CLUSTER_NAME} --region=${AWS_REGION} | grep -i node | awk '{print $1}'|tail -1)
export NODE_ROLE_NAME=$(echo $NODE_ROLE_ARN | cut -d'/' -f2)
aws iam create-policy --policy-name Falco-CloudWatchLogs --policy-document file://$TEMPOUT
aws iam attach-role-policy --role-name ${NODE_ROLE_NAME} --policy-arn `aws iam list-policies | jq -r '.[][] | select(.PolicyName == "Falco-CloudWatchLogs") | .Arn'`
- 다음 명령을 실행하여 클러스터 이름과 로그를 전송할 Region이 포함된 cluster-info라는 이름의 구성 맵을 생성합니다. 클러스터 이름과 클러스터 지역을 클러스터의 이름과 지역으로 바꿉니다.
ClusterName=myeks
RegionName=ap-northeast-2
FluentBitHttpPort='2020'
FluentBitReadFromHead='Off'
[[ ${FluentBitReadFromHead} = 'On' ]] && FluentBitReadFromTail='Off'|| FluentBitReadFromTail='On'
[[ -z ${FluentBitHttpPort} ]] && FluentBitHttpServer='Off' || FluentBitHttpServer='On'
kubectl create configmap fluent-bit-cluster-info \
--from-literal=cluster.name=${ClusterName} \
--from-literal=http.server=${FluentBitHttpServer} \
--from-literal=http.port=${FluentBitHttpPort} \
--from-literal=read.head=${FluentBitReadFromHead} \
--from-literal=read.tail=${FluentBitReadFromTail} \
--from-literal=logs.region=${RegionName} -n amazon-cloudwatch
- fluentbit 용 daemonset 리소스를 생성합니다.
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluent-bit
namespace: falco
labels:
k8s-app: fluent-bit
version: v1
kubernetes.io/cluster-service: "true"
spec:
selector:
matchLabels:
k8s-app: fluent-bit
template:
metadata:
labels:
k8s-app: fluent-bit
version: v1
kubernetes.io/cluster-service: "true"
spec:
containers:
- name: fluent-bit
image: public.ecr.aws/aws-observability/aws-for-fluent-bit:stable
imagePullPolicy: Always
env:
- name: AWS_REGION
valueFrom:
configMapKeyRef:
name: fluent-bit-cluster-info
key: logs.region
- name: CLUSTER_NAME
valueFrom:
configMapKeyRef:
name: fluent-bit-cluster-info
key: cluster.name
- name: HTTP_SERVER
valueFrom:
configMapKeyRef:
name: fluent-bit-cluster-info
key: http.server
- name: HTTP_PORT
valueFrom:
configMapKeyRef:
name: fluent-bit-cluster-info
key: http.port
- name: READ_FROM_HEAD
valueFrom:
configMapKeyRef:
name: fluent-bit-cluster-info
key: read.head
- name: READ_FROM_TAIL
valueFrom:
configMapKeyRef:
name: fluent-bit-cluster-info
key: read.tail
- name: HOST_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: HOSTNAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
- name: CI_VERSION
value: "k8s/1.3.15"
resources:
limits:
memory: 200Mi
requests:
cpu: 500m
memory: 100Mi
volumeMounts:
# Please don't change below read-only permissions
- name: fluentbitstate
mountPath: /var/fluent-bit/state
- name: varlog
mountPath: /var/log
readOnly: true
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
- name: fluent-bit-config
mountPath: /fluent-bit/etc/
- name: runlogjournal
mountPath: /run/log/journal
readOnly: true
- name: dmesg
mountPath: /var/log/dmesg
readOnly: true
terminationGracePeriodSeconds: 10
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet
volumes:
- name: fluentbitstate
hostPath:
path: /var/fluent-bit/state
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
- name: fluent-bit-config
configMap:
name: fluent-bit-config
- name: runlogjournal
hostPath:
path: /run/log/journal
- name: dmesg
hostPath:
path: /var/log/dmesg
serviceAccountName: fluent-bit
tolerations:
- key: node-role.kubernetes.io/master
operator: Exists
effect: NoSchedule
- operator: "Exists"
effect: "NoExecute"
- operator: "Exists"
effect: "NoSchedule"
- fluentbit 구성을 위한 configmap 리소스를 생성합니다:
apiVersion: v1
kind: ConfigMap
metadata:
name: fluent-bit-config
namespace: falco
labels:
k8s-app: fluent-bit
data:
fluent-bit.conf: |
[SERVICE]
Flush 5
Grace 30
Log_Level info
Daemon off
Parsers_File parsers.conf
HTTP_Server ${HTTP_SERVER}
HTTP_Listen 0.0.0.0
HTTP_Port ${HTTP_PORT}
storage.path /var/fluent-bit/state/flb-storage/
storage.sync normal
storage.checksum off
storage.backlog.mem_limit 5M
@INCLUDE application-log.conf
@INCLUDE dataplane-log.conf
@INCLUDE host-log.conf
application-log.conf: |
[INPUT]
Name tail
Tag falco.*
Exclude_Path /var/log/containers/cloudwatch-agent*, /var/log/containers/fluent-bit*, /var/log/containers/aws-node*, /var/log/containers/kube-proxy*
Path /var/log/containers/falco*.log
multiline.parser docker, cri
DB /var/fluent-bit/state/flb_container.db
Mem_Buf_Limit 50MB
Skip_Long_Lines On
Refresh_Interval 10
Rotate_Wait 30
storage.type filesystem
Read_from_Head ${READ_FROM_HEAD}
[INPUT]
Name tail
Tag application.*
Path /var/log/containers/fluent-bit*
multiline.parser docker, cri
DB /var/fluent-bit/state/flb_log.db
Mem_Buf_Limit 5MB
Skip_Long_Lines On
Refresh_Interval 10
Read_from_Head ${READ_FROM_HEAD}
[INPUT]
Name tail
Tag application.*
Path /var/log/containers/cloudwatch-agent*
multiline.parser docker, cri
DB /var/fluent-bit/state/flb_cwagent.db
Mem_Buf_Limit 5MB
Skip_Long_Lines On
Refresh_Interval 10
Read_from_Head ${READ_FROM_HEAD}
[FILTER]
Name kubernetes
Match application.*
Kube_URL https://kubernetes.default.svc:443
Kube_Tag_Prefix application.var.log.containers.
Merge_Log On
Merge_Log_Key log_processed
K8S-Logging.Parser On
K8S-Logging.Exclude Off
Labels Off
Annotations Off
Use_Kubelet On
Kubelet_Port 10250
Buffer_Size 0
[OUTPUT]
Name cloudwatch_logs
Match application.*
region ${AWS_REGION}
log_group_name /aws/containerinsights/${CLUSTER_NAME}/application
log_stream_prefix ${HOST_NAME}-
auto_create_group true
extra_user_agent container-insights
dataplane-log.conf: |
[INPUT]
Name systemd
Tag dataplane.systemd.*
Systemd_Filter _SYSTEMD_UNIT=docker.service
Systemd_Filter _SYSTEMD_UNIT=containerd.service
Systemd_Filter _SYSTEMD_UNIT=kubelet.service
DB /var/fluent-bit/state/systemd.db
Path /var/log/journal
Read_From_Tail ${READ_FROM_TAIL}
[INPUT]
Name tail
Tag dataplane.tail.*
Path /var/log/containers/aws-node*, /var/log/containers/kube-proxy*
multiline.parser docker, cri
DB /var/fluent-bit/state/flb_dataplane_tail.db
Mem_Buf_Limit 50MB
Skip_Long_Lines On
Refresh_Interval 10
Rotate_Wait 30
storage.type filesystem
Read_from_Head ${READ_FROM_HEAD}
[FILTER]
Name modify
Match dataplane.systemd.*
Rename _HOSTNAME hostname
Rename _SYSTEMD_UNIT systemd_unit
Rename MESSAGE message
Remove_regex ^((?!hostname|systemd_unit|message).)*$
[FILTER]
Name aws
Match dataplane.*
imds_version v1
host-log.conf: |
[INPUT]
Name tail
Tag host.dmesg
Path /var/log/dmesg
Key message
DB /var/fluent-bit/state/flb_dmesg.db
Mem_Buf_Limit 5MB
Skip_Long_Lines On
Refresh_Interval 10
Read_from_Head ${READ_FROM_HEAD}
[INPUT]
Name tail
Tag host.messages
Path /var/log/messages
Parser syslog
DB /var/fluent-bit/state/flb_messages.db
Mem_Buf_Limit 5MB
Skip_Long_Lines On
Refresh_Interval 10
Read_from_Head ${READ_FROM_HEAD}
[INPUT]
Name tail
Tag host.secure
Path /var/log/secure
Parser syslog
DB /var/fluent-bit/state/flb_secure.db
Mem_Buf_Limit 5MB
Skip_Long_Lines On
Refresh_Interval 10
Read_from_Head ${READ_FROM_HEAD}
[FILTER]
Name aws
Match host.*
imds_version v1
parsers.conf: |
[PARSER]
Name syslog
Format regex
Regex ^(?<time>[^ ]* {1,2}[^ ]* [^ ]*) (?<host>[^ ]*) (?<ident>[a-zA-Z0-9_\/\.\-]*)(?:\[(?<pid>[0-9]+)\])?(?:[^\:]*\:)? *(?<message>.*)$
Time_Key time
Time_Format %b %d %H:%M:%S
[PARSER]
Name container_firstline
Format regex
Regex (?<log>(?<="log":")\S(?!\.).*?)(?<!\\)".*(?<stream>(?<="stream":").*?)".*(?<time>\d{4}-\d{1,2}-\d{1,2}T\d{2}:\d{2}:\d{2}\.\w*).*(?=})
Time_Key time
Time_Format %Y-%m-%dT%H:%M:%S.%LZ
[PARSER]
Name cwagent_firstline
Format regex
Regex (?<log>(?<="log":")\d{4}[\/-]\d{1,2}[\/-]\d{1,2}[ T]\d{2}:\d{2}:\d{2}(?!\.).*?)(?<!\\)".*(?<stream>(?<="stream":").*?)".*(?<time>\d{4}-\d{1,2}-\d{1,2}T\d{2}:\d{2}:\d{2}\.\w*).*(?=})
Time_Key time
Time_Format %Y-%m-%dT%H:%M:%S.%LZ
- fluentbit에 대한 서비스 계정을 생성하고 클러스터 역할을 바인딩하여 POD 로그를 읽을 수 있도록 합니다:
cat << EoF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: fluent-bit
namespace: falco
---
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: falco
EoF
- fluentbit POD 를 확인합니다.
Installing Falco
Helm 차트를 통해 쿠버네티스 클러스터에 Falco 를 배포합니다:
helm repo add falcosecurity https://falcosecurity.github.io/charts
helm repo update
helm install falco falcosecurity/falco --namespace falco --create-namespace --set tty=true --set jsonOutput=true
위협 모방 & 실시간 탐지
- 프론트엔드 애플리케이션에서 몇 가지 위협을 시뮬레이션합니다.
- 프론트엔드 앱의 POD 이름을 얻습니다:
export TARGET_APP=$(kubectl get pods -n workshop | awk '/frontend/ {print $1; exit}')
echo $TARGET_APP
- 암호화된 사용자 비밀번호 세부 정보가 들어 있는 /etc/shadow를 읽어 보겠습니다:
kubectl -n workshop exec -it $TARGET_APP -- cat /etc/shadow
- AWS 콘솔에서 Cloudwatch → Logs → Log 그룹에서 해당 로그를 확인합니다.
- 다음으로 스토리지 시스템 구성 파일, 시스템 부팅에 필요한 실행 파일 및 일부 로그 파일을 저장하는 중요한 디렉토리인 /etc 폴더에 파일을 생성합니다.
kubectl -n workshop exec -it $TARGET_APP -- touch /etc/badconfigfile
- AWS 콘솔에서 Cloudwatch → Logs → Log 그룹에서 해당 로그를 확인합니다.
- 시스템 관리자와 사용자 모두가 사용할 수 있는 명령이 포함된 /bin 디렉토리를 대상으로 지정할 수 있습니다. 폴더를 생성합니다.
kubectl -n workshop exec -it $TARGET_APP -- mkdir /bin/dangerousfolder
- AWS 콘솔에서 Cloudwatch → Logs → Log 그룹에서 해당 로그를 확인합니다.
'AWS EKS Workshop Study' 카테고리의 다른 글
[7주차] EKS Automation - Continuous Delivery with Argo CD (0) | 2023.06.06 |
---|---|
[6주차] EKS Security - Pod Security Standards (0) | 2023.06.02 |
[6주차] EKS Security - IAM Roles for Service Accounts (0) | 2023.06.02 |
[6주차] EKS Security - RBAC (2) | 2023.05.31 |
[5주차] EKS Autoscaling - Scaling App and Cluster - USE FIS TO INTERRUPT A SPOT INSTANCE (0) | 2023.05.25 |
Comments