Skip to main content

Module route53

Module route53 

Source
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-date and (when present) x-amz-security-token.
xml 🔒
The handful of request bodies and response shapes this provider needs.

Structs§

HttpTransport 🔒
Imds 🔒
The real one: three IMDSv2 round trips against 169.254.169.254.
ImdsCredentials 🔒
What GET /latest/meta-data/iam/security-credentials/<role> answers with. Code, LastUpdated and Type are in the response and are not read.
InstanceCredentials
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 reqwest type so tests can build one without a client.
RetryPolicy
How this provider retries a throttled or transiently failed call.
Route53Credentials
Long-lived AWS credentials, scoped to one hosted zone.
Route53Dns
The production DnsProvider: writes and removes the TXT and CAA records in one Route53 hosted zone. See the module docs for the operating rules — propagation, retries, quotas and the credential’s scope.
TimedCredentials 🔒
A credential read from IMDS, with the moment IMDS says it stops being accepted.

Enums§

ImdsError
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§

CredentialFetch 🔒
The IMDS read InstanceCredentials repeats, isolated the same way Transport is 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. HttpTransport is the only real implementation. The tests in sigv4 check 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 at warn rather 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.