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

K8S自定義Webhook實現(xiàn)認證管理

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

[[439204]]

大家好,我是喬克。

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

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

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

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

這篇文章主要和大家討論認證環(huán)節(jié)。

認證

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

常用的認證插件有:

  • X509證書
  • 靜態(tài)Token
  • ServiceAccount
  • OpenID
  • Webhook
  • .....

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

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

K8s的Webhook就可以實現(xiàn)這種需求,Webhook是一個HTTP回調(diào),通過一個條件觸發(fā)HTTP POST請求發(fā)送到Webhook 服務端,服務端根據(jù)請求數(shù)據(jù)進行處理。

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

開發(fā)Webhook

簡介

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

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

Webhook開發(fā)

開發(fā)環(huán)境

構建符合規(guī)范的Webhook

在開發(fā)Webhook的時候,需要符合Kubernetes的規(guī)范,具體如下:

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

如果成功會返回:

  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 字段,以標明登錄操作是否成功。

開發(fā)認證服務

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

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

(2)在項目根目錄下創(chuàng)建webhook.go,寫入如下內(nèi)容

  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.  // 根據(jù)type, 取出不同的認證數(shù)據(jù) 
  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)創(chuàng)建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)創(chuàng)建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)創(chuàng)建main.go入口函數(shù)

  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.  // 啟動協(xié)程來處理 
  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.  // 優(yōu)雅退出 
  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()) 

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

Webhook測試

APIServer添加認證服務

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

  • --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中添加配置參數(shù)

  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: 為了節(jié)約篇幅,上面省略了部分配置。

當修改完過后,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是協(xié)議,不是軟件。

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

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)創(chuàng)建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執(zhí)行添加。

  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執(zhí)行添加。

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

配置kubeconfig,進行l(wèi)dap認證測試

(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的租戶和企業(yè)內(nèi)部賬戶系統(tǒng)進行打通,這樣可以方便管理用戶賬戶。

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

本文轉(zhuǎn)載自微信公眾號「運維開發(fā)故事」

 

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

2025-03-19 08:01:10

Kubernetes集群源碼

2022-04-22 13:32:01

K8s容器引擎架構

2015-02-12 15:33:43

微信SDK

2023-03-31 07:17:16

2015-02-12 15:38:26

微信SDK

2023-11-06 07:16:22

WasmK8s模塊

2021-02-03 14:04:52

k8spermissionm管理工具

2024-01-26 14:35:03

鑒權K8sNode

2022-04-29 10:40:38

技術服務端K8s

2022-09-07 15:57:41

KubernetesCRD

2023-09-06 08:12:04

k8s云原生

2023-01-04 17:42:22

KubernetesK8s

2023-09-11 14:21:00

2020-05-12 10:20:39

K8s kubernetes中間件

2022-09-05 08:26:29

Kubernetes標簽

2009-09-07 22:00:15

LINQ自定義

2025-04-01 00:06:50

JavaK8sSpring

2025-04-09 07:58:15

2023-05-25 21:38:30

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 亚洲精选一区二区 | 91国产视频在线观看 | 国产在线网站 | 人人澡视频 | 欧美日韩国产一区二区三区不卡 | 久久精品欧美一区二区三区不卡 | 精品乱人伦一区二区三区 | 美国一级片在线观看 | 国产三区在线观看视频 | 日本综合在线观看 | 精品在线观看一区 | 日日操视频| 成人av网站在线观看 | 欧美jizzhd精品欧美巨大免费 | 日日骚av | 婷婷激情综合 | 欧美一区二区大片 | 国产一级一级毛片 | 亚洲精品一区二区 | 欧一区| 久久免费精品 | 看a级黄色毛片 | 久久精品久久精品久久精品 | 在线播放国产一区二区三区 | 久久精品亚洲一区二区三区浴池 | 国产精品精品久久久 | 久久久久久免费毛片精品 | 日韩欧美在线观看视频 | 雨宫琴音一区二区在线 | 碰碰视频 | 欧美一区免费 | 日本一区二区三区四区 | 91成人免费观看 | 国产成视频在线观看 | 一区二区免费 | 日韩成人在线观看 | 亚洲精品福利在线 | 亚洲福利免费 | 男女啪啪网址 | 国产成人久久久 | 国产一区 |