본문 바로가기
Kubernetes (k8s)

[k8s] Security

by moveho 2023. 4. 10.

Authentication - Who can access

  • Files - Username and Passwords
  • Files - Username and Tokens
  • Certificates
  • External Authentication providers - LDAP
  • Service Accounts

Authorization - What can they do?

  • RBAC Authorization (Role Based Access Control)을 이용해 구현될 수 있음
  • ABAC Authorization (Attribute Based Access Control)
  • Node Authorization
  • Webhook Mode
  • 모든 component(ETCD, kube controller manager, api server, kubelet, ...) 사이의 통신 ⇒ TLS 암호화에 의해 보호
  • application 간의 통신 ⇒ Network Policy 에 의해 제한됨

Authentication

쿠버네티스는 기본적으로 사용자 계정을 관리하지 않으며, 사용자를 관리하기 위해 파일, 인증서, LDAP 같은 써드파티 서비스 등 외부 소스에 의존한다.

  • kubectl create serviceaccount sa1 → service account를 통해 관리 가능

Auth Mechanisms (권장x, 1.19버전에서 deprecated)

kube-apiserver가 요청이 들어왔을때, 어떻게 인증을 하는가

  • Static Password File
    • 사용자 id, 비밀번호가 담긴 csv 파일을 만들고, kube-apiserver.service 옵션에 넣어준다. (—basic-auth-file=user-details.csv)
    • (kube-apiserver.service 옵션 변경시 /etc/kubernetes/manifests/kube-apiserver.yaml 도 같이 변경해주어야함)
    • kube-apiserver에 요청시 사용자 id, 비밀번호와 함께 요청
  • Static Token File
    • 마찬가지로 토큰과 사용자 id가 포함된 csv 파일을 등록 (—token-auth-file=user-details.csv)

TLS Certificates (참고)

  1. 서버는 CSR(certificate signing request)를 CA에 보낸다.
  2. CA는 private key를 통해 CSR에 서명하고, 서명된 certificate는 다시 서버로 보내진다. 서버는 서명된 certificate를 통해 web application을 확인한다.
  3. 사용자가 web application에 접근하려할때, 서버는 첫번째로 public key로 암호화한 certificate를 보낸다.
  4. 사용자 또는 사용자의 브라우저가 certificate를 읽고, CA의 public key를 통해 유효성을 검사하고 서버의 public key를 찾는다.
  5. 그리고 앞으로 통신을 위해 사용할 symmetric key를 생성한다.
  6. symmetric key는 서버의 public key를 통해 암호화되어 서버로 보내진다.
  7. 서버는 자신의 private key로 이를 복호화하여 symmetric key를 찾는다.
  • Certificate(with public key)는 보통 crt 또는 pem 확장자로 된 파일
  • private key는 보통 .key 또는 -key.pem 확장자를 가짐

TLS in Kubernetes

참고:

https://kubernetes.io/ko/docs/setup/best-practices/certificates/

쿠버네티스 클러스터의 mater 노드와 worker 노드 간 통신, 또는 kube-api server와 admin(user)간의 통신은 모두 TLS 보안이 적용됨

  • 클러스터 내 서비스들은 server certificate 를 통해 통신을 보호하고, 클라이언트는 client certificate를 통해 자신의 신분을 증명해야함
  • 모든 certificate는 CA를 통해 서명이 되어야함
  • server 는 server certificate를 통해 통신을 보호
    • kube-api 서버 → apiserver.crt, apiserver.key
      • user가 kube-api 서버에 http요청을 통해 클러스터를 관리할 수 있으므로, 자격증명필요
      • kube-api 서버의 경우 etcd 서버와 통신할 때는 client에 해당→ 기존 key pair를 사용하거나, etcd 통신용 key pair를 만들 수도 있음
    • etcd server → etcdserver.crt, etcdserver.key
    • kubelet → kubelet.crt, kubelet.key
      • (kube-api 서버가 요청을 통해) 상태관리를 위한 api를 제공하므로 자격증명 필요
  • CA는 root certificate를 통해 cerificate를 확인
    • server certificate와 client certificate 서명 → ca.crt, ca.key
  • client는 client certificate를 이용해 서버가 자신을 증명하도록 함
    • kubectl를 사용한 client는 kube-api 서버와 통신하기 위해서 자격 증명 → admin.crt, admin.key
    • kube-scheduler도 pod 정보를 위해 kube-api 서버와 통신하므로 필요 → scheduler.crt, scheduler.key

CA 생성

Root Certificate

  1. private key 생성
    • openssl genrsa -out ca.key 2048
    • ca.key
  2. Certificate Signing Request
    • openssl req -new -key ca.key -subj "/CN=KUBERNETES-CA" -out ca.csr
    • ca.csr
    • 모든 detail이 있는 인증서지만, 서명이 되어있지 않은 상태
  3. Signing Certificate (인증서 생성)
    • 생성한 private key를 통해 CSR에 서명
    • openssl x509 -req -in ca.csr -signkey ca.key -out ca.crt
    • ca.crt
    • CA가 자신의 private key와 root certificate file을 가지게 됨

Client Certificate

admin user를 위한 certificate 생성

  1. private key 생성
    • openssl genrsa -out admin.key 2048
  2. Certificate Signing Request
    • openssl req -new -key admin.key -subj "/CN=kube-admin" -out admin.csr
    • 사용자 이름을 명시 (kube-admin)
  3. Singing Certificate
    • openssl x509 -req -in admin.csr -CA ca.crt -CAkey ca.key -out admin.crt
    • 앞서 만든 ca.crt 와 ca.key 를 통해 인증서를 서명
  • 위 과정은 새로운 user에 대해 새 계정을 만드는 것과 비슷 (user id는 certificate, key는 password와 같은 개념

)

다른 user와 admin user를 구분하는 방법?

  • certificate에 group detail을 추가할 수 있음
  • 쿠버네티스의 system masters 그룹이 관리자 권한을 가지고 있음
  • csr 생성시 다음과 같이 그룹 명시
    • openssl req -new -key admin.key -subj "/CN=kube-admin/O=system:masters" -out admin.csr

certificate 사용

Serverside Certificate

  • certificate 생성 과정은 이전과 동일함

ETCD 서버 certificate

  • ETCD 서버는 고가용성을 위해 클러스터 내 여러 서버에 배포될 수 있음
    • 각 멤버들 간 통신을 보호하기 위해, 추가적인 peer certificate를 생성해야함
    • certiciate가 한번 생성되면, ETCD 서버를 시작할때 이를 명시해주어야함
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    component: etcd
    tier: control-plane
  name: etcd
  namespace: kube-system
spec:
  containers:
  - command:
    - etcd
    - --advertise-client-urls=https://10.187.2.31:2379
    - --cert-file=/etc/kubernetes/pki/etcd/server.crt #
    - --client-cert-auth=true
    - --data-dir=/var/lib/etcd
    - --initial-advertise-peer-urls=https://10.187.2.31:2380
    - --initial-cluster=k8s-test001=https://10.187.2.31:2380
    - --key-file=/etc/kubernetes/pki/etcd/server.key #
    - --listen-client-urls=https://127.0.0.1:2379,<https://10.187.2.31:2379>
    - --listen-metrics-urls=http://127.0.0.1:2381
    - --listen-peer-urls=https://10.187.2.31:2380
    - --name=k8s-test001
    - --peer-cert-file=/etc/kubernetes/pki/etcd/peer.crt #
    - --peer-client-cert-auth=true #
    - --peer-key-file=/etc/kubernetes/pki/etcd/peer.key #
    - --peer-trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt #
    - --snapshot-count=10000
    - --trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt #

kube-api 서버 certificate

  • kube-api 서버는 많은 component의 요청이 들어오고, 많은 operation이 kube-api 서버를 통해 이루어지는 만큼, alias 가 많음
  • alias를 지정하는 방법
    • open ssl config 파일 → openssl.cnf 파일을 생성
      • alt_names 에 대체 DNS 또는 IP 지정
    • openssl req -new -key apiserver.key -subj "/CN=kube-apiserver" -out apiserver.csr -config openssl.cnf
      • cert signing request 시 해당 config 파일 지정
  • 그 후 인증서 사인
    • openssl x509 -req -in apiserver.csr -CA ca.crt -CAkey ca.key -out apiserver.crt

kubelet certificate

node 마다 있기 때문에, 각 node에 certificate 필요

→ 이름이 node 에 따라 정해진다.

certificate 를 생성하고, kubelet-config 파일에 해당 정보를 준다. (각 node에 수행)

kubelet 은 client로서 kube-api 서버와 통신하기 위해 client cert도 필요

  • api 서버가 어떤 노드인지 알수있도록 이름이 지정됨
  • 노드는 sytem component 이므로 system:node:node01 와 같이 지정
  • 그리고 api 서버가 올바른 permission 을 주기 위해, node들을 SYSTEM:NODES 라는 group에 추가

Certificate 상세 확인

kube-api서버 config 확인 → cert path 확인

  • cat /etc/kubernetes/manifests/kube-apiserver.yaml
    • certicate file ⇒ —tls-cert-file

특정 cert 정보 확인

  • openssl x509 -in /etc/kubernetes/pki/apiserver.crt -text -noout

Certificates API

새로운 cluster admin 을 추가하고자할 때

  1. 본인의 private key를 생성하고, certificate siging request를 요청 (기존 admin에게)
  2. 기존 admin은 request를 CA 서버를 통해 cert 생성 (CA서버의 private key와 root cert를 통해 서명)
  3. 새로운 관리자는 클러스터에 접근할 cert와 key를 가지게 됨

CA는 사실상 우리가 생성한 key, cert file pair 에 불과하다. 이 file을 보호하기 위해, 안전한 서버에 위치하도록하고, 이를 CA 서버라고 한다. 현재는 kubernetes 마스터 노드에 인증서가 위치하고, 따라서 master 노드 또한 CA 서버이다.

위 과정을 새로운 admin이 추가될 때마다 해야하고 cert가 만료되었을 때마다 cert를 교체해주어야하므로 더 나은 방법이 필요

→ kubernetes Certificates API 를 통해 가능

  1. CertificateSigningRequest 생성 (master node에 logging하고 직접 cert를 서명하지 않음)
  2. Review Requests
    • CertificateSigningRequest 가 생성되면, 클러스터의 admin들이 kubectl 을 통해서 확인 및 approve할 수 있음
  3. Approve Requests
  4. Share Certs to Users

참고: https://kubernetes.io/docs/reference/access-authn-authz/certificate-signing-requests/

ex.

  1. 새로운 사용자는 private key 생성
    • openssl genrsa -out jane.key 2048
    • openssl req -new -key jane.key -subj "/CN=jane" -out jane.csr
    • 위 결과와 함께 cert siging request를 보냄
  2. request를 받은 admin은 CertificateSigningRequest 생성
    1. apiVersion: certificates.k8s.io/v1beta1 kind: CertificateSigningRequest metadata: name: john spec: groups: - system:authenticated usages: - client auth request: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRV...
    2. 받은 csr 내용은 base64를 통해 encode 후 넣음
      • cat jane.csr | base64
  3. csr 확인
    • kubectl get csr
    • kubectl certificate approve jane

생성된 cert는 yaml 로 뽑은 후 status.certificate 값을 base64 로 디코딩하여 사용한다.

  • kubectl get csr jane -o yaml
  • echo "..." | base64 —decode

→ 쿠버네티스의 cert 관련 작업을 수행하는 component는 Controller Manager

새 user에 대해 인증하는 과정

https://kubernetes.io/docs/reference/access-authn-authz/certificate-signing-requests/#normal-user

KubeConfig

서버 및 cert 정보를 명시 → 일일이 해당 정보를 kubectl 사용시 반복할 필요 없음

  • ~/.kube/config (default)
  • clusters, contexts(어떤 cluster에 어떤 user?), users 로 구성

context 정의시 namespace를 지정할 수 있음

→ 해당 context 사용시 자동으로 default namespace를 해당 namespace로 사용

cluster의 ca는 path로 지정할 수도 있고 (certifiacte-authority: /etc/kubernetes/pki/ca.crt)

직접 encode된 data로 넣어줄 수도 있음 (certifiacte-authority-data: LS0t...)

config command

  • kubectl config view
  • kubectl config view --kubeconfig=my-custom-config
  • kubectl config use-config prod-user@production

API Groups

namespaces, pods, rc, nodes, services, .. 등 k8s 리소스 그룹

Authorization

access를 얻었다면, 무엇을 할 수 있는가

Node Authorizer

system:node (certificate)이름을 가진 user로부터의 요청은 node authorizer로 부터 인증되고 권한이 부여된다.

ABAC (Attribute Based Access Control)

참고: https://kubernetes.io/docs/reference/access-authn-authz/abac/

user 또는 user group을 권한 집합과 연결하는 역할

policy file을 생성해서 지정 가능

  • ex. Alice can do anything to all resources:
{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user": "alice", "namespace": "*", "resource": "*", "apiGroup": "*"}}

RBAC (Role Based access control)

user 또는 user group을 권한 집합과 연결하며 ABAC 보다 훨씬 쉽게 제어할 수 있음

  • 특정 권한을 생성하고, user를 연결하는 방식

Webhook

open policy agent와 같이 admission control과 authorization를 돕는 써드파티 사용

  • 쿠버네티스가 open policy agent에 권한 체크 요청을 하도록 할 수 있음

AlwaysAllow, AlwaysDeny

권한 체크 없이 모든 요청 수용, 거절

→ 이런 Auth mode 옵션은 kube-api 서버 옵션으로 지정됨

(—authorization-mode=AlwaysAllow)

  • kubectl describe pod kube-apiserver-controlplane -n kube-system

RBAC

참고: https://kubernetes.io/docs/reference/access-authn-authz/rbac/

Role 생성

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: pod-reader
rules:
- apiGroups: [""] # "" indicates the core API group
  resources: ["pods"]
  verbs: ["get", "watch", "list"]

RoleBinding

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods
  namespace: default
subjects:
- kind: User
  name: dev-user
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role#this must be Role or ClusterRolename: pod-reader# this must match the name of the Role or ClusterRole you wish to bind toapiGroup: rbac.authorization.k8s.io

kubectl get roles

kubectl get rolebindings

Check Access

kubectl auth can-i create deployments

kubectl auth can-i create pods --as dev-user

kubectl auth can-i create pods --as dev-user --namespace test → namespace test에 pod을 생성할 권한이 있는가

특정 pod에 대한 권한

rules:
- apiGroups: [""]# "" indicates the core API group
  resources: ["pods"]
  verbs: ["get", "watch", "list"]
  resourceNames: ["blue", "orange"]# 해당 이름의 pod에 대한 권한

Cluster Roles and Role Binding

참고: https://kubernetes.io/docs/reference/access-authn-authz/rbac/#role-and-clusterrole

resource는 namespace 또는 cluster 범위로 categorize 된다.

  • node는 cluster 범위의 resource (특정 namespace와 연관되지 않음)

cluster 범위의 resources에 대한 role ⇒ Cluster Role

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: secret-reader
rules:
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get", "watch", "list"]

Cluster RoleBinding

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: read-secrets-global
subjects:
- kind: Group
  name: manager
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io

namespace 에 해당하는 resource도 cluster role을 사용할 수 있음

  • 대신 모든 namespace에 대한 권한이 허용됨

Service Accounts

참고: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/

application 이 쿠버네티스 클러스터와 상호작용하기 위해 사용되는 계정

  • service account를 통해 요청이 인증됨

ex. prometheus 같은 모니터링 app이 metrics를 가져오기위해 쿠버네티스 api에 요청할때 사용

kubectl create serviceaccount dashboard-sa

  • serviceaccount가 생성되면, token이 자동으로 생성됨
    • → 이 token은 외부 application이 쿠버네티스 api에 인증하는 데 사용됨 → secret object로 생성됨

각 namespace는 default serviceaccount를 가지게 된다.

pod이 생성되면, default serviceaccount와 그의 token이 자동으로 pod에 volume mount로 mount된다.

  • kubectl exec -it my-kubernetes-dashboard cat /var/run/secrets/kubernetes.io/serviceaccount/token
  • → pod내에 mount된걸 확인할 수 있음

pod 생성시 다른 serviceaccount를 사용하려면, spec.serviceAccountName 에 지정하면 됨

(존재하는 pod에 변경불가, 변경시 pod 삭제후 재생성 / deployment는 edit 가능, rollout됨)

spec.automountServiceAccountToken을 false로 지정해 자동 mount 방지 가능

Images Security

참고: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#create-a-secret-by-providing-credentials-on-the-command-line (검색: pull an image)

쿠버네티스로 image를 받아오기 위한 credential 을 어떻게 넘기는가?

  1. credential을 가진 docker-registry 타입의 secret 생성
    • kubectl create secret docker-registry regcred --docker-server=<your-registry-server> --docker-username=<your-name> --docker-password=<your-pword> --docker-email=<your-email>
  2. pod 생성시 imagePullSecrets 지정 → 생성한 secret 지정
apiVersion: v1
kind: Pod
metadata:
  name: private-reg
spec:
  containers:
  - name: private-reg-container
    image: <your-private-image>
  imagePullSecrets:
  - name: regcred

Security Contexts

참고: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod

apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo
spec:
  securityContext:
    runAsUser: 1000 # all processes run with user id 1000
containers:
- name: ubuntu
  image: ubuntu
  command: []
  #securityContext:# 여기에 정의해서 container level로 지정 가능
  • pod 레벨 / container 레벨 지정 가능 (pod 레벨 지정시 모든 container에 적용)
  • pod 레벨/ container 레벨 모두 지정시 container 레벨의 설정이 적용됨

container 실행 user 확인

  • kubectl exec ubuntu-sleeper -- whoami

리눅스 기능 추가 및 제거

참고: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-capabilities-for-a-container

apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo-4
spec:
  containers:
  - name: sec-ctx-4
    image: gcr.io/google-samples/node-hello:1.0
    securityContext:
      capabilities:
        add: ["NET_ADMIN", "SYS_TIME"]

Network Policy

참고: https://kubernetes.io/docs/concepts/services-networking/network-policies/

기본적으로 쿠버네티스 클러스터 내 모든 pod들은 서로 통신이 가능

→ network policy를 통해 특정 통신만 허용할 수 있음

→ 즉, pod 내부로 들어오는 ingress 트래픽과 외부로 나가는 egress 트래픽을 제한할 수 있음

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: db-policy
  namespace: default
spec:
  podSelector:
    matchLabels:
      role: db
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          name: api-pod
    ports:
    - protocol: TCP
      port: 3306

  1. db pod에 network policy 를 적용하고자 함 → podSelector를 통해 어떤 pod에 적용할지 정의
  2. api pod으로부터의 ingress 통신만 허용하고 싶음 → policyTypes을 ingress 로 지정하고, ingress 필드 정의
  3. traffic을 허용할 port 정의 → ports

network policy는 쿠버네티스 클러스터에서 사용하는 네트워크 솔루션을 통해 동작

  • Network Policy 적용 : Kube-router, Calico, Romana, Weave-net
  • 적용 x : FlannelKubernetes Security PrimitivesAuthentication - Who can access

댓글