본문 바로가기
Tech/Kubernetes

[PKOS_2기] 5주차 보안

by 구름_쟁이 2023. 4. 7.

본 시리즈는 가시다님의 PKOS(Production Kubernetes Online Study) 2기 진행 내용입니다.

스터디에 사용된 서적은 이정훈님의 24단계 실습으로 정복하는 쿠버네티스 입니다.

도서 링크 : http://www.yes24.com/Product/Goods/115187666

 

 

보안이란?
사전적 의미로는 '위험, 손실 및 범죄가 발생하지 않도록 방지하는 상태를 가리킨다.'
결국 회사나 나의 컴퓨팅 자산(관리자 권한 및 정보, 개인정보 등등)을 보호하기 위한 모든 행위인 것이다.

 

 

현업에서 일을 하다보니 보안을 고려한 아키텍처와 그렇지 않은 아키텍처로 크게 나뉜다.

처음에는 보안을 고려하지 않았다가 외부 감사를 받아야하거나 ISMS(-P) 인증을 받을 때가 되어서야 부랴부랴 준비하게 된다.(그렇지 않은 곳도 많이 있겠지만, 나의 경우 그랬다)

5주차 실습을 진행하면서 다룰 내용들은 이런 인증에 대한 준비가 아닌 실제로 쿠버네티스를 운영함에 있어서 도움이 될만한 보안 스킬들을 다룬다.

 

목차

0. 클러스터 배포 및 프로비저닝

1. EC2 IAM Role & 메타데이터

2. 오픈소스 보안 점검 도구 - 1 (kubescape)

3. 오픈소스 보안 점검 도구 - 2 (polaris)

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

5. K8S 인증/인가 & RBAC

6. 도전 과제

 

 

서론이 길었지만 5주차 역시 고봉밥스러움이 있다. (고봉 맞습니다)

차근차근 할수 있는 것들을 해나가 보자!

 

 

 

 

0. 클러스터 배포 및 프로비저닝

원클릭 배포 사용.

 

배포 특이사항

- kops 인스턴스 t3.small & 노드 t3.xlarge (medium으로도 될 것으로 보임)

 

master & worker node 역할(EC2)에 AWS LoadBalancer Control 허용 정책 추가

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

 

 


1. EC2 IAM Role & 메타데이터

 

기본적으로 kops 로 클러스터를 구축할 때 메타데이터 보안이 설정되어 있다.

이 부분이 없다면 어떤 일이 일어날지 지금부터 알아보겠다.

 

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

#
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

 

# 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

# 파드1에서 EC2 메타데이터 정보 확인
kubectl exec -it $PODNAME1 -- curl 169.254.169.254/latest ;echo
kubectl exec -it $PODNAME1 -- curl 169.254.169.254/latest/meta-data/iam/security-credentials/ ;echo
kubectl exec -it $PODNAME1 -- curl 169.254.169.254/latest/meta-data/iam/security-credentials/nodes.$KOPS_CLUSTER_NAME | jq

# 파드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

# 메타데이터 메모해두기
{
  "Code": "Success",
  "LastUpdated": "2023-04-05T06:16:06Z",
  "Type": "AWS-HMAC",
  "AccessKeyId": "ASIAYA~~",
  "SecretAccessKey": "Y0e3oBFee~~~",
  "Token": "IQoJb3JpZ2luX2VjEC~~~~~~~~~",
  "Expiration": "2023-04-05T12:52:01Z"
}

 

 

메타데이터 보안 제거 상태 확인

첫번째 netshoot-pod 가 보안설정이 해제된 node-ap-northeast-2a 에 배포되었기 때문에 메타데이터 정보를 확인할 수 있게 되었다.

 

 

Expiration 시간에 도달하면 해당 토큰을 사용할 수 없게 된다

 

메타데이터 보안 해제된 node에 배포한 pod에서 할 수 있는 일

boto3

보안 허점이 생긴 node에서 pod를 배포한 경우 어떤 것들이 가능한지 살펴보자

python boto3 모듈이 설치된 파드에 접속해서 node (EC2)에 허가된 정책안에서 할 수 있는 것들을 수행할 수 있다.

단지 Pod 하나만 탈취당했을 때 벌어질 수 있는 일이라고 생각하면 굉장히 심각한 일이다.

(어떤 일이 가능했는지에 대한 추가적인 내용은 도전과제 1에서 기술)

# 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

# 파드 이름 변수 지정
PODNAME1=$(kubectl get pod -l app=boto3 -o jsonpath={.items[0].metadata.name})
PODNAME2=$(kubectl get pod -l app=boto3 -o jsonpath={.items[1].metadata.name})

# 파드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
------------

# 파드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
------------

# 실습 완료 후 삭제
kubectl delete deploy boto3-pod

 

boto3-pod 1은 보안 설정이 된 node에 배포된 상태

 

boto3-pod 2는 보안 설정이 해제된 node에 배포된 상태로 boto3에 의한 명령들이 정상적으로 수행된다.

 

awscli

boto3와 마찬가지로 awscli 가 사용가능한 pod를 배포하여 테스트해보자

# 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})

---
# 메타데이터 메모해두기 : 단 Expiration 시간이 지나면 Token은 유효하지 않음
{
  "Code": "Success",
  "LastUpdated": "2023-04-05T06:16:06Z",
  "Type": "AWS-HMAC",
  "AccessKeyId": "ASIAY~~~~",
  "SecretAccessKey": "Y0e3oB~~~~~",
  "Token": "IQoJb3JpZ2luX~~~~~~~~~~~",
  "Expiration": "2023-04-05T12:52:01Z"
}
---

# 파드 bash 실행 : 임시자격증명을 직접 입력하는 거라서 아무곳에서나 해도됨 >> 즉 별도의 ec2 1대를 신규 생성 후 임시자격증명 설정 후 확인해보자
# 실행하는 파드는 메타데이터 사용 시 Token이 필요한 노드에 배치된 파드에서 해볼 것!
kubectl get pod -owide
kubectl exec -it $APODNAME1 -- bash
kubectl exec -it $APODNAME2 -- bash
----------------------------------------------------------
# 위에서 출력된 AccessKeyId , SecretAccessKey , SessionToken 으로 임시자격증명 적용
export AWS_ACCESS_KEY_ID="ASIAY~~~~"
export AWS_SECRET_ACCESS_KEY="Y0e3oB~~~~~"
export AWS_SESSION_TOKEN="IQoJb3JpZ2luX~~~~~~~~~~~"

# ec2 정보 확인
bash-4.2# aws ec2 describe-instances --region ap-northeast-2 | head


# vpc 정보 확인
bash-4.2# aws ec2 describe-vpcs --region ap-northeast-2 | head


# 빠져나오기
exit
----------------------------------------------------------

aws cli 명령어도 잘 작동하는 것을 볼 수 있다.

 

이런 보안 취약점을 보완하기 위한 방안은 아래와 같다.

  • node (EC2) 메타데이터 접속 제한
  • AWS IRSA 사용

 

IRSA 설명

더보기
https://tech.devsisters.com/posts/pod-iam-role/
https://aws.amazon.com/ko/blogs/containers/diving-into-iam-roles-for-service-accounts/

 

 

 

결론

이 파트를 진행하면서 느낀 점은 공격자가 작정하고 메타데이터 정보 취득, IAM 권한 확인, 공격작업 에 대한 부분을 스크립트화 해둔다면 비집고 들어간 순간 어마어마한 사고가 될 것이라고 생각된다.

물론 위에 기술한대로 메타데이터 정보를 탈취하는 부분에서부터 막든, pod마다 IAM을 할당하는 방법(IRSA)으로든 대응할 수 있으니 쿠버네티스 클러스터를 설계하는 단계에서부터 고려해두면 분명 좋은 아키텍처가 될 것이다.

 

 


 

 

2. 오픈소스 보안 점검 도구 - 1 (kubescape)

 

kubescape란?

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

 

https://github.com/kubescape/kubescape/blob/master/docs/architecture.md

  • Kubescape는 IDE, CI/CD 파이프라인 및 클러스터를 위한 오픈 소스 Kubernetes 보안 플랫폼이다. 여기에는 위험 분석, 보안, 규정 준수 및 잘못된 구성 검색이 포함되어 있어 Kubernetes 사용자와 관리자의 소중한 시간, 노력 및 리소스를 절약할 수 있다.
  • Kubescape는 클러스터, YAML 파일 및 Helm 차트를 스캔한다.
    여러 프레임워크(NSA-CISA, MITRE ATT&CK®, CIS 벤치마크 포함)에 따라 잘못된 구성을 감지한다.
  • Kubescape는 ARMO에서 생성했으며 CNCF(Cloud Native Computing Foundation) 샌드박스 프로젝트이다.
 
kubescape의 관리 범위

https://github.com/kubescape/kubescape

 

# 설치
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

# 모니터링
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)

 

 

이러한 결과들을 프로메테우스 Exporter를 통해 프로메테우스에서 취약점 점검 결과 데이터를 수집하고 그라파나로 시각화까지 가능하다.

 

참고 링크

더보기

 

 

결론

kubescape는 쿠버네티스 클러스터 차원에서의 취약점들에 대해 점검 기능을 제공한다.

앞서 설명한 ARMO는 kubescape의 자체 웹 형태의 서비스이다. 이 서비스는 도전과제 2에서 진행해보도록 하자.

 

 

 

 


3. 오픈소스 보안 점검 도구 - 2 (polaris)

 

polaris란?

  • 오픈소스 보안 점검 도구
  • 리소스 구성을 검증하고 수정하는 Kubernetes용 오픈 소스 정책 엔진
    • 30개 이상의 기본 제공 구성 정책과 JSON 스키마로 사용자 지정 정책을 구축하는 기능이 포함
    • 명령줄에서 실행하거나 웹훅으로 실행될 때 Polaris 정책 기준에 따라 문제를 자동으로 해결
 
https://github.com/FairwindsOps/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 신뢰성 : 안정성 구성 여부 확인 - 링크
  • 검사 항목 상세 - 링크

 

 

 

보안 취약점 점검

default namespace에 배포된 워크로드로 확인

 

 

※ 스터디장님이 수행하고 제공해주신 netshoot-pod 취약점 보완한 yaml로 배포 후 확인

 

변경 전

더보기
# 삭제
kubectl delete deploy netshoot-pod

# 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:v0.9
        command: ["tail"]
        args: ["-f", "/dev/null"]
        imagePullPolicy: Always
        resources:
          limits:
            cpu: 150m
            memory: 512Mi
          requests:
            cpu: 100m
            memory: 128Mi
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            drop:
            - ALL
          privileged: false
          readOnlyRootFilesystem: true
          #runAsNonRoot: true
      terminationGracePeriodSeconds: 0
EOF

변경 후

 

Polaris CLI

kops ec2에 polaris CLI 설치하여 워크로드의 yaml 파일을 점검해보자

 

IaC Code도 polaris로 취약점 점검 가능 - 링크

 

결론

이 외에도 Polaris 대시보드 워크로드는 얼마나 견고하게 배포했는지 yaml로 출력해서 살펴보았는데, 고개를 조아리게 되었다... 

이걸 참고하는 것만으로도 많은 공부가 될 것 같다.

 

polaris 파드의 설정 확인

더보기
#
kubectl krew install neat
kubectl get deploy/polaris-dashboard -n polaris -o yaml | kubectl neat > polaris-pod.yaml
cat polaris-pod.yaml | yh
...
spec:
  template:
    spec:
      containers:
        imagePullPolicy: Always   # 이미지를 항상 리포지토리에서 가져오게 설정
        resources:                # 리소스 자원 사용 보장 및 제한  
          limits:
            cpu: 150m
            memory: 512Mi
          requests:
            cpu: 100m
            memory: 128Mi
        securityContext:          # 컨테이너, 파드의 권한 및 접근 제어
          allowPrivilegeEscalation: false   # 컨테이너의 하위 프로세스가 상위 프로세스보다 많은 권한을 얻을 수 없게 설정
          capabilities:                     # 호스트 커널 레벨 수정이 필요 시 root 권한으로 전체 커널을 수정하지 않고 특정 커널 권한만 부여 후 사용
            drop:
            - ALL
          privileged: false                 # true인 경우 호스트의 모든 장치에 접근 권한 가짐, 컨테이너의 root권한이더라도 namespace/cgroup으로 격리되어 호스트의 다른 장치 접근 불가
          readOnlyRootFilesystem: true      # 컨테이너의 root 파일시스템에 읽기 전용
          runAsNonRoot: true                # root 권한으로 컨테이너를 실행하지 않음
...

 


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

 

보안 컨텍스트(SecurityContext)란?

쿠버네티스의 Pod나 컨테이너에 대한 접근 제어 설정(Access Control Setting)이나, 특수 권한 (Privilege)를 설정하는 기능을 제공한다.

예를 들어 컨테이너 내에서 동작하는 프로세스의 유저 ID (UID)나, 그룹 ID (GID)를 설정하거나, 프로세스에 커널에 대한 접근 권한을 부여하는 등의 기능을 수행하도록 설정할 수 있다.

링크

 

이번 파트에서는 이러한 보안 컨텍스트를 실습하며 어떤 기능들을 제한/부여할 수 있는지 알아본다.

 

 

각 컨테이너에 대한 보안 설정 → 침해사고 발생 시 침해사고를 당한 권한을 최대한 축소하여 그 사고에 대한 확대를 방지

 

컨테이너 보안 컨텍스트
종류 개요
privileged 특수 권한을 가진 컨테이너로 실행
capabilities Capabilities 의 추가와 삭제
allowPrivilegeEscalation 컨테이너 실행 시 상위 프로세스보다 많은 권한을 부여할지 여부
readOnlyRootFilesystem root 파일 시스템을 읽기 전용으로 할지 여부
runAsUser 실행 사용자
runAsGroup 실행 그룹
runAsNonRoot root 에서 실행을 거부
SELinuxOptions SELinux 옵션

 

이 중에서 몇가지를 제외하고 하나씩 살펴보도록 한다.

 

컨테이너 보안 컨텍스트 확인

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

# 파일 생성 시도
kubectl exec -it rootfile-readonly -- touch /tmp/text.txt
touch: /tmp/text.txt: Read-only file system
command terminated with exit code 1

# 아래 방법으로 파일 수정 시도하면 되는 이유는?
kubectl exec -it rootfile-readonly -- cat /etc/hosts
kubectl exec -it rootfile-readonly -- sh -c "echo write > /etc/hosts"
kubectl exec -it rootfile-readonly -- cat /etc/hosts

# 파드 상세 정보 확인
kubectl get pod rootfile-readonly -o jsonpath={.spec.containers[0].securityContext} | jq
{
  "readOnlyRootFilesystem": true
}

 

readOnlyRootFilesystem 옵션을 true로 만든 pod에서 파일수정 관련 동작 결과가 상이한 이유

(PKOS 2기 썸업님 블로그를 보고 깨달음 - 링크)

결론만 말하자면 해당 옵션을 설정한다고해도 실제 pod 내에서 각 파티션에 대한 read/write 권한 설정은 다르게 되어 있음

처음에 touch로 파일 생성 시도한 /tmp의 경우 readOnly(정확하게는 / 파티션이 readOnly)지만,

두번째 echo write > /etc/hosts의 경우 해당 파티션은 read/write 모두 가능하도록 설정되어 있음.

자세한 내용은 썸업님의 블로그에 잘 기술되어 있음

 

 

 

Capabilities

슈퍼 유저의 힘을 작은 조각으로 나눔, Capabilities are a per-thread attribute - Capabilities  블로그설명 

 

특정한 관리 작업을 수행할 때 root가 가지고 있는 모든 권한을 부여하는 것이 아니라 해당 작업에 필요한 권한 만을 부여.

프로세스가 악의적인 사용자에게 제어 권한을 넘겨지게 되더라도, 프로그램 구동에 필요한 권한 외 다른 권한이 주어지지 않았으므로 시스템의 피해를 최소화

 

각 기능들은 고유bit값을 가지며 프로세스가 가지는 capability는 보유한 기능들의 bit값의 합으로 표현된다.

관련링크

 

# 현재 시스템(kops-ec2)의 Capabilities 확인 (38개)
capsh --print
...
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
cat /proc/1/status | egrep 'CapPrm|CapEff'
CapPrm:	0000003fffffffff
CapEff:	0000003fffffffff


# 마스터 노드 Linux Capabilities 확인 : 아래 굵은색은 파드 기본 Linux Capabilities(14개)
ssh -i ~/.ssh/id_rsa ubuntu@api.$KOPS_CLUSTER_NAME capsh --print
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

 

파드의 Linux Capabilities 기본 확인

# 샘플 파드 생성
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
Sat Apr  1 02:43:58 UTC 2023

# 파드에서 시간 변경 시도
kubectl exec -it sample-capabilities -- date -s "12:00:00"
kubectl exec -it sample-capabilities -- date

 

권한 부족

 

파드에 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 기본 확인
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_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

# 파드에서 시간 변경 시도 : 안되는 이유는...?
kubectl exec -it sample-capabilities2 -- date
kubectl exec -it sample-capabilities2 -- date -s "12:00:00"
kubectl exec -it sample-capabilities2 -- date

 

시간이 바뀌지 않는 이유를 아직 모르겠다

 

특수 권한 컨테이너 생성 : 호스트와 동등한 권한 부여됨

#
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

# 파드 상세 정보 확인
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

특수 권한을 줬는데도 변경이 안되는 상황.. 다른 해결 방법이 있을 것 같음

 

여기까지가 컨테이너 보안 컨텍스트에 대한 내용이였다. 

다음은 파드 보안 컨텍스트에 대해 알아보고 실습해보자.

앞서 설정은 가능하지만 다루지 않았던 runAsUser/Group/NonRoot를 여기서 다뤄본다.

 


파드 보안 컨텍스트
종류 개요
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

#
kubectl get pod rundefault -o jsonpath={.spec.securityContext} | jq
kubectl get pod runuser -o jsonpath={.spec.securityContext} | jq

# 실행 사용자 정보 확인
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

결과 스크린샷

 

root 사용자로 실행 제한

실행 사용자를 변경하지 않고 단순히 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

# 이벤트 확인
kubectl events --for pod/nonroot

결과

당연하게도 배포되지 않는다

 

파일 시스템 그룹 지정

  • 일반적으로 마운트한 볼륨의 소유자와 그룹은 root:root로 되어 있다. 실행 사용자를 변경한 경우에는 마운트한 볼륨에 권한이 없는 경우가 있다
  • 따라서 마운트하는 볼륨의 그룹을 변경할 수 있도록 되어 있다 (setdig도 설정된다) - 예) emptyDir 혹은 PV 등 volumeMounts

 

#
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

# 실행 사용자 정보 확인
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  1 05:15 demo

kubectl exec -it fsgoup2 -- ls -l /data
drwxrwsrwx 2 root 2000 4096 Apr  1 05:15 demo

# fsgoup2파드에서 파일 생성 및 확인
kubectl exec -it fsgoup2 -- sh -c "echo write > /data/demo/sample.txt"
kubectl exec -it fsgoup2 -- cat /data/demo/sample.txt
kubectl exec -it fsgoup2 -- ls -l /data/demo/sample.txt
-rw-r--r-- 1 1000 2000 6 Apr  1 05:20 /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

결과 스크린샷

더보기
파드 생성 시 마운트한 /data/demo가 아닌 /data 디렉토리는 root 권한이 있어야 제어 가능

 

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

커널 파라미터 변경 적용을 위해서는 컨테이너에서도 설정 필요, 파드 수준 적용으로 컨테이너 간에 공유됨 - 링크

 

  • 커널 파라미터는 안전한 것(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 설정 필요
      • 다만, 본 과정에서는 kubelet 설정 변경은 진행하지 않는다.

 

unsafe 파라미터를 변경

#
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

# 
kubectl events --for pod/unsafe

unsafe 커널 파라미터는 kubelet 설정을 변경하지 않으면 직접 변경 작업을 수행할 수 없다.

 

safe 파라미터 수정

#
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

#
kubectl get pod sysctl1 -o jsonpath={.spec.securityContext} | jq
kubectl get pod sysctl2 -o jsonpath={.spec.securityContext} | jq

# 
kubectl exec -it sysctl1 -- sysctl net.ipv4.ip_local_port_range

kubectl exec -it sysctl2 -- sysctl net.ipv4.ip_local_port_range

결과 스크린샷

더보기

unsafe 커널 파라미터 값은 직접 변경 작업이 가능하다.

 

kubelet 설정 변경없이 unsafe 커널 파라미터 변경하는 방법은?

(스터디장님이 제공해주신 방법)

# initContainer 와 privileged 를 활용하여 unsafe 커널 파라미터를 강제로 변경
curl -s -O https://raw.githubusercontent.com/MasayaAoyama/kubernetes-perfect-guide/ko/2nd-edition/samples/chapter13/sample-sysctl-initcontainer.yaml
cat sample-sysctl-initcontainer.yaml| yh
kubectl apply -f sample-sysctl-initcontainer.yaml

#
kubectl describe pod sample-sysctl-initcontainer
kubectl get pod sample-sysctl-initcontainer -o json | jq

# 확인
kubectl exec -it sample-sysctl-initcontainer -c tools-container -- sysctl net.core.somaxconn

진행 과정 및 결과 스크린샷

 

커널 파라미터는 무엇이고, 꼭 변경해야 하나?

리눅스 시스템의 커널에서 정의된 자원 설정값
해당변수값을 변경하여, 네트워크 성능, 서버 자원을 사용하는 프로세스들의 성능 등을 향상시킬수 있다.
커널 파라미터 값을 변경하면, 프로세스가 런타임중에도 즉시 반영되어 시스템을 최적화, 튜닝이 가능하다.
링크

 

 

 

결론

여기까지 열심히 쓰다가 글이 날아갔다... 임시저장도 했었는데 하하하

새벽까지 썼던 글이라 기억이 잘 안나지만, 이제 쿠버네티스 클러스터에 워크로드를 배포/운영할 때 필요하다면 커널 파라미터까지도 변경할 수 있는 사람이 되었다!

 


5. K8S 인증/인가 & RBAC

 

이쪽은 스터디 자료에도 나와있지만 김태민님 기술 블로그가 정말 잘 되어 있다.

K8S(API 접근) 인증/인가 소개 : 출처 - 김태민님 기술 블로그 - 링크1 링크2

  • 서비스 어카운트(Service Account)
  • API 서버 사용 : kubectl(config, 다수 클러스터 관리 가능), 서비스 어카운트, https(x.509 Client Certs) - 링크
  • API 서버 접근 과정 : 인증 → 인가 → Admission Control(API 요청 검증, 필요 시 변형 - 예. ResourceQuota, LimitRange) - 참고

 

 

인증(Authentication) 방식

  • X.509 Client Certs : kubeconfig 에 CA crt(발급 기관 인증서) , Client crt(클라이언트 인증서) , Client key(클라이언트 개인키) 를 통해 인증
  • kubectl : 여러 클러스터(kubeconfig)를 관리 가능 - contexts 에 클러스터와 유저 및 인증서/ 참고
  • Service Account : 기본 서비스 어카운트(default) - 시크릿(CA crt 와 token)

 

인가(Authorization)

  • 인가 방식 : RBAC(Role, RoleBinding), ABAC, Webhook, Node Authorization
  • RBAC : 역할 기반의 권한 관리, 사용자와 역할을 별개로 선언 후 두가지를 조합(binding)해서 사용자에게 권한을 부여하여 kubectl or API로 관리 가능
    • Namespace/Cluster - Role/ClusterRole, RoleBinding/ClusterRoleBinding, Service Account
    • Role(롤) - (RoleBinding 롤 바인딩) - Service Account(서비스 어카운트) : 롤 바인딩은 롤과 서비스 어카운트를 연결
    • Role(네임스페이스내 자원의 권한) vs ClusterRole(클러스터 수준의 자원의 권한)

 

결론

인증/인가 부분은 굉장히 봐야하는 부분이 많지만, 큰 흐름은 CKA를 취득하면서 이해하고 있고 딮한 부분은 별도로 공부하도록 한다!

실습 부분을 건너뛰고 도전과제4를 통해 어떤 흐름으로 작업되는지 확인해본다.

 

 

 


6. 도전 과제

 

드디어 도전과제까지 왔다.

 

 

[과제1] 파드에서 EC2 메타데이터의 IAM Role 토큰 정보를 활용하여(boto3), 스터디에서 소개한 것 이외의 다른 AWS 서비스(혹은 Action)를 사용 후 코드나 스샷

boto3로 진행하고 3개 정도를 진행했다.

  • Account ID 확인
  • NIC(Network Interface Card) 생성
  • ELB 생성
들어가기에 앞서
토큰 정보를 정확하게 사용하기 위해서 boto3.client 메서드 호출할 때 인자값으로 AccessKeyID, SecretAccessKey, Token 값을 넣어주었다. (aws_access_key_id, aws_secret_access_key, aws_session_token)
이때 cat <<EOF> 방식은 사용하지 않는다.
그 이유는 Token 값을 입력할 때 EOF 방식을 이용하게 되면 한 줄에 입력할 수 있는 텍스트 길이 제한 때문인지 Token 값이 전체가 입력되지 않는다.

다만, 이미 kops를 통해 worker node 한쪽의 보안 설정을 열어놓은 상태이고, 해당 node에 배포된 pod를 통해서 수행하는 것이기 때문에 메타데이터 값들을 굳이 넣어주지 않아도 된다.

 

Account ID 확인

vi account_check.py
##################################
import boto3

sts = boto3.client(
  'sts',
  aws_access_key_id="ASIAY~~~~",
  aws_secret_access_key="Y0e3oBF~~~~",
  aws_session_token="IQoJb3JpZ2lu~~~",
  region_name = 'ap-northeast-2',
)
AWS_ACCOUNT_ID = sts.get_caller_identity()["Account"]
print(AWS_ACCOUNT_ID)
##################################
python account_check.py

 


NIC(Network Interface Card) 생성

 


ELB 생성

이걸 해낸건 삽질의 연속과 집념 덕이다...

 

그 과정을 천천히 따라가보자. 우선 기본적으로 boto3 Docs를 보고 간략한 example 코드로 진행해본다. - 링크

 

vi create_elb.py
#========================================================================

import boto3

client = boto3.client(
  'elbv2',
  aws_access_key_id="ASIA~~~",
  aws_secret_access_key="Y0e3oB~~~~",
  aws_session_token="IQoJb3JpZ2~~~~~",
  region_name = 'ap-northeast-2',
)

response = client.create_load_balancer(
    Name='my-load-balancer',
    Subnets=[
        'subnet-0963ad6ff1a83bafb',  ## 현재 배포된 subnet1
        'subnet-0e0e345079f3bb5be',  ## 현재 배포된 subnet2
    ],
)

print(response)
#========================================================================

python create_elb.py

Access Denied 에러 발생

 

대충 읽어봐도 자세히 읽어봐도 이걸 수행하기에 권한이 부족하다고 밖에 안 읽힌다. 

 

해결 방법 1 (꼼수)

더보기

node의 EC2가 가진 권한 정책에 ELB에 대한 Full 권한 정책을 추가해주었다.

손쉽게 배포되는 것을 확인했다.

하지만 이런 꼼수말고 정공법으로 해결해보자.

 

해결 방법 2 (정답)

더보기

kops 클러스터 배포하고나서 항상 작업하는 AWSLoadBalancerControllerIAMPolicy 정책 추가를 하고나면 아래와 같은 권한이 추가로 생긴다.

에러 메시지를 확인해보면 이 권한이 있으니 되어야 하는 게 아닌가? 라고 생각했는데 아래 달려있는 Condition을 잘 봐야 한다.

 

해석해보자면 elbv2 형태의 Loadbalancer를 Create하기 위해서는 아래 조건의 태그값을 가져야 한다.

(이 Condition에 대한 내용, RequestTag가 뭔지에 대해 무지해서 이해하면서 해결하는 데에 시간이 좀 오래 걸렸다.)

  • Key : elbv2.k8s.aws/cluster
  • Value : Null이 아니어야 함

Condition-Null-링크

aws:RequestTag-링크

 

다시 boto3 Docs에서 Tag를 어떻게 추가하면 되는지 확인해보고 코드에 적용해보자

vi create_elb.py
========================================================================

import boto3

client = boto3.client(
  'elbv2',
  aws_access_key_id="ASIAYA~~~~",
  aws_secret_access_key="Y0e3o~~~~~",
  aws_session_token="IQoJb3J~~~~~~~",
  region_name = 'ap-northeast-2',
)

response = client.create_load_balancer(
    Name='my-load-balancer',
    Subnets=[
        'subnet-0963ad6ff1a83bafb',   ## 현재 배포된 subnet1
        'subnet-0e0e345079f3bb5be',   ## 현재 배포된 subnet2
    ],
    Tags=[
        {
            'Key': 'elbv2.k8s.aws/cluster', ## 태그 추가
            'Value': ''
        }
    ],

)

print(response)
========================================================================

python create_elb.py

 결과

되고나서 좀 신이난 나머지 태그 Value값을 바꿔가며 어떤 것들이 되는지 확인해봤다.

netcloudy.im, netcloudy.im2, test, 빈값('') 까지 잘 적용된다.

 

 

 


 

[과제2] 책 398~400페이지 - kubescape armo 웹 사용 후 관련 스샷

https://cloud.armosec.io/ 로 접속

현 화면은 이미 Initial 작업이 완료된 상태지만 초기에 Sign Up 후 로그인하면 보유한 클러스터와 연동할 수 있는 CLI 명령 줄은 제공해준다

아래는 명령줄 입력한 결과

연동이 끝나면 Compliance 탭을 통해 여러가지 취약점 점검에 걸러진 리소스들을 볼 수 있다.

아래는 그 중 Resource limits(C-0009) 항목에 적발된 리스트다.

여기서 boto3-pod라는 deployment를 Fix 버튼을 클릭하면 아래와 같은 화면이 나온다.

친절하게 개선하면 될 Yaml을 작성해주고 표시까지 해준다...너무 친절하다..

 

 


 

[과제3] polaris 관련 실습(아무거나) 후 관련 스샷

 

boto3-pod를 좋아하기 때문에 이걸로 간략하게 점검 내용 보완 후 재배포하는 것을 해보았다.

 

kubectl delete deployment boto3-pod
#########################################
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:latest
        resources:
          requests:
            memory: "64Mi"
            cpu: "250m"
          limits:
            memory: "128Mi"
            cpu: "500m"
        command: ["tail"]
        args: ["-f", "/dev/null"]
      terminationGracePeriodSeconds: 0
EOF

결과

 

스터디장님이 이미 잘 제공해주신 내용들이 스터디 자료에 있었기에 내 마음에 충족되는 결과물은 아니다.

다만 나중에는 프로메테우스 스택과 연동하여 그라파나 대시보드에 시각화하는 걸 해보면 좋을 듯 하다.

 

 


 

[과제4] 신규 서비스 어카운트(SA) 생성 후 클러스터 수준(모든 네임스페이스 포함)에서 읽기 전용의 권한을 주고 테스트 후 코드나 스샷

테스트를 위해 namespace 추가 생성 및 pod 생성

kubectl create ns netcloudy
kubectl describe -n netcloudy serviceaccounts
kubectl describe -n netcloudy secrets


cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: pod-1
  namespace: netcloudy
  labels:
     app: pod
spec:
  containers:
  - name: container
    image: kubetm/app
EOF

결과

 

SA, ClusterRole, ClusterRoleBinding 리소스 배포

vi challenge4.yaml
##################################
apiVersion: v1
kind: ServiceAccount
metadata:
  name: all-reader
  namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: CR-reader
rules:
- apiGroups: [""]
  resources: ["*"]
  verbs: ["get", "watch", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: CRB-reader
subjects:
- kind: ServiceAccount
  name: all-reader
  namespace: default
  apiGroup: ""
roleRef:
  kind: ClusterRole
  name: CR-reader
  apiGroup: rbac.authorization.k8s.io
##################################
kubectl apply -f challenge4.yaml

해당 SA(Service Account)로 여러가지 kubectl 명령어 테스트

모든 네임스페이스의 pod,service,endpoint 확인

 

deployment 수정(edit)

pod 수정(edit)

 

기존 kops-ec2에서 진행해본다면?

pod 수정은 정상적인 부분을 수정하지 않아서 취소 처리됨

 

 

결론

보안 파트는 정말 다양한 범위를 다룬 것 같다.

넓고 깊게 공부해볼 수 있는 기회였다. (스터디 자료는 깊게 볼 수 있는 장을 열어주었고, 그걸 떠먹는 내가 잘 떠먹어야 더 깊이가 생기겠지만... 스스로 부족하다고 느낀다.)

확실히 스터디를 하면서 내가 모르는 부분을 알게 되는 점이 가장 좋았다.

이 말이 단순히 몰랐던걸 알았네? 가 아니라 내가 이쪽 분야에서 뭘 모르는 지도 모르는 상태에서의 깨달음이기에 더 값지다고 할 수 있다. (무지한 상태인 걸 깨우쳤습니다 소크라테스 선생님...)

 

 

5주간 매주 힘들었지만, 그나마 다행이였던 것은 주차별로 커리큘럼의 레벨 디자인이 잘 되어있어서 첫 주차에 포기하게 되거나, 갑자기 너무 쉬워진다거나하는 일 없이 잘 할 수 있게 되었다.

 

 

이렇게 값진 스터디를 할 수 있게 해주신 CloudNet@팀 가시다님과 많은 분들께 다시 한번 감사드립니다.

 

댓글