Expand description
Route53Dns: the production backend that writes a real hosted zone.
§Why hand-rolled SigV4 over aws-sdk-route53
The official SDK is async-only (it is generated from Smithy models built
on hyper and tokio), which conflicts with this crate’s synchronous
trait — see the crate-level docs for why that is deliberate. Using it
would mean embedding a private Tokio runtime inside every provider just to
block_on each call, and pulling in aws-config, aws-runtime,
aws-smithy-runtime, aws-smithy-xml, aws-sigv4 and their own copies of
hyper/rustls — none of which are in Cargo.lock today.
Route53’s control-plane API is a small, stable REST/XML surface: five
operations, one signing scheme. Cargo.lock already carries everything
signing it needs — reqwest (with rustls-tls, already selected), hmac
and sha2 (both used elsewhere in this workspace for other purposes) —
so the addition for this feature is reqwest’s blocking client feature,
hex, time and quick-xml. The request bodies and the response shapes
this provider reads are serde types on the private xml module this file
defines, so the XML itself is quick-xml’s to escape and to parse.
§Eventual consistency: a challenge publish waits, a withdrawal does not
Every Route53 change is accepted as PENDING and becomes INSYNC some
seconds later, once it has propagated to every edge name server. The
caller of DnsProvider::publish_txt is about to ask a certificate
authority to look for the value it just published, so this provider
polls
GetChange
until the change reports INSYNC, up to a configurable timeout, and only
returns Ok once it does — never optimistically. A caller that gets Ok
back has a value a resolver can see.
Withdrawal makes the opposite call. A challenge value still visible for a
few more seconds after the order is done costs nothing, so
DnsProvider::withdraw_txt and DnsProvider::publish_caa return as
soon as Route53 accepts the change, not once it has propagated.
§Rate limits and retries
Route53 throttles the change APIs (ChangeResourceRecordSets) far below
the read APIs, and returns Throttling or PriorRequestNotComplete when
a caller exceeds it. Every call in this module retries those, and a
5xx, with capped exponential backoff (see RetryPolicy); every other
API error — including the one this provider maps to
DnsError::NotPublished — is not retried, because retrying a real
conflict cannot make it stop being one.
§Hosted-zone record limits
A Route53 hosted zone defaults to a 10,000-record-set quota. Under the
wildcard shape docs/names.md draws, the zone holds a handful of record
sets whatever the account count, and the one this provider creates and
deletes on every renewal is the _acme-challenge.<zone> TXT. Nothing
in this module enforces the quota locally — ChangeResourceRecordSets
reports it as a normal API error, which surfaces as
DnsError::Backend.
§The credential
Scope the IAM credential this provider is given to the records it writes in the one hosted zone:
{
"Version": "2012-10-17",
"Statement": [
{ "Effect": "Allow",
"Action": ["route53:ChangeResourceRecordSets"],
"Resource": "arn:aws:route53:::hostedzone/<HOSTED_ZONE_ID>",
"Condition": { "ForAllValues:StringEquals": {
"route53:ChangeResourceRecordSetsRecordTypes":
["TXT", "CAA"],
"route53:ChangeResourceRecordSetsNormalizedRecordNames":
["<zone>", "_acme-challenge.<zone>"] } } },
{ "Effect": "Allow",
"Action": ["route53:GetChange"],
"Resource": "arn:aws:route53:::change/*" }
]
}Both condition keys match per change batch — every record name and type
in the batch must be in the sets — and the names Route53 compares are
normalized: lowercase, no trailing dot. Route53Dns sends one record
set per batch, each one a pair from those sets.
docs/deployment.md already says why: control of the zone is control of
every DID under it, so this credential is as powerful as the signing
keys, and it should be scoped, rotated and kept off the host that holds
those keys the same way.
Modules§
- sigv4 🔒
- AWS Signature Version 4, the subset Route53 needs: no unsigned payload,
and headers limited to
host,x-amz-dateand (when present)x-amz-security-token. - xml 🔒
- The handful of request bodies and response shapes this provider needs.
Structs§
- Http
Transport 🔒 - Imds 🔒
- The real one: three IMDSv2 round trips against
169.254.169.254. - Imds
Credentials 🔒 - What
GET /latest/meta-data/iam/security-credentials/<role>answers with.Code,LastUpdatedandTypeare in the response and are not read. - Instance
Credentials - The credential every Route53 request in this process is signed with, kept current against the instance profile that issues it.
- Response 🔒
- One HTTP response, isolated from the
reqwesttype so tests can build one without a client. - Retry
Policy - How this provider retries a throttled or transiently failed call.
- Route53
Credentials - Long-lived AWS credentials, scoped to one hosted zone.
- Route53
Dns - The production
DnsProvider: writes and removes theTXTandCAArecords in one Route53 hosted zone. See the module docs for the operating rules — propagation, retries, quotas and the credential’s scope. - Timed
Credentials 🔒 - A credential read from IMDS, with the moment IMDS says it stops being accepted.
Enums§
- Imds
Error - Why credentials could not be fetched from the instance metadata service.
Constants§
- API_
VERSION 🔒 - DEFAULT_
ENDPOINT 🔒 - IMDS_
ENDPOINT 🔒 - IMDS_
TIMEOUT 🔒 - How long one IMDS round trip may take. Shorter than didbot-http’s shared default: 169.254.169.254 is a link-local address on the same host, so a slow answer means IMDS is not there at all rather than a congested route.
- REFRESH_
MARGIN - How far ahead of a credential’s stated expiry a refresh is made.
Traits§
- Credential
Fetch 🔒 - The IMDS read
InstanceCredentialsrepeats, isolated the same wayTransportis and for the same reason: there is no instance metadata service in this environment, so tests drive the holder against a fake that expires on command. - Transport 🔒
- The network boundary this provider drives, isolated so tests can replace
it with something that records calls and returns canned XML instead of
reaching the real Route53 API — which there is no AWS account to test
against in this environment.
HttpTransportis the only real implementation. The tests insigv4check each step of the signature it sends against AWS’s published signing test suite.
Functions§
- api_
client 🔒 - The client the Route53 API is reached through. Without a timeout, a
stalled call would hang whichever DNS operation asked for it, which is
why the fallback to
reqwest’s own defaults says so atwarnrather than leaving that hang with nothing to explain it. - imds_
client 🔒 - The client IMDS is reached through.
- imds_
request 🔒 - parse_
imds_ 🔒credentials - Reads the credentials out of a
GET /latest/meta-data/iam/security-credentials/<role>response.