├── LICENSE ├── README.md └── stanford-sso.sh /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Stanford University 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | This document shows an example of how to set up SSO for AWS console access with an 3 | IAM role, using Stanford production IdP service as identity provider. 4 | 5 | By using AWS SAML integration, you don't need to create AWS accounts for users who need to access AWS console. Instead, You create a new or use an existing Stanford workgroup that contains users who will use the SSO to gain access to your account's AWS console. 6 | 7 | ## Using stanford-sso.sh command line tool 8 | 9 | The following instructions apply to MacOS. For other platforms, follow the tool links and instructions on tool sites. 10 | 11 | - Install [Jq](http://stedolan.github.io/jq/) 12 | 13 | ``` 14 | $ brew install jq awscli 15 | ``` 16 | 17 | - Install and configure [AWS CLI](https://github.com/aws/aws-cli) 18 | 19 | If you have AWSCLI installed and configured, you can skip this step. 20 | 21 | ``` 22 | $ brew install awscli 23 | ``` 24 | ``` 25 | $ aws configure --profile 26 | ``` 27 | You will be prompted for AWS KEY and AWS SECRET for the aws user. The profile name will be used for AWS authentication/authorizatio to make AWS CLI calls. 28 | 29 | - Clone the repo 30 | 31 | ``` 32 | $ git clone https://github.com/Stanford/AWS-SSO.git 33 | $ cd AWS-SSO 34 | ``` 35 | 36 | - Run help 37 | 38 | ```console 39 | $ ./stanford-sso.sh -h 40 | stanford-sso -a -c -n -p -w [-u ] [-d] [-h] [-l ] [-r ] 41 | 42 | -a : action. create, show or delete SSO setup by this tool. 43 | -c : authenticate using profile defined by configuration. 44 | -n : the name of the idp provider, for example 'stanford-idp'. 45 | -p : ReadOnlyAccess, AdministratorAccess, or list other valid AWS managed polices. 46 | -u : optional. metadata url for the idp provider. Default 'https://login.stanford.edu/metadata.xml'. 47 | -w : Stanford workgroup name to link into this saml provider setup. e.g. itlab:anchorage-admin 48 | -l : Account label (alias) This will be the name displayed to users when logging in e.g. its-main-account 49 | -r : This defines the name of the role that will be created e.g. ops-readonly 50 | -a : action. create, show or delete SSO setup by this tool. 51 | -d : dryrun. print out the commands 52 | -h : Help 53 | 54 | ``` 55 | 56 | - Create SAML provider 57 | 58 | Dry-run: 59 | 60 | ```console 61 | $ ./stanford-sso.sh -d -a create -c idg-dev -u https://login-uat.stanford.edu/metadata.xml -l aws-idg-dev -n stanford-idp-uat -p AdministratorAccess -w itservices:idg-aws -r stanford-idp-uat 62 | Getting AWS account number ... 63 | create stanford-idp-uat 64 | Creating saml provider stanford-idp-uat. 65 | aws --profile idg-dev iam create-saml-provider --name=stanford-idp-uat --output=text --saml-metadata-document file:///tmp/samlMetadata.xml 66 | Creating account alias aws-idg-dev 67 | aws --profile idg-dev iam create-account-alias --account-alias aws-idg-dev 68 | Creating role stanford-idp-uat 69 | aws --profile idg-dev iam create-role --role-name stanford-idp-uat --assume-role-policy-document file:///tmp/trust-policy.json 70 | aws --profile idg-dev iam attach-role-policy --role-name stanford-idp-uat --policy-arn arn:aws:iam::aws:policy/AdministratorAccess 71 | Dryrun mode. Nothing is changed. 72 | ``` 73 | 74 | The above command will do a dry-run to show what will be created. __stanford-idp-uat__ is a descriptive name to identify the idp provider you use. You can pass in the medtadata url for the idp provider on the command line (see help). The default metadata is 'https://login.stanford.edu/metadata.xml' 75 | 76 | 77 | Real run: 78 | 79 | ```console 80 | $ ./stanford-sso.sh -a create -c idg-dev -u https://login-uat.stanford.edu/metadata.xml -l aws-idg-dev -n stanford-idp-uat -p AdministratorAccess -w itservices:idg-aws -r stanford-idp-uat 81 | Getting AWS account number ... 82 | create stanford-idp-uat 83 | Creating saml provider stanford-idp-uat. 84 | arn:aws:iam::123456789012:saml-provider/stanford-idp-uat 85 | Creating account alias aws-idg-dev 86 | Creating role stanford-idp-uat 87 | { 88 | "Role": { 89 | "AssumeRolePolicyDocument": { 90 | "Version": "2012-10-17", 91 | "Statement": [ 92 | { 93 | "Action": "sts:AssumeRoleWithSAML", 94 | "Principal": { 95 | "Federated": "arn:aws:iam::123456789012:saml-provider/stanford-idp-uat" 96 | }, 97 | "Effect": "Allow", 98 | "Condition": { 99 | "StringEquals": { 100 | "SAML:aud": "https://signin.aws.amazon.com/saml" 101 | } 102 | }, 103 | "Sid": "" 104 | } 105 | ] 106 | }, 107 | "RoleId": "*********************", 108 | "CreateDate": "2016-09-13T17:24:23.675Z", 109 | "RoleName": "stanford-idp-uat", 110 | "Path": "/", 111 | "Arn": "arn:aws:iam::123456789012:role/stanford-idp-uat" 112 | } 113 | } 114 | aws --profile idg-dev iam attach-role-policy --role-name stanford-idp-uat --policy-arn arn:aws:iam::aws:policy/AdministratorAccess 115 | 116 | All done! Next step. Submit the following request to https://helpsu.stanford.edu/helpsu/3.0/auth/helpsu-form?pcat=shibboleth to create idp server setup. 117 | 118 | When idp server setup is complete, you can login to AWS console SSO through this url: 119 | https://idp.stanford.edu/idp/profile/SAML2/Unsolicited/SSO?providerId=urn:amazon:webservices 120 | ``` 121 | 122 | - Delete SAML provider 123 | 124 | ``` 125 | $ ./stanford-sso.sh -a delete -c -n stanford-idp -p AdministratorAccess -w myworkgroup 126 | ``` 127 | 128 | ## Manual steps 129 | 130 | ### identity provider setup 131 | 132 | Login to your AWS console. 133 | 134 | 1. Select IAM service 135 | 1. Click 'Identity Providers' 136 | 1. Click 'Create SAML Provider' 137 | Choose a name that is easy to identify which provider provides SSO, e.g. stanford-idp. 138 | 1. Upload idP-only SAML metadata document from: 139 | 140 | [idp-only metadata](https://login.stanford.edu/metadata.xml) 141 | 142 | Click "Create" button to finish the provider setup. 143 | 144 | ### Create IAM role using this provider in the trust policy 145 | 146 | When you finish identy provider creation, there is a link to take you 147 | to create an IAM role, or you you can go back to IAM service, select 148 | "Roles->Create New Role". 149 | 150 | 1. Create role name: e.g. admin-sso 151 | 1. Select __Role for Identity Provider Access__ in the role type selection screen near 152 | the bottom. 153 | 1. Select __Grant Web Single Sign-On (WebSSO) access to SAML providers__ 154 | 1. Accept the default "Verify Role Trust" policy. 155 | 1. Set permissions: assign the role a permission, e.g. Administrator, Power admin user, etc. It depends on your use case. 156 | 1. Click "Create Role" to finish 157 | 158 | ## Configure relying party trust between IdP and AWS 159 | 160 | Amazon currently only works with IdP-initiated SSO - our Unsolicited SSO Endpoints 161 | are not listed in our IdP metadata, but the handlers and decoders appear to be 162 | enabled. AWS only uses the metadata for the certificate / entity ID. 163 | 164 | You need to submit the request to [HelpSU](https://stanford.service-now.com/it_services?id=sc_cat_item&sys_id=21cfc2684fdf6e0054c23f828110c77e) to complete the setup with the following information. 165 | 166 | * Attribute Resolver Settings 167 | 168 | Replace the account number, role-name, and workgroup with the values you created in the SSO setup steps. You can find the information from AWS console under IAM->Roles, IAM->Identity Provider sections. 169 | 170 | Account number: 123456789012 171 | Provider name (ARN): arn:aws:iam::123456789012:saml-provider/stanford-idp 172 | Role-name (ARN): arn:aws:iam::728004059242:role/admin-sso 173 | Workgroup: x-stem:y-group 174 | 175 | ## Create a virtual host to access the your accoount's AWS console 176 | 177 | After idp service is updated with your AWS SSO data, you can access AWS console by going to: 178 | 179 | https://idp.stanford.edu/idp/profile/SAML2/Unsolicited/SSO?providerId=urn:amazon:webservices 180 | 181 | Note that if you have multiple accounts with the same idp provider, you will see the list of SSO accounts on AWS console. Pick the account number for which you want to login. 182 | 183 | You can also create a virtual hostname, e.g. "my-account-aws-console.stanford.edu" 184 | and redirect it to: 185 | 186 | https://idp.stanford.edu/idp/profile/SAML2/Unsolicited/SSO?providerId=urn:amazon:webservices 187 | 188 | 189 | -------------------------------------------------------------------------------- /stanford-sso.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2015 3 | # The Board of Trustees of the Leland Stanford Junior University 4 | 5 | set -e 6 | shopt -s extglob # enables pattern lists like +(...|...) 7 | # Default variables 8 | init() { 9 | # Script name 10 | script=$(basename "${BASH_SOURCE[0]}" .sh) 11 | 12 | # Script actions 13 | actionsRegexp='+(create|delete|show)' 14 | 15 | # Dryrun flag. Default not run-run. 16 | dryrun=0 17 | 18 | # Metadata URL 19 | metadataUrl='https://idp.stanford.edu/metadata.xml' 20 | } 21 | 22 | # Create saml provider 23 | create_saml_provider() { 24 | samlMetadata=$(curl -s -k $metadataUrl | tee /tmp/samlMetadata.xml) 25 | if ! [[ "$samlMetadata" =~ "entityID=\"https://" ]]; 26 | then 27 | echo "$samlMetadata" 28 | echo "Invalid metadata" 29 | exit 1 30 | else 31 | idpId=${metadataUrl%/metadata.xml} 32 | fi 33 | 34 | echo "Creating saml provider $name." 35 | cmd="aws --profile $profile iam create-saml-provider --name=$name --output=text --saml-metadata-document file:///tmp/samlMetadata.xml" 36 | [ $dryrun -eq 0 ] && $cmd || echo $cmd 37 | 38 | # If no account alias and not passed as a parameter 39 | if [ -z "$accountAlias" ]; 40 | then 41 | accountAlias=$(aws --profile $profile iam list-account-aliases | jq --raw-output '.AccountAliases[]') 42 | userProvidedLabel=0 43 | else 44 | userProvidedLabel=1 45 | fi 46 | if [ -z "$accountAlias" ] || [ $userProvidedLabel -eq 1 ]; 47 | then 48 | if [ $userProvidedLabel -eq 1 ]; 49 | then 50 | echo "Creating account alias ${accountAlias}" 51 | cmd="aws --profile $profile iam create-account-alias --account-alias ${accountAlias}" 52 | [ $dryrun -eq 0 ] && $cmd || echo $cmd 53 | else 54 | echo "Creating account alias ${script}-${profile}" 55 | cmd="aws --profile $profile iam create-account-alias --account-alias ${script}-${profile}" 56 | [ $dryrun -eq 0 ] && $cmd || echo $cmd 57 | fi 58 | fi 59 | } 60 | 61 | # Show SAML provider 62 | show_saml_provider() { 63 | aws --profile $profile iam get-saml-provider --saml-provider-arn=$samlProviderArn 64 | } 65 | 66 | # Delete SAML provider 67 | delete_saml_provider() { 68 | echo "Deleting saml provider $name." 69 | cmd="aws --profile $profile iam delete-saml-provider --saml-provider-arn=$samlProviderArn" 70 | [ $dryrun -eq 0 ] && $cmd || echo $cmd 71 | 72 | # Also remove alias we created 73 | accountAlias=$(aws --profile $profile iam list-account-aliases | jq --raw-output '.AccountAliases[]') 74 | if [ "$accountAlias" = "${script}-${profile}" ]; 75 | then 76 | cmd="aws --profile $profile iam delete-account-alias --account-alias ${script}-${profile}" 77 | [ $dryrun -eq 0 ] && $cmd || echo $cmd 78 | fi 79 | } 80 | 81 | # Create role 82 | create_role() { 83 | cat > /tmp/trust-policy.json < -c -n -p -w [-u ] [-d] [-h] [-l ] [-r ]" 159 | echo "" 160 | echo " -a : action. create, show or delete SSO setup by this tool." 161 | echo " -c : authenticate using profile defined by configuration." 162 | echo " -n : the name of the idp provider, for example 'stanford-idp'." 163 | echo " -p : ReadOnlyAccess, AdministratorAccess, or list other valid AWS managed polices." 164 | echo " -u : optional. metadata url for the idp provider. Default 'https://idp.stanford.edu/metadata.xml'." 165 | echo " -w : Stanford workgroup name to link into this saml provider setup. e.g. itlab:anchorage-admin" 166 | echo " -l : Account label (alias) This will be the name displayed to users when logging in e.g. its-main-account" 167 | echo " -r : This defines the name of the role that will be created e.g. ops-readonly" 168 | echo " -a : action. create, show or delete SSO setup by this tool." 169 | echo " -d : dryrun. print out the commands" 170 | echo " -h : Help" 171 | } 172 | 173 | # Main 174 | 175 | # Set default values 176 | init 177 | while getopts "a:c:p:n:u:w:hdl:r:" OPTION 178 | do 179 | case $OPTION in 180 | a) 181 | action=$OPTARG 182 | case $action in 183 | $actionsRegexp) 184 | ;; 185 | *) 186 | echo "Unsupported action $action." 187 | exit 1 188 | esac 189 | ;; 190 | c) 191 | profile=$OPTARG 192 | ;; 193 | p) 194 | permission=$OPTARG 195 | ;; 196 | n) 197 | name=$OPTARG 198 | ;; 199 | u) 200 | metadataUrl=$OPTARG 201 | if ! [[ "$metadataUrl" =~ "https" ]]; 202 | then 203 | echo "metadata url should contain https." 204 | echo "e.g. https://idp.stanford.edu/metadata.xml" 205 | exit 1 206 | fi 207 | ;; 208 | w) 209 | workgroup=$OPTARG 210 | ;; 211 | l) 212 | accountAlias=$OPTARG 213 | ;; 214 | r) 215 | roleName=$OPTARG 216 | ;; 217 | d) 218 | dryrun=1 219 | ;; 220 | [h?]) 221 | help 222 | exit 223 | ;; 224 | esac 225 | done 226 | 227 | if [[ -z $action || -z $profile || -z $name ]]; then 228 | help 229 | echo "-a, -c, and -n are required." 230 | exit 1 231 | elif [[ $action =~ ^(create|delete)$ && -z $permission && -z $workgroup ]]; then 232 | echo "create or delete requires access permission and workgroup name." 233 | exit 1 234 | fi 235 | 236 | echo "Getting AWS account number ..." 237 | accountId=$(aws --profile $profile iam get-user | jq '.User.Arn' | grep -Eo '[[:digit:]]{12}') 238 | if [ -z "$accountId" ]; then 239 | echo "Cannot find AWS account number." 240 | exit 1 241 | fi 242 | 243 | # Get saml provider arn 244 | echo "$action $name" 245 | samlProviderArn=$(aws --profile $profile iam list-saml-providers | jq --raw-output ".SAMLProviderList[] | select(.Arn == \"arn:aws:iam::$accountId:saml-provider/$name\") | .Arn") 246 | 247 | # Call functions based on action 248 | case $action in 249 | 'create') 250 | if [ "$samlProviderArn" == "arn:aws:iam::$accountId:saml-provider/$name" ]; 251 | then 252 | show_saml_provider 253 | echo "SAML provider $name is already setup." 254 | exit 0 255 | else 256 | validate_policy && create_saml_provider && create_role 257 | fi 258 | ;; 259 | 'show') 260 | if [ "$samlProviderArn" != "arn:aws:iam::$accountId:saml-provider/$name" ]; 261 | then 262 | echo "SAML provider $name doesn't exist." 263 | exit 1 264 | else 265 | show_saml_provider 266 | fi 267 | ;; 268 | 'delete') 269 | if [ "$samlProviderArn" != "arn:aws:iam::$accountId:saml-provider/$name" ]; 270 | then 271 | echo "SAML provider $name doesn't exist." 272 | exit 1 273 | else 274 | validate_policy && delete_saml_provider && delete_role 275 | fi 276 | ;; 277 | esac 278 | 279 | [ $dryrun -eq 1 ] && echo "Dryrun mode. Nothing is changed." || print_info 280 | 281 | # Cleanup 282 | rm -rf /tmp/trust-policy.json 283 | rm -rf /tmp/samlMetadata.xml 284 | 285 | exit 0 286 | --------------------------------------------------------------------------------