이 글은 스터디를 참여하면서 학습한 내용을 중심으로 Kubernetes를 정리하는 연재 글이다. '24단계 실습으로 정복하는 쿠버네티스' 도서 내용을 중심으로 정리했다. 이 글은 마지막 스터디 글이다!
이번 글에서는 Kubernetes의 보안을 알아보겠다. 다른 다양한 요구사항과 서비스 운영 등에 밀려 보안은 가장 후순위로 밀려날 때가 많다. 하지만 보안 이슈가 터졌을 때를 생각하면 제일 끔찍하다. 이 거대하고 복잡한 Kubernetes의 보안을 어떻게 관리 할 수 있을지 알아보자.
1. 실습환경 배포
kops 환경을 배포해준다. kops 배포는 [PKOS] kOps를 사용한 Cluster 설치와 기본 Kubernetes 관리 방법 글에 자세히 기술되어 있다. 저번 실습과 같이 이번에도 CPU를 많이 요구하는 시스템으로 비교적 높은 사양의 인스턴스 타입을 사용할 예정이다. 마스터 노드는 t3.xlarge / 워커 노드는 c5a.2xlarge를 사용했다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
# YAML 파일 다운로드
curl -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/K8S/kops-oneclick-f1.yaml
# CloudFormation 스택 배포 : 노드 인스턴스 타입 변경 - MasterNodeInstanceType=t3.medium WorkerNodeInstanceType=c5d.large
aws cloudformation deploy --template-file kops-oneclick-f1.yaml --stack-name mykops --parameter-overrides KeyName=ygpark SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32 MyIamUserAccessKeyID=AKIA6NUKWRMLEXRDE3VA MyIamUserSecretAccessKey='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' ClusterBaseName='ygpark.net' S3StateStore='ygpark-s3' MasterNodeInstanceType=t3.medium WorkerNodeInstanceType=c5a.2xlarge --region ap-northeast-2
# CloudFormation 스택 배포 완료 후 kOps EC2 IP 출력
aws cloudformation describe-stacks --stack-name mykops --query 'Stacks[*].Outputs[0].OutputValue' --output text
13.125.192.64
# 13분 후 작업 SSH 접속
ssh -i /mnt/c/vswork/_pkos2/secret/ygpark.pem ec2-user@$(aws cloudformation describe-stacks --stack-name mykops --query 'Stacks[*].Outputs[0].OutputValue' --output text)
# EC2 instance profiles 에 IAM Policy 추가(attach) : 처음 입력 시 적용이 잘 안될 경우 다시 한번 더 입력 하자! - IAM Role에서 새로고침 먼저 확인!
aws iam attach-role-policy --policy-arn arn:aws:iam::$ACCOUNT_ID:policy/AWSLoadBalancerControllerIAMPolicy --role-name masters.$KOPS_CLUSTER_NAME
aws iam attach-role-policy --policy-arn arn:aws:iam::$ACCOUNT_ID:policy/AWSLoadBalancerControllerIAMPolicy --role-name nodes.$KOPS_CLUSTER_NAME
|
cs |
워커 노드 1대 EC2 메타데이터 보안 제거
워커노드 한대의 보안정보을 삭제하여 보안에 취약해지도록 만들어 놓는다. httpPutResponseHopLimit: 메타데이터를 획득하는 홉에 제한을 두는 정책 / httpTokens: 메타데이터 토큰으로 발급 받아야 하도록 하는 정책을 제거해준다.
1
2
3
4
5
6
7
8
9
10
11
12
|
# 워커노드 한대의 보안정보을 삭제하여 보안에 취약해지도록 만들어 놓는다.
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
|
cs |
2. 파드 / 컨테이너 메타데이터 탈취
워커 노드 1대 EC2 메타데이터 탈취
파드 2개 배포하면 각각 다른 노드에 배포 된다. 위에서 보안을 제거한 노드와 보안이 되고 있는 노드에 각각 배포된다. 파드1번에는 EC2 메타데이터가 보이고, 파드2번에는 안보인다. 위 메타데이터 보안을 삭제했기 때문에 파드레벨에서 노드 메타데이터에 접근 할 수 있고, AccessKeyId / SecretAccessKey과 Token까지 탈취가 가능하다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
|
# 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
k get pods -owide
k get pods,node -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod/netshoot-pod-7757d5dd99-vpng2 1/1 Running 0 79s 172.30.59.226 i-02efcebe833916388 <none> <none>
pod/netshoot-pod-7757d5dd99-zbbxs 1/1 Running 0 79s 172.30.94.181 i-0b169dc4ebfdd3820 <none> <none>
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
node/i-013c32ef6c6893c64 Ready control-plane 130m v1.24.12 172.30.34.193 3.35.236.227 Ubuntu 20.04.5 LTS 5.15.0-1031-aws containerd://1.6.18
node/i-02efcebe833916388 Ready node 18m v1.24.12 172.30.62.240 3.39.227.219 Ubuntu 20.04.5 LTS 5.15.0-1031-aws containerd://1.6.18
node/i-0b169dc4ebfdd3820 Ready node 128m v1.24.12 172.30.69.214 3.36.71.188 Ubuntu 20.04.5 LTS 5.15.0-1031-aws containerd://1.6.18
# 파드 이름 변수 지정
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
2021-01-03
2021-03-23
2021-07-15
2022-09-24
latest
kubectl exec -it $PODNAME2 -- curl 169.254.169.254 ;echo
# 파드1에서 EC2 메타데이터 정보 확인
kubectl exec -it $PODNAME1 -- curl 169.254.169.254/latest ;echo
dynamic
meta-data
user-data
kubectl exec -it $PODNAME1 -- curl 169.254.169.254/latest/meta-data/iam/security-credentials/ ;echo
nodes.ygpark.net
kubectl exec -it $PODNAME1 -- curl 169.254.169.254/latest/meta-data/iam/security-credentials/nodes.$KOPS_CLUSTER_NAME | jq
# 메타데이터 메모해두기
{
"Code": "Success",
"LastUpdated": "2023-03-01T14:11:26Z",
"Type": "AWS-HMAC",
"AccessKeyId": "XXXXXXLF2FJIZ3XXXXXX",
"SecretAccessKey": "XXXXXX1eKupIXNl9w/XXXXXXYdSOYcFsGv3r+EX/",
"Token": "XXXXXXJpZ2luX2VjEOb//////////XXXXXXwLW5vcnRoZWFzdC0yIkcwRQIhAJRp7ov3nvS7fErWhNqo7ebeYZSS5UpL/XXXXXXQGDu7AiB23SvUsLr0J1bU/XXXXXX7KHta9Jm7pNwHyQrXZIMQSSrVBQiP//////////8BEAEaDDkxMTI4MzQ2NDc4NSIMgF5IMoJtct/G4uoYKqkFpUmOepDUb2NyRJ5b4CoeAzfEAReqtndk3EJiodz9RQiR0HTtkjfSHpQpCA5JuKDJR+kU8vkhbB+YHgrcL167k+lnblzJuhqYIAtTVYQ3UmZCfVYiwnA/ZhBxqzEvfQrmXQck990rT0sCHf0+fsWeeZHylsHi4DXINSv8BZbgm3leC/n77bJifeYvskbcWML9yEYAMK/rolnehtz/yuKqdWqN1PcmzL8KfkytBkCIEzezDVPF+9DLXbvOQXn26+41PzvXd+pB+d+IkbP8zCnC5hcbLZwE5z8/KYyqTdj9cJqTzQhSnMFkWFiYh4HddVpOq6oj0hOmIbjH8iAD9oMwEuVd/BgBPm9yadVugDVp6XWkLSaQ0esCoVALKzlbr06Tg7JUSpniuSEApqbAKM1dJlHck76UQwXot1n/D54JNyAq2GXbJz/ndqNsBCdtbVWERjcOyCZOH14uLlrRKZUmTEXpIhGOgpq4AHE+/KdisVDu0aaOvaQUhAzMg+x5ROqHCtJoLByYcez0A6PNqiBRIupKWYyRhZY7WzyH21BYw4pgI6ytjBOAQIknCyf5XzU/r+g6r8Ts9BXF75gPfIsMQQbKXdvjoJAHN3HMcfeiiuu6/LQ+bUkkiWdGepyx8iE+UzT8+AMa0Le3zNVSJCK+qz4gSnD/HW87+JPzzZyHA2yYMyXXAq0mBqxtiqI/maMx2o7Mi+xfsBa8cnvWOV3oAQN+drfkkRWWMZaEGVrCLBUf8KSQKup6hu4m257ytBUR3TH5schErAmksSadBzkiK8QX8ZOWNcuHQNcvPd56oLvKgDb1I5ehe96wgHWa/ZZz4jZZIrXKlVC30hRARARqTQxeo1CPi9P2lZrC+Z7vlHVGtazonB+atbQOFScDLAnBrAW4cpxM3sK9MOK5/Z8GOrEB+xhO3/OVp3bCpZ92G97tUUS07j4DWyCeFs1FXSUgLfwvXh7w0e9KPNW+tq87knudsCimTlsw5b/BFBCk5T386qv12m5CqYfOmTwLovpVUmR1wAoeIMgpBTb5AdpXkeuuMdCAc5nNqv3Zdp84+acyR1V2DmvRcFkQqtrCuc4Wt9FYi2QAcurNK9+HGDtlOstFVr2TiiPHpg1hCB3AhX8gpiiC4p5sK5P0VePWyjvSfirl",
"Expiration": "2023-03-01T20:12:11Z"
}
# 파드2에서 EC2 메타데이터 정보 확인
kubectl exec -it $PODNAME2 -- curl 169.254.169.254/latest ;echo
kubectl exec -it $PODNAME2 -- curl 169.254.169.254/latest/meta-data/iam/security-credentials/ ;echo
kubectl exec -it $PODNAME2 -- curl 169.254.169.254/latest/meta-data/iam/security-credentials/nodes.$KOPS_CLUSTER_NAME | jq
|
cs |
탈취한 IAM Role Token 활용해서 AWS 사용하기
awscli 파드 생성해서 내부에 탈취한 자격증명 정보를 입력하면 aws CLI를 사용할 수 있게된다. ec2 정보 확인이 가능하다. 이렇게 메타데이터의 보안이 중요하다. IAM에 따라 AWS 대부분이 탈취당할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
|
# awscli 파드 생성
cat <<EOF | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: awscli-pod
spec:
replicas: 2
selector:
matchLabels:
app: awscli-pod
template:
metadata:
labels:
app: awscli-pod
spec:
containers:
- name: awscli-pod
image: amazon/aws-cli
command: ["tail"]
args: ["-f", "/dev/null"]
terminationGracePeriodSeconds: 0
EOF
# 파드 이름 변수 지정
APODNAME1=$(kubectl get pod -l app=awscli-pod -o jsonpath={.items[0].metadata.name})
APODNAME2=$(kubectl get pod -l app=awscli-pod -o jsonpath={.items[1].metadata.name})
# 파드 bash 실행 : 임시자격증명을 직접 입력하는 거라서 아무곳에서나 해도됨 >> 즉 별도의 ec2 1대를 신규 생성 후 임시자격증명 설정 후 확인해보자
# 실행하는 파드는 메타데이터 사용 시 Token이 필요한 노드에 배치된 파드에서 해볼 것!
kubectl get pod -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
awscli-pod-5c87478747-cwdjl 1/1 Running 0 2m25s 172.30.59.228 i-02efcebe833916388 <none> <none>
awscli-pod-5c87478747-gvdgh 1/1 Running 0 2m25s 172.30.94.183 i-0b169dc4ebfdd3820 <none> <none>
netshoot-pod-7757d5dd99-vpng2 1/1 Running 0 11m 172.30.59.226 i-02efcebe833916388 <none> <none>
netshoot-pod-7757d5dd99-zbbxs 1/1 Running 0 11m 172.30.94.181 i-0b169dc4ebfdd3820 <none> <none>
kubectl exec -it $APODNAME1 -- bash
kubectl exec -it $APODNAME2 -- bash
----------------------------------------------------------
# 위에서 출력된 AccessKeyId , SecretAccessKey , SessionToken 으로 임시자격증명 적용
export AWS_ACCESS_KEY_ID="XXXXXXLF2FJIZ3XXXXXX"
export AWS_SECRET_ACCESS_KEY="XXXXXX1eKupIXNl9w/XXXXXXYdSOYcFsGv3r+EX/"
export AWS_SESSION_TOKEN="XXXXXXJpZ2luX2VjEOb//////////XXXXXXwLW5vcnRoZWFzdC0yIkcwRQIhAJRp7ov3nvS7fErWhNqo7ebeYZSS5UpL/XXXXXXQGDu7AiB23SvUsLr0J1bU/XXXXXX7KHta9Jm7pNwHyQrXZIMQSSrVBQiP//////////8BEAEaDDkxMTI4MzQ2NDc4NSIMgF5IMoJtct/G4uoYKqkFpUmOepDUb2NyRJ5b4CoeAzfEAReqtndk3EJiodz9RQiR0HTtkjfSHpQpCA5JuKDJR+kU8vkhbB+YHgrcL167k+lnblzJuhqYIAtTVYQ3UmZCfVYiwnA/ZhBxqzEvfQrmXQck990rT0sCHf0+fsWeeZHylsHi4DXINSv8BZbgm3leC/n77bJifeYvskbcWML9yEYAMK/rolnehtz/yuKqdWqN1PcmzL8KfkytBkCIEzezDVPF+9DLXbvOQXn26+41PzvXd+pB+d+IkbP8zCnC5hcbLZwE5z8/KYyqTdj9cJqTzQhSnMFkWFiYh4HddVpOq6oj0hOmIbjH8iAD9oMwEuVd/BgBPm9yadVugDVp6XWkLSaQ0esCoVALKzlbr06Tg7JUSpniuSEApqbAKM1dJlHck76UQwXot1n/D54JNyAq2GXbJz/ndqNsBCdtbVWERjcOyCZOH14uLlrRKZUmTEXpIhGOgpq4AHE+/KdisVDu0aaOvaQUhAzMg+x5ROqHCtJoLByYcez0A6PNqiBRIupKWYyRhZY7WzyH21BYw4pgI6ytjBOAQIknCyf5XzU/r+g6r8Ts9BXF75gPfIsMQQbKXdvjoJAHN3HMcfeiiuu6/LQ+bUkkiWdGepyx8iE+UzT8+AMa0Le3zNVSJCK+qz4gSnD/HW87+JPzzZyHA2yYMyXXAq0mBqxtiqI/maMx2o7Mi+xfsBa8cnvWOV3oAQN+drfkkRWWMZaEGVrCLBUf8KSQKup6hu4m257ytBUR3TH5schErAmksSadBzkiK8QX8ZOWNcuHQNcvPd56oLvKgDb1I5ehe96wgHWa/ZZz4jZZIrXKlVC30hRARARqTQxeo1CPi9P2lZrC+Z7vlHVGtazonB+atbQOFScDLAnBrAW4cpxM3sK9MOK5/Z8GOrEB+xhO3/OVp3bCpZ92G97tUUS07j4DWyCeFs1FXSUgLfwvXh7w0e9KPNW+tq87knudsCimTlsw5b/BFBCk5T386qv12m5CqYfOmTwLovpVUmR1wAoeIMgpBTb5AdpXkeuuMdCAc5nNqv3Zdp84+acyR1V2DmvRcFkQqtrCuc4Wt9FYi2QAcurNK9+HGDtlOstFVr2TiiPHpg1hCB3AhX8gpiiC4p5sK5P0VePWyjvSfirl"
# ec2 정보 확인
aws ec2 describe-instances --region ap-northeast-2 | head
bash-4.2# aws ec2 describe-instances --region ap-northeast-2 | head
{
"Reservations": [
{
"Groups": [],
"Instances": [
{
"AmiLaunchIndex": 0,
"ImageId": "ami-035e3e44dc41db6a2",
"InstanceId": "i-0b169dc4ebfdd3820",
"InstanceType": "t3.xlarge",
...
# vpc 정보 확인
aws ec2 describe-vpcs --region ap-northeast-2 | head
bash-4.2# aws ec2 describe-vpcs --region ap-northeast-2 | head
...
# 빠져나오기
exit
----------------------------------------------------------
|
cs |
3. kubescape
kubescap 사용 해보기
kubescape는 보안 권고 사항 기반으로 쿠버네티스 클러스터(YAML, Helm chart)의 취약점을 점검해준다. IDE, CI/CD pipelines에 적용할 수도 있다.
kubescape를 다운로드 하고 artifacts 다운로드 된 것을 보면 cis 보안 기준에 맞는 프레임워크 등들이 다운로드 된다. 1주차에 진행 했었던 스토리지 부하 테스트 처럼 관련 파드를 배포해서 스캔을 돌려 취합해준다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
# 설치
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
[success] Downloaded. artifact: framework; name: ArmoBest; path: /root/.kubescape/armobest.json
[success] Downloaded. artifact: framework; name: cis-v1.23-t1.0.1; path: /root/.kubescape/cis-v1.23-t1.0.1.json
[success] Downloaded. artifact: framework; name: cis-eks-t1.2.0; path: /root/.kubescape/cis-eks-t1.2.0.json
[success] Downloaded. artifact: framework; name: NSA; path: /root/.kubescape/nsa.json
[success] Downloaded. artifact: framework; name: MITRE; path: /root/.kubescape/mitre.json
[success] Downloaded. artifact: framework; name: DevOpsBest; path: /root/.kubescape/devopsbest.json
[success] Downloaded. artifact: framework; name: AllControls; path: /root/.kubescape/allcontrols.json
[success] Downloaded. attack tracks: attack-tracks; path: /root/.kubescape/attack-tracks.json
[success] Downloaded. artifact: controls-inputs; path: /root/.kubescape/controls-inputs.json
[success] Downloaded. artifact: exceptions; path: /root/.kubescape/exceptions.json
tree ~/.kubescape/
/root/.kubescape/
├── allcontrols.json
├── armobest.json
├── attack-tracks.json
├── cis-eks-t1.2.0.json
├── cis-v1.23-t1.0.1.json
├── controls-inputs.json
├── devopsbest.json
├── exceptions.json
├── mitre.json
└── nsa.json
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
# 모니터링
watch kubectl get pod -A
# 클러스터 스캔
# Deploy Kubescape host-sensor daemonset in the scanned cluster. Deleting it right after we collecting the data.
# Required to collect valuable data from cluster nodes for certain controls.
# Yaml file: https://github.com/kubescape/kubescape/blob/master/core/pkg/hostsensorutils/hostsensor.yaml
kubescape scan --help
kubescape scan --enable-host-scan --verbose
+----------+-------------------------------------------------------+------------------+---------------+---------------------+
| SEVERITY | CONTROL NAME | FAILED RESOURCES | ALL RESOURCES | % RISK-SCORE |
+----------+-------------------------------------------------------+------------------+---------------+---------------------+
| Critical | Disable anonymous access to Kubelet service | 0 | 0 | Action Required * |
| Critical | Enforce Kubelet client TLS authentication | 0 | 0 | Action Required * |
| High | Forbidden Container Registries | 0 | 21 | Action Required *** |
| High | Resources memory limit and request | 0 | 21 | Action Required *** |
...
+----------+-------------------------------------------------------+------------------+---------------+---------------------+
| | RESOURCE SUMMARY | 47 | 204 | 8.78% |
+----------+-------------------------------------------------------+------------------+---------------+---------------------+
FRAMEWORKS: AllControls (risk: 9.17), NSA (risk: 11.82), MITRE (risk: 6.94)
|
cs |
kubescape armo 웹 사용
armo 웹의 레포지토리 스캔이 되면 위의 스크린샷 처럼 취약점을 확인 할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
# 설치
helm repo add kubescape https://kubescape.github.io/helm-charts/ ; helm repo update ; helm upgrade --install kubescape kubescape/kubescape-cloud-operator -n kubescape --create-namespace --set clusterName=`kubectl config current-context` --set account=62555776-ef0f-46df-89fe-5eddb788c0a1
Name=`kubectl config current-context` --set account=62555776-ef0f-46df-89fe-5eddb788c0a1
"kubescape" has been added to your repositories
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "kubescape" chart repository
...Successfully got an update from the "fairwinds-stable" chart repository
Update Complete. ⎈Happy Helming!⎈
Release "kubescape" does not exist. Installing it now.
NAME: kubescape
LAST DEPLOYED: Sun Apr 9 17:08:13 2023
NAMESPACE: kubescape
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
Thank you for installing kubescape-cloud-operator version 1.10.8.
You can see and change the values of your's recurring configurations daily scan in the following link:
https://cloud.armosec.io/settings/assets/clusters/scheduled-scans?cluster=ygpark-net
> kubectl -n kubescape get cj kubescape-scheduler -o=jsonpath='{.metadata.name}{"\t"}{.spec.schedule}{"\n"}'
You can see and change the values of your's recurring images daily scan in the following link:
https://cloud.armosec.io/settings/assets/images
> kubectl -n kubescape get cj kubevuln-scheduler -o=jsonpath='{.metadata.name}{"\t"}{.spec.schedule}{"\n"}'
See you!!!
# install ARMO
curl -s https://raw.githubusercontent.com/kubescape/kubescape/master/install.sh | /bin/bash
Installing Kubescape...
######################################################################## 100.0%
Finished Installation.
Your current version is: v2.2.6 [git enabled in build: true]
Usage: $ kubescape scan --enable-host-scan
# scan your repository
kubescape scan https://github.com/prometheus-community/helm-charts --submit --account 62555776-ef0f-46df-89fe-5eddb788c0a1
|
cs |
4. 파드/컨테이너 보안 컨텍스트
파드/컨테이너 보안 컨텍스트를 yaml에서 확인 할 수 있다. 뒤에서 사용해 볼 polaris 같은 보안 툴로 컨테이너 취약점을 확인 할 수 있는데, 취약점을 확인 하더라도 이 yaml에 설정된 보안 요소들이 어떻게 작동하는 지 알아야 반영이 가능하다. 이 보안 통제는 하나의 방법이다. 컨테이너 / 파드 레벨에서 사용할 수 있다.
컨테이너 보안 컨텍스트 SecurityContext
종류 | 개요 |
privileged | 특수 권한을 가진 컨테이너로 실행 |
capabilities | Capabilities 의 추가와 삭제 |
allowPrivilegeEscalation | 컨테이너 실행 시 상위 프로세스보다 많은 권한을 부여할지 여부 |
readOnlyRootFilesystem | root 파일 시스템을 읽기 전용으로 할지 여부 |
runAsUser | 실행 사용자 |
runAsGroup | 실행 그룹 |
runAsNonRoot | root 에서 실행을 거부 |
seLinuxOptions | SELinux 옵션 |
컨테이너 보안 컨텍스트 SecurityContext는 파드 yaml의 컨테이너 레벨의 시큐리티 컨텍스트에 있다. 이 파드는 readOnlyRootFilesystem: root 파일 시스템을 읽기 전용으로 할지 여부로 true로 설정 되어 있다. 파드 상세 정보를 조회하면 확인이 된다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
|
# 컨테이너 시큐리티 컨텍스트가 적용된 것들만 볼 수 있다.
kubectl get pod -n kube-system -o jsonpath={.items[*].spec.containers[*].securityContext} | jq
# 파드 생성
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
# 파일 생성 시도
kubectl exec -it rootfile-readonly -- touch /tmp/text.txt
touch: /tmp/text.txt: Read-only file system
command terminated with exit code 1
# 기존 파일 수정 시도 : 아래 /etc/hosts파일 말고 다른 파일로 예제 만들어 두자
## 기본적으로 mount 옵션이 ro 이긴 한데. 특정 파일이나 폴더가 rw로 mount가 되어서 그곳에서는 파일 생성, 삭제등이 가능하네요.
## 특히 /etc/hosts 파일은 HostAliases로 항목 추가가 가능한데, 해당 파링은 kubelet에 의해 관리되고, 파드 생성/재시작 중 덮었여질 수 있다.
## /dev 라던가 /sys/fs/cgroup 폴더 안에서도 가능하네요.
## /etc/hostname 같은 경우는 호스트와 별도의 파일이지만 mount가 / (ro)에 속하게 되어 제한이 걸리네요.
kubectl exec -it rootfile-readonly -- cat /etc/hosts
# Kubernetes-managed hosts file.
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
fe00::0 ip6-mcastprefix
fe00::1 ip6-allnodes
fe00::2 ip6-allrouters
172.30.59.227 rootfile-readonly
kubectl exec -it rootfile-readonly -- sh -c "echo write > /etc/hosts"
kubectl exec -it rootfile-readonly -- cat /etc/hosts
write
# 특정 파티션, 파일의 ro/rw 확인
kubectl exec -it rootfile-readonly -- mount | grep hosts
/dev/root on /etc/hosts type ext4 (rw,relatime,discard)
kubectl exec -it rootfile-readonly -- mount | grep ro
overlay on / type overlay (ro,relatime,lowerdir=/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/52/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/51/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/50/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/49/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/48/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/47/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/46/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/45/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/44/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/43/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/42/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/41/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/40/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/39/fs,upperdir=/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/75/fs,workdir=/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/75/work)
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
sysfs on /sys type sysfs (ro,nosuid,nodev,noexec,relatime)
tmpfs on /sys/fs/cgroup type tmpfs (rw,nosuid,nodev,noexec,relatime,mode=755,inode64)
cgroup on /sys/fs/cgroup/systemd type cgroup (ro,nosuid,nodev,noexec,relatime,xattr,name=systemd)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (ro,nosuid,nodev,noexec,relatime,cpu,cpuacct)
cgroup on /sys/fs/cgroup/pids type cgroup (ro,nosuid,nodev,noexec,relatime,pids)
cgroup on /sys/fs/cgroup/memory type cgroup (ro,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/freezer type cgroup (ro,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/misc type cgroup (ro,nosuid,nodev,noexec,relatime,misc)
cgroup on /sys/fs/cgroup/cpuset type cgroup (ro,nosuid,nodev,noexec,relatime,cpuset)
cgroup on /sys/fs/cgroup/blkio type cgroup (ro,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/rdma type cgroup (ro,nosuid,nodev,noexec,relatime,rdma)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (ro,nosuid,nodev,noexec,relatime,net_cls,net_prio)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (ro,nosuid,nodev,noexec,relatime,hugetlb)
cgroup on /sys/fs/cgroup/perf_event type cgroup (ro,nosuid,nodev,noexec,relatime,perf_event)
cgroup on /sys/fs/cgroup/devices type cgroup (ro,nosuid,nodev,noexec,relatime,devices)
/dev/root on /etc/hosts type ext4 (rw,relatime,discard)
/dev/root on /dev/termination-log type ext4 (rw,relatime,discard)
/dev/root on /etc/hostname type ext4 (ro,relatime,discard)
/dev/root on /etc/resolv.conf type ext4 (ro,relatime,discard)
tmpfs on /run/secrets/kubernetes.io/serviceaccount type tmpfs (ro,relatime,size=16092696k,inode64)
proc on /proc/bus type proc (ro,nosuid,nodev,noexec,relatime)
proc on /proc/fs type proc (ro,nosuid,nodev,noexec,relatime)
proc on /proc/irq type proc (ro,nosuid,nodev,noexec,relatime)
proc on /proc/sys type proc (ro,nosuid,nodev,noexec,relatime)
proc on /proc/sysrq-trigger type proc (ro,nosuid,nodev,noexec,relatime)
tmpfs on /proc/acpi type tmpfs (ro,relatime,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)
tmpfs on /proc/scsi type tmpfs (ro,relatime,inode64)
tmpfs on /sys/firmware type tmpfs (ro,relatime,inode64)
## /proc, /dev, /sys/fs/cgroup, /etc/hosts, /proc/kcore, /proc/keys, /proc/timer_list
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)
...
# 파드 상세 정보 확인
kubectl get pod rootfile-readonly -o jsonpath={.spec.containers[0].securityContext} | jq
{
"readOnlyRootFilesystem": true
}
|
cs |
Linux Capabilities
Linux Capabilities는 슈퍼 유저인 root의 권한(privileges)을 더 나누고 할당할 수 있게 한다. 접속한 os 터미널에서 확인해 보면 가지고 있는 cacpability를 확인 할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
# Linux Capabilities 확인 : 현재 38개
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
Ambient set =
Current IAB:
Securebits: 00/0x0/1'b0 (no-new-privs=0)
secure-noroot: no (unlocked)
secure-no-suid-fixup: no (unlocked)
secure-keep-caps: no (unlocked)
secure-no-ambient-raise: no (unlocked)
uid=0(root) euid=0(root)
gid=0(root)
groups=0(root)
Guessed mode: UNCERTAIN (0)
# proc 에서 확인 : bit 별 Capabilities - 링크
cat /proc/1/status | egrep 'CapPrm|CapEff'
CapPrm: 0000003fffffffff
CapEff: 0000003fffffffff
|
cs |
마스터 노드가 가지고 있는 capability도 확인 할 수 있다. 이 중에 시스템 시간 변경하는 capa인 cap_sys_time을 이용 하여 테스트 해볼 것이다.
파드의 Linux Capabilities 기본 확인
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
# 샘플 파드 생성
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
name: sample-capabilities
spec:
containers:
- name: nginx-container
image: masayaaoyama/nginx:capsh
command: ["tail"]
args: ["-f", "/dev/null"]
terminationGracePeriodSeconds: 0
EOF
# 파드의 Linux Capabilities 기본 확인
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
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
# proc 에서 확인 : bit 별 Capabilities - 링크
kubectl exec -it sample-capabilities -- cat /proc/1/status | egrep 'CapPrm|CapEff'
CapPrm: 00000000a80425fb
CapEff: 00000000a80425fb
# 파드에서 시간 변경 시도
kubectl exec -it sample-capabilities -- date
Sun Apr 9 03:40:48 UTC 2023
# 파드에서 시간 변경 시도
kubectl exec -it sample-capabilities -- date -s "12:00:00"
date: cannot set date: Operation not permitted
Sun Apr 9 12:00:00 UTC 2023
command terminated with exit code 1
kubectl exec -it sample-capabilities -- date
|
cs |
파드에 Linux Capabilities 부여 및 삭제
파드에 Linux Capabilities 부여 및 삭제해본다. 시간 동기화하는 다른 우선순위가 있기 떄문에 노드 단에서 종료 후에 변경 시도한다. 변경이 가능해진다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
|
#
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 기본 확인
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
비교
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
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
# 파드 상세 정보 확인
kubectl get pod sample-capabilities2 -o jsonpath={.spec.containers[0].securityContext} | jq
{
"capabilities": {
"add": [
"NET_ADMIN",
"SYS_TIME"
],
"drop": [
"AUDIT_WRITE"
]
}
}
# proc 에서 확인 : bit 별 Capabilities - 링크
kubectl exec -it sample-capabilities2 -- cat /proc/1/status | egrep 'CapPrm|CapEff'
CapPrm: 000000008a0435fb
CapEff: 000000008a0435fb
# 파드에서 시간 변경 시도 : 시간 동기화하는 다른 우선순위가 있다. 노드 단에서 종료 후에 변경 시도한다.
k get node -o wide | grep i-0b169dc4ebfdd3820
i-0b169dc4ebfdd3820 Ready node 3h26m v1.24.12 172.30.69.214 3.36.71.188 Ubuntu 20.04.5 LTS 5.15.0-1031-aws containerd://1.6.18
# 노드 외부 IP로 접근
ssh -i ~/.ssh/id_rsa ubuntu@3.36.71.188
# systemd-timesyncd 종료
ubuntu@i-0b169dc4ebfdd3820:~$ sudo systemctl stop systemd-timesyncd
exit
# 시간 변경 확인
(ygpark:default) [root@kops-ec2 ~]# kubectl exec -it sample-capabilities2 -- date -s "12:00:00"
Sun Apr 9 12:00:00 UTC 2023
(ygpark:default) [root@kops-ec2 ~]# kubectl exec -it sample-capabilities2 -- date
Sun Apr 9 12:00:09 UTC 2023
# systemd-timesyncd 시작
ssh -i ~/.ssh/id_rsa ubuntu@3.36.71.188
sudo systemctl start systemd-timesyncd
exit
# 확인 해 보면 싱크로 인해 다시 시간이 돌아가 있다.
kubectl exec -it sample-capabilities2 -- date
Sun Apr 9 04:05:29 UTC 2023
|
cs |
특수 권한 컨테이너 생성
파드임에도 호스트와 동등한 권한을 부여할 수있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
#
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
name: sample-capabilities3
spec:
containers:
- name: nginx-container
image: masayaaoyama/nginx:capsh
command: ["tail"]
args: ["-f", "/dev/null"]
securityContext:
privileged: true
terminationGracePeriodSeconds: 0
EOF
# 파드의 Linux Capabilities 기본 확인
kubectl exec -it sample-capabilities3 -- capsh --print | grep Current
Current: = 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,38,39,40+ep
# 파드 상세 정보 확인
kubectl get pod sample-capabilities3 -o jsonpath={.spec.containers[0].securityContext} | jq
{
"privileged": true
}
# proc 에서 확인 : bit 별 Capabilities - 링크
kubectl exec -it sample-capabilities3 -- cat /proc/1/status | egrep 'CapPrm|CapEff'
CapPrm: 000001ffffffffff
CapEff: 000001ffffffffff
|
cs |
파드 보안 컨텍스트
파드 레벨에서 보안 컨텍스트를 적용하면 파드에 포함된 모든 컨테이너가 영향을 받고 컨테이너 정책과 중복시에는 컨테이너 정책이 우선 적용된다.
종류 | 개요 |
runAsUser | 실행 사용자 |
runAsGroup | 실행 그룹 |
runAsNonRoot | root 에서 실행을 거부 |
supplementalGroups | 프라이머리 GUI에 추가로 부여할 GID 목록을 지정 |
fsGroup | 파일 시스템 그룹 지정 |
systls | 덮어 쓸 커널 파라미터 지정 |
seLinuxOptions | SELinux 옵션 지정 |
파드 1은 컨택스트에 기본으로 생성, 파드2는 실행 사용자를 변경한다. runuser 파드는 실행 사용자를 nobody(UID:65534) 사용자로 실행하고, 실행권한에 서브그룹 1001/1002 추가한다. spec.securityContext에 표기한다. 확인 해보면 컨테이너 보안시 유저를 루트가 아닌 다른 유저로 사용한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
kubectl get pod -n kube-system -o jsonpath={.items[*].spec.securityContext} | jq
...
#
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
#
kubectl get pod rundefault -o jsonpath={.spec.securityContext} | jq
{}
kubectl get pod runuser -o jsonpath={.spec.securityContext} | jq
{
"runAsGroup": 65534,
"runAsUser": 65534,
"supplementalGroups": [
1001,
1002
]
}
# 실행 사용자 정보 확인
kubectl exec -it rundefault -- id
uid=0(root) gid=0(root) groups=0(root)
kubectl exec -it runuser -- id
uid=65534 gid=65534 groups=65534,1001,1002
# 프로세스 정보 확인
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
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 19 ps
|
cs |
실행 사용자를 변경하지 않고 단순히 root 사용자로 실행을 거부하도록 설정 시 거부되는 에러가 확인 된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
|
```bash
#
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
#
**kubectl** get pod **fsgoup1** -o jsonpath={.spec.securityContext} | jq
{}
**kubectl** get pod **fsgoup2** -o jsonpath={.spec.securityContext} | jq
{
"fsGroup": 2000,
"runAsGroup": 3000,
"runAsUser": 1000
}
# 실행 사용자 정보 확인
**kubectl exec -it fsgoup1 -- id**
uid=0(root) gid=0(root) groups=0(root)
**kubectl exec -it fsgoup2 -- id**
uid=1000 gid=3000 groups=3000,2000
# 프로세스 정보 확인
**kubectl exec -it fsgoup1 -- ps -axo uid,user,gid,group,pid,comm**
**kubectl exec -it fsgoup2 -- ps -axo uid,user,gid,group,pid,comm**
# 디렉터리 정보 확인 : fsgoup2파드의 마운트 볼륨 그룹의 GID가 2000 (fsGroup: 2000)
**kubectl exec -it fsgoup1 -- ls -l /data**
drwxrwxrwx 2 root root 4096 Apr 9 04:13 demo
**kubectl exec -it fsgoup2 -- ls -l /data**
drwxrwsrwx 2 root 2000 4096 Apr 9 04:13 demo
# fsgoup2파드에서 파일 생성 및 확인
**kubectl exec -it fsgoup2 --** sh -c "echo write > /data/demo/sample.txt"
**kubectl exec -it fsgoup2 -- cat /data/demo/sample.txt
write**
**kubectl exec -it fsgoup2 -- ls -l /data/demo/sample.txt**
-rw-r--r-- 1 1000 2000 6 Apr 9 04:16 /data/demo/sample.txt
# fsgoup2파드에서 다른 디렉토리에 파일 생성 시도 >> 안되는 이유가 뭘까요?
**kubectl exec -it fsgoup2 --** sh -c "echo write > /data/sample.txt"
sh: /data/sample.txt: Permission denied
command terminated with exit code 1
```
|
cs |
다양한 볼륨을 사용할 수 있는데, 기본은 사용자 그룹이 root이다. yaml에 설정을 루트가 아니라 user로 변경했을 때 마운트한 볼륨이 소유자가 루트이다. 확인해보면 파일 시스템 그룹 2000으로 변경되어 있고, 해당 디렉터리에 파일 생성이 가능하고 권한 범위가 아닌 다른 디렉토리에 파일을 생성하면 권한 에러가 뜬다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
|
#
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
#
kubectl get pod fsgoup1 -o jsonpath={.spec.securityContext} | jq
{}
kubectl get pod fsgoup2 -o jsonpath={.spec.securityContext} | jq
{
"fsGroup": 2000,
"runAsGroup": 3000,
"runAsUser": 1000
}
# 실행 사용자 정보 확인
kubectl exec -it fsgoup1 -- id
uid=0(root) gid=0(root) groups=0(root)
kubectl exec -it fsgoup2 -- id
uid=1000 gid=3000 groups=3000,2000
# 프로세스 정보 확인
kubectl exec -it fsgoup1 -- ps -axo uid,user,gid,group,pid,comm
kubectl exec -it fsgoup2 -- ps -axo uid,user,gid,group,pid,comm
# 디렉터리 정보 확인 : fsgoup2파드의 마운트 볼륨 그룹의 GID가 2000 (fsGroup: 2000)
kubectl exec -it fsgoup1 -- ls -l /data
drwxrwxrwx 2 root root 4096 Apr 9 04:13 demo
kubectl exec -it fsgoup2 -- ls -l /data
drwxrwsrwx 2 root 2000 4096 Apr 9 04:13 demo
# fsgoup2파드에서 파일 생성 및 확인
kubectl exec -it fsgoup2 -- sh -c "echo write > /data/demo/sample.txt"
kubectl exec -it fsgoup2 -- cat /data/demo/sample.txt
write
kubectl exec -it fsgoup2 -- ls -l /data/demo/sample.txt
-rw-r--r-- 1 1000 2000 6 Apr 9 04:16 /data/demo/sample.txt
# fsgoup2파드에서 다른 디렉토리에 파일 생성 시도 >> 안되는 이유가 뭘까요?
kubectl exec -it fsgoup2 -- sh -c "echo write > /data/sample.txt"
sh: /data/sample.txt: Permission denied
command terminated with exit code 1
|
cs |
글 이 길어져 polaris와 RBAC은 다음 포스팅에 이어서 작성하겠다.
'스터디 > Kubernetes' 카테고리의 다른 글
[AEWS] Amzaon EKS (0) | 2023.04.30 |
---|---|
[PKOS] Kubernetes 보안 - kubescape, polaris, RBAC (2/2) (0) | 2023.04.10 |
[PKOS] Kubernetes 모니터링과 로깅 - Prometheus, Grafana, Loki (0) | 2023.04.02 |
[PKOS] Kubernetes GitOps 시스템 - Harbor, GitLab, ArgoCD (0) | 2023.03.26 |
[PKOS] AWS Kubernetes 네트워크, 노드의 Max Pod 제한 (2) | 2023.03.18 |