Authentication
개발자나 관리자가 K8s Cluster 와 소통하기 위해선 항상 kube-apiserver 를 거치게된다. 때문에 kube-apiserver 에서 모든 Authentication 이 이루어진다. kube-apiserver 는 요청을 Authenticate 하기 위해 Static Password File, Static Token File, Certificates, Identity Service 등을 활용한다.
# user-details.csv
password123,user1,u0001,group1
password123,user2,u0002,group1
password123,user3,u0003,group1위처럼 CSV 형식의 Static Password File 을 생성하고,
ExecStart=/usr/local/bin/kube-apiserver \
...
--basic-auth-file=user-details.csvkube-apiserver 를 실행할 때 지정해주거나,
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
name: kube-apiserver
namespace: kube-system
spec:
containers:
- command:
- kube-apiserver
- ...
- --basic-auth-file=user-details.csvkubeadm 을 사용했을 경우 yaml 파일에 옵션을 추가해주면된다.
curl -v -k https://master-node-ip:6443/api/v1/pods -u "user1:password123"
이후 요청을 보낼 때 아이디와 비밀번호를 위와 같은 방식으로 전달해주면된다.
# user-token-details.csv
KpjCq12wfdfqgwdfw,user4,u0004,group1
rJjncqwe1dgi2dow0,user5,u0005,group1
mjpOFiEq1dngi3dkf,user6,u0006,group1Static Token File 을 사용할 경우엔
--token-auth-file=user-token-details.csv
옵션을 대신 사용하면 된다.
curl -v -k https://master-node-ip:6443/api/v1/pods --header "Authorization: Bearer KpjCq12wfdfqgwdfw"
이후 요청 시 토큰을 헤더에 포함하여 전달해줄 수 있다.
위 방법은 아주 기초적인 방법으로 v1.19 에 Deprecated 됐다.
TLS Certificates for Cluster Components
K8s 내부의 암호화된 통신을 위해 각 컴포넌트 마다 TLS Certificate 과 Private Key 가 필요하다.
CAca.crtca.key
Adminadmin.crt+admin.key
kube-schedulerscheduler.crt+scheduler.key
kube-controller-managercontroller-manager.crt+controller-manager.key
kube-proxykube-proxy.crt+kube-proxy.key
kube-apiserverapiserver.crt+apiserver.keyapiserver-etcd-client.crt+apiserver-etcd-client.keyapiserver-kubelet-client.crt+apiserver-kubelet-client.key
etcd-serveretcdserver.crt+etcdserver.key
kubelet-serverkubelet.crt+kubelet.key
# Key 생성
openssl genrsa -out ca.key 2048
# Certificate Signing Request 생성
openssl req -new -key ca.key -subj "/CN=KUBERNETES-CA" -out ca.csr
# Sign Certificate
openssl x509 -req -in ca.csr -signkey ca.key -out ca.crt
먼저 CA Certificate 을 발급받은 후,
# Key 생성
openssl genrsa -out admin.key 2048
# Certificate Signing Request 생성
openssl req -new -key admin.key -subj "/CN=kube-admin" -out admin.csr
# Admin Privilage 를 포함한 CSR 생성
openssl req -new -key admin.key -subj "/CN=kube-admin/O=system:masters" -out admin.csr
# Sign Certificate
openssl x509 -req -in admin.csr -CA ca.crt -CAkey ca.key -out admin.crt
Admin 을 위한 Certificate 을 발급받는다. 이후 모든 컴포넌트에 동일한 작업을 수행해주면된다.
openssl x509 -in /etc/kubernetes/pki/apiserver.crt -text -noout
Certificate 을 확인하기 위해서 위 명령어를 실행한 뒤 Issuer, Subject 등의 내용을 확인할 수 있다.
cat /etc/kubernetes/manifests/kube-apiserver.yaml
kube-apiserver 에 어떤 Certificate 이 사용됐는지 확인하려면 위 yaml 파일에서 --tls-cert-file 옵션을 확인해보자.
cat /etc/kubernetes/manifests/etcd.yaml
etcd 에 어떤 Certificate 이 사용됐는지 확인하려면 위 yaml 파일을 찾아보자.
Certificates API
openssl genrsa -out meatsby.key 2048
openssl req -new -key meatsby.key -subj "/CN=meatsby" -out meatsby.csr
cat meatsby.csr | base64 -w 0
apiVersion: certificates.k8s.io/v1beta1
kind: CertificateSigningRequest
metadata:
name: meatsby
spec:
groups:
- system:authenticated
usages:
- digital signature
- key encipherment
- server auth
request:
<certificate-goes-here>kubectl get csr
kubectl certificate approve meatsby
kubectl get csr meatsby -o yaml
echo "<certificate>" | base64 --decode
K8s 는 Admin 의 Certificate 작업을 대신해주기 위한 API 를 제공한다. Controller Manager 가 Certificate 관련 작업을 모두 처리한다.
KubeConfig
curl https://my-kube-playground:6443/api/v1/pods \
--key admin.key
--cert admin.crt
--cacert ca.crt
kubectl get pods
--server my-kube-playground:6443
--client-key admin.key
--client-certificate admin.crt
--certificate-authority ca.crt
kubectl get pods --kubeconfig config
curl 또는 kubectl 로 kube-apiserver 와 통신할 때 사실 위와 같이 인증 관련 옵션들을 지정해줘야 하는데 이를 간편하게 KubeConfig 로 설정해줄 수 있다. $HOME/.kube/config 경로를 기본값으로 사용하기 때문에 여태껏 제외하고 사용 가능했던 것이다.
apiVersion: v1
kind: Config
current-context: my-kube-admin@my-kube-playground
clusters:
- name: my-kube-playground
cluster:
certificate-authority: ca.crt
server: https:///my-kube-playground:6443
contexts:
- name: my-kube-admin@my-kube-playground
context:
cluster: my-kube-playground
user: my-kube-admin
namespace: finance
users:
- name: my-kube-admin
user:
client-certificate: admin.crt
client-key: admin.keykubectl config view --kubeconfig=my-custom-config
kubectl config use-context prod-user@production
Authorization
K8s 에 대한 작업을 위해 curl 이나 kubectl 로 요청을 보내면 kube-apiserver 에서 제공하는 API 에서 요청을 처리하게 된다. 이 때 K8s Object 에 대한 작업들을 API Group 으로 나뉘게 되는데, /apis 엔드포인트 기준으로 아래와 같은 API Group 이 존재한다.
/apps/v1/deployments/replicasets/statefulsets
/extensions/networking.k8s.io/storage.k8s.io/authentication.k8s.io/certificates.k8s.io
K8s 는 위와 같은 다양한 Resource 에 대한 작업의 권한을 위해 아래와 같은 방법을 사용한다.
- Node Authorization
kubelet의 경우 Certificate 에 명시된system:node:node01이라는 이름을 통해 Node Authorization 방식으로kube-apiserver와 통신할 수 있다.
- Attribute-Based Access Controls (ABAC)
- 각 유저마다 Policy 를 적용해서 허용 가능한 요청들을 지정할 수 있다.
- Role-Based Access Controls (RBAC)
- Policy 를 Role 로 생성해 유저들이 Role 을 기반으로 요청할 수 있도록 한다.
- Webhook
ExecStart=/usr/local/bin/kube-apiserver \
...
--authorization-mode=Node,RBAC,Webhook
kube-apiserver 를 실행할 때 --authorization-mode 옵션을 통해 적용할 Authorization Mechanism 을 지정해줄 수 있다. 위와 같이 설정해 줄 경우, Node Authorization 을 시도하고 실패하면 RBAC, 그리고 Webhook 순으로 권한을 확인한다.
Role-Based Access Controls
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: developer
rules:
- apiGroups: [""] # "" indicates the core API group
resources: ["pods"]
verbs: ["get", "update", "create"]
resourceNames: ["blue", "orange"]
- apiGroups: [""]
resources: ["ConfigMap"]
verbs: ["create"]apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: devuser-developer-binding
subjects:
- kind: User
name: dev-user # "name" is case sensitive
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: developer
apiGroup: rbac.authorization.k8s.ioRole 과 RoleBinding 을 통해 RBAC 을 적용할 수 있다.
kubectl get roles
kubectl get rolebindings
kubectl describe role developer
kubectl describe rolebinding devuser-developer-binding
Role 과 RoleBinding 모두 K8s Object 이기 때문에 위 명령어들로 조회가 가능하다.
kubectl auth can-i create deployments
kubectl auth can-i delete nodes
kubectl auth can-i create deployments --as dev-user
kubectl auth can-i create pods --as dev-user
kubectl auth can-i create pods --as dev-user --namespace test
Role 이 어떤 작업을 수행할 수 있는지 확인해야할 경우 위 명령어들을 사용할 수 있다.
Cluster Roles and Role Bindings
# namespace 에 포함된 리소스 확인
kubectl api-resources --namespaced=true
# namespace 에 포함되지 않은 리소스 확인
kubectl api-resources --namespaced=false
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cluster-administrator
rules:
- apiGroups: [""] # "" indicates the core API group
resources: ["nodes"]
verbs: ["get", "list", "delete", "create"]apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: cluster-admin-role-binding
subjects:
- kind: User
name: cluster-admin
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: cluster-administrator
apiGroup: rbac.authorization.k8s.ioRole 은 Namespace 를 지정해주지 않을 경우 default namespace 로 설정된다. pods, replicasets, deployment 등 namespace 에 포함되는 K8s Object 들과 다르게 nodes, PV, namespaces 와 같은 Cluster Scoped Object 들은 Cluster Role 을 통해 접근을 제한할 수 있다.
Service Accounts
K8s Account 는 User Account 와 Service Account 로 나뉜다. User Account 는 Admin, Developer 등이 사용하고 Service Account 는 Prometheus, Jenkins 등의 Application 이 사용한다. Application 이 kube-apiserver 와 통신하기 위해 Authentication 을 거쳐야 하는데 이를 Service Account 를 통해 이뤄낼 수 있다.
kubectl create serviceaccount dashboard-sa
kubectl get serviceaccount
kubectl describe serviceaccount dashboard-sa
kubectl describe secret dashboard-sa-token-kbbdm
Service Account 를 생성하면 인증을 위해 사용되는 Token 역시 생성된다. Token 의 경우 Secret Object 를 통해 관리된다. 해당 Token 을 활용해 Application 을 인증 받을 수 있다.
만약 Application 을 Pod 의 형태로 K8s Cluster 내에서 호스팅한다면 Token 을 수동으로 제공하는 대신 볼륨을 마운팅하여 간단하게 제공할 수 있다. K8s 는 기본적으로 각 namespace 마다 Service Account 를 자동으로 생성한다. 해당 namespace 에 Pod 가 생성될 때 마다 생성된 Service Account 와 Token 이 자동으로 볼륨에 마운팅된다.
kubectl create serviceaccount dashboard-sa
kubectl create token dashbaord-sa
apiVersion: v1
kind: Secret
type: kubernetes.io/service-account-token
metadata:
name: mysecretname
annotations:
kubernetes.io/service-account.name: dashboard-sav1.22 부터 위 내용이 변경되었는데, Service Account 가 TokenRequestAPI 로 부터 Token 을 받아오는 형식으로 작동한다. v1.24 엔 Service Account 를 생성하면 Token 을 직접 생성해서 설정해주어야한다.
kubectl describe pod my-kubernetes-dashboard
kubectl exec -it my-kubernetes-dashboard -- ls /var/run/secrets/kubernetes.io/serviceaccount
kubectl exec -it my-kubernetes-dashboard cat /var/run/secrets/kubernetes.io/serviceaccount/token
Pod 를 생성한 뒤 describe 를 통해 확인해보면 volumes.default-token-xxxxx 이 생성되어 있는 것을 확인할 수 있다.
apiVersion: v1
kind: Pod
metadata:
name: my-kubernetes-dashboard
spec:
containers:
- name: my-kubernetes-dashboard
image: my-kubernetes-dashboard
serviceAccountName: dashboard-sa
automountServiceAccountToken: falsePod 에 Service Account 를 지정해주기 위해 spec.serviceAccountName 을 사용할 수 있고 자동으로 Service Account 가 배정되는 것을 막기 위해 spec.automountServiceAccountToken 을 사용할 수 있다.
Image Security
docker login private-registry.io
docker run private-registry.io/apps/internal-app
Pod 에서 실행될 Container 의 Image 는 기본적으로 Docker Hub 에서 가져와 사용하게 된다. 만약 Private Registry 에서 Image 를 가져온다면 위와 같이 Docker 로 로그인해서 가져오게 된다.
kubectl create secret docker-registry regcred \
--docker-server=private-registry.io \
--docker-username=registry-user \
--docker-password=registry-password \
--docker-email=registry-user@org.com
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
spec:
containers:
- name: nginx
image: private-registry.io/apps/internal-app
imagePullSecrets:
- name: regcredK8s 에서 Pod Definition File 로 Private Registry 를 이용하려면 Image 의 전체경로와 Private Registry 에 로그인하기 위한 Secret 를 연동해줘야 한다.
Security Contexts
Docker Security
Docker Container 는 기본적으로 호스트의 Process 로써 실행되고 Linux namespace 를 통해 격리된다.
docker run --user=1000 ubuntu sleep 3600
Docker Container 는 기본적으로 root user 로 실행되기 때문에 Container 를 실행할 때 --user 옵션을 통해 root user 권한을 제한할 수 있다.
docker run ubuntu
Docker Container 는 mac_admin, broadcast, net_admin, sys_admin 등 호스트에 영향을 줄 수 있는 Capability 들이 제한된다. 모든 Capability 는 /usr/include/linux/capability.h 에서 확인할 수 있다.
docker run --cap-add MAC_ADMIN ubuntu
docker run --cap-drop KILL ubuntu
docker run --privillege ubuntu
위 옵션들을 통해 Container 의 권한을 제어할 수 있다.
Kubernetes Security
apiVersion: v1
kind: Pod
metadata:
name: web-pod
spec:
securityContext:
runAsUser: 1000
capabilities:
add: ["MAC_ADMIN"]
containers:
- name: ubuntu
image: ubuntu
command: ["sleep", "3600"]
securityContext:
runAsUser: 1001
capabilities:
add: ["MAC_ADMIN"]K8s Pod 에서도 마찬가지로 user 의 권한을 securityContext 를 통해 제어할 수 있다. Container 레벨에 선언된 securityContext 는 해당 Container 의 권한을 제어하고, Pod 레벨에 선언된 securityContext 는 Pod 내 모든 Container 의 권한을 제어한다.
kubectl exec ubuntu-sleeper -- whoami
위 명령어를 통해 Container User 를 확인할 수 있다.
Network Policy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: db-policy
spec:
podSelector:
matchLabels:
role: db
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
role: api-pod
- namespaceSelector:
matchLabels:
name: prod
- ipBlock:
cidr: 192.168.5.10/32
ports:
- protocol: TCP
port: 3306
egress:
- to:
- ipBlock:
cidr: 192.168.5.10/32
ports:
- protocol: TCP
port: 80K8s Cluster 는 기본적으로 속한 모든 Pod 간의 통신을 허용한다. 만약 Ingress 와 Egress 를 제한하고 싶다면 NetworkPolicy 를 생성하자. 위 yaml 예시는 role=db 레이블을 가진 Pod 는 같은 네임스페이스의 role=api-pod 레이블을 가진 Pod 또는 prod 네임스페이스의 모든 Pod 또는 IP 192.168.5.10 로부터 TCP 3306 포트로만 접근을 허용하는 Ingress Rule 을 가지고 있고, role=db 레이블을 가진 Pod 는 192.168.5.10:80 (TCP) 로만의 outbound 통신을 허용하는 Egress Rule 을 가지고 있다.
Custom Resource Definition
apiVersion: flights.com/v1
kind: FlightTicket
metadata:
name: my-flight-ticket
spec:
from: Mumbai
to: London
number: 2K8s 에선 ReplicaSet, Deployment 등 기본적으로 제공되는 Resource 외에 사용자가 커스텀으로 Resource 를 만들 수 있다. 위와 같은 Resource 를 만들고 싶을 때 Custom Resource Definition 을 생성해 Object 를 생성하고 관리할 수 있다.
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: flighttickets.flights.com
spec:
scope: Namespaced
group: flights.com
names:
kind: FlightTicket
singular: flightticket
plural: flighttickets
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
from:
type: string
to:
type: string
number:
type: integer
minimum: 1
maximum: 10위와 같이 Custom Resource Definition 을 생성하면 정의된 Custom Resource 를 생성하고 관리할 수 있다. 다만 ETCD 에 저장만 될 뿐 어떤 기능이 있는 것은 아니다. 기능을 추가하기 위해 Custom Controller 를 작성할 수 있다.
Custom Controllers
Custom Resource 를 제어하려면 Custom Controller 를 작성해야 하는데, 파이썬 등 다양한 언어로 작성할 수 있지만 API 콜을 기반으로 통신하기 때문에 자체적으로 큐 및 캐싱 시스템을 포함해야 하는 수고가 있을 수 있다. 때문에 K8s Go 클라이언트로 Custom Controller 를 작성하면 좀 더 쉽게 Controller 를 구축할 수 있다.
Operator Framework
Operator Framework 는 CustomResourceDefinition 과 CustomController 를 패키지화하여 K8s 에 배포할 수 있도록 도와준다.