Kubernetes Authenticating

kubeadm을 통해 클러스터를 구축하였다면 사용자를 인증할 수 있는 방법을 알아보자. (현재 시점 kubernetes version 1.24)

지난 번에는 Webhook인증에 대해서 알아보았다. Authenticating

Webhook 인증에 대한 내용을 기반으로 k8s-keystone-auth를 이용해서 Openstack의 Keystone과 연동할 것이며 client-keystone-auth 프로그램을 이용해서 client인증을 진행할 것이다.
자세한 내용은 공식문서를 참고한다. k8s-keystone-auth

Kubernetes webhook authentication and authorization for OpenStack Keystone. With k8s-keystone-auth, the Kubernetes cluster administrator only need to know the OpenStack project names or roles, it’s up to the OpenStack project admin for user management, as a result, the OpenStack users could have access to the Kubernetes cluster.

1. k8s-keystone-auth webhook server 구성

authorization policy(Optional)
우리는 인가에 대해서 kubernetes native rbac을 사용할 수도 있고, k8s-keystone-auth의 인가를 사용할 수도 있다. kubernetes native rbac을 사용할 경우에는 해당 cm을 배포하지 않아도 된다. 여기서는 하지 않는다. 이 cm은 추후 kube-apiserver 설정 시 옵션과 같이 적용되는 것으로 보인다.

--authorization-webhook-config-file=/etc/kubernetes/webhooks/webhookconfig.yaml
--authorization-mode=Node,RBAC,Webhook


service certificates
k8s-keystone-auth는 보안상의 이유로 HTTPS 서비스를 하며 인증서가 필요하다. 여기서는 self-signed 인증서를 사용한다.
운영환경에서는 신뢰할 수 있는 발급기관에서 서명한 인증서를 사용하여야 한다.

openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes -subj /CN=k8s-keystone-auth.kube-system/
kubectl --namespace kube-system create secret tls keystone-auth-certs --cert=cert.pem --key=key.pem


service account & rbac

deployment k8s-keystone-auth

service k8s-keystone-auth

2. k8s-keystone-auth webhook server 테스트

openstack token issue
openstack 서버로 가서 token을 발급 받는다.

openstack token issue
+------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Field      | Value                                                                                                                                                                                   |
+------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| expires    | 2022-07-27T06:06:16+0000                                                                                                                                                                |
| id         | gAAAAABi4MfIzdQ4yrA2o7HmOpt73qz5FAh_GQOPN64E0LiDDAyJLjATHOTRguxhS7OlUIVJMm-GzRd37Ay5dL7sxRfJq3UIcgJxA1fJNVzrN22mLnwTlODhAD2IGNNzEVwG-M9C6CtX65v0M5_fNs-bpGJZ5YNq6XaKHBXjpLgwt4yJ4oHmMaw |
| project_id | 7d3ac9731b5d40c78d1966d48b4122a0                                                                                                                                                        |
| user_id    | a6f69f35144f4cce81ca03c70dfc438d                                                                                                                                                        |
+------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+


Authentication

token=gAAAAABi4MfIzdQ4yrA2o7HmOpt73qz5FAh_GQOPN64E0LiDDAyJLjATHOTRguxhS7OlUIVJMm-GzRd37Ay5dL7sxRfJq3UIcgJxA1fJNVzrN22mLnwTlODhAD2IGNNzEVwG-M9C6CtX65v0M5_fNs-bpGJZ5YNq6XaKHBXjpLgwt4yJ4oHmMaw

kubectl run curl --rm -it --restart=Never --image curlimages/curl -- \
  -k -XPOST https://k8s-keystone-auth-service.kube-system:8443/webhook -d '
{
  "apiVersion": "authentication.k8s.io/v1beta1",
  "kind": "TokenReview",
  "metadata": {
    "creationTimestamp": null
  },
  "spec": {
    "token": "'$token'"
  }
}'

정상 호출이된다면 아래와 같이 출력된다. webhook token인증시 살펴봤던 status데이터에 authenticated값이 true로 넘어오면서 token을 발급받은 username: “admin”이 넘어왔다. 또한 그 외에도 프로젝트 정보 도메인 정보 등이 모두 같이 넘어왔다.

{
  "apiVersion": "authentication.k8s.io/v1beta1",
  "kind": "TokenReview",
  "metadata": {
    "creationTimestamp": null
  },
  "spec": {
    "token": "gAAAAABi4Mr2bXOtIfsi3uFYzbQZt8t52yafK8dZJcVqClFpfirTDPIDCqv7aPKngDadW-Tp-PrtOwpNIQy-I-milBhQQpUIzllRNCsFEOzN1_Sj7DHa-uXDixYGC3FyK2tkpFT2IuPR3d7G2xY2cO69CA-NG3pg0OKdz0Jsgx043KrgK5GtaJU"
  },
  "status": {
    "authenticated": true,
    "user": {
      "username": "admin",
      "uid": "a6f69f35144f4cce81ca03c70dfc438d",
      "groups": [
        "7d3ac9731b5d40c78d1966d48b4122a0"
      ],
      "extra": {
        "alpha.kubernetes.io/identity/project/id": [
          "7d3ac9731b5d40c78d1966d48b4122a0"
        ],
        "alpha.kubernetes.io/identity/project/name": [
          "admin"
        ],
        "alpha.kubernetes.io/identity/roles": [
          "reader",
          "admin",
          "member"
        ],
        "alpha.kubernetes.io/identity/user/domain/id": [
          "default"
        ],
        "alpha.kubernetes.io/identity/user/domain/name": [
          "Default"
        ]
      }
    }
  }
}

token에 임의의 값을 넣고 실패한 결과를 받아보면 아래와 같이 status.authenticated가 false로 넘어왔음을 알 수 있다.

{
  "apiVersion": "authentication.k8s.io/v1beta1",
  "kind": "TokenReview",
  "metadata": {
    "creationTimestamp": null
  },
  "spec": {
    "token": "gAAAAABi4M0cKoPwc7VMpfEZdzdNUJt3rr1PISpiazAAVYr7FDhOEIzt2aqQGpamuEW1EOT5H6JAD2z22gH9CJy7SGb81Vag3KV9UssW-ObapTSyndqyYJXSoK2uGDyViHXtaaysurM8Mf9Mtu_Nh04-YmadeK9VLlPpc7TPaBva2pOVMshlN"
  },
  "status": {
    "authenticated": false,
    "user": {
      "username": "",
      "uid": "",
      "groups": null,
      "extra": null
    }
  }
}


3. Kubernetes kube-apiserver 설정

Webhook config

config kube-apiserver

4. Client(kubectl) Configuration

Webhook token인증 시 client-keystone-auth를 사용하여 exec mode로 token을 받아와 처리한다. clientkubectl-configuration

The recommended way of client authentication is to use exec mode with the client-keystone-auth binary.

install client-keystone-auth
최신 버전을 받기 위해서는 아래의 스크립트를 사용하라고 되어 있지만 실제로 해당 스크립트대로 실행하면 client-keystone-auth를 받을 때 404오류가 나온다.

repo=kubernetes/cloud-provider-openstack
version=$(curl --silent "https://api.github.com/repos/${repo}/releases/latest" | grep '"tag_name":' | awk -F '"' '{print $4}')
curl -L https://github.com/kubernetes/cloud-provider-openstack/releases//download/${version}/client-keystone-auth -o ~/client-keystone-auth
sudo chmod u+x ~/client-keystone-auth

때문에 직접 검색을 통해 client-keystone-auth의 가장 최신 파일을 검색해보면 현재기준으로는 아래의 파일이다.

wget https://github.com/kubernetes/cloud-provider-openstack/releases/download/v1.18.0/client-keystone-auth
sudo chmod u+x ~/client-keystone-auth
mv client-keystone-auth /usr/local/bin/


config파일 수정
user정보에 아래와 같이 정보를 추가한다.
env에는 openstack keystone에서 인증받는데 필요한 정보를 입력한다.

apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUMvakNDQWVhZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRJeU1EY3lOekEwTWpnMU5Wb1hEVE15TURjeU5EQTBNamcxTlZvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTTIzCnVkNnRmL1VRenY2ZStiTGdjUDJDUU9IMmlma25mL1VvNUtUKzBJQTUyYjZyUFdnblZGUHFOQkZ2V2t3SHIwMDkKcnN2MXFmekZyclhoUE1lMGxWUTVYczE0aVBKU3JEamlrcjUxd2ZTMVhhQ2pudVlLdWNRQ3dYTWx1M3hGVExQbwpyV3J0OGJKeFlPblpKRXlYYW5qQkNCZCtLSncwbjRkMjY2U2lNS3B1dXRTMEk5M296VkE1Z3JOR0NmT1ZOVVpsCjRvNm02UkZ0TXFhVFJpNWRFOVNSYWczbmViZTZUVFhlYnNFY255UE5OTTg5RllrMTJYNzNiUUo2VEE4d2Vrc3cKdUMrUFlucmd2MW9jbnlTNkpaYmcxUVI5WTg4WGVsNnpNWVhSc0xwUk1wWFptek84OWxvTTJrZjl2ckI2TGRUTApUcm5xZmk4TVpvUlNaZVhHaEFrQ0F3RUFBYU5aTUZjd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0hRWURWUjBPQkJZRUZEMkdlUGxHcmM5ZXl0Y2dLMVNURm5EZDFTTW5NQlVHQTFVZEVRUU8KTUF5Q0NtdDFZbVZ5Ym1WMFpYTXdEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBSXB3eld5SVNlUjlHV2NEY011RgpFN25MSGp5bmp3TFRMR2lEeUJ2bk5IUXBacGJHOWFJcm1kM0NDNDUycGwvUnFHMldQd1o0T1ZmeWEvc3lZWjdvCnRVbXJhcTFoeXlra1RxdXAyWWhTNFVsZlFQSXBtOTJaNnp4S0dhR25Gb2wrUUhIbVdQUElyVVp6bHZvOXhwSDMKbkwrVGZtU05FYjY3UXJiaTdVeTRkU29TUWxPY2hraUJTMVMyZm5YR0k0QUhsK3B6OTlkdmcycitlWnBNUUVSRAoybUFnNWk2TFNCeEx6VXVtM2R2a0ZERGVPN3FZT3FodW9MNkx1SmRxTE1ZaGdvVlF5dmpZdmttZGprbDR3RDBiCkdVa3JpaUJ2WGFkb1FxQU1mSDN5RTFORDFDd0s3WE9zVWhvL05PV09IajlQQ0VLWkFQbmlUMmRreEd4SmJxa2UKMk1vPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
    server: https://nlb-kube-master-a0dca3b259bf3238.elb.ap-northeast-2.amazonaws.com:6443
  name: kubernetes
- context:
    cluster: kubernetes
    user: openstackuser
  name: openstackuser@kubernetes
- name: openstackuser
  user:
    exec:
      apiVersion: client.authentication.k8s.io/v1beta1
      args: null
      command: client-keystone-auth
      env:
      - name: OS_AUTH_URL
        value: http://10.20.2.79:5000/v3/
      - name: OS_DOMAIN_NAME
        value: default
      - name: OS_PROJECT_NAME
        value: admin
      - name: OS_USERNAME
        value: admin
      - name: OS_PASSWORD
        value: password
      interactiveMode: IfAvailable
      provideClusterInfo: false


이제 kubectl 커맨드를 확인해본다. 현재는 admin 유저에게 아무런 권한이 없어서 클러스터내 리소스를 조회하지 못하고 있다.

kubectl get all -A
##Print
Error from server (Forbidden): pods is forbidden: User "admin" cannot list resource "pods" in API group "" at the cluster scope
Error from server (Forbidden): replicationcontrollers is forbidden: User "admin" cannot list resource "replicationcontrollers" in API group "" at the cluster scope
Error from server (Forbidden): services is forbidden: User "admin" cannot list resource "services" in API group "" at the cluster scope
Error from server (Forbidden): daemonsets.apps is forbidden: User "admin" cannot list resource "daemonsets" in API group "apps" at the cluster scope
Error from server (Forbidden): deployments.apps is forbidden: User "admin" cannot list resource "deployments" in API group "apps" at the cluster scope
Error from server (Forbidden): replicasets.apps is forbidden: User "admin" cannot list resource "replicasets" in API group "apps" at the cluster scope
Error from server (Forbidden): statefulsets.apps is forbidden: User "admin" cannot list resource "statefulsets" in API group "apps" at the cluster scope
Error from server (Forbidden): horizontalpodautoscalers.autoscaling is forbidden: User "admin" cannot list resource "horizontalpodautoscalers" in API group "autoscaling" at the cluster scope
Error from server (Forbidden): cronjobs.batch is forbidden: User "admin" cannot list resource "cronjobs" in API group "batch" at the cluster scope
Error from server (Forbidden): jobs.batch is forbidden: User "admin" cannot list resource "jobs" in API group "batch" at the cluster scope

따라서 admin user에게 kubernetes rbac을 통해 권한을 부여해본다.

5. RBAC 및 계정

admin 유저에게 rbac을 통해 권한을 부여하고 조회에 관한 테스트를 진행해본다.


6. 정리

정리를 하면 k8s-keystone-auth를 통해 kubernetes webhook token authentication을 통해 openstack keystone과 연동을 시켰다.

그리고 client(kubectl)쪽에서는 keystone의 유저정보를 사용하여 exec mode로 토큰을 얻고 유효한 토큰임을 keystone에 인증을 받는 과정을 거쳤다.
이 때 사용자는 모든 권한을 Kubernetes의 RBAC을 통해 부여 받는다. 따라서 적절한 user에게 role을 설정하여 주입한다.

7. TroubleShooting

여기까지 구성하는데 꽤 많은 시간을 소비하였고 헛짓도 많이 하는 바람에 kubeadm reset을 시키고 다시 구성하는 작업만 4~5번을 반복한 것 같다.
먼저 k8s-keystone-auth의 문제가 아니었음에도 어딘지를 몰라서 많이 찾았다.

7.1 calico cni

먼저 cluster 내부에서 정상적으로 dns lookup이 되지 않았다. 또한 pod -> pod 간 ping이 안되는 구간이 있었다.
테스트를 위해 아래 스크립트를 호출했을 때 k8s-keystone-auth-service.kube-system 룩업이 되지 않았다.
이유는 dns 서비스에 정상적으로 접근할 수 없었기 때문이다.

kubectl run curl --rm -it --restart=Never --image curlimages/curl -- \
-k -XPOST https://k8s-keystone-auth-service.kube-system:8443/webhook -d '
{
  "apiVersion": "authentication.k8s.io/v1beta1",
  "kind": "TokenReview",
  "metadata": {
    "creationTimestamp": null
  },
  "spec": {
    "token": "'$TOKEN'"
  }
}'

dns-debugging홈페이지에서 많은 도움을 받았다. 우선 dnsutils 파드를 배포하여 어떤부분에서 문제가 있었는지를 파악했고, 워커노드쪽에서 우선 AllTraffic - kube-master-node, kube-worker-node 보안그룹을 열고 우선 통신에 대한 확인을 했다.
이 후에는 정상적으로 dns lookup이 가능하였다.

7.2 unknown apiVersion “authentication.k8s.io/v1”

마지막 test 과정에서 unknown apiVersion 오류가 발생했었다. 지금 생각해보면 당연히 apiVersion이 잘못된 것이었을 것이다. 그런데 이 때는 위의 네트워크 이슈랑 같이 맞물려서 어떤게 문제인지 제대로 파악하지 못했던 것 같다.

kubeadm_auth_webhook006
Issue:Missing support for client.authentication.k8s.io/v1

네트워크 정상 통신 여부를 모두 확인한 뒤에는 해당 이슈가 나왔을 때 위의 문서들을 찾았다. 결국 공식문서에 나와있던 v1으로는 호출시 오류가 발생하는게 맞고 v1beta1으로 버전을 수정했다.


📚 References

[1] Using client keystone auth

[2] Using keystone webhook authenticator and authorizer

[3] Debugging DNS Resolution