saml2aws

package module
v2.17.0+incompatible Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 3, 2019 License: MIT Imports: 27 Imported by: 50

README

saml2aws Build Status Build status - Windows

CLI tool which enables you to login and retrieve AWS temporary credentials using with ADFS or PingFederate Identity Providers.

This is based on python code from How to Implement a General Solution for Federated API/CLI Access Using SAML 2.0.

The process goes something like this:

  • Setup an account alias, either using the default or given a name
  • Prompt user for credentials
  • Log in to Identity Provider using form based authentication
  • Build a SAML assertion containing AWS roles
  • Exchange the role and SAML assertion with AWS STS service to get a temporary set of credentials
  • Save these credentials to an aws profile named "saml"

Table of Contents

Requirements

  • One of the supported Identity Providers
  • AWS SAML Provider configured

Caveats

Aside from Okta, most of the providers in this project are using screen scraping to log users into SAML, this isn't ideal and hopefully vendors make this easier in the future. In addition to this there are some things you need to know:

  1. AWS defaults to session tokens being issued with a duration of up to 3600 seconds (1 hour), this can now be configured as per Enable Federated API Access to your AWS Resources for up to 12 hours Using IAM Roles and --session-duration flag.
  2. Every SAML provider is different, the login process, MFA support is pluggable and therefore some work may be needed to integrate with your identity server

Install

OSX

If you're on OSX you can install saml2aws using homebrew!

brew tap versent/homebrew-taps
brew install saml2aws
Windows

If you're on Windows you can install saml2aws using chocolatey!

choco install saml2aws
saml2aws --version

Dependency Setup

Install the AWS CLI see, in our case we are using homebrew on OSX.

brew install awscli

Usage

usage: saml2aws [<flags>] <command> [<args> ...]

A command line tool to help with SAML access to the AWS token service.

Flags:
      --help                   Show context-sensitive help (also try --help-long
                               and --help-man).
      --version                Show application version.
      --verbose                Enable verbose logging
  -i, --provider=PROVIDER      This flag is obsolete. See:
                               https://github.com/Versent/saml2aws#configuring-idp-accounts
  -a, --idp-account="default"  The name of the configured IDP account. (env:
                               SAML2AWS_IDP_ACCOUNT)
      --idp-provider=IDP-PROVIDER
                               The configured IDP provider. (env:
                               SAML2AWS_IDP_PROVIDER)
      --mfa=MFA                The name of the mfa. (env: SAML2AWS_MFA)
  -s, --skip-verify            Skip verification of server certificate.
      --url=URL                The URL of the SAML IDP server used to login.
                               (env: SAML2AWS_URL)
      --username=USERNAME      The username used to login. (env:
                               SAML2AWS_USERNAME)
      --password=PASSWORD      The password used to login. (env:
                               SAML2AWS_PASSWORD)
      --mfa-token=MFA-TOKEN    The current MFA token (supported in Keycloak,
                               ADFS). (env: SAML2AWS_MFA_TOKEN)
      --role=ROLE              The ARN of the role to assume. (env:
                               SAML2AWS_ROLE)
      --aws-urn=AWS-URN        The URN used by SAML when you login. (env:
                               SAML2AWS_AWS_URN)
      --duo-mfa-option         The MFA option you want to use to authenticate (env: SAML_DUO_MFA_OPTION)
      --skip-prompt            Skip prompting for parameters during login.
      --exec-profile           Execute the given command utilizing a specific profile from your ~/.aws/config file
      --session-duration=SESSION-DURATION
                               The duration of your AWS Session. (env:
                               SAML2AWS_SESSION_DURATION)

Commands:
  help [<command>...]
    Show help.

  configure [<flags>]
    Configure a new IDP account.

  login [<flags>]
    Login to a SAML 2.0 IDP and convert the SAML assertion to an STS token.

  exec [<flags>] [<command>...]
    Exec the supplied command with env vars from STS token.

  list-roles
    List available role ARNs.

  script [<flags>]
    Emit a script that will export environment variables.
saml2aws script

If the script sub-command is called, saml2aws will output the following temporary security credentials:

export AWS_ACCESS_KEY_ID="ASIAI....UOCA"
export AWS_SECRET_ACCESS_KEY="DuH...G1d"
export AWS_SESSION_TOKEN="AQ...1BQ=="
export AWS_SECURITY_TOKEN="AQ...1BQ=="
SAML2AWS_PROFILE=saml

Powershell, and fish shells are supported as well.

If you use eval $(saml2aws script) frequently, you may want to create a alias for it:

zsh:

alias s2a="function(){eval $( $(command saml2aws) script --shell=bash --profile=$@);}"

bash:

function s2a { eval $( $(which saml2aws) script --shell=bash --profile=$@); }
saml2aws exec

If the exec sub-command is called, saml2aws will execute the command given as an argument: By default saml2aws will execute the command with temp credentials generated via saml2aws login.

The --exec-profile flag allows for a command to execute using an aws profile which may have chained "assume role" actions. (via 'source_profile' in ~/.aws/config) *See section "blah" for scenario where this is useful as well as example below.

options:
--exec-profile           Execute the given command utilizing a specific profile from your ~/.aws/config file
Configuring IDP Accounts

This is the new way of adding IDP provider accounts, it enables you to have named accounts with whatever settings you like and supports having one default account which is used if you omit the account flag. This replaces the --provider flag and old configuration file in 1.x.

To add a default IdP account to saml2aws just run the following command and follow the prompts.

$ saml2aws configure
? Please choose a provider: Ping
? AWS Profile myaccount

? URL https://example.com
? Username me@example.com

? Password
No password supplied

account {
  URL: https://example.com
  Username: me@example.com
  Provider: Ping
  MFA: Auto
  SkipVerify: false
  AmazonWebservicesURN: urn:amazon:webservices
  SessionDuration: 3600
  Profile: myaccount
}

Configuration saved for IDP account: default

Then to login using this account.

saml2aws login

You can also add named accounts, below is an example where I am setting up an account under the wolfeidau alias, again just follow the prompts.

saml2aws configure -a wolfeidau

You can also configure the account alias without prompts.

saml2aws configure -a wolfeidau --idp-provider KeyCloak --username mark@wolfe.id.au \
  --url https://keycloak.wolfe.id.au/auth/realms/master/protocol/saml/clients/amazon-aws --skip-prompt

Then your ready to use saml2aws.

Example

Log into a service (without MFA).

$ saml2aws login
Using IDP Account default to access Ping https://id.example.com
To use saved password just hit enter.
Username [mark.wolfe@example.com]:
Password: ************

Authenticating as mark.wolfe@example.com ...
Selected role: arn:aws:iam::123123123123:role/AWS-Admin-CloudOPSNonProd
Requesting AWS credentials using SAML assertion
Saving credentials
Logged in as: arn:aws:sts::123123123123:assumed-role/AWS-Admin-CloudOPSNonProd/wolfeidau@example.com

Your new access key pair has been stored in the AWS configuration
Note that it will expire at 2016-09-19 15:59:49 +1000 AEST
To use this credential, call the AWS CLI with the --profile option (e.g. aws --profile saml ec2 describe-instances).

Log into a service (with MFA).

$ saml2aws login
Using IDP Account default to access Ping https://id.example.com
To use saved password just hit enter.
Username [mark.wolfe@example.com]:
Password: ************

Authenticating as mark.wolfe@example.com ...
Enter passcode: 123456

Selected role: arn:aws:iam::123123123123:role/AWS-Admin-CloudOPSNonProd
Requesting AWS credentials using SAML assertion
Saving credentials
Logged in as: arn:aws:sts::123123123123:assumed-role/AWS-Admin-CloudOPSNonProd/wolfeidau@example.com

Your new access key pair has been stored in the AWS configuration
Note that it will expire at 2016-09-19 15:59:49 +1000 AEST
To use this credential, call the AWS CLI with the --profile option (e.g. aws --profile saml ec2 describe-instances --region us-east-1).

Advanced Configuration

Configuring multiple accounts with custom role and profile in ~/.aws/config with goal being isolation between infra code when deploying to these environments. This setup assumes you're using separate roles and probably AWS accounts for dev and test and is designed to help operations staff avoid accidentally deploying to the wrong AWS account in complex environments. Note that this method configures SAML authentication to each AWS account directly (in this case different AWS accounts). In the example below, separate authentication values are configured for AWS accounts 'profile=customer-dev/awsAccount=was 121234567890' and 'profile=customer-test/awsAccount=121234567891'

Dev Account Setup

To setup the dev account run the following and enter URL, username and password, and assign a standard role to be automatically selected on login.

saml2aws configure -a customer-dev --role=arn:aws:iam::121234567890:role/customer-admin-role -p customer-dev

This will result in the following configuration in ~/.saml2aws.

[customer-dev]
url                     = https://id.customer.cloud
username                = mark@wolfe.id.au
provider                = Ping
mfa                     = Auto
skip_verify             = false
timeout                 = 0
aws_urn                 = urn:amazon:webservices
aws_session_duration    = 28800
aws_profile             = customer-dev
role_arn                = arn:aws:iam::121234567890:role/customer-admin-role

To use this you will need to export AWS_DEFAULT_PROFILE=customer-dev environment variable to target dev.

Test Account Setup

To setup the test account run the following and enter URL, username and password.

saml2aws configure -a customer-test --role=arn:aws:iam::121234567891:role/customer-admin-role -p customer-test

This results in the following configuration in ~/.saml2aws.

[customer-test]
url                     = https://id.customer.cloud
username                = mark@wolfe.id.au
provider                = Ping
mfa                     = Auto
skip_verify             = false
timeout                 = 0
aws_urn                 = urn:amazon:webservices
aws_session_duration    = 28800
aws_profile             = customer-test
role_arn                = arn:aws:iam::121234567891:role/customer-admin-role

To use this you will need to export AWS_DEFAULT_PROFILE=customer-test environment variable to target test.

Advanced Configuration (Multiple AWS account access but SAML authenticate against a single 'SSO' AWS account)

Example: (Authenticate to my 'SSO' AWS account. With this setup, there is no need to authenticate again. We can now rely on IAM to assume role cross account)

~/.aws/credentials: #(these are generated by saml2aws login. Sets up SAML authentication into my AWS 'SSO' account)

[saml]
aws_access_key_id        = AAAAAAAAAAAAAAAAB
aws_secret_access_key    = duqhdZPRjEdZPRjE=dZPRjEhKjfB
aws_session_token        = #REMOVED#
aws_security_token       = #REMOVED#
x_principal_arn          = arn:aws:sts::000000000123:assumed-role/myInitialAccount
x_security_token_expires = 2019-08-19T15:00:56-06:00

(Use AWS profiles to assume an aws role cross-account) (Note that the "source_profile" is set to SAML which is my SSO AWS account since it is already authenticated)

~/.aws/config:

[profile roleIn2ndAwsAccount]
source_profile=saml
role_arn=arn:aws:iam::123456789012:role/OtherRoleInAnyFederatedAccount # Note the different account number here
role_session_name=myAccountName

Running saml2aws without --exec-profile flag:

saml2aws exec aws sts get-caller-identity
{
    "UserId": "AROAYAROAYAROAYOO:myInitialAccount",
    "Account": "000000000123",
    "Arn": "arn:aws:sts::000000000123:assumed-role/myInitialAccount"  # This shows my 'SSO' account (SAML profile)
}

Running saml2aws with --exec-profile flag:

saml2aws exec --exec-profile roleIn2ndAwsAccount aws sts get-caller-identity
{
    "UserId": "YOOYOOYOOYOOYOOA:/myAccountName",
    "Account": "123456789012",
    "Arn": "arn:aws:sts::123456789012:assumed-role/myAccountName"  # When using '--exec-profile' I can assume-role into a                                                                        # different AWS account without re-authenticating. 
                                                                   # Note that it does not re-authenitcate since we are 
                                                                   # alredy authenticated via the SSO account
}

As an example

saml2aws login

aws s3 ls --profile saml

An error occurred (AccessDenied) when calling the ListBuckets operation: Access Denied
# This is denied in this example because there are no S3 buckets in the 'SSO' AWS account

saml2aws exec --exec-profile roleIn2ndAwsAccount aws s3 ls  # Runs given CMD with environment configured from --exec-profile role

# If we check env variables we see that our environment is configured with temporary credentials for our 'assumed role'
env | grep AWS
AWS_SESSION_TTL=12h
AWS_FEDERATION_TOKEN_TTL=12h
AWS_ASSUME_ROLE_TTL=1h
AWS_ACCESS_KEY_ID=AAAAAAAASORTENED
AWS_SECRET_ACCESS_KEY=secretShortened+6jJ5SMqsM5CkYi3Gw7
AWS_SESSION_TOKEN=ShortenedTokenXXX=
AWS_SECURITY_TOKEN=ShortenedSecurityTokenXXX=

# If we desire to execute multiple commands utilizing our assumed profile, we can obtain a new shell with Env variables configured for access

saml2aws exec --exec-profile roleIn2ndAwsAccount $SHELL  # Get a new shell with AWS env vars configured for 'assumed role' account access

# We are now able to execute AWS cli commands with our assume role permissions

# Note that we do not need a --profile flag because our environment variables were set up for this access when we obtained a new shell with the --exec-profile flag

aws s3 ls  
2019-07-30 01:32:59 264998d7606497040-sampleBucket

aws iam list-groups
{
    "Groups": [
        {
            "Path": "/",
            "GroupName": "MyGroup",
            "GroupId": "AGAGTENTENTENGOCQFK",
            "Arn": "arn:aws:iam::123456789012:group/MyGroup",
            "CreateDate": "2019-05-13T16:12:19Z"
            ]
        }
}

## Building

To build this software on osx clone to the repo to `$GOPATH/src/github.com/versent/saml2aws` and ensure you have `$GOPATH/bin` in your `$PATH`.

make deps


Install the binary to `$GOPATH/bin`.

make install


Then to test the software just run.

make test


## Environment vars

The exec sub command will export the following environment variables.

* AWS_ACCESS_KEY_ID
* AWS_SECRET_ACCESS_KEY
* AWS_SESSION_TOKEN
* AWS_SECURITY_TOKEN
* EC2_SECURITY_TOKEN
* AWS_PROFILE
* AWS_DEFAULT_PROFILE

Note: That profile environment variables enable you to use `exec` with a script or command which requires an explicit profile.

## Provider Specific Documentation

* [Azure Active Directory](./doc/provider/aad)
* [JumpCloud](./doc/provider/jumpcloud)

# Dependencies

This tool would not be possible without some great opensource libraries.

* [goquery](https://github.com/PuerkitoBio/goquery) html querying
* [etree](https://github.com/beevik/etree) xpath selector
* [kingpin](https://github.com/alecthomas/kingpin) command line flags
* [aws-sdk-go](https://github.com/aws/aws-sdk-go) AWS Go SDK
* [go-ini](https://github.com/go-ini/ini) INI file parser
* [go-ntlmssp](https://github.com/Azure/go-ntlmssp) NTLM/Negotiate authentication

# Releasing

Install `github-release`.

go get github.com/buildkite/github-release


To release run.

make release


# Debugging Issues with IDPs

There are two levels of debugging, first emits debug information and the URL / Method / Status line of requests.

saml2aws login --verbose


The second emits the content of requests and responses, this includes authentication related information so don't copy and paste it into chat or tickets!

DUMP_CONTENT=true saml2aws login --verbose


# License

This code is Copyright (c) 2018 [Versent](http://versent.com.au) and released under the MIT license. All rights not explicitly granted in the MIT license are reserved. See the included LICENSE.md file for more details.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrMissingAssertion = ErrMissingElement{Tag: assertionTag}
)

ErrMissingAssertion indicates that an appropriate assertion element could not be found in the SAML Response

View Source
var MFAsByProvider = ProviderList{
	"AzureAD":    []string{"Auto", "PhoneAppOTP", "PhoneAppNotification", "OneWaySMS"},
	"ADFS":       []string{"Auto", "VIP"},
	"ADFS2":      []string{"Auto", "RSA"},
	"Ping":       []string{"Auto"},
	"PingOne":    []string{"Auto"},
	"JumpCloud":  []string{"Auto"},
	"Okta":       []string{"Auto", "PUSH", "DUO", "SMS", "TOTP", "OKTA"},
	"OneLogin":   []string{"Auto", "OLP", "SMS", "TOTP"},
	"KeyCloak":   []string{"Auto"},
	"GoogleApps": []string{"Auto"},
	"Shibboleth": []string{"Auto"},
	"PSU":        []string{"Auto"},
	"F5APM":      []string{"Auto"},
}

MFAsByProvider a list of providers with their respective supported MFAs

Functions

func AssignPrincipals added in v1.8.0

func AssignPrincipals(awsRoles []*AWSRole, awsAccounts []*AWSAccount)

AssignPrincipals assign principal from roles

func ExtractAwsRoles

func ExtractAwsRoles(data []byte) ([]string, error)

ExtractAwsRoles given an assertion document extract the aws roles

func ExtractSessionDuration

func ExtractSessionDuration(data []byte) (int64, error)

ExtractSessionDuration this will attempt to extract a session duration from the assertion see https://aws.amazon.com/SAML/Attributes/SessionDuration

func PromptForConfigurationDetails

func PromptForConfigurationDetails(idpAccount *cfg.IDPAccount) error

PromptForConfigurationDetails prompt the user to present their hostname, username and mfa

func PromptForLoginDetails added in v1.1.0

func PromptForLoginDetails(loginDetails *creds.LoginDetails, provider string) error

PromptForLoginDetails prompt the user to present their username, password

Types

type AWSAccount added in v1.5.0

type AWSAccount struct {
	Name  string
	Roles []*AWSRole
}

AWSAccount holds the AWS account name and roles

func ExtractAWSAccounts added in v1.5.0

func ExtractAWSAccounts(data []byte) ([]*AWSAccount, error)

ExtractAWSAccounts extract the accounts from the AWS html page

func ParseAWSAccounts added in v1.5.0

func ParseAWSAccounts(samlAssertion string) ([]*AWSAccount, error)

ParseAWSAccounts extract the aws accounts from the saml assertion

type AWSRole

type AWSRole struct {
	RoleARN      string
	PrincipalARN string
	Name         string
}

AWSRole aws role attributes

func LocateRole added in v1.8.0

func LocateRole(awsRoles []*AWSRole, roleName string) (*AWSRole, error)

LocateRole locate role by name

func ParseAWSRoles added in v1.3.0

func ParseAWSRoles(roles []string) ([]*AWSRole, error)

ParseAWSRoles parses and splits the roles while also validating the contents

func PromptForAWSRoleSelection

func PromptForAWSRoleSelection(accounts []*AWSAccount) (*AWSRole, error)

PromptForAWSRoleSelection present a list of roles to the user for selection

type ErrMissingElement

type ErrMissingElement struct {
	Tag, Attribute string
}

ErrMissingElement is the error type that indicates an element and/or attribute is missing. It provides a structured error that can be more appropriately acted upon.

func (ErrMissingElement) Error

func (e ErrMissingElement) Error() string

type ProviderList

type ProviderList map[string][]string

ProviderList list of providers with their MFAs

func (ProviderList) Mfas

func (mfbp ProviderList) Mfas(provider string) []string

Mfas retrieve a sorted list of mfas from the provider list

func (ProviderList) Names

func (mfbp ProviderList) Names() []string

Names get a list of provider names

type SAMLClient added in v1.3.0

type SAMLClient interface {
	Authenticate(loginDetails *creds.LoginDetails) (string, error)
}

SAMLClient client interface

func NewSAMLClient added in v1.3.0

func NewSAMLClient(idpAccount *cfg.IDPAccount) (SAMLClient, error)

NewSAMLClient create a new SAML client

Directories

Path Synopsis
cmd
helper
osxkeychain
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
wincred
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
Code generated by mockery v1.0.0
Code generated by mockery v1.0.0
pkg
cfg
cookiejar
Package cookiejar implements an in-memory RFC 6265-compliant http.CookieJar.
Package cookiejar implements an in-memory RFC 6265-compliant http.CookieJar.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL