스터디/PKOS

5주차 - 보안

시스템 엔지니어 2023. 4. 5. 21:41

5주차 - 보안

실습 준비

kops 원클릭 배포 - 링크

워커 노드 1대 EC2 메타데이터 보안 제거

노드 확인

(thumbup:N/A) [root@kops-ec2 ~]# kops get instances

ID                      NTERNAL-IP     INSTANCE-GROUP                                          MACHINE-TYPE
i-038c6921803a6372b     172.30.42.82    nodes-ap-northeast-2a.thumbup.click                     t3.medium
i-06b9e0c9a0a7c02b4     172.30.84.99    nodes-ap-northeast-2c.thumbup.click                     t3.medium
i-0e148bf6d66f76841     172.30.35.141   control-plane-ap-northeast-2a.masters.thumbup.click     t3.medium
메타데이터 보안 제거 (5분 소요)
  • httpPutResponseHopLimit
    • 인스턴스 메타데이터 요청에 대해 원하는 HTTP PUT 응답 홉 제한입니다. 숫자가 클수록 더 많은 인스턴스 메타데이터 요청이 이동할 수 있습니다.
  • httpTokens: required
    • IMDSv2는 토큰 지원 세션을 사용합니다.
    • required: IMDSv2가 필요한 경우 인스턴스 메타데이터 검색 요청과 함께 세션 토큰을 보내야 합니다. 이 상태에서 IAM 역할 자격 증명을 검색하면 항상 IMDSv2 자격 증명이 반환됩니다.
    • IMDSv2: 인스턴스 메타데이터 서비스이다. IMDSv2는 IMDS의 보안이 업그레이드 된 버전이다. IMDS는 인스턴스 메타데이터 IAM역할, IP 주소, 인스턴스 유형 등을 가져오는데 사용한다.
nodes-ap-northeast-2a(i-038c6921803a6372b) 취약점 발생
kops edit ig nodes-ap-northeast-2a

---
# 아래 3줄 제거
spec:
  instanceMetadata:
    httpPutResponseHopLimit: 1
    httpTokens: required
---

# 업데이트 적용 : 노드1대 롤링업데이트
kops update cluster --yes && echo && sleep 3 && kops rolling-update cluster --yes

파드에서 EC2 메타데이터 사용 가능 확인

netshoot-pod 생성

cat <<EOF | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: netshoot-pod
spec:
  replicas: 2
  selector:
    matchLabels:
      app: netshoot-pod
  template:
    metadata:
      labels:
        app: netshoot-pod
    spec:
      containers:
      - name: netshoot-pod
        image: nicolaka/netshoot
        command: ["tail"]
        args: ["-f", "/dev/null"]
      terminationGracePeriodSeconds: 0
EOF

파드 이름 변수 지정

PODNAME1=$(kubectl get pod -l app=netshoot-pod -o jsonpath={.items[0].metadata.name})
PODNAME2=$(kubectl get pod -l app=netshoot-pod -o jsonpath={.items[1].metadata.name})

EC2 메타데이터 정보 확인

kubectl exec -it $PODNAME1 -- curl 169.254.169.254 ;echo
kubectl exec -it $PODNAME2 -- curl 169.254.169.254 ;echo

(thumbup:N/A) [root@kops-ec2 ~]# kubectl exec -it $PODNAME1 -- curl 169.254.169.254 ;echo
### 출력이 안된다!
### $PODNAME2 가 있는 노드가 메타데이터 보안설정 해제한 노드
(thumbup:N/A) [root@kops-ec2 ~]# kubectl exec -it $PODNAME2 -- curl 169.254.169.254 ;echo
1.0
2007-01-19
2007-03-01
2007-08-29
2007-10-10
2007-12-15
2008-02-01
2008-09-01
2009-04-04
2011-01-01
2011-05-01
2012-01-12
2014-02-25
2014-11-05
2015-10-20
2016-04-19
2016-06-30
2016-09-02
2018-03-28
2018-08-17
2018-09-24
2019-10-01
2020-10-27
2021-01-03
2021-03-23
2021-07-15
2022-09-24
latest

보안 설정이 해제된 인스턴스에서 메타데이터 탈취

(thumbup:N/A) [root@kops-ec2 ~]# kubectl exec -it $PODNAME2 -- curl 169.254.169.254/latest/meta-data/iam/security-credentials/nodes.$KOPS_CLUSTER_NAME | jq
{
  "Code": "Success",
  "LastUpdated": "2023-04-05T12:36:49Z",
  "Type": "AWS-HMAC",
  "AccessKeyId": "ASIA~~~~~",
  "SecretAccessKey": "Omb3+~~~~~~~~",
  "Token": "IQoJb3JpZ2lu~~~~~~~~",
  "Expiration": "2023-04-05T19:04:28Z"
}

탈취한 EC2 메타데이터 IAM Role 토큰 정보를 활용해 python boto3를 통해 SDK로 AWS 서비스 강제 사용

boto3 사용을 위한 파드 생성

cat <<EOF | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: boto3-pod
spec:
  replicas: 2
  selector:
    matchLabels:
      app: boto3
  template:
    metadata:
      labels:
        app: boto3
    spec:
      containers:
      - name: boto3
        image: jpbarto/boto3
        command: ["tail"]
        args: ["-f", "/dev/null"]
      terminationGracePeriodSeconds: 0
EOF

# 파드 이름 변수 지정

파드1에서 boto3 사용

kubectl exec -it $PODNAME1 -- sh
------------
cat <<EOF> ec2.py
import boto3

ec2 = boto3.client('ec2', region_name = 'ap-northeast-2')
response = ec2.describe_instances()
print(response)
EOF

python ec2.py  # aws ec2 describe-vpcs
exit
------------

~/dev # python ec2.py
{u'Reservations': [{u'OwnerId': '300349756168', u'ReservationId': 'r-0c307dc84628bded2', u'Groups': [], u'RequesterId': '722737459838', u'Instances': [{u'Monitoring': {u'State': 'disabled'}, u'PublicDnsName': 'ec2-13-209-22-147.ap-northeast-2.compute.amazonaws.com', u'State': {u'Code': 16, u'Name': 'running'}, u'EbsOptimized': False, u'LaunchTime': datetime.datetime ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

파드2에서 boto3 사용

kubectl exec -it $PODNAME2 -- sh
------------
cat <<EOF> ec2.py
import boto3

ec2 = boto3.client('ec2', region_name = 'ap-northeast-2')
response = ec2.describe_instances()
print(response)
EOF

python ec2.py  # aws ec2 describe-vpcs
exit
------------
~/dev # python ec2.py  # aws ec2 describe-vpcs
Traceback (most recent call last):
  File "ec2.py", line 4, in <module>
    response = ec2.describe_instances()
  File "/usr/local/lib/python2.7/site-packages/botocore/client.py", line 251, in _api_call
    return self._make_api_call(operation_name, kwargs)
  File "/usr/local/lib/python2.7/site-packages/botocore/client.py", line 526, in _make_api_call
    operation_model, request_dict)
  File "/usr/local/lib/python2.7/site-packages/botocore/endpoint.py", line 141, in make_request
    return self._send_request(request_dict, operation_model)
  File "/usr/local/lib/python2.7/site-packages/botocore/endpoint.py", line 166, in _send_request
    request = self.create_request(request_dict, operation_model)
  File "/usr/local/lib/python2.7/site-packages/botocore/endpoint.py", line 150, in create_request
    operation_name=operation_model.name)
  File "/usr/local/lib/python2.7/site-packages/botocore/hooks.py", line 227, in emit
    return self._emit(event_name, kwargs)
  File "/usr/local/lib/python2.7/site-packages/botocore/hooks.py", line 210, in _emit
    response = handler(**kwargs)
  File "/usr/local/lib/python2.7/site-packages/botocore/signers.py", line 90, in handler
    return self.sign(operation_name, request)
  File "/usr/local/lib/python2.7/site-packages/botocore/signers.py", line 147, in sign
    auth.add_auth(request)
  File "/usr/local/lib/python2.7/site-packages/botocore/auth.py", line 315, in add_auth
    raise NoCredentialsError
botocore.exceptions.NoCredentialsError: Unable to locate credentials

EC2 인스턴스에 IAM Role이 부여가 되어 있는 상태인데, 노드에 있는 pod중 하나라도 탈취 당했을때 탈취 당한 pod에서 IAM Role 권한이 있는 리소스들은 전부 사용이 가능하다.

대응 방법으로는 EC2 메타데이터 제한 or AWS IRSA를 사용한다.

kubescape

  • 보안 권고 사항 기반 현재 쿠버네티스 클러스터(YAML, Helm chart)의 취약점을 점검

설치

# 설치
curl -s https://raw.githubusercontent.com/kubescape/kubescape/master/install.sh | /bin/bash

# Download all artifacts and save them in the default path (~/.kubescape)
kubescape download artifacts
tree ~/.kubescape/
cat ~/.kubescape/attack-tracks.json | jq

제공하는 보안 프레임워크 확인

kubescape list frameworks --format json | jq '.[]'
"AllControls"
"ArmoBest"
"DevOpsBest"
"MITRE"
"NSA"
"cis-eks-t1.2.0"
"cis-v1.23-t1.0.1"

제공하는 통제 정책 확인

kubescape list controls
+------------+---------------------------------------------------------------+------------------------------------+------------+
| CONTROL ID |                         CONTROL NAME                          |                DOCS                | FRAMEWORKS |
+------------+---------------------------------------------------------------+------------------------------------+------------+
| C-0001     | Forbidden Container Registries                                | https://hub.armosec.io/docs/c-0001 |            |
+------------+---------------------------------------------------------------+------------------------------------+------------+
| C-0002     | Exec into container                                           | https://hub.armosec.io/docs/c-0002 |            |
... 등등등

클러스터 스캔

  kubescape scan --enable-host-scan --verbose
  Controls: 65 (Failed: 28, Passed: 29, Action Required: 8)
Failed Resources by Severity: Critical — 0, High — 32, Medium — 110, Low — 35

+----------+-------------------------------------------------------+------------------+---------------+--------------------+
| SEVERITY |                     CONTROL NAME                      | FAILED RESOURCES | ALL RESOURCES |    % RISK-SCORE    |
+----------+-------------------------------------------------------+------------------+---------------+--------------------+
| Critical | API server insecure port is enabled                   |        0         |       1       |         0%         |
| Critical | Disable anonymous access to Kubelet service           |        0         |       3       |         0%         |
| Critical | Enforce Kubelet client TLS authentication             |        0         |       6       |         0%         |
| Critical | CVE-2022-39328-grafana-auth-bypass                    |        0         |       0       |         0%         |
| High     | Forbidden Container Registries                        |        0         |      25       | Action Required *  |
...
|          |                   RESOURCE SUMMARY                    |        54        |      232      |       10.60%       |
+----------+-------------------------------------------------------+------------------+---------------+--------------------+
FRAMEWORKS: AllControls (risk: 11.02), NSA (risk: 14.74), MITRE (risk: 6.52)

kubescape 취약점 점검툴인데 클러스터 전체적으로 취약점을 확인해 주는 것 같다. 여러가지 기능들을 제공해 주는데, 활용해보면 좋을 것 같다.

파드/컨테이너 보안 컨텍스트

컨테이너 보안 컨텍스트 확인 : kube-system 파드 내 컨테이너 대상

kubectl get pod -n kube-system -o jsonpath={.items[*].spec.containers[*].securityContext} | jq
{
  "allowPrivilegeEscalation": false,
  "readOnlyRootFilesystem": true,
  "runAsNonRoot": true
}
...

readOnlyRootFilesystem : root 파일 시스템을 읽기 전용으로 사용

cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: rootfile-readonly
spec:
  containers:
  - name: netshoot
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
    securityContext:                      ####
      readOnlyRootFilesystem: true        ####
  terminationGracePeriodSeconds: 0
EOF

파일 생성 시도

# 에러가 확인 된다.
(thumbup:N/A) [root@kops-ec2 ~]# kubectl exec -it rootfile-readonly -- touch /tmp/text.txt
touch: /tmp/text.txt: Read-only file system
command terminated with exit code 1

파일 수정시 수정이 되는게 있고 안되는게 있다.

(thumbup:N/A) [root@kops-ec2 ~]# kubectl exec -it rootfile-readonly -- sh -c "echo write > /etc/hosts"
(thumbup:N/A) [root@kops-ec2 ~]# kubectl exec -it rootfile-readonly -- cat /etc/hosts
write

/ 파티션은 ro 확인

(thumbup:N/A) [root@kops-ec2 ~]# kubectl exec -it rootfile-readonly -- mount | grep ro
overlay on / type overlay (ro,relatime~~~~~~~~~~

특정 파티션은 rw 확인

  • /proc, /dev, /sys/fs/cgroup, /etc/hosts, /proc/kcore, /proc/keys, /proc/timer_list
(thumbup:N/A) [root@kops-ec2 ~]# kubectl exec -it rootfile-readonly -- mount | grep rw
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
tmpfs on /dev type tmpfs (rw,nosuid,size=65536k,mode=755,inode64)
devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=666)
mqueue on /dev/mqueue type mqueue (rw,nosuid,nodev,noexec,relatime)
tmpfs on /sys/fs/cgroup type tmpfs (rw,nosuid,nodev,noexec,relatime,mode=755,inode64)
/dev/root on /etc/hosts type ext4 (rw,relatime,discard)
/dev/root on /dev/termination-log type ext4 (rw,relatime,discard)
shm on /dev/shm type tmpfs (rw,nosuid,nodev,noexec,relatime,size=65536k,inode64)
tmpfs on /proc/kcore type tmpfs (rw,nosuid,size=65536k,mode=755,inode64)
tmpfs on /proc/keys type tmpfs (rw,nosuid,size=65536k,mode=755,inode64)
tmpfs on /proc/timer_list type tmpfs (rw,nosuid,size=65536k,mode=755,inode64)

rw /etc/hosts도 포함 확인

(thumbup:N/A) [root@kops-ec2 ~]# kubectl exec -it rootfile-readonly -- mount | grep hosts
/dev/root on /etc/hosts type ext4 (rw,relatime,discard)

readOnlyRootFilesystem 이지만, / 파티션은 ro로 마운트 되지만, ro 영향을 안받는 파일과 디렉토리가 존재한다.

Linux Capabilities : 루트 사용자의 모든 권한이 아닌 일부 권한을 프로세스에 부여

Linux Capabilities 확인

capsh --print
Current: =ep
Bounding set =cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,cap_audit_read

proc 에서 확인 : bit 별 Capabilities

(thumbup:N/A) [root@kops-ec2 ~]# cat /proc/1/status | egrep 'CapPrm|CapEff'
CapPrm: 0000003fffffffff
CapEff: 0000003fffffffff

Linux Capabilities 설명

cap_chown                    파일이나 디렉토리의 소유자를 변경할 수 있는 권한
cap_dac_override             파일이나 디렉토리의 접근 권한을 무시하고 파일이나 디렉토리에 대한 접근을 수행할 수 있는 권한 (DAC의 약자는 Discretionary access control이다)
cap_dac_read_search          파일이나 디렉토리를 읽거나 검색할 수 있는 권한
cap_fowner                   파일이나 디렉토리의 소유자를 변경할 수 있는 권한
cap_fsetid                   파일이나 디렉토리의 Set-User-ID (SUID) 또는 Set-Group-ID (SGID) 비트를 설정할 수 있는 권한
cap_kill                     다른 프로세스를 종료할 수 있는 권한
cap_setgid                   프로세스가 그룹 ID를 변경할 수 있는 권한
cap_setuid                   프로세스가 사용자 ID를 변경할 수 있는 권한
cap_setpcap                  프로세스가 자신의 프로세스 권한을 변경할 수 있는 권한
cap_linux_immutable          파일의 immutability(불변성) 속성을 변경할 수 있는 권한을 제공
cap_net_bind_service         프로그램이 특정 포트에 바인딩(bind)하여 소켓을 개방할 수 있는 권한
cap_net_broadcast            프로세스가 네트워크 브로드캐스트 메시지를 보낼 수 있는 권한
cap_net_admin                네트워크 인터페이스나 소켓 설정을 변경할 수 있는 권한
cap_net_raw                  네트워크 패킷을 송수신하거나 조작할 수 있는 권한
cap_ipc_lock                 메모리 영역을 잠금(lock)하고 언락(unlock)할 수 있는 권한
cap_ipc_owner                IPC 리소스(Inter-Process Communication Resources)를 소유하고, 권한을 변경할 수 있는 권한
cap_sys_module               커널 모듈을 로드하거나 언로드할 수 있는 권한
cap_sys_rawio                입출력(I/O) 포트와 같은 하드웨어 리소스를 직접 접근할 수 있는 권한
cap_sys_chroot               프로세스가 chroot() 시스템 콜을 호출하여 프로세스의 루트 디렉토리를 변경할 수 있는 권한
cap_sys_ptrace               다른 프로세스를 추적(trace)하거나 디버깅할 수 있는 권한
cap_sys_pacct                프로세스 회계(process accounting)를 위한 파일에 접근할 수 있는 권한
cap_sys_admin                시스템 관리자 권한을 제공하는 권한
cap_sys_boot                 시스템 부팅과 관련된 작업을 수행할 수 있는 권한
cap_sys_nice                 프로세스의 우선순위를 변경할 수 있는 권한
cap_sys_resource             자원 제한(resource limit)과 관련된 작업을 수행할 수 있는 권한
cap_sys_time                 시스템 시간을 변경하거나, 시간 관련 시스템 콜을 사용할 수 있는 권한
cap_sys_tty_config           터미널 설정을 변경할 수 있는 권한
cap_mknod                    mknod() 시스템 콜을 사용하여 파일 시스템에 특수 파일을 생성할 수 있는 권한
cap_lease                    파일의 잠금과 관련된 작업을 수행할 수 있는 권한
cap_audit_write              시스템 감사(audit) 로그에 대한 쓰기 권한
cap_audit_control            시스템 감사(audit) 설정과 관련된 작업을 수행할 수 있는 권한
cap_setfcap                  파일 시스템 캡러빌리티(file system capability)을 설정할 수 있는 권한
cap_mac_override             SELinux 또는 AppArmor과 같은 MAC(Mandatory Access Control) 시스템을 우회하고 자신의 프로세스가 접근 가능한 파일, 디바이스, 네트워크 등을 제한 없이 접근할 수 있는 권한
cap_mac_admin                SELinux 또는 AppArmor과 같은 MAC(Mandatory Access Control) 시스템을 관리하고 수정할 수 있는 권한
cap_syslog                   시스템 로그를 읽거나, 쓸 수 있는 권한
cap_wake_alarm               시스템의 RTC(Real-Time Clock)를 사용하여 장치를 깨우거나 슬립 모드를 해제할 수 있는 권한
cap_block_suspend            시스템의 전원 관리 기능 중 하나인 Suspend(절전 모드)를 방지하는 권한
cap_audit_read               시스템 감사(audit) 로그를 읽을 수 있는 권한

파드의 Linux Capabilities 기본 확인

(thumbup:N/A) [root@kops-ec2 ~]# kubectl exec -it sample-capabilities -- capsh --print | grep Current
Current: = 
cap_chown
cap_dac_override
cap_fowner
cap_fsetid
cap_kill
cap_setgid
cap_setuid
cap_setpcap
cap_net_bind_service
cap_net_raw
cap_sys_chroot
cap_mknod
cap_audit_write
cap_setfcap+ep

(thumbup:N/A) [root@kops-ec2 ~]# kubectl exec -it sample-capabilities -- cat /proc/1/status | egrep 'CapPrm|CapEff'
CapPrm: 00000000a80425fb
CapEff: 00000000a80425fb

파드에서 시간 변경 시도

(thumbup:N/A) [root@kops-ec2 ~]# kubectl exec -it sample-capabilities -- date
Wed Apr  5 14:12:27 UTC 2023

파드에서 시간 변경 실패 확인

  • 시간 관련 권한이 없다.
  • cap_sys_time
(thumbup:N/A) [root@kops-ec2 ~]# kubectl exec -it sample-capabilities -- date -s "12:00:00"
date: cannot set date: Operation not permitted
Wed Apr  5 12:00:00 UTC 2023
command terminated with exit code 1

(thumbup:N/A) [root@kops-ec2 ~]# kubectl exec -it sample-capabilities -- date
Wed Apr  5 14:12:35 UTC 2023

파드에 Linux Capabilities 부여 및 삭제

cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: sample-capabilities2
spec:
  containers:
  - name: nginx-container
    image: masayaaoyama/nginx:capsh
    command: ["tail"]
    args: ["-f", "/dev/null"]
    securityContext:
      capabilities:
        add: ["NET_ADMIN", "SYS_TIME"]         ### 추가
        drop: ["AUDIT_WRITE"]                  ### 추가
  terminationGracePeriodSeconds: 0
EOF

파드의 Linux Capabilities 추가 삭제 확인

(thumbup:N/A) [root@kops-ec2 ~]# kubectl exec -it sample-capabilities2 -- capsh --print | grep Current
Current: = 
cap_chown
cap_dac_override
cap_fowner
cap_fsetid
cap_kill
cap_setgid
cap_setuid
cap_setpcap
cap_net_bind_service
cap_net_admin   # 추가
cap_net_raw
cap_sys_chroot
cap_sys_time    # 추가
cap_mknod
cap_setfcap+ep
--#cap_audit_write--  # 제거됨

파드 상세 정보

(thumbup:N/A) [root@kops-ec2 ~]# kubectl get pod sample-capabilities2 -o jsonpath={.spec.containers[0].securityContext} | jq
{
  "capabilities": {
    "add": [
      "NET_ADMIN",
      "SYS_TIME"
    ],
    "drop": [
      "AUDIT_WRITE"
    ]
  }
}

파드에서 시간 변경 시도 (성공)

# 노드 time sync 수정
NAME                            NOMINATED NODE
sample-capabilities2            i-038c6921803a6372b

$ k get node -o wide | grep i-038c6921803a6372b
i-038c6921803a6372b   172.30.42.82    3.36.92.81

# 노드 외부 IP로 접근
ssh -i ~/.ssh/id_rsa ubuntu@3.36.92.81

# systemd-timesyncd 종료
root@i-038c6921803a6372b:~# systemctl stop systemd-timesyncd

# 시간 변경 확인
(thumbup:N/A) [root@kops-ec2 ~]# kubectl exec -it sample-capabilities2 -- date -s "12:00:00"
Wed Apr  5 12:00:00 UTC 2023
(thumbup:N/A) [root@kops-ec2 ~]# kubectl exec -it sample-capabilities2 -- date
Wed Apr  5 12:00:04 UTC 2023

특수 권한 컨테이너 생성 : 호스트와 동등한 권한 부여됨 (조심해서 사용)

    securityContext:
      privileged: true

파드 보안 컨텍스트

  • 파드 레벨에서 보안 컨텍스트를 적용 : 파드에 포함된 모든 컨테이너가 영향을 받음
  • 파드와 컨테이너 정책 중복 시, 컨테이너 정책이 우선 적용됨
종류 개요
runAsUser 실행 사용자
runAsGroup 실행 그룹
runAsNonRoot root 에서 실행을 거부
supplementalGroups 프라이머리 GUI에 추가로 부여할 GID 목록을 지정
fsGroup 파일 시스템 그룹 지정
systls 덮어 쓸 커널 파라미터 지정
seLinuxOptions SELinux 옵션 지정

runuser 파드는 실행 사용자를 nobody(UID:65534) 사용자로 실행, 실행권한에 서브그룹 1001/1002 추가

cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: rundefault
spec:
  containers:
  - name: centos
    image: centos:7
    command: ["tail"]
    args: ["-f", "/dev/null"]
    securityContext:
      readOnlyRootFilesystem: true
  terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
  name: runuser
spec:
  securityContext:
    runAsUser: 65534
    runAsGroup: 65534
    supplementalGroups:
    - 1001
    - 1002
  containers:
  - name: centos
    image: centos:7
    command: ["tail"]
    args: ["-f", "/dev/null"]
    securityContext:
      readOnlyRootFilesystem: true
  terminationGracePeriodSeconds: 0
EOF

securityContext 확인

(thumbup:N/A) [root@kops-ec2 ~]# kubectl get pod rundefault -o jsonpath={.spec.securityContext} | jq
{}
(thumbup:N/A) [root@kops-ec2 ~]# kubectl get pod runuser -o jsonpath={.spec.securityContext} | jq
{
  "runAsGroup": 65534,
  "runAsUser": 65534,
  "supplementalGroups": [
    1001,
    1002
  ]
}

실행 사용자 정보 확인

(thumbup:N/A) [root@kops-ec2 ~]# kubectl exec -it rundefault -- id
uid=0(root) gid=0(root) groups=0(root)

(thumbup:N/A) [root@kops-ec2 ~]# kubectl exec -it runuser    -- id
uid=65534 gid=65534 groups=65534,1001,1002

프로세스 정보 확인

(thumbup:N/A) [root@kops-ec2 ~]# kubectl exec -it rundefault -- ps -axo uid,user,gid,group,pid,comm
  UID USER       GID GROUP        PID COMMAND
    0 root         0 root           1 tail
    0 root         0 root          13 ps

(thumbup:N/A) [root@kops-ec2 ~]# kubectl exec -it runuser    -- ps -axo uid,user,gid,group,pid,comm
  UID USER       GID GROUP        PID COMMAND
65534 65534    65534 65534          1 tail
65534 65534    65534 65534         14 ps

root 사용자로 실행 제한

cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: nonroot
spec:
  securityContext:
    runAsNonRoot: true              #######
  containers:
  - name: netshoot
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0
EOF

(thumbup:N/A) [root@kops-ec2 ~]# kubectl events --for pod/nonroot
LAST SEEN                       TYPE      REASON      OBJECT        MESSAGE
<invalid> (x2 over <invalid>)   Warning   Failed      Pod/nonroot   Error: container has runAsNonRoot and image has non-numeric user (root), cannot verify user is non-root (pod: "nonroot_default(6b53402b-fea5-4440-8bdc-cedd2035a261)", container: netshoot)

파일 시스템 그룹 지정

  • 일반적으로 마운트한 볼륨의 소유자와 그룹은 root:root로 되어 있다. 실행 사용자를 변경한 경우에는 마운트한 볼륨에 권한이 없는 경우가 있다
  • 따라서 마운트하는 볼륨의 그룹을 변경할 수 있도록 되어 있다 (setdig도 설정된다)
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: fsgoup1
spec:
  volumes:
  - name: vol1
    emptyDir: {}
  containers:
  - name: centos
    image: centos:7
    command: [ "sh", "-c", "sleep 1h" ]
    volumeMounts:
    - name: vol1
      mountPath: /data/demo
  terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
  name: fsgoup2
spec:
  securityContext:
    runAsUser: 1000
    runAsGroup: 3000
    fsGroup: 2000
  volumes:
  - name: vol2
    emptyDir: {}
  containers:
  - name: centos
    image: centos:7
    command: [ "sh", "-c", "sleep 1h" ]
    volumeMounts:
    - name: vol2
      mountPath: /data/demo
  terminationGracePeriodSeconds: 0
EOF

securityContext 설정 확인

(thumbup:N/A) [root@kops-ec2 ~]# kubectl get pod fsgoup1 -o jsonpath={.spec.securityContext} | jq
{}
(thumbup:N/A) [root@kops-ec2 ~]# kubectl get pod fsgoup2 -o jsonpath={.spec.securityContext} | jq
{
  "fsGroup": 2000,
  "runAsGroup": 3000,
  "runAsUser": 1000
}

실행 사용자 정보 확인

(thumbup:N/A) [root@kops-ec2 ~]# kubectl exec -it fsgoup1 -- id
uid=0(root) gid=0(root) groups=0(root)

(thumbup:N/A) [root@kops-ec2 ~]# kubectl exec -it fsgoup2 -- id
uid=1000 gid=3000 groups=3000,2000

프로세스 정보 확인

(thumbup:N/A) [root@kops-ec2 ~]# kubectl exec -it fsgoup1 -- ps -axo uid,user,gid,group,pid,comm
  UID USER       GID GROUP        PID COMMAND
    0 root         0 root           1 sleep
    0 root         0 root          12 ps

(thumbup:N/A) [root@kops-ec2 ~]# kubectl exec -it fsgoup2 -- ps -axo uid,user,gid,group,pid,comm
  UID USER       GID GROUP        PID COMMAND
 1000 1000      3000 3000           1 sleep
 1000 1000      3000 3000          13 ps

디렉터리 정보 확인

(thumbup:N/A) [root@kops-ec2 ~]# kubectl exec -it fsgoup1 -- ls -l /data
drwxrwxrwx 2 root root 4096 Apr  5 15:02 demo

(thumbup:N/A) [root@kops-ec2 ~]# kubectl exec -it fsgoup2 -- ls -l /data
drwxrwsrwx 2 root 2000 4096 Apr  5 12:18 demo

fsgoup2파드에서 파일 생성 및 확인

(thumbup:N/A) [root@kops-ec2 ~]# kubectl exec -it fsgoup2 -- sh -c "echo write > /data/demo/sample.txt"
(thumbup:N/A) [root@kops-ec2 ~]# kubectl exec -it fsgoup2 -- cat /data/demo/sample.txt
write

(thumbup:N/A) [root@kops-ec2 ~]# kubectl exec -it fsgoup2 -- ls -l /data/demo/sample.txt
-rw-r--r-- 1 1000 2000 6 Apr  5 12:21 /data/demo/sample.txt

fsgoup2파드에서 다른 디렉토리에 파일 생성 시도

# 안된다.
(thumbup:N/A) [root@kops-ec2 ~]# kubectl exec -it fsgoup2 -- sh -c "echo write > /data/sample.txt"
sh: /data/sample.txt: Permission denied
command terminated with exit code 1

# uid=1000 gid=3000 인데, root 소유자만 쓰기가 가능해서 불가능
(thumbup:N/A) [root@kops-ec2 ~]# kubectl exec -it fsgoup2 -- sh -c "ls -ld /data"
drwxr-xr-x 3 root root 4096 Apr  5 12:18 /data

sysctl을 사용한 커널 파라미터 설정

  • 커널 파라미터 변경 적용을 위해서는 컨테이너에서도 설정 필요, 파드 수준 적용으로 컨테이너 간에 공유됨
  • https://kubernetes.io/ko/docs/tasks/administer-cluster/sysctl-cluster/
  • 커널 파라미터는 안전한 것(safe)과 안전하지 않은 것(unsafe)으로 분류된다.
  • 안전한 것 safe : 호스트의 커널과 적절하게 분리되어 있으며 다른 파드에 영향이 없는 것, 파드가 예상치 못한 리소스를 소비하지 않는 것
    ```
  • kernel.shm_rmid_forced
  • net.ipv4.ip_local_port_range
  • net.ipv4.tcp_syncookies
  • net.ipv4.ping_group_range (since Kubernetes 1.18),
  • net.ipv4.ip_unprivileged_port_start (since Kubernetes 1.22).
    ```
  • 안전하지 않은 것 unsafe : 사실상 대부분의 커널 파라미터 ⇒ 적용을 위해서는 kubelet 설정 필요unsafe 파라미터를 변경 시도
  • (thumbup:N/A) [root@kops-ec2 ~]# kubectl events --for pod/unsafe LAST SEEN TYPE REASON OBJECT MESSAGE <invalid> Warning SysctlForbidden Pod/unsafe forbidden sysctl: "net.core.somaxconn" not allowlisted
  • 에러 확인
  • cat <<EOF | kubectl create -f - apiVersion: v1 kind: Pod metadata: name: unsafe spec: securityContext: sysctls: - name: net.core.somaxconn value: "12345" containers: - name: centos-container image: centos:7 command: ["/bin/sleep", "3600"] terminationGracePeriodSeconds: 0 EOF
  • # Unsafe sysctls are enabled on a node-by-node basis with a flag of the kubelet kubelet --allowed-unsafe-sysctls 'kernel.msg*,net.core.somaxconn' ...

safe 파라미터 수정 ⇒ sysctl2 파드가 배포된 노드의 net.ipv4.ip_local_port_range 값과 다를 경우에는 어떻게 동작할까요?

cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: sysctl1
spec:
  containers:
    - name: centos-container
      image: centos:7
      command: ["/bin/sleep", "3600"]
  terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
  name: sysctl2
spec:
  securityContext:
    sysctls:
    - name: net.ipv4.ip_local_port_range
      value: "1025 61000"
  containers:
    - name: centos-container
      image: centos:7
      command: ["/bin/sleep", "3600"]
  terminationGracePeriodSeconds: 0
EOF

(thumbup:N/A) [root@kops-ec2 ~]# kubectl get pod sysctl1 -o jsonpath={.spec.securityContext} | jq
kubectl get pod sysctl2 -o jsonpath={.spec.securityContext} | jq{}

(thumbup:N/A) [root@kops-ec2 ~]# kubectl get pod sysctl2 -o jsonpath={.spec.securityContext} | jq
{
  "sysctls": [
    {
      "name": "net.ipv4.ip_local_port_range",
      "value": "1025 61000"
    }
  ]
}

설정 확인

(thumbup:N/A) [root@kops-ec2 ~]# kubectl exec -it sysctl1 -- sysctl net.ipv4.ip_local_port_range
net.ipv4.ip_local_port_range = 32768    60999

(thumbup:N/A) [root@kops-ec2 ~]# kubectl exec -it sysctl2 -- sysctl net.ipv4.ip_local_port_range
net.ipv4.ip_local_port_range = 1025     61000

initContainer 와 privileged 를 활용하여 unsafe 커널 파라미터를 강제로 변경

(thumbup:N/A) [root@kops-ec2 ~]# cat sample-sysctl-initcontainer.yaml| yh
apiVersion: v1
kind: Pod
metadata: 
  name: sample-sysctl-initcontainer
spec: 
  initContainers: 
  - name: initialize-sysctl
    image: busybox:1.27
    command: 
    - /bin/sh
    - -c
    - |
      sysctl -w net.core.somaxconn=12345
    securityContext: 
      privileged: true
  containers: 
  - name: tools-container
    image: amsy810/tools:v2.0

(thumbup:N/A) [root@kops-ec2 ~]# kubectl apply -f sample-sysctl-initcontainer.yaml

확인

(thumbup:N/A) [root@kops-ec2 ~]# kubectl exec -it sample-sysctl-initcontainer -c tools-container -- sysctl net.core.somaxconn
net.core.somaxconn = 12345net.core.somaxconn = 12345

Polaris

  • 오픈소스 보안 점검 도구

설치

# 설치
kubectl create ns polaris

#
cat <<EOT > polaris-values.yaml
dashboard:
  replicas: 1
  service:
    type: LoadBalancer
EOT

# 배포
helm repo add fairwinds-stable https://charts.fairwinds.com/stable
helm install polaris fairwinds-stable/polaris --namespace polaris --version 5.7.2 -f polaris-values.yaml

# CLB에 ExternanDNS 로 도메인 연결
kubectl annotate service polaris-dashboard "external-dns.alpha.kubernetes.io/hostname=polaris.$KOPS_CLUSTER_NAME" -n polaris

# 웹 접속 주소 확인 및 접속
echo -e "Polaris Web URL = http://polaris.$KOPS_CLUSTER_NAME"

  • Score : 모범 사례 대비 전체 클러스터 구성 내역 점수, 권고 사항 만족이 많을 수록 점수 상승
  • Passing/Warning/Dangerous Checks : 위험 등급 분류, 위험 단계 취약점은 조치를 권고
  • 측정 범위 : Efficiency, Reliability, Security
    • Security 보안성 : 보안 관련 구성 확인 - 링크
    • Efficiency 효율성 : CPU,Memory 리소스 사용 관련 - 링크
    • Reliability 신뢰성 : 안정성 구성 여부 확인 - 링크
  • 검사 항목 상세 - 링크

과제2

  • 책 398~400페이지 - kubescape armo 웹 사용 후 관련 스샷을 올려주세요

https://cloud.armosec.io/ URL 접근

[Repository Scanning] -> [Get started with repository scanning] 클릭

명령어 실행

(thumbup:polaris) [root@kops-ec2 ~]# kubescape scan https://github.com/prometheus-community/helm-charts --submit --account f1a4c887-85a6-45d5-a04f-d06bf167739f
[info] Kubescape scanner starting
[info] Downloading/Loading policy definitions
[success] Downloaded/Loaded policy
[info] Accessing local objects
[info] cloning. repository url: https://github.com/prometheus-community/helm-charts
[warning] Rendering of Helm chart template 'prometheus', failed: [template: prometheus/templates/NOTES.txt:37:63: executing "prometheus/templates/NOTES.txt" at <.Values.alertmanager.service.servicePort>: nil pointer evaluating interface {}.servicePort]
[success] Done accessing local objects
[info] Scanning Repo
Control: C-0084 100% |███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| (65/65, 56 it/s)        
[success] Done scanning Repo

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Controls: 65 (Failed: 25, Passed: 35, Action Required: 5)
Failed Resources by Severity: Critical — 0, High — 48, Medium — 210, Low — 108

+----------+-------------------------------------------------+------------------+---------------+-------------------+
| SEVERITY |                  CONTROL NAME                   | FAILED RESOURCES | ALL RESOURCES |   % RISK-SCORE    |
+----------+-------------------------------------------------+------------------+---------------+-------------------+
| High     | Forbidden Container Registries                  |        0         |      39       | Action Required * |
| High     | Resources memory limit and request              |        0         |      39       | Action Required * |
| High     | Resource limits                                 |        37        |      39       |        95%        |
| High     | Applications credentials in configuration files |        0         |      72       | Action Required * |
| High     | List Kubernetes secrets                         |        4         |      17       |        24%        |
| High     | Host PID/IPC privileges                         |        1         |      39       |        3%         |
| High     | HostNetwork access                              |        3         |      39       |        8%         |
| High     | Writable hostPath mount                         |        1         |      39       |        3%         |
| High     | Resources CPU limit and request                 |        0         |      39       | Action Required * |
| High     | Privileged container                            |        2         |      39       |        5%         |
| Medium   | Data Destruction                                |        1         |      17       |        6%         |
| Medium   | Non-root containers                             |        32        |      39       |        82%        |
| Medium   | Allow privilege escalation                      |        32        |      39       |        82%        |
| Medium   | Ingress and Egress blocked                      |        39        |      39       |       100%        |
| Medium   | Automatic mapping of service account            |        23        |      68       |        34%        |
| Medium   | CoreDNS poisoning                               |        1         |      17       |        6%         |
| Medium   | Malicious admission controller (mutating)       |        1         |       1       |       100%        |
| Medium   | Access container service account                |        7         |       7       |       100%        |
| Medium   | Linux hardening                                 |        33        |      39       |        85%        |
| Medium   | Configured liveness probe                       |        11        |      39       |        28%        |
| Medium   | Images from allowed registry                    |        0         |      39       | Action Required * |
| Medium   | CVE-2022-0492-cgroups-container-escape          |        30        |      39       |        77%        |
| Low      | Immutable container filesystem                  |        29        |      39       |        74%        |
| Low      | Configured readiness probe                      |        11        |      39       |        28%        |
| Low      | Malicious admission controller (validating)     |        1         |       1       |       100%        |
| Low      | Pods in default namespace                       |        26        |      39       |        67%        |
| Low      | Naked PODs                                      |        4         |       4       |       100%        |
| Low      | Image pull policy on latest tag                 |        1         |      39       |        3%         |
| Low      | Label usage for resources                       |        27        |      39       |        69%        |
| Low      | K8s common labels usage                         |        9         |      39       |        23%        |
+----------+-------------------------------------------------+------------------+---------------+-------------------+
|          |                RESOURCE SUMMARY                 |        61        |      124      |      23.36%       |
+----------+-------------------------------------------------+------------------+---------------+-------------------+
FRAMEWORKS: AllControls (risk: 23.52), NSA (risk: 34.48), MITRE (risk: 4.73)

* Control configurations are empty


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   << WOW! Now you can see the scan results on the web >>

   https://cloud.armosec.io/repository-scanning/b9c873ea-f8cc-4a8d-95ab-547e6f2e7762
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


[info] Run with '--verbose'/'-v' flag for detailed resources view

데이터 업로드 성공

분석

마치며..

보안은 역시 배울 것이 많다. EC2 메타데이터의 취약점을 알수 있었고, IRSA를 왜 써야 되는지 알수 있었다. 취약점을 진단하는 다양한 도구들을 알 수 있었다. 파드와 컨테이너 사용자ID, 그룹ID 설정 방법을 알아 보았고 Linux Capabilities 권한에 대해서도 알아보았다.

5주차로 pkos 2기가 끝났습니다. 역시나 배울것이 많았습니다! 항상 감사합니다!