TLS(Transport Layer Security)

TLS는 인터넷에서의 정보를 암호화 해서 송수신하는 프로토콜로서 SSL에 기반한 기술이며 국제 인터넷 표준화 기구에서 표준으로 인정받은 프로토콜이다. TLS 위에 HTTP 프로토콜을 얹어 보안된 HTTP 통신을 하는 것을 HTTPS라고 하며, 443 포트를 사용한다.

 

Symmetric / Asymmetric encryption

클라이언트와 서버사이에 같은 symmetric key를 가지고 통신할 때 해커가 키를 갈취할 수 있는 방법이 있어 asymmetric encryption에 대한 필요성이 제기된다.  Asymmetric encryption은 public key로 암호화하고, private key로 복호화하며 통신한다. 어떤 사람도 public key를 소유할 수 있지만 private key는 그렇지 않다.

 

TLS는 symmetric과 asymmetric encrption을 모두 사용한다. Asymmetric encryption은 클라이언트와 서버사이의 세션을 안전하게 유지하기 위해 사용되고, Symmetric encryption은 이 세션을 통해 데이터를 교환할 때 사용된다.

 

PKI(Public Key Infrastructure)

디지털 증명서의 생성, 관리, 배포, 사용, 저장 등 공개키 암호화의 관리에 필요한 역할, 정책 등 일련의 절차들을 포괄하는 개념이다.

 

- Public Key : *crt, *pem 등으로 발급된다.

- Private Key : *.key, *-key.pem 등으로 발급된다.

openssl genrsa -out my-bank.key 1024

--> my-bank.key

openssl rasa -in my-bank.key -pubout > mybank.pem

--> my-bank.key, mybank.pem

 

PKI 동작 방식을 순서대로 살펴보자.

 

public key를 보내주는 서버에 대해서 특정 에이전시가 사인해준다면(Certificate), 위험 사이트 인지 아닌지에 대해서 클라이언트가 알수 있게된다. 또한, 브라우저는 이 사인의 주체가 공인된 CA(Certificate Authority) 이라면 합법적이라고 판단하고, 그렇지 않다면 클라이언트에게 경고 메세지를 보낸다.

 

1. 서버는 CSR(certificate signing request)를 CA에게 보낸다.

openssl req -new -key my-bank.key -out my-bank.scr -subj "/C=US/ST=CA/O=MyOrg, Inc./CN=mydomain.com"

--> my-bank.key, my-bank.csr(Certificate Siging Request)

2. CA는 CA private key로 해당 요청을 사인한다.

3. CA 사인된 증명서를 서버에게 보낸다.(해커가 요청했을 경우 CA는 증명서를 발급하지 않는다)

------> 로컬 PC에는 CA의 Public Key를 가지고 있는 상태가 된다.

4. 최초 사이트에 접속을 하게 되면 유저에게 사인된 증명서 public key를 함께 보낸다.

5. 유저는 보유한 CA public key로 증명서를 검증하고, the public key를 얻게된다. 브라우저는 그 후 sysmetric key를 생성한다.

6. 사용자의 브라우저는 the symmetric key the public key로 암호화(encrypt)하여 서버에 보낸다.

7. 서버는 private key를 가지고 the symmetric key를 복호화(decrypt) 한다.(해커는 private key를 가지고있지 않기 때문에 복호화 불가!)

8. 사용자와 서버는 서로 the symmetric key를 갖게되고, 서로 데이터를 암호화하고 복호화할 수 있는 상태가된다.

(user/password 정보를 안심하고 입력해서 서버에 전달해도 되는 상태)

 

결론적으로  Asymmetric encryption은 sysmetric key 를 안전하게 암호화하는데 사용되고, Symmetric encryption은 sysmetric key 를 이용하여 안전하게 데이터를 교환할 때 사용된다고 할 수 있다.

 

서버가 클라이언트가 해커가 아니라는 것을 어떻게 판단할까?

 

1. 서버가 클라이언트에게 증명서를 요청한다.

2. 클라이언트는 public key와 private key를 생성한다 for CSR(certificate signing request).

3. 클라이언트는 CSR(certificate signing request)를 CA에 보내고, 증명서를 발급받는다.

4. 클라이언트는 CA로 부터 받은 증명서를 서버에 보낸다.

 

위의 과정은 브라우저와 서버사이에 내부적으로 일어나는 프로세스라서 유저가 관여하지 않는다. 이 과정까지 포함하여 PKI라고 부른다.

 

참고

* CA는 private key와 public key를 가진다. CA는 private CA(회사 내부), public CA로 나눌 수 있다.

- private key는 증명서를 서명하는데 사용하고,

- public key는 브라우저에 내장되어 있고, 증명서를 검사할 때 사용한다.

 

*The symmetric key : 랜덤 대칭 암호화키(Random symmetric encryption key) by 브라우저(동일한 키로 암호화/복호화)


이제 TLS와 PKI에 대해서 살펴보았으니 Kubernetes에서 어떻게 적용되는지 살펴보자.

TLS in Kubernetes

Certificate for Certificate Authority

- private key through openssl

openssl genrsa -out ca.key 2048
-> ca.key

- certificate signing request

openssl req -new -key ca.key -subj "/CN=KUBERNETES-CA" -out ca.scr
-> ca.csr

- sign certificates

oepnssl x509 -req -in ca.csr -signkey ca.key -out ca.crt
-> ca.crt

 

결과적으로 CA는 privat key와 root certificate file을 갖게된다.

 

Client Certificates

Admin : admin.crt, admin.key

- private key

openssl genrsa -out admin.key 2048
-> admin.key

- certificate signing request

openssl req -new -key admin.key -subj "/CN=kube-admin/O=system:masters" -out admin.csr
-> admin.csr

O=system:masters -> 그룹정보(admin user with admin privileges)

 

- signed certificate

openssl x509 -req -in admin.csr -CA ca.crt -CAkey ca.key -out admin.crt
-> admin.crt

 

결과적으로 client(admin)는 privat key와 certificate file을 갖게된다.

 

client private key와 certificate file은 2가지 형태로 사용될 수 있다.

 

첫째, command line으로 kube api-server로 리소스를 요청.

curl https://kube-apiserver:643/api/v1/pods \
--key admin.key --cert admin.crt
--cacert ca.crt

{
	"kind": :PodList",
    "apiVersion": "v1",
    "metadata": {
    	"selfLink": "/api/v1/pods",
    },
    "items": []
}

둘째, kube config 설정

apiVersion: v1
clusters:
- cluster:
    certificate-authority: ca.crt
    server: https://kube-apiserver:6443
  name: kubernetes
kind: Config
users:
- name: kubernetes-admin
  user:
    client-certificate: admin.crt
    client-key: admin.key

 

그 밖의 Client Certificates ...

Client Certificates
kube-scheduler scheduler.crt, scheduler.key
kube-controller-manager controller-manager.crt, controller-manager.key
kube-proxy kube-proxy.crt, kube-proxy.key
kube-api-server apiserver-kubelet-client.crt, apiserver-kubelet-client.key
apiserver-etcd-client.crt, apiserver-etcd-client.key
kubelet server kubelet-client.crt, kubelet-client.key

 

Server Certificates

Etcd-server & etcd-peer

- etcd
- --advertise-client-urls=https://127.0.0.1:2379
# ETCD server
- --key-file=/path-to-certs/etcdserver.key
- --cert-file=/path-to-certs/etcdserver.crt

- --client-cert-auth=true
- --data-dir=/var/lib/etcd

- --initial-advertise-peer-urls=https://127.0.0.1:2380
- --initial-cluster=master=https://127.0.0.1:2380
- --listen-client-urls=https://127.0.0.1:2379
- --listen-peer-urls=https://127.0.0.1:2380
- --name=master

# ETCD peer
- --peer-key-file=/etc/kubernetes/pki/etcd/peer.key
- --peer-cert-file=/path-to-certs/etcdpeer1.crt
- --peer-trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt

- --peer-client-cert-auth=true
- --snapshot-count=10000

# ROOT CA
- --trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt

 

Kube-api-server : apisever.crt, apiserver.key

- private key

openssl genra -out apiserver.key 2048
-> apiserver.key

- certificate signing request

openssl req -new -key apiserver.key -sub "/CN=kube-apiserver" -out apiserver.csr -openssl.cnf
-> apiserver.csr
# openssl.cnf
[req]
req_extensions = v3_req
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation,
subjectAltName = @alt_names
[alt_names]
DNS.1 = kubernetes
DNS.2 = kubernetes.default
DNS.3 = kubernetes.default.svc
DNS.4 = kubernetes.default.svc.cluster.local
IP.1 = 10.96.0.1
IP.2 = 172.17.0.87

- sign certificates

openssl x509 -req -in apiserver.csr -CA ca.crt -CAkey ca.key -out apiserver.crt

 

결과적으로 api server는 private key와 certificate file을 갖게된다.

 

 

예제) kube-apiserver 실행 with related certificates

ExecStart=/usr/local/bin/kube-apiserver \\
--advertise-address=${INTERNAL_IP} \\
--allow-privileged=true \\
--apiserver-count=3 \\
--authorization-mode=Node,RBAC \\
--bind-address=0.0.0.0 \\
--enable-swagger-ui=true \\

# ETCD client certificates
--etcd-cafile=/var/lib/kubernetes/ca.pem \\
--etcd-certfile=/var/lib/kubernetes/apiserver-etcd-client.crt \\
--etcd-keyfile=/var/lib/kubernetes/apiserver-etcd-client.key \\

--etcd-servers=https://127.0.0.1:2379 \\
--event-ttl=1h \\

# Kubelet client certificates
--kubelet-certificate-authority=/var/lib/kubernetes/ca.pem \\
--kubelet-client-certificate=/var/lib/kubernetes/apiserver-etcd-client.crt \\
--kubelet-client-key=/var/lib/kubernetes/apiserver-etcd-client.key \\

--kubelet-https=true \\
--runtime-config=api/all \\
--service-account-key-file=/var/lib/kubernetes/service-account.pem \\
--service-cluster-ip-range=10.32.0.0/24 \\
--service-node-port-range=30000-32767 \\

# ROOT certificate
--client-ca-file=/var/lib/kubernetes/ca.pem \\

# Kube api server certificates
--tls-cert-file=/var/lib/kubernetes/apiserver.crt \\
--tls-private-key-file=/var/lib/kubernetes/apiserver.key \\

--v=2

 

Kubelet-server certificates for kubelet nodes : kubelet.crt, kubelet.key

# kubelet-config.yaml(node01)
kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
authentication:
  x509:
    clientCAFile: "/var/lib/kubernetes/ca.pem"
authorization:
  mode: Webhook
clusterDomain: "cluster.local"
clusterDNS:
  - "10.32.0.10"
podCIDR: "${POD_CIDR}"
resolvConf: "/run/systemd/resolve/resolv.conf"
runtimeRequestTimeout: "15m"
tlsCertFile: "/var/lib/kubelet/kubelet-node01.crt"
tlsPrivateKeyFile: "/var/lib/kubelet/node01.key"

 

이렇게 생성된 증명서들은 어디에 저장되어 있을까?

 

/etc/kubernetes/manifests/kube-apiserver.yaml

...

spec:
containers:
- command:
- kube-apiserver
- --authorization-mode=Node,RBAC
- --advertise-address=172.17.0.32
- --allow-privileged=true

- --client-ca-file=/etc/kubernetes/pki/ca.crt

- --disable-admission-plugins=PersistentVolumeLabel
- --enable-admission-plugins=NodeRestriction
- --enable-bootstrap-token-auth=true

- --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt
- --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt
- --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key

- --etcd-servers=https://127.0.0.1:2379
- --insecure-port=0

- --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt
- --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key

- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname

- --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt
- --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key

- --secure-port=6443
- --service-account-key-file=/etc/kubernetes/pki/sa.pub
- --service-cluster-ip-range=10.96.0.0/12

- --tls-cert-file=/etc/kubernetes/pki/apiserver.crt
- --tls-private-key-file=/etc/kubernetes/pki/apiserver.key

 

/etc/kubernetes/pki/apiserver.crt 를 살펴보자.

 

- Subject : CN=kube-apiserver

- Alternative Names : ...

- Not After : F2b 11 05:39:20 2020 GMT

- Issuer : CN=kubernetes

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

Certificate:
Data:
Version: 3 (0x2)
Serial Number: 3147495682089747350 (0x2bae26a58f090396)
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN=kubernetes
Validity
Not Before: Feb 11 05:39:19 2019 GMT
Not After : Feb 11 05:39:20 2020 GMT
Subject: CN=kube-apiserver
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:d9:69:38:80:68:3b:b7:2e:9e:25:00:e8:fd:01:
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Key Usage: critical
Digital Signature, Key Encipherment
X509v3 Extended Key Usage:
TLS Web Server Authentication
X509v3 Subject Alternative Name:
DNS:master, DNS:kubernetes, DNS:kubernetes.default,
DNS:kubernetes.default.svc, DNS:kubernetes.default.svc.cluster.local, IP
Address:10.96.0.1, IP Address:172.17.0.27

Certificate Signing Request in Kubernetes

첬째, private key를 생성한다.

openssl genrsa -out jane.key 2048
-> jane.key

둘째, private key를 이용해서 csr을 생성한다.

openssl req -new -key jane.key -subj "/CN=jane" -out jane.csr
-> jane.csr
cat jane.csr | base64

셋째, csr 파일을 base64 처리한 뒤 request 필드에 입력한다.

apiVersion: certificates.k8s.io/v1beta1
kind: CertificateSigningRequest
metadata:
  name: jane
spec:
  groups:
  - system:authenticated
  usages:
  - digital signature
  - key encipherment
  - server auth
request:
     LS0tLSdfdfdff ...(jane.csr by base64)

 

Certificate Signing Request  조회, 제어

- kubectl get csr

- kubectl certificate approve jane

kubectl certificate deny jane

- kubectl delete csr jane

- kubectl get csr jane -o yaml

- certificate 원문을 확인하고 싶을 경우 : echo "LS0adsf ..." | base64 --decode

----BEGIN CERTIFICATE -----
xxxxxxxxxx
xxxxxxxxxx
xxxxxxxxxx
----END CERTIFICATE -----

 

Certificate Signing Request는 누가 관리할까?

 

kube-controller-manager

- csr에 대한 관리를 맡고 있음(csr-approving, csr-signing)

- root certificate : cluster-signing-cert-file, cluster-signing-key-file

# /etc/kubernetes/manifests/kube-controller-manager.yaml
spec:
containers:
- command:
- kube-controller-manager
- --address=127.0.0.1
# Root Certificates
- --cluster-signing-cert-file=/etc/kubernetes/pki/ca.crt
- --cluster-signing-key-file=/etc/kubernetes/pki/ca.key

- --controllers=*,bootstrapsigner,tokencleaner
- --kubeconfig=/etc/kubernetes/controller-manager.conf
- --leader-elect=true

- --root-ca-file=/etc/kubernetes/pki/ca.crt

- --service-account-private-key-file=/etc/kubernetes/pki/sa.key
- --use-service-account-credentials=true

 


여러 개의 Kubernetes cluster가 존재할 경우 kubectl context를 사용해서 손쉽게 원하는 클러스터 환경으로 전환할 수 있다. 이때 context에 대한 환경설정을 kubeConfig에 설정할 수 있다.

 

매번 다른 증명서로 리소스 조회를 요청하는 것은 아주 지루한 작업이 될 수 있다.

$ curl https://kube-apiserver:6443/api/v1/pods \
--key admin.key 
--cert admin.crt
--cacert ca.crt

{
	"kind": :PodList",
    "apiVersion": "v1",
    "metadata": {
    	"selfLink": "/api/v1/pods",
    },
    "items": []
}

$ kubectl get pods
--server kube-apiserver:6443
--client-key admin.key
--client-certificate admin.crt
--certificate-authority ca.crt

 

KubeConfig

kubectl을 설치하면 $HOME/.kube/config 파일에 context 설정을 할 수 있다.

 

아래는 자주 사용하는 명령어이다.

- kubectl config view

- kubectl config view --kubeconfig=my-custom-config

- kubectl config use-context prod-user@production

- kubectl config -h (help)

 

To use that context, run the command

>> kubectl config --kubeconfig=/root/my-kube-config use-context research


To know the current context, run the command

>> kubectl config --kubeconfig=/root/my-kube-config current-context

apiVersion: v1
kind: Config
current-context: my-admin@my-ground

# Contexts
contexts:
- name: my-admin@my-ground
  context:
    cluster: my-ground
    user: my-admin
    namespace: default
    
# Clusters
clusters:
- name: my-ground
  cluster:
    certificate-authority: ca.crt
    #certificate-authority-data: xxx(cat ca.crt | base64)
    server: https://my-ground:6443

# Users
users:
- name: my-admin
  user:
    client-certificate: admin.crt
    client-key: admin.key

 

증명서 없이 kube api server에 접근할 수 있는 방법이 있을까?

 

kubectl proxy

아래는 kube api server를 증명서 없이 접근하는 예시이다.

$ kubectl proxy
Starting to server on 127.0.0.1:8001

$ curl http://localhost:8081 -k
{
    "paths": [
    "/api",
    "/api/v1,
  
    ...


}

 


참고)

1. SSH access using 키페어(public key, private key)

ssh-keygen로 키페어 생성

- id_rsa(private key), id_rsa.pub(public key)

$ ssh-keygen -t rsa -f id_rsa
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in id_rsa.
Your public key has been saved in id_rsa.pub.
The key fingerprint is:
SHA256:rEDRHwUPEG/HgPZNhjFrFVTHpDfPKDBPjpEeRbpFjVU aicar@aicar-dev-baston-server
The key's randomart image is:
+---[RSA 2048]----+
|    ..o+**=+=*+.E|
|     .+.oOo=.oo  |
|    .. o==@ + o  |
|   .   +oo.% . = |
|    .   S + + . o|
|     . .     .   |
|      .          |
|                 |
|                 |
+----[SHA256]-----+

- ssh -i id_rsa user@server

서버에는 public key가 ~/.ssh/authorized_keys에 보관된다.

클라이언트는 private key로 서버에 접속할 수 있다.

 

2. 도커 로그

kubectl 동작하지 않을 경우 서비스 로그를 확인하고 싶을 경우 도커 로그를 활용하자.

docker ps -a
docker logs xxx

 

+ Recent posts