-
Notifications
You must be signed in to change notification settings - Fork 179
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Updated tool to read/write from AWS profile configurations. #7
Changes from all commits
5771dd3
bf47832
d09736f
d495b51
b56b624
694833a
9d9ba18
9717b34
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,14 +11,18 @@ | |
import json | ||
from bs4 import BeautifulSoup | ||
from lxml import etree | ||
import configparser | ||
|
||
VERSION = "0.0.6" | ||
import prepare | ||
|
||
VERSION = "0.0.7" | ||
|
||
REGION = os.getenv("AWS_DEFAULT_REGION") or "ap-southeast-2" | ||
IDP_ID = os.getenv("GOOGLE_IDP_ID") | ||
SP_ID = os.getenv("GOOGLE_SP_ID") | ||
USERNAME = os.getenv("GOOGLE_USERNAME") | ||
DURATION = os.getenv("DURATION") | ||
PROFILE = os.getenv("AWS_PROFILE") | ||
|
||
class GoogleAuth: | ||
def __init__(self, **kwargs): | ||
|
@@ -277,32 +281,41 @@ def cli(): | |
parser.add_argument('-S', '--sp-id', default=SP_ID, help='Google SSO SP identifier ($GOOGLE_SP_ID)') | ||
parser.add_argument('-R', '--region', default=REGION, help='AWS region endpoint ($AWS_DEFAULT_REGION)') | ||
parser.add_argument('-d', '--duration', default=DURATION, help='Credential duration ($DURATION)') | ||
parser.add_argument('-p', '--profile', default=PROFILE, help='AWS profile ($AWS_PROFILE)') | ||
|
||
args = parser.parse_args() | ||
|
||
if args.username is None: | ||
args.username = raw_input("Google username: ") | ||
|
||
if args.idp_id is None or args.sp_id is None: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. LGTM 👍 |
||
print "Must set both GOOGLE_IDP_ID and GOOGLE_SP_ID" | ||
parser.print_help() | ||
sys.exit(1) | ||
|
||
if args.duration is None: | ||
print "Setting duration to 3600 seconds" | ||
args.duration = 3600 | ||
|
||
if args.duration > 3600: | ||
print "Duration must be less than or equal to 3600" | ||
duration = 3600 | ||
|
||
config = prepare.get_prepared_config( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This looks up the config parameters from the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Noice |
||
args.profile, | ||
args.region, | ||
args.username, | ||
args.idp_id, | ||
args.sp_id, | ||
args.duration | ||
) | ||
|
||
if config.google_username is None: | ||
config.google_username = raw_input("Google username: ") | ||
else: | ||
print "Google username: " + config.google_username | ||
|
||
if config.google_idp_id is None: | ||
config.google_idp_id = raw_input("Google idp: ") | ||
|
||
if config.google_sp_id is None: | ||
config.google_sp_id = raw_input("Google sp: ") | ||
|
||
passwd = getpass.getpass() | ||
|
||
google = GoogleAuth( | ||
username=args.username, | ||
username=config.google_username, | ||
password=passwd, | ||
idp_id=args.idp_id, | ||
sp_id=args.sp_id | ||
idp_id=config.google_idp_id, | ||
sp_id=config.google_sp_id | ||
) | ||
|
||
google.do_login() | ||
|
@@ -312,18 +325,69 @@ def cli(): | |
doc = etree.fromstring(base64.b64decode(encoded_saml)) | ||
roles = dict([x.split(',') for x in doc.xpath('//*[@Name = "https://aws.amazon.com/SAML/Attributes/Role"]//text()')]) | ||
|
||
role, provider = pick_one(roles) | ||
if not config.role_arn in roles: | ||
config.role_arn, config.provider = pick_one(roles) | ||
|
||
print "Assuming " + role | ||
print "Assuming " + config.role_arn | ||
|
||
sts = boto3.client('sts', region_name=REGION) | ||
sts = boto3.client('sts', region_name=config.region) | ||
token = sts.assume_role_with_saml( | ||
RoleArn=role, | ||
PrincipalArn=provider, | ||
RoleArn=config.role_arn, | ||
PrincipalArn=config.provider, | ||
SAMLAssertion=encoded_saml, | ||
DurationSeconds=args.duration) | ||
DurationSeconds=config.duration) | ||
|
||
print "export AWS_ACCESS_KEY_ID='{}'".format(token['Credentials']['AccessKeyId']) | ||
print "export AWS_SECRET_ACCESS_KEY='{}'".format(token['Credentials']['SecretAccessKey']) | ||
print "export AWS_SESSION_TOKEN='{}'".format(token['Credentials']['SessionToken']) | ||
print "export AWS_SESSION_EXPIRATION='{}'".format(token['Credentials']['Expiration']) | ||
if conifig.profile is None: | ||
print "export AWS_ACCESS_KEY_ID='{}'".format(token['Credentials']['AccessKeyId']) | ||
print "export AWS_SECRET_ACCESS_KEY='{}'".format(token['Credentials']['SecretAccessKey']) | ||
print "export AWS_SESSION_TOKEN='{}'".format(token['Credentials']['SessionToken']) | ||
print "export AWS_SESSION_EXPIRATION='{}'".format(token['Credentials']['Expiration']) | ||
|
||
_store(config, token) | ||
|
||
|
||
def _store(config, aws_session_token): | ||
|
||
def store_config(profile, config_location, storer): | ||
config_file = configparser.RawConfigParser() | ||
config_file.read(config_location) | ||
|
||
if not config_file.has_section(profile): | ||
config_file.add_section(profile) | ||
|
||
storer(config_file, profile) | ||
|
||
with open(config_location, 'w+') as f: | ||
try: | ||
config_file.write(f) | ||
finally: | ||
f.close() | ||
|
||
def credentials_storer(config_file, profile): | ||
config_file.set(profile, 'aws_access_key_id', aws_session_token['Credentials']['AccessKeyId']) | ||
config_file.set(profile, 'aws_secret_access_key', aws_session_token['Credentials']['SecretAccessKey']) | ||
config_file.set(profile, 'aws_session_token', aws_session_token['Credentials']['SessionToken']) | ||
config_file.set(profile, 'aws_security_token', aws_session_token['Credentials']['SessionToken']) | ||
|
||
def config_storer(config_file, profile): | ||
config_file.set(profile, 'region', config.region) | ||
config_file.set(profile, 'output', config.output_format) | ||
config_file.set(profile, 'google_config.role_arn', config.role_arn) | ||
config_file.set(profile, 'google_config.provider', config.provider) | ||
config_file.set(profile, 'google_config.google_idp_id', config.google_idp_id) | ||
config_file.set(profile, 'google_config.google_sp_id', config.google_sp_id) | ||
config_file.set(profile, 'google_config.google_username', config.google_username) | ||
config_file.set(profile, 'google_config.duration', config.duration) | ||
|
||
store_config(config.profile, config.aws_credentials_location, credentials_storer) | ||
if config.profile == 'default': | ||
store_config(config.profile, config.aws_config_location, config_storer) | ||
else: | ||
store_config('profile {}'.format(config.profile), config.aws_config_location, config_storer) | ||
|
||
|
||
if __name__ == '__main__': | ||
try: | ||
cli() | ||
except KeyboardInterrupt: | ||
pass |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
import ast | ||
import configparser | ||
import os | ||
import botocore.session | ||
from types import MethodType | ||
|
||
|
||
def get_prepared_config( | ||
profile, | ||
region, | ||
google_username, | ||
google_idp_id, | ||
google_sp_id, | ||
duration | ||
): | ||
|
||
def default_if_none(value, default): | ||
return value if value is not None else default | ||
|
||
google_config.profile = default_if_none(profile, google_config.profile) | ||
|
||
_create_base_aws_cli_config_files_if_needed(google_config) | ||
_load_google_config_from_stored_profile(google_config, google_config.profile) | ||
|
||
google_config.region = default_if_none(region, google_config.region) | ||
google_config.google_username = default_if_none(google_username, google_config.google_username) | ||
google_config.google_idp_id = default_if_none(google_idp_id, google_config.google_idp_id) | ||
google_config.google_sp_id = default_if_none(google_sp_id, google_config.google_sp_id) | ||
google_config.duration = default_if_none(duration, google_config.duration) | ||
|
||
return google_config | ||
|
||
|
||
def _create_google_default_config(): | ||
config = type('', (), {})() | ||
|
||
# Use botocore session API to get defaults | ||
session = botocore.session.Session() | ||
|
||
# region: The default AWS region that this script will connect | ||
# to for all API calls | ||
config.region = session.get_config_variable('region') or 'eu-central-1' | ||
|
||
# aws cli profile to store config and access keys into | ||
config.profile = session.profile or 'default' | ||
|
||
# output format: The AWS CLI output format that will be configured in the | ||
# adf profile (affects subsequent CLI calls) | ||
config.output_format = session.get_config_variable('format') or 'json' | ||
|
||
# aws credential location: The file where this script will store the temp | ||
# credentials under the configured profile | ||
config.aws_credentials_location = os.path.expanduser(session.get_config_variable('credentials_file')) | ||
config.aws_config_location = os.path.expanduser(session.get_config_variable('config_file')) | ||
|
||
config.role_arn = None | ||
config.provider = None | ||
|
||
config.google_sp_id = None | ||
config.google_idp_id = None | ||
config.google_username = None | ||
config.duration = 3600 | ||
|
||
return config | ||
|
||
|
||
def _load_google_config_from_stored_profile(google_config, profile): | ||
|
||
def get_or(self, profile, option, default_value): | ||
if self.has_option(profile, option): | ||
return self.get(profile, option) | ||
return default_value | ||
|
||
def load_from_config(config_location, profile, loader): | ||
config = configparser.RawConfigParser() | ||
config.read(config_location) | ||
if config.has_section(profile): | ||
setattr(config, get_or.__name__, MethodType(get_or, config)) | ||
loader(config, profile) | ||
|
||
del config | ||
|
||
def load_config(config, profile): | ||
google_config.region = config.get_or(profile, 'region', google_config.region) | ||
google_config.output_format = config.get_or(profile, 'output', google_config.output_format) | ||
|
||
google_config.role_arn = config.get_or(profile, 'google_config.role_arn', google_config.role_arn) | ||
google_config.provider = config.get_or(profile, 'google_config.provider', google_config.provider) | ||
google_config.google_idp_id = config.get_or(profile, 'google_config.google_idp_id', google_config.google_idp_id) | ||
google_config.google_sp_id = config.get_or(profile, 'google_config.google_sp_id', google_config.google_sp_id) | ||
google_config.google_username = config.get_or(profile, 'google_config.google_username', google_config.google_username) | ||
|
||
if profile == 'default': | ||
load_from_config(google_config.aws_config_location, profile, load_config) | ||
else: | ||
load_from_config(google_config.aws_config_location, 'profile ' + profile, load_config) | ||
|
||
|
||
def _create_base_aws_cli_config_files_if_needed(google_config): | ||
def touch(fname, mode=0o600): | ||
flags = os.O_CREAT | os.O_APPEND | ||
with os.fdopen(os.open(fname, flags, mode)) as f: | ||
try: | ||
os.utime(fname, None) | ||
finally: | ||
f.close() | ||
|
||
aws_config_root = os.path.dirname(google_config.aws_config_location) | ||
|
||
if not os.path.exists(aws_config_root): | ||
os.mkdir(aws_config_root, 0o700) | ||
|
||
if not os.path.exists(google_config.aws_credentials_location): | ||
touch(google_config.aws_credentials_location) | ||
|
||
aws_credentials_root = os.path.dirname(google_config.aws_credentials_location) | ||
|
||
if not os.path.exists(aws_credentials_root): | ||
os.mkdir(aws_credentials_root, 0o700) | ||
|
||
if not os.path.exists(google_config.aws_config_location): | ||
touch(google_config.aws_config_location) | ||
|
||
|
||
google_config = _create_google_default_config() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking for advice on how to update the README to reflect the new features.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about rewriting the
Usage
section of the README. Something along the lines of:and then, I can merge this into the bash_wrapper branch and update the section on "environment variables only"