k8s https证书认证

HTTPS单向认证流程

1. 客户端发起建立HTTPS连接请求,将SSL协议版本的信息发送给服务端。
2. 服务端将自己的公钥证书(server.crt)发送给客户端。
3. 客户端通过自己的根证书(root.crt)验证服务端的公钥证书(server.crt)的合法性,取出服务端公钥。
4. 客户端生成密钥R,用服务端公钥去加密它形成密文,发送给服务端。
5. 服务端用自己的私钥(server.key)去解密这个密文,得到客户端的密钥R。
6. 服务端和客户端使用密钥R进行通信。

HTTPS双向认证流程

1. 客户端发起建立HTTPS连接请求,将SSL协议版本的信息发送给服务端。
2. 服务端将自己的公钥证书(server.crt)发送给客户端。
3. 客户端通过自己的根证书(root.crt)验证服务端的公钥证书(server.crt)的合法性,取出服务端公钥。
4. 客户端将自己的公钥证书(client.crt)发送给服务端。
5. 服务端使用根证书(root.crt)验证客户端公钥证书的合法性,取出客户端公钥。
6. 客户端发送自己支持的加密方案给服务端。
7. 服务端选择一个双方都能接受的加密方案,使用客户端的公钥加密后发送给客户端。
8. 客户端使用自己的私钥解密加密方案,生成密钥R,使用服务端公钥加密后传给服务端。
9. 服务端用自己的私钥去解密这个密文,得到了密钥R。
10. 服务端和客户端使用密钥R进行通信。

双向认证使用的证书

服务端公钥证书:server.crt
服务端私钥:server.key
根证书(服务端和客户端都有,内容相同):root.crt
客户端公钥证书:client.crt
客户端私钥:client.key

curl -k背后逻辑

作用是,跳过客户端通过自己的根证书(root.crt)验证服务端的公钥证书(server.crt)的合法性这一步。

通过curl来模拟请求

# 对HTTPS服务发起HTTP请求(请求方式错误)
$ curl http://192.168.0.100:6443/apis/apps/v1/namespaces/kube-system/deployments/coredns
Client sent an HTTP request to an HTTPS server.

# 不带证书发起HTTPS请求(认证失败)
$ curl https://192.168.0.100:6443/apis/apps/v1/namespaces/kube-system/deployments/coredns
...
If this HTTPS server uses a certificate signed by a CA represented in
 the bundle, the certificate verification probably failed due to a
 problem with the certificate (it might be expired, or the name might
 not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
 the -k (or --insecure) option.
 
 # 以跳过SSL证书认证的方式来发起HTTPS请求(认证失败,无法通过TLS双向认证)
 $ curl -k https://192.168.0.100:6443/apis/apps/v1/namespaces/kube-system/deployments/coredns
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {
    
  },
  "status": "Failure",
  "message": "Unauthorized",
  "reason": "Unauthorized",
  "code": 401
}

# 携带客户端根证书的方式来发起HTTPS请求(因缺少客户端公钥证书和私钥而认证失败)
$ curl https://192.168.0.100:6443/apis/apps/v1/namespaces/kube-system/deployments/coredns --cacert /etc/kubernetes/pki/ca.crt
{
  ...
  "status": "Failure",
  "message": "Unauthorized",
  "reason": "Unauthorized",
  "code": 401
}

# 携带错误的客户端根证书的方式来发起HTTPS请求(认证失败)
$ curl https://192.168.0.100:6443/apis/apps/v1/namespaces/kube-system/deployments/coredns --cacert /root/cert/root.crt
curl: (60) Peer's Certificate issuer is not recognized.

# 拒绝匿名请求
$ curl https://192.168.0.100:6443/apis/apps/v1/namespaces/kube-system/deployments/coredns --key /etc/kubernetes/pki/ca.crt/apiserver-kubelet-clien.key --cert /etc/kubernetes/pki/ca.crt/apiserver-kubelet-client.crt --cacert /etc/kubernetes/pki/ca.crt
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {

  },
  "status": "Failure",
  "message": "deployments.apps \"coredns\" is forbidden: User \"system:anonymous\" cannot get resource \"deployments\" in API group \"apps\" in the namespace \"kube-system\"",
  "reason": "Forbidden",
  "details": {
    "name": "coredns",
    "group": "apps",
    "kind": "deployments"
  },
  "code": 403
}

# kubeconfig管理访问kube-apiserver的配置信息。k8s组件都使用kubeconfig配置信息来连接kube-apiserver,包括kubectl。
# 根据kubeconfig(~/.kube/config)来保存证书内容(base64解码:echo “xxx” | base64 -d),client-certificate-data对应client.crt,client-key-data对应client.key,certificate-authority-data对应root.crt
$ curl https://192.168.0.100:6443/apis/apps/v1/namespaces/kube-system/deployments/coredns --key /root/cert/client.key --cert /root/cert/client.crt --cacert /root/cert/root.crt
{
  "kind": "Deployment",
  "apiVersion": "apps/v1",
  "metadata": {
    "name": "coredns",
    "namespace": "kube-system",
    "selfLink": "/apis/apps/v1/namespaces/kube-system/deployments/coredns",
    "uid": "6c1c53e8-4e6e-4f76-8509-cd018a016f02",
    "resourceVersion": "1494",
    "generation": 1,
...
}

基于beego框架向kube-apiserver发起PATCH请求(API项目)

routers/router.go配置固定路由

func init() {
    ...
    beego.Router("/scale", &controllers.ScaleDeployController{})
}

models/scale.go配置json接收的数据模型

package models

type ScaleDeploy struct {
   M map[string]interface{}
}

controllers/scale.go配置ScaleDeployController

package controllers

import (
   ...
)

type ScaleDeployController struct {
   beego.Controller
}

func (o *ScaleDeployController) Patch() {
   fmt.Println("enter")
   var sd models.ScaleDeploy
   err := json.Unmarshal(o.Ctx.Input.RequestBody, &sd.M)
   if err != nil {
      panic(err)
   }

   ...
}

curl命令触发PATCH请求

curl -X PATCH -H 'Content-Type: application/strategic-merge-patch+json' --data '{"Namespace":"default", "DeployName":"nginx-deploy", "Replicas":5}' 'http://127.0.0.1:8080/scale'

向kube-apiserver发送请求失败:x509: certificate signed by unknown authority

// 添加证书(客户端CA证书、客户端包含公钥的证书、客户端私钥)
// 加载客户端ca证书
caCert, err := ioutil.ReadFile("/root/k8s/client-ca.crt")
if err != nil {
   panic(err)
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
// 加载客户端包含公钥的证书和私钥
cert, err := tls.LoadX509KeyPair("/root/k8s/client.crt", "/root/k8s/client.key")
if err != nil {
   panic(err)
}
client := &http.Client{
   Transport: &http.Transport{
      TLSClientConfig: &tls.Config{
         RootCAs:      caCertPool,
         Certificates: []tls.Certificate{cert},
      },
   },
   Timeout: 5 * time.Second,
}

完整代码

package controllers

import (
   "beego-test/models"
   "bytes"
   "crypto/tls"
   "crypto/x509"
   "encoding/json"
   "fmt"
   "github.com/astaxie/beego"
   "io/ioutil"
   "net/http"
   "strconv"
   "time"
)

type ScaleDeployController struct {
   beego.Controller
}

func (o *ScaleDeployController) Patch() {
   fmt.Println("enter")
   var sd models.ScaleDeploy
   err := json.Unmarshal(o.Ctx.Input.RequestBody, &sd.M)
   if err != nil {
      panic(err)
   }

   deployName := ""
   namespace := ""
   replicas := -1
   for key, value := range sd.M {
      switch cur := value.(type) {
      case float64:
         if key == "Replicas" {
            replicas = int(int64(cur))
         } else {
            panic("key error")
         }
      case string:
         if key == "DeployName" {
            deployName = cur
         } else if key == "Namespace" {
            namespace = cur
         } else {
            panic("key error")
         }
      default:
         panic("type error")
      }
   }

   fmt.Println(replicas)
   replicasStr := strconv.Itoa(replicas)
   // 请求体字节流,转义双引号
   jsonStr := []byte("{\"spec\":{\"replicas\":" + replicasStr + "}}")

   // 加载客户端ca证书
   caCert, err := ioutil.ReadFile("/root/k8s/client-ca.crt")
   if err != nil {
      panic(err)
   }
   caCertPool := x509.NewCertPool()
   caCertPool.AppendCertsFromPEM(caCert)
   // 加载客户端包含公钥的证书和私钥
   cert, err := tls.LoadX509KeyPair("/root/k8s/client.crt", "/root/k8s/client.key")
   if err != nil {
      panic(err)
   }
   client := &http.Client{
      Transport: &http.Transport{
         TLSClientConfig: &tls.Config{
            RootCAs:      caCertPool,
            Certificates: []tls.Certificate{cert},
         },
      },
      Timeout: 5 * time.Second,
   }

   req, err := http.NewRequest("PATCH",
      "https://192.168.0.100:6443/apis/apps/v1/namespaces/" + namespace + "/deployments/" + deployName,
      bytes.NewBuffer(jsonStr))
   req.Header.Add("content-type", "application/strategic-merge-patch+json")

   if err != nil {
      panic(err)
   }
   defer req.Body.Close()

   resp, error := client.Do(req)
   if error != nil {
      panic(error)
   }
   defer resp.Body.Close()

   //result, _ := ioutil.ReadAll(resp.Body)
   //content := string(result)
   //o.Data["json"] = content
   o.Data["json"] = resp.Status
   o.ServeJSON()
}

运行结果

"200 OK"

 

posted on 2022-01-21 18:48  王景迁  阅读(1758)  评论(0编辑  收藏  举报

导航