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.csv
kube-apiserver λ₯Ό μ€νν λ μ§μ ν΄μ£Όκ±°λ,
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
name: kube-apiserver
namespace: kube-system
spec:
containers:
- command:
- kube-apiserver
- ...
- --basic-auth-file=user-details.csv
kubeadm μ μ¬μ©νμ κ²½μ° 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,group1
Static 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 κ° νμνλ€.
CA
ca.crt
ca.key
Admin
admin.crt
+admin.key
kube-scheduler
scheduler.crt
+scheduler.key
kube-controller-manager
controller-manager.crt
+controller-manager.key
kube-proxy
kube-proxy.crt
+kube-proxy.key
kube-apiserver
apiserver.crt
+apiserver.key
apiserver-etcd-client.crt
+apiserver-etcd-client.key
apiserver-kubelet-client.crt
+apiserver-kubelet-client.key
etcd-server
etcdserver.crt
+etcdserver.key
kubelet-server
kubelet.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.key
kubectl 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.io
Role κ³Ό 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.io
Role μ 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-sa
v1.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: false
Pod μ 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: regcred
K8s μμ 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: 80
K8s Cluster λ κΈ°λ³Έμ μΌλ‘ μν λͺ¨λ Pod κ°μ ν΅μ μ νμ©νλ€. λ§μ½ Ingress μ Egress λ₯Ό μ ννκ³ μΆλ€λ©΄ NetworkPolicy λ₯Ό μμ±νμ.
Custom Resource Definition
apiVersion: flights.com/v1
kind: FlightTicket
metadata:
name: my-flight-ticket
spec:
from: Mumbai
to: London
number: 2
K8s μμ 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 μ λ°°ν¬ν μ μλλ‘ λμμ€λ€.