Kubernetes-身份认证

1Kubernetes中的用户

所有的系统都存在访问和使用其的用户,Kubernetes也一样,在Kubernetes集群中有存在两类用户:

  • service accounts:由Kubernetes进行管理的特殊用户
  • 普通用户:普通用户是由外部应用进行管理的用户。

对于普通用户,Kubernetes管理员只负责为其分配私钥。普通用户可能来自于Keystone或google中,或者甚至是存储在文件中的用户名和密码列表。在Kubernetes中,没有表达普通用户的对象,因此,也就不能通过API将普通用户添加到集群中。

而Service Account是由Kubernetes API管理的用户,它们被绑定到特定的命名空间中,并由API服务器自动创建或通过API调用手动创建。Service Account与存储在Secrets的一组证书相关联,这些凭据被挂载到pod中,以允许集群中进程与Kubernetes API进行通信。

API请求要么来自于普通用户或Service Account,或来自于匿名请求。这就意味着集群内外部的所有进程(从来自于用户使用kubectl输入的请求,或来自于Nodes中kubelet的请求,或来自控制板的成员的请求)都需要进行认证才能与API server进行交互。。

2、认证策略(Authentication strategies

Kubernetes的用户可以使用客户端证书、Bearer Token、身份验证代理或HTTP基本认证,通过身份验证插件来验证API请求。比如,当HTTP请求到达API server,插件尝试将以下的属性与请求进行关联:

  • Username:用户名,标识最终用户的字符串。通常,Username的值可能像“kube-admin”或者“jane@example.com”。
  • UID:用户的唯一标识标识。
  • Groups:用户组组名。
  • Extra fields:记录用户其他信息的属性。

上述所有值对于认证系统都是不透明的,只有在被授权者解释时才有意义。

可以同时启用上面的多个认证方法。通常至少使用两种:

  • service accounts使用serive account tokens。
  • 用户认证至少使用另外一种方法。

当同时启用多个认证模块时,根据短路径评估,使用第一个认证模块成功地认证了请求。API server不保证接下来的认证是通过的。

system:authenticated 组被包括在所有已认证用户的组列表中。

可以通过使用authenticating.proxy或者authenticaiton webhook与其他的认证协议进行集成(LDAP, SAML, Kerberos, alternate x509 schemes, etc)

2.1 X509客户端证书

客户端证书身份认证模式通过在API Server中设置–client-ca-file = SOMEFILE选项来启用。所引用的文件中必须包含一个或多个证书管理机构,用以验证提交给API服务器的客户端证书。如果客户端提交的证书通过验证,主体的通用名称将被用作请求的用户名。从Kubernetes的1.4版本起,客户端证书也可以通过证书的组织(organization)区域指定用户的组成员资格。客户端证书认证叫作TLS双向认证,也就是服务器客户端互相验证证书的正确性,在都正确的情况下协调通信加密方案。CA_CERTIFICATE_FILE肯定包括一个或者多个认证中心,可以被用来验证呈现给api-server的客户端证书。客户端证书的/CN将作为用户名。

例如,使用openssl命令行工具生成证书签名请求:

$ openssl req -new -key jbeda.pem -out jbeda-csr.pem -subj ”/CN=jbeda/O=app1/O=app2”

这将会创建一个用户名为“jbeda”,所属组为”app1”和“app2”的签名请求。 使用客户端证书身份验证时,可以通过easyrsa、OpenSSL或cfssl手动生成证书,x509证书一般会用到三类文件,key(私用密钥),csr(证书请求文件,用于申请证书),crt(CA认证后的证书文件)。

数字证书则是由证书认证机构(CA)对证书申请者真实身份验证之后,用CA的根证书对申请人的一些基本信息以及申请人的公钥进行签名(相当于加盖发证书机构的公章)后形成的一个数字文件。数字证书包含证书中所标识的实体的公钥(就是说你的证书里有你的公钥),由于证书将公钥与特定的个人匹配,并且该证书的真实性由颁发机构保证(就是说可以让大家相信你的证书是真的),因此,数字证书为如何找到用户的公钥并知道它是否有效这一问题提供了解决方案。

此处以Openssl为例进行阐述,通过openssl手动创建。首先,生成一个CA根证书;然后,用CA根证书来签发用户证书。  用户的证书过程是:先生成一个私钥;然后,用私钥生成证书请求(证书请求里应含有公钥信息);最后,利用CA根证书来签发用户证书。通过Openssl生成证书的过程如下:

2.1.1 生成CA根证书

1)用2048位生成一个ca私钥:

$ openssl genrsa -out ca.key 2048

2)根据ca私钥生成证书ca根证书(用天设置证书的有效时间):

$ openssl req -x509 -new -nodes -key ca.key -subj "/CN=${MASTER_IP}" -days 10000 -out ca.crt

2.1.2 生成服务器端用户证书

1)使用2048位生成服务器端用户私钥:

$ openssl genrsa -out server.key 2048

2)为证书签名请求(CSR)创建一个配置文件(例如:csr.conf)。在将真实的值保持到文件之前,一定要用要尖括号把这些值标起来(例如< master_ip >)。注意,MASTER_CLUSTER_IP的值是服务集群IP服务器如前所描述的服务群集IP。

[ req ]default_bits = 2048 prompt = no default_md = sha256 req_extensions = req_ext distinguished_name = dn
[ dn ]C = <country> ST = <state> L = <city> O = <organization> OU = <organization unit> CN = <MASTER_IP>
[ req_ext ]subjectAltName = @alt_names
[ alt_names ]DNS.1 = kubernetes DNS.2 = kubernetes.default DNS.3 = kubernetes.default.svc 
DNS.4 = kubernetes.default.svc.cluster DNS.5 = kubernetes.default.svc.cluster.local IP.1 = <MASTER_IP> 
IP.2 = <MASTER_CLUSTER_IP> 
[ v3_ext ]authorityKeyIdentifier=keyid,issuer:alwaysbasicConstraints=CA:FALSEkeyUsage=keyEncipherment,
dataEnciphermentextendedKeyUsage=serverAuth,clientAuthsubjectAltName=@alt_names

3)基于配置文件和私钥生成证书签名请求:

$ openssl req -new -key server.key -out server.csr -config csr.conf

4)使用ca根证书,ca私钥和服务器端用户证书请求创建服务器用户证书:

$ openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \-CAcreateserial -out server.crt -days 10000 \-extensions v3_ext -extfile csr.conf

5)查看服务器端用户证书:

$ openssl x509 -noout -text -in ./server.crt

最后,将相同的参数添加到API Server的启动参数。

6)生成pem格式证书:

有时可能需要用到pem格式的证书,可以用以下方式合并证书文件(crt)和私钥文件(key)来生成

$cat server.crt server.key > server.pem

2.2 Service Account令牌

在有些情况下,希望在 Pod 内部访问 API server,获取集群的信息,以及对集群进行改动。针对这种情况,kubernetes 提供了一种特殊的认证方式:Service Account令牌。 Service Account 是限定命名空间的。在创建命名空间的时候,kubernetes 会为每一个命名空间创建一个默认的 Service Account;这个默认的 Service Account 只能访问该命名空间内的资源。Service Account 和 Pod、Service、Deployment 一样是 Kubernetes 集群中的一种资源,用户也可以通过手动的方式创建Service Account。

Service Account是一个自动启用的认证器,它使用被签名的Bearer Token对请求进行认证,该插件接受两个可选参数:

  • – -service-account-key-file: 一个包含用于对Bearer Token进行签名的PEM编码密钥文件。如果不指定,将使用API服务器的TLS私钥。
  • – -service-account-lookup :如果启用,从API中删除的tokens将会被废除。

Service Account通常由API服务器自动创建,并通过ServiceAccount Admission Controller与集群中的Pods进行关联。 Bearer tokens被挂载到pod中众所周知的位置,从使集群中的进程可以与API服务器进行通信。Service Account可以使用PodSpec的serviceAccountName字段来关联到Pod中。

注意:通常省略serviceAccountName字段,因为一般是自动完成的。

apiVersion: apps/v1 # this apiVersion is relevant as of Kubernetes 1.9
kind: Deployment
metadata:
  name: nginx-deployment
  namespace: default
spec:
  replicas: 3
  template:
    metadata:
    # ...
    spec:
      serviceAccountName: bob-the-bot
      containers:
      - name: nginx
        image: nginx:1.7.9

Service Account的bearer tokens也在集群外部使用,可以用于创建希望与API进行通信的身份。通过使用kubectl create serviceaccount(NAME)命令,可以创建Service Account,并会创建一个关联的密钥。

$ kubectl create serviceaccount jenkins

serviceaccount "jenkins" created
$ kubectl get serviceaccounts jenkins -o yaml

创建的证书保存了API server公共CA和签名的JSON Web Token(JWT)。

$ kubectl get secret jenkins-token-mk89c -o yaml
apiVersion: v1
data:
  # ca证书
ca.crt: (APISERVER'S CA BASE64 ENCODED)
  # 命名空间
namespace: ZGVmYXVsdA==
# 令牌
  token: (BEARER TOKEN BASE64 ENCODED)
kind: Secret
metadata:
  # ...
type: kubernetes.io/service-account-token

注意:保密字典的值是基于base64编码的,因为通常秘钥都是使用base64进行编码的。

签名的JWT可以作为bearer token来验证给定的service account。通常,这些秘钥被挂载到Pods中,用于集群中访问API服务器,但也可以在群集外部使用。

Service Account使用用户名进行验证 system:serviceaccount:(NAMESPACE):(SERVICEACCOUNT),并分配给组 system:serviceaccounts 和 system:serviceaccounts:(NAMESPACE)

警告:由于service account 令牌存储在秘钥中,任何具有对这些秘钥的读取访问权限的用户都可以作为service account 进行身份验证。

ServiceAccount 主要包含了三个内容:命名空间、令牌 CA。命名空间指定了 Pod 所在的 命名空间;CA是用于验证 api server 的证书;令牌用作身份验证。它们都通过挂接的方式保存在 pod 的文件系统中,其中令牌保存的路径是: /var/run/secrets/kubernetes.io/serviceaccount/token ,这是 apiserver 使用base64 编码通过私钥签的令牌; CA 保存的路径是 /var/run/secrets/kubernetes.io/serviceaccount/ca.crt ,命名空间保存的路径是 /var/run/secrets/kubernetes.io/serviceaccount/namespace ,也是使用 base64 编码。

如果令牌能够通过认证,那么请求的用户名将被设置为 system:serviceaccount:(NAMESPACE):(SERVICEACCOUNT) ,而请求的组名有两个: system:serviceaccounts 和 system:serviceaccounts:(NAMESPACE)

2.3 静态Tokent文件

API server通过-token-auth-file=SOMEFILE选择读取不记名的Token。当前,token是无期限持续的,除非重启API server。Token文件是一个至少包含3列的csv文件: token, user name, user uid,后跟可选的组名。注意,如果您有多个组,则列必须是双引号,例如:

token,user,uid,"group1,group2,group3"

用token唯一标识请求者,只要apiserver存在该token,则认为认证通过,但是如果需要新增Token,则需要重启kube-apiserver组件,实际效果不是很好。

当通过客户端使用 bearer token 认证时,API服务器需要一个值为带有Bearer THETOKEN值的Authorization头。bearer token必须是一个字符序列,能够放在HTTP请求头中。例如:如果bearer token是31ada4fd-adec-460c-809a-9e56ceb75269,它将会在HTTP头中按下面的方式呈现:

Authorization: Bearer 31ada4fd-adec-460c-809a-9e56ceb75269

2.4 Bootstrap令牌

此特性目前还是alpha阶段。为了能够在新的集群中使用bootstrapping认证,Kubernetes需要包括一个动态管理的Bearer token(成为Bootstrap Token),这种token以Secrets的方式存储在kube-system命名空间中,在这个命名空间token可以被动态的管理和创建。Controller Manager有一个TokenCleaner控制器,通过此控制器删除过期了的bootstrap token。

token证书格式为:[a-z0-9]{6}.[a-z0-9]{16},Token的第一部分是一个Token ID,第二部分是token的秘钥。你需要在http协议头中加上类似的信息:

Authorization: Bearer 781292.db7bc3a58fc5f07e

如果要使用Bootstrap,需要在API Sever中设置–experimental-bootstrap-token-auth。同时,必须在Controller Manager中设置–controllers=*,tokencleaner来启用TokenCleaner控制器。

在使用kubeadm部署Kubernetes时,kubeadm会自动创建默认token,可通过kubeadm token list命令查询。

2.5 静态密码文件

基础认证模式通过在API Server中设置–basic-auth-file=SOMEFILE来启用。一旦API server服务启动,加载的用户名和密码信息就不会发生改变,任何对源文件的修改必须重启 apiserver 才能生效。

静态密码文件是 CSV 格式的文件,每行对应一个用户的信息,前面3列为:密码、用户名、用户 ID,这些是是必须的;第四列是可选的组名(如果有多个组,必须用双引号):

password,user,uid,"group1,group2,group3"

当Http客户端使用基础认证时,API Server需要一个带有Basic BASE64ENCODED(USER:PASSWORD) 值的 Authorization 头。API Server 解析出客户端提供的用户名和密码,如果和文件中的某一行匹配,就认为认证成功。

注意: 这种方式很不灵活,也不安全,可以说名存实亡,不推荐使用。

2.6 Keystone密码

Kubernetes也可以使用Openstack的Keystone组件进行身份认证和授权,这个方法对于已经使用 openstack 来搭建 IaaS 平台的公司比较适用,直接使用 keystone 可以保证 Iaas 和 Caas 平台保持一致的用户体系。

需要API Server在启动时指定–experimental-keystone-url=<AuthURL>,而https时还需要设置–experimental-keystone-ca-file=SOMEFILE

3、匿名请求

如果用户请求没有Kubernetes任何方式的身份认证,在正常情况下,Kubernetes会直接返回 “401” 错误信息。但是,在 kubernetes 还提供另外一种方案,即给没有通过认证的请求一个特殊的用户名 :system:anonymous 和组名: system:unauthenticated 。这样的话,就可以跟授权模式结合起来,为匿名请求设置一些特殊的权限,比如,只能读取当前 namespace 的 pod 信息,以方便用户访问。

在Kubernetes的1.5.1-1.5.x版本,在默认情况下匿名请求是不可用的,但能够通过在API Server中设置–anonymous-auth=true来启用。

在Kubernetes的1.6+版本,如果使用AlwaysAllow以外的收取模式,则匿名请求默认开启,但可用通过设置–anonymous-auth=false禁止匿名请求。从1.6版本开始,ABAC和RBAC授权需要显示对system:anonymous用户和system:unauthenticated的组进行授权,通过* user或*group这种方式进行访问授权将不包含匿名的用户。

 

参考材料

  1. 《Authenticating》地址:https://kubernetes.io/docs/admin/authentication/
  2. 《OpenSSL》地址:https://wiki.archlinux.org/index.php/OpenSSL

作者简介:
季向远,北京神舟航天软件技术有限公司产品经理。本文版权归原作者所有。

微信公众号:ik8s

K8S中文社区微信公众号