보안
Mont Kim / March 2023 (1522 Words, 9 Minutes)
7주차
KOPS를 이용한 프로비저닝
➝ 저번주차와 다르게 kops edit명령어를 별도로 선언하지않아도 cluster 정보가 custom 되어있는 클러스터를 프로비전이 할 수 있다.
추가적으로 이번주차에서 다루는 내용에선 큰 사이즈의 노드가 필요하지않기때문에, c5.2xlarge 가 아닌 t3.xlarge 인스턴스로 프로비저닝을 진행한다.
첫번째로 EC2의 IAM ROLE의 메타데이터를 제거한다.
kops edit ig nodes-ap-northeast-2a
---
spec:
instanceMetadata:
httpPutResponseHopLimit: 1
httpTokens: required
---
세줄을 삭제해 해당 노드(AZ지만 해당AZ에 노드 한대)에서 메타데이터를 삭제한다.
Boto3을 이용해 pod에서 ec2의 메타데이터가 조회 가능한지 확인한다.
# 파드 이름 변수 지정
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/securi
권한이 있는 노드에서만 인스턴스의 메타데이터를 확인 할 수 있다.
키를 조금 더 확대해보면 다음과 같다.
{
"Code": "Success",
"LastUpdated": "2023-03-02T13:57:43Z",
"Type": "AWS-HMAC",
"AccessKeyId": "ASIAT65RMYESLPVSJLFJ",
"SecretAccessKey": "5tXQiXVyVZ+xnsZSkTwfF/cDGrieLM8zAQWNy4xz",
"Token": "IQoJb3JpZ2luX2VjEP7//////////wEaDmFwLW5vcnRoZWFzdC0yIkgwRgIhALaidTi8U836bYs04n4A/ayxXX1vihJmsNVmv2Tdm45kAiEAu1u7j86RiJATmW8epkfPFZ7eUJZka+eNaQBTb7CCFS4q1QUIp///////////ARAAGgwyNzI1NjU1ODQxNjQiDCXN19y8VktBq3cG1iqpBUspF6MmaYc2qAve4y4EkkZXe2AaRbVJSZP34JsZ7a+Jjh0/15Xe84x7VIeRIOyK12820il+6+F6wu1OkXHgNMp4NXrAmPALdG+AzHMMMMwWNOqHq2cGFjoIjY8UHa/Yl2WonqeXKmJjAsMld105TVDTcadD+rdT1tceZNfcy1SCsAL/g2lIV50hiK57G+6IummCz2kkMFNuDPdI6KQowb2g0cbGdQMbKtmGrE0UD1pYao937xEEmb4xvTbWNC+s/8ux1SromRuPU6j8ej517TfzVnmlEk8gpmsFptsqgwrRgtO/d1WthKFSUvMf3P8UozwdMVavgQae22nokm7x2a3Pt5/XNVxgFIX7tljShS4Dmoi7+toixADSjY9GPJ+ORrL160t4LeizeLlq4LXdF2gRyDd7d9KACxHxzy/69mHWTizVC/O9WFCn7Q5omkenf1E7Tv6IPK/T+0r8IzeOrEx4VKFbwI8zqqUqmAaMClAtiPpmA/UJUcii2WAclg36Qdzx7VbfUkgRavy+tUnyAg957OjmfQA/QBjMfpNdkvA3UEdiDbDfk/Axu9RTuNuhF2oJetFCP4td8c9p79IYg5FreSsZFnnUiCT+Ez72p1cSwDvQLNle1B/KElscuyJoXc6oSTD2jq8WlNnmC9x/OfyRhiBlT+KOdrzodeSsasOr7pD5WZYTpih+WE5hQL1CAM+l9kLwDv6oLNpZJdlmV2Dt7XA6SGJs9ic80jYZvoJEfhL5o80lJAIUdx8nFtm+ygV5Fa1vqR36AJkKA32g3dZQDJjAyRe8W5rWeounu+ukS03wjMAvWeUuue6wTJ5RO/pkjXfq3eMUCrkqmYGoTCqiF06ZCqFpepkPDFDKvqUZswq6SfzlN5Kfk0gLjek1Rm4CqVbYI7Ic5TC71oKgBjqwAVeeFxy183C86T4U8JQ9ahfsGgMyOYXg4hDGgxUIiRhtAmXUkpJaQsR+dURpGvKTX7+w064tP0H6FY19pg9p3/meOLC81s2ZnwINxpcgHUMsHGy0QCAtMBlemiJA5uza2QozoiwRprCUDmAKlW+U/uSMJxEzc+r7ZITwUcGofxNibl7pS1yZ5rV0YcY94V2eCehCNsiUYm/vHHr5Tl04dwifLS/SNNWqlNLH4uHcPQ2L",
"Expiration": "2023-03-02T20:32:15Z"
}
7주차 과제
[과제1]
파드에서 EC2 메타데이터의 IAM Role 토큰 정보를 활용하여(boto3), 스터디에서 소개한 것 이외의 다른 AWS 서비스(혹은 Action)를 사용 후 코드나 스샷을 올려주세요
새로운 IAM을 추가해 인스턴스에 적용을 한다.
IAM 콘솔에서 역할 ➝ 역할 생성
사용사례 ➝ EC2 를 선택해
정책을 선택한다.
해당 역할을 선택후 역할생성은 완료한다
이후에 인스턴스에서 해당 IAM을 선택해 별도로 적용해주는 과정을 거쳐야 한다.
EC2 콘솔에 들어가 인스턴스 정보 수정을 진행한다.
작업 ➝ 보안 ➝ IAM 역할수정을 선택해 역할을 수정 또는 편집을 진행한다.
방금전 IAM 콘솔에서 생성했던 Read Only 인스턴스 IAM을 선택 후,
IAM 역할 업데이트를 시작한다.
boto3 이미지를 사용해 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})
# 파드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 desc
PODNAME1의 경우엔 권한이 없어 정보획득에 실패한것을 볼 수 있다.
반면에 PODNAME2의 경우엔 노드의 정보들을 그대로 확인한것을 볼 수 있다.
kubescape
kubescape는 쿠버네티스 클러스터의 yaml, helm chart등의 취약점을 점검하는 도구이다.
cli 기반 환경을 다루는것과 web에서 다루는것은 큰 차이가 없으나, 과제에서 다루는 내용과 동일하여 과제설명창에서 같이 다룬다.
[과제2]
책 398~400페이지 - kubescape armo 웹 사용 후 관련 스샷을 올려주세요
kubescape 다운로드
curl -s https://raw.githubusercontent.com/kubescape/kubescape/master/install.sh | /bin/bash
kubescape download artifacts
kubescape scan --enable-host-scan --verbos
로컬에서 kubescape scan 명령어를 하면 위와같이 결과물이 나온다.
웹에서 회원가입후 Repository Scan 버튼을 누르면, account info가 출력된 명령어를 안내해준다.
문제는 웹에서 정보 연동을 위한 account 파라미터를 추가해도 실패한다…
- v 옵션을 붙여 자세한 정보를 확인해보려했지만, 여전히 연동이되지않는다.
이유가 뭘까…?
Polaris
Polaris 또한 kubescape와 유사한 오픈소스 보안 점검 도구이다.
kubescape와 동일하게 cli기반 / 웹기반 서비스가 모두 존재하기때문에, web에서 다루는 과제의 내용과 함께 다루도록 한다.
[과제3]
polaris 관련 실습(아무거나) 후 관련 스샷을 올려주세요
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.yam
polaris-values.yaml 파일에서 Classic Loadbalancer Type로 생성을하지만
annotate service 기능을 이용해 도메인을 연결이 안된다…
CLB이기때문에 생성까지 약 5분 소요된다.
CLB 주소로 로그인을 진행한다.
AWS EC2 ➝ 로드밸런서 항목에서 Endpoint를 획득하거나
kubectl get svc
에서 서비스에 부여된 Endpoint를 획득해 해당 주소로 polaris에 접근한다.
polaris 서비스에 접속하면 클러스터의 정보를 조회 할 수 있다.
클러스터 한정이 아닌, 특정 pod 자체에 대해 진단결과를 확인 할 수 있다
cat <<EOT > polaris-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: polaris-demo
spec:
containers:
- name: nginx
image: nginx:latest
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
EOT
kubectl apply -f polaris-demo.yaml
해당 pod을 배포후, polaris 웹 에서의 “Filter by namespace” 를 apply 해
polaris-demo pod의 상태를 확인 할 수 있다.
demo pod의 몇가지 문제점에 대해 진단을 해준것을 확인할 수 있다.
해당 문제점을 수정후 pod을 재배포 해본다.
kubectl delete -f polaris-demo.yaml
cat <<EOT > polaris-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: polaris-demo
spec:
containers:
- name: nginx
image: nginx:1.23.3
imagePullPolicy: Always
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
securityContext:
runAsUser: 1000
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
EOT
kubectl apply -f polaris-demo.y
문제점을 수정후 배포된 pod의 상태를 진단한다
warning은 모두 사라지고, 주의가 필요한 pod중, pod이 정상적으로 구동되고있는지
readiness, liveness 를 추가해주면 좋다는 조언만 한 채로 마친다.
web에서 생성되어있는 pod을 확인해보는것 뿐만 아니라
cli 기반 환경에서도 작성한 yaml 파일을 진단 할 수 있는 기능도 있다.
wget https://github.com/FairwindsOps/polaris/releases/download/7.3.2/polaris_linux_amd64.tar.gz
tar zxvf polaris_linux_amd64.tar.gz
mv polaris /usr/local/bin
polaris audit --audit-path ~/pkos/1/mario.yaml --format=pretty
polaris에서 주로 진단해주는 기능들은 security에 관련된것이 많다.
CKS 시험에서도 주로 다루는 영역이기도하면서
실제로 보안 (pod내부에서 호스트에 미치는 영역) 등에 관련된 항목들이 많은것 같다.
주로 확인해볼만한 부분은 securityContext와 Pod Security Policy 인것같다.
다만 PSP의 경우 1.21버젼에서 deprecated 되었기때문에 특별히 다루지않는다.
인증 / 인가 & RBAC
인증 및 인가에 개념에 대해 간단하 다뤄본다.
인증은 자격을 증명하는 행위이고
인가는 해당 인증의 접근권한을 부여/거부하는 행위입니다
쿠버네티스에서의 인증은 X509 인증서 (kube config 에서 사용하는 ca.crt, client.crt, client key)
kubectl에서 사용하는 config와 context들
그리고 Service Account가 있다.
반면에 쿠버네티스에서의 인가는 RBAC, ABAC, Webhook, Node Authorization이 있다.
가장 대표적으로 사용되는 인가는 RBAC이다
새로운 SA를 생성해서 dev와 infra 관리자를 구현하는 과정을 담아본다
kubectl create namespace dev-team
kubectl create ns infra-team
kubectl create sa dev-k8s -n dev-team
kubectl create sa infra-k8s -n infra-team
cat <<EOF | kubectl create -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: role-dev-team
namespace: dev-team
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]
EOF
cat <<EOF | kubectl create -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: role-infra-team
namespace: infra-team
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]
EOF
cat <<EOF | kubectl create -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: roleB-dev-team
namespace: dev-team
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: role-dev-team
subjects:
- kind: ServiceAccount
name: dev-k8s
namespace: dev-team
EOF
cat <<EOF | kubectl create -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: roleB-infra-team
namespace: infra-team
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: role-infra-team
subjects:
- kind: ServiceAccount
name: infra-k8s
namespace: infra-team
E
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
name: dev-kubectl
namespace: dev-team
spec:
serviceAccountName: dev-k8s
containers:
- name: kubectl-pod
image: bitnami/kubectl:1.24.10
command: ["tail"]
args: ["-f", "/dev/null"]
terminationGracePeriodSeconds: 0
EOF
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
name: infra-kubectl
namespace: infra-team
spec:
serviceAccountName: infra-k8s
containers:
- name: kubectl-pod
image: bitnami/kubectl:1.24.10
command: ["tail"]
args: ["-f", "/dev/null"]
terminationGracePeriodSeconds: 0
EOF
alias k1='kubectl exec -it dev-kubectl -n dev-team -- kubectl'
alias k2='kubectl exec -it infra-kubectl -n infra-team -- kubectl'
k1 get pods
k1 run nginx --image nginx:1.20-alpine
k1 get pods -n kube-system
k2 get pods
k2 run nginx --image nginx:1.20-alpine
k2 get pods -n kube-system
각각 role와 rolebinding을 통해 각각 dev / infra의 권한을 부여해주고 binding을 통해 연동해줬다
dev-k8s 라는 Service Account와 infra-k8s라는 Service Account를 생성해 각각 pod의 정보 조회, pod 생성하는 과정을 담았다
[과제4]
신규 서비스 어카운트(SA) 생성 후 ‘클러스터 수준(모든 네임스페이스 포함)에서 읽기 전용’의 권한을 주고 테스트 후 코드나 스샷을 올려주세요
SA 생성후 권한을 부여하는과정은
CKA에서 다루는 비중이 큰 문제이기도하다
Role Based Access Control (RBAC)을 자랑하는 쿠버네티스의 대표적인 기능이라고도 볼 수 있기 때문이다.
Service Account, ClusterRole을 생성후
ClusterRolebinding을
clusterrole.yaml
cat <<EOF | kubectl create -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: kube-reader-cluster-role
rules:
- apiGroups:
- '*'
resources:
- '*'
verbs:
- get
- list
- watch
EOF
clusterrole을 생성한다.
kubectl create sa kubernetes-monitor
kubectl create clusterrolebinding kubernetes-monitor --clusterrole=kube-reader-cluster-role --serviceaccount=default:kubernetes-monitor
kubectl describe clusterrolebindings kubernetes-monito
service account를 생성하고
생성했던 clusterrole와 serviceaccount를 연결하는 clusterrolebinding을 생성한다.
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
name: read-only-kubectl
namespace: default
spec:
serviceAccountName: kubernetes-monitor
containers:
- name: kubectl-pod
image: bitnami/kubectl:1.24.10
command: ["tail"]
args: ["-f", "/dev/null"]
terminationGracePeriodSeconds: 0
EOF
생성된 SA를 이용해 pod을 생성하는 yaml 파일을 작성후 실행해본다.
kubectl exec -it read-only-kubectl -n default -- ls /run/secrets/kubernetes.io/serviceaccount
kubectl exec -it read-only-kubectl -n default -- cat /run/secrets/kubernetes.io/serviceaccount/token
kubectl exec -it read-only-kubectl -n default -- cat /run/secrets/kubernetes.io/serviceaccount/namespace
kubectl exec -it read-only-kubectl -n default -- cat /run/secrets/kubernetes.io/serviceaccount/ca.crt
pod에 부여된 정보들을 확인해본다.
alias k3='kubectl exec -it read-only-kubectl -n default -- kubectl'
이제 생성한 pod을 이용해 kubectl 명령어를 사용하는 단축키를 지정했다.
k3 get pods -n kube-system
k3 run nginx --image nginx:1.20-alpine
Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:default:kubernetes-monitor" cannot create resource "pods" in API group "" in the namespace "default"
command terminated with exit code
get pod는 정상적으로 응답이 오지만, run 명령어는 실행 할 수 없는것을 확인 할 수 있다.
이렇게 이번주차마저 스터디가 끝났다 7주차로 구성된 PKOS 스터디는 길다면 길고 짧다면 짧은 스터디였던것같다.
마지막 주차는 쿠버네티스 운영을하면서 조금씩 쌓아가던 보안에서 다루지 못했던부분들 그리고 어떤 발표에서 들었던 “인증, 인가의 중요성”을 다시끔 상기하면서 공부할수 있어서 좋았다
생각보다 흔하게 알고있던 지식을 다시끔 한번 밟으며 놓쳤던 부분들을 다시 배우며 많은 실력을 쌓을 수 있었고, 무엇보다 클라우드 기반으로 구현을 진행하며 배우는것들이 더욱 많았던것같다.
이번 스터디를 마무리하면서 그동안 알고있었던 지식을 정형적으로 정리를 해보는게 스스로에게 도움이 될거같다는 생각을 하게된다.
외쳐 갓시다!!!!