成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

K8S自定義Webhook實現認證管理

運維 系統運維
在Kubernetes中,APIServer是整個集群的中樞神經,它不僅連接了各個模塊,更是為整個集群提供了訪問控制能力。這篇文章主要和大家討論認證環節。

[[439204]]

大家好,我是喬克。

在Kubernetes中,APIServer是整個集群的中樞神經,它不僅連接了各個模塊,更是為整個集群提供了訪問控制能力。

Kubernetes API的每個請求都要經過多階段的訪問控制才會被接受,包括認證、授權、準入,如下所示。

客戶端(普通賬戶、ServiceAccount等)想要訪問Kubernetes中的資源,需要通過經過APIServer的三大步驟才能正常訪問,三大步驟如下:

  1. Authentication 認證階段:判斷請求用戶是否為能夠訪問集群的合法用戶。如果用戶是個非法用戶,那 apiserver會返回一個 401 的狀態碼,并終止該請求;
  2. 如果用戶合法的話,我們的 apiserver 會進入到訪問控制的第二階段 Authorization:授權階段。在該階段中apiserver 會判斷用戶是否有權限進行請求中的操作。如果無權進行操作,apiserver 會返回 403的狀態碼,并同樣終止該請求;
  3. 如果用戶有權進行該操作的話,訪問控制會進入到第三個階段:AdmissionControl。在該階段中 apiserver 的admission controller 會判斷請求是否是一個安全合規的請求。如果最終驗證通過的話,訪問控制流程才會結束。

這篇文章主要和大家討論認證環節。

認證

Kubernetes中支持多種認證機制,也支持多種認證插件,在認證過程中,只要一個通過則表示認證通過。

常用的認證插件有:

  • X509證書
  • 靜態Token
  • ServiceAccount
  • OpenID
  • Webhook
  • .....

這里不會把每種認證插件都介紹一下,主要講講Webhook的使用場景。

在企業中,大部分都會有自己的賬戶中心,用于管理員工的賬戶以及權限,而在K8s集群中,也需要進行賬戶管理,如果能直接使用現有的賬戶系統是不是會方便很多?

K8s的Webhook就可以實現這種需求,Webhook是一個HTTP回調,通過一個條件觸發HTTP POST請求發送到Webhook 服務端,服務端根據請求數據進行處理。

下面就帶大家從0到1開發一個認證服務。

開發Webhook

簡介

WebHook的功能主要是接收APIServer的認證請求,然后調用不同的認證服務進行認證,如下所示。

這里只是做一個Webhook的例子,目前主要實現了Github和LDAP認證,當然,認證部分的功能比較單一,沒有考慮復雜的場景。

Webhook開發

開發環境

構建符合規范的Webhook

在開發Webhook的時候,需要符合Kubernetes的規范,具體如下:

  • URL:https://auth.example.com/auth
  • Method:POST
  • Input參數
  1.   "apiVersion""authentication.k8s.io/v1beta1"
  2.   "kind""TokenReview"
  3.   "spec": { 
  4.     "token""<持有者令牌>" 
  5.   } 
  • Output參數

如果成功會返回:

  1.   "apiVersion""authentication.k8s.io/v1beta1"
  2.   "kind""TokenReview"
  3.   "status": { 
  4.     "authenticated"true
  5.     "user": { 
  6.       "username""janedoe@example.com"
  7.       "uid""42"
  8.       "groups": [ 
  9.         "developers"
  10.         "qa" 
  11.       ], 
  12.       "extra": { 
  13.         "extrafield1": [ 
  14.           "extravalue1"
  15.           "extravalue2" 
  16.         ] 
  17.       } 
  18.     } 
  19.   } 

如果不成功,會返回:

  1.   "apiVersion""authentication.k8s.io/v1beta1"
  2.   "kind""TokenReview"
  3.   "status": { 
  4.     "authenticated"false 
  5.   } 

遠程服務應該會填充請求的 status 字段,以標明登錄操作是否成功。

開發認證服務

(1)創建項目并初始化go mod

  1. # mkdir kubernetes-auth-webhook 
  2. # cd kubernetes-auth-webhook 
  3. # go mod init 

(2)在項目根目錄下創建webhook.go,寫入如下內容

  1. package main 
  2.  
  3. import ( 
  4.  "encoding/json" 
  5.  "github.com/golang/glog" 
  6.  authentication "k8s.io/api/authentication/v1beta1" 
  7.  "k8s.io/klog/v2" 
  8.  "net/http" 
  9.  "strings" 
  10.  
  11. type WebHookServer struct { 
  12.  server *http.Server 
  13.  
  14. func (ctx *WebHookServer) serve(w http.ResponseWriter, r *http.Request) { 
  15.  // 從APIServer中取出body 
  16.  // 將body進行拆分, 取出type 
  17.  // 根據type, 取出不同的認證數據 
  18.  var req authentication.TokenReview 
  19.  decoder := json.NewDecoder(r.Body) 
  20.  err := decoder.Decode(&req) 
  21.  if err != nil { 
  22.   klog.Error(err, "decoder request body error."
  23.   req.Status = authentication.TokenReviewStatus{Authenticated: false
  24.   w.WriteHeader(http.StatusUnauthorized) 
  25.   _ = json.NewEncoder(w).Encode(req) 
  26.   return 
  27.  } 
  28.  // 判斷token是否包含':' 
  29.  // 如果不包含,則返回認證失敗 
  30.  if !(strings.Contains(req.Spec.Token, ":")) { 
  31.   klog.Error(err, "token invalied."
  32.   req.Status = authentication.TokenReviewStatus{Authenticated: false
  33.   //req.Status = map[string]interface{}{"authenticated"false
  34.   w.WriteHeader(http.StatusUnauthorized) 
  35.   _ = json.NewEncoder(w).Encode(req) 
  36.   return 
  37.  } 
  38.  // split token, 獲取type 
  39.  tokenSlice := strings.SplitN(req.Spec.Token, ":", -1) 
  40.  glog.Infof("tokenSlice: ", tokenSlice) 
  41.  hookType := tokenSlice[0] 
  42.  switch hookType { 
  43.  case "github"
  44.   githubToken := tokenSlice[1] 
  45.   err := authByGithub(githubToken) 
  46.   if err != nil { 
  47.    klog.Error(err, "auth by github error"
  48.    req.Status = authentication.TokenReviewStatus{Authenticated: false
  49.    w.WriteHeader(http.StatusUnauthorized) 
  50.    _ = json.NewEncoder(w).Encode(req) 
  51.    return 
  52.   } 
  53.   klog.Info("auth by github success"
  54.   req.Status = authentication.TokenReviewStatus{Authenticated: true
  55.   w.WriteHeader(http.StatusOK) 
  56.   _ = json.NewEncoder(w).Encode(req) 
  57.   return 
  58.  case "ldap"
  59.   username := tokenSlice[1] 
  60.   password := tokenSlice[2] 
  61.   err := authByLdap(username, password
  62.   if err != nil { 
  63.    klog.Error(err, "auth by ldap error"
  64.    req.Status = authentication.TokenReviewStatus{Authenticated: false
  65.    //req.Status = map[string]interface{}{"authenticated"false
  66.    w.WriteHeader(http.StatusUnauthorized) 
  67.    _ = json.NewEncoder(w).Encode(req) 
  68.    return 
  69.   } 
  70.   klog.Info("auth by ldap success"
  71.   req.Status = authentication.TokenReviewStatus{Authenticated: true
  72.   //req.Status = map[string]interface{}{"authenticated"true
  73.   w.WriteHeader(http.StatusOK) 
  74.   _ = json.NewEncoder(w).Encode(req) 
  75.   return 
  76.  } 

主要是解析認證的請求Token,然后將Token進行拆分判斷是需要什么認證,Token的樣例如下:

  • Github認證:github:
  • LDAP認證:ldap::

這樣就可以獲取到用戶想用哪種認證,再掉具體的認證服務進行處理。

(3)創建github.go,提供github認證方法

  1. package main 
  2.  
  3. import ( 
  4.  "context" 
  5.  "github.com/golang/glog" 
  6.  "github.com/google/go-github/github" 
  7.  "golang.org/x/oauth2" 
  8.  
  9. func authByGithub(token string) (err error) { 
  10.  glog.V(2).Info("start auth by github......"
  11.  tokenSource := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token}) 
  12.  tokenClient := oauth2.NewClient(context.Background(), tokenSource) 
  13.  githubClient := github.NewClient(tokenClient) 
  14.  _, _, err = githubClient.Users.Get(context.Background(), ""
  15.  if err != nil { 
  16.   return err 
  17.  } 
  18.  return nil 

可以看到,這里僅僅做了一個簡單的Token認證,認證的結果比較粗暴,如果err=nil,則表示認證成功。

(4)創建ldap.go,提供ldap認證

  1. package main 
  2.  
  3. import ( 
  4.  "crypto/tls" 
  5.  "errors" 
  6.  "fmt" 
  7.  "github.com/go-ldap/ldap/v3" 
  8.  "github.com/golang/glog" 
  9.  "k8s.io/klog/v2" 
  10.  "strings" 
  11.  
  12. var ( 
  13.  ldapUrl = "ldap://" + "192.168.100.179:389" 
  14.  
  15. func authByLdap(username, password string) error { 
  16.  groups, err := getLdapGroups(username, password
  17.  if err != nil { 
  18.   return err 
  19.  } 
  20.  if len(groups) > 0 { 
  21.   return nil 
  22.  } 
  23.  
  24.  return fmt.Errorf("No matching group or user attribute. Authentication rejected, Username: %s", username) 
  25.  
  26. // 獲取user的groups 
  27. func getLdapGroups(username, password string) ([]string, error) { 
  28.  glog.Info("username:password", username, ":"password
  29.  var groups []string 
  30.  
  31.  config := &tls.Config{InsecureSkipVerify: true
  32.  ldapConn, err := ldap.DialURL(ldapUrl, ldap.DialWithTLSConfig(config)) 
  33.  if err != nil { 
  34.   glog.V(4).Info("dial ldap failed, err: ", err) 
  35.   return groups, err 
  36.  } 
  37.  defer ldapConn.Close() 
  38.  
  39.  binduser := fmt.Sprintf("CN=%s,ou=People,dc=demo,dc=com", username) 
  40.  
  41.  err = ldapConn.Bind(binduser, password
  42.  if err != nil { 
  43.   klog.V(4).ErrorS(err, "bind user to ldap error"
  44.   return groups, err 
  45.  } 
  46.  
  47.  // 查詢用戶成員 
  48.  searchString := fmt.Sprintf("(&(objectClass=person)(cn=%s))", username) 
  49.  memberSearchAttribute := "memberOf" 
  50.  searchRequest := ldap.NewSearchRequest( 
  51.   "dc=demo,dc=com"
  52.   ldap.ScopeWholeSubtree, 
  53.   ldap.NeverDerefAliases, 
  54.   0, 
  55.   0, 
  56.   false
  57.   searchString, 
  58.   []string{memberSearchAttribute}, 
  59.   nil, 
  60.  ) 
  61.  searchResult, err := ldapConn.Search(searchRequest) 
  62.  if err != nil { 
  63.   klog.V(4).ErrorS(err, "search user properties error"
  64.   return groups, err 
  65.  } 
  66.  // 如果沒有查到結果,返回失敗 
  67.  if len(searchResult.Entries[0].Attributes) < 1 { 
  68.   return groups, errors.New("no user in ldap"
  69.  } 
  70.  entry := searchResult.Entries[0] 
  71.  for _, e := range entry.Attributes { 
  72.   for _, attr := range e.Values { 
  73.    groupList := strings.Split(attr, ","
  74.    for _, g := range groupList { 
  75.     if strings.HasPrefix(g, "cn=") { 
  76.      group := strings.Split(g, "="
  77.      groups = append(groups, group[1]) 
  78.     } 
  79.    } 
  80.   } 
  81.  } 
  82.  return groups, nil 

這里的用戶名是固定了的,所以不適合其他場景。

(5)創建main.go入口函數

  1. package main 
  2.  
  3. import ( 
  4.  "context" 
  5.  "flag" 
  6.  "fmt" 
  7.  "github.com/golang/glog" 
  8.  "net/http" 
  9.  "os" 
  10.  "os/signal" 
  11.  "syscall" 
  12.  
  13. var port string 
  14.  
  15. func main() { 
  16.  flag.StringVar(&port, "port""9999""http server port"
  17.  flag.Parse() 
  18.  // 啟動httpserver 
  19.  wbsrv := WebHookServer{server: &http.Server{ 
  20.   Addr: fmt.Sprintf(":%v", port), 
  21.  }} 
  22.  mux := http.NewServeMux() 
  23.  mux.HandleFunc("/auth", wbsrv.serve) 
  24.  wbsrv.server.Handler = mux 
  25.  
  26.  // 啟動協程來處理 
  27.  go func() { 
  28.   if err := wbsrv.server.ListenAndServe(); err != nil && err != http.ErrServerClosed { 
  29.    glog.Errorf("Failed to listen and serve webhook server: %v", err) 
  30.   } 
  31.  }() 
  32.  
  33.  glog.Info("Server started"
  34.  
  35.  // 優雅退出 
  36.  signalChan := make(chan os.Signal, 1) 
  37.  signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM) 
  38.  <-signalChan 
  39.  
  40.  glog.Infof("Got OS shutdown signal, shutting down webhook server gracefully..."
  41.  _ = wbsrv.server.Shutdown(context.Background()) 

到此整個認證服務就開發完畢了,是不是很簡單?

Webhook測試

APIServer添加認證服務

使用Webhook進行認證,需要在kube-apiserver里開啟,參數如下:

  • --authentication-token-webhook-config-file 指向一個配置文件,其中描述 如何訪問遠程的 Webhook 服務
  • --authentication-token-webhook-config-file 指向一個配置文件,其中描述 如何訪問遠程的 Webhook 服務

配置文件使用 kubeconfig 文件的格式。文件中,clusters 指代遠程服務,users 指代遠程 API 服務 Webhook。配置如下:

(1)、將配置文件放到相應的目錄

  1. # mkdir /etc/kubernetes/webhook 
  2. # cat >> webhook-config.json <EOF 
  3.   "kind""Config"
  4.   "apiVersion""v1"
  5.   "preferences": {}, 
  6.   "clusters": [ 
  7.     { 
  8.       "name""github-authn"
  9.       "cluster": { 
  10.         "server""http://10.0.4.9:9999/auth" 
  11.       } 
  12.     } 
  13.   ], 
  14.   "users": [ 
  15.     { 
  16.       "name""authn-apiserver"
  17.       "user": { 
  18.         "token""secret" 
  19.       } 
  20.     } 
  21.   ], 
  22.   "contexts": [ 
  23.     { 
  24.       "name""webhook"
  25.       "context": { 
  26.         "cluster""github-authn"
  27.         "user""authn-apiserver" 
  28.       } 
  29.     } 
  30.   ], 
  31.   "current-context""webhook" 
  32. EOF 

 (2)在kube-apiserver中添加配置參數

  1. # mkdir /etc/kubernetes/backup 
  2. # cp /etc/kubernetes/manifests/kube-apiserver.yaml /etc/kubernetes/backup/kube-apiserver.yaml 
  3. # cd /etc/kubernetes/manifests/ 
  4. # cat kube-apiserver.yaml 
  5. apiVersion: v1 
  6. kind: Pod 
  7. metadata: 
  8.   annotations: 
  9.     kubeadm.kubernetes.io/kube-apiserver.advertise-address.endpoint: 10.0.4.9:6443 
  10.   creationTimestamp: null 
  11.   labels: 
  12.     component: kube-apiserver 
  13.     tier: control-plane 
  14.   name: kube-apiserver 
  15.   namespace: kube-system 
  16. spec: 
  17.   containers: 
  18.   - command: 
  19.     - kube-apiserver 
  20.     - ...... 
  21.     - --authentication-token-webhook-config-file=/etc/config/webhook-config.json 
  22.     image: registry.cn-hangzhou.aliyuncs.com/google_containers/kube-apiserver:v1.22.0 
  23.     imagePullPolicy: IfNotPresent 
  24.     ...... 
  25.     volumeMounts: 
  26.     ...... 
  27.     - name: webhook-config 
  28.       mountPath: /etc/config 
  29.       readOnly: true 
  30.   hostNetwork: true 
  31.   priorityClassName: system-node-critical 
  32.   securityContext: 
  33.     seccompProfile: 
  34.       type: RuntimeDefault 
  35.   volumes: 
  36.   ...... 
  37.   - hostPath: 
  38.       path: /etc/kubernetes/webhook 
  39.       type: DirectoryOrCreate 
  40.     name: webhook-config 
  41. status: {} 

ps: 為了節約篇幅,上面省略了部分配置。

當修改完過后,kube-apiserver會自動重啟。

測試Github認證

(1)在github上獲取Token,操作如圖所示

(2)配置kubeconfig,添加user

  1. # cat ~/.kube/config  
  2. apiVersion: v1 
  3. ...... 
  4. users: 
  5. name: joker 
  6.   user
  7.     token: github:ghp_jevHquU4g43m46nczWS0ojxxxxxxxxx 

(3)用Joker用戶進行訪問

返回結果如下,至于報錯是因為用戶的權限不足。

  1. # kubectl get po --user=joker 
  2. Error from server (Forbidden): pods is forbidden: User "" cannot list resource "pods" in API group "" in the namespace "default" 

可以在webhook上看到日志信息,如下:

  1. # ./kubernetes-auth-webhook  
  2. I1207 15:37:29.531502   21959 webhook.go:55] auth by github success 

從日志和結果可以看到,使用Github認證是OK的。

測試LDAP認證

LDAP簡介

LDAP是協議,不是軟件。

LDAP是輕量目錄訪問協議,英文全稱是Lightweight Directory Access Protocol,一般都簡稱為LDAP。按照我們對文件目錄的理解,ldap可以看成一個文件系統,類似目錄和文件樹。

OpenLDAP是常用的服務之一,也是我們本次測試的認證服務。

安裝OpenLDAP

OpenLDAP的安裝方式有很多,可以使用容器部署,也可以直接安裝在裸機上,這里采用后者。

  1. # yum install -y openldap openldap-clients openldap-servers  
  2. # systemctl start slapd 
  3. # systemctl enable slapd 

默認配置文件,位于/etc/openldap/slapd.d, 文件格式為LDAP Input Format (LDIF), ldap目錄特定的格式。這里不對配置文件做太多的介紹,有興趣可以自己去學習學習【1】。

在LDAP上配置用戶

(1)導入模板

  1. ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/schema/cosine.ldif  
  2. ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/schema/nis.ldif  
  3. ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/schema/inetorgperson.ldif  

(2)創建base組織

  1. # cat base.ldif 
  2. dn: dc=demo,dc=com 
  3. objectClass: top 
  4. objectClass: dcObject 
  5. objectClass: organization 
  6. o: ldap測試組織 
  7. dc: demo 
  8.  
  9. dn: cn=Manager,dc=demo,dc=com 
  10. objectClass: organizationalRole 
  11. cn: Manager 
  12. description: 組織管理人 
  13.  
  14. dn: ou=People,dc=demo,dc=com 
  15. objectClass: organizationalUnit 
  16. ou: People 
  17.  
  18. dn: ou=Group,dc=demo,dc=com 
  19. objectClass: organizationalUnit 
  20. ou: Group 

使用ldapadd添加base。

  1. ldapadd -x -D cn=admin,dc=demo,dc=com -w admin -f base.ldif  

(3)添加成員

  1. # cat adduser.ldif 
  2. dn: cn=jack,ou=People,dc=demo,dc=com 
  3. changetype: add 
  4. objectClass: inetOrgPerson 
  5. cn: jack 
  6. departmentNumber: 1 
  7. title: 大牛 
  8. userPassword: 123456 
  9. sn: Bai 
  10. mail: jack@demo.com 
  11. displayName: 中文名 

使用ldapadd執行添加。

  1. ldapadd -x -D cn=admin,dc=demo,dc=com -w admin -f adduser.ldif 

(4)將用戶添加到組

  1. # cat add_member_group.ldif  
  2. dn: cn=g-admin,ou=Group,dc=demo,dc=com 
  3. changetype: modify 
  4. add: member 
  5. member: cn=jack,ou=People,dc=demo,dc=com 

使用ldapadd執行添加。

  1. ldapadd -x -D cn=admin,dc=demo,dc=com -w admin -f add_member_group.ldif 

配置kubeconfig,進行ldap認證測試

(1)修改~/.kube/config配置文件

  1. # cat ~/.kube/config  
  2. apiVersion: v1 
  3. ...... 
  4. users: 
  5. name: joker 
  6.   user
  7.     token: github:ghp_jevHquU4g43m46nczWS0oxxxxxxxx 
  8. name: jack 
  9.   user
  10.     token: ldap:jack:123456 

(2)使用kubectl進行測試

  1. # kubectl get po --user=jack 
  2. Error from server (Forbidden): pods is forbidden: User "" cannot list resource "pods" in API group "" in the namespace "default" 

webhook服務日志如下:

  1. # ./kubernetes-auth-webhook  
  2. I1207 16:09:09.292067    7605 webhook.go:72] auth by ldap success 

通過測試結果可以看到使用LDAP認證測試成功。

總結

使用Webhook可以很靈活的將K8S的租戶和企業內部賬戶系統進行打通,這樣可以方便管理用戶賬戶。

不過上面開發的Webhook只是一個簡單的例子,驗證方式和手法都比較粗暴,CoreOS開源的Dex【2】是比較不錯的產品,可以直接使用。

本文轉載自微信公眾號「運維開發故事」

 

責任編輯:姜華 來源: 運維開發故事
相關推薦

2025-03-19 08:01:10

Kubernetes集群源碼

2022-04-22 13:32:01

K8s容器引擎架構

2015-02-12 15:33:43

微信SDK

2023-11-06 07:16:22

WasmK8s模塊

2023-03-31 07:17:16

2015-02-12 15:38:26

微信SDK

2023-09-06 08:12:04

k8s云原生

2021-02-03 14:04:52

k8spermissionm管理工具

2023-01-04 17:42:22

KubernetesK8s

2022-04-29 10:40:38

技術服務端K8s

2024-01-26 14:35:03

鑒權K8sNode

2022-09-07 15:57:41

KubernetesCRD

2023-09-11 14:21:00

2025-04-09 07:58:15

2020-05-12 10:20:39

K8s kubernetes中間件

2022-09-05 08:26:29

Kubernetes標簽

2023-05-25 21:38:30

2023-08-03 08:36:30

Service服務架構

2023-08-04 08:19:02

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产精品三级久久久久久电影 | av影片在线 | 日韩欧美一区二区三区四区 | 国产激情视频在线观看 | 国产精品久久久久久久午夜片 | 日韩精品视频在线观看一区二区三区 | 四虎成人免费电影 | 国产丝袜一区二区三区免费视频 | 久久精品91久久久久久再现 | 欧美日韩精品国产 | 欧美精品一区二区三区在线 | 国产中文区二幕区2012 | av毛片免费 | 一级黄色裸片 | 成人在线中文字幕 | 黄色三级毛片 | 欧美三区 | 好姑娘影视在线观看高清 | a视频在线观看 | 日韩精品 电影一区 亚洲 | 中文字幕av高清 | 国产黄色网址在线观看 | 欧美日韩久 | 欧美三区在线观看 | 精品国偷自产在线 | 国产综合精品一区二区三区 | 久久最新精品 | 国产精品美女久久久久aⅴ国产馆 | 日韩国产中文字幕 | 欧美在线激情 | 日韩成人久久 | 国产农村妇女毛片精品久久麻豆 | 久久久亚洲成人 | 日韩在线精品强乱中文字幕 | 亚洲欧美综合 | 欧美一区二区免费电影 | 久久99精品国产麻豆婷婷 | 精品一区二区在线观看 | 九九伦理电影 | 一级黄色片一级黄色片 | 亚洲欧美一区二区三区国产精品 |