K8s认证详解

栏目: 服务器 · 发布时间: 4年前

内容简介:以前在基于k8s做应用开发的时候,都是使用admin来使用k8s,基本不用去关注授权的问题。但是,当我们将k8s作为PaaS平台的容器编排引擎,并引入多租户时,就涉及到权限管理相关的问题了。那么,如果你刚好是公司的系统管理员,当你部署完一套k8s,准备将其提供给多个部门使用的时候(他们希望彼此互不影响),接下来的内容就特别适合你了。K8s的安全问题越来越成为大家都在关注的重点,挑战一方面来自于运行时容器层面宿主机的安全,另一方面来自于k8s本身的安全。k8s的授权默认是基于RBAC的方式,这个比较好理解,但

以前在基于k8s做应用开发的时候,都是使用admin来使用k8s,基本不用去关注授权的问题。但是,当我们将k8s作为PaaS平台的容器编排引擎,并引入多租户时,就涉及到权限管理相关的问题了。那么,如果你刚好是公司的系统管理员,当你部署完一套k8s,准备将其提供给多个部门使用的时候(他们希望彼此互不影响),接下来的内容就特别适合你了。

概要介绍

K8s的安全问题越来越成为大家都在关注的重点,挑战一方面来自于运行时容器层面宿主机的安全,另一方面来自于k8s本身的安全。

认证方式

k8s的授权默认是基于RBAC的方式,这个比较好理解,但是认证却支持好几种方式:

  • 客户端证书
  • Bearer Tokens
    • Service Account Token
    • BootStrap Token
    • Static Token
  • HTTP Basic Auth
  • Authenticating Proxy

接下来我们重点分析前三种类型,至于Authenticating Proxy之类,这里暂不做分析。

权限控制原理

要解决文章开始时提到的问题,按照常规业务系统的设计,不外乎以下几步:

  1. 将系统中模块的子功能的操作权限赋予角色;
  2. 将用户与角色绑定,让用户拥有角色对应的操作权限;
  3. 认证用户的登录;

k8s系统中基于RBAC的授权刚好就是前面两步做的事情:

  1. 首先创建role,在role中指定该role所拥有的权限,能够允许执行的所有操作。
  2. 然后创建rolebinding,这一步binding的不止是用户;在k8s中,允许binding到role的可以是 user , group , service account 。其中 user 就是用户, group 是用户组, service account 可以理解为系统为内部服务分配的一个账户。

如果希望role和rolebinding不止是局限于对应的namespace,可以使用clusterrole和clusterrolebinding,这将使role的操作权限扩大到集群层面,而不是在单独某一个namespace内。

那么,第三步又是如何实现的呢?下面就介绍几种方法。

客户端证书认证

客户端证书认证就是客户端向k8s提交带有用户名、用户组等信息的CSR,然后管理员在k8s上为该客户签发证书。以后,用户使用该证书去访问k8s,api-server就能够辨识出用户。结合RBAC的配置,如果该用户或者用户组通过rolebinding绑定了role,那么该用户就拥有该role对对应资源的操作权限。

下面便是操作流程,通过示例,可以再理解一遍上面的流程。

  • 在本地主机上生成客户端的key和CSR文件

    # 生成key文件
    $ openssl genrsa -out ljchen.key 4096
    
    # 创建csr配置文件
    $ cat csr.cnf
    
    [ req ]
    default_bits = 2048
    prompt = no
    default_md = sha256
    distinguished_name = dn
    [ dn ]
    CN = ljchen
    O = dev
    [ v3_ext ]
    authorityKeyIdentifier=keyid,issuer:always
    basicConstraints=CA:FALSE
    keyUsage=keyEncipherment,dataEncipherment
    extendedKeyUsage=serverAuth,clientAuth
    
    # 生成csr文件
    $ openssl req -config ./csr.cnf -new -key ljchen.key -nodes -out ljchen.csr
    
  • 向k8s提交CSR,管理员签发后生成客户端证书

    # 创建CSR的k8s yaml
    $ cat csr.yaml
    
    apiVersion: certificates.k8s.io/v1beta1
    kind: CertificateSigningRequest
    metadata:
    name: mycsr
    spec:
    groups:
    - system:authenticated
    request: ${BASE64_CSR}
    usages:
    - digital signature
    - key encipherment
    - server auth
    - client auth
    
    # 下发CSR到k8s
    $ export BASE64_CSR=$(cat ./ljchen.csr | base64 | tr -d '\n')
    $ cat csr.yaml | envsubst | kubectl apply -f -
    
    # 在k8s上为CSR生成证书
    $ kubectl get csr
    $ kubectl certificate approve mycsr
    $ kubectl get csr
    NAME        AGE   REQUESTOR            CONDITION
    mycsr       9s    28b93...d73801ee46   Approved,Issued
    
    # 导出证书
    $ kubectl get csr mycsr -o jsonpath=’{.status.certificate}’ \
    | base64 --decode > ljchen.crt
    
    # 查看证书信息
    $ openssl x509 -in ./ljchen.crt -noout -text
    Certificate:
        Data:
            Version: 3 (0x2)
            Serial Number:
                01:27:cb:76:d1:72:cd:73:a5:be:06:b1:ae:f9:6c:99:11:5c:36:c8
        Signature Algorithm: sha256WithRSAEncryption
            Issuer: CN=kubernetes
            Validity
                Not Before: Jun 13 02:04:00 2019 GMT
                Not After : Jun 12 02:04:00 2020 GMT
            Subject: O=dev, CN=ljchen
            Subject Public Key Info:
                Public Key Algorithm: rsaEncryption
                    Public-Key: (4096 bit)
        ...
    
  • 配置RBAC, 在k8s端使用证书来控制用户认证

    $ kubectl create ns development
    
    # 创建role
    $ cat role.yaml
    
    kind: Role
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
    namespace: development
    name: dev
    rules:
    - apiGroups: [""]
    resources: ["pods", "services"]
    verbs: ["create", "get", "update", "list", "delete"]
    - apiGroups: ["apps"]
    resources: ["deployments"]
    verbs: ["create", "get", "update", "list", "delete"]
    
    $ kubectl apply -f role.yaml
    
    # 创建role-binding, 分两种情况,可以绑定到user或者group,两者二选一
    
    # 绑定到user上
    $ cat role-binding-user.yaml
    
    kind: RoleBinding
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
    name: dev
    namespace: development
    subjects:
    - kind: User
    name: ljchen
    apiGroup: rbac.authorization.k8s.io
    roleRef:
    kind: Role
    name: dev
    apiGroup: rbac.authorization.k8s.io
    
    # 绑定到group上
    $ cat role-binding-group.yaml
    
    kind: RoleBinding
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
    name: dev
    namespace: development
    subjects:
    - kind: Group
    name: dev
    apiGroup: rbac.authorization.k8s.io
    roleRef:
    kind: Role
    name: dev
    apiGroup: rbac.authorization.k8s.io
    
    $ kubectl apply -f role-binding-group.yaml
    
  • 在客户端,配置kubeconfig文件

    # 1. 先基于生成的ljchen.crt和已存在的ca来配置kubeconfig文件
    # 2. 将ljchen.key应用到kubeconfig中
    $ kubectl config set-credentials ljchen --client-key=~/.kube/ljchen.key --embed-certs=true
    

Service Account Token认证

service account本来是系统服务使用的账户,当我们创建service account时,它会自带一个secret,这个secret就是token。

前面谈到,rolebinding除了支持绑定user,group之外,还支持绑定到service account。所以,为了方便,我们有时候通过绑定role到某个service account的方式来直接使用sa的sercret来访问系统资源。比如部署dashboard的时候,就经常用这种方法来获取token登录UI。

下面的示例就是创建service account并取得token来登录系统的流程。

  • 服务端获取token

    #Create a new ServiceAccount
    kubectl create serviceaccount k8sadmin -n kube-system
    
    #Create a ClusterRoleBinding with Cluster Admin Privileges(系统管理员角色!!)
    kubectl create clusterrolebinding k8sadmin --clusterrole=cluster-admin --serviceaccount=kube-system:k8sadmin
    
    
    #serviceAccount 会自动生成secret,对应的token就是secret的值
    kubectl get secret -n kube-system | grep k8sadmin | cut -d " " -f1 | xargs -n 1 | xargs kubectl get secret   -n kube-system -o yaml
    
    apiVersion: v1
    data:
    ca.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN5RENDQWJDZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRFNU1EWXhNakV6TWpZd09Gb1hEVEk1TURZd09URXpNall3T0Zvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTktnCnRiSnQxSTZ6QlFVTmE2WHROK1pQN2xLeDdxbVk2aGJiaVByTXVGbmRhanBvd0RYdDhHVlJmNGFhbWVLS2Z0enEKWlJvSko5eHg5T3lnRkVINzRLY2tGRFhCbElUck9oY0t6TjJIWEI4SUlKM3BvN0Qzc0VScHZEYVZ6L0Z5TmRWagpBU0VhQzhlRWRSa2g2akhVeHJUTWlUMlJZemMrZGNaWC83MHcrTW90bjE5Skt4d0Y5N3h3U2EvYmd6NUdXK1V3CllBd0E0TWRwWG1YM1R1TUx5Rm9kSTVZOUt1VHBpVWd1OFp5Sm5ySzBpQ3g5MlpQMGRSVUlITXVRakxKWkhyY2QKOERsSmNnL0ZsOXlwaDh3UTZScDZSbzFlZ05tY2xGVFJBQm12OVhvMmNOdHJ4Mm1BTmp4aDBycjFkU21NWEJqUgpqS1Y0SFdqVWRzcERKbVpXRnJzQ0F3RUFBYU1qTUNFd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFNZkxia016RzZ6aGpUSGxtaU93SFJqL05BSWEKM0ZOY2lvekhJcUU2VkE5UUlOZ0V4a1pzQnBRREdBWVZvYk1Ec3RSQXQxY2M4cW5qdVltSnoreFlDZTFYY0xLMgpWRlpWdkdOZzJCRmJrTFdtYlRPeXhNNmQ0S2VvZnh6VDQ4cmU3U1pmQWRLRnRrVXN5T3AvUndQWGlOMVFpcy8vCkV6NDA1RlczTFZRWGtpMjh2cG5KUG5WNUhoZy9FY0ZMOXoveXdSUEtldmFRZUE5NGFFVXNuOEJXTGtZcWs3NWgKalM4dlhlNi9KOGVvWDdybVdhOG9QZUdQYmpqZ2E0Q0UyK2N1L0RqMFNFWEduK2xzM2w1V3dGZVNVNXdwd21XUwptdUdPdDZXVTdaa3hJbkNSTERudGovZzlJOFVGai9JZFBvd3B2bU1oUW5DOXFtV2pCZ21RSkhsN29iUT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
    namespace: a3ViZS1zeXN0ZW0=
    token: ZXlKaGJHY2lPaUpTVXpJMU5pSXNJbXRwWkNJNklpSjkuZXlKcGMzTWlPaUpyZFdKbGNtNWxkR1Z6TDNObGNuWnBZMlZoWTJOdmRXNTBJaXdpYTNWaVpYSnVaWFJsY3k1cGJ5OXpaWEoyYVdObFlXTmpiM1Z1ZEM5dVlXMWxjM0JoWTJVaU9pSnJkV0psTFhONWMzUmxiU0lzSW10MVltVnlibVYwWlhNdWFXOHZjMlZ5ZG1salpXRmpZMjkxYm5RdmMyVmpjbVYwTG01aGJXVWlPaUpyT0hOaFpHMXBiaTEwYjJ0bGJpMXFiamQ0ZGlJc0ltdDFZbVZ5Ym1WMFpYTXVhVzh2YzJWeWRtbGpaV0ZqWTI5MWJuUXZjMlZ5ZG1salpTMWhZMk52ZFc1MExtNWhiV1VpT2lKck9ITmhaRzFwYmlJc0ltdDFZbVZ5Ym1WMFpYTXVhVzh2YzJWeWRtbGpaV0ZqWTI5MWJuUXZjMlZ5ZG1salpTMWhZMk52ZFc1MExuVnBaQ0k2SW1RMll6TmpPR1V5TFRoa01UVXRNVEZsT1MwNE0yVmtMVEF3TVRZelpURTBPV05tTWlJc0luTjFZaUk2SW5ONWMzUmxiVHB6WlhKMmFXTmxZV05qYjNWdWREcHJkV0psTFhONWMzUmxiVHByT0hOaFpHMXBiaUo5LnBlRGtqREp5UlliRDVfdFJQZDBKaDB5VEpSTDhNM3M1ZlJka1dYUWNDd2RGM0J3TE9mRVBPaWI0bEF3US1OcUptQS1sUlZ2dnQ4ZTVqU3h4UVVJSlRJYWp6S0RxaWYzQUpPVElYVEpmYy1vRGg1VUw2T01WdVJhdlhLNkZkbk1rd281RlhOSzVOYnl4WGx5RGdoaUJqWGc0ZUVXUWRLcXdKc05GYjBtcm15N2czb2NrZUsyQjZONzNDMWNwUGtIajRqTzY3bHl0OVFrQmtRRU1odGlNaURoZlJlTWFzLXF1ZFIzbGh3Z2xZUlNBbkdzelFOSnFKbFZxTzNUSkdEVjNmc0tUNVBlZFVHU1ZjM0JlQm1ZLXhyLUtyZFR5dE1rSk1rRUtoOHNOWEZ2UzN1MFowYjRRaVhwd296cjFkVlBkUnU4NDB2X2hkN1JPUkpOQkZ0UlV1dw==
    kind: Secret
    metadata:
    annotations:
        kubernetes.io/service-account.name: k8sadmin
        kubernetes.io/service-account.uid: d6c3c8e2-8d15-11e9-83ed-00163e149cf2
    creationTimestamp: "2019-06-12T13:27:34Z"
    name: k8sadmin-token-jn7xv
    namespace: kube-system
    resourceVersion: "487"
    selfLink: /api/v1/namespaces/kube-system/secrets/k8sadmin-token-jn7xv
    uid: d6c522cd-8d15-11e9-83ed-00163e149cf2
    type: kubernetes.io/service-account-token
    
    # 获取token值
    kubectl get secret -n kube-system | grep k8sadmin | cut -d " " -f1 | xargs -n 1 | xargs kubectl get secret  -o 'jsonpath={.data.token}' -n kube-system | base64 --decode
    
  • 客户端使用token

    kubectl -s https://10.x.x.x:6443 --token eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJrOHNhZG1pbi10b2tlbi1iNG1xcCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJrOHNhZG1pbiIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjVjN2RlYTVkLTUzYWMtMTFlOS1iMmNmLTUyNTQwMGZmNzI5YSIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDprdWJlLXN5c3RlbTprOHNhZG1pbiJ9.bpyv4SuQ1P-LyGIs5udfx74qonM21ickQA60og47HT5MHQDCypPyrsjBwq73mEzVvDmw6A42WKtBq_Tv4V7cT4fB-pnmxtkuESkmDdIr0FH7LCSyd6MIvIkqxkVvawy74AiWB7YbgbA1bJq5_btNPl8kPfFNz9SSPpKSnv3wc6Ln8kfvZRSWM-K_6sE4QQOhlcoshwqzCPSH9hU3HjkcYTg-QAry-IThjSLAoFiq4sgSsl95MG51sxPYhvM5hl-FLVGAehnJtlPlwc29BU5zfeCv64_hhLHIwYT74pLgVNe6O_McuhnreG5W2YcqxngFOUHuHrdogLlwRe_KELRS-Q  get nodes
    
    NAME         STATUS   ROLES    AGE   VERSION
    k8s-master   Ready    master   73d   v1.14.0
    

BootStrap Token认证

bootstrap token是kubeadm部署系统的时候,为了方便node加入到集群,从而提供了一个后门。该token对应的user拥有自动签发客户端CSR的权限,这样添加一台新节点的时候,就不用管理员到系统上面手动签发CSR请求了。

bootstrap-token都是以“bootstrap-token-”开头的名称,其中auth-extra-groups指定了其所属的group信息。clusterrolebinding也就是通过绑定clusterrole到该group,从而允许持有该token的kubeadm节点拥有签发证书的权限的。

  • token中的auth-extra-groups信息

    # 查看secret内容
    $ kcs get secret bootstrap-token-gb4kis -o yaml
    
    apiVersion: v1
    data:
    auth-extra-groups: c3lzdGVtOmJvb3RzdHJhcHBlcnM6a3ViZWFkbTpkZWZhdWx0LW5vZGUtdG9rZW4=
    description: VGhlIGRlZmF1bHQgYm9vdHN0cmFwIHRva2VuIGdlbmVyYXRlZCBieSAna3ViZWFkbSBpbml0Jy4=
    expiration: MjAxOS0wNi0xM1QyMToyNjozMyswODowMA==
    token-id: Z2I0a2lz
    token-secret: cW40M3d6ZGJzd2c4ZjdlOA==
    usage-bootstrap-authentication: dHJ1ZQ==
    usage-bootstrap-signing: dHJ1ZQ==
    kind: Secret
    metadata:
    creationTimestamp: "2019-06-12T13:26:33Z"
    name: bootstrap-token-gb4kis
    namespace: kube-system
    resourceVersion: "168"
    selfLink: /api/v1/namespaces/kube-system/secrets/bootstrap-token-gb4kis
    uid: b228ddbb-8d15-11e9-83ed-00163e149cf2
    type: bootstrap.kubernetes.io/token
    
    # 解出auth-extra-groups对应的base64编码信息为
    $ echo "c3lzdGVtOmJvb3RzdHJhcHBlcnM6a3ViZWFkbTpkZWZhdWx0LW5vZGUtdG9rZW4=" | base64 -d
    
    system:bootstrappers:kubeadm:default-node-token
    # 其中system:bootstrappers为group信息
    
    #接下来重点关注该group对应的clusterrolebinding
    $ kcs get clusterrolebinding | grep kubeadm
    
    kubeadm:kubelet-bootstrap                              14h
    kubeadm:node-autoapprove-bootstrap                     14h
    kubeadm:node-autoapprove-certificate-rotation          14h
    kubeadm:node-proxier                                   14h
    
  • 绑定的clusterrole

clusterrolebinding clusterrole
kubeadm:kubelet-bootstrap system:node-bootstrapper
kubeadm:node-autoapprove-bootstrap system:certificates.k8s.io:certificatesigningrequests:nodeclient
kubeadm:node-autoapprove-certificate-rotation system:certificates.k8s.io:certificatesigningrequests:selfnodeclient
kubeadm:node-proxier system:node-proxier

静态Token认证

api server参数:

–token-auth-file string

If set, the file that will be used to secure the secure port of the API server via token authentication.

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唯一标识客户端,只要api server存在该token,则认为认证通过,但是如果需要新增Token,则需要重启kube-apiserver组件。当通过客户端使用 bearer token 认证时,API服务器需要一个值为带有 Bearer THETOKEN 值的Authorization头。bearer token必须是一个字符序列,能够放在HTTP请求头中。

HTTP Basic Auth

api server参数:

–basic-auth-file string

If set, the file that will be used to admit requests to the secure port of the API server via http basic authentication.

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

静态密码文件是CSV格式的文件,每行对应一个用户的信息:

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

当Http客户端使用基础认证时,api server需要一个带有Basic BASE64ENCODED(USER:PASSWORD) 值的Authorization头。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

JavaScript语言精粹

JavaScript语言精粹

道格拉斯•克罗克福德 (Douglas Crockford) / 赵泽欣、鄢学鹍 / 电子工业出版社 / 2012-9-1 / 49.00元

JavaScript 曾是“世界上最被误解的语言”,因为它担负太多的特性,包括糟糕的交互和失败的设计,但随着Ajax 的到来,JavaScript“从最受误解的编程语言演变为最流行的语言”,这除了幸运之外,也证明了它其实是一门优秀的语言。Douglas Crockford 在本书中剥开了JavaScript 沾污的外衣,抽离出一个具有更好可靠性、可读性和可维护性的JavaScript 子集,让你看......一起来看看 《JavaScript语言精粹》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具