카이도스의 Tech Blog

PKOS 7주차 - 보안 본문

KOPS

PKOS 7주차 - 보안

카이도스 2023. 2. 27. 13:06
728x90
반응형

쿠버네티스 공부는 ‘24단계 실습으로 정복하는 쿠버네티스 책을 기준으로 진행.


kubescape

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

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

kubescape?

 

kubescape는 DevOps를 위한 kubernetes용 보안 플랫폼이다. NSA/CISA의 ‘쿠버네티스 보안강화지침(kubernetest hadrdening guidance)’에 맞게 안전하게 배포되었는지 테스트하는 도구로 단일 yaml파일부터 클러스터 단위의 테스트를 수행한다. 또한 그 결과로 JSON 형태의 출력을 하여 이를 기반으로 CI를 비롯한 다른 시스템의 연동이 용이하다.

쿠버네티스 보안강화지침의 주요 내용

  • 취약점 또는 잘못된 구성을 찾기 위해 컨테이너와 포드 스캔하기
  • 최소한의 권한으로 컨테이너와 포드 실행하기
  • 네트워크를 분리하여 발생할 수 있는 데미지를 제어하기
  • 방화벽을 사용해서 필요없는 네트웍 연결을 제한하고, 암호화하기
  • 강력한 인증 및 권한관리를 통해서 사용자 및 관리자의 접근을 제한하고, 공격 표면(Attack Surface)을 한정시키기.
  • 관리자가 활동을 모니터링하고, 잠재적인 위험 동작에 대해 경고할 수 있도록 Log Auditing 사용하기
  • 정기적으로 모든 k8s 설정을 검토하고, Vulnerability 스캔을 사용해서 위험 방지및 보안 패치가 적용되었는지 확인하기

kubescape의 핵심가치

  • 쿠버네티스 상에서 잘못된 설정정보나 취약점을 감지
  • 이를 수치화해서 위험정도를 체크하고 과거의 수치들을 기반으로 경향도 파악
  • 실시간 설정변경파악
  • 특정 기준(NSA, Mitre, Devops best practice)을 준수하도록 프레임 워크들 제공하고 사용자화도 할수 있음
  • 예외관리를 통해 무분별한 알람발생 제어
  • 다양한 devops 도구들고 연동 (Jenkins, CircleCI, Github workflows, Gitlab, Prometheus, Slack,…)
  • 사용하기 쉬운 cli 제공과 유연한 결과출력 조정 (json, junit xml..)
  • 쿠버네티스의 상태와 정책의 준수를 보여주는 ui
  • 복구지원 - 장애지정과 원인을 알려주고 자원 선택시 crd에서 문제를 일이킨 지점으로 이동
  • 이미지 스캔 - 취약점을 보여주는데 편리하게 정령이나 필터등 가능
  • RBAC 생성지원 - 시각화 지원을 통해 클러스터에 추가되는 rbac의 복잡도를 줄여줌

내부동작

  • OPA 엔진을 기반으로 하고 있고 ARMO의 조작도구를 사용한다.
  • ARMO가 개발한 rego snippet 들을 실행하여 스캔을 수행
  • 다양한 기준을 얼마나 준수하고 있는지 파악

링크 - https://devocean.sk.com/blog/techBoardDetail.do?ID=164199 


polaris

소개 및 설치 : 오픈소스 보안 점검 도구, Polaris is an open source policy engine for Kubernetes that validates and remediates resource configuration - helm

  • 여기에는 30개 이상의 기본 제공 구성 정책과 JSON 스키마로 사용자 지정 정책을 구축하는 기능이 포함됩니다.
  • 명령줄에서 실행하거나 변형 웹후크로 실행될 Polaris 정책 기준에 따라 문제를 자동으로 해결할 있습니다.


실습환경 구성

kops 인스턴스 t3.small & 노드 t3.xlarge (vCPU 4, Memory 16GiB) 배포 및 kops 인스턴스(t3.small)에 SSH 접속


# 배포 완료 후 접속

# EC2 instance profiles 에 IAM Policy 추가(attach)
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
aws iam attach-role-policy --policy-arn arn:aws:iam::$ACCOUNT_ID:policy/AllowExternalDNSUpdates --role-name masters.$KOPS_CLUSTER_NAME
aws iam attach-role-policy --policy-arn arn:aws:iam::$ACCOUNT_ID:policy/AllowExternalDNSUpdates --role-name nodes.$KOPS_CLUSTER_NAME

 


EC2 IAM Role & 메타데이터

워커 노드 1대 EC2 메타데이터 보안 제거 ⇒ 적용 및 재생성 5분 시간 소요

#
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
 

[매뉴얼] Boto3가 AWS의 자격증명(Credentials)을 확인하는 순서 .from Python

  I.   개요 이번 포스팅은 'Boto3가 AWS의 자격증명(Credentials)을 확인하는 순서' 인 Credentials — Boto3 Docs 1.17.21 documentation > Configuring credentials

tech.cloud.nongshim.co.kr

파드에서 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

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

파드(컨테이너) 탈취 후 EC2 메타데이터의 IAM Role 토큰 정보를 활용해 python boto3를 통해 SDK로 AWS 서비스 강제 사용 - boto3

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

# Pod2에서 성공!!
# 실습 완료 후 삭제
kubectl delete deploy boto3-pod

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)

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"

Image tag 조치 - 링크

# 기존 netshoot-pod 삭제
kubectl delete deploy netshoot-pod

# netshoot-pod 1대 생성
cat <<EOF | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: netshoot-pod
spec:
  replicas: 1
  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"]
      terminationGracePeriodSeconds: 0
EOF

deployment 의 replica 조치 후 확인

kubectl scale deployment netshoot-pod --replicas 2

보안 모범 사례 적용 후 재점검

폴라리스 파드의 설정 참고

# 설치 및 확인
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 권한으로 컨테이너를 실행하지 않음
...

netshoot-pod 에 보안 모범 사례 적용

# 삭제
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 파드와 CLB가 완료되었음에도 polaris.pjhtest.click 상태를 확인해봤다.

a38ef87cdbbe84265a4f545160f11186-2026951006.ap-northeast-2.elb.amazonaws.com로는 접속이 되지만 Route53 레코드가 없어서 아래 레코드 추가 후 정상적으로 사이트 호출 확인했다.

polaris.pjhtest.click CNAME 단순 - a38ef87cdbbe84265a4f545160f11186-2026951006.ap-northeast-2.elb.amazonaws.com


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

nodes.pjhtest.click 에 AmazonS3FullAccess IAM 권한 추가

# 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> s3.py
import boto3

s3 = boto3.client('s3')
response = s3.list_buckets()
print(response)
EOF

python s3.py 
exit
------------

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

조회 완료


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

1. https://portal.armo.cloud/ 접속

2. 회원가입

3. 클러스터 연결을 위해 필요한 패키지 설치

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 account=b71b65a7-967c-48b2-8ffc-00cc1ef8d3a0 --set clusterName=`kubectl config current-context

4. 패키지 설치 후 자동 검색 후 연결완료 되면 아래와같이 확인됨

 


[과제3] polaris 관련 실습(아무거나) 후 관련 스샷을 올려주세요

위에 polaris 실습 부분에서 진행


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

# 신규 어카운트 생성
kubectl create sa infra-k8s-cluster -n infra-team
# 신규 POD 생성
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: infra-kubectl-cluster
  namespace: infra-team
spec:
  serviceAccountName: infra-k8s-cluster
  containers:
  - name: kubectl-pod
    image: bitnami/kubectl:1.24.10
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0
EOF

# cluster 롤 생성
cat <<EOF | kubectl create -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: role-infra-team-cluster
  namespace: infra-team
rules:
- apiGroups: [""]
  resources: ["*"]
  verbs: ["get", "watch", "list"]
EOF

# cluster 롤 확인
kubectl describe clusterRole role-infra-team-cluster -n infra-team
Name:         role-infra-team-cluster
Labels:       <none>
Annotations:  <none>
PolicyRule:
  Resources  Non-Resource URLs  Resource Names  Verbs
  ---------  -----------------  --------------  -----
  *          []                 []              [get watch list]

# 롤바인딩 생성 : '서비스어카운트 <-> 롤' 간 서로 연동
cat <<EOF | kubectl create -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: roleB-infra-team-cluster
  namespace: infra-team
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: role-infra-team-cluster
subjects:
- kind: ServiceAccount
  name: infra-k8s-cluster
  namespace: infra-team
EOF

# 확인
kubectl describe clusterrolebindings roleB-infra-team-cluster -n infra-team
Name:         roleB-infra-team-cluster
Labels:       <none>
Annotations:  <none>
Role:
  Kind:  ClusterRole
  Name:  role-infra-team-cluster
Subjects:
  Kind            Name               Namespace
  ----            ----               ---------
  ServiceAccount  infra-k8s-cluster  infra-team
# 파드로 Shell 접속하여 정보 확인 : 단축 명령어(alias) 사용
alias k1='kubectl exec -it infra-kubectl-cluster -n infra-team -- kubectl'

# 권한 테스트
k1 get pods 
k1 run nginx --image nginx:1.20-alpine
k1 get nodes
k1 get ns
k1 create ns test

# (옵션) kubectl auth can-i 로 kubectl 실행 사용자가 특정 권한을 가졌는지 확인
k1 auth can-i get pods
yes

728x90
반응형
Comments