如果是第一次看这个文章,可以先看下这篇openresty介绍性的文章:Openresty宏观介绍
没用过openresty的人可能不知道这个是干啥的,但是用过的人大概就明白这个到底是个啥。在此简单说一下,openresty功能和Nginx功能一样,在用户如果需要做https访问的时候,需要在Nginx的配置文件nginx.conf文件里面配置证书和对应的KEY秘钥,这样Nginx才能提供https的访问请求。那么此时可能有这么一个需求,证书过期或者其他什么原因需要在不停止服务的情况下更改证书和秘钥,实现动态更改证书和秘钥的功能。实现如下功能需要下面几步
- 编写lua脚本文件,在脚本文件里面去修改证书和密钥
- 拷贝lua文件到openresty的安装路径
- 修改nginx.conf文件,引入lua脚本,实现动态更新证书
一般证书/私钥的格式分两种,一种是文本格式的 PEM,另一种是二进制格式的 DER
1、编写lua脚本
命名脚本文件为ssl.lua,如果证书是PEM格式的话,通过如下方法更新证书和私钥:
local ssl = require "ngx.ssl"
-- 清除之前设置的证书和私钥
local ok, err = ssl.clear_certs()
if not ok then
ngx.log(ngx.ERR, "failed to clear existing (fallback) certificates")
return ngx.exit(ngx.ERROR)
end
-- 获取证书内容,比如 io.open("my.crt"):read("*a")
local cert_data, err = get_my_pem_cert_data()
if not cert_data then
ngx.log(ngx.ERR, "failed to get PEM cert: ", err)
return
end
-- 解析出 cdata 类型的证书值,你可以用 lua-resty-lrucache 缓存解析结果
local cert, err = ssl.parse_pem_cert(cert_data)
if not cert then
ngx.log(ngx.ERR, "failed to parse PEM cert: ", err)
return
end
local ok, err = ssl.set_cert(cert)
if not ok then
ngx.log(ngx.ERR, "failed to set cert: ", err)
return
end
local pkey_data, err = get_my_pem_priv_key_data()
if not pkey_data then
ngx.log(ngx.ERR, "failed to get DER private key: ", err)
return
end
local pkey, err = ssl.parse_pem_priv_key(pkey_data)
if not pkey then
ngx.log(ngx.ERR, "failed to parse pem key: ", err)
return
end
local ok, err = ssl.set_priv_key(pkey)
if not ok then
ngx.log(ngx.ERR, "failed to set private key: ", err)
return
end
备注:里面的get_my_pem_cert_data函数是获取证书函数,此处没有原函数,用户根据自己情况获取,有些是文件直接读取,有些是网络请求获取,有些是字符串直接使用。
如果证书格式是DER的话,使用如下代码进行动态更新
local ssl = require "ngx.ssl"
-- 清除之前设置的证书和私钥
local ok, err = ssl.clear_certs()
if not ok then
ngx.log(ngx.ERR, "failed to clear existing (fallback) certificates")
return ngx.exit(ngx.ERROR)
end
-- 获取证书内容,比如 io.open("my.crt.der"):read("*a")
local cert_data, err = get_my_der_cert_data()
-- 你也可以把 pem 格式的证书直接转换成 der 格式的,像这样:
-- local cert_pem_data = get_my_pem_cert_data()
-- local cert_data, err = ssl.cert_pem_to_der(cert_pem_data)
if not cert_data then
ngx.log(ngx.ERR, "failed to get DER cert: ", err)
return
end
-- 这里的 cert_data 是 string 类型的,所以可以直接缓存到 lua_shared_dict 当中
local ok, err = ssl.set_der_cert(cert_data)
if not ok then
ngx.log(ngx.ERR, "failed to set DER cert: ", err)
return
end
local pkey_data, err = get_my_der_priv_key_data()
-- 你也可以把 pem 格式的私钥直接转换成 der 格式的,像这样:
-- local pkey_pem_data = get_my_pem_priv_key_data()
-- local pkey_data, err = ssl.priv_key_pem_to_der(pkey_pem_data)
if not pkey_data then
ngx.log(ngx.ERR, "failed to get DER private key: ", err)
return
end
local ok, err = ssl.set_der_priv_key(pkey_data)
if not ok then
ngx.log(ngx.ERR, "failed to set DER private key: ", err)
return
end
2、拷贝lua文件到openresty安装目录
拷贝编辑的ssl.lua文件到目录/usr/local/openresty/nginx/lua/下面,如果没有,新建一个这个路径
3、修改nginx.conf文件
修改路径/usr/local/openresty/nginx/nginx.conf文件的内容如下:
#user nobody;
worker_processes 1;
error_log logs/error.log error;
error_log logs/error.log notice;
error_log logs/error.log info;
pid logs/nginx.pid;
events {
worker_connections 1024;
}
env SERVER_ADDR;
env SERVER_ADDR_TOKEN;
http {
include mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# access_log logs/access.log main;
# error_log logs/error.log error;
lua_package_path "/usr/local/openresty/lualib/?.lua;/usr/local/openresty/nginx/lua/?.lua;";
lua_package_cpath "/usr/local/openresty/lualib/?.so;;";
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
proxy_connect_timeout 3s;
#gzip on;
# HTTPS server
server {
listen 443 ssl;
server_name localhost;
ssl_certificate server.crt;
ssl_certificate_key server.key;
ssl_certificate_by_lua_file lua/ssl.lua;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location / {
#设置代理目的url变量
proxy_pass https://127.0.0.1;
}
}
}
最重要的两句代码就是上面的
lua_package_path "/usr/local/openresty/lualib/?.lua;/usr/local/openresty/nginx/lua/?.lua;";
和
ssl_certificate_by_lua_file lua/ssl.lua;
第一句是添加lua代码的路径,第二句代码是指定ssl请求的lua文件,意思就是所有的ssl访问请求都会经过ssl.lua代码文件的代码操作,在里面去动态修改证书。