11 个不稳定版本 (3 个重大更新)
0.6.1 | 2023年10月16日 |
---|---|
0.6.0 | 2023年10月2日 |
0.5.2 | 2023年6月29日 |
0.5.0 | 2023年4月20日 |
0.3.3 | 2023年4月14日 |
#328 in 密码学
每月下载量 57
445KB
9K SLoC
Björn - AS207960 ACME 服务器
Björn 本身不是一个完整的 CA,但它包含了完整 ACME CA 的许多构建模块。
在 Debian 上安装
apt install curl build-essential libssl-dev pkg-config protobuf-compiler libpq-dev
curl https://sh.rustup.rs -sSf | sh
source "$HOME/.cargo/env"
cargo install bjorn-acme
组件
Björn 由三个主要组件组成,并包括一个使用 Django 编写的示例 CA 后端。此 CA 后端**严禁在生产环境中使用**。它仅用于演示目的。
内部组件之间的通信通过 gRPC 进行。
组件包括
- Björn - ACME 前端和最大的组件。它处理账户注册,并作为 CA 后端和 ACME 客户端之间的代理。
- Benny - OCSP 响应者。它接收并响应来自 TLS 客户端的 OCSP 请求,并将请求路由到处理最终实体证书中使用的签名证书的后端。
- Frida - ACME 验证器。它处理 CAA 检查,并验证 ACME
http-01
和dns-01
挑战。
它们之间基本的工作关系图如下;
End user <-> CA boundary
| *------------*
| | PostgreSQL | <--------------
| *------------* \
| |
*-------------* | *-------*
| ACME Client | ---ACME over HTTPS--|-- --> | Björn | ------
*-------------* | \ *-----------------* / *-------* \
| --> | TLS terminating | -- gRPC |
| --> | ingress proxy | -- |
*------------* | / *-----------------* \ *-------* |
| TLS Client | --OCSP over HTTP(S)--|-- --> | Benny | ---- |
*------------* | *-------* \ |
| gRPC | |
| / |
*-------------* | http-01 | | *-------* *------------* <-- |
| User server | <--| dns-01 |----|----- | Frida | <----gRPC---- | Backend CA | /
*-------------* |tls-alpn-01| | *-------* *------------* <----
仓库布局
目录 | 内容 |
---|---|
migrations |
SQL 数据库迁移 |
proto |
gRPC 互操作定义 |
python-ca |
示例 CA 后端**严禁在生产环境中使用** |
src |
通用 Rust 源代码 |
src/bin/bjorn-acme.rs |
Björn 二进制文件 |
src/acme |
Björn 特定工具 |
src/bin/bjorn-ocsp.rs |
Benny 二进制文件 |
src/ocsp |
Benny 特定工具 |
src/bin/bjorn-validator.rs |
Frida 二进制文件 |
src/validator |
Frida 特定工具 |
templates |
Björn HTML 模板 |
实现 RFC
- RFC 6960 - 在线证书状态协议 - OCSP
- RFC 7638 - JSON Web Key (JWK) Thumbprint
- RFC 8555 - 自动证书管理环境 (ACME)
- RFC 8657 - 账户 URI 和自动证书管理环境 (ACME) 方法绑定证书授权 (CAA) 记录扩展
- RFC 8659 - DNS 证书授权 (CAA) 资源记录
- RFC 8737 - 自动证书管理环境 (ACME) TLS 应用层协议协商 (ALPN) 挑战扩展
- RFC 8738 - 自动证书管理环境 (ACME) IP 标识符验证扩展
- RFC 8954 - 在线证书状态协议 (OCSP) 非法扩展
- draft-shoemaker-caa-ip-01 - IP 地址的证书授权 (CAA) 验证
- draft-ietf-acme-onion-01 - 自动证书管理环境 (ACME) 对 ".onion" 专用域名名的扩展
注意:CAA iodef 尚未得到支持
设置
所有组件都使用 Rocket 配置系统 进行配置。请参阅链接的 Rocket 文档以了解配置监听端口和地址的详细信息。
Björn
Björn 应该设置在实现速率限制和 TLS 终止的 HTTP 代理后面。
示例 Rocket.toml
[development]
external_uri = "https://127.0.0.1:8000"
tos_uri = "https://as207960.net/assets/docs/AS207960_T_C_s.pdf"
tos_agreed_to_after = "2021-09-29T00:00:00Z"
website_uri = "https://as207960.net"
caa_identities = ["bjorn.as207960.net"]
ca_grpc_uri = "http://[::1]:50051"
template_dir = "templates/"
external_account_required = false
in_band_onion_caa_required = false
[[development.acme_issuers]]
cert_id = "a"
issuer_cert_file = "python-ca/ca-certs/intermediate-crt.pem"
[development.databases]
db = { url = "postgres://postgres@localhost/bjorn" }
配置说明。
external_uri
ACME 服务器可以通过外部最终用户访问的基本 URL,包括协议和端口。
tos_uri
ACME 服务器的服务条款和条件页面的 URL。最终用户应在他们的 ACME 客户端中看到此页面,并在继续之前同意它。
tos_agreed_to_after
一个 ISO8601 时间戳,对于在之前同意服务条款的账户,在继续请求之前将需要再次同意。如果 ToS 已更新且用户需要了解新/更新条款,则非常有用。
website_uri
运行 ACME 服务器的实体的主页。
caa_identities
哪些 CAA 发起者身份被 ACME 服务器识别为允许此 CA 发行证书。
ca_grpc_uri
CA 后端 gRPC URL。它应该实现以下部分中描述的协议。
template_dir
存储索引页(当 ACME 服务器未使用 ACME 客户端访问时)和更新 ToS 协议页面的 HTML 模板所在的目录路径。
external_account_required
此服务器是否要求所有账户注册使用 EAB。
in_band_onion_caa_required
此服务器是否仅支持 Tor 隐藏服务的带内 CAA。
acme_issuers
此服务器可以处理吊销请求的发行证书列表。
cert_id
后端 CA 用来识别发行证书的 ID。
issuer_cert_file
发行证书的 X.509 公钥文件(PEM 编码)的路径。
db.url
存储 ACME 账户的 PostgreSQL 数据库的连接 URL。
Benny
Benny 应该设置在尊重 Benny 设置的 Cache-Control
,Expires
,ETag
等等 HTTP 头的缓存 HTTP 代理后面。
示例 Rocket.toml
[[development.ocsp_issuers]]
cert_id = "issuer-1"
issuer_cert_file = "python-ca/ca-certs/intermediate-crt.pem"
signer_pkcs12_file = "ocsp-signer.p12"
grpc_uri = "http://[::1]:50051"
配置说明。
ocsp_issuers
此响应者具有权威性的发行证书列表。
cert_id
后端 CA 用来识别发行证书的 ID。
issuer_cert_file
发行证书的 X.509 公钥文件(PEM 编码)的路径。
signer_pkcs12_file
包含用于签署OCSP响应的私钥、其相关公钥以及需要链接到根的任何证书的PKCS.12编码文件。签名证书应该是发行者证书直接(不推荐),或者是带有id-kp-OCSPSigning
扩展关键用途属性的发行者证书。
grpc_uri
此发行者的CA后端gRPC URL。它应该实现以下部分描述的协议。
Frida
Frida应该在配置了本地DNSSEC验证解析器的机器上设置。DNSSEC验证对于CAA的完整性至关重要。
示例 Rocket.toml
[development]
caa_identities = ["bjorn.as207960.net"]
tor_storage = "./tor"
配置说明。
caa_identities
哪些CAA发行者身份被ACME服务器认可为允许由该CA签发(理想情况下与Björn上配置的相同)。
tor_storage
Frida将在此目录中存储Tor隐藏服务的缓存状态。此设置不是必需的。如果没有设置,Frida将不会连接到Tor网络,并且只能验证隐藏服务的带内CAA。
互操作协议描述
通用
错误
Björn理解基本的gRPC错误,例如未找到和内部服务器错误,并将它们转换为适合传输给客户端的错误。
为了获得更大的控制和具体性,后端CA还可以直接构建ACME错误以发送给客户端。《Error》类型基于ACME返回的JSON错误。
enum ErrorType {
...
}
message Error {
ErrorType error_type = 1;
string title = 2;
uint32 status = 3;
string detail = 4;
google.protobuf.StringValue instance = 5;
repeated Error sub_problems = 6;
Identifier identifier = 7;
}
error_type
字段包含在RFC 8555 § 6.7中定义的错误。应包含故障的概括摘要的title
字段。应包含故障的合适HTTP错误代码的status
字段。应包含故障的详细描述的detail
字段。仅在返回userActionRequired
字段时,应使用instance
字段,并应包含一个URL以直接指导最终用户。可以使用sub_problems
字段将故障分解成更小的故障。可以使用identifier
字段来识别此故障与订单中的哪个标识符相关。
Björn
ValidateEAB
此函数用于检查客户端指定的外部账户绑定是否有效。后端应查找由kid
字段指定的HMAC密钥,并使用指定的signature_method
检查对signed_data
字段的signature
字段是否有效。
如果HMAC密钥不存在,或签名无效,后端必须返回一个结果,其中包含valid
字段为false
,否则必须返回true
。
CreateOrder
后端必须检查请求的标识符是否未受策略禁止,并且not_before
和not_after
是否在服务器策略内。
account_id
字段包含由前端生成的ACME账户ID,而eab_id
字段包含在先前的ValidateEAB
成功后的EAB kid
。
后端必须以挂起状态的Order
响应,或者如果它不愿意以请求的方式创建订单,则返回错误。
CreateAuthorization
后端必须检查请求的标识符是否未受策略禁止,并且它是否愿意允许预先授权。
account_id
字段包含由前端生成的ACME账户ID,而eab_id
字段包含在先前的ValidateEAB
成功后的EAB kid
。
后端必须以挂起状态的Authorization
响应,或者如果它不愿意以请求的方式创建订单,则返回错误。
FinalizeOrder
后端必须根据之前订单对象中生成的ID查找订单。后端无需检查权限,因为前端已经完成了这项检查。如果指定的订单不存在,应使用标准的gRPC NOT_FOUND
状态码。
后端必须检查订单是否处于愿意颁发证书的状态(即有效),否则返回错误。
csr
字段包含DER编码的CSR。后端必须通过策略检查CSR是否有效。如果是,则必须开始颁发证书,这应该在后台完成。
account_uri
字段包含ACME账户的URI,用于CAA检查。
onion_caa
字段包含客户端提供的Tor隐藏服务的带内CAA记录。
后端必须以处理状态返回一个 Order
,或者如果它不愿意按照请求处理订单,则返回错误。
DeactivateAuthorization
后端必须通过之前授权对象中生成的ID查找授权。后端无需检查权限,因为前端已经完成了这项检查。如果指定的授权不存在,应使用标准的gRPC NOT_FOUND
状态码。
如果授权处于可用状态(即未停用、未过期或未被撤销),后端必须将授权标记为停用,并使其对未来的订单不可用。
CompleteChallenge
后端必须通过之前授权对象中生成的ID和授权ID查找挑战。后端无需检查权限,因为前端已经完成了这项检查。后端必须检查挑战对象是否属于授权,否则返回 NOT_FOUND
错误。
account_thumbprint
可以传递给Frida以允许验证发生。验证应该在后台进行,因为客户端将轮询服务器,直到挑战成功或失败。
GetOrder
后端必须根据之前订单对象中生成的ID查找订单。后端无需检查权限,因为前端已经完成了这项检查。如果指定的订单不存在,应使用标准的gRPC NOT_FOUND
状态码。
GetAuthorization
后端必须通过之前授权对象中生成的ID查找授权。后端无需检查权限,因为前端已经完成了这项检查。如果指定的授权不存在,应使用标准的gRPC NOT_FOUND
状态码。
GetChallenge
后端必须通过之前授权对象中生成的ID和授权ID查找挑战。后端无需检查权限,因为前端已经完成了这项检查。后端必须检查挑战对象是否属于授权,否则返回 NOT_FOUND
错误。
GetCertificate
后端必须根据之前订单对象中生成的ID查找证书。后端无需检查权限,因为前端已经完成了这项检查。后端必须以DER编码的二进制列表元素返回每个链中的每个证书。
RevokeCertificate
后端应查找与 issuer_id
(如在前端配置中设置)和证书的 serial_number
对应的证书。如果 revocation_reason
已设置,后端应检查它是否是策略可接受的理由。
如果 authz_checked
设置为 true
,则后端无需检查请求上的权限,因为前端已经完成了这些检查。如果没有,则应检查ACME account_id
是否有权撤销证书。根据RFC 8555,后端“必须考虑以下账户有权撤销特定证书”
- 颁发证书的账户。
- 持有证书中所有标识符的授权的账户。
如果撤销成功,则必须返回一个空响应,否则必须返回一个解释请求被拒绝原因的错误。
Benny
Benny期望后端CA有一个用于证书状态检查的单个方法:CheckCert
。
请求消息包含配置中设置的发行者ID和要检查的证书的序列号字段。
响应表示证书状态以及一些关于撤销的可选元数据。
后端可能会表示它不知道证书的状态,该证书是已知的良好,证书已知已撤销,或者该证书从未签发。
撤销原因是与 CRL 中相同的值。有关可用撤销原因的更多信息,请参阅RFC 5280 § 5.3.1
revocation_timestamp
表示证书被撤销的时间(如果已知)。invalidity_date
表示被认为证书被破坏的时间(如果已知)。this_update
表示检查此证书的权威源的最后时间。next_update
表示权威源将具有更多最新信息的下一个最早时间。archive_cutoff
的工作方式如RFC 6960 § 4.4.4中定义。
Frida
Frida 有 4 种验证方法;ValidateHTTP01
,ValidateDNS01
,ValidateTLSALPN01
和 ValidateOnionCSR01》。它们分别执行
http-01
,dns-01
,tls-alpn-01
和 onion-csr-01
验证。
使用 CheckCAA
方法执行 CAA 检查。
Frida 支持验证 dns
和 ip
标识符类型,不支持使用 email-reply-00
验证 email
。
KeyValidationRequest
输入消息包含;
token
- 验证令牌,由后端 CA 生成。account_thumbprint
- 由 Björn 提供的 ACME 账户指纹。identifier
- 要验证的名称。hs_private_key
- 用于连接隐藏服务的 32 字节私钥(如果适用)。
响应消息包含一个布尔指示验证成功或失败,在失败的情况下,包含导致验证失败的错误列表的错误对象。错误格式在上文描述。
在 dns
类型标识符的情况下,标识符值必须为小写,非 punny 代码(IDNA),并且根区域后不带尾随点。
OnionCSRValidationRequest
输入消息包含;
csr
- 由 ACME 客户端生成的 DER 编码的 CSR。ca_nonce
- 由后端 CA 生成的 nonce。identifier
- 要验证的名称。
CAACheckRequest
输入消息包含;
validation_method
- 用于验证标识符的方法。identifier
- 要检查的名称。account_uri
- 为 RFC 8657 目的请求验证的账户的 URI,由 Björn 提供。hs_private_key
- 用于连接隐藏服务的 32 字节私钥(如果适用)。onion_caa
- 隐藏服务的带内 CAA,如果由 ACME 客户端提供。
依赖关系
~54–91MB
~1.5M SLoC