使用Functiongraph同步Cert-manager证书到ELB,实现证书定期自动轮转
一. 使用Cert-manager自动轮转Prestashop网站证书,证书在Nginx生效
架构图
环境准备
# 创建CCE集群和node节点。
# 申请公网EIP,EIP绑定到node节点,node节点可以访问外网下载资源
# 申请TCP/UDP类型的ELB,用于绑定ingress
# 安装ingress插件,数量和规格都可自行调整
# 登录node节点,配置kubectl
工具准备
安装helm
# 登录配置了kubectl的Node节点
cd /opt wget https://get.helm.sh/helm-v3.13.2-linux-amd64.tar.gz tar -zxvf helm-v3.13.2-linux-amd64.tar.gz cd linux-amd64/ cp -f helm /usr/local/bin/ |
安装Git
yum install git
安装cert-manager及ClusterIssuer
# install cert-manager
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.12.0/cert-manager.yaml
# install cert-manager-webhook-huaweidns
kubectl apply -f https://github.com/innoai-tech/cert-manager-webhook-huaweidns/releases/download/latest/cert-manager-webhook-huaweidns.yaml
# install ClusterIssuer(修改红色部分的参数)
新建ClusterIssuer.yaml文件,内容如下
apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: letsencrypt-prod spec: acme: email: <email> preferredChain: '' privateKeySecretRef: name: letsencrypt-prod server: https://acme-v02.api.letsencrypt.org/directory solvers: - dns01: webhook: config: appKey: <appKey> # iam用户AK appSecret: <appSecret> # iam用户SK region: <region> # 域名所在 region zoneId: <zoneId> # 域名 zoneId groupName: acme.innoai.tech solverName: huawei-dns
|
其中,region参数填写CCE集群所在的regionID,zoneId通过API Explorer 查询
文件准备好后,执行命令创建
kubectl apply -f ClusterIssuer.yaml
本章节参考资料:https://www.cnblogs.com/gshelldon/p/17787386.html
安装prestashop
# 下载包
mkdir /opt/ bitnami cd /opt/bitnami/ git clone https://github.com/bitnami/charts.git cp /opt/bitnami/charts/prestashop/values.yaml /opt/bitnami/ chmod 777 values.yaml |
#修改values.yaml
# prestashop及DB的用户名密码根据需要自行修改
# pvc卷声明必须修改(增加红色字体部分)
persistence: ## @param persistence.enabled Enable persistence using PVC ## enabled: true ## @param persistence.storageClass PrestaShop Data Persistent Volume Storage Class ## If defined, storageClassName: <storageClass> ## If set to "-", storageClassName: "", which disables dynamic provisioning ## If undefined (the default) or set to null, no storageClassName spec is ## set, choosing the default provisioner. (gp2 on AWS, standard on ## GKE, AWS & OpenStack) ## annotations: everest.io/disk-volume-type: SAS volume.beta.kubernetes.io/storage-provisioner: everest-csi-provisioner volume.kubernetes.io/storage-provisioner: everest-csi-provisioner storageClass: "csi-disk"
## @param persistence.accessModes PVC Access Mode for PrestaShop volume ## accessModes: - ReadWriteOnce ## @param persistence.size PVC Storage Request for PrestaShop volume ## size: 8Gi |
# service type必须修改,默认安装为LB类型,这里需要修改成集群内访问
service: ## @param service.type Kubernetes Service type ## type: ClusterIP |
# Ingress开关必须修改,默认ingress是false,这里需要打开,在ingress层做证书校验
ingress: ## @param ingress.enabled Enable ingress controller resource ## enabled: true 。。此处省略其他默认参数。。。 hostname: <自定义域名> annotations: kubernetes.io/ingress.class: nginx #定义证书签发者,这里设置后 cert-manager会自动生成证书 cert-manager.io/cluster-issuer: letsencrypt-prod #定义证书有效时间,到期会自动生成和替换 cert-manager.io/renew-before: 1h tls: true |
#安装prestashop
helm install my-release oci://registry-1.docker.io/bitnamicharts/prestashop -f values.yaml
# 检查各组件状态
到CCE Console,查看负载、服务、服务路由、秘钥等状态是否正常,如有异常查看日志。
秘钥状态可登录node节点,执行命令查看ready状态是否为True:kubectl get cert -A
# 登录Prestashop管理员界面,修改TLS开关
http://<你的网站域名>/administration
备注:登录Node节点,查看管理员账号和密码:
- 账号是yaml配置文件中的prestashopEmail参数
- 密码通过如下命令查询:
export APP_PASSWORD=$(kubectl get secret --namespace default my-release-prestashop -o jsonpath="{.data.prestashop-password}" | base64 -d)
echo $APP_PASSWORD
配置DNS域名解析
添加记录集
IP配置为ELB的公网IP
测试
登录Node节点,执行命令
wget https://<你的网站域名>
或者打开浏览器,输入
https://<你的网站域名>
查看证书
设置证书定期轮转时间
参考https://cert-manager.io/docs/usage/ingress/,配置证书重置策略
更新Nginx的路由yaml文件,添加如下字段:
cert-manager.io/renew-before: 360h
证书会在过期前的360h自动更新。
可以登录CCE Node节点,查看证书的过期时间
二. 使用ELB模式WAF防护Prestashop网站,证书在ELB生效
架构图
资源准备
购买7层ELB
使用与CCE相同region, VPC和子网,创建最小规格的应用型独享ELB,创建时选择带公网EIP
在ELB服务下创建证书
获取证书
Prestashop及Cert-manager网站在CCE部署成功后,CCE容器的“配置与秘钥”界面可以看到,Prestashop网站的证书被Cert-manager定期更新,在这里复制对应证书的内容。
创建证书
ELB服务界面下,点证书管理,选择创建证书,此处创建证书用于https监听器
创建监听器
创建http监听器,创建时不添加后端服务器
创建https监听器,创建时不添加后端服务器,证书参考“在ELB服务下创建证书”章节
购买独享WAF
使用与CCE相同region, VPC, 子网, 安全组,创建最小规格的单实例独享WAF
添加WAF实例作为7层ELB的后端服务器
查看7层ELB后端服务器监控检查结果
默认健康检查的协议和端口与监听器的配置一样,如果健康检查失败,可以修改健康检查的协议为TCP
在WAF服务下创建证书(使用ELB+WAF模式时此步不需要)
在WAF服务界面,找到“对象管理”“证书管理”,添加证书
此处添加的证书,与Cert-manager定期颁发的证书保持一致
添加防护网站
在WAF服务界面,找到“网站设置”,添加防护网站,选择独享WAF模式,填写参数
修改DNS域名解析
在ELB服务界面,查看7层ELB的公网IP地址
在DNS服务界面,修改域名记录集的IP值
域名修改后,访问网站域名至少20次,触发WAF工作。
查看网站接入状态
三. 使用FunctionGraph定期更新ELB证书
架构图
依赖包准备
制作ELB依赖包
参考https://support.huaweicloud.com/devg-functiongraph/functiongraph_02_0616.html,制作ELB的python3.9依赖包
制作ELB依赖包案例:
cd /opt wget https://github.com/huaweicloud/huaweicloud-sdk-python-v3/archive/refs/heads/master.zip unzip master.zip cd huaweicloud-sdk-python-v3-master/huaweicloud-sdk-elb/
python3 setup.py install --root /tmp/pyelb cd /tmp/pyelb/ cd usr/local/python3/lib/python3.9/site-packages/ zip -rq pyelb.zip * obsutil cp pyelb.zip obs://wangji-test/ |
在Functiongraph服务界面创建依赖包
制作kubernetes依赖包(不是CCE SDK)
参考https://support.huaweicloud.com/devg-functiongraph/functiongraph_02_0616.html,制作kubernetes的python3.9依赖包
mkdir /tmp/kubectl cd /tmp/kubectl/ pip3 install kubernetes --root /tmp/kubectl cd usr/local/python3/lib/python3.9/site-packages/ zip -rq kubernetes.zip *mv kubernetes.zip /opt/ cd /opt obsutil cp kubernetes.zip obs://wangji-test/ |
制作WAF依赖包(如果不用ELB模式的WAF,同步证书到WAF)
配置K8s事件日志采集到LTS
Functiongraph函数
创建空白函数
进入Functiongraph console,创建空白函数,选择python3.9开发语言,委托需选择具有LTS权限的
设置函数相关配置
- 触发器设置
创建LTS触发器,选择K8s事件日志所在的日志组和日志流
- 确认权限配置
- 超时时间设置
- 网络配置确认
Functiongraph可以通过VPC或者EIP访问CCE资源,采用EIP访问时,CCE的集群需要关联EIP,kubectl配置项需要包含externalCluster的信息。
- 环境变量设置
- 访问ELB的资源,这里采用ELB的SDK,需要配置鉴权参数AK/SK,及待更新的ELB对象ID参数
- 访问CCE的资源,这里采用的开源kubernetes的SDK,通过配置文件的方式设置环境变更
新建名称为config的配置文件,文件内容从CCE界面的kubectl配置获取。
注意访问CCE资源可采用VPC或者EIP方式,如果采用VPC方式,需要在函数设置->网络配置界面 打开VPC访问开关;如果采用EIP方式,需要为CCE集群绑定EIP,并获取带有externalCluster信息的kubectl配置文件。
- 访问Cert-manager定期更新的证书名称及名空间,通过配置文件的方式设置环境变量
函数代码
对LTS日志过滤,判断是否为Cert更新证书事件
import base64 import json
def handler(event, context): certificate_name = context.getUserData('certificate_name', '').strip() namespace_name = context.getUserData('namespace_name', '').strip()
encodingData = event["lts"]["data"] data = base64.b64decode(encodingData) body = json.loads(data) logs = body['logs'] messages = json.loads(logs)
for line in messages: lineStr = json.dumps(line) message_i = line["message"] message_i = json.loads(message_i) print(message_i) print(message_i['resource_name']) if message_i['resource_name'] == certificate_name and message_i['namespace'] == namespace_name: print("cert has event log...") |
获取certificate证书内容
# coding: utf-8 from huaweicloudsdkcore.auth.credentials import BasicCredentials from huaweicloudsdkcore.exceptions import exceptions from kubernetes import client, config, watch import os import base64
# Set config file path config_file = "/opt/function/code/config" config.load_kube_config(config_file=config_file)
client_v1 = client.CoreV1Api() result = client_v1.read_namespaced_secret(name=certificate_name, namespace=namespace_name)
#print("print secret detail...") #print(result.data) certificate = base64.b64decode(result.data['tls.crt']) private_key = base64.b64decode(result.data['tls.key']) |
更新ELB证书
from huaweicloudsdkcore.auth.credentials import BasicCredentials from huaweicloudsdkelb.v2.region.elb_region import ElbRegion from huaweicloudsdkcore.exceptions import exceptions from huaweicloudsdkelb.v2 import *
# update elb cert ak = context.getUserData('CLOUD_SDK_AK', '').strip() sk = context.getUserData('CLOUD_SDK_SK', '').strip() elb_certificate_id = context.getUserData('elb_certificate_id', '').strip() credentials = BasicCredentials(ak, sk) \
elb_client = ElbClient.new_builder() \ .with_credentials(credentials) \ .with_region(ElbRegion.value_of("ap-southeast-1")) \ .build()
print("update elb secret...")
try: request = UpdateCertificateRequest() request.certificate_id = elb_certificate_id request.body = UpdateCertificateRequestBody( private_key=private_key, certificate=certificate ) response = elb_client.update_certificate(request) print(response) except exceptions.ClientRequestException as e: print(e.status_code) print(e.request_id) print(e.error_code) print(e.error_msg) |
- 点赞
- 收藏
- 关注作者
评论(0)