k8s 인증 완벽이해 #5 - Proxy 인증

쿠버네티스 인증 완벽 이해 시리즈 5탄, Proxy를 통한 쿠버네티스 인증에 대해서 살펴보는 시간을 가져 보겠습니다.

쿠버네티스 인증 완벽 이해 시리즈

  1. X.509 Client Certs: X.509 인증서를 이용한 상호 TLS 인증
  2. HTTP Authentication: HTTP Authentication을 이용한 사용자 인증
  3. OpenID Connect: Google OAuth와 같은 인증 provider를 이용한 인증
  4. Webhook 인증: Webhook 인증서버를 통한 사용자 인증
  5. Proxy Auth: Proxy 서버를 통한 대리 인증

쿠버네티스 인증 완벽 이해 시리즈의 마지막 포스트, “Proxy 서버를 이용한 대리 인증” 편입니다. Webhook을 통한 인증에서도 쿠버네티스의 인증을 Webhook 서버에게 대리했었는데 proxy 대리 인증과는 어떤 차이점이 있을까요? 이번 포스트에서는 Webhook 인증과 Proxy 인증의 차이점에 대해서 살펴보고 Proxy 서버의 장점에 대해서 살펴 보도록 하겠습니다.


환경설정

Proxy 인증을 위한 테스트 환경은 다음과 같습니다.

  • OS: Ubuntu 18.04 기준
  • root 권한 (sudo 권한) 필요
  • proxy 서버: nginx (docker)
  • 쿠버네티스 설치툴: kubeadm

Proxy 서버

proxy 서버란 무엇가를 대신 해주는 서버를 말합니다. 웹 서버 앞단에서 네트워크 보안과 캐싱 레이어를 제공해주며 외부 트래픽을 받아서 웹 서버로 전달해주는 서버를 proxy 서버 (엄밀히 얘기해서는 reverse proxy) 하죠. 쿠버네티스 proxy 인증 방식에서는 바로 이 proxy 서버가 인증을 대신합니다.

Proxy vs Webhook 인증 서버

Proxy 서버도, Webhook 서버도 둘다 쿠버네티스를 대신하여 외부 시스템이 대리로 인증을 처리합니다. 이 두개 방식의 가장 큰 차이점은 무엇일까요?

보시다시피 Webhook 방식의 경우, 쿠버네티스로 사용자 인증 요청이 들어오면 쿠버네티스 API 서버에서 Webhook 서버로 인증 대리 요청을 전달합니다. 반면에 Proxy 방식인 경우, 쿠버네티스 API 서버 앞단에 위치하여 처음부터 Proxy 서버로 사용자 인증 요청이 들어오게 됩니다.

Proxy 서버 인증의 장점

Proxy 서버를 통하여 인증 절차를 대리하면 어떤 장점이 있을까요?

1. API 서버 보안 강화

API 서버의 주소를 사용자로부터 직접적으로 노출시키지 않기 때문에 API 서버의 보안 측면이 강화됩니다.

2. API 서버 추상화

사용자는 Proxy 서버의 IP만 알고 있기 때문에 앞서 얘기한 보안 측면과 더불어 API 서버의 주소가 바뀌게 되더라도 사용자 입장에서는 변경점 없이 동일한 IP로 접근을 할 수 있습니다.

2. 가용성 증대

Proxy 서버가 API 서버 앞단에 위치하며 LoadBalancer 역할까지 수행할 수도 있습니다. 복수의 API 서버를 Proxy 서버 뒤에 놓게 되면 부하 분산 효과와 더불어 가용성을 증대 시킬 수 있습니다.

3. 트래픽 모니터링

사용자가 항상 Proxy 서버를 거치기 때문에 Proxy 서버에 적절한 모니터링 기능을 탑재한다면 API서버 호출량 및 트래픽 등을 확인할 수 있습니다.

Proxy 서버 구축

Proxy 인증 서버를 구축하기 위해서는 크게 다음 두가지 설정이 필요합니다.

Mutual TLS

각 서버간의 암호 통신 및 신뢰 관계를 구축하기 위해 상호 TLS 인증을 사용합니다. 첫번째 포스트, X.509 Cert에서 살펴본 것과 동일한 방식으로 사용자와 proxy간 통신, proxy 서버와 API 서버간의 통신을 상호 TLS로 교신합니다.

Proxy Header

Proxy 서버에서 인증된 사용자의 정보가 API 서버에 전달되어야 합니다. 인증 이후의 권한허가 작업을 위한 사용자 인식이 필요하기 때문입니다. 이를 위해 Proxy 서버에서 Proxy Header를 통하여 API 서버에 필요한 정보를 전달합니다. API 서버에서는 사전에 정의된 Proxy Header를 통하여 인증 받은 사용자가 누구인지 인식을 하게 됩니다.

Proxy 인증 실험

API 서버 설정

Proxy 서버와 통신하기 위해 API 서버에 다음과 같은 설정이 필요합니다.

  1. API Server Cert: API 서버의 인증서 설정이 필요합니다.
  2. Client CA 설정: Proxy 서버의 인증서를 검증하기 위한 클라이언트 CA가 필요합니다.
  3. Proxy header: Proxy 서버에서 전달하는 헤더 정보의 의미를 지정합니다.

1. API 서버 Cert 설정

  • --tls-cert-file: API 서버의 인증서
  • --tls-private-key-file: API 서버 인증서 개인키

2. Client CA 설정

  • --requestheader-client-ca-file: 클라이언트 인증서를 확인할 CA 인증서
  • --requestheader-allowed-names: CA를 통해 검증된 인증서 중 특정 이름을 가진 인증서만 선별하여 허용할 수 있습니다.

3. Proxy Header 설정

  • --requestheader-username-headers: 쿠버네티스 사용자로 식별할 헤더 이름 설정
  • --requestheader-group-headers: 쿠버네티스 그룹으로 식별할 헤더 이름 설정
  • --requestheader-extra-headers-prefix: 그외 추가 헤더 이름 설정
# API 서버 설정 파일
sudo vi /etc/kubernetes/manifests/kube-apiserver.yaml
# -----[kube-apiserver.yaml]------
    - kube-apiserver
    - --allow-privileged=true
    - --authorization-mode=Node,RBAC
    # .....
    # API 서버 인증서 설정
    - --tls-cert-file=/etc/kubernetes/pki/apiserver.crt
    - --tls-private-key-file=/etc/kubernetes/pki/apiserver.key
    # Client CA 설정
    - --requestheader-allowed-names=front-proxy-client
    - --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt
    # Proxy 헤더 설정
    - --requestheader-username-headers=X-Remote-User
    - --requestheader-extra-headers-prefix=X-Remote-Extra-
    - --requestheader-group-headers=X-Remote-Group
# --------------------------------

kubeadm을 이용하여 쿠버네티스 구축시, proxy 서버 연결을 위한 API 서버의 옵션들이 기본적으로 이미 설정되어 있습니다. 자세한 값들을 살펴보겠습니다.

  • --requestheader-allowed-names: front-proxy-client라는 이름을 가진 인증서에 대해서 허용합니다.
  • --requestheader-client-ca-file: front-proxy-ca.crt라는 클라이언트 인증용 CA를 사용합니다.
  • --requestheader-username-headers: 사용자 식별 헤더로 X-Remote-User를 사용합니다. NGINX proxy에서 다음 헤더값을 전달합니다.
  • --requestheader-group-headers: 그룹 식별 헤더로 X-Remote-Group을 사용합니다.
  • --requestheader-extra-headers-prefix: 추가 헤더로 X-Remote-Extra prefix를 추가하여 헤더로 전달합니다.

NGINX Proxy 서버 설정

NGINX Proxy 서버에서는 다음과 같은 설정이 필요합니다.

  1. Proxy Server cert: 사용자 입장에서 NGINX Proxy는 server-side입니다. server용 인증서가 필요합니다.
  2. Proxy client cert: API 서버 입장에서 NGINX Proxy는 client-side입니다. client용 인증서가 필요합니다.
  3. Proxy pass header: Proxy 서버에서 어떤 헤더들을 API 서버로 보낼지 설정합니다.
  4. API server CA: Proxy 서버 입장에서 API 서버 인증서를 확인하기 위해 API 서버의 CA가 필요합니다.
  5. Proxy auth: API 서버를 대신할 Proxy 서버의 인증 설정이 필요합니다. 예시에서는 단순 basic auth를 사용할 예정입니다.

1. Proxy Server Cert 설정

NGINX 인증서 설정에 인증 완벽이해 1편에서 만든 인증서들을 가져와 사용하겠습니다. 가장 먼저 사용자에게 제공할 Proxy Server Cert를 설정합니다.

# default.conf
server {
    listen 443 ssl;
    server_name localhost;

    # Proxy Server Cert
    ssl_certificate      /etc/nginx/conf.d/server.pem;
    ssl_certificate_key  /etc/nginx/conf.d/server-key.pem;
}
  • ssl_certificate: Proxy 서버의 인증서를 설정합니다.
  • ssl_certificate_key: 인증서의 개인키를 설정합니다.

2. Proxy Client Cert 설정

이제 API 서버에게 인증을 받기 위한 client-side 인증서를 설정합니다.

server {
    listen 443 ssl;
    server_name localhost;

    # Proxy Server Cert
    ssl_certificate      /etc/nginx/conf.d/server.pem;
    ssl_certificate_key  /etc/nginx/conf.d/server-key.pem;

    location / {
      proxy_pass https://$API_SERVER_ADDR:$API_SERVER_PORT;

      # Proxy Client Cert
      proxy_ssl_certificate         /etc/kubernetes/pki/front-proxy-client.crt;
      proxy_ssl_certificate_key     /etc/kubernetes/pki/front-proxy-client.key;
    }
}
  • proxy_pass: 트래픽을 전달할 서버(API 서버)의 주소 및 포트를 입력합니다.
  • proxy_ssl_certificate: API 서버에게 인증을 받을 클라이언트 인증서를 설정합니다. (kubeadm설치시, front-proxy-client라는 이름으로 제공합니다.)
  • proxy_ssl_certificate_key: 클라이언트 개인키를 설정정합니다.

3. Proxy pass header 설정

server {
    listen 443 ssl;
    server_name localhost;

    # Proxy Server Cert
    ssl_certificate      /etc/nginx/conf.d/server.pem;
    ssl_certificate_key  /etc/nginx/conf.d/server-key.pem;

    location / {
      proxy_pass https://$API_SERVER_ADDR:$API_SERVER_PORT;

      # Proxy Client Cert
      proxy_ssl_certificate         /etc/kubernetes/pki/front-proxy-client.crt;
      proxy_ssl_certificate_key     /etc/kubernetes/pki/front-proxy-client.key;

      # Proxy header
      proxy_set_header X-Remote-User $remote_user;
      proxy_set_header X-Remote-Group system:masters;
    }
}
  • X-Remote-User: API 서버에 전달할 사용자 식별자를 지정합니다.
  • X-Remote-Group: API 서버에 전달할 그룹 식별자를 지정합니다.

API 서버에서 지정한 헤더값들을 proxy 서버에서 전달합니다.

4. API Server CA 설정

server {
    listen 443 ssl;
    server_name localhost;

    # Proxy Server Cert
    ssl_certificate      /etc/nginx/conf.d/server.pem;
    ssl_certificate_key  /etc/nginx/conf.d/server-key.pem;

    location / {
      proxy_pass https://$API_SERVER_ADDR:$API_SERVER_PORT;

      # Proxy Client Cert
      proxy_ssl_certificate         /etc/kubernetes/pki/front-proxy-client.crt;
      proxy_ssl_certificate_key     /etc/kubernetes/pki/front-proxy-client.key;

      # Proxy header
      proxy_set_header X-Remote-User $remote_user;
      proxy_set_header X-Remote-Group system:masters;

      # API server CA
      proxy_ssl_trusted_certificate /etc/kubernetes/pki/ca.crt;
      proxy_ssl_name                kubernetes;
      proxy_ssl_verify              on;

    }
}
  • proxy_ssl_trusted_certificate: API 서버의 인증서를 검증하기 위해 API 서버 인증키를 발급한 CA 인증서를 설정합니다.
  • proxy_ssl_name: API 서버의 검증할 인증서 이름을 설정합니다. (API 서버의 SAN (Subject Alternative Name)을 입력합니다.)
  • proxy_ssl_verify: 서버 인증서 검증을 활성화합니다. (default는 비활성화 입니다.)

5. Proxy auth 설정

server {
    listen 443 ssl;
    server_name localhost;

    # Proxy Server Cert
    ssl_certificate      /etc/nginx/conf.d/server.pem;
    ssl_certificate_key  /etc/nginx/conf.d/server-key.pem;

    # Proxy auth
    auth_basic "basic auth";
    auth_basic_user_file /etc/nginx/conf.d/auth; 

    location / {
      proxy_pass https://$API_SERVER_ADDR:$API_SERVER_PORT;

      # Proxy Client Cert
      proxy_ssl_certificate         /etc/kubernetes/pki/front-proxy-client.crt;
      proxy_ssl_certificate_key     /etc/kubernetes/pki/front-proxy-client.key;

      # Proxy header
      proxy_set_header X-Remote-User $remote_user;
      proxy_set_header X-Remote-Group system:masters;

      # API server CA
      proxy_ssl_trusted_certificate /etc/kubernetes/pki/ca.crt;
      proxy_ssl_name                kubernetes;
      proxy_ssl_verify              on;

    }
}

Basic Auth 파일은 쿠버네티스 인증 완벽이해 2탄에서 생성한 사용자 파일을 동일하게 사용합니다.

  • auth_basic: 사용자 검증을 위해 basic auth를 사용합니다.
  • auth_basic_user_file: 사용자 파일을 설정합니다.

default.conf 파일 작성이 완료되면 NGINX proxy 서버를 실행합니다. 정상적으로 실행이 된다면 이상없이 설정이 완료된 것입니다.

docker run -p 8443:443 -v $(pwd):/etc/nginx/conf.d nginx

인증 테스트

curl --cacert k8s-rootCA.pem -v -H "Authorization: Basic $(echo -n user1:pass1 | base64)" https://localhost:8443/api
kubectl config set-cluster proxy --server=https://localhost:8443 --insecure-skip-tls-verify=true
kubectl config set-credentials proxy-auth --username=user1 --password=pass1
kubectl config set-context proxy-context --cluster=proxy --user=proxy-auth
kubectl config use-context proxy-context

kubectl get pod -n kube-system
# 성공!

# verbose 레벨을 높히면 API 서버 IP가 아닌 Proxy 서버로 요청이 전달되는 것을 확인할 수 있습니다.
kubectl get pod -n kube-system -v 6
# GET https://localhost:8443/api/v1/namespaces/kube-system/pods?limit=500 200 OK

마치며

쿠버네티스 인증 완벽이해 시리즈의 마지막 포스트, Proxy 인증 서버를 이용한 대리 인증에서 Proxy 서버가 무엇인지에 대해서 알아보았고 그 장점과 사용법에 대해서 살펴 보았습니다. Proxy 서버를 사용함으로써, 보안과 가용성, 두마리 토끼를 다 잡을 수 있었습니다.

지금까지 여러 쿠버네티스 인증 방법에 대해서 하나씩 실습과 함께 살펴 봤습니다. 부족하지만 “k8s 인증 완벽이해 시리즈”를 준비해 봤습니다. 해당 포스트들을 통해 쿠버네티스에 대해 조금 더 자세히 이해할 수 있는 시간이 되셨으면 좋겠습니다.