分类目录归档:分布式

Kubernetes中的RBAC

  Kubernetes中,授权有ABAC(基于属性的访问控制)、RBAC(基于角色的访问控制)、Webhook、Node、AlwaysDeny(一直拒绝)和AlwaysAllow(一直允许)这6种模式。需要在kube-apiserver设置–authorization-mode=RBAC参数,启用RABC模式,下面的操作版本为v1.10.1;
  当应用没有指定serviceAccountName,它将使用default服务帐户。

  在RABC API中,通过如下的步骤进行授权:
  1)定义角色:定义角色时会指定此角色对于资源的访问控制的规则;
  2)定义主体:用户、组和服务帐户
  3)绑定角色:将主体与角色进行绑定,对主体进行访问授权。

        enter image description here
                    RBAC API中的对象关系图

  Kubernetes中角色包含代表权限集合的规则,权限只有被授予,没有被拒绝的设置。
  在Kubernetes中有两类角色:普通角色和集群角色。
可以通过Role定义在一个命名空间中的角色,或是使用ClusterRole定义集群范围的角色。

普通角色只能被授予访问单一命令空间中的资源。
集群角色(ClusterRole)能够被授予资源权限有:集群范围资源(Node、NameSpace)、非资源端点(/healthz)、集群所有命名空间资源(跨名称空间);

角色绑定和集群角色绑定
  角色绑定用于将角色与一个主体进行绑定,从而实现将对主体授权的目的,主体分为用户、组和服务帐户。
角色绑定分为:普通角色绑定和集群角色绑定

角色绑定中不同主体定义有:
名称为 demo 用户:

 subjects:
 - kind:User
   name:"demo"
   apiGroup:rbac.authorization.k8s.io

名称为 demo 组:

 subjects:
 - kind:Group
   name:"demo-group"
   apiGroup:rbac.authorization.k8s.io

kube-system命名空间中,名称为default的服务帐户

 subjects:
 - kind:ServiceAccount
   name:default
   namespace:kube-system

so命名空间中,所有的服务帐户:

 subjects:
 - kind:Group
   name:system:serviceaccounts:so
   apiGroup:rbac.authorization.k8s.io

所有的服务帐户:

 subjects:
 - kind:Group
   name:system:serviceaccounts
   apiGroup:rbac.authorization.k8s.io

所有用户:

 subjects:
 - kind:Group
   name:system:authenticated    #授权用户
   apiGroup:rbac.authorization.k8s.io
 - kind:Group
   name:system:unauthenticated  #未授权用户
   apiGroup:rbac.authorization.k8s.io

授予cluster-admin集群角色给admin用户:

 kubectl create clusterrolebinding admin-cluster-admin-binding --clusterrole=cluster-admin --user=admin  

授予cluster-admin集群角色给so名称空间中的app服务帐户:

 kubectl create clusterrolebinding app-admin-binding --clusterrole=cluster-admin --serviceaccount=so:app  

  RBAC实例demo下面创建solinx-service-account.yml文件包含以下内容:

 # 服务账号
 apiVersion: v1
 kind: ServiceAccount   
 metadata:
   name: solinx
 # 角色
 ---
 kind: Role
 apiVersion: rbac.authorization.k8s.io/v1beta1
 metadata:
   name: solinx
 rules:                #规则
 - apiGroups: [""]       # 所有核心api
   resources: ["pods"]   # 资源
   verbs: ["create","delete","get","list","patch","update","watch"]  #操作
 - apiGroups: [""]
   resources: ["namespaces"]
   verbs: ["create","delete","get","list","patch","update","watch"]
 # 角色绑定
 ---
 apiVersion: rbac.authorization.k8s.io/v1beta1
 kind: RoleBinding
 metadata:
   name: solinx
 roleRef:     # 上面定义的角色
   apiGroup: rbac.authorization.k8s.io
   kind: Role
   name: solinx
 subjects:   # 上面定义的服务账户
 - kind: ServiceAccount
   name: solinx
   namespace: default

  上面定义了一个服务账户solinx、普通角色solinx,并将该服务账户与角色进行绑定,该角色定义了对了pod的增删查改等操作,所以该服务账户也具备了该权限;
  这里使用solinx服务账户对资源进行操作:

 kubectl get po --as system:serviceaccount:default:solinx

enter image description here

由于只授予了pod的操作权限,当访问service资源时被拒绝:

 kubectl get svc --as system:serviceaccount:default:solinx

 Error from server (Forbidden): services is forbidden: User "system:serviceaccount:default:solinx" cannot list services in the namespace "default"

enter image description here

该角色为普通角色Role,当访问集群资源NameSpace时同样被拒绝:

 kubectl get ns --as system:serviceaccount:default:solinx

 Error from server (Forbidden): namespaces is forbidden: User "system:serviceaccount:default:solinx" cannot list namespaces at the cluster scope

enter image description here

  此时把角色改为集群角色:ClusterRole,并重新绑定服务账户:
enter image description here

  此时该服务账户已经具备集群角色权限,访问集群资源:NameSpace

 kubectl get ns --as system:serviceaccount:default:solinx

enter image description here

参考资料: https://kubernetes.io/docs/reference/access-authn-authz/rbac/

图解Raft之日志复制

  日志复制可以说是Raft集群的核心之一,保证了Raft数据的一致性,下面通过几张图片介绍Raft集群中日志复制的逻辑与流程;

enter image description here

  在一个Raft集群中只有Leader节点能够接受客户端的请求,由Leader向其他Follower转发所有请求日志,并且有那么两条规则:Leader不删除任何日志、Follower只接收Leader所发送的日志信息;

enter image description here

  此图介绍了Raft集群中日志的组成结构,日志由序号与条目组成,每个条目又由任期与指令组成,committed范围内为已提交的日志是指过半节点已经接收并存储的日志;

enter image description here

  上图从整个上介绍了Raft集群的日志复制流程,Leader接收到指令后写入到本地日志,在随后的心跳中(AppendEntries)往其他追随者发送该条目,等待收到过半追随者响应后将该条目标志位已提交状态,并发往状态机执行,完成后返回结果给客户端;在后续心跳包(AppendEntries)中通知所有追随者哪些条目为已提交状态,以便追随者更新在自己状态机中执行该指令; 只有Leader能够接受客户端的指令,追随者只能够接收领导者的AppendEntries请求;

enter image description here

  在Raft集群中可通过条目索引号、任期号唯一确定一个条目,该条目前序所有条目也是一致的,如上图中索引号为5的条目为已提交状态的条目,则从索引号1到5的所有条目均为已提交的状态;

enter image description here

  上图中Leader发送AppendEntries请求时带有其前序索引位置4、前序任期号2,发往Follower1、Follower2;
  Follower1由于前序索引与前序任期能匹配本地条目所以将会接受该请求;
  Follower2由于前序索引与前序任期未能够匹配所以拒绝该请求;

enter image description here

  Raft处理日志不一致的情况是通过强制追随者复制领导者日志来调整日志一致性的,所以当追随者与领导者出现日志不一致时,追随者日志将会被领导者日志覆盖;

  要使领导者与追随者保持一致性的状态,需要两者找到一致性的位置,删除追随者该位置之后所有日志条目,发送领导者日志给追随者;
  领导者通过在每一个追随者维护了一个 nextIndex,表示下一个需要发送给跟随者的日志条目索引地址,领导者刚获得选举时,初始化所有 nextIndex 值为自己的最后一条日志的index加1;当追随者的日志和领导者不一致,那在下一次的AppendEntries时的一致性检查会失败,被追随者拒绝后,领导者就会减小 nextIndex 值进行重试,nextIndex 会在某位置使领导者和追随者日志达成一致。
  当日志达成一致时,追随者会接受该AppendEntries请求,这时追随者冲突的日志条目将全部被领导者的日志所覆盖。一旦AppendEntries成功,那么跟随者的日志就会和领导人保持一致,并且在接下来的任期里一直继续保持。

参考资料:
http://ramcloud.stanford.edu/raft.pdf