OpenResty 自动生成 ssl 免费证书(可自动签发泛域名)

450 阅读6分钟

兄弟们,白嫖党们。现在证书为了治你们这些人,都已经只有 30 天的免费期了。我们在这介绍怎么样使用 openresty 来自动生成证书的功能。

这次的主人工是 simplessl 这个 Go 的工具,可以为 OpenResty/nginx、Envoy 和 Golang TLS 程序提供即时免费的自动 SSL 注册与续期功能,支持 Let's Encrypt。该工具能在收到请求时自动签发 SSL 证书,并在后台异步续期。

所以我们的 OpenResty 是结合这个一起来工作,所以可以一个 simplessl 来管理所有的证书,其它的 OpenResty 的主机都连接它来拿证书。

我们先介绍一下

功能特性

  • 最小依赖:易于部署,适合分布式环境

  • 高性能:用户请求延迟极低

  • 多种证书支持:HTTP-01、TLS-ALPN-01 挑战,通配符证书(DNS-01 挑战)

  • 混合证书管理:支持自动签发与手动管理证书共存

  • OCSP 装订:带缓存和异步续期

  • 自签名证书:支持生成和提供自签名证书

  • 无感更新:证书更新无需重载服务

  • 多级缓存:LRU 缓存 + 共享内存缓存 + 服务端缓存 + 持久存储

  • 中心化证书服务:统一管理所有证书

概念

这有一些基本的介绍,就是当证书签发的机构,在验证你对域名的控制权时,ACME 协议(如 Let's Encrypt)申请证书有如下三种验证方式。

  1. HTTP-01 挑战: CA 要求你在域名对应的服务器上放置一个特定文件(通过 HTTP 访问),以证明你拥有该域名。

  2. TLS-ALPN-01 挑战: 在 TLS 握手过程中,通过 ALPN(应用层协议协商)扩展完成验证,无需 HTTP 请求。CA 通过连接到域名的 443 端口,验证 TLS 握手时的 ALPN 扩展。

  3. DNS-01 挑战:要求你在域名的 DNS 记录中添加一条特定的 TXT 记录,以证明你拥有该域名。

安装 OpenResty 的前端 simplessl 步骤

这个服务,需要同时安装二个应用,前端使用 OpenResty 来加载 simplessl 的模块做为前端,在需要证书的时候,从后端 go 写的 simplessl 的后端服务来取证书。

你的商业服务,就跑在这个 OpenResty 上。只是由这个 simplessl 模块来管理你的证书,并去后端服务上取合适的证书。

安装服务

  1. OPM 安装 simplessl 到 openresty 当中

这个用于来从 simplessl 中来读证书的 openresty 的模块。

opm get jxskiss/simplessl

nginx 配置

events {
    worker_connections 1024;
}

http {
    lua_package_path "/usr/local/openresty/site/?.lua;/etc/openresty/lua/?.lua;;"; # 路径加进去

    lua_shared_dict ssl_certs_cache 1m;

    init_by_lua_block {
        function allow_domain(domain)
            -- 允许 example.com 后缀的域名
            return domain:find("example.com$")
        end

        simplessl = (require "resty.simplessl").new({
            backend = '127.0.0.1:8999',  # simplessl 服务地址
            allow_domain = allow_domain,
            lru_maxitems = 100,
        })
    }

    # HTTPS 服务
    server {
        listen 80;
        listen 443 ssl;
        server_name *.example.com;

        # 后备自签名证书(需预先生成)
        ssl_certificate /etc/nginx/certs/fallback.crt;
        ssl_certificate_key /etc/nginx/certs/fallback.key;
        
        ssl_certificate_by_lua_block {
            simplessl:ssl_certificate()
        }
        # 文件验证的地址
        location /.well-known/acme-challenge/ {
            content_by_lua_block { simplessl:challenge_server() }
        }
        
        # 内容
        location / {
   
        }
    }
}

安装 go 的 simplessl 后端

这个是主要的服务,分离做不同的事情,签发机构来取证书,更新证书,管理所有证书服务是由这个服务来做的,还有修改 dns 和通过认证之类。

依赖要求

  • OpenResty >= 1.9.7.2
  • lua-resty-http >= 0.16.1
  1. 下载二进制文件Release 页面 下载预编译版本或自行构建:

  2. golang 程序安装

go env -w GOPROXY=https://goproxy.cn,direct
go get github.com/jxskiss/simplessl/lib/tlsconfig@latest
go install github.com/jxskiss/simplessl@latest
  1. 配置服务 复制示例配置文件并修改:
cp example.conf.yaml /path/to/your-conf.yaml
vi /path/to/your-conf.yaml  # 根据需求调整配置
  1. 启动服务
/path/to/simplessl run -c /path/to/your-conf.yaml

simplessl 配置简介

go 程序的 simplessl 需要创建一个配置文件, 如叫 list.yaml 这种。 我使用了阿里云,这时因为泛域名需要修改 dns ,所以我使用了 DNS-01 挑战

version: "2"
listen: "127.0.0.1:8999"
pid_file: "simplessl.pid"

storage:
  type: "dir_cache"
  dir_cache: "./certs"  # 证书存储目录

acme:
  directory_url: "https://acme-v02.api.letsencrypt.org/directory"
  force_rsa: false
  renew_before: 10 # days
  default_account:
    email: "你的邮箱"
  accounts:
    - email: "你的邮箱"
  dns_credentials:
    - name: alidns
      provider: alidns
      env:
        ALICLOUD_ACCESS_KEY: ""
        ALICLOUD_SECRET_KEY: ""
  on_demand: # 首次访问匹配的域名时动态申请, 通常为 HTTP-01(需域名解析到服务器)
    domain_regex:
      - "(\w+)\.domain\.com"你要的泛域名
  named: # 服务启动时自动申请或续期, 通配符必须用 DNS-01(需配置 DNS 凭证)
    certificates:
      - name: "_wildcard.domain.com 你要的泛域名"
        account: "iakuf@163.com"
        dns_credential: alidns
        force_rsa: false
        domains:
          - "*.listlive.space"

完整 simplessl 配置文件说明

version: "2"  # 配置文件格式版本,0.6.0 版本新增
listen: "127.0.0.1:8999"  # simplessl 服务监听地址,请勿对外暴露
pid_file: "simplessl.pid"  # PID 文件路径,用于优雅重启

# Envoy SDS 配置
enable_sds: true  # 是否启用 Envoy SDS(Secret Discovery Service),默认 false
sds_listen: "127.0.0.1:8998"  # SDS 服务监听地址,需 enable_sds 为 true
sds_ca_cert: "./secret-dir/ca.cert"  # SDS gRPC 客户端验证的根证书路径
sds_server_cert: "./secret-dir/sds-server.cert"  # SDS gRPC 服务器证书路径
sds_server_key: "./secret-dir/sds-server.key"  # SDS gRPC 服务器私钥路径

# 存储配置
storage:
  type: "dir_cache"  # 存储类型,支持 "dir_cache" 或 "redis"
  dir_cache: "./secret-dir"  # 如果类型为 "dir_cache",指定证书缓存目录
  redis:  # 如果类型为 "redis",配置 Redis 连接
    addr: "redis://<user>:<password>@<host>:<port>/<db_number>"  # Redis 连接字符串
    prefix: ""  # 证书键名前缀,可选

# 自签名证书配置
self_signed:
  enable: false  # 是否启用自签名证书,默认 false
  check_domain_name: false  # 是否检查域名,默认 false
  domains:  # 允许使用自签名证书的域名列表
    - "a.example.com"
    - "a.example-1.com"
  domain_regex:  # 允许使用自签名证书的正则表达式域名
    - "[a-z]+\.example\.com"
    - "a\.example-1\.com"
  valid_days: 3650  # 自签名证书有效期(天)
  organization: "SSL Cert Server Self-Signed"  # 自签名证书的组织名称

# 托管证书配置
managed:
  reload_interval: "10m"  # 从存储中重新加载托管证书的时间间隔
  certificates:  # 托管证书配置列表
    - name: "abc.example.com"  # 证书名称
      domains:  # 证书域名列表
        - "abc.example.com"
      domain_regex:  # 证书域名正则表达式
        - "(abc|def)\.example\.com"
        - "img-\d+\.example\.com"
      no_ocsp_stapling: false  # 是否禁用 OCSP 装订,默认 false
    - name: "_wildcard.example.com"  # 通配符证书示例
      domains:
        - "example.com"
        - "*.example.com"
      domain_regex:
        - "(abc|def)\.example\.com"
        - "biz-\w+\.example\.com"
      no_ocsp_stapling: false

# ACME 配置
acme:
  directory_url: "https://acme-v02.api.letsencrypt.org/directory"  # ACME 目录 URL
  force_rsa: false  # 是否强制使用 2048 位 RSA 密钥,默认 false
  renew_before: 30  # 证书到期前多少天续期,默认 30 天
  default_account:  # 默认 ACME 账户
    email: "acme@example.com"  # 默认账户邮箱
  accounts:  # 其他 ACME 账户
    - email: "another@somedomain.com"
  dns_credentials:  # DNS 凭证配置,用于 DNS-01 挑战
    - name: example_alidns  # 凭证名称
      provider: alidns  # DNS 提供商类型
      env:  # 所需环境变量
        ALICLOUD_ACCESS_KEY: "my_access_key_xxxx"
        ALICLOUD_SECRET_KEY: "my_secret_key_xxxx"
    - name: another_godaddy_1
      provider: godaddy
      env:
        GODADDY_API_KEY: "my_api_key_xxxx"
        GODADDY_API_SECRET: "my_api_secret_xxxx"
    - name: another_cloudflare_1
      provider: cloudflare
      env:
        CLOUDFLARE_EMAIL: "my_email_xxxx"
        CLOUDFLARE_API_KEY: "my_api_key_xxxx"
  on_demand:  # 按需证书配置
    domains:  # 允许按需签发证书的域名列表
      - a.example-2.com
      - b.example-2.com
    domain_regex:  # 允许按需签发证书的正则表达式域名
      - "api-1-(\w+)\.example\.com"
      - "api-2-(\w+)\.example\.com"
  named:  # 命名证书配置
    certificates:
      - name: "z_san_example_com"  # 证书名称
        account: "acme@example.com"  # 使用的 ACME 账户
        dns_credential: example_alidns  # 使用的 DNS 凭证
        force_rsa: false  # 是否强制使用 RSA 密钥
        domains:  # 证书域名列表
          - "z1.example.com"
          - "z2.example.com"
          - "z3.example.com"
      - name: "_wildcard.y.somedomain.com"  # 通配符证书示例
        account: "another@somedomain.com"
        dns_credential: another_cloudflare_1
        force_rsa: false
        domains:
          - "y.somedomain.com"
          - "*.y.somedomain.com"
  • 当前版本主要设计用于内网环境
  • 务必做好证书服务器的安全防护
  • 关注后续安全更新