# AcmeX v0.4.0 - 新功能使用指南
## 📌 内置 DNS 提供商
### CloudFlare DNS-01
```rust
use acmex::{
AcmeClient, AcmeConfig, Dns01Solver, ChallengeSolverRegistry,
CloudFlareDnsProvider, Contact,
};
use std::sync::Arc;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 1. 创建 CloudFlare 提供商
let cf_provider = CloudFlareDnsProvider::new(
CloudFlareConfig {
api_token: std::env::var("CF_API_TOKEN")?,
zone_id: std::env::var("CF_ZONE_ID")?,
}
);
// 2. 配置 ACME 客户端
let config = AcmeConfig::lets_encrypt_staging()
.with_contact(Contact::email("admin@example.com"))
.with_tos_agreed(true);
let mut client = AcmeClient::new(config)?;
client.register_account().await?;
// 3. 创建 DNS-01 求解器
let mut registry = ChallengeSolverRegistry::new();
registry.register(Dns01Solver::new(
Arc::new(cf_provider),
"example.com".to_string(),
));
// 4. 申请证书 (支持通配符)
let domains = vec![
"example.com".to_string(),
"*.example.com".to_string(),
];
let cert = client.issue_certificate(domains, &mut registry).await?;
cert.save_to_files("certificate.pem", "private_key.pem")?;
println!("✅ 通配符证书已签发!");
Ok(())
}
```
### DigitalOcean DNS-01
```rust
use acmex::{
AcmeClient, AcmeConfig, Dns01Solver, ChallengeSolverRegistry,
DigitalOceanDnsProvider, Contact,
};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let do_provider = DigitalOceanDnsProvider::new(
DigitalOceanConfig {
api_token: std::env::var("DO_API_TOKEN")?,
domain: "example.com".to_string(),
}
);
// ... 其他配置类似 CloudFlare ...
Ok(())
}
```
### Linode DNS-01
```rust
use acmex::{
LinodeDnsProvider, LinodeConfig,
};
let linode_provider = LinodeDnsProvider::new(
LinodeConfig {
api_token: std::env::var("LINODE_TOKEN") ?,
domain_id: 12345, // Linode 域名 ID
}
);
```
---
## 🔄 自动续期系统
### 基础续期示例
```rust
use acmex::{
AcmeClient, AcmeConfig, RenewalScheduler, RenewalHook,
storage::{FileStorage, CertificateStore},
CertificateBundle,
};
use std::sync::Arc;
use std::time::Duration;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 1. 创建存储后端
let storage = FileStorage::new(".acmex");
let store = CertificateStore::new(storage);
// 2. 创建 ACME 客户端
let config = AcmeConfig::lets_encrypt();
let client = AcmeClient::new(config)?;
// 3. 创建续期调度器
let scheduler = RenewalScheduler::new(client, store)
.with_check_interval(Duration::from_secs(3600)) // 每小时检查
.with_renew_before(Duration::from_secs(30 * 24 * 3600)); // 30 天前续期
// 4. 启动后台任务
let domains_list = vec![
vec!["example.com".to_string(), "www.example.com".to_string()],
vec!["api.example.com".to_string()],
];
scheduler.run(domains_list).await?;
Ok(())
}
```
### 带续期钩子的示例
```rust
use acmex::RenewalHook;
struct LoggingHook;
impl RenewalHook for LoggingHook {
fn before_renewal(&self, domains: &[String]) {
tracing::info!("开始续期:{:?}", domains);
}
fn after_renewal(&self, domains: &[String], bundle: &acmex::CertificateBundle) {
tracing::info!("续期成功:{:?}", domains);
// 可以在这里部署证书到服务器
// 示例:复制到 /etc/nginx/certs/
let _ = std::fs::copy(
&bundle.certificate_pem,
format!("/etc/nginx/certs/{}.pem", domains[0]),
);
}
fn on_error(&self, domains: &[String], error: &acmex::AcmeError) {
tracing::error!("续期失败 {:?}: {}", domains, error);
// 可以在这里发送告警邮件
}
}
// 使用钩子
let scheduler = scheduler.with_hook(Arc::new(LoggingHook));
```
---
## 💾 证书存储后端
### 文件系统存储 (默认)
```rust
use acmex::storage::{FileStorage, CertificateStore};
let storage = FileStorage::new(".acmex");
let store = CertificateStore::new(storage);
// 保存证书
store.save(&certificate_bundle).await?;
// 加载证书
if let Some(bundle) = store.load(&["example.com"]).await? {
println!("找到已保存的证书");
}
// 删除证书
store.delete(&["example.com"]).await?;
```
### Redis 存储
```rust
// 启用 redis feature
// cargo build --features redis
use acmex::storage::{RedisStorage, CertificateStore};
let redis_storage = RedisStorage::new("redis://127.0.0.1:6379")?;
let store = CertificateStore::new(redis_storage);
// API 与文件存储相同
store.save(&certificate_bundle).await?;
```
### 加密存储
```rust
use acmex::storage::{FileStorage, EncryptedStorage, CertificateStore};
use rand::RngCore;
// 生成 256-bit 加密密钥
let mut key = [0u8; 32];
rand::rngs::OsRng.fill_bytes(&mut key);
// 创建加密的文件存储
let file_storage = FileStorage::new(".acmex");
let encrypted_storage = EncryptedStorage::new(file_storage, key);
let store = CertificateStore::new(encrypted_storage);
// 所有数据自动加密存储
store.save(&certificate_bundle).await?;
```
### Redis + 加密存储
```rust
use acmex::storage::{RedisStorage, EncryptedStorage, CertificateStore};
let redis_storage = RedisStorage::new("redis://127.0.0.1:6379") ?;
let encrypted = EncryptedStorage::new(redis_storage, key);
let store = CertificateStore::new(encrypted);
// Redis 中的数据被加密存储
store.save( & certificate_bundle).await?;
```
---
## 📊 Prometheus 指标
### 基础使用
```rust
use acmex::MetricsRegistry;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let metrics = MetricsRegistry::new();
// 记录请求
metrics.requests_total.inc();
// 记录续期
metrics.renewals_total.inc();
// 更新当前管理的证书数
metrics.certs_managed.set(5);
// 输出 Prometheus 格式
let text = metrics.gather_text();
println!("{}", text);
Ok(())
}
```
### 与 Axum 服务器集成
```rust
use axum::{
routing::get,
Router,
};
use std::sync::Arc;
async fn metrics_handler(
axum::extract::State(metrics): axum::extract::State<Arc<MetricsRegistry>>
) -> String {
metrics.gather_text()
}
#[tokio::main]
async fn main() {
let metrics = Arc::new(MetricsRegistry::new());
let app = Router::new()
.route("/metrics", get(metrics_handler))
.with_state(metrics);
let listener = tokio::net::TcpListener::bind("127.0.0.1:9090")
.await
.unwrap();
axum::serve(listener, app).await.unwrap();
}
```
---
## 🎛️ CLI 工具使用
### 申请证书
```bash
# 基础用法
acmex obtain --domains example.com --email admin@example.com
# 多个域名
acmex obtain \
--domains example.com \
--domains www.example.com \
--email admin@example.com
# 使用 DNS-01 和生产环境
acmex obtain \
--domains example.com \
--domains "*.example.com" \
--email admin@example.com \
--challenge dns-01 \
--prod
# 自定义输出路径
acmex obtain \
--domains example.com \
--email admin@example.com \
--cert-path /etc/ssl/certs/example.pem \
--key-path /etc/ssl/private/example.key
```
### 续期证书
```bash
# 续期单个域名
acmex renew --domains example.com
# 续期多个域名
acmex renew \
--domains example.com \
--domains www.example.com
# 强制续期
acmex renew --domains example.com --force
```
### 启动续期守护程序
```bash
# 使用默认配置
acmex daemon --storage-dir .acmex --interval 3600
# 使用配置文件
acmex daemon --config /etc/acmex/daemon.toml
```
### 查看证书信息
```bash
acmex info --cert certificate.pem
```
### 日志级别控制
```bash
# Debug 日志
acmex --log-level debug obtain --domains example.com
# 生产环境 (Info 日志)
acmex --log-level info daemon
```
---
## 🔧 Feature Flags 使用
### 最小化构建 (仅库)
```bash
cargo build --release
```
### 完整构建 (所有功能)
```bash
cargo build --release \
--features dns-cloudflare,dns-route53,dns-digitalocean,dns-linode,redis,metrics,cli
```
### 特定组合
```bash
# CloudFlare + Redis + 指标
cargo build --features dns-cloudflare,redis,metrics
# 所有 DNS 提供商 + 加密
cargo build --features dns-cloudflare,dns-route53,dns-digitalocean,dns-linode
# CLI 工具
cargo build --features cli --bin acmex
```
### 使用 Ring 而不是 AWS-LC
```bash
cargo build --no-default-features --features ring-crypto,dns-cloudflare
```
---
## 📋 高级配置示例
### 多提供商 DNS-01
```rust
use acmex::{Dns01Solver, ChallengeSolverRegistry};
use std::sync::Arc;
let mut registry = ChallengeSolverRegistry::new();
// 为不同的域名注册不同的 DNS 提供商
registry.register(Dns01Solver::new(
Arc::new(CloudFlareDnsProvider::new(cf_config)),
"example.com".to_string(),
));
registry.register(Dns01Solver::new(
Arc::new(DigitalOceanDnsProvider::new(do_config)),
"api.otherdomain.com".to_string(),
));
// ACME 客户端会自动使用合适的提供商
```
### 完整的企业部署
```rust
use acmex::*;
use std::sync::Arc;
use std::time::Duration;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 1. 初始化日志
tracing_subscriber::fmt::init();
// 2. 存储配置 (加密 Redis)
let redis_storage = storage::RedisStorage::new("redis://redis.local:6379")?;
let encrypted = storage::EncryptedStorage::new(redis_storage, key);
let store = storage::CertificateStore::new(encrypted);
// 3. 创建 ACME 客户端
let config = AcmeConfig::lets_encrypt()
.with_contact(Contact::email("admin@company.com"))
.with_tos_agreed(true);
let client = AcmeClient::new(config)?;
// 4. 创建续期调度器
let scheduler = RenewalScheduler::new(client, store)
.with_check_interval(Duration::from_secs(3600))
.with_renew_before(Duration::from_secs(30 * 24 * 3600))
.with_hook(Arc::new(CompanyRenewalHook));
// 5. 启动 Prometheus 指标服务
let metrics = Arc::new(MetricsRegistry::new());
tokio::spawn(metrics_server(metrics.clone()));
// 6. 启动续期守护程序
let domains = vec![
vec!["company.com".to_string(), "www.company.com".to_string()],
vec!["api.company.com".to_string()],
vec!["*.internal.company.com".to_string()],
];
scheduler.run(domains).await?;
Ok(())
}
struct CompanyRenewalHook;
impl RenewalHook for CompanyRenewalHook {
fn after_renewal(&self, domains: &[String], bundle: &CertificateBundle) {
// 1. 部署证书
deploy_certificate(domains, bundle);
// 2. 重加载服务
reload_nginx();
// 3. 通知监控系统
notify_monitoring("Certificate renewed", domains);
}
fn on_error(&self, domains: &[String], error: &AcmeError) {
// 发送告警
send_alert(&format!("Certificate renewal failed: {}", error), domains);
}
}
```
---
## 🚀 性能优化建议
### 1. 使用 Redis 存储
```rust
// 相比文件存储快 5-10 倍
let storage = RedisStorage::new("redis://...") ?;
```
### 2. 调整检查间隔
```rust
// 每 6 小时检查一次 (而不是每小时)
.with_check_interval(Duration::from_secs(6 * 3600))
```
### 3. 批量操作
```rust
// 在一个续期周期中处理多个域名
let domains = vec![
vec!["site1.com".to_string()],
vec!["site2.com".to_string()],
// ...
];
```
---
**版本**: v0.4.0
**最后更新**: 2026-02-07