Kubernetes叢集中配置Ingress支援HTTPS訪問(一):cfssl

人生的哲理發表於2024-05-22

目錄
  • 一.系統環境
  • 二.前言
  • 三.對稱加密和非對稱加密簡介
  • 四.什麼是HTTPS
  • 五.Ingress簡介
  • 六.配置ingress對外發布服務
    • 6.1 安裝NGINX ingress controller控制器
    • 6.2 建立pod
    • 6.3 為pod建立svc服務
    • 6.4 使用ingress釋出服務
    • 6.5 訪問服務
      • 6.5.1 使用Linux客戶端來訪問服務
      • 6.5.2 使用Windows客戶端訪問服務
  • 七.配置Ingress支援HTTPS訪問
    • 7.1 使用ingress-nginx-controller自帶的證書
    • 7.2 使用cfssl工具生成證書
      • 7.2.1 安裝cfssl
      • 7.2.2 生成CA
      • 7.2.3 生成使用者證書
      • 7.2.4 使用自定義的證書
  • 八.總結

一.系統環境

本文主要基於Kubernetes1.22.2和Linux作業系統Ubuntu 18.04。

伺服器版本 docker軟體版本 Kubernetes(k8s)叢集版本 kube-bench版本 CPU架構
Ubuntu 18.04.5 LTS Docker version 20.10.14 v1.22.2 0.6.7 x86_64

Kubernetes叢集架構:k8scludes1作為master節點,k8scludes2,k8scludes3作為worker節點。

伺服器 作業系統版本 CPU架構 程序 功能描述
k8scludes1/192.168.110.128 Ubuntu 18.04.5 LTS x86_64 docker,kube-apiserver,etcd,kube-scheduler,kube-controller-manager,kubelet,kube-proxy,coredns,calico k8s master節點
k8scludes2/192.168.110.129 Ubuntu 18.04.5 LTS x86_64 docker,kubelet,kube-proxy,calico k8s worker節點
k8scludes3/192.168.110.130 Ubuntu 18.04.5 LTS x86_64 docker,kubelet,kube-proxy,calico k8s worker節點

二.前言

HTTPS是安全的HTTP協議,它透過SSL/TLS協議為客戶端與伺服器之間的通訊提供加密。在Kubernetes叢集中,Ingress資源管理叢集外的訪問,透過配置Ingress,我們可以為服務提供安全的HTTPS訪問。

在Kubernetes叢集中配置Ingress以支援HTTPS訪問的前提是已經有一套可以正常執行的Kubernetes叢集,關於Kubernetes(k8s)叢集的安裝部署,可以檢視部落格《Ubuntu 安裝部署Kubernetes(k8s)叢集》https://www.cnblogs.com/renshengdezheli/p/17632858.html。

三.對稱加密和非對稱加密簡介

資料加密的型別有:

  • 對稱加密:使用相同的金鑰進行加密和解密。優點是演算法公開、計算量小、加密速度快、加密效率高,但金鑰分發是一個挑戰,如果金鑰在協商過程中被洩露,那麼密文就可能被破解。常見的對稱加密演算法有des,aes,des256,des512等
  • 非對稱加密:使用一對金鑰,一個用於加密(公鑰(可公開)),另一個用於解密(私鑰(私有的))。優點是安全性高,即使密文被攔截、公鑰被獲取,由於無法獲取到私鑰,攻擊者也無法破譯密文。常見的非對稱加密演算法有RSA、DSA、ECC等。但是非對稱加密的計算過程複雜,加解密效率相對較低,舉個例子:A給B傳輸檔案,A先使用B的公鑰加密,然後把加密檔案傳送給B,B使用自己的私鑰進行解密。
  • 雜湊函式:輸入不定長的值,總能得到一個定長的值。

另外非對稱加密除了可以用來做資料加密(公鑰加密,私鑰解密),還可以用來做數字簽名(私鑰加密,公鑰解密),數字簽名用來驗證身份,舉個例子:A要給B傳送一個資料,A需要向B證明這資料就是A傳送的,不是別人傳送的。A對需要傳輸的檔案生成一個雜湊值,檔案內容不發生變化,雜湊值就不會發生變化,A使用私鑰加密雜湊值,然後把資料和加密後的雜湊值傳送給B, B使用A的公鑰解密雜湊值,B將收到的資料生成一個雜湊值,如果檔案在傳輸的過程中沒有被修改,則兩個雜湊值應該是一樣的 ,這樣就可以驗證資料是不是A傳送的,以及資料有沒有被修改過。

混合加密即對稱加密和非對稱加密的混合使用,有時會把檔案先進行對稱加密再非對稱加密,再傳輸,這樣對稱加密的金鑰傳輸過程就是安全的,但是存在中間人攻擊,B把公鑰發給A,途中被中間人C截獲了,然後把C的公鑰發給A,A自以為使用了“正確的B的公鑰”,其實收到的是C偽裝的公鑰,A加密之後把加密檔案發給B,C截獲了加密檔案,並使用C的私鑰解密得到明文,最後使用正確的B公鑰加密檔案,把檔案發給B,這就是中間人攻擊

解決中間人攻擊的方法是使用CA,認證中心(CA)是一個權威的、受信任的第三方機構,其核心職能是發放和管理數字證書,用於證明和確認交易參與者的身份,保證電子商務交易過程中身份可認證性。舉個例子:B傳送csr(證書請求檔案)給CA證書中心,CA稽核透過之後,給B頒發證書,證書上有CA的蓋章(數字簽名),說明這個證書是CA認證過的沒問題,B把證書發給A,A使用CA的公鑰驗證證書的數值簽名,驗證是不是CA頒發的證 書,瀏覽器裡也儲存著證書資訊,確定這是CA給B頒發的證書之後,使用證書加密金鑰,傳輸給B。

注意:證書的本質就是公鑰。

總的來說,對稱加密和非對稱加密各有優缺點,具體使用哪種方式取決於實際需求和場景。

四.什麼是HTTPS

HTTPS,全稱Hypertext Transfer Protocol Secure,是超文字傳輸安全協議。它是一種透過計算機網路進行安全通訊的傳輸協議,以安全為目標的 HTTP 通道。在HTTP的基礎上,HTTPS透過傳輸加密和身份認證保證了傳輸過程的安全性。

HTTPS的誕生是為了解決HTTP通訊使用明文的問題,驗證通訊方的身份,以及證明報文的完整性。在HTTP與TCP之間,HTTPS加入了SSL(Secure Sockets Layer)/TLS(Transport Layer Security)協議來為資料傳輸提供加密和身份驗證。如果進行更具體的解釋,可以將HTTPS理解為身披SSL/TLS協議這層外殼的HTTP,即https=http+tls/ssl,ssl是tls的前身。

HTTPS解決資料傳輸安全問題的方案就是使用加密演算法,具體來說是混合加密演算法,也就是對稱加密和非對稱加密的混合使用。

五.Ingress簡介

Ingress是Kubernetes中的一個API物件,用於管理對叢集內服務的外部訪問。Ingress資源允許您定義如何路由外部HTTP(S)流量到叢集中的不同服務。

kubernetes服務的釋出方式有:NodePort,LoadBalancer,Ingress,關於使用NodePort或者LoadBalancer釋出Kubernetes服務,詳情請檢視部落格《Kubernetes(k8s)服務service:service的發現和service的釋出》,Kubernetes(k8s)使用ingress釋出服務,我們在部落格《Kubernetes(k8s)使用ingress釋出服務》中也做了詳細介紹。但是之前使用ingress釋出服務,使用的是http的方式,是以明文的方式訪問的,這樣不安全。

本文配置Ingress支援HTTPS訪問,提高安全性,避免流量被劫持,提高搜尋站點的權重。

六.配置ingress對外發布服務

6.1 安裝NGINX ingress controller控制器

要使用Ingress,需要先安裝一個Ingress控制器來處理Ingress物件。本次使用Nginx Ingress Controller控制器,Nginx Ingress Controller控制器本質上是一個nginx的反向代理(根據訪問地址的不同,轉發到不同的伺服器)。Nginx Ingress Controller的官網為:https://kubernetes.github.io/ingress-nginx/deploy/。

下載ingress-nginx的部署yaml檔案。

root@k8scludes1:~/TLS-ingress# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.1.1/deploy/static/provider/cloud/deploy.yaml
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.109.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 19299 (19K) [text/plain]
Saving to: ‘deploy.yaml’

deploy.yaml                                          100%[====================================================================================================================>]  18.85K  35.5KB/s    in 0.5s    

2022-04-20 15:09:22 (35.5 KB/s) - ‘deploy.yaml’ saved [19299/19299]

檢視ingress-nginx所需的映象。

root@k8scludes1:~/TLS-ingress# grep image deploy.yaml 
          image: k8s.gcr.io/ingress-nginx/controller:v1.1.1@sha256:0bc88eb15f9e7f84e8e56c14fa5735aaa488b840983f87bd79b1054190e660de
          imagePullPolicy: IfNotPresent
          image: k8s.gcr.io/ingress-nginx/kube-webhook-certgen:v1.1.1@sha256:64d8c73dca984af206adf9d6d7e46aa550362b1d7a01f3a0a91b20cc67868660
          imagePullPolicy: IfNotPresent
          image: k8s.gcr.io/ingress-nginx/kube-webhook-certgen:v1.1.1@sha256:64d8c73dca984af206adf9d6d7e46aa550362b1d7a01f3a0a91b20cc67868660
          imagePullPolicy: IfNotPresent

因為k8s.gcr.io/ingress-nginx/controller:v1.1.1映象下載不了,我們搜尋可用的映象。

root@k8scludes1:~# docker search controller:v1.1.1 --no-trunc
NAME                              DESCRIPTION                                  STARS     OFFICIAL   AUTOMATED
loging/ingress-nginx-controller   k8s.gcr.io/ingress-nginx/controller:v1.1.1   1                    
jhonsun777/controller             k8s.gcr.io/ingress-nginx/controller:v1.1.1   0           

root@k8scludes1:~/TLS-ingress# docker search kube-webhook-certgen:v1.1.1 --no-trunc
NAME                                              DESCRIPTION                                                       STARS     OFFICIAL   AUTOMATED
lianyuxue1020/kube-webhook-certgen                new pull lianyuxue1020/kube-webhook-certgen:v1.1.1                1                    
xyz349925756/ingress-nginx-kube-webhook-certgen   k8s.gcr.io/ingress-nginx/kube-webhook-certgen:v1.1.1              0                    
freemankevin/kube-webhook-certgen                 correspond:k8s.gcr.io/ingress-nginx/kube-webhook-certgen:v1.1.1   0                    
longjianghu/ingress-nginx-kube-webhook-certgen    k8s.gcr.io/ingress-nginx/kube-webhook-certgen:v1.1.1原版映象          0                    
caihy/ingress-nginx-kube-webhook-certgen          FROM k8s.gcr.io/ingress-nginx/kube-webhook-certgen:v1.1.1         0                    

在worker節點下載controller映象和kube-webhook-certgen映象。

root@k8scludes2:~# docker pull  willdockerhub/ingress-nginx-controller:v1.0.0
root@k8scludes2:~# docker pull  docker.io/liangjw/kube-webhook-certgen:v1.1.1

root@k8scludes3:~# docker pull  willdockerhub/ingress-nginx-controller:v1.0.0
root@k8scludes3:~# docker pull  docker.io/liangjw/kube-webhook-certgen:v1.1.1

把deploy.yaml檔案裡的映象修改為我們下載好的映象,注意ingress-nginx-controller和kube-webhook-certgen映象的版本不要差太多,不然部署失敗。

root@k8scludes1:~/TLS-ingress# grep image deploy.yaml
          image: willdockerhub/ingress-nginx-controller:v1.0.0
          imagePullPolicy: IfNotPresent
          image: docker.io/liangjw/kube-webhook-certgen:v1.1.1
          imagePullPolicy: IfNotPresent
          image: docker.io/liangjw/kube-webhook-certgen:v1.1.1
          imagePullPolicy: IfNotPresent

應用ingress-nginx-controller。

root@k8scludes1:~/TLS-ingress# kubectl apply -f deploy.yaml
namespace/ingress-nginx created
serviceaccount/ingress-nginx created
configmap/ingress-nginx-controller created
clusterrole.rbac.authorization.k8s.io/ingress-nginx created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx created
role.rbac.authorization.k8s.io/ingress-nginx created
rolebinding.rbac.authorization.k8s.io/ingress-nginx created
service/ingress-nginx-controller-admission created
service/ingress-nginx-controller created
deployment.apps/ingress-nginx-controller created
ingressclass.networking.k8s.io/nginx created
validatingwebhookconfiguration.admissionregistration.k8s.io/ingress-nginx-admission created
serviceaccount/ingress-nginx-admission created
clusterrole.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
role.rbac.authorization.k8s.io/ingress-nginx-admission created
rolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
job.batch/ingress-nginx-admission-create created
job.batch/ingress-nginx-admission-patch created

顯示如下,則ingress-nginx-controller控制器部署成功。

root@k8scludes1:~/TLS-ingress# kubectl get pod -n ingress-nginx -o wide 
NAME                                        READY   STATUS      RESTARTS   AGE   IP               NODE         NOMINATED NODE   READINESS GATES
ingress-nginx-admission-create--1-t5hqt     0/1     Completed   0          23s   10.244.218.147   k8scludes2   <none>           <none>
ingress-nginx-admission-patch--1-wzb6x      0/1     Completed   1          23s   10.244.218.146   k8scludes2   <none>           <none>
ingress-nginx-controller-6b64bc6f47-dgjdq   1/1     Running     0          23s   10.244.218.148   k8scludes2   <none>           <none>

注意deploy.yaml檔案裡- --watch-ingress-without-class=true引數加不加都沒有影響。

root@k8scludes1:~/TLS-ingress# grep -A12 arg deploy.yaml
          args:
            - /nginx-ingress-controller
            - --election-id=ingress-controller-leader
            - --controller-class=k8s.io/ingress-nginx
            - --configmap=$(POD_NAMESPACE)/ingress-nginx-controller
            - --validating-webhook=:8443
            - --validating-webhook-certificate=/usr/local/certificates/cert
            - --validating-webhook-key=/usr/local/certificates/key
              #- --watch-ingress-without-class=true

6.2 建立pod

NGINX ingress controller控制器搭建成功之後建立pod。

在worker節點提前下載好nginx映象。

root@k8scludes2:~# docker pull hub.c.163.com/library/nginx:latest
root@k8scludes3:~# docker pull hub.c.163.com/library/nginx:latest

pod配置檔案如下,功能為使用Nginx映象建立pod。

root@k8scludes1:~/TLS-ingress# vim pod.yaml 

#使用nginx映象建立pod
root@k8scludes1:~/TLS-ingress# cat pod.yaml 
apiVersion: v1
kind: Pod
metadata:
  labels:
    test: podtest
  name: podtest
spec:
  #當需要關閉容器時,立即殺死容器而不等待預設的30秒優雅停機時長。
  terminationGracePeriodSeconds: 0
  containers:
  - name: nginx
    image: hub.c.163.com/library/nginx:latest
    #imagePullPolicy: IfNotPresent:表示如果本地已經存在該映象,則不重新下載;否則從遠端 Docker Hub 下載該映象
    imagePullPolicy: IfNotPresent

生成三個pod用於ingress訪問。

root@k8scludes1:~/TLS-ingress# sed 's/podtest/nginx1/' pod.yaml | kubectl apply -f -
pod/nginx1 created

root@k8scludes1:~/TLS-ingress# sed 's/podtest/nginx2/' pod.yaml | kubectl apply -f -
pod/nginx2 created

root@k8scludes1:~/TLS-ingress# sed 's/podtest/nginx3/' pod.yaml | kubectl apply -f -
pod/nginx3 created

檢視pod。

root@k8scludes1:~/TLS-ingress# kubectl get pod -o wide
NAME     READY   STATUS    RESTARTS   AGE   IP               NODE         NOMINATED NODE   READINESS GATES
nginx1   1/1     Running   0          31s   10.244.218.149   k8scludes2   <none>           <none>
nginx2   1/1     Running   0          21s   10.244.218.150   k8scludes2   <none>           <none>
nginx3   1/1     Running   0          12s   10.244.1.81      k8scludes3   <none>           <none>

修改nginx的index.html檔案,用於辨別每個pod。

root@k8scludes1:~/TLS-ingress# kubectl exec -it nginx1 -- sh -c "echo 111 >/usr/share/nginx/html/index.html"

root@k8scludes1:~/TLS-ingress# kubectl exec -it nginx2 -- sh -c "echo 222 >/usr/share/nginx/html/index.html"

root@k8scludes1:~/TLS-ingress# kubectl exec -it nginx3 -- sh -c "mkdir /usr/share/nginx/html/ingress; echo 333 >/usr/share/nginx/html/ingress/index.html"

6.3 為pod建立svc服務

給每個pod建立一個svc服務。

root@k8scludes1:~/TLS-ingress# kubectl expose --name=nginx1svc pod nginx1 --port=80
service/nginx1svc exposed

root@k8scludes1:~/TLS-ingress# kubectl expose --name=nginx2svc pod nginx2 --port=80
service/nginx2svc exposed

root@k8scludes1:~/TLS-ingress# kubectl expose --name=nginx3svc pod nginx3 --port=80
service/nginx3svc exposed

檢視svc。

root@k8scludes1:~/TLS-ingress# kubectl get svc -o wide
NAME        TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE   SELECTOR
nginx1svc   ClusterIP   10.98.119.83    <none>        80/TCP    24s   test=nginx1
nginx2svc   ClusterIP   10.106.4.10     <none>        80/TCP    17s   test=nginx2
nginx3svc   ClusterIP   10.101.154.93   <none>        80/TCP    9s    test=nginx3

訪問svc。

root@k8scludes1:~/TLS-ingress# curl 10.98.119.83
111

root@k8scludes1:~/TLS-ingress# curl 10.106.4.10
222

root@k8scludes1:~/TLS-ingress# curl 10.101.154.93/ingress/index.html
333

6.4 使用ingress釋出服務

建立ingress規則。

root@k8scludes1:~/TLS-ingress# vim ingress-rule.yaml

#注意在annotations中需要指定你的ingress是何種,此處使用的nginx-ingress所以是Nginx,否則無法透過配置的域名www.nginx13.com訪問
#訪問www.nginx13.com就相當於訪問nginx1svc服務
root@k8scludes1:~/TLS-ingress# cat ingress-rule.yaml 
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-ingress
  annotations:
    kubernetes.io/ingress.class: "nginx"
spec:
  rules:
  - host: www.nginx13.com
    http:
      paths:
      #訪問網址目錄
      - path: /
        pathType: Prefix
        backend:
          service:
            name: nginx1svc
            port:
              number: 80
      - path: /ingress
        pathType: Prefix
        backend:
          service:
            name: nginx3svc
            port:
              number: 80

  - host: www.nginx2.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: nginx2svc
            port:
              number: 80

應用ingress規則。

root@k8scludes1:~/TLS-ingress# kubectl apply -f ingress-rule.yaml 
ingress.networking.k8s.io/my-ingress created

檢視ingress。

root@k8scludes1:~/TLS-ingress# kubectl get ingress -o wide
NAME         CLASS    HOSTS                            ADDRESS   PORTS   AGE
my-ingress   <none>   www.nginx13.com,www.nginx2.com             80      10s

root@k8scludes1:~/TLS-ingress# kubectl get ing -o wide
NAME         CLASS    HOSTS                            ADDRESS   PORTS   AGE
my-ingress   <none>   www.nginx13.com,www.nginx2.com             80      34s

可以發現svc的80埠被對映為32253埠。

root@k8scludes1:~/TLS-ingress# kubectl get svc -n ingress-nginx -o wide
NAME                                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE   SELECTOR
ingress-nginx-controller             NodePort    10.96.184.210   <none>        80:32253/TCP,443:30876/TCP   22m   app.kubernetes.io/component=controller,app.kubernetes.io/instance=ingress-nginx,app.kubernetes.io/name=ingress-nginx
ingress-nginx-controller-admission   ClusterIP   10.102.52.109   <none>        443/TCP                      22m   app.kubernetes.io/component=controller,app.kubernetes.io/instance=ingress-nginx,app.kubernetes.io/name=ingress-nginx

當在其他名稱空間建立相同的ingress規則時,會提醒重複。

root@k8scludes1:~/TLS-ingress# kubectl apply -f ingress-rule.yaml -n default
Error from server (BadRequest): error when creating "ingress-rule.yaml": admission webhook "validate.nginx.ingress.kubernetes.io" denied the request: host "www.nginx13.com" and path "/" is already defined in ingress tls-ingress/my-ingress

6.5 訪問服務

6.5.1 使用Linux客戶端來訪問服務

ingress規則建立之後,我們使用Linux客戶端來訪問服務。

我們使用etcd2機器作為客戶端,因為ingress-nginx-controller控制器在k8scludes2上,k8scludes2的IP地址為192.168.110.129。

root@k8scludes1:~/TLS-ingress# kubectl get pod -o wide -n ingress-nginx
NAME                                        READY   STATUS      RESTARTS   AGE   IP               NODE         NOMINATED NODE   READINESS GATES
ingress-nginx-admission-create--1-t5hqt     0/1     Completed   0          23m   10.244.218.147   k8scludes2   <none>           <none>
ingress-nginx-admission-patch--1-wzb6x      0/1     Completed   1          23m   10.244.218.146   k8scludes2   <none>           <none>
ingress-nginx-controller-6b64bc6f47-dgjdq   1/1     Running     0          23m   10.244.218.148   k8scludes2   <none>           <none>

我們配置客戶端etcd2機器的 /etc/hosts檔案,ingress-nginx-controller控制器IP和域名的對映。

[root@etcd2 ~]# vim /etc/hosts

ingress-nginx-controller控制器IP和域名的對映
[root@etcd2 ~]# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.110.133 etcd1
192.168.110.131 etcd2
192.168.110.132 etcd3
192.168.110.129 www.nginx13.com
192.168.110.129 www.nginx2.com

直接訪問域名的80埠被拒絕了。

[root@etcd2 ~]# curl www.nginx13.com
curl: (7) Failed connect to www.nginx13.com:80; 拒絕連線

[root@etcd2 ~]# curl www.nginx2.com
curl: (7) Failed connect to www.nginx2.com:80; 拒絕連線

ingress-nginx-controller服務把80埠被對映為32253埠,所以外界需要訪問32253埠。

root@k8scludes1:~/TLS-ingress# kubectl get svc -n ingress-nginx -o wide
NAME                                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE   SELECTOR
ingress-nginx-controller             NodePort    10.96.184.210   <none>        80:32253/TCP,443:30876/TCP   22m   app.kubernetes.io/component=controller,app.kubernetes.io/instance=ingress-nginx,app.kubernetes.io/name=ingress-nginx
ingress-nginx-controller-admission   ClusterIP   10.102.52.109   <none>        443/TCP                      22m   app.kubernetes.io/component=controller,app.kubernetes.io/instance=ingress-nginx,app.kubernetes.io/name=ingress-nginx

現在透過域名就可以訪問nginx的不同服務了。

[root@etcd2 ~]# curl www.nginx13.com:32253
111

[root@etcd2 ~]# curl www.nginx2.com:32253/
222

[root@etcd2 ~]# curl www.nginx13.com:32253/ingress/index.html
333

6.5.2 使用Windows客戶端訪問服務

我們也可以使用Windows機器作為客戶端訪問服務。修改Windows機器的C:\Windows\System32\drivers\etc\HOSTS檔案的IP域名對映。

192.168.110.129 www.nginx13.com
192.168.110.129 www.nginx2.com

然後使用瀏覽器透過域名訪問nginx服務。

image-20230728163747507

image-20230728163803302

image-20230728163819194

自此ingress對外發布服務成功,但是現在使用的是http://www.nginx13.com訪問的,http是明文傳輸,不安全。

七.配置Ingress支援HTTPS訪問

7.1 使用ingress-nginx-controller自帶的證書

此時pod,svc沒有做任何https配置,客戶端使用https訪問ingress-nginx-controller,瀏覽器會發出警告,這是因為ingress-nginx-controller也有證書,但是客戶端一核實,不是CA頒發的證書,瀏覽器就報警了。

https埠為443,對應的埠為30876。

root@k8scludes1:~/TLS-ingress# kubectl get svc -n ingress-nginx -o wide
NAME                                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE   SELECTOR
ingress-nginx-controller             NodePort    10.96.184.210   <none>        80:32253/TCP,443:30876/TCP   22m   app.kubernetes.io/component=controller,app.kubernetes.io/instance=ingress-nginx,app.kubernetes.io/name=ingress-nginx
ingress-nginx-controller-admission   ClusterIP   10.102.52.109   <none>        443/TCP                      22m   app.kubernetes.io/component=controller,app.kubernetes.io/instance=ingress-nginx,app.kubernetes.io/name=ingress-nginx

瀏覽器訪問https://www.nginx2.com:31473/,檢視證書,點選證書無效。

image-20230728164551030

可以看到,證書為ingress自帶的證書。

image-20230728164634801

繼續訪問。

image-20230728164721194

使用https可以訪問服務,但是證書是ingress自帶的證書。

image-20230728164756572

ingress-nginx-controller給不同的域名提供域名解析服務,證書是各個域名共享的,即所有的站點共享這個證書。

7.2 使用cfssl工具生成證書

7.2.1 安裝cfssl

現在我們想申請自己專屬的證書,而不是ingress-nginx-controller共享的證書,可以使用cfssl工具自己生成一個證書。

cfssl下載地址:https://github.com/cloudflare/cfssl/releases

下載這三個檔案:cfssl_1.6.1_linux_amd64 , cfssl-certinfo_1.6.1_linux_amd64 , cfssljson_1.6.1_linux_amd64 。

image-20230728164902561

把下載好的cfssl檔案放到 /usr/local/bin/目錄下。

root@k8scludes1:~/TLS-ingress# cd /usr/local/bin/

root@k8scludes1:/usr/local/bin# ls

root@k8scludes1:/usr/local/bin# pwd
/usr/local/bin

root@k8scludes1:/usr/local/bin# rz -E
rz waiting to receive.

root@k8scludes1:/usr/local/bin# ls
cfssl_1.6.1_linux_amd64  cfssl-certinfo_1.6.1_linux_amd64  cfssljson_1.6.1_linux_amd64

檔案重新命名。

root@k8scludes1:/usr/local/bin# mv cfssl_1.6.1_linux_amd64 cfssl

root@k8scludes1:/usr/local/bin# mv cfssl-certinfo_1.6.1_linux_amd64 cfssl-certinfo

root@k8scludes1:/usr/local/bin# mv cfssljson_1.6.1_linux_amd64 cfssljson

root@k8scludes1:/usr/local/bin# ll -h
total 40M
drwxr-xr-x  2 root root 4.0K Apr 21 19:45 ./
drwxr-xr-x 10 root root 4.0K Nov 27  2020 ../
-rw-r--r--  1 root root  16M Apr 21 17:39 cfssl
-rw-r--r--  1 root root  13M Apr 21 17:50 cfssl-certinfo
-rw-r--r--  1 root root  11M Apr 21 17:42 cfssljson

賦予可執行許可權。

root@k8scludes1:/usr/local/bin# chmod +x ./*

7.2.2 生成CA

建立tls目錄存放證書檔案。

root@k8scludes1:/usr/local/bin# cd

root@k8scludes1:~/TLS-ingress# mkdir tls

root@k8scludes1:~/TLS-ingress# cd tls/

root@k8scludes1:~/TLS-ingress/tls# pwd
/root/TLS-ingress/tls

使用cfssl工具生成證書的原理是:自己模擬一套完整的環境,包括CA也是自己搭建,需要CA(ca公鑰,ca私鑰),我們自己的私鑰和ca給我們頒發的證書,申請ca證書還需要證書請求檔案csr。

生成CA配置檔案。

  • ca-config.json:可以定義多個 profiles,分別指定不同的過期時間、使用場景等引數;後續在簽名證書時使用某個profile;www,client都是profile。
  • signing:表示該證書可用於簽名其它證書;生成的ca.pem證書中 CA=TRUE;
  • server auth:表示client可以用該 CA 對server提供的證書進行驗證;
  • client auth:表示server可以用該CA對client提供的證書進行驗證 ;
root@k8scludes1:~/TLS-ingress/tls# cfssl print-defaults config > ca-config.json

root@k8scludes1:~/TLS-ingress/tls# cat ca-config.json 
{
    "signing": {
        "default": {
            "expiry": "168h"
        },
        "profiles": {
            "www": {
                "expiry": "8760h",
                "usages": [
                    "signing",
                    "key encipherment",
                    "server auth"
                ]
            },
            "client": {
                "expiry": "8760h",
                "usages": [
                    "signing",
                    "key encipherment",
                    "client auth"
                ]
            }
        }
    }
}

修改CA配置檔案,這裡只設定一個profiles:www。

root@k8scludes1:~/TLS-ingress/tls# vim ca-config.json 

root@k8scludes1:~/TLS-ingress/tls# cat ca-config.json 
{
    "signing": {
        "default": {
            "expiry": "1680h"
        },
        "profiles": {
            "www": {
                "expiry": "8760h",
                "usages": [
                    "signing",
                    "key encipherment",
                    "server auth"
                ]
            }
        }
    }
}

現在生成ca的證書請求檔案csr。

  • CN: Common Name,瀏覽器使用該欄位驗證網站是否合法,一般寫的是域名;
  • C: Country, 國家;
  • L: Locality,地區,城市;
  • O: Organization Name,組織名稱,公司名稱;
  • OU: Organization Unit Name,組織單位名稱,公司部門;
  • ST: State,州,省。
root@k8scludes1:~/TLS-ingress/tls# cfssl print-defaults csr > ca-csr.json

root@k8scludes1:~/TLS-ingress/tls# cat ca-csr.json 
{
    "CN": "example.net",
    "hosts": [
        "example.net",
        "www.example.net"
    ],
    "key": {
        "algo": "ecdsa",
        "size": 256
    },
    "names": [
        {
            "C": "US",
            "ST": "CA",
            "L": "San Francisco"
        }
    ]
}

ca的證書請求檔案ca-csr.json,修改域名為nginxx.com,城市資訊換成廣州,algo表示加密演算法,size表示演算法長度。

root@k8scludes1:~/TLS-ingress/tls# vim ca-csr.json 

root@k8scludes1:~/TLS-ingress/tls# cat ca-csr.json 
{
    "CN": "nginxx.com",
    "key": {
        "algo": "ecdsa",
        "size": 256
    },
    "names": [
        {
            "C": "CN",
            "ST": "guangdon",
            "L": "guangzhou"
        }
    ]
}

下面生成CA權威機構。

root@k8scludes1:~/TLS-ingress/tls# cfssl gencert -initca ca-csr.json | cfssljson -bare ca
2022/04/22 15:42:54 [INFO] generating a new CA key and certificate from CSR
2022/04/22 15:42:54 [INFO] generate received request
2022/04/22 15:42:54 [INFO] received CSR
2022/04/22 15:42:54 [INFO] generating key: ecdsa-256
2022/04/22 15:42:54 [INFO] encoded CSR
2022/04/22 15:42:54 [INFO] signed certificate with serial number 361660789191812789469217914317150175823294603356

生成了一個自簽名的證書,CA的證書(ca.pem),CA的私鑰(ca-key.pem)。

root@k8scludes1:~/TLS-ingress/tls# ls
ca-config.json  ca.csr  ca-csr.json  ca-key.pem  ca.pem

7.2.3 生成使用者證書

生成使用者test的證書請求檔案。

root@k8scludes1:~/TLS-ingress/tls# cfssl print-defaults csr >test-csr.json

root@k8scludes1:~/TLS-ingress/tls# cat test-csr.json 
{
    "CN": "example.net",
    "hosts": [
        "example.net",
        "www.example.net"
    ],
    "key": {
        "algo": "ecdsa",
        "size": 256
    },
    "names": [
        {
            "C": "US",
            "ST": "CA",
            "L": "San Francisco"
        }
    ]
}

修改test-csr.json,"www.nginxx.com"表示申請的證書只給www.nginxx.com使用,hosts引數為空的話,便是所有的站點都可以使用該證書。

root@k8scludes1:~/TLS-ingress/tls# vim test-csr.json 

root@k8scludes1:~/TLS-ingress/tls# cat test-csr.json 
{
    "CN": "nginxx.com",
    "key": {
        "algo": "ecdsa",
        "size": 256
    },
    "hosts":[
          "www.nginxx.com"
    ], 
    "names": [
        {
            "C": "CN",
            "ST": "guangdon",
            "L": "guangzhou"
        }
    ]
}

root@k8scludes1:~/TLS-ingress/tls# ls
ca-config.json  ca.csr  ca-csr.json  ca-key.pem  ca.pem  test-csr.json

把test使用者的證書請求檔案發給CA,讓CA審批,ca.pem是ca公鑰,ca-key.pem是ca私鑰,ca-config.json是ca配置檔案,profile指定為www,最後的使用者名稱test是給使用者頒發證書名字的字首。

root@k8scludes1:~/TLS-ingress/tls# cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=www test-csr.json | cfssljson -bare test
2022/04/22 16:05:02 [INFO] generate received request
2022/04/22 16:05:02 [INFO] received CSR
2022/04/22 16:05:02 [INFO] generating key: ecdsa-256
2022/04/22 16:05:02 [INFO] encoded CSR
2022/04/22 16:05:02 [INFO] signed certificate with serial number 468422675225780030350363273085193709570812230921

root@k8scludes1:~/TLS-ingress/tls# ls
ca-config.json  ca.csr  ca-csr.json  ca-key.pem  ca.pem  test.csr  test-csr.json  test-key.pem  test.pem

test-key.pem是test使用者的私鑰,test.pem是ca給test頒發的證書(也就是公鑰)。

root@k8scludes1:~/TLS-ingress/tls# ls test*
test.csr  test-csr.json  test-key.pem  test.pem

7.2.4 使用自定義的證書

接下來要替換掉ingress-nginx-controller自帶的證書,使用我們生成的證書。

檢視secret。

root@k8scludes1:~/TLS-ingress/tls# kubectl get secrets 
NAME                  TYPE                                  DATA   AGE
default-token-mxb4r   kubernetes.io/service-account-token   3      3d23h

建立一個tls型別的secret,裡面包含test使用者的私鑰和證書,檢視tls型別的secret的語法。

root@k8scludes1:~/TLS-ingress/tls# kubectl create secret tls --help
Create a TLS secret from the given public/private key pair.

 The public/private key pair must exist beforehand. The public key certificate must be .PEM encoded and match the given
private key.

Examples:
  # Create a new TLS secret named tls-secret with the given key pair
  kubectl create secret tls tls-secret --cert=path/to/tls.cert --key=path/to/tls.key

Options:
      --allow-missing-template-keys=true: If true, ignore any errors in templates when a field or map key is missing in
the template. Only applies to golang and jsonpath output formats.
      --append-hash=false: Append a hash of the secret to its name.
      --cert='': Path to PEM encoded public key certificate.
      --dry-run='none': Must be "none", "server", or "client". If client strategy, only print the object that would be
sent, without sending it. If server strategy, submit server-side request without persisting the resource.
      --field-manager='kubectl-create': Name of the manager used to track field ownership.
      --key='': Path to private key associated with given certificate.
  -o, --output='': Output format. One of:
json|yaml|name|go-template|go-template-file|template|templatefile|jsonpath|jsonpath-as-json|jsonpath-file.
      --save-config=false: If true, the configuration of current object will be saved in its annotation. Otherwise, the
annotation will be unchanged. This flag is useful when you want to perform kubectl apply on this object in the future.
      --show-managed-fields=false: If true, keep the managedFields when printing objects in JSON or YAML format.
      --template='': Template string or path to template file to use when -o=go-template, -o=go-template-file. The
template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview].
      --validate=true: If true, use a schema to validate the input before sending it

Usage:
  kubectl create secret tls NAME --cert=path/to/cert/file --key=path/to/key/file [--dry-run=server|client|none]
[options]

Use "kubectl options" for a list of global command-line options (applies to all commands).

建立一個tls型別的secret,裡面包含test使用者的私鑰和證書。

root@k8scludes1:~/TLS-ingress/tls# ls
ca-config.json  ca.csr  ca-csr.json  ca-key.pem  ca.pem  test.csr  test-csr.json  test-key.pem  test.pem

root@k8scludes1:~/TLS-ingress/tls# kubectl create secret tls test-tls-secret --cert=test.pem --key=test-key.pem
secret/test-tls-secret created

secrets建立成功。

root@k8scludes1:~/TLS-ingress/tls# kubectl get secrets 
NAME                  TYPE                                  DATA   AGE
default-token-mxb4r   kubernetes.io/service-account-token   3      3d23h
test-tls-secret       kubernetes.io/tls                     2      8s

刪除現有的ingress規則。

root@k8scludes1:~/TLS-ingress/tls# pwd
/root/TLS-ingress/tls

root@k8scludes1:~/TLS-ingress/tls# kubectl get ingress
NAME         CLASS    HOSTS                            ADDRESS           PORTS   AGE
my-ingress   <none>   www.nginx13.com,www.nginx2.com   192.168.110.129   80      46h

root@k8scludes1:~/TLS-ingress/tls# cd ../

root@k8scludes1:~/TLS-ingress# ls
bak  certgen15.tar  controller1.tar  deploy.yaml  deploy.yaml.1  deploy.yml  ingress-rule.yaml  pod.yaml  tls  vim

root@k8scludes1:~/TLS-ingress# kubectl delete -f ingress-rule.yaml 
ingress.networking.k8s.io "my-ingress" deleted
root@k8scludes1:~/TLS-ingress# kubectl get ingress
No resources found in tls-ingress namespace.

修改ingress規則,ingress-rule.yaml裡指定了tls的域名資訊和secret,secret裡包含了證書。

root@k8scludes1:~/TLS-ingress# vim ingress-rule.yaml 

root@k8scludes1:~/TLS-ingress# cat ingress-rule.yaml 
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-ingress
  annotations:
    kubernetes.io/ingress.class: "nginx"
spec:
  tls: 
  - hosts: 
    - www.nginxx.com
    secretName: test-tls-secret
  rules:
  - host: www.nginx13.com
    http:
      paths:
      #訪問網址目錄
      - path: /
        pathType: Prefix
        backend:
          service:
            name: nginx1svc
            port:
              number: 80
      - path: /ingress
        pathType: Prefix
        backend:
          service:
            name: nginx3svc
            port:
              number: 80

  - host: www.nginx2.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: nginx2svc
            port:
              number: 80

應用ingress規則。

root@k8scludes1:~/TLS-ingress# kubectl apply -f ingress-rule.yaml 
ingress.networking.k8s.io/my-ingress created

檢視ingress規則,現在已經有443埠了,http對應80埠,https對應443埠。

root@k8scludes1:~/TLS-ingress# kubectl get ingress -o wide
NAME         CLASS    HOSTS                            ADDRESS   PORTS     AGE
my-ingress   <none>   www.nginx13.com,www.nginx2.com             80, 443   16s

443埠對應的是31473埠,因為secret裡包含了證書,會自動覆蓋ingress-nginx-control裡面的證書。

root@k8scludes1:~/TLS-ingress# kubectl get svc -o wide -n ingress-nginx
NAME                                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE   SELECTOR
ingress-nginx-controller             NodePort    10.98.61.146    <none>        80:31853/TCP,443:31473/TCP   46h   app.kubernetes.io/component=controller,app.kubernetes.io/instance=ingress-nginx,app.kubernetes.io/name=ingress-nginx
ingress-nginx-controller-admission   ClusterIP   10.102.212.60   <none>        443/TCP                      46h   app.kubernetes.io/component=controller,app.kubernetes.io/instance=ingress-nginx,app.kubernetes.io/name=ingress-nginx

下面使用Linux客戶端進行訪問,客戶端的/etc/hosts如下:

[root@etcd2 ~]# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.110.133 etcd1
192.168.110.131 etcd2
192.168.110.132 etcd3
192.168.110.129 www.nginx13.com
192.168.110.129 www.nginx2.com

使用https訪問,可以發現證書為ingress自帶的證書:CN=Kubernetes Ingress Controller Fake Certificate。

[root@etcd2 ~]# curl -kv https://www.nginx13.com:31473/ingress/index.html
* About to connect() to www.nginx13.com port 31473 (#0)
*   Trying 192.168.110.129...
* Connected to www.nginx13.com (192.168.110.129) port 31473 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
* skipping SSL peer certificate verification
* SSL connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* Server certificate:
* 	subject: CN=Kubernetes Ingress Controller Fake Certificate,O=Acme Co
* 	start date: 4月 22 04:09:27 2022 GMT
* 	expire date: 4月 22 04:09:27 2023 GMT
* 	common name: Kubernetes Ingress Controller Fake Certificate
* 	issuer: CN=Kubernetes Ingress Controller Fake Certificate,O=Acme Co
> GET /ingress/index.html HTTP/1.1
> User-Agent: curl/7.29.0
> Host: www.nginx13.com:31473
> Accept: */*
> 
< HTTP/1.1 200 OK
< Date: Fri, 22 Apr 2022 08:51:08 GMT
< Content-Type: text/html
< Content-Length: 4
< Connection: keep-alive
< Last-Modified: Fri, 22 Apr 2022 07:17:09 GMT
< ETag: "62625675-4"
< Accept-Ranges: bytes
< Strict-Transport-Security: max-age=15724800; includeSubDomains
< 
333
* Connection #0 to host www.nginx13.com left intact

Windows客戶端使用https訪問服務,瀏覽器輸入https://www.nginx13.com:31473/ingress/index.html,我們檢視證書。

image-20230728165144101

發現證書是ingress自帶的證書。

image-20230728165213137

繼續訪問。

image-20230728165248610

可以發現,現在可以https訪問ingress了,但是證書不是我們自己生成的那個證書,而是ingress自帶的證書,是因為我們自己生成的證書只給域名www.nginxx.com使用,其他域名訪問只能使用ingress自帶的證書。

image-20230728165322147

現在修改ingress規則,使其使用我們自己生成的證書。

刪除ingress規則。

root@k8scludes1:~/TLS-ingress# ls
bak  certgen15.tar  controller1.tar  deploy.yaml  deploy.yaml.1  deploy.yml  ingress-rule.yaml  pod.yaml  tls  vim

root@k8scludes1:~/TLS-ingress# kubectl delete -f ingress-rule.yaml 
ingress.networking.k8s.io "my-ingress" deleted

root@k8scludes1:~/TLS-ingress# kubectl get ingress
No resources found in tls-ingress namespace.

修改ingress規則,host域名只有www.nginxx.com。

root@k8scludes1:~/TLS-ingress# vim ingress-rule.yaml 

root@k8scludes1:~/TLS-ingress# cat ingress-rule.yaml 
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-ingress
  annotations:
    kubernetes.io/ingress.class: "nginx"
spec:
  tls: 
  - hosts: 
    - www.nginxx.com
    secretName: test-tls-secret
  rules:
  - host: www.nginxx.com
    http:
      paths:
      #訪問網址目錄
      - path: /
        pathType: Prefix
        backend:
          service:
            name: nginx1svc
            port:
              number: 80
      - path: /ingress
        pathType: Prefix
        backend:
          service:
            name: nginx3svc
            port:
              number: 80
      - path: /n2
        pathType: Prefix
        backend:
          service:
            name: nginx2svc
            port:
              number: 80

應用ingress規則。

root@k8scludes1:~/TLS-ingress# kubectl apply -f ingress-rule.yaml 
ingress.networking.k8s.io/my-ingress created

root@k8scludes1:~/TLS-ingress# kubectl get ingress -o wide
NAME         CLASS    HOSTS            ADDRESS           PORTS     AGE
my-ingress   <none>   www.nginxx.com   192.168.110.129   80, 443   2m6s

root@k8scludes1:~/TLS-ingress# kubectl get svc -o wide -n ingress-nginx
NAME                                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE   SELECTOR
ingress-nginx-controller             NodePort    10.98.61.146    <none>        80:31853/TCP,443:31473/TCP   47h   app.kubernetes.io/component=controller,app.kubernetes.io/instance=ingress-nginx,app.kubernetes.io/name=ingress-nginx
ingress-nginx-controller-admission   ClusterIP   10.102.212.60   <none>        443/TCP                      47h   app.kubernetes.io/component=controller,app.kubernetes.io/instance=ingress-nginx,app.kubernetes.io/name=ingress-nginx

首先修改Linux客戶端的/etc/hosts,新增IP域名對映:192.168.110.129 www.nginxx.com。

[root@etcd2 ~]# vim /etc/hosts

[root@etcd2 ~]# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.110.133 etcd1
192.168.110.131 etcd2
192.168.110.132 etcd3
192.168.110.129 www.nginx13.com
192.168.110.129 www.nginx2.com
192.168.110.129 www.nginxx.com

使用Linux客戶端訪問服務,使用https訪問,發現證書變為我們自定義的nginxx.com。

[root@etcd2 ~]# curl -kv https://www.nginxx.com:31473/ingress/index.html
* About to connect() to www.nginxx.com port 31473 (#0)
*   Trying 192.168.110.129...
* Connected to www.nginxx.com (192.168.110.129) port 31473 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
* skipping SSL peer certificate verification
* SSL connection using TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
* Server certificate:
* 	subject: CN=nginxx.com,L=guangzhou,ST=guangdon,C=CN
* 	start date: 4月 22 08:00:00 2022 GMT
* 	expire date: 4月 22 08:00:00 2023 GMT
* 	common name: nginxx.com
* 	issuer: CN=nginxx.com,L=guangzhou,ST=guangdon,C=CN
> GET /ingress/index.html HTTP/1.1
> User-Agent: curl/7.29.0
> Host: www.nginxx.com:31473
> Accept: */*
> 
< HTTP/1.1 200 OK
< Date: Fri, 22 Apr 2022 09:26:31 GMT
< Content-Type: text/html
< Content-Length: 4
< Connection: keep-alive
< Last-Modified: Fri, 22 Apr 2022 07:17:09 GMT
< ETag: "62625675-4"
< Accept-Ranges: bytes
< Strict-Transport-Security: max-age=15724800; includeSubDomains
< 
333
* Connection #0 to host www.nginxx.com left intact

下面使用Windows客戶端訪問服務,檢視證書。

image-20230728174014215

證書為我們自定義的證書。

image-20230728174049992

繼續訪問。

image-20230728174122626

image-20230728174148127

現在既可以使用https訪問服務,又使用了我們自定義的證書了。

八.總結

透過本文,您應該瞭解瞭如何在Kubernetes叢集中為Ingress配置HTTPS。透過使用TLS證書和適當的Ingress配置,您可以確保服務與客戶端之間的通訊是安全和加密的。

相關文章