peter_sslers-lua-resty

用于 peter_sslers SSL 证书管理器的 openresty ssl 证书例程

$ opm get aptise/peter_sslers-lua-resty

lua-resty-peter_sslers

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

状态

该软件包几乎已准备好投入生产。

安装

一行代码...

    sudo opm install aptise/peter_sslers-lua-resty

概述

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

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

它作为带有调用它的示例脚本的库实现。

  • 核心库

    • ssl_certhandler.lua

  • 示例

    • ssl_certhandler-lookup.lua

    • ssl_certhandler-expire.lua

    • ssl_certhandler-status.lua

-lookup.lua-expire.lua-status.lua 脚本可以复制到一个块中。

该库被硬编码为使用 redis 中的 db9。如果你想要其他选项,请编辑或对看起来像这样的行进行 PR 修复

ngx.log(ngx.ERR, "changing to db 9: ", times) redcon:select(9)

Redis 不是必需的,但建议使用。作为替代,你可以故障转移到直接查询 peter_sslers 金字塔实例。

要使用 Peter SSlers 金字塔回退,将使用以下库

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

来自回退 API 的命中和未命中将被缓存到共享缓存字典中。如果你需要删除值,你需要重启服务器或使用一个 nginx/lua 示例来清除缓存。回退 API 请求将通知金字塔应用程序请求应具有写入缓存行为。

缓存说明

为了最大限度地提高性能,nginx/openresty 中有 2 层缓存

  • 证书在给定工作进程中以 ssl_certhandler.lru_cache_duration 秒(默认 60 秒)的原生 cdata 格式保存在 LRU 缓存中

  • 证书在所有工作进程中以 PEM 格式保存在 ssl_certhandler.cert_cache_duration 秒(默认 600 秒)中

这些值可以调整。

为什么?

nginx 共享字典很容易查询、刷新/过期/覆盖值,但它只能存储 PEM 证书(而不是 cdata 指针),因此证书需要被反复解析。

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

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

用法

确保你的 nginx 包含

`` server { init_by_lua_block { require "resty.core" local ssl_certhandler = require "peter_sslers.ssl_certhandler" ssl_certhandler.initialize() } init_worker_by_lua_block { require "resty.core" local ssl_certhandler = require "peter_sslers.ssl_certhandler" -- 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)

ssl_certhandler.lua

核心库。公开多个函数。

examples/ssl_certhandler-lookup.lua

这非常简单,它只是指定缓存、持续时间和 prime_version

在 nginx 中调用...

`` server { listen 443 default_server; ... // nginx 必须具有一个默认配置的 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 端点。

peter_sslers.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 "peter_sslers.ssl_certhandler"

                                        -- 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"}

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

金字塔组件可以自动为您查询这些端点。

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 "peter_sslers.ssl_certhandler"

                                        -- 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 "peter_sslers.ssl_certhandler"

            -- alias functions
            local ssl_certhandler_set = ssl_certhandler.set_ssl_certificate

            local prime_version = 1
            local fallback_server = 'http://0.0.0.0:6543/.well-known/admin'
            ssl_certhandler_set(prime_version, fallback_server)
        }
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: 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

已知问题

作者

Jonathan Vanasco <jonathan@findmeon.com>

最初在 https://github.com/aptise/peter_sslers 中开始

许可证

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

作者

Jonathan Vanasco (github.com/jvanasco)

许可证

mit

依赖项

版本