티스토리 뷰

728x90
반응형

서론

본 포스팅에서는 Kubernetes Ingress에 SSL 인증서를 적용하는 방법에 대해 알아보겠습니다.

기존 3Tier legacy 환경의 경우 SSL 인증서는 WebServer에 적용하는 경우가 많았습니다.

Kubernetes에서는 Ingress Controller가 이 역할을 담당합니다. Ingress는 Kubernetes의 유입점에서 Client의 요청을 받아주는 역할을 담당하기에 SSL 인증서를 이곳에 적용하게 됩니다.

지금부터 SSL/TLS에 대해 살펴보고 Kubernetes Ingress Controller에 SSL 인증서 적용 방법에 대해 살펴보도록 하겠습니다.

본문

SSL 인증서를 Kubernetes Ingress에 적용하기 전에 먼저 간단히 SSL/TLS에 대해 살펴보고 Client와 Server간 어떻게 통신이 이루어지는지 살펴보도록 하겠습니다.

1. SSL(Secure Socket Layer)과 TLS(Transport Layer Security)

웹 보안은 전자상거래에서 빠질 수 없는 필수 요건이 되었으며, 홈페이지 운영 시 개인정보를 취급하거나 민감정보를 다룰 경우 보안서버의 기반이 되는 SSL/TLS 프로토콜을 반드시 적용해야 합니다. 

SSL(Secure Sockets Layer)은 보안 소켓 계층 이라는 뜻으로 인터넷을 통해 전달되는 정보 보안의 안전한 거래를 허용하기 위해 Netscape 사에서 개발한 인터넷 통신 규약 프로토콜이며, TLS(Transport Layer Security)는 SSL 3.0을 기초로 해서 IETF가 만든 프로토콜로 이는 SSL3.0을 보다 안전하게 하고 프로토콜의 스펙을 더 정확하고 안정성을 높이는 목적으로 고안되었습니다.

SSL과 TLS 두 가지 프로토콜은 TCP/IP 네트워크를 사용하는 통신에 적용되며, 통신과정 에서 전송계층 종단간 보안과 데이터 무결성을 확보 할 수 있게 합니다.

*참고
1.0 버전은 공개 된 적이 없고, 2.0 버전이 1995년 2월에 이르러 릴리스가 되지만 이 버전은 많은 보안 결함
때문에 3.0 버전으로 곧바로 이어집니다.
3.0은 1996년 릴리스 되었고, 3.0 버전은 TLS 버전 1.0의 기초가 된 후, IETF에서 1999년 1월에 RFC 2246 
표준 규약으로 정의하게 되었습니다.

2. SSL/TLS 통신 과정

SSL/TLS통신 과정은 아래의 그림과 같은 과정으로 handshaking 이 이루어 집니다.

1) Client가 request를 하면 TCP에서 연결이 맺어진 이후 SSL/TLS Handshacke 을 수행합니다.
ClientHello 메시지를 보내면서 이 메시지에는 클라이언트에서 가능한 TLS 버전, 세션 식별자, 암호 설정 등의 정보가 포함됩니다.

2~4) Server에서 암호화를 위한 인증서의 공개키를 내려주게 되면 Client는 이 공개키가 신뢰할 수 있는 인증서의 키 인지 확인(client에 등록이 되어있음, 예를들어 브라우저의 경우 신뢰할 수 있는 인증기관 list가 등록되어있음)합니다.

서버는 ServerHello 메시지를 클라이언트에게 보냅니다. 여기에는 ClientHello 메시지의 정보 중 서버에서 사용하기로 선택한 TLS 버전, 세션 식별자, 암호 설정 등의 정보가 포함됩니다.

또한 Certificate 메시지를 전송 합니다.(서버 인증서) 이 인증서는 인증 기관에서 발급받은 것이며, 서버가 신뢰할 수 있는 자임을 인증합니다. 전송이 끝나면 ServerHelloDone 메시지를 보내 끝났음을 알립니다.

5~7) Client 단에서는 해당 공개키를 신뢰한다면 이 Client의 Session Key를 생성하여, 서버로부터 받은 공개키로 암호화하고 Server로 전달합니다. (사용 가능한 cipherspec 포함)

또한, Client는 서버에서 받은 인증서를 검증합니다. 유효 기간이 만료되지 않았는지, 신뢰할 수 있는 인증 기관에서 발급되었는지, 그 인증서가 해당 서버에게 발급된 인증서가 맞는지 등을 확인합니다. 서버를 신뢰할 수 있다고 판단하였다면 다음 단계로 넘어갑니다.

클라이언트는 임의의 pre-master secret을 생성한 뒤, 서버가 보낸 인증서에 포함된 공개키를 사용해 암호화하고, 이렇게 암호화된 pre-master secret을 ClientKeyExchange 메시지에 포함시켜 서버에 전송합니다.

8~9) 서버는 서버에 있는 개인키를 통해 복호화를 하여 Client의 Session Key를 확보하고, Client가 준 cipherspec에서 서버가 허용하는 chipherspec을 client에 전달합니다.

서버는 전송받은 정보를 복호화하여 pre-master secret을 알아낸 뒤, 이 정보를 사용해 master secret을 생성합니다. 그 뒤 master secret에서 세션 키를 생성해내며, 이 세션 키는 서버와 클라이언트 간의 통신을 암호화하는데 사용됩니다. 물론 클라이언트 역시 자신이 만들어낸 pre-master secret을 알고 있으므로, 같은 과정을 거쳐 세션 키를 스스로 만들게 됩니다.

 

위 과정을 통해 server와 client는 동일한 session key를 가지게 되어 이후부터는 이 session key를 가지고 상호 암, 복호화를 수행하게됩니다.

이제 서버와 클라이언트는 각자 동일한 세션 키를 가지고 있으며, 이를 사용해 대칭 키 암호를 사용하는 통신을 할 수 있습니다. 따라서 우선 서로에게 ChangeCipherSpec 메시지를 보내 앞으로의 모든 통신 내용은 세션 키를 사용해 암호화해 보낼 것을 알려준 뒤, Finished 메시지를 보내 각자의 Handshacke 과정이 끝났음을 알리게 됩니다.

이와 같은 방식으로 적용된 SSL 인증서가 옳바른 인증서 인지 검증하기 위해 다양한 openssl 명령어를 다루기도 하지만, 인터넷 환경에서 검증이 가능하다면 다음 사이트를 참고해서 SSL 인증 적용 여부를 검증할 수 있습니다.

추천 1) https://www.digicert.com/help/

추천 2) https://www.ssllabs.com/ssltest/

위 사이트 이외에도 다양한 SSL 검증 사이트 들이 존재하며, 현재 적용된 버전에 대한 검증은 물론 취약점으로 진단된 결과에 대해 조치 방안까지 제공해 주고 있으니, 홈페이지 오픈 이전 한번씩 테스트 해보는 것도 도움이 될 듯합니다.

그럼 본격적으로 Kubernetes에 인증서를 적용해 보도록 하겠습니다.

3. Kubernetes 인증서 적용

1) Kubernetes Sample 인증서 생성

먼저 Kubernetes에서 사용하는 Sample 인증서를 이용해 Ingress에 SSL을 적용해 보도록 하겠습니다.

Kubernetes의 Sample 인증서는 사실 Kubernetes 내부에서 사용하는 인증서 역할을 수행합니다. Kubernetes Master Node가 설치된 환경의 계정 홈 하위 .kube/config 파일에서 crt & key 파일을 추출할 수 있습니다.

[root@kubemaster ~]# grep 'client-certificate-data' ~/.kube/config | head -n 1 | awk '{print $2}' | base64 -d >> kubecfg.crt

[root@kubemaster ~]# cat kubecfg.crt 

-----BEGIN CERTIFICATE-----

...

-----END CERTIFICATE-----

[root@kubemaster ~]#

kubecfg.crt 파일은 파일을 암호화 할 수 있도록 Client에게 보내주는 Public Key 역할을 하는 CertificateFile입니다.

[root@kubemaster ~]# grep 'client-key-data' ~/.kube/config | head -n 1 | awk '{print $2}' | base64 -d >> kubecfg.key

[root@kubemaster ~]# cat kubecfg.key 

-----BEGIN RSA PRIVATE KEY-----

...

-----END RSA PRIVATE KEY-----

[root@kubemaster ~]# 

kubecfg.key 파일은 Client가 보내온 암호화 된 파일을 서버에서 복호화 하기 위한 Private Key 역할을 하는 CertificateKeyFile입니다.

crt & key 파일이 생성되면 다음과 같이 Kubernetes Ingress에 적용할 수 있습니다.

2) secret tls 적용

Kubernetes에 SSL 인증서를 적용하기 위해서는 인증서를 포함하는 Secret tls를 생성해야 합니다.

다음과 같은 형태로 생성할 수 있습니다.

kubectl create secret tls [secret_name] --cert [crtfile_name] --key [keyfile_name]

[root@kubemaster ~]# kubectl create secret tls nrson-tls --cert test.nrson.co.kr/kubecfg.crt --key test.nrson.co.kr/kubecfg.key

secret/nrson-tls created

[root@kubemaster ~]# kubectl get secret
NAME                  TYPE                                  DATA   AGE
nrson-tls             kubernetes.io/tls                     2      6m18s
[root@kubemaster ~]# kubectl describe secret nrson-tls
Name:         nrson-tls
Namespace:    default
Labels:       
Annotations:  

Type:  kubernetes.io/tls

Data
====
tls.key:  1675 bytes
tls.crt:  1082 bytes
[root@kubemaster ~]#

위와 같이 secret을 생성이 완료되면, 이를 ingress에 생성한 tls를 적용해 주어야 합니다.

기존 Ingress의 yaml 파일에 아래 빨간색으로 표시한 tls 부분을 추가해 주어야 합니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: wildfly-app-deployment
  labels:
    app: wildfly-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: wildfly-app
  template:
    metadata:
      labels:
        app: wildfly-app
    spec:
      containers:
      - name: wildfly-app
        image: 192.168.56.100:5000/middleware/wildfly/wildfly_custom_image:latest
        imagePullPolicy: Always
        ports:
        - containerPort: 8080
      nodeSelector:
        key: worker
---
apiVersion: v1
kind: Service
metadata:
  name: wildfly-app
spec:
  selector:
    app: wildfly-app
  ports:
    - protocol: TCP
      port: 8080
      targetPort: 8080
  type: ClusterIP
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: wildfly-app-ingress
  namespace: default
spec:
  rules:
  - host: test.nrson.co.kr
    http:
      paths:
      - backend:
          serviceName: wildfly-app
          servicePort: 8080
        path: /
  tls:
  - hosts:
    - test.nrson.co.kr
    secretName: nrson-tls

이후 ingress를 재반영합니다.

[root@kubemaster deployment]# kubectl get ingress

NAME                  HOSTS              ADDRESS   PORTS     AGE

wildfly-app-ingress   test.nrson.co.kr             80, 443   8m25s

[root@kubemaster deployment]# kubectl describe ingress wildfly-app-ingress

Name:             wildfly-app-ingress

Namespace:        default

Address:          

Default backend:  default-http-backend:80 ()

TLS:

  nrson-tls terminates test.nrson.co.kr

Rules:

  Host              Path  Backends

  ----              ----  --------

  test.nrson.co.kr  

                    /   wildfly-app:8080 (10.233.104.32:8080)

Annotations:

  kubectl.kubernetes.io/last-applied-configuration:  {"apiVersion":"extensions/v1beta1","kind":"Ingress","metadata":{"annotations":{},"name":"wildfly-app-ingress","namespace":"default"},"spec":{"rules":[{"host":"test.nrson.co.kr","http":{"paths":[{"backend":{"serviceName":"wildfly-app","servicePort":8080},"path":"/"}]}}],"tls":[{"hosts":["test.nrson.co.kr"],"secretName":"nrson-tls"}]}}

Events:

  Type    Reason  Age    From                      Message

  ----    ------  ----   ----                      -------

  Normal  CREATE  8m30s  nginx-ingress-controller  Ingress default/wildfly-app-ingress

  Normal  UPDATE  41s    nginx-ingress-controller  Ingress default/wildfly-app-ingress

[root@kubemaster deployment]#

위와 같이 wildfly-app-ingress에 443 포트가 함께 활성화 되었고, test.nrson.co.kr을 서비스하는 wildfly-app-ingress에 nrson-tls가 적용된 것을 확인할 수 있습니다.

4. 유의 사항

다음으로 살펴볼 내용은 인증서 적용 시 유의해야 할 점들에 대해 몇가지 살펴보도록 하겠습니다.

1) RootCA / CAChain 인증서 적용

SSL 인증서에는 Certificate & CertificateKey 파일 이외에도 RootCA & CAChain 인증서가 존재합니다.

이 인증서는 어떠한 것이며, Kubernetes에 반영하기 위한 방법을 살펴보도록 하겠습니다.

- RootCA는 말 그대로 Certificate Authority를 제공하는 인증서 발급 업체 즉, 많은 사람들이 알고 있는 GPKI, DigiCert 등의 기관의 인증서입니다.

- Client와 Server간의 인증서 체크 이외에도 이를 Client가 보다 신뢰할 수 있는 인증서 인지를 기관인증을 통해 추가로 신뢰성을 확보하기 위함이며, Client는 RootCA 인증서를 통해 인증 받기 위한 Intermediate(중개) 인증서 즉 CA Chain 인증서를 통해 인증을 받게 됩니다.

이러한 기관 인증서를 포함하여 Kubernetes에 적용하기 위해서는 다음과 같이 Public 인증서 파일(*.crt)을 수정해 주어야 합니다.

[root@kubemaster ~]# vi kubecfg.crt 

-----BEGIN CERTIFICATE-----

CRT FILE ....

-----END CERTIFICATE-----

-----BEGIN CERTIFICATE-----

CA FILE ....

-----END CERTIFICATE-----

[root@kubemaster ~]# 

기존에 생성한 crt 파일 하단에 CA Chain 인증서를 추가하고 저장합니다.

추가한 인증서를 기반으로 secret을 재생성합니다.

재생성하는 절차는 사실상 인증서 기간이 만료되었을 경우 재반영하는 절차와 비슷하다고 볼 수 있습니다.

[root@kubemaster ~]# kubectl delete secret nrson-tls

secret "nrson-tls" deleted

[root@kubemaster ~]# kubectl create secret tls nrson-tls --cert test.nrson.co.kr/kubecfg.crt --key test.nrson.co.kr/kubecfg.key

secret/nrson-tls created

[root@kubemaster ~]#

위와 같이 재반영 절차라고는 하지만, 사실상 기존에 생성한 nrson-tls secret을 삭제하고 재생성하는 순서로 적용됩니다.

2) http → https redirect 적용 또는 해제

TLS 적용하면 기본으로 http 서비스는 https로 redirect 되게 됩니다.

http 서비스는 http 서비스로 https 서비스는 https 서비스로 적용하려면 annotation을 사용하여 redirect를 막아주어야 합니다.

[Command 활용 방법]

[root@kubemaster ~]# kubectl annotate ingress wildfly-app-ingress nginx.ingress.kubernetes.io/ssl-redirect="false"

ingress.extensions/wildfly-app-ingress annotated

[root@kubemaster ~]#

위와 같이 kubectl annotate를 활용하여 ssl-redirect=false를 적용합니다.

또는 직접 yaml 파일을 수정하여 반영할 수 있습니다.

[Ingress.yaml 수정]

[root@kubemaster ~]# kubectl get ingress wildfly-app-ingress -o yaml

apiVersion: extensions/v1beta1

kind: Ingress

metadata:

  annotations:

    nginx.ingress.kubernetes.io/ssl-redirect: "false"

  creationTimestamp: "2020-01-04T11:14:09Z"

  generation: 1

  name: wildfly-app-ingress

  namespace: default

  resourceVersion: "241087"

  selfLink: /apis/extensions/v1beta1/namespaces/default/ingresses/wildfly-app-ingress

  uid: 31b063f1-fbf4-4da0-bc1e-fb3807f4b9ee

spec:

  rules:

  - host: test.nrson.co.kr

    http:

      paths:

      - backend:

          serviceName: wildfly-app

          servicePort: 8080

        path: /

  tls:

  - hosts:

    - test.nrson.co.kr

    secretName: nrson-tls

status:

  loadBalancer: {}

[root@kubemaster ~]#

위와 같이 Ingress.yaml에 직접 annotation을 지정할 수 있습니다.

결론

지금까지 Kubernetes에 SSL/TLS 인증서를 적용하는 방법에 대해 살펴보았습니다.

Kubernetes에서 secret tls를 추가하면 Ingress에서는 인증서를 적용할지 여부만 결정해서 해당 deployment application에 반영하는 방식으로 ssl을 손쉽게 적용할 수 있습니다.

물론 여러개의 인증서를 등록하고 서비스 별로 적용하여 사용할 수 있으며, 인증서에서 사용하는 도메인을 멀티도메인 또는 서브도메인 지원 인증서(ex - *.nrson.co.kr)를 적용하여 여러 서비스에서 함께 사용할 수도 있습니다.

본 포스팅에서는 논의되지 않았지만, 인증서가 꼭 Kubernetes Ingress에 있어야하는 것은 아닙니다. 인증서를 기존과 같이 WebServer에 포함시키고 이를 Ingress 뒷 단인 Pod 내부에 두는 방식 역시 사용 가능하며, L4 Switch & CDN 등 Kubernetes 앞단에 인증서를 적용하여 활용할 수도 있습니다.

각 도메인에 맞는 구성과 아키텍처를 수립하고, 인증서를 반영하는 것이 중요하며, 인증서가 두군데 이상 적용되어 불필요한 암복호화 과정이 추가되어 성능 저하 현상을 발생시키지 않아야 할 것입니다.

728x90
반응형