Kubernetes集群中配置Ingress支持HTTPS访问(一):cfssl

时间:2024-05-22 17:53:50

目录
  • 一.系统环境
  • 二.前言
  • 三.对称加密和非对称加密简介
  • 四.什么是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配置,您可以确保服务与客户端之间的通信是安全和加密的。