kubernetes如何使用自有的用户系统
kubernetes如何使用自有的用户系统
K8s 支持基于第三方的webhook token 的认证方式,有了这套机制可以可以让k8s使用公司现有的用户管理系统无需维护多套。
要支持webhook token 认证需要对api server 进行一下配置
-
--authentication-token-webhook-config-file
描述如何调用远程认证服务器的配置文件 -
--authentication-token-webhook-cache-ttl
认证的缓存时间,默认2分钟。
配置文件使用Kubeconfig 文件格式
# api 版本
apiVersion: v1
# api 类型
kind: Config
# 远程服务器信息
clusters:
- name: name-of-remote-authn-service
cluster:
certificate-authority: /path/to/ca.pem # 验证远程服务的CA
server: https://authn.example.com/authenticate # 远程服务器地址
# users 指代 API 服务的 Webhook 配置
users:
- name: name-of-api-server
user:
client-certificate: /path/to/cert.pem # Webhook 插件要使用的证书
client-key: /path/to/key.pem # 与证书匹配的**
# kubeconfig 文件需要一个上下文(Context),此上下文用于本 API 服务器
current-context: webhook
contexts:
- context:
cluster: name-of-remote-authn-service
user: name-of-api-server
name: webhook
当用户调用 api server 服务使用token 时,身份认证webhook 会用POST请求发送一个JSON 序列化的对象到远程服务器。该对象是 authentication.k8s.io/v1beta1
的 Token Review
对象,其中包含了Token 信息。
POST 请求的Body 如下:
{
"apiVersion": "authentication.k8s.io/v1beta1",
"kind": "TokenReview",
"spec": {
"token": "<持有者令牌>"
}
}
拿到了k8s 发送的请求 我们必须填充该请求的status 字段,标记该登录请求是否成功。如果成功 需要返回以下内容
{
"apiVersion": "authentication.k8s.io/v1beta1",
"kind": "TokenReview",
"status": {
"authenticated": true,
"user": {
"username": "[email protected]",
"uid": "42",
"groups": [
"developers",
"qa"
],
"extra": {
"extrafield1": [
"extravalue1",
"extravalue2"
]
}
}
}
}
若不成功 则将authenticated
置成false。
认证服务器
根据以上信息,我们就可以实现我们的认证服务器了。下面代码是一个基于github验证token的认证方式,根据上文:
STEP 1: 解析请求的body,并获取到token
STEP 2: 将获取的token 发送到github 进行认证
STEP 3: 将github返回的用户信息组装成 TokenReview 的status 并返回
package main
import (
"context"
"encoding/json"
"github.com/google/go-github/github"
"golang.org/x/oauth2"
authentication "k8s.io/api/authentication/v1"
"log"
"net/http"
)
func authenticate(w http.ResponseWriter,r *http.Request) {
// 1. 解析请求体中的body,获取token
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
}
// 2. token 发送到github 进行验证
log.Println("receiving request")
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: tr.Spec.Token},
)
cli := oauth2.NewClient(context.TODO(),ts)
client := github.NewClient(cli)
user, _, err := client.Users.Get(context.TODO(),"")
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,
},
})
}
// 3. 验证成功 获取用户信息,并填充到status 字段
log.Printf("[Success] login as %s", *user.Login)
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,
})
}
func main() {
http.HandleFunc("/authenticate", authenticate)
log.Fatal(http.ListenAndServe(":3000",nil))
}
配置api server
启动上述项目(本地地址为 8.11.0.186:3000);然后去修改api server 的配置,因为api server是以容器的方式运行,因此需要将配置文件进容器中。
创建存放配置文件的文件夹
mkdir /etc/config/
cd /etc/config/
编辑配置文件web hook-config.json
注意将server 地址修改为上述项目的地址
{
"kind": "Config",
"apiVersion": "v1",
"preferences": {},
"clusters": [
{
"name": "github-authn",
"cluster": {
"server": "http://8.11.0.186:3000/authenticate"
}
}
],
"users": [
{
"name": "authn-apiserver",
"user": {
"token": "secret"
}
}
],
"contexts": [
{
"name": "webhook",
"context": {
"cluster": "github-authn",
"user": "authn-apiserver"
}
}
],
"current-context": "webhook"
}
修改/etc/kubernetes/manifests/kube-apiserver.yaml
文件。 主要是将配置文件进来,并启动命令添加- --authentication-token-webhook-config-file=/etc/config/webhook-config.json
,全文如下。
apiVersion: v1
kind: Pod
metadata:
annotations:
kubeadm.kubernetes.io/kube-apiserver.advertise-address.endpoint: 8.15.0.7:6443
creationTimestamp: null
labels:
component: kube-apiserver
tier: control-plane
name: kube-apiserver
namespace: kube-system
spec:
containers:
- command:
- kube-apiserver
- --advertise-address=8.15.0.7
- --allow-privileged=true
- --authorization-mode=Node,RBAC
- --client-ca-file=/etc/kubernetes/pki/ca.crt
- --enable-admission-plugins=NodeRestriction
- --enable-bootstrap-token-auth=true
- --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt
- --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt
- --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key
- --etcd-servers=https://127.0.0.1:2379
- --insecure-port=0
- --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt
- --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
- --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt
- --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key
- --requestheader-allowed-names=front-proxy-client
- --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt
- --requestheader-extra-headers-prefix=X-Remote-Extra-
- --requestheader-group-headers=X-Remote-Group
- --requestheader-username-headers=X-Remote-User
- --secure-port=6443
- --service-account-key-file=/etc/kubernetes/pki/sa.pub
- --service-cluster-ip-range=10.96.0.0/16
- --runtime-config=authentication.k8s.io/v1beta1=true
- --authentication-token-webhook-config-file=/etc/config/webhook-config.json
- --tls-cert-file=/etc/kubernetes/pki/apiserver.crt
- --tls-private-key-file=/etc/kubernetes/pki/apiserver.key
image: registry.aliyuncs.com/k8sxio/kube-apiserver:v1.19.2
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 8
httpGet:
host: 8.15.0.7
path: /livez
port: 6443
scheme: HTTPS
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 15
name: kube-apiserver
readinessProbe:
failureThreshold: 3
httpGet:
host: 8.15.0.7
path: /readyz
port: 6443
scheme: HTTPS
periodSeconds: 1
timeoutSeconds: 15
resources:
requests:
cpu: 250m
startupProbe:
failureThreshold: 24
httpGet:
host: 8.15.0.7
path: /livez
port: 6443
scheme: HTTPS
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 15
volumeMounts:
- mountPath: /etc/ssl/certs
name: ca-certs
readOnly: true
- mountPath: /etc/pki
name: etc-pki
readOnly: true
- mountPath: /etc/kubernetes/pki
name: k8s-certs
readOnly: true
- mountPath: /etc/config
name: webhook-config
readOnly: true
hostNetwork: true
priorityClassName: system-node-critical
volumes:
- hostPath:
path: /etc/ssl/certs
type: DirectoryOrCreate
name: ca-certs
- hostPath:
path: /etc/pki
type: DirectoryOrCreate
name: etc-pki
- hostPath:
path: /etc/config
type: DirectoryOrCreate
name: webhook-config
- hostPath:
path: /etc/kubernetes/pki
type: DirectoryOrCreate
name: k8s-certs
验证
-
去github 申请一个token
-
修改kubeconfig 添加一个user
- name: vic user: token: xxxx
-
调用获取pod 命令。 报错的原因是没有给该用户配置权限。但k8s 确实识别出对应的用户了。创建个role 和rolebinding 绑定下就可以了,这里不再缀述。
$ kubectl get po --user vic
Error from server (Forbidden): pods is forbidden: User "Archer1A" cannot list resource "pods" in API group "" in the namespace "webhook-demo"