kubernetes API Server安全

来源:互联网 发布:泰国明星互动软件 编辑:程序博客网 时间:2024/05/03 22:31

用户访问API Server(以下简称Server),K8S的安全检查步骤:认证和授权。

认证解决用户是谁的问题,就是验证用户名密码;授权解决用户能做什么的问题,就是检查该用户是否拥有权限访问请求的资源。通过合理的权限管理,能够保证系统的安全可靠。

网络

Server通过本地端口(Localhost Port)和安全端口(Secure Port)对外提供API服务,其中本地端口是基于HTTP协议的,用于本地无限制访问,安全端口是基于HTTPS协议的,用于远程有限制访问。

本地端口

默认配置中,Server的本地端口默认绑定到地址127.0.0.1上,只能在本机访问。由于本地端口将绕过了Server的认证和授权,只要能够访问本地端口,可以无限制访问Server。基于安全考虑,尽量不要将本地端口绑定到127.0.0.1以外的地址上。

本地端口默认绑定到端口8080上,可以通过Server启动参数--insecure-port进行指定,如果为0关闭本地端口。参数--insecure-bind-address指定绑定地址。

安全端口

安全端口是Server对外提供的,用于外部访问的安全的可控的API调用接口,Server只允许通过认证的用户(认证信息来源于User Account或者Service Account)才能够通过安全端口访问,对于匿名用户401拒绝访问。

默认绑定到端口6443上,通过Server的启动参数--secure-port进行指定,如果为0关闭安全端口。默认绑定到地址0.0.0.0上,通过参数--bind-address进行指定。

通过参数--tls-cert-file和--tls-private-key-file指定证书和私钥。

代理和防火墙

在实际应用中,可能现有的认证体系无法与K8S集成或者需要执行特殊认证和授权逻辑的情况,可以考虑引入代理(Proxy)来解决认证和授权的问题,在认证通过后,代理将请求转发到Server。

当代理能够与Server部署在同一台主机时,建议按照下面的方式进行集成:

关闭安全端口,确保所有的API请求只能通过代理接入将本地端口绑定到地址127.0.0.1上,确保只能在本机访问设置防火墙规则,仅开放本机的443端口配置nginx监听443端口,并且在此端口上配置认证和HTTPS配置nginx将请求转发到本地端口,默认情况下为127.0.0.1:8080

当代理无法与Server部署在同一台主机时,从安全角度看,再试图通过本地端口与Server集成不是好的选择,使用安全端口与Server集成更好更安全。

认证

只能用于HTTPS(安全端口),对http(非安全端口)来说无效,原因请看上节的“本地端口”。

认证方式有很多种,这里主要介绍以下几种:

静态密码/用户账号

用户账号(User Accounts,以下简称UA),也称静态密码,是用于管理者通过web访问Server的认证用户名和密码。

静态密码是提前在某个文件中保存了用户名和密码的信息,然后通过Server启动参数 --basic-auth-file=SOMEFILE指定文件路径。

文件是CSV格式,每行对应一个用户信息,前面三列密码、用户名、UID 是必须的,第四列是可选的组名(多个组,必须用双引号):password,user,uid,"group1,group2,group3" 例如 cat /etc/kubernetes/useraccount.csv 1qaz2wsx,lykops,1 1qaz2wsx,lykchat,2

如果是通过客户端发送请求时(非用户通过web页面请求的),需要指定ca证书、用户名和密码。例如:

curl -u lykops:1qaz2wsx https://192.168.20.128:6443/ --cacert /etc/ssl/kube/ca.pem

注意:不能动态更新,需要重启api server。

服务账号

服务账号(Service Accounts,以下简称SA)并不是给K8S集群的用户使用的,而是用于Pod中的程序访问API的account,它为Pod中的程序提供了一种身份标识。UA的作用域为K8S集群全局,是全局性权限,SA的作用域为命名空间(简称ns)。SA作为一种资源对象存在于K8S中。具体请看资源对象—service acecount章节。

原理

查看一下kube-system ns下名为默认sa的详细信息: kubectl describe serviceaccount/default -n kube-system Name: default Namespace: kube-system Labels: Image pull secrets: Mountable secrets: default-token-hpni0 Tokens: default-token-hpni0

看到sa并不复杂,只是关联了一个secret资源对象作为token,该token也叫service-account-token,该token才是真正在Server验证(authentication)环节起作用的: kubectl get secret -n kube-system NAME TYPE DATA AGE default-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  name: default-token-hpni0  namespace: kube-systemtype: kubernetes.io/service-account-token

看到这个类型为service-account-token的secret资源包含的数据有三部分:ca.crt、namespace和token。

ca.crt是Server的CA公钥证书;namespace是Secret所在ns值的base64编码token是一段用Server私钥(在创建sa时用Server参数--service-account-key-file指定的,如果未指定,那么将默认使用--tls-private-key-file值,即Server的私钥server.key)文件内容签发(sign)的bearer tokens的base64编码。

认证过程

在K8S的认证环节中,以某个sa提供身份的Pod的用户名为:system:serviceaccount:(ns):(sa)。以kube-system ns下的默认sa为例,使用它的Pod的username全称为:system:serviceaccount:kube-system:default。有了用户名,那么credentials(凭证)呢?就是上面提到的service-account-token中的token。

如果客户端使用sa token方式(携带service account token)访问api,Server采用signed bearer token方式进行身份校验。 通过身份校验后,Server将根据Pod用户名所在的group:system:serviceaccounts和system:serviceaccounts:(NAMESPACE)的权限分配权限(authority和admission control两个环节)。在这两个环节中,管理员可以对service account的权限进行细化设置。

引用

如果Pod中没有显式指定spec.serviceAccount字段值,那么K8S会将该ns下的默认sa自动mount到在该ns中创建的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.

可以看到,K8S将default ns中的默认sa的service account token挂载(mount)到了Pod中容器的/var/run/secrets/kubernetes.io/serviceaccount路径下。

深入容器内部,查看mount的sa路径下的结构:

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

这三个文件与上面提到的sa中的数据是一一对应的。

双向认证

客户端证书认证叫作TLS双向认证,就是服务器客户端互相验证证书的正确性,在都正确的情况下协调通信加密方案。

双向认证方式是最为严格和安全的集群安全配置方式,主要配置流程如下:

生成根证书、Server服务端证书、服务端私钥、各个组件所用的客户端证书和客户端私钥。修改K8S各个服务进程的启动参数,启用双向认证模式。

静态Token文件

类似于静态密码,只是通过token来代替密码。

当在命令行指定--token-auth-file=SOMEFILE选项时,Server从文件中读取bearer tokens。令牌文件是一个至少包含3列的csv文件: token, user name, user uid,后跟可选的组名。注意,如果有多个组,则列必须是双引号,例如:token,user,uid,"group1,group2,group3" 当客户端使用bearer token认证时,需要发送一个“Authorization: Bearer ”+token(token必须是一个可以放在HTTP请求头中且值不需要转码和引用)的字符串。例如:token是31ada4fd-adec-460c-809a-9e56ceb75269,客户端的HTTP头必须添加:Authorization: Bearer 31ada4fd-adec-460c-809a-9e56ceb75269

注意:不能动态更新,需要重启api server,很不灵活,也不安全,不推荐使用。

OpenID Connect Tokens

OpenID Connect是由OAuth2提供商支持的OAuth2,特别是Azure Active Directory,Salesforce和Google。OAuth2的协议的主要扩展是增加一个额外字段,返回了一个叫ID token的access token。这个token是被服务器签名的JSON Web Token (JWT) ,具有众所周知的字段,比如用户的email。

为了识别用户,验证使用来自OAuth2 token响应的idtoken (而不是accesstoken)作为bearer token。

使用OpenID认证,API Server需要配置

- --oidc-issuer-url,如https://accounts.google.com- --oidc-client-id,如kubernetes- --oidc-username-claim,如sub- --oidc-groups-claim,如groups- --oidc-ca-file,如/etc/kubernetes/ssl/kc-ca.pem

Webhook Token

Webhook Token认证方式可以让用户使用自己的认证方式(类似于单点登录),用户按照约定的请求格式和应答格式提供HTTPS服务,当用户把Bearer Token放到请求的头部,K8S会把token发送给事先配置的地址进行认证,如果认证结果成功,则认为请求用户合法。这种方式下有两个参数 可以配置:

–authentication-token-webhook-config-file :认证URL地址的配置文件

–authentication-token-webhook-cache-ttl :认证结果要缓存多久,默认是两分钟

这种方式下,自定义认证的请求和应答都有一定的格式,具体的规范请参考官方文档。

认证代理

Server配置文件需要制定以下配置

--requestheader-username-headers=X-Remote-User--requestheader-group-headers=X-Remote-Group--requestheader-extra-headers-prefix=X-Remote-Extra-# 为了防止头部欺骗,证书是必选项--requestheader-client-ca-file# 设置允许的CN列表。可选。--requestheader-allowed-names

Keystone Password

Keystone是OpenStack提供的认证和授权组件,这个方法对于已经使用openstack来搭建Iaas平台的公司比较适用,直接使用keystone可以保证Iaas和Caas平台保持一致的用户体系。

需要Server在启动时指定--experimental-keystone-url=,而https时还需要设置--experimental-keystone-ca-file=SOMEFILE。

引导Token

在v1.6版本中,这个特性还是alpha特性。为了能够在新的集群中使用bootstrapping认证。Kubernetes包括一种动态管理的Bearer(持票人) token,这种token以Secrets的方式存储在kube-system命名空间中,在这个命名空间token可以被动态的管理和创建。Controller Manager有一个管理中心,如果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。

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

匿名请求

如果请求没有通过以上任何方式的认证,正常情况下应该是直接返回 401 错误。但K8S还提供另外一种选择,给没有通过认证的请求一个特殊的用户名system:anonymous和组名 system:unauthenticated 。 这样可以跟下面要讲的授权结合起来,为匿名请求设置一些特殊的权限,比如只能读取当前ns的pod信息,方便用户访问。 如果使用AlwaysAllow、AlwaysDeny以外的认证模式,则匿名请求默认开启,但可用--anonymous-auth=false禁止匿名请求。

授权插件

K8S的授权是通过插件方式来实现的,目前K8S内置提供了AlwaysDeny、AlwaysAllow、ABAC、RBAC以及WebHook等几种授权插件(在1.5.2版本中,只有这五种),通过配置Server启动参数--authorization-mode来指定授权模式。

授权处理以下的请求属性: user, group, extra API、请求方法(如get、post、update、patch和delete)和请求路径(如/api) 请求资源和子资源 Namespace API Group

AlwaysDeny

该模式将会拒绝任何对安全端口的请求。任何访问,服务端总是返回Forbidden: "URL",表示访问被拒绝。

AlwaysDeny模式主要用于测试,当然也可以用来暂时停止集群的对外服务。

AlwaysAllow

默认模式,该模式下只要通过认证,服务端将会接受任何对安全端口的请求,换句话说就是除了认证没有任何权限限制。

当集群不需要做授权限制时,直接使用该模式,以降低配置的复杂性。

ABAC

ABAC(Attribute-based access control,基于属性的访问控制),ABAC的核心是根据请求的相关属性,例如用户属性、资源属性以及环境属性等属性,作为授权的基础来进行访问控制,以解决分布式系统的可信任关系的访问控制问题。

ABAC的策略文件是一个one JSON object per line格式的文本文件。ABAC的授权过程可以简单的理解为将请求属性转换为一个spec对象,然后拿到这个spec对象与策略文件中定义的spec对象进行匹配,如果这个spec对象能够与策略文件中定义的任何一条规则允许的spec对象匹配,那么授权通过;如果这个spec对象无法与任何一条规则匹配,那么授权失败。

RBAC

WebHook

WebHook模式是一种扩展授权模式,在这种模式下,API Server将授权过程委派到外部的一个REST服务,由外部的服务决定是否授予指定请求继续访问的权限。

WebHook模式的开启非常的简单,只需要通过API Server的启动参数--authorization-mode设置为WebHook并且通过启动参数--authorization-webhook-config-file将外部授权服务的配置信息告诉API Server即可。

Admission Control 准入控制

当请求通过了前面的认证和授权后,还需要经过准入控制处理通过之后,server才会处理这个请求。Admission Control 有一个准入控制列表,可以通过命令行设置选择执行哪几个准入控制器。只有所有的准入控制器都检查通过之后,apiserver 才执行该请求,否则返回拒绝。


原创粉丝点击