본 포스팅은 https://www.udemy.com/course/certified-kubernetes-administrator-with-practice-tests 강의와 https://kodekloud.com/의 내용을 공부하며 기록한 내용입니다.
- 141 Authentication
- 146 TLS in Kubernetes
- 147 TLS in Kubernetes - Certification Creation
- 152 Certificates API
- 155 KubeConfig
- 159 API Groups
- 160 Authorization
- 161 Role Based Access Controls
- 164 Cluster Roles and Roles Bindings
- 167 Service Account
- 170 Image CKA_7_Security
- 173 Security in docker
- 174 Security Contexts
- 177 Network Policy
- 178 Developing network policies
141 Authentication
Kubernetes에서는 User Account를 생성하는 기능은 없지만 serviceaccount 를 생성하는 기능은 있다.

User는 kubectl 이나 API로 kubenetes클러스터에 접근할 수 있다.

kube-apiserver를 시작할 떄 --basic-auth-file=<filename>을 지정하면 Password 인증 방식의 User를 등록가능하다. 하지만 보안이 좋지 않고, user가 변경되거나 할 때 api서버를 재시작해야하는 점 때문에 권장되는 인증 방식은 아니다. 이렇게 생성된 user/password는 curl command로 인증할 때 사용된다.

Password대시 Token으로 인증도 가능하다.

146 TLS in Kubernetes
Kubernetes 내부에서는 모든 인증이 TLS로 동작한다. 따라서 모든 컴포넌트들이 .crt로 끝나는 공개키와 .key 로끝나는 개인키가 존재한다.
아래는 kubernetes를 동작시키는 서버 컴포넌트이다.

kubernetes에 접근하는 Client도 마찬가지로 TLS 인증을 사용한다.

그리고 여기에 생성되는 모든 Certificate의 공개키가 진짜임을 인증해주는 CA(Certificate Authorityh)가 존재해야한다.
ETCD용 CA를 아래와 같이 분리하여 운영할 수 도 있지만 일반적으로 하나의 CA로 운영한다. 이 경우 전체 클러스터에 ca.crt, ca.key 한 쌍이 존재한다.

147 TLS in Kubernetes - Certification Creation
Certification 생성 도구로 유명한 것은 EASYRSA, CFSSL, OPENSSL 이 있는데 OPENSSL을 중심으로 알아본다.
CA(Certificate Authority)의 Key를 생성하려면 아래와 같은 명령어로 진행할 수 있다.
openssh genrsa -out ca.key 2048은 개인키를 만드는 것이고, openssl req -new -key ca.key -subj "/CN=KUBERNETES-CA" -out ca.csr은 sign을 한 공개키 만드는 과정이며 openssl x509 -req -in ca.csr -signkey ca.key -out ca.crt는 sign을 한 공개키를 개인키로 암호화하는 명령어다.

Admin User를 만들 때, ca.crt와 ca.key 를 이용해서 admin.csr를 암호화(CA로 인증서를 발급)한 한다.

이후에 생성해야 하는 대부분의 계정들도 비슷한 방식인데, .crt는 ID와 같고, .key 는 Password와 동일한 것이라고 생각하면 된다.
Certificate Signing Request에서 서명을 할 때 /CN=kube-admin/O=system:masters 를 추가하여 admin 계정과 일반 계정과 차이를 둔다. 이렇게 생성한 admin certificate은 curl 명령어를 실행할 때 사용하거나, kube-config.yaml에 등록하여 사용한다.

ETCD 클러스터는 High Availability로 구성되기 때문에, Peer certification이라는 추가적인 certification도 필요하다.

api server는 가장 많이 사용하기 때문에 이름이 많다. (kubernetes, kubernetes.default, kubernetes.default.svc, kubernetes.default.svc.cluster.local, IP address). 따라서 CA를 통해 sign할 때에도 alternative 이름을 전부 명시해주어야 한다.


Kubelet은 모든 Node가 각각의 certificate을 생성하고 가져야 한다.

위에서 언급했듯이 하나의 cluster에도 수많은 Certificate이 존재한다. 이 Certificate이 모두 정상적으로 설정되어있는지 체크하고 관리하는 것은 굉장히 어렵고 따분한 일이다. kubeadm 툴을 활용하면 이러한 일들을 쉽게할 수 있도록 도와준다.
openssl x509 -in <.crt file name> -text -noout 명령어를 통해 Certificate에 대한 정보를 확인할 수 있다.

kubectl logs etcd-master 또는 docker ps -a와 docker logs로 log를 분석해서 문제가 있는 것을 확인할 수 있다.
152 Certificates API
CA(Certificate Authority)는 ca의 공개키랑 ca의 비밀키 두 개라고 봐도 무방하다. 만약 이 key가 유출된다면, 이 키를 이용해서 많은 user를 만들고 kubernetes에 접근하는 hacking이 일어날 수 있다. 따라서 이 두가지의 key는 반드시 안전한 장소에 보관되어야 한다. 일반적으로 master node에서 만들고 관리 한다. kubeadm은 이 CA용 key 쌍을 보관하고 관리하는 역할까지 해준다. (Key의 유효기간 관리 포함).
Signing 과정도 간편하다. kube-apiserver에 아래와 같이 user를 생성하여 base64로 encoding하여 signing을 요청하면 된다.

base64로 encoding 할 때 -w 0 옵션을 주면 하나의 줄로 만들 수 있고 이를 request 부분에 붙여넣으면 된다.
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
name: akshay
spec:
signerName: youngju.dev.com/youngju
usages:
- digital signature
- key encipherment
- server auth
request: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQ1ZqQ0NBVDRDQVFBd0VURVBNQTBHQTFVRUF3d0dZV3R6YUdGNU1JSUJJakFOQmdrcWhraUc5dzBCQVFFRgpBQU9DQVE4QU1JSUJDZ0tDQVFFQXBkakxiamFRNWZMdklvNlBXTFpYaEwwa25wKzZlaUZHd0ZHUFErdmhrNWFBClpyOURYUVJINE9IZ1JESktvUGVxdTRGeDB6TkJUQndKa0ZpZEl4U01rUWNsc0lVT3VncSsyMUNaaHlkd0hFYzgKK1AzQzk2L0JVN3o1T25nWVBsa0ZyZmdBcHhacWo2WVVyTHJpS2V4Q0JHZ0cybzVBN205Q2xzRUhhalQwYlU2SwowWGxCQW9sc2cyM3NMUE1jR1RSdGZ0bGhDUDJoQkZPdHhyRW9SUHFsSUdFdWxEVytpSjZFeVY2SW9NcDhFVmxKCmJvb2MzaW93UE8zOGx4SmwrcWloVGd0OHMwNlYwTHhML0pVejdJQ3FkdFQzcGkvNHJ6VzBFeE16eVNLcllxZjYKZDVNK1RVZDdMMk1vQjF1aTgxQXFJcVpBQWQybExCR0M5dm9vbWpwZWd3SURBUUFCb0FBd0RRWUpLb1pJaHZjTgpBUUVMQlFBRGdnRUJBQXBpdUl4bU9mcEU2NEhTd0VCc1RsbnRScjVyZUNWMStlSzFKSVEyM0UyUXRiL2FrUGZuClNmRmpaRTB0ZmNaVUFHMzVoL214ZTVleEo3ZGNMN3dNT2wxYkVsTXNoTXlydlMyajVKckJhS3ZOaXFXVXo2VTYKbGRydWFlc0dUZDVoTncwVkNUaGpmaXNMcXBWZDI1TWlPcE11SFM0eXFVWUl4dGR1RGQzb01haFVTaXpyY1kzVwpXM0tycTBjSWxaU2p5L2twSzAwdmduclQ2ZlIzaTN4bE1jSkcrVTYzalBnYUhNUHM2MS9YZGhlL0s4dmRyeDllClc2MU1GalRGa0tpY25qYnBCNGZ1Uno3SmRRNW94TS90RFFveEtEYTNGTFNHcklvSGg1YkZlcDFESlZlaHkyeTcKekJFaXZiYWFjaEpGT2xvWHYrZnlVMVpUcS9JUTRLZEdjeU09Ci0tLS0tRU5EIENFUlRJRklDQVRFIFJFUVVFU1QtLS0tLQo=
$ kubectl create -f akshay-csr.yaml
certificatesigningrequest.certificates.k8s.io/akshay created
kubectl get csr 명령어로 csr의 signing 요청 목록을 확인할 수 있고, kubectl certificate approve <csr-name> 을 통해 approve 하면 된다. 이후 kubectl get csr jane -o yaml 명령어로 Certificate 정보를 확인가능하다.
$ kubectl get csr
NAME AGE SIGNERNAME REQUESTOR REQUESTEDDURATION CONDITION
akshay 79s youngju.dev.com/youngju kubernetes-admin <none> Pending
csr-xjv6n 36m kubernetes.io/kube-apiserver-client-kubelet system:node:controlplane <none> Approved,Issued
$ kubectl certificate approve akshay
certificatesigningrequest.certificates.k8s.io/akshay approved
$ kubectl certificate deny agent-smith
certificatesigningrequest.certificates.k8s.io/agent-smith denied
$ kubectl delete csr agent-smith
certificatesigningrequest.certificates.k8s.io "agent-smith" deleted

Certificated Related Operation은 Controller Manager에서 관리된다. CSR-Approving, CSR-Signing의 기능이다. 따라서 kube-controller-manager.yaml에 ca.key와 ca.crt를 등록해줘야한다.

155 KubeConfig
kubectl명령어를 입력하기 위해서 매번 certificate을 명시하는 것은 굉장히 귀찮다.

home directory 하위에 .kube/config 파일에 위의 정보를 등록해두면 kubectl 명령어를 입력할 여기에 입력한 정보가 들어가기 때문에 편리하다.
config 파일은 format에 맞게 작성해야하는데, Cluster명 Context, User 세가지를 정의한다.

여러개의 context중에 .kube/config 파일에 current-context 에 default context를 정의할 수 있다.

kubectl config view를 통해 config파일을 볼 수 있다. kubectl config user-context <context>를 통해 context를 변경한다.
각각의 Cluster에는 여러개의 namespace가 있다. 이는 context 필드 내부에서 정의할 수 있다.

159 API Groups
Kubernetes API는 여러개의 group으로 나뉘어져 있다. 이 중 api와 apis는 중요한 역할을 하는 API endpoint Group을 제공한다.


Kubectl proxy를 사용하면 권한을 가진 kube proxy 서버가 API 서버로의 요청을 중개해주기 때문에 certificate을 명시하지 않아도 된다.

160 Authorization
Authorization은 Authentication 이 된 후, 어디까지 접근할 것인가를 정하는 권한관리이다.

Node, AttributeBase, RoleBase, Webhook 4가지 방식으로 Authorization을 관리할 수 있다.
Node Authorization은 Kublet이 API서버에 접근할 때 사용하는 권한이다.

Attributes Base 권한관리는 user가 추가될 때마다 attribute를 바꿔줘야하고 API서버를 재시작 해야하기 떄문에 관리가 어렵다.
그에 반해 Role Base는 관리가 쉽다.

Authorization mode는 kubee api 서버를 시작할 때 설정가능하다. Node,RBAC,Webhook 같이 여러개 설정도 가능하다. 만약 Authorization mode로 여러개가 설정될 경우 지정한 순서대로 Authorization을 시도하게 된다.

161 Role Based Access Controls
authorization-mode 확인하는 법.
$ kubectl describe pod kube-apiserver-controlplane -n kube-system
-- Authorization-mode=Node,RBAC
현재 존재하는 Role 확인하는 법
$ kubectl get roles --all-namespaces
role의 내용 확인하는 법. configmapdp get하는 role이 존재
$ kubectl describe role kube-proxy -n kube-system
Name: kube-proxy
Labels: <none>
Annotations: <none>
PolicyRule:
Resources Non-Resource URLs Resource Names Verbs
--------- ----------------- -------------- -----
configmaps [] [kube-proxy] [get]
Role Binding 확인
$ kubectl describe rolebinding kube-proxy -n kube-system
Name: kube-proxy
Labels: <none>
Annotations: <none>
Role:
Kind: Role
Name: kube-proxy
Subjects:
Kind Name Namespace
---- ---- ---------
Group system:bootstrappers:kubeadm:default-node-token
Role Based를 사용하기위해 Role을 생성해야하는데, 아래와 같은 파일로 생성할 수 있다.


role binding으로 user와 role을 연결해준다.

can-i command로 권한 체크가 가능하다.

접근 가능한 Resource에 대해서 더 세세하게 권한 설정도 가능하다.
다른 user로 다른 작업을 해볼 때는 --as 를 사용한다.
$ kubectl get pods --as dev-user
Error from server (Forbidden): pods is forbidden: User "dev-user" cannot list resource "pods" in API group "" in the namespace "default"
164 Cluster Roles and Roles Bindings
API resource 중에는 namespace안에 있지만 cluster role은 그 외에 존재한다. 이것을 Cluster Scope이라고 부른다. 일종의 super 권한을 가진 role이기 때문에 사용에 주의해야한다.

이중 Cluster Admin이나 Storage Admin Role은 아래와 같은 권한을 가진다. 그리고 Role을 생성하는 것은 ClusterRole kind로 생성가능하다.

이렇게 생성된 Role과 User를 Binding 하려면 아래 같은 binding 필요하다.

$ kubectl get clusterroles
$ kubectl get clusterrolebindings
167 Service Account
Kubernetes에서 Account는 두 가지다. User 와 Service. 이름에서도 알 수 있듯이 User Account는 사람이 사용하는 계정이고 Service Account는 machine이나 program이 사용하는 계정이다.
kubectl create serviceaccount <account_name>
kubectl create token <account_name>
kubectl get serviceaccount
Serivce Account 를 생성하면 token이 함께 생성되는데, 이 토큰은 외부 어플리케이션이 API가 인증할 떄 사용하게 되고 이 정보는 Kubernetes secret으로 저장되어 관리된다.

우리가 어떤 Pod를 생성할 때 default Service account계정의 volume이 생성되는 것도 이 token을 저장하기 위함이다.
저장 장소는 /var/run/secret/kubernetes.io/serviceaccount 아래다. 만약 serviceAccount 저장을 하지 않기를 원한다면 pod에 automountServiceAccountToken을 false로 설정하면 된다.
pod에 저장된 이 토큰에 접근하려면 kubectl exec -it <pod_name> ls /var/run/secret/kubernetes.io/serviceaccount 를 통해 할 수 있다.

default service accout는 매우 제한된 권한을 가진다. 따라서 pod-definition.yml에 spec.serviceAccountName에 service account를 적는다.
Kubernetes 1.22 version 부터는 No expire token의 취약성 개선을 위해 service api token API를 도입했으며, 이 API를 사용하면 expire date가 있는 token을 발급받기 때문에 더 안전하다. TokenRequest를 사용할 수 없는 경우에만 service account token secret Object를 생성할 것을 권고하고 있다.

170 Image CKA_7_Security
image를 가져올 때 image 이름 앞에 User를 생략하면 'library' 라는 user가 Default로 붙는다.
그리고 그 앞에 registry를 설정할 수 있고 가장 유명한 public registry는 docker.io, gcr.io 가 있다.
public에 image를 올리면 안될 경우 private registry를 사용할 수도 있다.
docker login private-registry.io
docker run private-registry.io/apps/internal-app
private registry의 경우 username, password가 필요한데 이걸 그대로 pod definition에 hard coded하여 사용할 경우 보안상 좋지 않다. secret을 사용하는데 아래처럼 secret을 만들어 저장한 뒤, imagePullSecret section에 secret Name을 기입해주면 pod를 private registry에서 가져오는데 문제가 없다.

173 Security in docker
Docker는 container를 동작하는데 이 process를 isolate(분리)하는데 namespace라는 개념이 도입된다.
Host machine에서 확인한 container PID와 container내부의 PID가 서로 다르다. 또한 기본적으로 명시하지 않을경우 container내부에서 process를 구동하는 user는 root가 된다.

container내부의 user인 root 유저가 host의 root 유저랑 동일한 권한을 가지는가? 정답은 그렇지 않다 이다. 그렇다면 어디까지 host machine을 이용할 권한을 가지는지 궁금할 수 있다. 사실 docker는 container를 실행하는 user에게 제한적인 권한만 부여한다. 그리고 만약 더 많은 권하는 부여하고 싶을 때는 --cap-add <capability>를 run 명령어에 추가하면 된다. 반대로 privilege를 drop 하려면 --cap-drop을 사용하면 된다. 또는 모든 권한을 container user에게 부여하려면 --privileged를 붙이면 된다.

174 Security Contexts
docker에서 설정했던 capability를 cotainer Level에서 설정하려면 아래와 같이 할 수 있다. Pod level에서는 설정이 불가하다.

177 Network Policy
kubernetes예선, 클러스터 내부의 모든 traffic을 allow 하는 전략을 취한다. 하지만 어떤 이유로 특정 port 나 IP만 허용해야하는 상황이라면, Network Policy를 정할 수 있다.

만약 아래처럼 DB Pod에서 3306 port로 들어오는 Ingress만 허용하는 Network Policy를 설정하면 나머지 Port로 들어오는 traffic은 무시된다.

Network Policy는 Selector로 Label을 이용한다.

Network Policy를 지원하는 Addon에는 Kube-router, Calico, Romana, Weave-net이 있고, Flannel의 경우는 Network Policy를 지원하지 않으니 Addone을 선택할 때 참고해야한다.
178 Developing network policies

ingress 내부에 rule을 여러개로 나눌 경우, or 로 작동하여 하나라도 해당되는 것들이 있는경우 접근이 허용된다. 반대로 -를 없애서 하나의 block으로 만들경우 AND로 동작한다. ipBlock을 정의하여 특정 IP가 pod에 접근하는 것을 허용할 수도 있다.

egress용 network policy도 ingress와 비슷하게 정의할 수 있다.

Network Policy 생성 방법은 아래와 같다.
Policy Name: internal-policy Policy Type: Egress Egress Allow: payroll Payroll Port: 8080 Egress Allow: mysql MySQL Port: 3306
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: internal-policy
namespace: default
spec:
podSelector:
matchLabels:
name: internal
policyTypes:
- Egress
- Ingress
ingress:
- {}
egress:
- to:
- podSelector:
matchLabels:
name: mysql
ports:
- protocol: TCP
port: 3306
- to:
- podSelector:
matchLabels:
name: payroll
ports:
- protocol: TCP
port: 8080
- ports:
- port: 53
protocol: UDP
- port: 53
protocol: TCP
egress를 여러개 생성할 때는 아래와같이 하면 된다.
kind: NetworkPolicy
metadata:
name: internal-policy
namespace: default
spec:
podSelector:
matchLabels:
name: internal
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
name: mysql
ports:
- protocol: TCP
port: 3306
- to:
- podSelector:
matchLabels:
name: payroll
ports:
- protocol: TCP
port: 8080