fix(aws): send x-amz-security-token so temporary STS credentials work - #120
fix(aws): send x-amz-security-token so temporary STS credentials work#120whatticklesyou wants to merge 3 commits into
Conversation
|
Nice catch. Clean fix too! I checked the signature against botocore and it matches byte for byte, with and without a session token, including tokens containing Two things and it can go in:
The docs and the message strings still advertise the two part token format. That is Both notes in your description hold up. The early return on profile really does mean the env fallback never applies once a profile is configured. And an inline session token expires within the hour, so that path is mostly for a one-off CLI sync. The expired versus invalid error message is worth doing, happy to take that separately. Thank you! |
|
Thanks for the review! Both fixed, pushed. Formatting: the string in Tests: Leaving the docs and message strings to you. Happy to take the expired-vs-invalid error separately. Also checked |
|
Separate from this PR, but found while testing it.
This matters more for temporary credentials than long-lived ones. Possible fix: skip the token requirement for AWS when |
A token written as ACCESS_KEY:SECRET: kept the trailing separator on the secret key, so every request signed with a key the account never issued. An empty third component now means no session token. Cover the security token on the wire. One test asserts the x-amz-security-token header reaches the endpoint for STS credentials, another asserts it stays absent for long-lived keys. Both fail when the header call in ec2_get is removed, which nothing caught before. The profile-over-environment test now writes a real credentials file into a tempdir and asserts every resolved field comes from it, so it fails if that precedence ever flips.
|
Pushed a commit to your branch instead of bouncing this back for a third round. Say the word if you would rather have had it as notes. A mutation pass turned up three things:
Precommit is 16 of 16 green on the result. On your CLI find: reproduced, though The env fallback is the real hole. It goes deeper than the CLI: Which means the wiki has been advertising an auth option you can only reach by hand-writing |
Problem
AWS EC2 sync fails with
AWS EC2: Authentication failed. Check your API token.for anyone whose credentials come from STS — IAM Identity Center (SSO),assume-role,get-session-tokenor any corporate federation wrapper. The request is signed and sent; EC2 rejects it.The cause is in
src/providers/aws.rs:AwsCredentialshas onlyaccess_keyandsecret_key. There is no field for a session token.parse_credentialsmatchesaws_access_key_idandaws_secret_access_keyand drops everything else via_ => {}, soaws_session_tokenin~/.aws/credentialsis silently discarded.test_parse_credentials_extra_keys_ignoredcurrently asserts this behaviour.sign_requesthardcodessigned_headers = "host;x-amz-date"andec2_getsends onlyAuthorizationandx-amz-date.SigV4 with temporary credentials requires the session token to be sent as
x-amz-security-tokenand included in the canonical request andSignedHeaders. Without it AWS cannot validate the signature, so everyASIA...credential fails 100% of the time regardless of expiry, region or IAM policy. Only long-livedAKIAkeys work today — which is exactly the credential type most organisations prohibit.Reproduce: configure the AWS provider with a profile whose credentials came from
aws sts assume-roleor SSO, thenpurple --verbose sync.Fix
session_token: Option<String>toAwsCredentials.aws_session_tokeninparse_credentials.AWS_SESSION_TOKENin the env-var fallback, via a newEnv::aws_session_token()accessor alongside the existingEnv::aws_credentials().ACCESS_KEY_ID:SECRET_ACCESS_KEY[:SESSION_TOKEN]. Two-part tokens keep working unchanged; secret access keys are base64 and never contain:, so the split is unambiguous.sign_request, when a session token is present, addx-amz-security-tokento the canonical headers and toSignedHeaders. Header order is preserved: canonical headers must be sorted by lowercase name, andx-amz-security-tokensorts afterx-amz-date.ec2_get, send thex-amz-security-tokenheader when present.The
Nonebranch is byte-identical to the previous behaviour, so signatures for long-livedAKIAcredentials are unchanged.Tests
test_parse_credentials_extra_keys_ignoredreworded — it no longer usesaws_session_tokenas its example of an ignored key, and now assertssession_token == Nonefor a profile without one.test_parse_credentials_session_token— token is parsed from the profile.test_resolve_credentials_token_with_session_token— three-part inline token.test_sign_request_includes_security_token_when_present—SignedHeaders=host;x-amz-date;x-amz-security-token.test_sign_request_session_token_changes_signature— the token actually enters the signature rather than just being appended as a header.Existing
sign_requesttests updated withsession_token: Noneand still assertSignedHeaders=host;x-amz-date.Notes
--token AKID:SECRETthere could be updated to--token AKID:SECRET[:SESSION_TOKEN].resolve_credentialsreturns early onif !profile.is_empty(), so a configured-but-unreadable profile fails without ever reaching the token or env branches. That is defensible, but it means the documented env-var fallback never applies to anyone with a profile configured, which is surprising in practice.