Skip to content

Commit 88761aa

Browse files
authored
Merge pull request #197 from venth/print_console_signin_url
Print AWS web console sign-in url
2 parents a6d6ee8 + f39bc0a commit 88761aa

File tree

4 files changed

+151
-8
lines changed

4 files changed

+151
-8
lines changed

README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,14 @@ aws-adfs integrates with:
253253
AWS_DEFAULT_REGION environmental variables
254254
instead of saving them to the aws
255255
configuration file.
256+
--print-console-signin-url Output a URL that lets users who sign in to
257+
your organization's network securely access
258+
the AWS Management Console.
259+
--console-role-arn TEXT Role to assume for use in conjunction with
260+
--print-console-signin-url
261+
--console-external-id TEXT External ID to pass in assume role for use
262+
in conjunction with --print-console-signin-
263+
url
256264
--role-arn TEXT Predefined role arn to selects, e.g. aws-
257265
adfs login --role-arn arn:aws:iam::123456789
258266
012:role/YourSpecialRole
@@ -420,4 +428,6 @@ poetry run pytest
420428
* [johan1252](https://github.com/johan1252) for: Ask for authentication method if there is no default method set in Duo Security settings
421429
* [pdecat](https://github.com/pdecat) for: Always return the same number of values from _initiate_authentication()
422430
* [mikereinhold](https://github.com/mikereinhold) for: Feature credential process
423-
* [pdecat](https://github.com/pdecat) for: Add --username-password-command command line parameter
431+
* [pdecat](https://github.com/pdecat) for:
432+
* Add --username-password-command command line parameter
433+
* Add --print-console-signin-url, --console-role-arn and --console-external-id command line parameters

aws_adfs/login.py

Lines changed: 97 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,17 @@
55
import os.path
66
import subprocess
77
import sys
8+
import urllib
89
from datetime import datetime, timezone
910
from os import environ
1011
from platform import system
1112

13+
import boto3
1214
import botocore
1315
import botocore.exceptions
1416
import botocore.session
1517
import click
18+
import requests
1619
from botocore import client
1720

1821
from . import authenticator, helpers, prepare, role_chooser
@@ -90,6 +93,19 @@
9093
is_flag=True,
9194
help='Output commands to set AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN, AWS_DEFAULT_REGION environmental variables instead of saving them to the aws configuration file.',
9295
)
96+
@click.option(
97+
'--print-console-signin-url',
98+
is_flag=True,
99+
help='Output a URL that lets users who sign in to your organization\'s network securely access the AWS Management Console.',
100+
)
101+
@click.option(
102+
"--console-role-arn",
103+
help="Role to assume for use in conjunction with --print-console-signin-url",
104+
)
105+
@click.option(
106+
"--console-external-id",
107+
help="External ID to pass in assume role for use in conjunction with --print-console-signin-url",
108+
)
93109
@click.option(
94110
'--role-arn',
95111
help='Predefined role arn to selects, e.g. aws-adfs login --role-arn arn:aws:iam::123456789012:role/YourSpecialRole',
@@ -133,6 +149,9 @@ def login(
133149
authfile,
134150
stdout,
135151
printenv,
152+
print_console_signin_url,
153+
console_role_arn,
154+
console_external_id,
136155
role_arn,
137156
session_duration,
138157
no_session_cache,
@@ -259,7 +278,11 @@ def login(
259278
_emit_json(aws_session_token)
260279
elif printenv:
261280
_emit_summary(config, aws_session_duration)
262-
_print_environment_variables(aws_session_token,config)
281+
_print_environment_variables(aws_session_token, config)
282+
elif print_console_signin_url:
283+
_print_console_signin_url(
284+
aws_session_token, adfs_host, console_role_arn, console_external_id
285+
)
263286
else:
264287
_store(config, aws_session_token)
265288
_emit_summary(config, aws_session_duration)
@@ -275,7 +298,7 @@ def _emit_json(aws_session_token):
275298
}))
276299

277300

278-
def _print_environment_variables(aws_session_token,config):
301+
def _print_environment_variables(aws_session_token, config):
279302
envcommand = "export"
280303
if(sys.platform=="win32"):
281304
envcommand="set"
@@ -287,8 +310,79 @@ def _print_environment_variables(aws_session_token,config):
287310
click.echo(
288311
u"""{} AWS_SESSION_TOKEN={}""".format(envcommand,aws_session_token['Credentials']['SessionToken']))
289312
click.echo(
290-
u"""{} AWS_DEFAULT_REGION={}""".format(envcommand,config.region))
313+
u"""{} AWS_DEFAULT_REGION={}""".format(envcommand, config.region))
314+
315+
316+
def _print_console_signin_url(
317+
aws_session_token, adfs_host, console_role_arn, console_external_id
318+
):
319+
# The steps below come from https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_enable-console-custom-url.html
320+
321+
if console_role_arn:
322+
# Step 2: Using the access keys for an IAM user in your AWS account,
323+
# call "AssumeRole" to get temporary access keys for the federated user
324+
325+
# Note: Calls to AWS STS AssumeRole must be signed using the access key ID
326+
# and secret access key of an IAM user or using existing temporary credentials.
327+
# The credentials can be in EC2 instance metadata, in environment variables,
328+
# or in a configuration file, and will be discovered automatically by the
329+
# client('sts') function. For more information, see the Python SDK docs:
330+
# http://boto3.readthedocs.io/en/latest/reference/services/sts.html
331+
# http://boto3.readthedocs.io/en/latest/reference/services/sts.html#STS.Client.assume_role
332+
333+
# FIXME: use botocore instead of boto3: https://github.com/boto/botocore/blob/1.21.49/botocore/credentials.py#L766
334+
sts_connection = boto3.client(
335+
"sts",
336+
aws_access_key_id=aws_session_token["Credentials"]["AccessKeyId"],
337+
aws_secret_access_key=aws_session_token["Credentials"]["SecretAccessKey"],
338+
aws_session_token=aws_session_token["Credentials"]["SessionToken"],
339+
)
340+
341+
if console_external_id:
342+
aws_session_token = sts_connection.assume_role(
343+
RoleArn=console_role_arn,
344+
RoleSessionName="aws-adfs",
345+
ExternalId=console_external_id,
346+
)
347+
else:
348+
aws_session_token = sts_connection.assume_role(
349+
RoleArn=console_role_arn,
350+
RoleSessionName="aws-adfs",
351+
)
291352

353+
# Step 3: Format resulting temporary credentials into JSON
354+
url_credentials = {}
355+
url_credentials['sessionId'] = aws_session_token['Credentials']['AccessKeyId']
356+
url_credentials['sessionKey'] = aws_session_token['Credentials']['SecretAccessKey']
357+
url_credentials['sessionToken'] = aws_session_token['Credentials']['SessionToken']
358+
json_string_with_temp_credentials = json.dumps(url_credentials)
359+
360+
# Step 4. Make request to AWS federation endpoint to get sign-in token. Construct the parameter string with
361+
# the sign-in action request, a 12-hour session duration, and the JSON document with temporary credentials
362+
# as parameters.
363+
request_parameters = "?Action=getSigninToken"
364+
365+
# https://signin.aws.amazon.com/federation endpoint returns a HTTP/1.1 400 Bad Request error with AssumeRole credentials when SessionDuration is set
366+
if not console_role_arn:
367+
request_parameters += "&SessionDuration=43200"
368+
369+
request_parameters += "&Session=" + urllib.parse.quote_plus(json_string_with_temp_credentials)
370+
request_url = "https://signin.aws.amazon.com/federation" + request_parameters
371+
r = requests.get(request_url)
372+
# Returns a JSON document with a single element named SigninToken.
373+
signin_token = json.loads(r.text)
374+
375+
# Step 5: Create URL where users can use the sign-in token to sign in to
376+
# the console. This URL must be used within 15 minutes after the
377+
# sign-in token was issued.
378+
request_parameters = "?Action=login"
379+
request_parameters += "&Issuer=" + urllib.parse.quote_plus("https://" + adfs_host + "/adfs/ls/IdpInitiatedSignOn.aspx?loginToRp=urn:amazon:webservices")
380+
request_parameters += "&Destination=" + urllib.parse.quote_plus("https://console.aws.amazon.com/")
381+
request_parameters += "&SigninToken=" + signin_token["SigninToken"]
382+
request_url = "https://signin.aws.amazon.com/federation" + request_parameters
383+
384+
# Send final URL to stdout
385+
click.echo("""\nAWS web console signin URL:\n\n{}""".format(request_url))
292386

293387
def _emit_summary(config, session_duration):
294388
click.echo(

poetry.lock

Lines changed: 42 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ requests-kerberos = [
4242
requests-negotiate-sspi= [
4343
{ version = ">=0.3.4", markers = "platform_system == 'Windows'" },
4444
]
45+
boto3 = "^1.20.50"
4546

4647
[tool.poetry.dev-dependencies]
4748
coverage = "<4"

0 commit comments

Comments
 (0)