1. 程式人生 > >Kubernetes 的證書認證_Kubernetes中文社群

Kubernetes 的證書認證_Kubernetes中文社群

今天讓我們聊聊 Kubernetes 的公私鑰和證書認證

本文內容會提及如何根據需要對 CA、公私鑰進行組織並對叢集進行設定。

Kubernetes 的元件中有很多不同的地方可以放置證書之類的東西。在進行叢集安裝的時候,我感覺有一百多億個不同的命令引數是用來設定證書、金鑰的,真不明白是怎麼弄到一起工作的。

當然了,沒有一百億那麼多的引數,不過的確很多的。比如 API Server 的引數吧,有大概 16 個引數是跟這些東西有關的(下面是節選):

--cert-dir string                           The directory where the TLS certs are located. If --tls-cert-file and --tls-private-key-file are provided, this flag will be ignored. (default "/var/run/kubernetes")
--client-ca-file string                     If set, any request presenting a client certificate signed by one of the authorities in the client-ca-file is authenticated with an identity corresponding to the CommonName of the client certificate.
--etcd-certfile string                      SSL certification file used to secure etcd communication.
--etcd-keyfile string                       SSL key file used to secure etcd communication.
--kubelet-certificate-authority string      Path to a cert file for the certificate authority.
--kubelet-client-certificate string         Path to a client cert file for TLS.
--kubelet-client-key string                 Path to a client key file for TLS.
--proxy-client-cert-file string             Client certificate used to prove the identity of the aggregator or kube-apiserver when it must call out during a request. This includes proxying requests to a user api-server and calling out to webhook admission plugins. It is expected that this cert includes a signature from the CA in the --requestheader-client-ca-file flag. That CA is published in the 'extension-apiserver-authentication' configmap in the kube-system namespace. Components recieving calls from kube-aggregator should use that CA to perform their half of the mutual TLS verification.
--proxy-client-key-file string              Private key for the client certificate used to prove the identity of the aggregator or kube-apiserver when it must call out during a request. This includes proxying requests to a user api-server and calling out to webhook admission plugins.
--requestheader-allowed-names stringSlice   List of client certificate common names to allow to provide usernames in headers specified by --requestheader-username-headers. If empty, any client certificate validated by the authorities in --requestheader-client-ca-file is allowed.
--requestheader-client-ca-file string       Root certificate bundle to use to verify client certificates on incoming requests before trusting usernames in headers specified by --requestheader-username-headers
--service-account-key-file stringArray      File containing PEM-encoded x509 RSA or ECDSA private or public keys, used to verify ServiceAccount tokens. If unspecified, --tls-private-key-file is used. The specified file can contain multiple keys, and the flag can be specified multiple times with different files.
--ssh-keyfile string                        If non-empty, use secure SSH proxy to the nodes, using this user keyfile
--tls-ca-file string                        If set, this certificate authority will used for secure access from Admission Controllers. This must be a valid PEM-encoded CA bundle. Alternatively, the certificate authority can be appended to the certificate provided by --tls-cert-file.
--tls-cert-file string                      File containing the default x509 Certificate for HTTPS. (CA cert, if any, concatenated after server cert). If HTTPS serving is enabled, and --tls-cert-file and --tls-private-key-file are not provided, a self-signed certificate and key are generated for the public address and saved to /var/run/kubernetes.
--tls-private-key-file string               File containing the default x509 private key matching --tls-cert-file.
--tls-sni-cert-key namedCertKey             A pair of x509 certificate and private key file paths, optionally suffixed with a list of domain patterns which are fully qualified domain names, possibly with prefixed wildcard segments. If no domain patterns are provided, the names of the certificate are extracted. Non-wildcard matches trump over wildcard matches, explicit domain patterns trump over extracted names. For multiple key/certificate pairs, use the --tls-sni-cert-key multiple times. Examples: "example.crt,example.key" or "foo.crt,foo.key:*.foo.com,foo.com". (default [])

接下來是 Controller Manager 的:

--cluster-signing-cert-file string          Filename containing a PEM-encoded X509 CA certificate used to issue cluster-scoped certificates (default "/etc/kubernetes/ca/ca.pem")
--cluster-signing-key-file string           Filename containing a PEM-encoded RSA or ECDSA private key used to sign cluster-scoped certificates (default "/etc/kubernetes/ca/ca.key")
--root-ca-file string                       If set, this root certificate authority will be included in service account's token secret. This must be a valid PEM-encoded CA bundle.
--service-account-private-key-file string   Filename containing a PEM-encoded private RSA or ECDSA key used to sign service account tokens.

再來個 Kubelet:

--client-ca-file string                   If set, any request presenting a client certificate signed by one of the authorities in the client-ca-file is authenticated with an identity corresponding to the CommonName of the client certificate.
--tls-cert-file string                    File containing x509 Certificate used for serving HTTPS (with intermediate certs, if any, concatenated after server cert). If --tls-cert-file and --tls-private-key-file are not provided, a self-signed certificate and key are generated for the public address and saved to the directory passed to --cert-dir.
--tls-private-key-file string             File containing x509 private key matching --tls-cert-file.

本文假設讀者:

  • 對 TLS 認證和 CA 有一些瞭解。
  • 能把這些東西跑起來,但是不知道為啥。

下面還會說明在 Kubernetes 中的不同 CA,以及不同 CA 的協同工作。

另外有些我在工作中學到的一些:

  • 不要用 CA 來檢查 Service Account Key。Service Account key 有點古怪,他跟其他的 Key 不是一同處理的。
  • 如果 kubernetes 建立使用者和組的方式不適合需求,可以(應該)設定一個認證代理。
  • API Server 如果設定了太多 CA,會顯得有點亂。

這個題目有點複雜,如果閱讀中發現任何問題請通知我。

PKI 和 Kubernetes

在閱讀 Kubernetes 材料的過程中,我注意到一個詞出現了很多次:“PKI”,我不很清楚這是個什麼。

如果你有個在執行的 Kubernetes 叢集,其中可能有幾百上千個公鑰私鑰(客戶端認證、服務認證等等)。

如果這幾百個 Key 是獨立的互不相關的,就會讓安全性墮入泥潭。因此我們需要個 CA,CA 的職責就是簽發證書,並告訴使用者“這個公鑰是我發的,靠譜”。

PKI 就是組織 Key 的方式 —— 什麼 Key 是用什麼 CA 簽發的。

例如:

  • 可以為每個叢集準備一個 CA,叢集所有的私鑰都從這個 CA 簽發(Kubernetes 文件中多數是這個情況)。
  • 可以準備一個全域性 CA,所有的私鑰都從此而來。
  • 單獨給服務使用一個 CA,對外可見;內部另外使用一個 CA 作為專門用途。
  • 還有其他。

我不是安全專家,所以不想說如何管理私鑰和 CA 才更好。但是不管你用的什麼樣的模型,其實都可以跟 Kubernetes 協調工作。

下面根據需求來確認管理 PKI 的方式,以及如何在 Kubernetes 中實現。

Kubernetes 叢集需要一個單根結構的 CA 麼?不

如果你讀了不少 Kubernetes 叢集的安裝文件,會注意到總有一步:設定一個 CA。Kelsey Hightower 的大作 “Kubernetes the hard way” 中是第二步,“在叢集中信任 TLS ”中說:

每個 Kubernetes 叢集都有一個叢集根 CA。CA 一般用於叢集中的元件驗證 API Server 的合法性,在 API Serverl 來說,就是驗證 kubelet 客戶端的證書,諸如此類的。

基本上來說:

  1. 設定一個 CA
  2. 用這個 CA 生成不同的證書,給 Kubernetes 叢集的不同元件使用。

如果不想為每個叢集設定一個新的 CA 呢?可能有很多理由。但是我擔心,最終還是需要提供一個根 CA。

這好像說上面的話是假的,其實可以使用很多不同的 CA 簽發的證書來管理 Kubernetes。總的說來,還是要結合具體場景的需求。

接下來我們來探討一下證書相關的引數,以及互相之間的關係。每一節都會包含一個可以定義的 CA。每一個都是獨立的,並不需要一致。不過在實際操作中,可能你並不想管理 6 個不同的 CA。

API Server 的 TLS 證書(以及 CA)

–tls-cert-file string

包含預設的 x509 https 認證的檔案(可以包含 CA 證書),如果啟用了 HTTPS 服務,又沒有指定–tls-cert-file和–tls-private-key-file引數,就會在 /var/run/kubernetes生成一個自簽名證書以及 Key

–tls-private-key-file

–tls-cert-file證書的 x509 私鑰。

如果用 TLS 連線 API Server,就需要這兩個引數來選擇 API Server 使用的證書。

證書設定了之後,還需要給各個元件的 kubeconfig 檔案進行相關設定。

current-context: my-context
apiVersion: v1
clusters:
- cluster:
    certificate-authority: /path/to/my/ca.crt # 簽發 API Server 證書的 CA 證書
    server: https://horse.org:4443
  name: my-cluster
kind: Config
users:
- name: green-user
  user:
    client-certificate: path/to/my/client/cert # 後面會講
    client-key: path/to/my/client/key # 後面會講

有個讓我驚奇的事情就是——這個宇宙裡面的幾乎所有其他使用 TLS 的系統都會去 /etc/ssl 查詢一個本機信任的 CA 列表,但是 Kubernetes 很傲嬌,他不會去找,必須顯式的進行告知簽發 CA。

可以使用引數–kubeconfig /path/to/kubeconfig.yaml將配置檔案分配給各個元件進行使用。

這樣我們完成了第一個 CA 的設定:簽發 API Server 證書的 CA。這個 CA 跟其他的 CA 可以不一致。

API Server 客戶證書認證

–client-ca-file

如果設定了這一引數,所有的請求都應該使用該檔案中所包含的 CA 簽發的證書進行簽署,證書中的 Common Name 會作為使用者名稱進行使用。

Kubernetes 元件獲得 API Server 認證的方法之一就是使用這一引數。

所有客戶端證書都應該由這一 CA 簽發(不需要和 API Server 的 CA 一致)。

當使用 kubeconfig 檔案的時候,可以按照如下方式設定使用證書:

kind: Config
users:
- name: green-user
  user:
    client-certificate: path/to/my/client/cert
    client-key: path/to/my/client/key

Kubernetes 做了很多使用者證書方面的假設(使用者名稱就是 Common Name,Group 就是 Organization)。如果這些假設不符合需求,那麼就應該停用客戶端證書認證,改用認證代理。

請求 Header 的證書認證(或者:認證代理)

API server 引數

–requestheader-allowed-names stringSlice

–requestheader-username-headers 中指定的 Header 中包含使用者名稱,這一引數的列表確定了允許有效的 Common Name,如果這一引數的列表為空,則所有通過–requestheader-client-ca-file校驗的都允許通過。

–requestheader-client-ca-file string

針對收到的請求,在信任–requestheader-username-headers中指定的 Header 裡面包含的使用者名稱之前,首先會用這一 CA 對客戶證書進行驗證。

另外一個設定 Kubernetes 認證的方式就是認證代理。如果你對如何向 API Server 傳送使用者名稱和組有很多想法,可以設定一個代理,這一代理會使用 HTTP Header 將使用者名稱和組傳送給 API Server。

文件中簡單的解釋了一下工作方式。代理使用一個客戶端證書表明身份,–requestheader-client-ca-file告知 API Server,該證書所屬的 CA。

我覺得——API Server 有太多認證方式了(客戶端認證、認證代理、Token 等等),讓人很迷惑。建議使用者儘量少的同時使用認證方式,便於管理、使用和除錯。

service account 私鑰(不是 CA 簽發的)

API Server 引數

–service-account-key-file

PEM 編碼的 X509 RSA 或者 ECDSA 的私鑰或者公鑰,用於檢驗 ServiceAccount 的 token。如果沒指定的話,會使用–tls-private-key-file替代。檔案中可以包含多個 Key,這一引數可以重複指定多個檔案。

Controller Manager 引數

–service-account-private-key-file

PEM 編碼的 X509 RSA 或者 ECDSA Key,用於簽署 Service Account Token。

Controller Manager 使用私鑰簽署 Service Account Token。跟 Kubernetes 中使用的其他私鑰不同的是,這個私鑰是不支援同一 CA 驗證的,因此上,需要給每個 Controller Manager 指定一致的私鑰檔案。

這個 Key 也不需要什麼 CA 來做簽署,生成很容易:

openssl genrsa -out private.key 4096

然後分發給每個 Controller Manager 和 API Server 就可以了。

使用和–tls-private-key-file一致的檔案是可以工作的——只要你給每個 API Server 用的都是同一個 TLS Key(一般都這麼做的吧?)。(這裡我假設你執行的一個有高可用支援的,多個 API Server 和多個 Controller Manager同時執行的叢集)

如果兩個不同的 Controller Manager 用了兩個不同的 Key,那就杯具了,他們會用各自的 Key 來生成 Token,最終導致無效判定。我覺得這點不太合理,Kubernetes 應該和其他方面一樣,使用 CA 進行管理。通過對原始碼的月度,我覺得原因可能是 jwt-go 不支援 CA。

Kubelet 證書認證

總算到了 Kubelet 環節了,下面是 API Server 和 Kubelet 相關的內容:

API Server 引數

–kubelet-certificate-authority CA 證書的路徑。

–kubelet-client-certificate TLS 證書檔案

–kubelet-client-key TLS Key 檔案

Kubelet 引數

–client-ca-file

請求中的客戶端證書如果是由檔案中的 CA 簽署的,那麼他的 Common Name 就會被用作 ID 進行認證。

–tls-cert-file

用來提供 HTTPS 服務的 x509 證書(其中也可包含中間人證書)。如果不提供–tls-cert-file和–tls-private-key-file,就會為主機地址生成一個自簽名的證書和對應的 Key,並儲存到–cert-dir目錄裡。

–tls-private-key-file

–tls-cert-file 對應的 Key

校驗 kubelet 的請求是有用的,因為 Kubelet 的職責就是在主機上執行程式碼。

這裡實際上有兩個 CA,這裡不準備深入描述,情況和 API Server 是一樣的,Kubelet 用 TLS 來進行認證,也支援客戶證書認證。

另外還要告知 API Server,用什麼 CA 檢查 Kubelet 的 TLS,另外用什麼證書來跟 Kubelet 通訊。

再說一次,這兩個 CA 是可以不同的。

太多 CA 了

現在我們找到了五個不同的 CA,他們各自獨立的為 Kubernetes 提供支援。

其實還有一些沒討論到的證書,不過希望本文能給你閱讀官方文件提供一點幫助。

當然了,每個 CA 獨立設定可能不是必要的,我是希望幫助讀者理解這些東西如何設定使之符合各種需求,而不是簡單的面向文件照本宣科。