在Kubernetes Pod中使用Service Account訪問API Server

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

Kubernetes API Server是整個Kubernetes叢集的核心,我們不僅有從叢集外部存取API Server的需求,有時,我們還需要從Pod的內部訪問API Server。

然而,在生產環境中,Kubernetes API Server都是“設防”的。在《Kubernetes叢集的安全配置》一文中,我提到過:Kubernetes通過client cert、static token、basic auth等方法對用戶端請求進行身分識別驗證。對於運行於Pod中的Process而言,有些時候這些方法是適合的,但有些時候,像client cert、static token或basic auth這些資訊是不便於暴露給Pod中的Process的。並且通過這些方法通過API Server驗證後的請求是具有全部授權的,可以任意操作Kubernetes cluster,這顯然是不能滿足安全要求的。為此,Kubernetes更推薦大家使用service account這種方案的。本文就帶大家詳細說說如何通過service account從一個Pod中訪問API Server的。

零、實驗環境

本文的實驗環境是Kubernetes 1.3.7 cluster,雙節點,master承載負荷。cluster通過kube-up.sh搭建的,具體的搭建方法見《一篇文章帶你瞭解Kubernetes安裝》。

一、什麼是service account?

什麼是service account? 顧名思義,相對於user account(比如:kubectl訪問APIServer時用的就是user account),service account就是Pod中的Process用於訪問Kubernetes API的account,它為Pod中的Process提供了一種身份標識。相比於user account的全域性許可權,service account更適合一些輕量級的task,更聚焦於授權給某些特定Pod中的Process所使用。

service account作為一種resource存在於Kubernetes cluster中,我們可以通過kubectl擷取當前cluster中的service acount列表:

# kubectl get serviceaccount --all-namespacesNAMESPACE                    NAME           SECRETS   AGEdefault                      default        1         140dkube-system                  default        1         140d

我們查看一下kube-system namespace下名為”default”的service account的詳細資料:

# kubectl describe serviceaccount/default -n kube-systemName:        defaultNamespace:    kube-systemLabels:        <none>Image pull secrets:    <none>Mountable secrets:     default-token-hpni0Tokens:                default-token-hpni0

我們看到service account並不複雜,只是關聯了一個secret資源作為token,該token也叫service-account-token,該token才是真正在API Server驗證(authentication)環節起作用的:

# kubectl get secret  -n kube-systemNAME                  TYPE                                  DATA      AGEdefault-token-hpni0   kubernetes.io/service-account-token   3         140d# kubectl get secret default-token-hpni0 -o yaml -n kube-systemapiVersion: v1data:  ca.crt: {base64 encoding of ca.crt data}  namespace: a3ViZS1zeXN0ZW0=  token: {base64 encoding of bearer token}kind: Secretmetadata:  annotations:    kubernetes.io/service-account.name: default    kubernetes.io/service-account.uid: 90ded7ff-9120-11e6-a0a6-00163e1625a9  creationTimestamp: 2016-10-13T08:39:33Z  name: default-token-hpni0  namespace: kube-system  resourceVersion: "2864"  selfLink: /api/v1/namespaces/kube-system/secrets/default-token-hpni0  uid: 90e71909-9120-11e6-a0a6-00163e1625a9type: kubernetes.io/service-account-token

我們看到這個類型為service-account-token的secret資源套件含的資料有三部分:ca.crt、namespace和token。

  • ca.crt
    這個是API Server的CA密鑰憑證,用於Pod中的Process對API Server的服務端數位憑證進行校正時使用的;

  • namespace
    這個就是Secret所在namespace的值的base64編碼:# echo -n “kube-system”|base64 => “a3ViZS1zeXN0ZW0=”

  • token

這是一段用API Server私密金鑰簽發(sign)的bearer tokens的base64編碼,在API Server authenticating環節,它將派上用場。

二、API Server的service account authentication(身分識別驗證)

前面說過,service account為Pod中的Process提供了一種身份標識,在Kubernetes的身份校正(authenticating)環節,以某個service account提供身份的Pod的使用者名稱為:

system:serviceaccount:(NAMESPACE):(SERVICEACCOUNT)

以上面那個kube-system namespace下的“default” service account為例,使用它的Pod的username全稱為:

system:serviceaccount:kube-system:default

有了username,那麼credentials呢?就是上面提到的service-account-token中的token。在《Kubernetes叢集的安全配置》一文中我們談到過,API Server的authenticating環節支援多種身份校正方式:client cert、bearer token、static password auth等,這些方式中有一種方式通過authenticating(Kubernetes API Server會逐個方式嘗試),那麼身份校正就會通過。一旦API Server發現client發起的request使用的是service account token的方式,API Server就會自動採用signed bearer token方式進行身份校正。而request就會使用攜帶的service account token參與驗證。該token是API Server在建立service account時用API server啟動參數:–service-account-key-file的值簽署(sign)產生的。如果–service-account-key-file未傳入任何值,那麼將預設使用–tls-private-key-file的值,即API Server的私密金鑰(server.key)。

通過authenticating後,API Server將根據Pod username所在的group:system:serviceaccounts和system:serviceaccounts:(NAMESPACE)的許可權對其進行authority 和admission control兩個環節的處理。在這兩個環節中,cluster管理員可以對service account的許可權進行細化設定。

三、預設的service account

Kubernetes會為每個cluster中的namespace自動建立一個預設的service account資源,並命名為”default”:

# kubectl get serviceaccount --all-namespacesNAMESPACE                    NAME           SECRETS   AGEdefault                      default        1         140dkube-system                  default        1         140d

如果Pod中沒有顯式指定spec.serviceAccount欄位值,那麼Kubernetes會將該namespace下的”default” service account自動mount到在這個namespace中建立的Pod裡。我們以namespace “default”為例,我們查看一下其中的一個Pod的資訊:

# kubectl describe pod/index-api-2822468404-4oofrName:        index-api-2822468404-4oofrNamespace:    default... ...Containers:  index-api:   ... ...    Volume Mounts:      /var/run/secrets/kubernetes.io/serviceaccount from default-token-40z0x (ro)    Environment Variables:    <none>... ...Volumes:... ...  default-token-40z0x:    Type:    Secret (a volume populated by a Secret)    SecretName:    default-token-40z0xQoS Class:    BestEffortTolerations:    <none>No events.

可以看到,kubernetes將default namespace中的service account “default”的service account token掛載(mount)到了Pod中容器的/var/run/secrets/kubernetes.io/serviceaccount路徑下。

深入容器內部,查看mount的serviceaccount路徑下的結構:

# docker exec 3d11ee06e0f8 ls  /var/run/secrets/kubernetes.io/serviceaccountca.crtnamespacetoken

這三個檔案與上面提到的service account的token中的資料是一一對應的。

四、default service account doesn’t work

上面提到過,每個Pod都會被自動掛載一個其所在namespace的default service account,該service account用於該Pod中的Process訪問API Server時使用。Pod中的Process該怎麼用這個service account呢?Kubernetes官方提供了一個client-go項目可以為你示範如何使用service account訪問API Server。這裡我們就基於client-go項目中的examples/in-cluster/main.go來測試一下是否能成功訪問API Server。

先下載client-go源碼:

# go get k8s.io/client-go# ls -FCHANGELOG.md  dynamic/   Godeps/     INSTALL.md   LICENSE   OWNERS  plugin/    rest/     third_party/  transport/  vendor/discovery/    examples/  informers/  kubernetes/  listers/  pkg/    README.md  testing/  tools/        util/

我們改造一下examples/in-cluster/main.go,考慮到panic會導致不便於觀察Pod日誌,我們將panic改為輸出到“標準輸出”,並且不return,讓Pod周期性的輸出相關日誌,即便fail:

// k8s.io/client-go/examples/in-cluster/main.go... ...func main() {    // creates the in-cluster config    config, err := rest.InClusterConfig()    if err != nil {        fmt.Println(err)    }    // creates the clientset    clientset, err := kubernetes.NewForConfig(config)    if err != nil {        fmt.Println(err)    }    for {        pods, err := clientset.CoreV1().Pods("").List(metav1.ListOptions{})        if err != nil {            fmt.Println(err)        } else {            fmt.Printf("There are %d pods in the cluster\n", len(pods.Items))        }        time.Sleep(10 * time.Second)    }}

基於該main.go的go build預設輸出,建立一個簡單的Dockerfile:

From ubuntu:14.04MAINTAINER Tony Bai <bigwhite.cn@gmail.com>COPY main /root/mainRUN chmod +x /root/mainWORKDIR /rootENTRYPOINT ["/root/main"]

構建一個測試用docker image:

# docker build -t k8s/example1:latest .... ...# docker images|grep k8sk8s/example1                                                  latest              ceb3efdb2f91        14 hours ago        264.4 MB

建立一份deployment manifest:

//main.yamlapiVersion: extensions/v1beta1kind: Deploymentmetadata:  name: k8s-example1spec:  replicas: 1  template:    metadata:      labels:        run: k8s-example1    spec:      containers:      - name: k8s-example1        image: k8s/example1:latest        imagePullPolicy: IfNotPresent

我們來建立該deployment(kubectl create -f main.yaml -n kube-system),觀察Pod中的main程式能否成功訪問到API Server:

# kubectl logs k8s-example1-1569038391-jfxhxthe server has asked for the client to provide credentials (get pods)the server has asked for the client to provide credentials (get pods)API Server log(/var/log/upstart/kube-apiserver.log):E0302 15:45:40.944496   12902 handlers.go:54] Unable to authenticate the request due to an error: crypto/rsa: verification errorE0302 15:45:50.946598   12902 handlers.go:54] Unable to authenticate the request due to an error: crypto/rsa: verification errorE0302 15:46:00.948398   12902 handlers.go:54] Unable to authenticate the request due to an error: crypto/rsa: verification error

出錯了!kube-system namespace下的”default” service account似乎不好用啊!(注意:這是在kubernetes 1.3.7環境)。

五、建立一個新的自用的service account

在kubernetes github issues中,有好多issue是關於”default” service account不好用的問題,給出的解決方案似乎都是建立一個新的service account。

service account的建立非常簡單,我們建立一個serviceaccount.yaml:

//serviceaccount.yamlapiVersion: v1kind: ServiceAccountmetadata:  name: k8s-example1

建立該service account:

# kubectl create -f serviceaccount.yamlserviceaccount "k8s-example1" created# kubectl get serviceaccountNAME           SECRETS   AGEdefault        1         139dk8s-example1   1         12s

修改main.yaml,讓Pod顯示使用這個新的service account:

//main.yamlapiVersion: extensions/v1beta1kind: Deploymentmetadata:  name: k8s-example1spec:  replicas: 1  template:    metadata:      labels:        run: k8s-example1    spec:      serviceAccount: k8s-example1      containers:      - name: k8s-example1        image: k8s/example1:latest        imagePullPolicy: IfNotPresent

好了,我們重新建立該deployment,查看Pod日誌:

# kubectl logs k8s-example1-456041623-rqj87There are 14 pods in the clusterThere are 14 pods in the cluster... ...

我們看到main程式使用新的service account成功通過了API Server的身分識別驗證環節,並獲得了cluster的相關資訊。

六、尾聲

在我的另外一個使用kubeadm安裝的k8s 1.5.1環境中,我重複做了上面這個簡單測試,不同的是這次我直接使用了default service account。在k8s 1.5.1下,pod的執行結果是ok的,也就是說通過default serviceaccount,我們的client-go in-cluster example程式可以順利通過API Server的身分識別驗證,擷取到相關的Pods元資訊。

七、參考資料

  • Kubernetes authentication
  • Service Accounts
  • Accessing the cluster
  • Service Accounts Admin

微博:@tonybai_cn
公眾號:iamtonybai
github.com: https://github.com/bigwhite

2017, bigwhite. 著作權.

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.