k8s-身份认证与权限

k8s-身份认证与权限

K8s中的用户

k8s中的有两类型的用户:被k8s集群管理的服务账号 ( Service Account Pod 对象)和常规用户 (User Account 现实中的“人”)。

**User Account(用户账号):**一般是指由独立于Kubernetes之外的其他服务管理的用户账号,例如由管理员分发的密钥、Keystone一类的用户存储(账号库)、甚至是包含有用户名和密码列表的文件等。Kubernetes中不存在表示此类用户账号的对象, 因此不能被直接添加进 Kubernetes 系统中 。普通用户若要安全访问集群API Server,往往需要证书、Token或者用户名+密码。

**Service Account(服务账号):**是指由Kubernetes API 管理的账号,用于为Pod 之中的服务进程在访问Kubernetes API时提供身份标识( identity ) 。Service Account通常要绑定于特定的命名空间,它们由 API Server 创建,或者通过 API 调用手动创建 ,附带着一组存储为Secret的用于访问API Server的凭据。

K8s API的访问控制

用户访问API服务的途径通常有三种:kubectl、客户端库或者直接使用 REST接口。 用户和 K8s 服务账户访问K8S集群的资源需要过三关:认证、鉴权、准入控制,如下图所示:

k8s-身份认证与权限_第1张图片

Step1 认证 Authentication

认证模块负责鉴定⽤户⾝份。

一旦建立了安全传输层协议(TLS),请求会进入认证步骤。API server 会执行配置的一个或多个认证模块 。认证模块一般通过检查请求头或者客户端证书来进行认证。认证模块支持客户端证书、用户名+密码、令牌、bootstrap tokens和 JSON Web 令牌(JWT,用于服务账户)。可以指定多个认证模块,认证时会遍历认证模块直到有一个模块认证通过。如果认证未认证通过,会拒绝请求,返回401状态码。认证通过后会指定相应的username,用于后续步骤。

Step2 鉴权 Authorization

授权模块⽤于操作权限许可鉴别。

当请求被认证模块认证通过,认证为一个特定的用户后,请求会继续进行鉴权检查。可以配置多个鉴权模块进行鉴权检查。Kubernetes 支持多种鉴权模式,例如 ABAC 模式、RBAC 模式和 Webhook 模式等。 管理员创建集群时,会在 API Server中配置使用的鉴权模块。 如果配置了多个鉴权模块,则 Kubernetes 会检查每个模块,任意一个模块授权请求,请求即可继续; 如果所有模块拒绝了该请求,请求将会被拒绝(HTTP 状态码 403)。

Step3 准入控制 Admission control

准⼊控制⽤于在资源对象的创建、删除、更新或连接(proxy)操作时实现更精细的许可检查。

准入控制模块是可以修改或拒绝请求的软件模块。 除鉴权模块可用的属性外,准入控制模块还可以访问正在创建或修改的对象的内容。准入控制器对创建、修改、删除或(通过代理)连接对象的请求进行操作。

准入控制器不会对仅读取对象的请求起作用。

有多个准入控制器被配置时,服务器将依次调用它们。

与身份认证和鉴权模块不同,如果任何准入控制器模块拒绝某请求,则该请求将立即被拒绝。

请求通过所有准入控制器后,将使用检验程序检查对应的 API 对象,然后将其写入对象存储(如步骤 4 所示)。

API Server 服务端口

API Server使用HTTP协议在两个端口上提供服务,localhost端口和Secure端口:

  1. localhost端口:用于测试和引导,以及主控节点上的其他组件(调度器,控制器管理器)与 API 通信

    1. 没使用安全传输层协议(TLS)
    2. 默认为端口 8080,使用 --insecure-port 进行更改
    1. 默认 IP 为 localhost,使用 --insecure-bind-address 进行更改
    2. 请求 绕过 身份认证和鉴权模块
    1. 由准入控制模块处理的请求
    2. 受需要访问主机的保护
  2. Secure端口

    1. 使用 TLS。 用 --tls-cert-file 设置证书,用 --tls-private-key-file 设置密钥
    2. 默认端口 6443,使用 --secure-port 更改
    1. 默认 IP 是第一个非本地网络接口,使用 --bind-address 更改
    2. 请求须经身份认证模块、鉴权模块处理
    1. 请求须经准入控制模块处理

apiserver 对外不暴露8080端口,只能内部访问,对外使用6443端口

身份认证策略

Kubernetes 使用身份认证插件利用客户端证书、持有者令牌(Bearer Token)、身份认证代理(Proxy) 或者 HTTP 基本认证机制(通过用户名密码的方式认证)来认证 API 请求的身份。HTTP 请求发给 API Server时, 插件会将以下属性关联到请求:

  • 用户名:用来辩识最终用户的字符串。常见的值可以是 kube-admin 或 [email protected]

  • 用户ID:用来辩识最终用户的字符串,旨在比用户名有更好的一致性和唯一性。

  • 用户组:取值为一组字符串,其中各个字符串用来标明用户是某个命名的用户逻辑集合的成员。 常见的值可能是 system:masters 或者 devops-team 等。

  • 附加字段:一组额外的键-值映射,键是字符串,值是一组字符串;用来保存一些鉴权组件可能觉得有用的额外信息。

当集群中启用了多个身份认证模块时,第一个成功地对请求完成身份认证的模块会直接做出评估决定。API Server并不保证身份认证模块的运行顺序。所有通过验证的用户都会被添加进system:authenticated组。

https证书认证,基于X509 CA证书

基于CA根证书签名的双向数字证书认证方式。X509证书详解参考

要启用 X509证书认证,需要在apiserver的启动参数中添加***–client-ca-file=SOMEFILE***, API Server X509 认证的完整启动参数如下

kube-apiserver
  --advertise-address=192.168.10.50
  --allow-privileged=true
  --authorization-mode=Node,RBAC
  --client-ca-file=/etc/kubernetes/pki/ca.crt
  --enable-admission-plugins=NodeRestriction
  --enable-bootstrap-token-auth=true
  --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt
  --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt
  --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key
  --etcd-servers=https://127.0.0.1:2379
  --insecure-port=0
  --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt
  --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key
  --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
  --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt
  --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key
  --requestheader-allowed-names=front-proxy-client
  --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt
  --requestheader-extra-headers-prefix=X-Remote-Extra-
  --requestheader-group-headers=X-Remote-Group
  --requestheader-username-headers=X-Remote-User
  --secure-port=6443
  --service-account-key-file=/etc/kubernetes/pki/sa.pub
  --service-cluster-ip-range=10.96.0.0/12
  --tls-cert-file=/etc/kubernetes/pki/apiserver.crt
  --tls-private-key-file=/etc/kubernetes/pki/apiserver.key

–client-ca-file 所引用的文件必须包含一个或者多个证书机构,用来验证向 API Server提供的客户端证书。 如果提供了客户端证书并且证书被验证通过,则 subject 中的公共名称(Common Name)就被作为请求的用户名。 自 Kubernetes 1.4 开始,客户端证书还可以通过证书的 organization 字段标明用户的组成员信息。 要包含用户的多个组成员信息,可以在证书种包含多个 organization 字段。

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

# 此命令将使用用户名 jbeda 生成一个证书签名请求(CSR),且该用户属于 "app" 和 "app2" 两个用户组。
openssl req -new -key jbeda.pem -out jbeda-csr.pem -subj "/CN=jbeda/O=app1/O=app2"

静态令牌文件

当 API Server的命令行设置了 –token-auth-file=SOMEFILE 选项时,会从文件中读取持有者令牌(bearer token)。令牌会长期有效,如果修改token file的内容,必须重启api server才能生效。

令牌文件是一个 CSV 文件,包含至少 3 个列:令牌、用户名和用户的UID。 其余列被视为可选的组名。例子:

#令牌,用户名,uid,组名(多个用双引号括起来)
token,user,uid,"group1,group2,group3"

Bearer Token

使用http协议,添加**Authorization请求头。**格式为:Bearer THETOKEN。

API Server会根据请求头token内容进行身份认证。

例如:如果持有者令牌为 31ada4fd-adec-460c-809a-9e56ceb75269,则请求头Authorization如下所示:

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

Bootstrap Tokens

为了支持方便地启动引导新的集群,Kubernetes 包含了一种动态管理的bearer token类型,称作启动引导令牌(Bootstrap Token)。 这些令牌以 Secret 的形式保存在 kube-system namespace中,可以被动态管理和创建。 控制器管理(Controller Manager)器包含的 TokenCleaner 控制器能够在启动引导令牌过期时将其删除。

Bootstrap Token令牌的格式为 [a-z0-9]{6}.[a-z0-9]{16}。第一个部分是令牌的 ID;第二个部分是令牌的 Secret。可以用如下所示的方式在 HTTP 头部设置令牌:

#781292就是用户ID
Authorization: Bearer 781292.db7bc3a58fc5f07e

在 API Server上设置 –enable-bootstrap-token-auth 标志来启用基于启动引导令牌的身份认证组件。

必须通过控制器管理器的 –controllers 标志来启用 TokenCleaner 控制器: --controllers=*,tokencleaner

如果使用 kubeadm 来启动引导新的集群时自动完成这些设置。

身份认证组件的认证结果为 system:bootstrap:<令牌 ID>,该用户属于 system:bootstrappers 用户组。

这里的用户名和组设置都是有意设计成这样,其目的是阻止用户在启动引导集群之后继续使用这些令牌。 这里的用户名和组名可以用来在启动引导新的的集群时构造合适的鉴权策略(kubadm中使用)。

Service Account Tokens

服务账号(Service Account)是一种自动被启用的用户认证机制,使用经过签名的bearer token来验证请求。该插件可接受两个可选参数:

  • –service-account-key-file:包含PEM编码密钥的文件,用于对token进行签名。如果未指定,将使用API服务器的TLS私钥。
  • –service-account-lookup 如果启用,通过API 删除的令牌会被回收。

服务账号通常由 API Server自动创建,并通过ServiceAccount Controller Manager与集群中运行的pod相关联。token被挂载到已知位置的pods中,并允许集群内进程与API Server通信。帐户可以使用PodSpec的serviceAccountName字段显式地与pod关联。

# 创建serviceaccount: jenkins
kubectl create serviceaccount jenkins
serviceaccount "jenkins" created

# 获取serviceaccount: jenkins
kubectl get serviceaccounts jenkins -o yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  # ...
secrets:
- name: jenkins-token-1yvwg



# 创建的secret包含API Server的公共CA和一个签名的JSON Web令牌(JWT)。
kubectl get secret jenkins-token-1yvwg -o yaml

apiVersion: v1
data:
  ca.crt: (APISERVER'S CA BASE64 ENCODED)
  namespace: ZGVmYXVsdA==
  token: (BEARER TOKEN BASE64 ENCODED)
kind: Secret
metadata:
  # ...
type: kubernetes.io/service-account-token

OpenID Connect Tokens

OpenID Connect 是一种 OAuth2 认证方式, 被某些 OAuth2 IDP身份提供者支持,例如Windows Azure Active Directory、Salesforce 和 Google。 协议对 OAuth2 的主要扩展体现在有一个附加字段会和访问令牌一起返回, 这一字段称作 ID Token(ID 令牌)。 这种令牌是一种由服务器签名的 JSON Web 令牌(JWT),其中包含一些可预知的字段, 例如用户的邮箱地址,要识别用户,身份认证组件使用 OAuth2 令牌响应 中的 id_token(而非 access_token)作为bearer token。认证中心把用户信息放在json里,用私钥加密,k8s拿到token后用公钥解密,只要解密成功token就是合法的而且能拿到用户信息,不需要再像认证中心请求。

OAuth2认证时序图:

k8s-身份认证与权限_第2张图片

  1. 用户登录IDP(身份认证提供器)

  2. 认证用户,返回access_token,id_token和刷新令牌

  3. 使用kubectl时带上参数–token id_token。或者将id_token配置到.kube/config中

  4. kubectl将id_token添加到 Authorization请求头中,请求API Server。

  5. API Server进行JWT签名校验

  6. API Server校验JWT是否过期

  7. API Server校验用户是否鉴权通过

  8. API Server鉴权通过,响应结果给kubectl

  9. kubectl返回执行结果给用户

由于用来验证你是谁的所有数据都在 id_token 中,Kubernetes 不需要再去联系IDP身份服务。所有请求都是无状态的,优点是这种身份认证方式更容易处理大规模请求。不过也有一些缺点:

  1. Kubernetes 并未提供 OpenID Connect 的身份服务。可以使用现有的公共的 OpenID Connect 身份服务。

  2. Kubernetes 没有提供用来触发身份认证过程的 “Web 界面”。 因为不存在用来收集用户凭据的浏览器或用户接口,你必须自己先行完成对身份服务的认证过程。

  3. id_token 令牌不可收回。因其属性类似于证书,其生命期一般很短(只有几分钟)。 所以每隔几分钟就要获得一个新的令牌。

  4. 进入Kubernetes控制面板时需要进行身份验证,必须使用kubectl代理命令或使用反向代理注入id_token。

要启用OpenID Connect Tokens需要在API Server进行配置:

参数 描述 示例 必需?
–oidc-issuer-url 允许 API Server发现公开的签名密钥的服务的 令牌发放者 URL。只接受https协议。此值通常设置为服务的issuer url,不含路径。例如:“https://accounts.google.com” 或 “https://login.salesforce.com”。 如果issuer url 是 https://accounts.google.com/.well-known/openid-configuration,则此值应为 https://accounts.google.com
–oidc-client-id 所有令牌都应发放给此客户 ID。 kubernetes
–oidc-username-claim 用作用户名的 JWT 申领(JWT Claim)。默认情况下使用 sub 值,即最终用户的一个唯一的标识符。管理员也可以选择其他申领,例如 email 或者 name,取决于所用的身份服务。不过,除了 email 之外的申领都会被添加令牌发放者的 URL 作为前缀,以免与其他插件产生命名冲突。 sub
–oidc-username-prefix 要添加到用户名申领之前的前缀,用来避免与现有用户名发生冲突(例如:system: 用户)。例如,此标志值为 oidc: 时将创建形如 oidc:jane.doe 的用户名。如果此标志未设置,且 --oidc-username-claim 标志值不是 email,则默认前缀为 <令牌发放者的 URL>#,其中 <令牌发放者 URL > 的值取自 --oidc-issuer-url 标志的设定。此标志值为 - 时,意味着禁止添加用户名前缀。 oidc:
–oidc-groups-claim 用作用户组名的 JWT申领。如果所指定的申领确实存在,则其值必须是一个字符串数组。 groups
–oidc-groups-prefix 添加到组申领的前缀,用来避免与现有用户组名(如:system: 组)发生冲突。例如,此标志值为 oidc: 时,所得到的用户组名形如 oidc:engineering 和 oidc:infra。 oidc:
–oidc-required-claim 取值为一个 key=value 偶对,意为 ID 令牌中必须存在的申领。如果设置了此标志,则 ID 令牌会被检查以确定是否包含取值匹配的申领。此标志可多次重复,以指定多个申领。 claim=value
–oidc-ca-file IDP的web证书签名的CA证书的路径。默认为主机的根ca。 /etc/kubernetes/ssl/kc-ca.pem
kubectl 使用方式一:使用 oidc 身份认证组件

该组件将 id_token 设置所有请求的bearer token,并且在令牌过期时自动刷新。在你登录到你的IDP之后, 可以使用 kubectl 来添加你的 id_token、refresh_token、client_id 和 client_secret,以配置该插件。

kubectl config set-credentials mmosley  \
        --auth-provider=oidc  \
        --auth-provider-arg=idp-issuer-url=https://oidcidp.tremolo.lan:8443/auth/idp/OidcIdP  \
        --auth-provider-arg=client-id=kubernetes  \
        --auth-provider-arg=client-secret=1db158f6-177d-4d9c-8a8b-d36869918ec5  \
        --auth-provider-arg=refresh-token=q1bKLFOyUiosTfawzA93TzZIDzH2TNa2SMm0zEiPKTUwME6BkEo6Sql5yUWVBSWpKUGphaWpxSVAfekBOZbBhaEW+VlFUeVRGcluyVF5JT4+haZmPsluFoFu5XkpXk5BXqHega4GAXlF+ma+vmYpFcHe5eZR+slBFpZKtQA= \
        --auth-provider-arg=idp-certificate-authority=/root/ca.pem \
        --auth-provider-arg=id-token=eyJraWQiOiJDTj1vaWRjaWRwLnRyZW1vbG8ubGFuLCBPVT1EZW1vLCBPPVRybWVvbG8gU2VjdXJpdHksIEw9QXJsaW5ndG9uLCBTVD1WaXJnaW5pYSwgQz1VUy1DTj1rdWJlLWNhLTEyMDIxNDc5MjEwMzYwNzMyMTUyIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL29pZGNpZHAudHJlbW9sby5sYW46ODQ0My9hdXRoL2lkcC9PaWRjSWRQIiwiYXVkIjoia3ViZXJuZXRlcyIsImV4cCI6MTQ4MzU0OTUxMSwianRpIjoiMm96US15TXdFcHV4WDlHZUhQdy1hZyIsImlhdCI6MTQ4MzU0OTQ1MSwibmJmIjoxNDgzNTQ5MzMxLCJzdWIiOiI0YWViMzdiYS1iNjQ1LTQ4ZmQtYWIzMC0xYTAxZWU0MWUyMTgifQ.w6p4J_6qQ1HzTG9nrEOrubxIMb9K5hzcMPxc9IxPx2K4xO9l-oFiUw93daH3m5pluP6K7eOE6txBuRVfEcpJSwlelsOsW8gb8VJcnzMS9EnZpeA0tW_p-mnkFc3VcfyXuhe5R3G7aa5d8uHv70yJ9Y3-UhjiN9EhpMdfPAoEB9fYKKkJRzF7utTTIPGrSaSU6d2pcpfYKaxIwePzEkT4DfcQthoZdy9ucNvvLoi1DIC-UocFD8HLs8LYKEqSxQvOcvnThbObJ9af71EwmuE21fO5KzMW20KtAeget1gnldOosPtz1G5EwvaQ401-RPQzPGMVBld0_zMCAwZttJ4knw

如果服务在其刷新令牌响应中不包含 id_token,则此插件无法支持该服务。 这时你应该考虑下面的选项二。

该操作会生成如下配置:

users:
- name: mmosley
  user:
    auth-provider:
      config:
        client-id: kubernetes
        client-secret: 1db158f6-177d-4d9c-8a8b-d36869918ec5
        id-token: eyJraWQiOiJDTj1vaWRjaWRwLnRyZW1vbG8ubGFuLCBPVT1EZW1vLCBPPVRybWVvbG8gU2VjdXJpdHksIEw9QXJsaW5ndG9uLCBTVD1WaXJnaW5pYSwgQz1VUy1DTj1rdWJlLWNhLTEyMDIxNDc5MjEwMzYwNzMyMTUyIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL29pZGNpZHAudHJlbW9sby5sYW46ODQ0My9hdXRoL2lkcC9PaWRjSWRQIiwiYXVkIjoia3ViZXJuZXRlcyIsImV4cCI6MTQ4MzU0OTUxMSwianRpIjoiMm96US15TXdFcHV4WDlHZUhQdy1hZyIsImlhdCI6MTQ4MzU0OTQ1MSwibmJmIjoxNDgzNTQ5MzMxLCJzdWIiOiI0YWViMzdiYS1iNjQ1LTQ4ZmQtYWIzMC0xYTAxZWU0MWUyMTgifQ.w6p4J_6qQ1HzTG9nrEOrubxIMb9K5hzcMPxc9IxPx2K4xO9l-oFiUw93daH3m5pluP6K7eOE6txBuRVfEcpJSwlelsOsW8gb8VJcnzMS9EnZpeA0tW_p-mnkFc3VcfyXuhe5R3G7aa5d8uHv70yJ9Y3-UhjiN9EhpMdfPAoEB9fYKKkJRzF7utTTIPGrSaSU6d2pcpfYKaxIwePzEkT4DfcQthoZdy9ucNvvLoi1DIC-UocFD8HLs8LYKEqSxQvOcvnThbObJ9af71EwmuE21fO5KzMW20KtAeget1gnldOosPtz1G5EwvaQ401-RPQzPGMVBld0_zMCAwZttJ4knw
        idp-certificate-authority: /root/ca.pem
        idp-issuer-url: https://oidcidp.tremolo.lan:8443/auth/idp/OidcIdP
        refresh-token: q1bKLFOyUiosTfawzA93TzZIDzH2TNa2SMm0zEiPKTUwME6BkEo6Sql5yUWVBSWpKUGphaWpxSVAfekBOZbBhaEW+VlFUeVRGcluyVF5JT4+haZmPsluFoFu5XkpXk5BXq
      name: oidc

当你的 id_token 过期时,kubectl 会尝试使用你的 refresh_token 来刷新你的 id_token,并且在 client_secret 中存放 refresh_token 的新值,同时把 id_token 的新值写入到 .kube/config 文件中。

kubectl 使用方式二:使用 --token 选项

kubectl 命令允许你使用 --token 选项传递一个令牌。 你可以将 id_token 的内容复制粘贴过来,作为此标志的取值:

kubectl --token=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL21sYi50cmVtb2xvLmxhbjo4MDQzL2F1dGgvaWRwL29pZGMiLCJhdWQiOiJrdWJlcm5ldGVzIiwiZXhwIjoxNDc0NTk2NjY5LCJqdGkiOiI2RDUzNXoxUEpFNjJOR3QxaWVyYm9RIiwiaWF0IjoxNDc0NTk2MzY5LCJuYmYiOjE0NzQ1OTYyNDksInN1YiI6Im13aW5kdSIsInVzZXJfcm9sZSI6WyJ1c2VycyIsIm5ldy1uYW1lc3BhY2Utdmlld2VyIl0sImVtYWlsIjoibXdpbmR1QG5vbW9yZWplZGkuY29tIn0.f2As579n9VNoaKzoF-dOQGmXkFKf1FMyNV0-va_B63jn-_n9LGSCca_6IVMP8pO-Zb4KvRqGyTP0r3HkHxYy5c81AnIh8ijarruczl-TK_yF5akjSTHFZD-0gRzlevBDiH8Q79NAr-ky0P4iIXS8lY9Vnjch5MF74Zx0c3alKJHJUnnpjIACByfF2SCaYzbWFMUNat-K1PaUk5-ujMBG7yYnr95xD-63n8CO8teGUAAEMx6zRjzfhnhbzX-ajwZLGwGUBT4WqjMs70-6a7_8gZmLZb2az1cZynkFRj2BaCkVT3A2RrjeEwZEtGXlMqKJ1_I2ulrOVsYx01_yD35-rw get nodes

Webhook Token Authentication

Webhook 身份认证是一种用来验证bearer token的回调机制。

  • –authentication-token-webhook-config-file: 指向一个配置文件,其中描述如何访问远程的Webhook服务。

  • –authentication-token-webhook-cache-ttl: 用来设定身份认证的缓存时间。 默认时长为 2 分钟。

  • –authentication-token-webhook-version:指定TokenReview对象的使用版本,authentication.k8s.io/v1beta1 或者 authentication.k8s.io/v1,默认为v1beta1。TokenReview对象用于从webhook中发送或接收信息。

配置文件使用 kubeconfig 文件的格式。文件中,clusters 指代远程服务,users 指代远程 API 服务 Webhook。

# Kubernetes API 版本apiVersion: v1# API 对象类别kind: Config# clusters 指代远程服务clusters:  - name: name-of-remote-authn-service    cluster:      certificate-authority: /path/to/ca.pem         # 用来验证远程服务的 CA      server: https://authn.example.com/authenticate # 要查询的远程服务 URL。必须使用 'https'。# users 指代 API Server的 Webhook 配置users:  - name: name-of-api-server    user:      client-certificate: /path/to/cert.pem # Webhook 插件要使用的证书      client-key: /path/to/key.pem          # 与证书匹配的密钥# kubeconfig 文件需要一个上下文(Context),此上下文用于本 API Servercurrent-context: webhookcontexts:- context:    cluster: name-of-remote-authn-service    user: name-of-api-sever  name: webhook

当客户端尝试在 API Server上使用bearer token完成身份认证时, 身份认证 Webhook 会用 POST 请求发送一个 JSON 序列化的对象到远程服务。 该对象是 authentication.k8s.io/v1beta1 组的 TokenReview 对象, 其中包含bearer token。

POST请求Body:

{
       "apiVersion": "authentication.k8s.io/v1beta1",  "kind": "TokenReview",  "spec": {
         "token": "<持有者令牌>"  }}

远程服务会填充请求的 status 字段,以标明登录操作是否成功,并还返回用户信息。

{
     
  "apiVersion": "authentication.k8s.io/v1beta1",
  "kind": "TokenReview",
  "status": {
     
    "authenticated": true, // 认证失败返回false
    "user": {
     
      "username": "[email protected]",
      "uid": "42",
      "groups": [
        "developers",
        "qa"
      ],
      "extra": {
     
        "extrafield1": [
          "extravalue1",
          "extravalue2"
        ]
      }
    }
  }
}

Authenticating Proxy 第三方授权协议

API Server可以配置成从请求头的字段值(如 X-Remote-User)中识别用户。 主要是用来与身份认证代理一起使用,代理负责设置请求头的字段值。配置参数:

  • –requestheader-username-headers: 必需字段,大小写不敏感。用来设置要获取用户身份的请求头字段名,可以指定多个,在请求中第一个有数值的字段会被用来提取用户名。

  • –requestheader-group-headers: 可选字段,在 Kubernetes 1.6 版本以后支持,大小写不敏感。 建议设置为 “X-Remote-Group”。用来根据配置的请求头名称获取用户所属的组名。所找到的全部头部字段的取值都会被用作用户组名。

  • –requestheader-extra-headers-prefix 可选字段,在 Kubernetes 1.6 版本以后支持,大小写不敏感。 建议设置为 “X-Remote-Extra-”。用来设置一个头部字段的前缀字符串,API Server会基于所给前缀来查找与用户有关的一些额外信息。这些额外信息通常用于所配置的鉴权插件。 API Server会将与所给前缀匹配的头部字段过滤出来,去掉其前缀部分,将剩余部分转换为小写字符串并在必要时执行 percent-decoded 解码后,作为新的附加信息字段扩展键名。header的值作为扩展键的值。

Api Server配置示例:

--requestheader-username-headers=X-Remote-User
--requestheader-group-headers=X-Remote-Group
--requestheader-extra-headers-prefix=X-Remote-Extra-

请求头:

GET / HTTP/1.1X-Remote-User: fidoX-Remote-Group: dogsX-Remote-Group: dachshundsX-Remote-Extra-Acme.com%2Fproject: some-projectX-Remote-Extra-Scopes: openidX-Remote-Extra-Scopes: profile

认证后的用户信息为:

name: fidogroups:- dogs- dachshundsextra:  acme.com/project:  - some-project  scopes:  - openid  - profile

X-Remote-Extra-Acme.com%2Fproject 去掉了*X-Remote-Extra-*前缀,Acme.com%2Fproject 经过percent-decoded解码后作为扩展字段的key,请求头的值作为该扩展字段键的值。

为了防止请求头欺骗,在检查请头之前,需要身份验证代理向API Server提供有效的客户端证书,以便针对指定的CA进行验证。相关配置:

  • –requestheader-client-ca-file:必需字段,PEM 编码的证书包。 在检查请求头中的用户名之前,必须提供有效的客户端证书并根据指定文件中的证书颁发机构进行验证。
  • –requestheader-allowed-names:可选字段,用来给出一组公共名称(CN)。 如果设置了,则必须在检查请求头的用户名之前提供具有指定列表中的CN的有效客户端证书。如果为空,则允许任何CN。

匿名请求

启用匿名请求支持之后,如果请求没有被已配置的其他身份认证策略拒绝,则被视作匿名请求(Anonymous Requests)。这类请求的用户名为:**system:anonymous,**对应的用户组为: system:unauthenticated

在一个配置了令牌身份认证且启用了匿名访问的服务器上,如果请求提供了非法的bearer toekn,则会返回 401 Unauthorized 错误。 如果请求没有提供bearer toekn,则被视为匿名请求。

授权

Kubernetes 使用 API Server对 API 请求进行鉴权。 它根据所有策略评估所有请求属性来决定允许或拒绝请求。 一个 API 请求的所有部分都必须被某些策略允许才能继续。当系统配置了多个鉴权模块时,Kubernetes 将按顺序执行每个模块。 如果任何鉴权模块批准或拒绝请求,则立即返回该决定,不会与其他鉴权模块协商。 如果所有模块对请求都没有授权,则拒绝该请求。响应HTTP 状态代码 403。

k8s审查的API请求属性

user 身份验证期间提供的用户字符串。
group 已验证用户所属的组名称列表。
extra 由身份认证层提供的任意字符串的键值字映射。
API 指示请求是否为API资源。 api reference
requestpath 到其他非资源端点(如/api或/healthz)的路径。
API request verb API请求动词:get、list、create、update、patch、watch、proxy、redirect、delete和deletecollection用于资源请求。要确定资源API端点的请求谓词,请参见 Determine the request verb.。
HTTP request verb HTTP请求方法:get、post、put和delete用于非资源请求。
resource 正在访问的资源的ID或名称(仅用于资源请求)
子资源 正在访问的子资源(仅用于资源请求)。
Namespace 正在访问的对象的名称空间(仅用于名称空间大小的资源请求)。
API group 正在访问的API组(仅用于资源请求)。空字符串指定核心API组
apiVersion 您使用Kubernetes API的哪个版本来创建这个对象,K8S api 遵守openapi规范. 在K8S的API 设计中,最重要的几个概念就是 api version, api group, resource type , meta, 以及 spec
kind 创建什么样的对象
meta 帮助惟一标识对象的数据,包括名称字符串、UID和可选名称空间

举例:

apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2 这里 app 就是API GROUP, v1 就是 api versionkind: Deploymentmetadata:  name: nginx-deploymentspec:  selector:    matchLabels:      app: nginx  replicas: 2 # tells deployment to run 2 pods matching the template  template:    metadata:      labels:        app: nginx    spec:      containers:      - name: nginx        image: nginx:1.7.9        ports:        - containerPort: 80

K8S 支持的授权模式

  1. **ABAC(Attribute-based Access Control)**授权:基于属性的访问控制(ABAC)定义的一种访问控制范式,通过将属性组合在一起的策略将访问权限授予用户。策略可以使用任何类型的属性(用户属性、资源属性、对象、环境属性等)。

  2. **RBAC(Role-based Access Control)**授权:基于角色的访问控制(RBAC)。其基本思想是,对系统操作的各种权限不是直接授予具体的用户,而是在用户集合与权限集合之间建立一个角色集合。每一种角色对应一组相应的权限。一旦用户被分配了适当的角色后,该用户就拥有此角色的所有操作权限。当指定RBAC时,使用RBAC.authority.k8s.io API组驱动授权决策,允许管理员通过Kubernetes API动态配置权限策略。

  3. NODE 授权:一个专用的鉴权组件,专门对 kubelet 发出的 API 请求执行鉴权。 Pod 为调度到该Pod的 kubelet 授予权限。

  4. WEBHOOK 授权:WebHook 是一个 HTTP 回调,当某个事件发生时触发一个HTTP POST的回调。实现了webhook的web应用程序将向URL发送一条消息。

  5. 允许所有访问

  6. 拒绝所有访问

授权模式的设置方法

kube-apiserver --authorization-mode=Example,RBAC --<其他选项> --<其他选项>--authorization-mode=ABAC--authorization-mode=RBAC--authorization-mode=Webhook--authorization-mode=Node--authorization-mode=AlwaysDeny--authorization-mode=AlwaysAllow

使用 RBAC 鉴权

参考文档

k8s-身份认证与权限_第3张图片

RBAC使用RBAC.authoriz.k8s.io API组驱动授权决策,允许管理员通过Kubernetes API动态配置策略。

API 对象

RBAC API 声明了四种 Kubernetes 对象:Role、ClusterRole、RoleBinding 和 ClusterRoleBinding。可以像使用其他 Kubernetes 对象一样, 通过类似 kubectl 获取对象描述, 或修改对象。

Role 一组权限的集合,只有许可形式的权限,没有拒绝形式的权限,用户被授予角色即表示拥有角色对应的资源的访问权限。角色存在于namespace内,Role 用来在某个namespace内设置访问权限;创建 Role 时,必须指定该 Role 所属的名字空间。跨namespace的角色需要使用ClusterRole。
ClusterRole 集群角色,和角色拥有同样的作用,但是其范围为整个集群。同时拥有集群范围的资源类型,可以为以下资源授予访问权限:集群范围资源(比如 节点(Node))非资源端点(比如 /healthz)跨名称空间访问的名称空间作用域的资源(如 Pods)。比如,你可以使用 ClusterRole 来允许某特定用户执行 kubectl get pods --all-namespacesClusterRole用法有:定义对某个namesapce域对象的访问权限,并将在各个名字空间内完成授权;为namesapce作用域的对象设置访问权限,并跨所有namesapce执行授权;为集群作用域的资源定义访问权限。
AggregatedClusterRole 聚合集群角色,可以让一个集群角色,拥有其他几个集群角色的权限,k8s version1.9+
RoleBinding 将角色绑定到到一个目标上,目标可以是user,group,serviceaccount
ClusterRoleBinding 将集群角色绑定到一个目标上,目标可以是user,group,serviceaccount。

Role 和 ClusterRole

Role 用来在某个名称空间内设置访问权限;在创建 Role 时,你必须指定该 Role 所属的名称空间。

ClusterRole 则是一个集群作用域的资源。

Kubernetes 对象要么是名称空间作用域的,要么是集群作用域的,不可两者兼具。

如果希望在名称空间内定义角色,应该使用 Role; 如果希望定义集群范围的角色,应该使用 ClusterRole。

Role示例:

一个读取default namespace 中 pod的Role的例子:

apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata:  namespace: default  name: pod-readerrules:- apiGroups: [""] # "" 标明 core API 组  resources: ["pods"] #resources 的值都是资源类型的复数形式,比如资源类型pod,就要写pods,rolebinding 就要写rolebindings  verbs: ["get", "watch", "list"]

一个能够访问所有namespace 中secret的ClusterRole的例子:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  # "namespace" 被忽略,因为 ClusterRoles 不受名字空间限制
  name: secret-reader
rules:
- apiGroups: [""]
  resources: ["secrets"]  # 在 HTTP 层面,用来访问 Secret 对象的资源的名称为 "secrets"
  verbs: ["get", "watch", "list"]

RoleBinding 和 ClusterRoleBinding

角色绑定(Role Binding)是将角色中定义的权限赋予一个或者一组用户。

它包含若干 主体(用户、组或服务账户)的列表和对这些主体所获得的角色的引用。

RoleBinding 在指定的名字空间中执行授权,ClusterRoleBinding 在集群范围执行授权。

一个 Rolebinding的例子,下面的例子中的 RoleBinding 将 “pod-reader” Role 授予在 “default” 名称空间中的用户 “jane”。 用户 “jane” 就具有了读取 “default” 名空称间中 pods 的权限。:

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods
  namespace: default
subjects:
- kind: User
  name: jane # 注意大小写敏感
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role #这里必须是 Role 或者 ClusterRole
  name: pod-reader # 这里的名称必须和创建的 role 或者 cluster role 匹配.
  apiGroup: rbac.authorization.k8s.io

一个ClusterRoleBinding 的例子:允许所有在manager 组下的用户都能够读取所有名称空间下的secret

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: read-secrets-global
subjects:
- kind: Group
  name: manager # 大小写敏感
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io

聚合的 ClusterRole

可以将若干 ClusterRole 聚合(Aggregate) 起来,形成一个复合的 ClusterRole。

当创建一个与既有 ClusterRole 的聚合标签相同的 ClusterRole时, 新的规则会被添加到聚合 ClusterRole中。

apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata:  name: monitoringaggregationRule:  clusterRoleSelectors:  - matchLabels:      rbac.example.com/aggregate-to-monitoring: "true"rules: [] # 控制面自动填充这里的规则

通过创建一个标签同样为 rbac.example.com/aggregate-to-monitoring: true 的 ClusterRole,新的规则可被添加到 “monitoring” ClusterRole 中。

apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata:  name: monitoring-endpoints  labels:    rbac.example.com/aggregate-to-monitoring: "true"# 当创建 "monitoring-endpoints" ClusterRole 时,# 下面的规则会被添加到 "monitoring" ClusterRole 中rules:- apiGroups: [""]  resources: ["services", "endpoints", "pods"]  verbs: ["get", "list", "watch"]

默认的面向用户的角色(ClusterRole 不是以前缀 system: 开头的)使用 ClusterRole 聚合。

包括超级用户(Super-User)角色(cluster-admin)、 使用 ClusterRoleBinding 在集群范围内完成授权的角色(cluster-status)、 以及使用 RoleBinding 在特定名字空间中授予的角色(admin、edit、view)。

管理员通过面向用户的 ClusterRole 使用 ClusterRole 聚合来给这些 ClusterRole 上添加资源的规则。角色聚合标签有:

默认角色 聚合标签
admin rbac.authorization.k8s.io/aggregate-to-admin
edit rbac.authorization.k8s.io/aggregate-to-edit
view rbac.authorization.k8s.io/aggregate-to-view

对资源的引用方式

在 Kubernetes API 中,大多数资源都是使用对象名称的字符串来访问的。 RBAC 使用对应的 API Endpoint URL 来引用资源。 例如,对于 Pod 应使用 “pods”。

资源引用:

api/GROUPNAME/VERSION/namespaces/{namespace-instance-name}/RESOURCENAME/{resource-instance-name}/$SUBRESOURCENAME
  • 大写字母表示k8s内建的术语值

  • {}表示某一类术语的实例值。

  • RESOURCENAME 可能是pods(复数形式)或者secrets等

  • 当groupname 是core的时候, groupname省略不写

参考:API 说明文档

例子:对 Pod 日志的请求如下所示:

GET /api/v1/namespaces/{namespace}/pods/{name}/log

pods 对应名称空间作用域的 Pod 资源,log 是 pods 的子资源。 在 RBAC 角色表达子资源时,使用斜线(/)来分隔资源和子资源。 要允许某主体读取 pods 同时访问这些 Pod 的 log 子资源,可以这么写:

apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata:  namespace: default  name: pod-and-pod-logs-readerrules:- apiGroups: [""]  resources: ["pods", "pods/log"]  verbs: ["get", "list"]

资源可以通过ResourceName 进行引用,在指定Resource Name 后,使用get delete update 和patch 动词的请求,就会被限制在这个资源范围内。

apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata:  namespace: default  name: configmap-updaterrules:- apiGroups: [""]  resources: ["configmaps"]  resourceNames: ["my-configmap"]  verbs: ["update", "get"]

使用 Node 鉴权

节点鉴权是一种特殊用途的鉴权模式,专门对 kubelet 发出的 API 请求进行鉴权。

节点鉴权器允许 kubelet 执行 API 操作有:

读取操作 servicesendpointsnodespodssecrets、configmaps、pvcs 以及绑定到 kubelet 节点的与 pod 相关的持久卷
写入操作 节点和节点状态(启用 NodeRestriction 准入插件以限制 kubelet 只能修改自己的节点)Pod 和 Pod 状态 (启用 NodeRestriction 准入插件以限制 kubelet 只能修改绑定到自身的 Pod)事件
认证相关操作 对基于 TLS 的启动引导过程时使用的 certificationsigningrequests API 的读/写访问为代理身份验证/授权 检查是否能够创建 tokenreviews 和 subjectaccessreviews

Webhook 模式

当在判断用户权限时,Webhook 模式会使 Kubernetes 调用外部的 REST 服务。webhook模式需要一个https请求配置文件,使用参数:–authorization-webhook-config-file=SOME_FILENAME

# Kubernetes API 版本apiVersion: v1# API 对象种类kind: Config# clusters 代表远程服务。clusters:  - name: name-of-remote-authz-service    cluster:      # 对远程服务进行身份认证的 CA。      certificate-authority: /path/to/ca.pem      # 远程服务的请求 URL。必须使用 'https'。      server: https://authz.example.com/authorize# users 代表 apiserver的 webhook 配置users:  - name: name-of-api-server    user:      client-certificate: /path/to/cert.pem # webhook plugin 使用 cert      client-key: /path/to/key.pem          # cert 所对应的 key# kubeconfig 文件必须有 context。需要提供一个给 API 服务器。current-context: webhookcontexts:- context:    cluster: name-of-remote-authz-service    user: name-of-api-server  name: webhook

请求载荷

在做认证决策时,apiserver会发出POST请求,body是一个使用JSON 序列化的 authorization.k8s.io/v1beta1 SubjectAccessReview对象。这个对象包含了描述用户请求的字段、需要被访问资源或请求特征的具体信息。

{
       "apiVersion": "authorization.k8s.io/v1beta1",  "kind": "SubjectAccessReview",  "spec": {
         "resourceAttributes": {
           // 请求访问的资源      "namespace": "kittensandponies",      "verb": "get",      "group": "unicorn.example.org",      "resource": "pods"    },    "user": "jane",  // 用户    "group": [   // 组      "group1",      "group2"    ]  }}

非资源的路径访问的授权请求:

非资源类的路径包括:/api, /apis, /metrics, /resetMetrics, /logs, /debug, /healthz, /swagger-ui/, /swaggerapi/, /ui, 和 /version。

{
       "apiVersion": "authorization.k8s.io/v1beta1",  "kind": "SubjectAccessReview",  "spec": {
         "nonResourceAttributes": {
           "path": "/debug",      "verb": "get"    },    "user": "jane",    "group": [      "group1",      "group2"    ]  }}

远程授权服务需要填充请求的 status 字段,响应允许或禁止访问:

{
       "apiVersion": "authorization.k8s.io/v1beta1",  "kind": "SubjectAccessReview",  "status": {
         "allowed": true  }}

禁止访问的返回

  1. 不允许访问,但可以让其他授权模块进行鉴权
{
       "apiVersion": "authorization.k8s.io/v1beta1",  "kind": "SubjectAccessReview",  "status": {
         "allowed": false,    "reason": "user does not have read access to the namespace"  }}
  1. 不允许访问,立即拒绝,短路配置的其他授权模块
{
     
  "apiVersion": "authorization.k8s.io/v1beta1",
  "kind": "SubjectAccessReview",
  "status": {
     
    "allowed": false,
    "denied": true,
    "reason": "user does not have read access to the namespace"
  }
}

使用 ABAC 鉴权

指定策略文件:–authorization-policy-file=SOME_FILENAME

文件内容每一行为一个JSON策略对象。

示例1,Alice 可以对所有资源做任何事情:

{
     
	"apiVersion": "abac.authorization.kubernetes.io/v1beta1",
	"kind": "Policy",
	"spec": {
     
		"user": "alice",
		"namespace": "*",
		"resource": "*",
		"apiGroup": "*"
	}
}

示例2:Bob 可以在命名空间 projectCaribou 中读取 pod:

{
     
	"apiVersion": "abac.authorization.kubernetes.io/v1beta1",
	"kind": "Policy",
	"spec": {
     
		"user": "bob",
		"namespace": "projectCaribou",
		"resource": "pods",
		"readonly": true
	}
}

示例3:多行策略,任何人都可以对所有非资源路径进行只读请求

{
     "apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {
     "group": "system:authenticated", "readonly": true, "nonResourcePath": "*"}}{
     "apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {
     "group": "system:unauthenticated", "readonly": true, "nonResourcePath": "*"}}

策略对象具有的属性有:

版本控制属性 apiVersion 版本控制属性,字符串类型:有效值为abac.authorization.kubernetes.io/v1beta1,允许对策略格式进行版本控制和转换。
kind 字符串类型:有效值为 Policy,允许对策略格式进行版本控制和转换。
spec主体匹配属性 user 字符串类型;来自 --token-auth-file 的用户字符串,如果指定 user,必须与验证用户的用户名匹配。
group 字符串类型;如果指定 group,必须与经过身份验证的用户的一个组匹配,system:authenticated匹配所有经过身份验证的请求。system:unauthenticated匹配所有未经过身份验证的请求。
spec资源匹配属性 apiGroup API 组,如extensions,*匹配所有 API 组。
namespace 命名空间,如kube-system,*匹配所有资源请求。
resource 资源类型,如pods,*匹配所有资源请求。
spec非资源匹配属性 nonResourcePath 非资源请求路径,如/version或 /apis。* 匹配所有非资源请求。例:/foo/* 匹配 /foo/ 的所有子路径。
specreadonly 如果为 true,则表示该策略仅适用于 get、list 和 watch 操作

准入控制

参考文档

准入控制器是在对象持久化之前用于对 Kubernetes API Server 的请求进行拦截的代码段。

在请求通过认证与授权后,并不能获得成功的响应还需要通过Adminssion Control的检查才能成功获取资源内容。

准入控制过程有两个阶段:首先执行改变(mutating)阶段,然后是验证(validating)阶段。准入控制器可以充当改变或验证控制器,或两者的组合。

Mutating 控制器可以修改他们的处理的资源对象,Validating 控制器不会,如果任何一个阶段中的任何控制器拒绝了请求,则会立即拒绝整个请求,并将错误返回给最终的用户。

k8s-身份认证与权限_第4张图片

准入控制器的作用

**安全性:**通过在整个命名空间和集群中强制设置合理的安全基线,准入控制器可以帮助提高整体安全性。

**IT 治理:**准入控制器可以帮助遵守某些规范,例如使用标签、注释、资源限制或其他设置,一些常见方案包括:

  • 对不同对象强制执行标签验证,确保始终将正确的标签用于各种对象;
  • 自动向对象添加注释,例如为“dev”部署资源指定正确的 cost center。

**配置管理:**准入控制器可以帮助工程师验证集群中运行时对象的配置,防止错误配置影响集群。对检测和修复不带语义标签的镜像很有用,例如:

  • 自动添加资源限制或验证资源限制;

  • 确保将合理的标签添加到 Pod;

  • 确保在生产部署中使用的镜像引用不使用 latest 标签,或带有 -dev 后缀的标签。

启用一个准入控制器

Kubernetes API 服务器的 enable-admission-plugins 标志接受一个用于在集群修改对象之前调用的准入控制插件顺序列表(以逗号分隔的)。

# 启用了 NamespaceLifecycle 和 LimitRanger 准入控制插件
kube-apiserver --enable-admission-plugins=NamespaceLifecycle,LimitRanger ...

关闭准入控制器

kube-apiserver --disable-admission-plugins=PodNodeSelector,AlwaysDeny ...

默认启用的插件有:

NamespaceLifecycle, LimitRanger, ServiceAccount, TaintNodesByCondition, Priority, DefaultTolerationSeconds, DefaultStorageClass, StorageObjectInUseProtection, PersistentVolumeClaimResize, RuntimeClass, CertificateApproval, CertificateSigning, CertificateSubjectRestriction, DefaultIngressClass, MutatingAdmissionWebhook, ValidatingAdmissionWebhook, ResourceQuota

你可能感兴趣的:(运维,kubernetes,docker,java)