NGINX ACME 模块全攻略:从编译安装到自动化证书管理
基于 Rust 的官方 NGINX ACME 动态模块集成指南
目录
ngx_http_acme_module 是由 NGINX 官方(F5, Inc.)开发的一个基于 Rust 的动态模块。它实现了 ACMEv2 协议(RFC 8555),允许 NGINX 直接处理证书的申请、续期和管理,支持 HTTP-01 和 TLS-ALPN-01 验证方式。
1. 编译与安装
由于该模块通常不包含在预编译的 NGINX 发行版中,我们需要通过源码编译的方式将其作为动态模块集成。
前置条件
- 操作系统:推荐 Debian 或 Ubuntu。
- 工具依赖:系统需安装有
curl,jq,tar以及基本的编译环境。 - 权限:具有
sudo权限。
使用自动化脚本编译
你可以创建一个名为 nginx-acme.sh 的脚本并运行它。该脚本会自动执行以下流程:
- 获取
nginx-acme最新版本。 - 检测系统当前 Nginx 版本并下载匹配的源码。
- 自动安装必要的编译依赖(Debian/Ubuntu)。
- 使用
--with-compat参数编译动态模块,确保二进制兼容性。 - 将
ngx_http_acme_module.so安装到 Nginx 模块目录并验证加载。
脚本内容如下:
#!/usr/bin/env bash
#============================================================
# File: nginx-acme.sh
# Description: 编译 nginx-acme 模块并集成到当前系统 Nginx
# Author: Jetsung Chan <i@jetsung.com>
# Version: 0.1.0
# CreatedAt: 2026-03-15
# UpdatedAt: 2026-03-15
#============================================================
if [[ -n "${DEBUG:-}" ]]; then
set -eux
else
set -euo pipefail
fi
USER_ID="$(id -u)"
sudo_exec() {
if [[ "$USER_ID" -ne 0 ]]; then
sudo "$@"
else
"$@"
fi
}
check_is_command() {
command -v "$1" >/dev/null 2>&1
}
# 1. 获取最新 nginx-acme 版本和下载链接
echo "正在获取 nginx-acme 最新版本信息..."
ACME_RELEASE_INFO=$(curl -fsSL https://api.github.com/repos/nginx/nginx-acme/releases/latest)
ACME_VERSION=$(echo "$ACME_RELEASE_INFO" | jq -r '.tag_name')
ACME_DOWNLOAD_URL=$(echo "$ACME_RELEASE_INFO" | jq -r '.assets[] | select(.name | endswith(".tar.gz")) | .browser_download_url')
echo "最新版本: $ACME_VERSION"
# 2. 获取当前系统 Nginx 版本
if ! check_is_command "nginx"; then
echo "错误: 系统未安装 nginx,无法编译模块。"
exit 1
fi
NGINX_VER=$(nginx -v 2>&1 | cut -d '/' -f 2)
echo "当前 Nginx 版本: $NGINX_VER"
# 3. 准备工作目录
WORK_DIR=$(mktemp -d /tmp/nginx-acme-build.XXXXXX)
cd "$WORK_DIR"
# 4. 下载源码
echo "正在下载 nginx-acme 源码..."
curl -fsSL "$ACME_DOWNLOAD_URL" -o "nginx-acme.tar.gz"
tar -xzf "nginx-acme.tar.gz"
ACME_SRC_DIR=$(find . -maxdepth 1 -type d -name "nginx-acme-*" | head -n 1)
echo "正在下载 Nginx $NGINX_VER 源码..."
curl -fsSL "https://nginx.org/download/nginx-${NGINX_VER}.tar.gz" -o "nginx.tar.gz"
tar -xzf "nginx.tar.gz"
NGINX_SRC_DIR="nginx-${NGINX_VER}"
# 5. 安装编译依赖
echo "正在安装编译依赖..."
sudo_exec apt-get update -y
sudo_exec apt-get install -y clang pkg-config libssl-dev libpcre2-dev zlib1g-dev libclang-dev
# 6. 编译模块
echo "开始编译模块..."
cd "$NGINX_SRC_DIR"
# 获取原始编译参数并添加动态模块支持
# 注意:必须包含 --with-compat 以确保二进制兼容
./configure --with-compat --with-http_ssl_module --add-dynamic-module="../$ACME_SRC_DIR"
make modules
# 7. 安装模块
MODULE_PATH=$(nginx -V 2>&1 | grep -oP "modules-path=\K[^ ]*")
if [[ -z "$MODULE_PATH" ]]; then
MODULE_PATH="/usr/lib/nginx/modules"
fi
echo "编译完成。正在将模块复制到 $MODULE_PATH..."
sudo_exec mkdir -p "$MODULE_PATH"
sudo_exec cp objs/ngx_http_acme_module.so "$MODULE_PATH/"
# 8. 测试模块是否正常加载
echo "正在验证模块是否加载正常..."
TEST_CONF=$(mktemp /tmp/nginx-acme-test.XXXXXX.conf)
cat > "$TEST_CONF" <<EOF
error_log /tmp/nginx-acme-test-error.log;
pid /tmp/nginx-acme-test.pid;
load_module $MODULE_PATH/ngx_http_acme_module.so;
events {
worker_connections 1024;
}
http {
access_log /tmp/nginx-acme-test-access.log;
}
EOF
if sudo_exec nginx -t -c "$TEST_CONF" > /dev/null 2>&1; then
echo "验证成功: 模块 $MODULE_PATH/ngx_http_acme_module.so 已成功加载并与当前 Nginx 兼容。"
else
echo "验证失败: 模块加载出错。请检查错误日志: /tmp/nginx-acme-test-error.log"
sudo_exec cat /tmp/nginx-acme-test-error.log
rm -f "$TEST_CONF"
exit 1
fi
rm -f "$TEST_CONF" /tmp/nginx-acme-test-error.log /tmp/nginx-acme-test.pid /tmp/nginx-acme-test-access.log
echo "----------------------------------------------------"
echo "nginx-acme 模块已安装并验证成功!"
echo "模块位置: $MODULE_PATH/ngx_http_acme_module.so"
echo "请在您的主 nginx.conf 顶层添加以下行以启用模块:"
echo "load_module modules/ngx_http_acme_module.so;"
echo "----------------------------------------------------"
# 清理
rm -rf "$WORK_DIR"赋予执行权限并运行:
chmod +x nginx-acme.sh
./nginx-acme.sh若看到“nginx-acme 模块已安装并验证成功!”的提示,说明安装已完成。
2. 模块加载
在 NGINX 中使用动态模块,必须在 nginx.conf 的最顶部(全局块)进行加载:
load_module modules/ngx_http_acme_module.so;3. 基础配置示例
以下是一个使用 Let’s Encrypt 获取证书并在 443 端口使用的典型配置:
http {
# 1. 必须配置解析器,用于解析 ACME 服务器域名
resolver 8.8.8.8 1.1.1.1 valid=300s;
resolver_timeout 5s;
# 2. 定义共享内存区域(必填),用于同步证书状态
# 1M 空间约可管理 50 个证书
acme_shared_zone zone=ngx_acme_shared:1M;
# 3. 定义 ACME 发行者
acme_issuer letsencrypt {
uri https://acme-v02.api.letsencrypt.org/directory;
state_path /var/cache/nginx/acme; # 存储账户和证书的目录
accept_terms_of_service;
contact mailto:admin@example.com;
}
# 4. HTTP 验证 Server (80 端口)
server {
listen 80;
server_name example.com;
# 模块会自动处理 /.well-known/acme-challenge/
location / {
return 301 https://$host$request_uri;
}
}
# 5. HTTPS Server (443 端口)
server {
listen 443 ssl;
server_name example.com;
# 关联发行者并请求证书
acme_certificate letsencrypt;
# 使用模块提供的动态变量加载证书和密钥
ssl_certificate $acme_certificate;
ssl_certificate_key $acme_certificate_key;
# 推荐:启用证书缓存
ssl_certificate_cache max=10;
location / {
root /usr/share/nginx/html;
}
}
}4. 指令详解
| 指令 | 上下文 | 说明 |
|---|---|---|
acme_issuer | http | 定义证书发行者块。内含 uri (必填)、contact、state_path 等子指令。 |
acme_certificate | server | 为当前虚拟主机请求证书。格式:acme_certificate issuer_name [identifiers...]; |
acme_shared_zone | http | 分配共享内存以在所有工作进程间同步证书数据。 |
state_path | acme_issuer | 证书和账户数据的持久化存储路径。必须对 Nginx 用户可见并有写权限。 |
5. 进阶用法
TLS-ALPN-01 验证
如果你无法开启 80 端口,可以使用 TLS-ALPN-01 方式(必须在 443 端口进行):
acme_issuer cloud_issuer {
uri https://...;
challenge tls-alpn-01;
state_path /etc/nginx/acme;
}
server {
listen 443 ssl;
server_name example.com;
acme_certificate cloud_issuer;
...
}IP 证书支持
该模块支持根据 RFC 8738 申请 IP 证书。只需将 IP 地址放入 server_name 或作为 acme_certificate 的参数即可。
嵌入式变量
模块提供了两个核心变量,直接用于 ssl_certificate 系列指令:
$acme_certificate: 签发后的证书内容(PEM 格式)。$acme_certificate_key: 对应的私钥内容。
6. 注意事项与维护
目录权限
在启动前,请务必手动创建状态目录并授予 Nginx 运行用户(如 www-data)读写权限:
sudo mkdir -p /var/cache/nginx/acme
sudo chown www-data:www-data /var/cache/nginx/acme域名解析
必须在 http 块中配置 resolver,否则模块无法连接到 ACME 发行者服务器。
验证配置与重启
完成修改后,请先测试配置:
sudo nginx -t
sudo systemctl reload nginxNginx 会自动启动后台进程开始与 ACME 服务器通信。你可以查看 Nginx 错误日志以追踪申请进度。
故障排查
- 验证失败:检查 80 端口是否被防火墙拦截,或者是否存在强制性的全局 301 跳转干扰了验证路径。
- 二进制不兼容:如果手动编译后无法加载,请确保编译环境的 Nginx 源码版本与系统安装的版本完全一致。