自动化证书管理
1.功能描述
由于ssl证书签发的有效期越来越短,运维人员花在证书管理的成本将成倍增加。结合NJet动态证书管理的能力,在初次配置后,NJet可以通过标准的ACME协议实现证书的自动签发、续签,并自动更新到服务器中,从而实现无需重启,就可以保持证书的有效性,保证业务的持续安全。
cert-manager实现的主要功能如下:
- 为server块中的server_name域名实现证书自动申请、续签。
- 支持http-01、tls-alpn-01两种挑战(challenge)验证方式。
- 应用自动签发的证书。通过实现njet https server 块ssl_certificate_key和ssl_certificate指令自动配置(调用njet ssl API实现,即动态证书)来应用证书。
- 管里面admin API,实现磁盘中证书文件的管理。
- 支持集群证书同步
由于njet本身的限制,每个server块在启动时需要预先配置证书,所以用户需要自己提前生成证书。后续使能了acme特性后,证书由cert-manager管理。
cert_manager是一个独立的go 实现的程序,由NJetCoPilot框架驱动。
证书申请流程
用户配置中使能acme特性后,会触发acme证书申请,可以通过静态或动态两种方式使能。使能后证书申请流程如下:
证书申请流程(自动化创建证书)
2.依赖模块
helper cert-manager modules/njt_helper_go_copilot_module.so conf/config.toml; #cert-manager
3.配置说明
ssl_certificate_management acme
Syntax: | ssl_certificate_management acme; |
---|---|
Default: | - |
Context: | http main, server |
目前只支持acme
在现有http_ssl 模块新增一个ssl_management 指令
静态配置文件中配置了该指令,在worker启动或重启时,由pa进程发送新增消息到mqtt
动态添加vs时,配置了该指令,会发送新增消息到mqtt
动态删除vs时,如果添加时配置了该指令,则会发送删除的消息到mqtt
config.toml配置参数
[copilot]
progName="sbin/cert-manager"
copilotType="cert"
stdoutFile = "/usr/local/njet/logs/cert_stdout.log"
stderrFile = "/usr/local/njet/logs/cert_stderr.log"
[acme]
email="liuqi@lq.com"
challengeType="tls"
acmeCaDir="http://127.0.0.1:4001/directory" #部署的acme ca的ip
tlsChallengeServerAddress=":10443"
[njet]
[admin]
[mqtt]
mqttServerAddress="127.0.0.1:1883"
[log]
v=2
copilot块下的参数不属于cert-manager程序的参数,为njet copilot框架标准参数。其余块为cert-manager程序的参数,主要有5块:acme、mqtt、admin、njet、log
acme常用的相关参数:
acme相关参数 | |||||
---|---|---|---|---|---|
参数字段 | 参数名称 | 类型 | 是否必填 | 默认值 | 说明 |
邮箱地址 | 字符串 | 是 | 空 | acme账号 | |
acmeCaDir | Acme ca地址 | 字符串 | 可选 | https://acme-v02.api.letsencrypt.org/directory | ca地址 |
challengeType | 挑战类型 | 字符串 | 可选 | http | http、tls |
tlsChallengeServerAddress | tls挑战验证server地址 | 字符串 | 可选 | :443 | 格式:ip:port、:port |
httpChallengeServerAddress | http挑战验证server地址 | 字符串 | 可选 | :80 | 格式:ip:port、:port |
proxyHttpChallengeServerIp | Njet http 挑战验证server ip | 字符串 | challengeType为http时,必填 | 空 | Njet 80端口监听的地址,cert-manager用来创建http 挑战验证location,动态location API使用。比如:如果njet使用 listen 80指令,这里为0.0.0.0 |
keyType | 证书私钥类型 | 字符串 | 可选 | ec256 | Supported: rsa2048, rsa3072, rsa4096, rsa8192, ec256, ec384 |
days | 证书续签剩余天数 | 整数 | 可选 | 30 |
mqtt相关参数:
连接mqtt server的参数
acme相关参数 | |||||
---|---|---|---|---|---|
参数字段 | 参数名称 | 类型 | 是否必填 | 默认值 | 说明 |
mqttServerAddress | mqtt server地址 | 字符串 | 可选 | 127.0.0.1:1883 |
Admin API相关参数:
内置admin http server相关参数
Admin 相关参数 | |||||
---|---|---|---|---|---|
参数字段 | 参数名称 | 类型 | 是否必填 | 默认值 | 说明 |
ip | cert-manager admin API监听的ip | 字符串 | 可选 | 127.0.0.1 | |
port | cert-manager admin API监听的port | 字符串 | 可选 | 2000 |
NJET相关参数:
与NjET 控制面交互的参数
njet相关参数 | |||||
---|---|---|---|---|---|
参数字段 | 参数名称 | 类型 | 是否必填 | 默认值 | 说明 |
ctrlServerAddress | Njet ctrl地址 | 字符串 | 可选 | 127.0.0.1:8081 | njet动态API调用使用,比如ssl、location |
schema | Schema类型 | 字符串 | 可选 | http | http、https |
Log相关参数:
cert-manager控制日志输出的参数
log相关参数 | |||||
---|---|---|---|---|---|
参数字段 | 参数名称 | 类型 | 是否必填 | 默认值 | 说明 |
v | 日志级别 | 整数 | 可选 | 0(info) | 1(debug)、2(trace) |
4.调用样例
本地搭建acme ca(letsencrypt)。
NJET 部署:
cert-manager作为NJET的copilot进程运行。njet.conf配置如下:
worker_processes auto;
cluster_name njet;
node_name node1;
error_log logs/error.log info;
helper ctrl modules/njt_helper_ctrl_module.so conf/njet_ctrl.conf;
helper broker modules/njt_helper_broker_module.so conf/mqtt.conf;
helper cert-manager modules/njt_helper_go_copilot_module.so conf/config.toml; #cert-manager 必须配置
load_module modules/njt_agent_dynlog_module.so;
load_module modules/njt_dyn_ssl_module.so;
load_module modules/njt_http_location_module.so;
load_module modules/njt_http_dyn_server_module.so;
events {
worker_connections 1024;
}
http {
include mime.types;
access_log off;
access_log_write_zone on;
access_log_zone log_zone 100m;
access_log_zone_valid 3;
server {
listen 80;
server_name vs10.com; #http挑战配置的域名要和申请证书的server 的域名保持一样
location / {
return 200 "test.com port 80 /";
}
}
server {
listen 80;
server_name vs.com;
location / {
return 200 "vs.com port 80 /";
}
}
server {
listen 50010 ssl;
server_name vs10.com;
ssl_protocols TLSv1.2;
ssl_certificate /usr/local/njet/certs/server.crt;
ssl_certificate_key /usr/local/njet/certs/server.key;
ssl_certificate_management acme;
location /{
return 200 "vs10.com ssl\n";
}
}
}
cert-manager配置文件config.toml:
[copilot]
progName="sbin/cert-manager"
copilotType="cert"
stdoutFile = "/usr/local/njet/logs/cert_stdout.log"
stderrFile = "/usr/local/njet/logs/cert_stderr.log"
[acme]
email="liuqi@lq.com"
challengeType="tls"
acmeCaDir="http://127.0.0.1:4001/directory" #部署的acme ca的ip
tlsChallengeServerAddress=":10443"
[njet]
[admin]
[mqtt]
mqttServerAddress="192.168.40.156:1883" #和mqtt.conf 配置的ip 和端口要一致
[log]
v=2
mqtt.conf:mqtt通过tcp通讯,所以需要配置mqtt.conf
log_dest file logs/mosquitto.log
log_type debug
log_type information
log_type error
log_type warning
log_type notice
allow_anonymous true
persistence_location /home/test_bridge/data/
listener 0 /home/limin/test_bridge/data/mosquitto.sock
listener 1883 192.168.40.156
4.1为一个域名自动颁发证书
使用ssl 挑战验证方式,进行证书签发。为域名vs9.com颁发证书(通过njet relaod方式,使vs9.com生效)。
njet.conf配置:
http {
server {
listen 50010 ssl;
server_name vs8.com;
ssl_certificate server.crt;
ssl_certificate_key server.key;
// 启用acme特性
ssl_certificate_management acme;
location /{
return 200 "vs8.com ssl\n";
}
}
}
// 10443为cert-manager程序内部验证tls_alpn_01挑战的端口
stream {
map $ssl_preread_alpn_protocols $acme_tls_alpn_01_challenge_port {
~\bacme-tls/1\b 10443;
// njet内部ssl端口
default 50010;
}
server {
listen 443;
listen [::]:443;
proxy_pass 127.0.0.1:$acme_tls_alpn_01_challenge_port;
ssl_preread on;
}
}
Tls 443挑战配置:
cert-manager配置文件:
[copilot]
progName="sbin/cert-manager"
copilotType="cert"
stdoutFile = "/usr/local/njet/logs/cert_stdout.log"
stderrFile = "/usr/local/njet/logs/cert_stderr.log"
[acme]
email="aa@lq.com"
challengeType="tls"
acmeCaDir="http://127.0.0.1:4001/directory"
tlsChallengeServerAddress=":10443"
[njet]
[mqtt]
mqttServerAddress="192.168.40.156:1883"
[log]
v=2
reload前的NJET配置状态
修改域名为vs10.com,重新reload:
mqtt日志:
cert-manager日志:
NJET配置:
证书文件查看:
4.2证书续签
cert-manager定期检查证书列表,计算哪些证书应该进行续签操作,默认情况下有效期不到30天的证书会进行续签操作,此参数可以在程序启动时重写默认值。
证书续签成功后,应用自动签发的证书,即实现NJET https server 块ssl_certificate_key和ssl_certificate指令自动配
4.3挑战
cert-manager内置实现了tls、http挑战验证server,acme ca验证域名所有权时cert-manager会启动server,验证后关闭server。
http-01
cert-manager http挑战验证server默认监听80端口。
如果NJET worker占用了80端口,那么http 挑战验证需要由NJET worker转发到cert-manager,njet.conf只需配置80端口对应的server_name即可,挑战location cert-manager负责创建。
在创建挑战location时,cert-manager需要知道location 关联的listen ip,这个参数在cert-manager启动时需要指定,toml配置文件参数为proxyHttpChallengeServerIp。例如我们为vs12.com域名颁发证书,且通过http-01挑战验证,njet.conf中需要配置对应server(原因是:cert-manager动态为vs12.com server创建挑战location),配置样例如下:
//njet.conf
// http挑战需要手动配置一个80端口的server,且server_name 和申请证书的域名一样
//挑战location不需要配置,cert-manager会自动创建。
server {
listen 80;
server_name vs12.com;
location /
{
return 200 "vs12.com port 80 /";
}
}}// 为vs12.com颁发证书
http {
server {
listen 50010 ssl;
server_name vs12.com;
ssl_certificate server.crt;
ssl_certificate_key server.key;
ssl_certificate_management acme;location /{
return 200 "vs12.com ssl\n";
}
}
}
cert-manager创建的挑战的location(生成证书的时候自动创建,不需要手动创建)
如果NJET worker不占用80,挑战验证就完全由cert-manager完成,不需要用户在njet.conf中创建对应域名的srever块,弊端是用户不能通过80端口访问NJET代理的http服务。
tls-alpn-01
cert-manager tls挑战验证server默认监听443端口。
如果NJET worker占用443端口,443端口也不能由https监听,只能由stream监听,我们需要用到NJET的ssl预读功能。那么tls挑战验证请求需要由NJET worker stream转发到cert-manager,https server需要使用内部端口,请求也由stream转发。配置如下:
http {
server {
listen 50010 ssl;
server_name vs1.com;
ssl_certificate server.crt;
ssl_certificate_key server.key;
// 启用acme特性
ssl_certificate_management acme;
location /{
return 200 "vs1.com ssl\n";
}
}
}
// 10443为cert-manager程序内部验证tls_alpn_01挑战的端口
stream {
map $ssl_preread_alpn_protocols $acme_tls_alpn_01_challenge_port {
~\bacme-tls/1\b 10443;
// njet内部http ssl端口
default 50010;
}
server {
listen 443;
listen [::]:443;
proxy_pass 127.0.0.1:$acme_tls_alpn_01_challenge_port;
ssl_preread on;
}
}
4.4证书管理admin API
除了mqtt证书删除事件可以删除磁盘上的证书文件,有的场景无法触发删除事件。所以cert-manager内置了一个HTTP server(称为admin API,即管理面API),提供HTTP API的方式去管理证书,目前支持get、list、delete证书。API定义如下:
API类型 | URL | HTTP方法 | 说明 |
---|---|---|---|
获取指定证书 | http://127.0.0.0:2000/api/v1//cert/{domain} | GET | 2000为admin API默认监听的端口。{domain}为域名 |
删除指定证书 | http://127.0.0.0:2000/api/v1//cert/{domain} | DELETE | {domain}为域名 |
获取所有证书 | http://127.0.0.0:2000/api/v1//cert | GET |
4.5acme copilot 支持NJET主备
1.支持NJET 集群,cert-manager启动后会等待30s,判断当前NJET是主还是备。
2.当NJET为master时,cert-manager启动证书管理功能,当NJET为backup时,cert-manager不启动启动证书管理功能。
3.在NJET运行过程中,cert-manager会根据集群事件(mqtt主题/gossip/nodeinfo)动态起停证书管理功能。
njet.conf:
worker_processes auto;
cluster_name njet;
node_name node54;
error_log logs/error.log info;
user root;
helper ctrl modules/njt_helper_ctrl_module.so conf/njet_ctrl.conf;
helper broker modules/njt_helper_broker_module.so conf/mqtt.conf;
helper access_data modules/njt_helper_access_data_module.so conf/goaccess.conf;
helper cert-manager modules/njt_helper_go_copilot_module.so conf/config.toml;
helper rsync modules/njt_helper_rsync_module.so conf/rsync.conf;
load_module modules/njt_http_split_clients_2_module.so;
load_module modules/njt_agent_dynlog_module.so;
load_module modules/njt_http_dyn_bwlist_module.so;
load_module modules/njt_dyn_ssl_module.so;
load_module modules/njt_http_vtsc_module.so;
load_module modules/njt_http_location_module.so;
load_module modules/njt_http_access_log_zone_module.so;
load_module modules/njt_http_dyn_server_module.so;
events {
worker_connections 1024;
}
http {
include mime.types;
access_log_write_zone on;
access_log_zone log_zone 100m;
access_log_zone_valid 3;
server {
listen 80;
server_name www.vsvs10.com www.vsvs11.com www.vsvs12.com www.vsvs13.com www.vsvs14.com www.vsvs15.com www.vsvs17.com www.vsvs18.com www.vsvs19.com www.vsvs21.com www.vsvs22.com;
location / {
return 200 $host;
}
}
server {
listen 50010 ssl;
server_name www.vsvs17.com;
ssl_protocols TLSv1.2;
ssl_certificate /usr/local/njet/certs/server.crt;
ssl_certificate_key /usr/local/njet/certs/server.key;
ssl_certificate_management acme;
location /{
return 200 "vsvs19.com ssl\n";
}
}
}
stream {
map $ssl_preread_alpn_protocols $acme_tls_alpn_01_challenge_port {
~\bacme-tls/1\b 10443;
default 50010;
}
server {
listen 443;
listen [::]:443;
proxy_pass 127.0.0.1:$acme_tls_alpn_01_challenge_port;
ssl_preread on;
}
server {
listen 238.255.253.251:5557 udp;
gossip zone=test:1m heartbeat_timeout=100ms nodeclean_timeout=1s local_ip=192.168.40.54 sync_port=8873 ctrl_port=8081 bridge_port=1883;
}
}
njet_ctrl.conf
load_module modules/njt_http_sendmsg_module.so;
load_module modules/njt_ctrl_config_api_module.so;
load_module modules/njt_helper_health_check_module.so;
load_module modules/njt_http_upstream_api_module.so;
load_module modules/njt_http_location_api_module.so;
load_module modules/njt_doc_module.so;
load_module modules/njt_http_vtsd_module.so;
load_module modules/njt_http_ssl_api_module.so;
load_module modules/njt_http_dyn_server_api_module.so;
load_module modules/njt_http_ctrl_request_forward_module.so;
error_log logs/error_ctrl.log info;
events {
worker_connections 1024;
}
http {
include mime.types;
access_log off;
server {
listen 8081;
location / {
return 200 "njet control panel\n";
}
location /api {
dyn_module_api;
}
location /doc {
doc_api;
}
location /metrics {
vhost_traffic_status_display;
vhost_traffic_status_display_format html;
}
location /report/ {
root html;
}
location /ws {
proxy_pass http://127.0.0.1:7890;
}
location /cluster_forward {
proxy_connect_timeout 1s;
proxy_read_timeout 2s;
proxy_send_timeout 2s;
proxy_pass http://${cluster_master_ip}:${cluster_master_ctrl_port}$request_uri;
}
}
}
config.toml
[copilot]
progName="sbin/cert-manager"
copilotType="cert"
stdoutFile = "/usr/local/njet/logs/cert_stdout.log"
stderrFile = "/usr/local/njet/logs/cert_stderr.log"
[acme]
email="limins@tmlake.com"
challengeType="tls"
#acmeCaDir="http://127.0.0.1:4001/directory"
acmeCaDir="https://192.168.40.54:14000/dir"
tlsChallengeServerAddress=":10443"
#httpChallengeServerAddress=":10080"
#proxyHttpChallengeServerIp="0.0.0.0"
#keyType="rsa8192"
days=30
#certificateRenewalPeriod="5m"
[njet]
[admin]
[mqtt]
mqttServerAddress="192.168.40.54:1883"
[log]
v=2
mqtt.conf
allow_anonymous true
listener 0 /usr/local/njet/data/mosquitto.sock
listener 1883 192.168.40.54
log_dest file logs/mosquitto.log
log_type debug
log_type information
log_type error
log_type warning
log_type notice
autosave_interval 1
autosave_on_changes true
persistence true
persistence_location /usr/local/njet/data/
connection bridge-backup inactive
address 192.168.40.54:1883
topic /dyn/# both 0
topic /ins/# both 0
集群内节点配置相同,分别启动njet 后,主节点
[root@kylin-54 njet]# /usr/local/njet/sbin/njet -p /usr/local/njet/ -c conf/njet.conf
[root@kylin-54 njet]# ll .lego/certificates/
总用量 16
-rw------- 1 root root 2059 8月 1 16:02 www.vsvs17.com.crt
-rw------- 1 root root 1192 8月 1 16:02 www.vsvs17.com.issuer.crt
-rw------- 1 root root 169 8月 1 16:02 www.vsvs17.com.json
-rw------- 1 root root 227 8月 1 16:02 www.vsvs17.com.key
158从节点:
[root@k8s-master158 njet]# ll .lego/certificates/
总用量 16
-rw------- 1 root root 2059 8月 1 16:02 www.vsvs17.com.crt
-rw------- 1 root root 1192 8月 1 16:02 www.vsvs17.com.issuer.crt
-rw------- 1 root root 169 8月 1 16:02 www.vsvs17.com.json
-rw------- 1 root root 227 8月 1 16:02 www.vsvs17.com.key
145 从节点:
root@ldap:/usr/local/njet# ll .lego/certificates/
total 24
drwxr-xr-x 2 root root 4096 Aug 1 16:02 ./
drwxr-xr-x 4 root root 4096 Aug 1 16:03 ../
-rw------- 1 root root 2059 Aug 1 16:02 www.vsvs17.com.crt
-rw------- 1 root root 1192 Aug 1 16:02 www.vsvs17.com.issuer.crt
-rw------- 1 root root 169 Aug 1 16:02 www.vsvs17.com.json
-rw------- 1 root root 227 Aug 1 16:02 www.vsvs17.com.key
备注
- NJET自动证书申请在3.3.1及以上支持
- 动态证书更新是NJET的动态特性之一,
- cert_manager是一个独立的go 实现的程序,由NJETCoPilot框架驱动,其2进制见下表:
二进制名称 | 下载链接 | 架构 | NJET版本 | 说明 |
---|---|---|---|---|
cert-manager-arm64 | https://gitee.com/njet-rd/njet/releases/download/v3.3.1/cert-manager-arm64 | arm64 | 3.3.1及以上 | arm架构 |
cert-manager | https://gitee.com/njet-rd/njet/releases/download/v3.3.1/cert-manager | x86-64 | 3.3.1及以上 | intel架构 |
cert-manager-loongarch64 | https://gitee.com/njet-rd/njet/releases/download/v3.3.1/cert-manager-loongarch64 | LoongArch | 3.3.1及以上 | 龙芯架构 |