关于jwt:KubeCube-用户管理与身份认证

5次阅读

共计 5897 个字符,预计需要花费 15 分钟才能阅读完成。

前言
KubeCube (https://kubecube.io) 是由网易数帆近期开源的一个轻量化的企业级容器平台,为企业提供 kubernetes 资源可视化治理以及对立的多集群多租户治理性能。KubeCube 社区将通过系列技术文章解读 KubeCube 的设计特点和技术实现,帮忙开发者和用户更快地了解和上手 KubeCube。本文是第三篇,重点介绍 KubeCube 中用户治理与身份认证的实现计划。

用户治理

所有 Kubernetes 集群都有两类用户:由 Kubernetes 治理的服务账号和普通用户。Kubernetes 假设普通用户是由一个与集群无关的服务通过以下形式之一进行治理的:* 负责散发私钥的管理员

* 相似 Keystone 或者 Google Accounts 这类用户数据库

* 蕴含用户名和明码列表的文件

有鉴于此,Kubernetes 并不蕴含用来代表普通用户账号的对象。普通用户的信息无奈通过 API 调用增加到集群中。

依据 Kubernetes 官网所述,Kubernetes 自身并不间接提供用户治理的个性,不反对普通用户对象,更不存储普通用户的任何信息。如果须要创立一个用户,须要为该用户创立私钥和证书,通过证书进行身份认证。并且,因为不存储用户信息,集群管理员无奈集中管理用户,对其余用户无感知。因而,KubeCube 首先从新定义了用户这一概念,即提供了 User 这一资源类型,存储用户信息,进行用户治理,同时不便后续的身份认证和权限校验等。

apiVersion: user.kubecube.io/v1
kind: User
metadata:
    name: 登录账号,用户惟一标识,用户自定义,不可反复,不可批改
spec:
    password: 明码,必填,零碎会将明码进行 md5 加盐加密后保留
    displayName: 用户名
    email: 邮箱
    phone: 电话
    language: 语言:en/ch
    loginType: 用户登录形式:normal/ldap/github/...
    state: 用户状态:normal/forbidden
status:
    lastLoginTime: 上次登录工夫
    lastLoginIp: 上次登录 IP

用户能够由管理员在前端页面手动创立,也能够在应用内部认证第一次登录时零碎主动创立。因而,用户在注册形式上能够分为零碎一般注册用户和第三方受权登录用户。但对于这两种创立形式,都是对应在管控集群创立相应的 User cr。而后 Warden 的资源同步管理器会将该 cr 从管控集群同步到计算集群,以便于后续多集群对立认证。

这样,在用户治理页面,只须要查问管控集群内的 User 资源,即可实现用户的集中管理。并且,能够轻松地增加用户、查问用户以及对用户元信息的批改。

身份认证

在 KubeCube 中,反对本地认证和内部认证。本地认证是指,在 KubeCube 中创立普通用户,用户再应用其创立时注册的用户名明码进行登录和认证。而内部认证,是指无需创立用户,通过第三方的认证平台认证用户身份,从而拜访 KubeCube。上面将别离介绍这两种认证形式的实现。

本地认证

在 KubeCube 中,次要是通过 JWT(JSON Web Token)进行用户的身份认证的。

在应用本地认证登录时,用户须要输出用户名和明码。KubeCube 会依据用户名在集群中查问 User cr 并比拟明码,如果查问到 User 并且明码统一,视为登录胜利。KubeCube 在更新用户登录状态后,会依据用户名生成 JWT 并拼接成 Bearer Token,存储在 cookie 中返回。

用户登录时校验用户名明码胜利后的代码如下:

  // generate token and return
    authJwtImpl := jwt.GetAuthJwtImpl()
    token, err := authJwtImpl.GenerateToken(&v1beta1.UserInfo{Username: name})
    if err != nil {response.FailReturn(c, errcode.AuthenticateError)
        return
    }
    bearerToken := jwt.BearerTokenPrefix + " " + token
    c.SetCookie(constants.AuthorizationHeader, bearerToken, int(authJwtImpl.TokenExpireDuration), "/", "", false, true)

    user.Spec.Password = ""
    response.SuccessReturn(c, user)
    return

用户胜利登录后,后续的每次申请,前端都会通过 cookie 带上该 JWT 进行申请,后端认证中间件再对该 JWT 进行校验。如果无效,则会生成新的 token 返回,循环上述过程。这样,即便 KubeCube 中生成 JWT 的默认无效工夫为 1 小时,只有用户继续拜访,JWT 便会一直刷新使用户始终处于登录状态。

认证中间件的局部代码如下:

func Auth() gin.HandlerFunc {return func(c *gin.Context) {if !withinWhiteList(c.Request.URL, c.Request.Method, whiteList) {authJwtImpl := jwt.GetAuthJwtImpl()
      userToken, err := token.GetTokenFromReq(c.Request)
      if err != nil {response.FailReturn(c, errcode.AuthenticateError)
        return
      }

      newToken, respInfo := authJwtImpl.RefreshToken(userToken)
      if respInfo != nil {response.FailReturn(c, errcode.AuthenticateError)
        return
      }

      v := jwt.BearerTokenPrefix + " " + newToken

      c.Request.Header.Set(constants.AuthorizationHeader, v)
      c.SetCookie(constants.AuthorizationHeader, v, int(authJwtImpl.TokenExpireDuration), "/", "", false, true)
            c.Next()}
    }
}

内部认证

内部认证的实现目前次要分为 3 种,别离为通用认证、LDAP 认证和 OAuth2 认证。

通用认证

为了不便用户能够对接一套本人的认证零碎,KubeCube 中反对了一种通用认证形式。用户能够通过开启通用认证形式以及配置认证零碎的地址,使用户在每一次拜访 KubeCube 时,都会去本人的认证零碎中进行认证。认证通过后,须要返回给 KubeCube 该用户的用户名,KubeCube 仍然会依据该用户名生成对应的 Bearer Token 放在 header 中,以进行后续的权限校验等。次要的逻辑代码实现如下:

func Auth() gin.HandlerFunc {return func(c *gin.Context) {if !withinWhiteList(c.Request.URL, c.Request.Method, whiteList) {authJwtImpl := jwt.GetAuthJwtImpl()
            if generic.Config.GenericAuthIsEnable {h := generic.GetProvider()
                user, err := h.Authenticate(c.Request.Header)
                if err != nil || user == nil {clog.Error("generic auth error: %v", err)
                    response.FailReturn(c, errcode.AuthenticateError)
                    return
                }
                newToken, error := authJwtImpl.GenerateToken(&v1beta1.UserInfo{Username: user.GetUserName()})
                if error != nil {response.FailReturn(c, errcode.AuthenticateError)
                    return
                }
                b := jwt.BearerTokenPrefix + " " + newToken
                c.Request.Header.Set(constants.AuthorizationHeader, b)
            }
            c.Next()}
    }
}

LDAP 认证

  1. 当用户抉择 LDAP 登录形式时,用户输出用户名和明码。首先会查看集群内是否存在该用户,并且该用户是否为“禁用”状态。如果不存在或存在且为失常状态,则开始进行 LDAP 认证
  2. KubeCube 作为 LDAP 客户端,获取到用户的用户名和明码,以管理员 DN 和管理员明码为参数向 LDAP 服务器发送管理员绑定申请报文以取得查问权限。
  3. LDAP 服务器收到管理员绑定申请报文后,验证管理员 DN 和管理员明码是否正确。如果管理员 DN 和管理员明码正确,则向 KubeCube 发送绑定胜利的管理员绑定响应报文。
  4. KubeCube 收到绑定响应报文后,以用户输出的用户名为参数结构过滤条件,向 LDAP 服务器发送用户 DN 查问申请报文。例如:结构过滤条件为 CN=User2。
  5. LDAP 服务器收到用户 DN 查问申请报文后,依据报文中的查问终点、查问范畴、以及过滤条件,对用户 DN 进行查找。如果查问胜利,则向 KubeCube 发送查问胜利的响应报文。查问失去的用户 DN 能够是一个或多个。如果失去的用户不为一个,认为用户名或明码谬误,认证失败。
  6. KubeCube 依据查问失去的用户 DN 和用户输出的明码为参数,向 LDAP 服务器发送用户绑定申请报文。
  7. LDAP 服务器收到用户绑定申请报文后,检查用户输出的明码是否正确。
  • 如果用户输出的明码正确,则向 KubeCube 发送绑定胜利的绑定响应报文。
  • 如果用户输出的明码不正确,则向 KubeCube 发送绑定失败的响应报文。KubeCube 以查问到的下一个用户 DN 为参数,持续向 LDAP 服务器发送绑定申请,直至有一个 DN 绑定胜利。如果所有用户 DN 都绑定失败,则 KubeCube 告诉用户认证失败。

认证胜利后,和本地认证的逻辑雷同:如果该用户在集群中未存在,则依据用户名创立 User cr;并且依据该用户名生成对应的 Bearer Token 存储到 Cookie 中,在下次申请时携带以辨认用户身份。

OAuth2 认证

在 KubeCube 中 OAuth2 认证采纳受权码模式,因为该模式是性能最残缺、流程最紧密的受权模式。OAuth2 通常的认证流程为:

  1. 用户拜访客户端,后者将前者导向认证服务器。
  2. 用户抉择是否给予客户端受权。
  3. 假如用户给予受权,认证服务器将用户导向客户端当时指定的 ” 重定向 URI”(redirection URI),同时附上一个受权码。
  4. 客户端收到受权码,附上新近的 ” 重定向 URI”,向认证服务器申请令牌。这一步是在客户端的后盾的服务器上实现的,对用户不可见。
  5. 认证服务器核查了受权码和重定向 URI,确认无误后,向客户端发送拜访令牌(access token)和更新令牌(refresh token)。

在 KubeCube 的实现中,以 GitHub 登录为例:

用户在登录时抉择 GitHub 认证登录,前端将申请转发给 GitHub;
GitHub 询问用户是否批准受权给 KubeCube;
如果用户批准,GitHub 就会重定向回 KubeCube(/oauth/redirect),同时发回一个受权码(code);
KubeCube 应用受权码,向 GitHub 申请令牌(access_token);
GitHub 返回令牌(access_token);
KubeCube 应用令牌(access_token),向 GitHub 申请用户信息数据;
查问集群,如果该用户不存在,则依据用户信息创立 User cr;
依据用户名生成拜访集群的 Bearer Token,并返回认证胜利;
前端将 Bearer Token 存储到 Cookie 中,在下次申请时携带。
OpenAPI 认证
基于以上的设计方案,能够轻松的推断出,OpenAPI 的认证实现,也是通过 JWT 实现。User 和每组 AK、SK 进行绑定,通过 AK、SK 查问到对应的 User,再通过该 User.Name 生成 Bearer Token 返回。在下次申请时,用户须要在 Cookie 或 header 中携带该 Token,KubeCube 认证中间件就能够通过该 Token 解析出用户身份,从而实现认证。

集群认证
在中间件实现身份认证后会 refresh token,然而如果间接在申请头中携带该 token 申请 kube-apiserver 来实现集群认证,则须要在部署 KubeCube 时批改 kube-apiserver 的认证后端,即批改 kube-apiserver 的配置。这会对原生的 kubernetes 集群造成侵入,大大增加 KubeCube 的部署老本和运维老本。因而,咱们须要建设另一模块来帮忙实现集群认证——auth-proxy。

用户对 KubeCube 进行拜访申请 kubernetes 资源时,在通过认证中间件进入到透传接口后,会走到 auth-proxy 模块;auth-proxy 将 request 中的 Bearer Token 解析为对应的 User;再应用 User impersonation 的形式,将 request 代理发送至 kube-apiserver,即应用“admin”用户伪装成以后用户来申请 kube-apiserver,从而“跳过”认证,并且有利于后续鉴权。

结语
KubeCube 的用户管理系统次要基于 User CRD 实现;认证零碎反对了本地和内部两种认证形式,本地认证基于 JWT 实现,内部认证在第三方认证平台认证通过后同样须要在集群内创立一个 User cr,以进行后续的用户治理、权限绑定等。对于集群认证,次要应用了 Kubernetes 提供的 Impersonation 办法“跳过认证”。整体设计和实现绝对简略,秉承了 KubeCube 轻量化的设计理念。

更多信息请参阅:

KubeCube 官网:https://www.kubecube.io/

KubeCube 源码:https://github.com/kubecube-i…

深刻解读 KubeCube 多集群治理

KubeCube 多级租户模型

KubeCube 开源:简化 Kubernetes 落地的六大个性

网易数帆更多开源我的项目

作者简介:嘉慧,网易数帆高级工程师,KubeCube 社区核心成员

正文完
 0