自动化证书管理

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证书申请,可以通过静态或动态两种方式使能。使能后证书申请流程如下:

证书申请流程(自动化创建证书)

img img

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相关参数
参数字段 参数名称 类型 是否必填 默认值 说明
email 邮箱地址 字符串 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配置状态

img img

修改域名为vs10.com,重新reload:

mqtt日志:

img img

cert-manager日志:

NJET配置:

img img

证书文件查看:

img img

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(生成证书的时候自动创建,不需要手动创建)

img img

如果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及以上 龙芯架构