Skip to content

认证

认证

开启 TLS 时, 所有的请求都需要首先认证. Kubernetes 支持多种认证机制, 并支持同时开启多个认证插件(只要有一个认证通过即可). 如果认证成功, 则用户的 username 会传入授权模块做进一步授权验证; 而对于认证失败的请求则返回 HTTP 401.

认证插件

  • X509 证书(默认)
    • 使用 X509 客户端证书只需要 API Server 启动时配置 --client-ca-file=SOMEFILE. 在证书认证时, 其 CN 域用作用户名, 而组织机构域则用作 group 名.
  • 静态 Token 文件(最简单)
    • 使用静态 Token 文件认证只需要 API Server 启动时配置 --token-auth-file=SOMEFILE.
    • 该文件为 csv 格式, 每行至少包括三列 token, username, user id. token, user, uid, "group1, group2, group3"
  • 引导 Token
    • 为了支持平滑地启动引导新的集群, Kubernetes 包含了一种动态管理的持有者令牌类型, 称作启动引导令牌(Bootstrap Token).
    • 这些令牌以 Secret 的形式保存在 kube-system 名字空间中, 可以被动态管理和创建.
    • 控制器管理器包含的 TokenCleaner 控制器能够在启动引导令牌过期时将其删除.
    • 在使用 kubeadm 部署 Kubernetes 时, 可通过 kubeadmtoken list 命令查询.
  • 静态密码文件(类似 "静态 Token 文件")
    • 需要 API Server 启动时配置 --basic-auth-file=SOMEFILE, 文件格式为 csv , 每行至少三列 password, user, uid, 后面是可选的 group 名. password, user, uid, "group1, group2, group3"
  • ServiceAccount(系统账户, Kubernetes 自带的)
    • ServiceAccount 是 Kubernetes 自动生成的, 并会自动挂载到容器的 /run/secrets/kubernetes.io/serviceaccount 目录中.
      • /run/secrets/kubernetes.io/serviceaccount/{ca.crt,namespace,token}
  • OpenID
    • OAuth 2.0 的认证机制
  • Webhook 令牌身份认证(集成现有的认证平台,应用场景比较广)
    • --authentication-token-webhook-config-file 指向一个配置文件, 其中描述如何访问远程的 Webhook 服务.
    • --authentication-token-webhook-cache-ttl 用来设定身份认证决定的缓存时间. 默认时长为 2 分钟.
  • 匿名请求(不建议用)
    • 如果使用 AlwaysAllow 以外的认证模式, 则匿名请求默认开启, 但可用 --anonymous-auth=false 禁止匿名请求.

X509 证书

/etc/kubernetes/admin.conf

apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: (CA-data)
    server: https://192.168.1.218:6443 (服务器地址)
  name: kubernetes (集群名称)
contexts: (集群上下文)
- context: 
    cluster: kubernetes (连接哪个集群)
    user: kubernetes-admin (用哪个用户连接这个集群)
  name: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes
kind: Config
preferences: {}
users: (用户信息)
- name: kubernetes-admin (用户名)
  user:
    client-certificate-data: (用户的 cert-data)
    client-key-data: (用户的 key-data)

基于 webhook 的认证服务集成

https://github.com/kubeguard/guard

构建符合 Kubernetes 规范的认证服务

需要依照 Kubernetes 规范, 构建认证服务, 用来认证 tokenreview request

构建认证服务

  • 认证服务需要满足如下Kubernetes的规范
    • URL: https://authn.example.com/authenticate (webhook 服务地址)
    • Method: POST
  • Input:
{
    "apiVersion": "authentication.k8s.io/v1beta1", 
    "kind": "TokenReview", 
    "spec": 
    {
        "token": "(BEARERTOKEN)" 
    } 
}
  • Output:
{
    "apiVersion": "authentication.k8s.io/v1beta1",
    "kind": "TokenReview",
    "status": 
    {
        "authenticated": true,
        "user": 
        {
            "username": "janedoe@example.com ",
            "uid": "42",
            "groups": 
            [
                "developers",
                "qa"
            ]
        }
    }
}

开发认证服务

解码认证请求

decoder := json.NewDecoder(r.Body)
var tr authentication.TokenReview
err := decoder.Decode(&tr)
if err != nil {
    log.Println("[Error]", err.Error())
    w.WriteHeader(http.StatusBadRequest)
    json.NewEncoder(w).Encode(
        map[string]interface{}{
            "apiVersion": "authentication.k8s.io/v1beta1",
            "kind":       "TokenReview",
            "status": authentication.TokenReviewStatus{
                Authenticated: false,
            },
        }
    )
    return
}

转发认证请求至认证服务器

// Check User

ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: tr.Spec.Token},)
tc := oauth2.NewClient(oauth2.NoContext, ts)
client := github.NewClient(tc)
user, _, err := client.Users.Get(context.Background(), "")
if err != nil {
    log.Println("[Error]", err.Error())
    w.WriteHeader(http.StatusUnauthorized)
    json.NewEncoder(w).Encode(
        map[string]interface{}{
            "apiVersion": "authentication.k8s.io/v1beta1",
            "kind": "TokenReview",
            "status": authentication.TokenReviewStatus{
                Authenticated: false,
            },
        }
    )
    return
}

认证结果返回给APIServer

w.WriteHeader(http.StatusOK)
trs := authentication.TokenReviewStatus{
    Authenticated: true,
    User: authentication.UserInfo{
    Username: *user.Login,
    UID:      *user.Login,
    },
}
json.NewEncoder(w).Encode(map[string]interface{}{
    "apiVersion": "authentication.k8s.io/v1beta1",
    "kind":       "TokenReview",
    "status":     trs,
})

配置认证服务

{
    "kind": "Config",
    "apiVersion": "v1",
    "preferences": {},
    "clusters": [
        {
            "name": "github-authn",
            "cluster": {
                "server": "http://localhost:3000/authenticate"
            }
        }
    ],
    "users": [
        {
            "name": "authn-apiserver",
            "user": {
                "token": "secret"
            }
        }
    ],
    "contexts": [
        {
            "name": "webhook",
            "context": {
                "cluster": "github-authn",
                "user": "authn-apiserver"
            }
        }
    ],
    "current-context": "webhook"
}

配置apiserver

可以是任何认证系统

  • 但在用户认证完成后, 生成代表用户身份的 token
  • 该 token 通常是有失效时间的
  • 用户获取该 token 以后以后, 将 token 配置进 kubeconfig 修改 apiserver 设置, 开启认证服务, apiserver 保证将所有收到的请求中的 token 信息, 发给认证服务进行验证
  • --authentication-token-webhook-config-file, 该文件描述如何访问认证服务
  • --authentication-token-webhook-cache-ttl, 默认 2 分钟

配置文件需要 mount 进 Pod

配置文件中的服务器地址需要指向 authService

{
    "kind": "Config",
    "apiVersion": "v1",
    "preferences": {},
    "clusters": [
        {
            "name": "github-authn",
            "cluster": {
                "server": "http://localhost:3000/authenticate" 
            }
        }
    ],
    "users": [
        {
            "name": "authn-apiserver",
            "user": {
                "token": "secret"
            }
        }
    ],
    "contexts": [
        {
            "name": "webhook",
            "context": {
                "cluster": "github-authn",
                "user": "authn-apiserver"
            }
        }
    ],
    "current-context": "webhook"
}

生产系统中遇到的陷阱

基于 Keystone 的认证插件导致 Keystone 故障且无法恢复
Keystone 是企业关键服务
Kubernetes 以 Keystone 作为认证插件
Keystone 在出现故障后会抛出 401 错误
Kubernetes 发现 401 错误后会尝试重新认证
大多数 controller 都有指数级 back off, 重试间隔越来越慢
但 gophercloud 针对过期 token 会一直 retry
大量的 request 积压在 Keystone 导致服务无法恢复
Kubernetes 成为压死企业认证服务的最后一根稻草

解决方案?

  • Circuit break
  • Rate limit

一般用法

主要防止 api-server 启动时,webhook 没有启动成功。

  1. 运行成 api-server 的一个 sidecar
  2. 写在 /etc/kubernetes/manifests/webhook.yaml