pub struct InstanceCredentials {
fetch: Box<dyn CredentialFetch>,
held: Mutex<TimedCredentials>,
}Expand description
The credential every Route53 request in this process is signed with, kept current against the instance profile that issues it.
An instance profile’s credential is temporary: IMDS states an
Expiration, and a signature made with a credential past it is rejected
by Route53 — which on this deployment stops agent hostnames being
published and stops ACME DNS-01 renewing the certificate, since
terraform-aws-didbot-pds/security_groups.tf opens no port 80 for an HTTP-01 order.
This holder reads IMDS again once the credential it holds is within
REFRESH_MARGIN of that expiry, and hands out the new one.
A refresh that fails is reported at error and tried again on the next
request rather than ending the process: while the credential in hand is
still valid it keeps signing with it, and once it is not,
Self::current returns the ImdsError, which reaches a caller as a
DnsError::Backend through the same retry path as any other transport
failure.
The refresh happens under the lock on the held credential, so callers that arrive during one wait for it and then sign with what it produced: a hosted zone being written by several threads makes one IMDS read between them, not one each.
Fields§
§fetch: Box<dyn CredentialFetch>§held: Mutex<TimedCredentials>Implementations§
Source§impl InstanceCredentials
impl InstanceCredentials
Sourcepub fn from_instance_metadata() -> Result<Self, ImdsError>
pub fn from_instance_metadata() -> Result<Self, ImdsError>
Reads the credential of whatever IAM role is attached to this EC2 instance, over IMDSv2, and keeps reading it as it expires.
IMDSv2 specifically, not v1: v1’s tokenless GET is what let the
2019 Capital One breach turn an unrelated request-smuggling bug into
stolen instance-role credentials, and infra/pds/’s launch template
requires the token hop (HttpTokens = required) for exactly that
reason — see docs/deployment.md. This is the one credential path
this crate offers a production deployment: there is no environment
variable or file this reads instead, because inventing a second path
is a second thing to misconfigure and a second place a credential can
leak from.
Three round trips, each against a 5-second timeout: the session token, the attached role’s name, and that role’s current credentials. Blocking, like the rest of this provider — see the module docs for why. The first read happens here, so a deployment whose instance profile is wrong fails at startup rather than at the first publish.
pub(crate) fn with_fetch( fetch: Box<dyn CredentialFetch>, ) -> Result<Self, ImdsError>
Sourcepub fn current(&self) -> Result<Route53Credentials, ImdsError>
pub fn current(&self) -> Result<Route53Credentials, ImdsError>
The credential to sign the next request with, refreshed first if the
one held is inside REFRESH_MARGIN of its expiry.
Source§impl InstanceCredentials
The one lever crate::aws_fake has on a credential holder that is
otherwise driven entirely by the wall clock.
impl InstanceCredentials
The one lever crate::aws_fake has on a credential holder that is
otherwise driven entirely by the wall clock.
Nothing outside this crate’s own tests can reach it: it is pub(crate)
and behind the aws-fake feature, which no production build turns on.
Sourcepub(crate) fn expire_held(&self)
pub(crate) fn expire_held(&self)
Makes the credential in hand expired as of now, so the next
Self::current takes the refresh path rather than answering from
what it holds.