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
- 검사 항목 상세 - 링크
과제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기가 끝났습니다. 역시나 배울것이 많았습니다! 항상 감사합니다!
'스터디 > PKOS' 카테고리의 다른 글
4주차 - 프로메테우스 & 그라파나 & 로키 (0) | 2023.03.29 |
---|