lua-resty-peter_sslers

OpenResty SSL 证书例程,用于 peter_sslers SSL 证书管理器

$ opm get aptise/lua-resty-peter_sslers

lua-resty-peter_sslers

Lua 支持 https://github.com/aptise/peter_sslers 证书管理器在 OpenResty 中使用

状态

此包已在生产环境中使用,自 2016 年起。

0.5.0 版本需要 peter_sslers >= 0.5.0

0.4.0 版本需要 peter_sslers >= 0.4.0, <0.5.0

早期版本需要 peter_sslers < 0.4.0

安装

一行代码…

    sudo opm install aptise/lua-resty-peter_sslers

升级?

    sudo opm upgrade aptise/lua-resty-peter_sslers

概述

lua-resty-peter_sslers 是一个可以在 OpenResty/Nginx 环境中使用的库,用于为给定域名动态提供正确的 SSL 证书。

与 https://github.com/aptise/peter_sslers 协同工作,此包将加载现有证书或执行“autocert”以配置新的证书。

它支持 peter_sslers 的两种主要缓存类型 1 和 2。

它支持 peter_sslers 的“autocert”功能。

它作为库实现,并附带一些示例脚本以调用它。

  • 核心库

    • peter_sslers.lua

示例可以在 examples 目录中找到,以及广泛的单元测试。

  • 示例

    • ssl_certhandler-lookup.lua

    • ssl_certhandler-expire.lua

    • ssl_certhandler-status.lua

-lookup.lua-expire.lua-status.lua 脚本可以复制到 nginx/openresty 服务器块中。作为参考,请查看测试套件。

库默认使用以下信息连接 Redis

local redis_server = '127.0.0.1' local redis_port = '6379' local redis_db_number = 9

这可以通过“redis_update_defaults”修改。

redis_update_defaults(_redis_server, _redis_port, _redis_db_number)

Redis 不是必需的,但建议使用。相反,您可以回退到直接查询 peter_sslers pyramid 实例。

禁用 Redis

使用 nil 而不是 12 调用 ssl_certhandler_set 并设置 redis_strategy。很简单!

Pyramid 回退

要使用 Peter SSlers Pyramid 回退来查询域名或 autocert 功能,请使用以下库

  • lua-resty-http https://github.com/pintsized/lua-resty-http

回退 API 的命中和未命中将缓存在共享缓存字典中。如果需要删除值,则需要重新启动服务器或使用 Nginx/lua 示例之一清除缓存。回退 API 请求将通知 Pyramid 应用程序请求应具有直通缓存行为。

调试语句发送到 nginx 服务器日志,因此最终用户可以调试回退和 autocert 功能。

缓存说明

为了最大限度地提高性能,Nginx/OpenResty 中有两个缓存层

  • 证书缓存在给定工作进程中的 LRU 缓存中,使用 peter_sslers.lru_cache_duration 秒(默认为 60 秒)的原生 CDATA 格式。

  • 证书以 PEM 格式缓存在所有工作进程中,使用 peter_sslers.cert_cache_duration 秒(默认为 600 秒)。

这些值可以调整。

为什么?

Nginx 共享字典可以轻松查询、刷新/过期/覆盖值,但它只能存储 PEM 证书(而不是 cdata 指针),因此需要重复解析证书。

LRU 缓存可以保存 cdata 指针,但 Nginx/OpenResty 的实现细节不允许轻松查询和操作缓存。在所有工作进程之间发送覆盖/过期消息也将是一项艰巨的任务。

处理部署问题的一种简单方法是在 LRU 缓存上使用超时,该超时足够长以在负载下执行良好,但足够短以允许共享字典中的更改传播。

用法

确保您的 Nginx 包含

`` server { # 初始化 cert_cache 到一个大小 # 它将通过 nginx.shared.cert_cache 访问 lua_shared_dict cert_cache 100k; lua_code_cache on; init_by_lua_block { require "resty.core" local ssl_certhandler = require "resty.peter_sslers" ssl_certhandler.initialize() } init_worker_by_lua_block { require "resty.core" local ssl_certhandler = require "resty.peter_sslers" -- cert_cache_duration, lru_cache_duration, lru_maxitems ssl_certhandler.initialize_worker(90, 30, 200) } } ``

  • initialize 目前什么也不做。

  • initialize_worker 接受三个参数

    • cert_cache_duration ngx.shared.DICT 中 PEM 缓存的秒数

    • lru_cache_duration 工作进程中 cdata 指针的 LRU 缓存的秒数

    • lru_maxitems 工作进程中 cdata 指针的 LRU 缓存的最大项目数

然后实现示例路由

由于 lua/luajit 的实现细节,以下示例必须在块/文件中实现,不能“require(/path/to/example)”。这是因为 Redis 连接的实例化方式。

参见

  • https://github.com/openresty/lua-resty-redis/issues/33

  • https://github.com/openresty/lua-nginx-module/issues/376

resty/peter_sslers.lua

核心库。公开多个函数。

examples/ssl_certhandler-lookup.lua

这非常简单,它仅仅指定了缓存、持续时间和 redis_strategy

在 Nginx 中调用…

        server {
            listen 443 default_server;
            ...
            // nginx must have a default configured
            ssl_certificate /path/to/default/fullchain.pem;
            ssl_certificate_key /path/to/default/privkey.pem;
                ssl_certificate_by_lua_block  {
                }

examples/ssl_certhandler-expire.lua

Nginx 共享内存缓存在配置重新加载后仍然存在。服务器必须完全重启才能清除内存。

解决方法?API 端点“刷新”缓存或使某些键(域名)过期。

examples/ssl_certhandler-expire 提供了一个简单的示例,可以很容易地在 Nginx 中调用它。

`` server { listen 443 default_server; ... location /.peter_sslers { auth_basic "peter_sslers-nginx"; auth_basic_user_file /path/to/peter_sslers-nginx.htpasswd; location /.peter_sslers/nginx/shared_cache/expire { content_by_lua_block { -- requirements local ssl_certhandler = require "resty.peter_sslers"

                                        -- alias functions
                                        local ssl_certhandler_expire = ssl_certhandler.expire_ssl_certs
                                        ssl_certhandler_expire()
                                }
}
}
}
C<>``

此过期工具创建以下路由

  • /peter_sslers/nginx/shared_cache/expire/all 刷新整个 Nginx 证书缓存

  • /peter_sslers/nginx/shared_cache/expire/domain/{DOMAIN} 刷新证书缓存中域名的 pkey 和 chain 条目

成功时,这些路由将在 HTTP-200-OK 文档中返回 JSON。

  • {"result": "success", "expired": "all"}

  • {"result": "success", "expired": "domain", "domain": "{DOMAIN}"}

  • {"result": "error", "expired": "None", "reason": "Unknown URI"}

出错时,这些路由应生成错误状态代码。

Pyramid 组件可以自动为您查询这些端点。

examples/ssl_certhandler-status.lua

状态路由显示有关系统的一些信息

`` server { listen 443 default_server; ... location /.peter_sslers/nginx { auth_basic "peter_sslers-nginx"; auth_basic_user_file /path/to/peter_sslers-nginx.htpasswd; location /.peter_sslers/nginx/shared_cache/status { content_by_lua_block { -- requirements local ssl_certhandler = require "resty.peter_sslers"

                                        -- alias functions
                                        local ssl_certhandler_status = ssl_certhandler.status_ssl_certs
                                        ssl_certhandler_status()
                                }
}
}
}
C<>``

examples/ssl_certhandler-lookup.lua

这是核心工作

`` ssl_certificate_by_lua_block { -- requirements local ssl_certhandler = require "resty.peter_sslers"

            -- alias functions
            local ssl_certhandler_set = ssl_certhandler.set_ssl_certificate

            local redis_strategy = 1
            local fallback_server = 'http://0.0.0.0:6543/.well-known/admin'
            local enable_autocert = 1
            ssl_certhandler_set(redis_strategy, fallback_server, enable_autocert)
        }
C<>``

完全配置的示例

在主 peter_sslers 仓库中提供了完全配置的示例

  • https://github.com/aptise/peter_sslers/blob/master/tools/nginx_conf/enabled.conf

详情

此方法积极利用 Nginx 工作进程(通过工作进程 lru 和共享字典)和 Redis 中的缓存;缓存命中和未命中。

Nginx 工作进程字典在重新加载后共享(kill -HUP {PID});因此,如果出现错误的值,则必须重新启动或等待超时。

伪代码中的逻辑

`` cert_cdata = lru_cache.get(domain) # 检查工作进程缓存 if hit(cert_cdata): if invalid(cert_cdata): return else: cert_pem = cert_cache.get(domain) # 检查共享缓存 if hit(cert_pem): if invalid(cert_pem): lru_cache.set(domain, invalid) return else: cert_pem = redis.get(domain) if hit(cert_pem): if invalid(cert_pem): lru_cache.set(domain, invalid) cert_cache.set(domain, invalid) return else: if autocert_enabled: cert_pem = upstream_https.get(domain) # autocert else: cert_pem = upstream_https.get(domain) # 查询 if hit(cert_pem): if invalid(cert_pem): lru_cache.set(domain, invalid) cert_cache.set(domain, invalid) return if valid(cert_pem) lru_cache.set(domain, cert_cdata) cert_cache.set(domain, cert_pem) cert_cdata = parse(cert_pem) if valid(cert_cdata): set_ssl_certificate(cert_cdata) ``

集成/调试方法

各种级别的信息发送到以下 Nginx 的调试级别。更改 Nginx 调试级别将显示更多数据

  • ERR

  • NOTICE

  • DEBUG

注意工作进程是如何初始化的

-- cert_cache_duration, lru_cache_duration, lru_maxitems ssl_certhandler.initialize_worker(90, 30, 100)

为了调试,您可能希望将这些值降低以将 LRU 缓存缩短到可以忽略不计的数字

ssl_certhandler.initialize_worker(5, 1, 100)

“/status”和“/expire”路由仅显示共享缓存中的信息 - 信息缓存在每个工作进程自己的 LRU 缓存中,并且这些路由无法访问。如果使用“/expire”,则域名将从共享缓存中删除,并且“/status”路由… 但可能仍存在于工作进程的 LRU 中。

检查状态

curl -k https://peter:sslers@127.0.0.1/.peter_sslers/nginx/shared_cache/status

过期

curl -k https://peter:sslers@127.0.0.1/.peter_sslers/nginx/shared_cache/expire

测试

Luacheck

test.yml 中的测试禁用未使用的变量

        run: luacheck lib --no-unused

执行本地测试

luarocks install luacheck luacheck lib

Test::Nginx

升级 CPAN

cpan upgrade

安装 cpanm

cpan App::cpanminus

安装测试工具

cpanm -q -n Test::Nginx

您的 openresty 在哪里?确保它在路径中

export PATH=/usr/local/openresty/nginx/sbin:$PATH export PATH=/usr/local/bin/:$PATH

运行测试

/usr/bin/prove -I../test-nginx/lib -r t/

已知问题

没有!

版本控制

此项目使用主版本号.次版本号.修订版本号 语义版本控制。

主版本号.次版本号 版本发布与 lua-resty-peter__slers 的相同主版本号.次版本号 版本发布挂钩。

例如

| peter_sslers | lua-resty-peter_sslers | 兼容? | | --- | --- | --- | | 0.5.1 | 0.5.1 | 是 | | 0.5.1 | 0.5.0 | 是。修订版本号不匹配是可以的! | | 0.5.1 | 0.4.2 | 否。次版本号不匹配。 |

作者

Jonathan Vanasco <jonathan@findmeon.com>

最初在 https://github.com/aptise/peter_sslers 中启动

测试和 github actions 复制或借鉴了优秀的 lua-resty-http 模块 https://github.com/ledgetech/lua-resty-http(由 James Hurst 创建)和 openresty 测试套件。

许可证

此模块根据 MIT 许可证授权。请参阅 LICENSE.txt

作者

Jonathan Vanasco (github.com/jvanasco)

许可证

mit

依赖项

版本