simplessl

使用 Let's Encrypt 自动颁发和更新 SSL 证书

$ opm get jxskiss/simplessl

simplessl

OpenResty/nginxEnvoy 和任何 Golang TLS 程序中,使用 Let's Encrypt 实时免费注册和更新 SSL 证书。

simplessl 会在收到请求时自动透明地从 Let's Encrypt 颁发 SSL 证书,当证书需要续期时,它会自动在后台异步续期证书。

OpenResty 插件使用 OpenResty 1.9.7.2+ 中的 ssl_certificate_by_lua 功能。

对于 Envoy,simplessl 实现 SDS 服务器以向数据平面提供 SSL 证书。

通过使用 simplessl 向 Let's Encrypt 注册 SSL 证书,您即表示同意 Let's Encrypt 订阅者协议

免责声明:我最初从很棒的项目 lua-resty-auto-ssl 和 Go 的 autocert 包中获得了灵感并借鉴了一些代码。此外,此程序使用 Lego 来解决 dns-01 挑战。非常感谢 😀

特性

  1. 依赖最小,部署简单,对分布式环境友好。

  2. 高性能,对用户请求增加的延迟非常低。

  3. 使用 http-01 挑战为每个域名颁发和更新证书,支持 OpenResty 和 Golang。

  4. 使用 tls-alpn-01 挑战为每个域名颁发和更新证书,支持 Golang。

  5. 使用 tls-alpn-01 挑战颁发和更新SAN 证书,支持 Golang。

  6. 使用 dns-01 挑战颁发和更新通配符证书,支持 OpenResty 和 Golang。

  7. 服务手动管理的证书。

  8. 服务 OCSP 装订,带缓存和异步更新,延迟可以忽略不计。

  9. 生成和服务自签名证书。

  10. 像 Nginx 一样优雅重启,不会丢失任何请求。

  11. 支持目录和 Redis 作为缓存存储,添加新的存储支持很容易。

注意:当前此程序设计用于内部网络,未认真考虑安全功能,请务必妥善保护您的证书服务器并关注安全问题。

集中式证书服务器

与其他类似项目相比,此项目提供了一个集中式证书服务器,用于在一个地方管理所有证书(自动颁发或手动管理,以及自签名)。OpenResty 插件和 Golang TLS 配置库充当服务器的客户端。

通过这种设计,有几个优点

  1. 将与 ACME 相关的工作和与存储的通信卸载到后端 Golang 服务器,让 Nginx/OpenResty 做它设计和擅长的事情;

  2. 它对分布式部署更加友好,可以在一个地方管理所有证书,OpenResty 插件和 Golang 库的部署简单直接;您可以为一个域名获取单个证书,而不是像您的 Web 服务器实例那样多(像某些其他类似项目那样);

  3. 与使用 Lua 进行 ACME 工作和存储相比,Golang 程序被认为更容易维护和进行故障排除;

  4. 此外,Golang 程序被认为更容易扩展以支持新型存储或新功能(例如通配符证书、安全性等);

使用多层缓存机制来帮助前端 Nginx 和 Golang Web 服务器自动更新到续期的证书,性能损失可以忽略不计,并且无需任何重新加载

  • OpenResty 每个工作进程的 LRU 缓存,使用 cdata 指针(Golang 客户端使用内存中的写时复制缓存),回退到

  • OpenResty 共享内存缓存(Golang 客户端不需要),回退到

  • 后端 simplessl 服务器中的内存缓存,最后转到

  • 存储或 ACME 服务器。

缓存的证书和 OCSP 装订会在后端 simplessl 服务器中自动续期和刷新。

状态

视为 Beta 版本。

虽然此程序已经运行了近 5 年,支持我的个人网站,但这是一个业余项目,并且没有已知的用于大型生产系统的部署。

强烈建议任何对此感兴趣的人在您的环境中进行测试。

安装

对于 OpenResty

lua 库使用 OPM 发布,以下命令将安装 simplessl 库及其依赖项“lua-resty-http”。

opm get jxskiss/simplessl

如果您没有 opm,您可以手动安装 lua 库,以 OpenResty 安装在“/usr/local/openresty”下为例(您可能需要使用 sudo 来授予适当的权限)

    mkdir -p /usr/local/openresty/site/lualib/resty
    cd /usr/local/openresty/site/lualib/resty
    wget https://raw.githubusercontent.com/ledgetech/lua-resty-http/v0.16.1/lib/resty/http.lua
    wget https://raw.githubusercontent.com/ledgetech/lua-resty-http/v0.16.1/lib/resty/http_connect.lua
    wget https://raw.githubusercontent.com/ledgetech/lua-resty-http/v0.16.1/lib/resty/http_headers.lua
    wget https://raw.githubusercontent.com/jxskiss/simplessl/master/lib/resty/simplessl.lua

对于 Golang TLS 程序

go get github.com/jxskiss/simplessl/lib/tlsconfig@latest

有关使用 lib/tlsconfig 的示例,请参阅以下文档。

运行 simplessl

下载证书服务器服务二进制文件,您可以自己构建

go install github.com/jxskiss/simplessl@latest

或从 发布页面 下载预构建的二进制文件。

example.conf.yaml 复制到您喜欢的地址并编辑它以满足您的需求。配置文件中解释了配置选项。

运行您的证书服务器

    /path/to/simplessl run -c /path/to/your/conf.yaml

或者要生成自签名证书,请参阅 simplessl generate-self-signed -h

现在您可以配置您的 OpenResty 或 Golang 程序以使用证书服务器获取 SSL 证书,请参阅以下示例。

Nginx 配置示例

    events {
        worker_connections 1024;
    }
    
    http {
        include       mime.types;
        default_type  application/octet-stream;
    
        lua_shared_dict ssl_certs_cache 1m;
    
        init_by_lua_block {
            -- Define a function to determine which SNI domains to automatically
            -- handle and register new certificates for. Defaults to not allowing
            -- any domain, so this must be configured.
            function allow_domain(domain)
                if domain:find("example.com$") then
                    return true
                end
                return false
            end
    
            -- Initialize backend certificate server instance.
            -- Change lru_maxitems according to your deployment, default 100.
            simplessl = (require "resty.simplessl").new({
                backend = '127.0.0.1:8999',
                allow_domain = allow_domain,
                lru_maxitems = 100,
            })
        }
    
        # HTTPS Server
        server {
            listen 443 ssl;
    
            # Works also with non-default HTTPS port.
            listen 8443 ssl;
    
            server_name hello.example.com;
    
            # Dynamic handler for issuing or returning certs for SNI domains.
            ssl_certificate_by_lua_block {
                simplessl:ssl_certificate()
            }
    
            # Fallback certificate required by nginx, self-signed is ok.
            # simplessl generate-self-signed \
            #   -days 3650 \
            #   -cert-out /etc/nginx/certs/fallback-self-signed.crt \
            #   -key-out /etc/nginx/certs/fallback-self-signed.key
            ssl_certificate /etc/nginx/certs/fallback-self-signed.crt;
            ssl_certificate_key /etc/nginx/certs/fallback-self-signed.key;
    
            location / {
                content_by_lua_block {
                    ngx.say("It works!")
                }
            }
        }
    
        # HTTP Server
        server {
            listen 80;
            server_name hello.example.com;
    
            # Endpoint used for performing domain verification with Let's Encrypt.
            location /.well-known/acme-challenge/ {
                content_by_lua_block {
                    simplessl:challenge_server()
                }
            }
        }
    
    }

Golang lib/tlsconfig

您可以使用包 lib/tlsconfig 来运行带有 TLS 的 Golang 程序。例如

    func main() {
        handler := func(w http.ResponseWriter, r *http.Request) {
                w.Write([]byte("It works!"))
        }
    
        // See doc of tlsconfig.Options for available options.
        tlsConfig := tlsconfig.NewConfig("127.0.0.1:8999", tlsconfig.Options{})
        listener, err := tls.Listen("tcp", ":8443", tlsConfig)
        if err != nil {
                log.Fatal(err)
        }
    
        http.Serve(listener, http.HandlerFunc(handler))
    }

依赖

更改历史

v0.6.2 @ 2022-11-02

  • 更改:整理日志消息

  • 更改:更新依赖项

  • 修复:无效的内存地址或空指针取消引用 (#10)

  • 修复:添加使用选项配置 redis 存储的示例 (#11)

v0.6.0 @ 2022-09-03

  • 从头开始进行主要重构。

  • 新增:支持除 Let's Encrypt 之外的 ACME 服务器

  • 新增:支持颁发和更新 SAN 证书

  • 新增:使用新的配置格式 (v2)

  • 新增:支持按配置的名称查询证书

  • 更改:重构 HTTP API 以使用 [drpc][drpc] 以提高可维护性和一致性

  • 更改:更改为使用 [acmez][acmez](而不是 acme/autocertlego)与 ACME 服务器通信

[drpc]: https://github.com/storj/drpc [acmez]: https://github.com/mholt/acmez

v0.5.0 @ 2022-06-12

  • 修复:在查询证书之前查询 OCSP 装订时的错误行为

  • 新增:支持通配符证书,使用 Lego 解决 dns-01 挑战

  • 更改:使 lib/tlsconfig 成为独立模块

  • 更改:将依赖项升级到最新版本

  • 更改:重构代码以使用更复杂的 cli 和日志库

  • 更改:重构代码以提高可维护性

v0.4.3 @ 2022-05-17

  • 修复:OCSP 装订失败,因为选择了错误的证书作为颁发者证书,感谢 @cedricdubois (#6)

  • 新增:Redis 存储的可选“前缀”选项

v0.4.2 @ 2021-06-02

  • 修复:仅配置默认证书可用时请求失败,#2

  • 修复:当 OCSP 服务器返回错误时抑制日志消息,#3

  • 更改:将依赖项升级到最新版本

v0.4.1 @ 2020-08-23

  • 新增:支持 Golang 库的 tls-alpn-01 挑战

  • 新增:使用 cdata 指针代替 der 用于 LRU 缓存

  • 更改:从 LRU 缓存中删除 OCSP 装订,这是不必要的并且没有正确刷新

v0.4.0 @ 2020-08-16

更新:此版本存在已知错误,请升级到较新版本。

  • 新增:支持管理证书

  • 新增:支持自签名证书

  • 新增:Golang 库,用于需要 TLS 支持的任意 Golang 程序

  • 新增:用于生成自签名证书的子命令

  • 新增:(lua)分层缓存,以实现最佳性能(每个工作进程的 LRU 缓存 + 共享内存缓存)

  • 新增:像 Nginx 一样优雅重启,不会丢失任何请求

  • 更改:使用 YAML 配置文件替换命令行标志,因为我们支持更多功能,命令行标志不足以进行配置

  • 更改:(内部)将代码重组为更小的文件,以提高可维护性

  • 更改:(内部)优化 lua 共享内存缓存,以提高性能

  • 修复:向证书和 OCSP 装订缓存添加指纹,以确保我们为相应的证书获取正确的 OCSP 装订,没有它,在证书更新后短时间内可能会使用错误的 OCSP 装订缓存

此版本是一个重大更改,包含大量新功能和改进。

v0.3.0 @ 2020-03-13

  • 新增:可选的 redis 作为缓存存储

  • 更改:更新 autocert 以支持 ACMEv2

  • 更改:使用 go module 管理 golang 依赖项

v0.2.1 @ 2018-10-10

  • 更改:整理日志消息

  • 更改:小幅改进

v0.2.0 @ 2018-08-11

  • 修复:OCSP 装订更新程序在运行数月后出现死循环

  • 更改:删除不必要的 golang 依赖项(gocraft/webjxskiss/glog),从而减少二进制文件大小并简化安装

  • 更改:由于已删除 glog 依赖项,因此 glog 提供的标志不再可用

  • 更改:使用官方的 acme/autocert 包而不是分叉,使代码更清晰并允许更容易地跟踪上游更改

  • 新增:使用 glide 管理 golang 依赖项

v0.1.2 @ 2018-06-20

  • 修复:来自 OCSP 装订服务器的 408 请求超时

v0.1.1 @ 2018-01-06

初始公开发布。

作者

Shawn Wang (jxskiss)

许可证

mit

依赖项

ledgetech/lua-resty-http >= 0.16.1,openresty >= 1.9.7.2

版本