k8s 인증 완벽이해 #1 - X.509 Client Certs

쿠버네티스를 지금까지 사용해 오면서 어렴풋이만 인증서와 토큰을 이용하여 사용자 인증을 하는지는 알고 있엇지만 그 이상 다른 방법에 대해서는 자세히 몰랐었습니다. 쿠버네티스 공인 자격증(CKA)을 취득하기 위해 잠깐 인증서를 이용한 사용자 인증에 대해서 살펴 보았지만 당장 쿠버네티스를 설치하고 사용하는데 만족하여 더 이상 살펴보지 않았습니다. 시간이 지나 인증 체계에 대해서 조금 더 깊은 이해가 필요하게 되어 시간을 갖고 쿠버네티스 인증 체계에 대해서 완벽히 이해해보는 시간을 가져 보도록 하겠습니다.

쿠버네티스 공식 홈페이지에 나와 있는 구성과 동일하게 살펴 보겠습니다. 다만 단순 번역이 아닌 개인적으로 아쉬웠던 부분에 대한 설명을 조금 더 보강하고 직접 따라해 볼 수 있게 재구성 하였습니다. 쿠버네티스 인증 완벽이해 순서는 다음과 같습니다.

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

  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 서버를 통한 대리 인증

환경설정

X.509 Client Certificate를 위한 테스트 환경은 다음과 같습니다.

  • OS: Ubuntu 18.04 기준
  • root 권한 (sudo 권한) 필요
  • 인증서 발급툴: cfssl, cfssljson
  • 인증 테스트 서버: nginx (docker 컨테이너)
  • 쿠버네티스 설치툴: kubeadm

시작하기에 앞서

시작하기에 앞서 다음과 같은 내용들을 먼저 짚고 넘어 가봅시다.

1. 쿠버네티스 접근제어 체계

  • Authentication: 접속한 사람의 신분을 시스템이 인증하는 단계입니다. (신분증 확인)
  • Authorization: 누가 어떤 권한을 가지고 어떤 행동을 할 수 있는지 확인하는 단계입니다. (view권한, create권한 등)
  • Admission Control: 인증과 권한확인 이후에 추가적으로 요청 내용에 대한 검증이나 요청 내용을 강제로 변경할 때 사용합니다.

쿠버네티스 인증 완벽이해 시리즈에서는 Authentication에 대해서 다뤄보려고 합니다.

2. 쿠버네티스 유저 저장소 부재

특이하게 쿠버네티스에서는 내부적으로 유저 인증 정보를 저장하지 않습니다. 대부분의 웹 서비스나 인증 서버들은 사용자 정보를 내부적으로 저장하여 사용자로부터 인증 정보를 전달 받았을 때 저장된 정보를 바탕으로 인증을 처리합니다. (웹 사이트에서 계정과 비밀번호를 입력 받아 유저DB를 조회하여 사용자 인증을 처리하는 방법을 떠올릴 수 있습니다.) 쿠버네티스는 이와 다르게 따로 인증 정보를 저장하지 않고 각각의 인증 시스템에서 제공해주는 신원 확인 기능들을 활용하여 사용자 인증을 하고 유저를 인식(identify) 합니다. 처음에는 이러한 쿠버네티스의 방식에 대해서 의아해 했지만 이러한 특징으로 인해 쿠버네티스에서는 쉽게 인증체계를 확장할 수 있습니다. 쿠버네티스 내부 인증체계에 종속되는 부분이 거의 없기 때문입니다. 쿠버네티스는 사용자 인증체계를 전부 외부 시스템 (혹은 메커니즘)에 의존한다고 볼 수 있습니다. (X.509, HTTP Auth, Proxy Authentication 등)

3. 쿠버네티스 그룹

쿠버네티스 내부적으로 그룹이라는 개념이 존재합니다. 실제 그룹이라는 리소스가 존재하진 않지만 RoleBinding (혹은 ClusterRoleBinding) 리소스 내부에서 string match로 그룹에 따른 권한을 부여할 수 있습니다. 신규 사용자를 생성할 때, 해당 그룹에 속하여서 만들게 되면 그 그룹이 가지고 있는 권한을 동일하게 사용할 수 있습니다. 쿠버네티스에는 특별한 용도로 사용하기 위핸 몇가지 그룹 예약어들이 존재합니다.

그룹 예약어

  • system:authenticated: 사용자 인증을 통과한 그룹을 나타냅니다.
  • system:anonymous: 사용자 인증을 하지 않은 익명 그룹을 나타냅니다.
  • system:masters: 쿠버네티스의 full access 권한을 가진 그룹을 나타냅니다. (admin)

더 자세한 그룹 소개는 쿠버네티스 RBAC 페이지를 참고 바랍니다. 쿠버네티스 인증 시리즈에서 실험을 위해 새로 생성한 사용자에게 바로 권한을 부여하기 위해 간혹 system:masters 그룹을 사용할 예정입니다.

4. Static Pod란?

쿠버네티스에는 Static Pod라는 개념이 존재합니다. kubelet에서 API 서버의 요청과는 상관 없이 특정 디렉토리 안의 Pod YAML 정의서를 바라보고 직접 생성하는 Pod를 의미합니다. api-server와 무관하게 생성된다는 점 이외에는 일반 Pod와 동작 방식이 동일합니다. kubeadm으로 쿠버네티스 클러스터 구축시, Static Pod의 default 디렉토리로 /etc/kubernetes/manifest를 바라봅니다. 앞으로 여러 인증 체계를 살펴보기 위해서는 api server의 설정값을 직접 고쳐야하는 경우가 많은데 kubeadm에서는 이 api 서버가 바로 Static pod로 만들어지기 때문입니다. (어찌보면 당연한 얘기 같습니다. api 서버를 생성하기 위해서 api 서버에 Pod 생성 요청을 할 수 없으니 Static Pod를 통해서 api 서버를 생성합니다. 이러한 이유 때문에 kubeadm이 아닌 다른 쿠버네티스 설치툴에서는 kube-apiserver와 같은 core 컴포넌트들을 Pod (컨테이너) 형태가 아닌 일반적인 프로세스로 실행하는 방법을 사용하기도 합니다.)

5. API 서버는 그냥 웹서버

API 서버는 굉장히 복잡해 보이지만 사실 멀리서 겉모습만 바라보면 일반적인 웹서버와 다를 바가 없습니다. REST API로 요청을 보내면 json, yaml 형식으로 결과를 리턴해주며 내부적으로 DB에 데이터를 저장하는 웹 서버입니다. 그렇기 때문에 쿠버네티스의 사용자 인증 방식도 평범한 웹 서버의 인증 방식과 크게 다르지 않습니다. TLS를 이용한 서버 인증, HTTP Authorization Header를 통한 인증, OAuth 인증, Webhook 및 proxy 인증 서버를 통한 인증이 그것이죠. 저도 복잡하고 거대한 쿠버네티스의 인증 체계를 공부한다기 보다는 작고 간단한 웹서버 인증 방법들에 대해서 살펴본다는 생각으로 공부하였습니다.

그럼 쿠버네티스 authentication 첫번째 방법, X.509 Client Certificate에 대해서 알아보도록 합시다.

X.509 Certificate

X.509 기술에 대해서 알아보고 이 기술을 이용하여 어떻게 사용자 인증을 할 수 있는지 살펴 봅니다.

Public Key Infrastructure

PKI는 비대칭 암호화 기술을 이용한 공개키 기반의 인증 체계입니다. PKI 기술을 이용하여 암호화된 통신(HTTPS)을 하거나 인증(certificate)에 사용할 수도 있습니다. X.509PKI 기술 중에서 가장 널리 알려진 표준 포맷입니다. 쿠버네티스에서는 X.509 Certificate를 이용하여 사용자의 신원을 인증하는 목적으로 사용하고 있습니다.

PKI 기술에는 다양한 개념들이 있지만 여기서는 다음 3가지를 중점으로 설명 드립니다.

  • Public Private Key: 각 키마다 고유의 역할을 가집니다.
  • Certificate: 사용자 신원 정보가 담긴 문서이며 Private키 없이 변조가 불가능한 특징을 가집니다.
  • Certificate Authority: 발급한 Certificate을 인증해주는 공인 인증 기관입니다.

Public Private Keys

X.509 기술의 근간이 되는 암호화 기술입니다. 여기에는 Public 키와 Private 키가 존재합니다. Public 키는 누구나 가질 수 있으며 Private 키는 오직 Public, Private 키페어 소유자만 가지고 있습니다. Public 키를 이용하여 메세지를 암호화하게 되면 해당 Public키에 매핑되는 Private 키로만 복호화가 가능합니다. 반대로 Priate 키를 이용하여 메세지를 암호화할 수 있는데, 이때 누구나 해당 Private 키에 매핑되는 Public 키를 이용하여 암호화된 메세지에 대해서 정말 Private 키의 소유자가 작성한 메세지인지를 검증할 수 있습니다. Private 키로 메세지를 암호화하는 것을 디지털 서명(digital signature)이라고 합니다. X.509에서 디지털 서명을 이용하여 사용자의 신원을 확인합니다.

Certificate

Public 키는 단순히 메세지를 암호화하거나 서명된 메세지를 검증하는 키에 불과합니다. 인증 체계를 구축하기 위해서는 인증서 발급 기관, 인증서 유효기간, 인증 신원, 서명 알고리즘 등과 같이 다양한 정보들이 필요합니다. Certificate은 Public 키를 포함한 다양한 인증 정보를 담고 있는 문서입니다. 다른 사람이 Private키 없이 Certificate이 담고 있는 내용을 변조하는 것은 불가능합니다. Certificate에는 문서 전체 내용을 private키를 이용하여 서명한 해쉬값이 포함되어 있는데 문서 내용 중 한 글자라도 변경이 되면 해쉬값이 달라져서 변조된 문서임을 알아차릴 수 있기 때문입니다.

Certificate Authority

인증서에는 해당 인증서를 발급해준 발급 기관 (Issuer) 정보를 포함하고 있습니다. 해당 정보를 이용하여 지금 보고 있는 인증서가 아무에게서나 발급된 인증서가 아니라 공인된 인증 기관에서 발급된 인증서라는 것을 확인할 수 있습니다. 이것을 Certificate Authority (인증 기관)이라 부르고 짧게 CA라고도 합니다. 이 CA 또한 인증서로 이루어져 있어 해당 Public 키로 인증기관의 유효성을 동일한 메커니즘으로 검사할 수 있습니다. CA도 마찬가지로 CA의 인증서를 발급한 인증 기관(CA의 CA)이 존재하며 이러한 연결고리를 Certificate Chain (인증 체인)이라고 부릅니다. 인증 체인의 가장 끝에는 Root CA라고 하는 인증기관이 있습니다. 이 Root CA는 따로 인증 기관이 존재하지 않으며 스스로가 스스로를 인증합니다. 우리가 웹 브러우저를 통해 HTTPS 사이트를 접속할 때 인증기관의 인증서를 따로 전송하지 않아도 정상적으로 연결이 되는 이유는 웹 브러우저 내부적으로 Root CA들을 가지고 있기 때문입니다. 이 Root CA를 이용하여 다른 CA를 검증하고 해당 CA가 최종 Certificate을 인증합니다.


Certificate을 이용한 사용자 인증

지금까지 X.509 Certificate에 대해 알아 봤습니다. 이 기술을 이용하여 어떻게 사용자 인증을 할 수 있는지 살펴 보겠습니다.

1. 인증서 발급

먼저 테스트에 사용할 Root CA를 만듭니다. 직접 생성한 Root CA를 통하여 서버 인증에 사용할 server-side PKI 키페어와 사용자 인증에 사용할 client-side PKI 키페어를 생성합니다.

  • Root CA Certificate: 클라이언트와 서버 인증서를 발급하는 주체, 클라이언트와 서버는 서로의 인증서가 Root CA가 발급한 공인된 인증서임을 확인할 수 있습니다.
    • rootCA.pem: root CA의 인증서
    • rootCA-key.pem: root CA의 키 (private key)
  • Server Certificate: 서버의 신원을 확인하는 인증서와 서버 자체 key
    • server.pem: 서버의 인증서
    • server-key.pem: 서버의 키
  • Client Certificate: 클라이언트의 신원을 확인하는 인증서와 클라이언트 자체 key
    • client.pem: 클라이언트의 인증서
    • client-key.pem: 클라이언트의 키

인증서 생성툴 설치

사용자 인증서를 직접 생성하지 않고 인증서 생성툴을 이용하여 편하게 생성할 수 있습니다. easyrsa, openssl, cfssl 등과 같이 다양한 툴들이 존재하는데 여기서는 Cloudflare에서 개발한 cfssl을 이용하여 생성하겠습니다.

  • cfssl: json 형태로 csr, cert, key를 생성합니다.
  • cfssljson: cfssl의 output을 가져다 파일로 만들어 줍니다.

인증서는 CSR (Certificate Signing Request - 인증서 서명 요청) 문서를 통하여 실제 인증서가 생성됩니다. 간단하게 인증서를 생성하기 위한 요청 문서라고 생각하시면 됩니다. 더 자세한 내용은 아래 문서들을 참고하시기 바랍니다.

  • https://en.wikipedia.org/wiki/Certificate_signing_request
  • https://www.sslshopper.com/what-is-a-csr-certificate-signing-request.html
wget -q --show-progress --https-only --timestamping \
  https://storage.googleapis.com/kubernetes-the-hard-way/cfssl/linux/cfssl \
  https://storage.googleapis.com/kubernetes-the-hard-way/cfssl/linux/cfssljson

chmod +x cfssl cfssljson
sudo mv cfssl cfssljson /usr/local/bin/

Root CA 생성

mkdir ~/auth
cd ~/auth
cat > rootCA-config.json <<EOF
{
  "signing": {
    "default": {
      "expiry": "8760h"
    },
    "profiles": {
      "root-ca": {
        "usages": ["signing", "key encipherment", "server auth", "client auth"],
        "expiry": "8760h"
      }
    }
  }
}
EOF

cat > rootCA-csr.json <<EOF
{
  "CN": "rootCA",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "O": "Kubernetes"
    }
  ]
}
EOF
# names의 O property는 Organazation을 뜻합니다. 쿠버네티스에서는 사용자의 그룹으로 인식합니다.

cfssl gencert -initca rootCA-csr.json | cfssljson -bare rootCA

ls -l

rootCA-config.json  # 인증서 생성에 필요한 rootCA config 파일
rootCA-csr.json     # rootCA 인증서 서명 요청 json
rootCA.pem          # rootCA 인증서
rootCA-key.pem      # rootCA private key

Server, Client 인증서 생성

서버 인증서를 아래와 같이 생성합니다. 이때 서버의 외부 공인 IP 주소가 있다면 추가하길 바랍니다.

# 서버의 공인 IP 확인
curl ifconfig.co
# 예시) 54.180.125.182

cat > server-csr.json <<EOF
{
  "CN": "localhost",
  "hosts": [
    "localhost",
    "54.180.125.182"
  ],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "O": "server-group"
    }
  ]
}
EOF

cfssl gencert \
  -ca=rootCA.pem \
  -ca-key=rootCA-key.pem \
  -config=rootCA-config.json \
  -profile=root-ca \
  server-csr.json | cfssljson -bare server

ls -l

server-csr.json  # server 인증서 서명 요청 json
server.pem       # server 인증서
server-key.pem   # server private key

사용자 인증서를 아래와 같이 생성합니다.

cat > client-csr.json <<EOF
{
  "CN": "localhost",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "O": "client-group"
    }
  ]
}
EOF

cfssl gencert \
  -ca=rootCA.pem \
  -ca-key=rootCA-key.pem \
  -config=rootCA-config.json \
  -profile=root-ca \
  client-csr.json | cfssljson -bare client

ls -l

client-csr.json  # client 인증서 서명 요청 json
client.pem       # client 인증서
client-key.pem   # client private key

최종 파일 리스트는 다음과 같습니다.

  • root CA
    • 인증서: rootCA.pem
    • 개인키: rootCA-key.pem
    • 요청서: rootCA-csr.json
    • 설정파일: rootCA-config.json
  • server
    • 인증서: server.pem
    • 개인키: server-key.pem
    • 요청서: server-csr.json
  • client
    • 인증서: client.pem
    • 개인키: client-key.pem
    • 요청서: client-csr.json

2. TLS가 적용된 HTTPS 웹 서버 구성

인증에 필요한 인증서 발급이 완료되었습니다. 다음으로 서버 인증을 위한 certificate을 서버에 등록해 보도록 하겠습니다. 아래와 같이 server-side 인증서를 nginx 설정파일에 적용합니다.

# $HOME/auth/default.conf
server {
    listen 443 ssl;
    server_name localhost;
    ssl_certificate      /etc/nginx/conf.d/server.pem;        # 서버 인증서
    ssl_certificate_key  /etc/nginx/conf.d/server-key.pem;    # 서버 key

    location / {
        root  /usr/share/nginx/html;
        index index.html index.htm;
    }
}

HTTPS 연결 테스트

이제 도커를 이용하여 nginx 컨테이너를 실행합니다. 로컬 호스트 서버의 8443 포트가 컨테이너의 443 포트와 연결이 되도록 실행합니다.

docker run -p 8443:443 -v $(pwd):/etc/nginx/conf.d nginx
# http 호출
curl http://localhost:8443/
# 400 The plain HTTP request was sent to HTTPS port
# http 프로토콜로 서비스를 open한 것이 없기 때문에 에러가 발생합니다.

# https 호출, w/o CA 인증서
curl https://localhost:8443/
# curl: (60) SSL certificate problem: unable to get local issuer certificate
# https로 접근하지만 인증서가 없기 때문에 에러가 발생합니다.
# 유명한 CA를 이용하여 인증서를 발급한 경우 기본적으로 브라우저나 리눅스 폴더에 내장되어 있기 때문에 문제 없이 연결이 되지만
# 예시에서는 자체 발급한(self-signed) CA를 사용했기 때문에 명시적으로 CA를 전달해야 합니다.

# https 호출, skip tls verification
curl -k https://localhost:8443/
# Welcome to nginx!
# https로 접근하지만 서버 인증을 건너 뛰기 때문에 서버의 신원 확인 없이 연결이 됩니다.
# 웹 브라우저의 '안전하지 않는 페이지로 이동하기' 기능과 유사하다고 보면 됩니다.

# https 호출, CA 인증서
curl --cacert rootCA.pem https://localhost:8443/
# Welcome to nginx!
# 직접 생성한 rootCA 인증서를 통하여 서버의 신원을 확인합니다.
# 클라이언트가 가진 rootCA와 서버 인증서를 발급한 인증 기관이 동일하거나 인증 체인에 속해 있으므로 서버의 신원을 확증할 수 있습니다.

3. Client Certificate 추가

지금까지 클라이언트가 서버의 신원을 확인할 수 있도록 구성하였습니다. 일반적인 https 서버를 구성한 것과 동일하였습니다. 이번에는 서버에서 클라이언트의 신원을 확인할 수 있도록 default.conf 파일을 약간 수정해 보겠습니다.

# $HOME/auth/default.conf
server {
    listen 443 ssl;
    server_name localhost;
    ssl_certificate      /etc/nginx/conf.d/server.pem;
    ssl_certificate_key  /etc/nginx/conf.d/server-key.pem;

    ssl_verify_client on;                                  # 클라이언트 인증 ON
    ssl_client_certificate /etc/nginx/conf.d/rootCA.pem;   # 클라이언트의 인증서를 확인할 수 있는 인증 기관의 인증서 설정


    location / {
        root  /usr/share/nginx/html;
        index index.html index.htm;
    }
}
curl --cacert rootCA.pem https://localhost:8443/
# 400 No required SSL certificate was sent
# 클라이언트에서도 본인을 인증할 수 있는 인증서와 key를 서버로 전송해야 합니다.

# 클라이언트의 인증서 및 key 전송
curl --cacert rootCA.pem --cert client.pem --key client-key.pem  https://localhost:8443/
# Welcome to nginx!
# 클라이언트 인증서 및 key를 이용하여 서버는 클라이언트의 신원 및 소유자임을 확인

클라이언트 인증의 경우 서버 인증과는 다르게 클라이언트 인증서 뿐만 아니라 key까지 서버에 전송해야 합니다. 그 이유는 무엇일까요?

  • 서버를 인증하는 경우 클라이언트 입장에서 명시적으로 서버의 주소를 입력하기 때문에 서버가 전달하는 서버 인증서의 소유자를 확인할 필요가 없습니다.
  • 반대로 서버 입장에서 클라이언트가 전달하는 클라이언트 인증서를 검증할 때, 클라이언트 인증서의 실제 소유자인지를 확인해야 합니다. 인증서는 공개되어 있기 때문에 다른 클라이언트 인증서를 가져다가 서버로 전송할 수 있기 때문입니다.

쉽게 생각해서 클라이언트 인증서를 계정 ID, 클라이언트 key를 비밀번호라고 이해하면 편합니다. 서버에서 사용자의 비밀번호를 체크하지 않고 사용자 계정만 확인하는 경우, 누구나 다른 사용자의 계정 정보를 이용하여 접근할 수 있기 때문에 서버는 꼭 인증서의 소유권을 확인해야 합니다.

4. 쿠버네티스에서 제공하는 인증서로 변경

지금까지는 cfssl 툴을 이용하여 직접 생성한 인증서 및 키를 활용하였습니다. 이번에는 kubeadm을 통해서 생서된 PKI 키들을 이용하여 nginx 서버의 키들을 대체해 보겠습니다.

# root CA PKI
sudo cp /etc/kubernetes/pki/ca.crt ~/auth/k8s-rootCA.pem

# server PKI
sudo cp /etc/kubernetes/pki/apiserver.crt ~/auth/k8s-server.pem
sudo cp /etc/kubernetes/pki/apiserver.key ~/auth/k8s-server-key.pem

# client PKI
sudo cat /etc/kubernetes/admin.conf | grep client-certificate-data | awk '{print $2}' | base64 -d > ~/auth/k8s-client.pem
sudo cat /etc/kubernetes/admin.conf | grep client-key-data | awk '{print $2}' | base64 -d > ~/auth/k8s-client-key.pem

ls -l

# root CA
k8s-rootCA.pem
# server
k8s-server.pem
k8s-server-key.pem
# client
k8s-client.pem
k8s-client-key.pem

nginx 설정의 인증서, key 파일 앞에 각각 k8s- prefix를 붙여 줍니다.

# $HOME/auth/default.conf
server {
    listen 443 ssl;
    server_name localhost;
    ssl_certificate      /etc/nginx/conf.d/k8s-server.pem;
    ssl_certificate_key  /etc/nginx/conf.d/k8s-server-key.pem;

    ssl_verify_client on;
    ssl_client_certificate /etc/nginx/conf.d/k8s-rootCA.pem;


    location / {
        root  /usr/share/nginx/html;
        index index.html index.htm;
    }
}

한가지 주의해야 할 점은 server의 인증서가 kubernetes 전용으로 만들어졌기 때문에 localhost로 인증을 할 수 없습니다. 이를 우회하고자 로컬 도메인네임 매핑 파일을 아래와 같이 수정합니다.

sudo vi /etc/hosts

# 127.0.0.1 localhost
# 127.0.0.1 kubernetes  # kubernetes --> 127.0.0.1로 매핑
# 호출시 `localhost`가 아닌 `kubernetes` 요청
curl --cacert k8s-rootCA.pem --cert k8s-client.pem --key k8s-client-key.pem  https://kubernetes:8443/

위와 같은 방법으로도 nginx 서버에 정상적으로 연결이 되는 것을 확인할 수 있습니다. 이를 미루어 볼 때, 다음과 같이 해석할 수 있습니다.

root CA

쿠버네티스에서 사용하는 root CA의 PKI 키들은 다음과 같습니다.

  • CA 인증서: /etc/kubernetes/pki/ca.crt

server 인증서

api 서버의 인증서 및 키는 다음과 같습니다.

  • 인증서: /etc/kubernetes/pki/apiserver.crt
  • 개인키: /etc/kubernetes/pki/apiserver.key

client 인증서

클라이언트의 인증서 및 키는 다음과 같습니다.

  • 인증서: /etc/kubernetes/admin.conf 파일내 client-certificate-data 부분
  • 개인키: /etc/kubernetes/admin.conf 파일내 client-key-data 부분

5. 쿠버네티스 CA를 이용하여 신규 인증서 발급

그럼 이제 마지막으로 쿠버네티스에서 사용하는 root CA를 이용하여 쿠버네티스 신규 사용자 인증서를 발급해 봅시다.

1) 신규 사용자 인증서 생성

cfssl 툴을 이용하여 쿠버네티스에서 제공하는 rootCA의 인증서, key를 이용하여 k8s-new-client라는 PKI를 생성합니다. (certs, key)

cat > k8s-new-client-csr.json <<EOF
{
  "CN": "k8s-new-client",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "O": "system:masters"
    }
  ]
}
EOF

# names.O property에 system:masters 설정

cfssl gencert \
  -ca=k8s-rootCA.pem \              # 쿠버네티스 CA 인증서 사용
  -ca-key=k8s-rootCA-key.pem \      # 쿠버네티스 CA key 사용
  -config=rootCA-config.json \
  -profile=root-ca \
  k8s-new-client-csr.json | cfssljson -bare k8s-new-client

ls -l

k8s-new-client-csr.json  # 신규 사용자 인증서 csr json
k8s-new-client.pem       # 신규 사용자 인증서
k8s-new-client-key.pem   # 신규 사용자 private key

k8s-new-client-csr.json 파일 생성시, Organazation (O)을 system:masters 라고 지정하였습니다. 쿠버네티스에서는 사용자를 user와 group으로 인식하는데 이때 Organazation (O) 영역을 group으로 인식합니다. 쿠버네티스에서 system:masters 그룹은 쿠버네티스 마스터 그룹을 뜻하는 예약어입니다. 이를 통해 k8s-new-client라는 유저가 마스터와 동일하게 모든 권한을 소유하게 됩니다. 자세한 사항은 다음 웹 페이지를 참고하시기 바랍니다.

https://kubernetes.io/docs/setup/best-practices/certificates/#configure-certificates-manually

2) 신규 사용자 kubeconfig 설정

새롭게 만든 인증서를 이용하여 kubeconfig 파일 설정을 합니다.

# 기존 admin 사용자 kubeconfig 파일 복사
sudo cp /etc/kubernetes/admin.conf $HOME/kubeconfig
sudo chown $(id -u):$(id -g) $HOME/kubeconfig

# kubectl 신규 사용자 설정 - X.509
kubectl config --kubeconfig=$HOME/kubeconfig set-credentials x509 --client-certificate=k8s-new-client.pem --client-key=k8s-new-client-key.pem
kubectl config --kubeconfig=$HOME/kubeconfig set-context kubernetes-admin@kubernetes --user=x509
kubectl config --kubeconfig $HOME/kubeconfig view

3) 신규 사용자로 쿠버네티스 api 서버에 클라이언트 인증

# api 서버 주소 확인
kubectl cluster-info
# Kubernetes master is running at https://XXXX:XXX

# API 서버 주소 및 포트 설정
API_SERVER_ADDR=XXXX  # 예시) localhost
API_SERVER_PORT=XXX   # 예시) 6443

# curl - 신규 X.509 사용자 인증
curl --cacert k8s-rootCA.pem --cert k8s-new-client.pem --key k8s-new-client-key.pem https://$API_SERVER_ADDR:$API_SERVER_PORT/api

# kubectl - 신규 X.509 사용자 인증
kubectl --kubeconfig=$HOME/kubeconfig get pod -n kube-system
# 혹은 직접 파라미터를 이용하여 호출할 수도 있습니다.
kubectl get pod -n kube-system --client-certificate=k8s-new-client.pem --client-key=k8s-new-client-key.pem

마치며

이번 포스트에서 PKI 기술에 대한 전반적인 내용에 대해서 다뤄보았고 X.509 Certificate을 이용하여 서버, 클라이언트간 인증 방법을 살펴 보았습니다. API 서버도 일반적인 웹 서버와 마찬가지로 TLS mutual authenticate을 통하여 서로의 신원을 확인하는 방법에 대해서 확인해 보았습니다. 다음 포스트에서는 HTTP Authentication을 이용하여 쿠버네티스 인증을 처리하는 방법에 대해서 살펴보도록 하겠습니다.