feat(aws): add support for AWS IAM Identity Center (SSO)#598
Open
rjwhitworth wants to merge 2 commits into1Password:mainfrom
Open
feat(aws): add support for AWS IAM Identity Center (SSO)#598rjwhitworth wants to merge 2 commits into1Password:mainfrom
rjwhitworth wants to merge 2 commits into1Password:mainfrom
Conversation
Adds an SSO Profile credential type. The plugin reads the access token cached by `aws sso login` from ~/.aws/sso/cache/<sha1>.json and exchanges it for short-lived role credentials via sso:GetRoleCredentials. Supports both the legacy `sso_start_url` form and the consolidated `sso_session` form. Role credentials get cached in the plugin's encrypted store, so subsequent invocations within the credential TTL skip the remote call. Access Key and SSO Profile coexist on the same `aws` executable. Each provisioner yields silently when the active profile is configured for the other type, so users can link both items against the same plugin and the right one runs per invocation. The importer scans ~/.aws/config for both forms and surfaces a candidate per SSO-bearing profile. cdk, eksctl, awslogs, and sam also accept the SSO Profile credential. `aws sso login`, `aws sso logout`, `aws configure sso`, `aws configure sso-session`, and the read-only `aws configure list/list-profiles/get/ set` subcommands skip credential provisioning so they can run without an active session. SDK changes: - Allow plugins to expose more than one credential type. The op runtime already accepts this; the validator was the only block. - Downgrade "has at least 1 field that is secret" from error to warning so credential types whose secret lives in an external token cache (SSO, OAuth, gcloud) can be represented without a placeholder field. aws-sdk-go-v2/credentials and aws-sdk-go-v2/service/sso were already on the dependency graph via aws-vault/v7; both move from indirect to direct. Resolves: 1Password#210
Address findings from a pre-PR security review. Each fix is local to the AWS SSO net-new code introduced in the previous commit; pre-existing SDK bugs surfaced by the same review are deferred to a follow-up PR. Importer (plugins/aws/sso_importer.go): - Validate sso_start_url (https + non-empty host) - Regex-check sso_account_id (12 digits) and sso_region - Reject NUL bytes mid-value - Refuse non-regular, symlinked, or non-owned AWS_CONFIG_FILE overrides - Match SDK ~/ prefix convention (bare ~root no longer silently joined) - Use ini.LoadSources directly with strict botocore-parity options: KeyValueDelimiters="=", IgnoreContinuation=true, Loose=true Provisioner (plugins/aws/sso_provisioner.go): - assertSSOTokenCacheSafe rejects symlinks, non-regular files, group/world readable modes, and files not owned by the current uid before passing the path to ssocreds.New - translateSSORetrieveError whitelists known smithy codes (Unauthorized, Forbidden, ResourceNotFound, TooManyRequests); unknown codes get a generic plugin-controlled message so server-controlled error text doesn't reach user-visible output - Wrap sso:GetRoleCredentials in context.WithTimeout(30s) SDK validator (sdk/schema/credential_type.go): - Replace the previous severity downgrade with an opt-in AllowsExternalSecretCache flag on CredentialType. The "must have at least one secret field" check is restored to Error severity globally; only the SSO Profile (whose bearer token lives in the AWS SDK's external cache) opts out Tests (plugins/aws/sso_*_test.go): - Hostile-input cases for the importer (non-HTTPS, file:// scheme, short account ID, malformed region, NUL byte, malformed section header, duplicate-section last-wins) - Direct unit tests for assertSSOTokenCacheSafe and validateExternalConfigPath covering symlink, world-readable, directory, non-existent - Smithy-error translation table with a token-leak guard that asserts no JSON-key-shaped or JWT-shaped fragment from a hostile server message ever appears in the translated user-visible error Deferred to follow-up PR (pre-existing code, out of scope here): - F-2: plugins/registry.go:50-60 GetCredentialType ignores credentialName - F-6: sdk/schema/plugin.go:116-134 MarshalJSON truncates to Credentials[0] - F-7: plugins/aws/cli_provisioner.go:28-53 strip-by-value mis-routing - F-9: sdk/importer/helpers.go:41-53 SanitizeNameHint byte-truncation - F-15: plugins/aws/sts_provisioner.go:399-405 log.SetOutput global state
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overview
Adds AWS IAM Identity Center (SSO) authentication to the AWS plugin so users with SSO-only AWS access can authenticate the AWS CLI via 1Password. The plugin reads the access token cached by
aws sso loginfrom~/.aws/sso/cache/<sha1>.jsonand exchanges it for short-lived role credentials viasso:GetRoleCredentials. Both the legacysso_start_urlform and the consolidatedsso_sessionform are supported. Role credentials get cached in the plugin's encrypted store, so subsequent invocations within the credential TTL skip the remote call.Access Key and SSO Profile coexist on the same plugin: each provisioner yields silently when the active profile is configured for the other type, so users can link both items against the same plugin without conflict. The importer scans
~/.aws/configfor both forms and surfaces a candidate per SSO-bearing profile. Thecdk,eksctl,awslogs, andsamexecutables also accept the SSO Profile credential.aws sso login,aws sso logout,aws configure sso,aws configure sso-session, and the read-onlyaws configure list/list-profiles/get/setsubcommands skip credential provisioning so they can run without an active session.Two SDK changes:
opruntime already accepts this; the validator was the only block.AllowsExternalSecretCacheflag onCredentialTypelets a credential opt out of the "must have at least one secret field" check. Only the SSO Profile uses it (its bearer token lives in the AWS SDK's external cache, not in the vault item). Every other credential continues to fail validation if no secret field is declared.aws-sdk-go-v2/credentials,aws-sdk-go-v2/service/sso, andsmithy-gowere already on the dependency graph viaaws-vault/v7; all three move from indirect to direct.Security hardening
The second commit on this branch addresses findings from a pre-PR adversarial security review:
plugins/aws/sso_importer.go): validatessso_start_url(https, non-empty host),sso_account_id(12-digit regex), andsso_region. Rejects NUL bytes mid-value. Refuses non-regular, symlinked, or non-ownedAWS_CONFIG_FILEoverrides. Uses strict botocore-parity INI options:KeyValueDelimiters="=",IgnoreContinuation=true,Loose=true.plugins/aws/sso_provisioner.go): asserts~/.aws/sso/cache/<sha1>.jsonis a regular non-symlink file owned by the current uid with no group/world read bits before passing the path tossocreds.New. AWS SSO API errors flow through a smithy code whitelist; unknown codes get a generic plugin-controlled message so server-controlled error text doesn't reach user-visible output. Thesso:GetRoleCredentialscall runs under a 30-secondcontext.WithTimeout.AllowsExternalSecretCacheflag (above) replaces an earlier severity downgrade so the secret-field check stays an Error for the rest of the catalogue.Type of change
Related Issue(s)
How To Test
Configure an SSO profile in
~/.aws/config(either form). Legacy:Or consolidated:
aws sso login --profile my-sso(populates~/.aws/sso/cache/<sha1>.json).op plugin init aws, pick SSO Profile, accept the auto-detected candidate.op plugin run -- aws sts get-caller-identity --profile my-sso— prints the assumed-role ARN.Re-run within the credential TTL — the second invocation hits the plugin's encrypted cache (no SSO endpoint round-trip).
(Optional) Wipe
~/.aws/sso/cache/<sha1>.jsonand re-run — the plugin should surface:AWS SSO token is missing or expired; run \aws sso login --profile my-sso` and try again`.(Optional, cache-file safety)
chmod 644 ~/.aws/sso/cache/<sha1>.jsonand re-run on a cache-miss — the plugin should refuse withAWS SSO token cache "..." is group/world readable (mode 644); chmod 600 it and re-run. Restorechmod 600afterwards.Changelog
Authenticate the AWS CLI with AWS IAM Identity Center (SSO) profiles using cached SSO tokens.