Skip to content

Commit

Permalink
Merge pull request #157 from SirFerdek/prefix_role_with_acc
Browse files Browse the repository at this point in the history
Ability to download all credentials for all roles without creating conflicts
  • Loading branch information
Sector95 authored Jan 6, 2020
2 parents ba24d2c + d9a5277 commit 09e6c32
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 26 deletions.
59 changes: 42 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,38 +16,45 @@ With gimme-aws-creds all you need to know is your username, password, Okta url a
Python 3

### Optional
[Gimme-creds-lambda](https://github.com/Nike-Inc/gimme-aws-creds/tree/master/lambda) can be used as a proxy to the Okta APIs needed by gimme-aws-creds. This removes the requirement of an Okta API key. Gimme-aws-creds authenticates to gimme-creds-lambda using OpenID Connect and the lambda handles all interactions with the Okta APIs. Alternately, you can set the `OKTA_API_KEY` environment variable and the `gimme_creds_server` configuration value to 'internal' to call the Okta APIs directly from gimme-aws-creds.

[Gimme-creds-lambda](https://github.com/Nike-Inc/gimme-aws-creds/tree/master/lambda) can be used as a proxy to the Okta APIs needed by gimme-aws-creds. This removes the requirement of an Okta API key. Gimme-aws-creds authenticates to gimme-creds-lambda using OpenID Connect and the lambda handles all interactions with the Okta APIs. Alternately, you can set the `OKTA_API_KEY` environment variable and the `gimme_creds_server` configuration value to 'internal' to call the Okta APIs directly from gimme-aws-creds.

## Installation

This is a Python 3 project.

Install/Upgrade from PyPi:

```bash
pip3 install --upgrade gimme-aws-creds
```

__OR__

Install/Upgrade the latest gimme-aws-creds package direct from GitHub:

```bash
pip3 install --upgrade git+git://github.com/Nike-Inc/gimme-aws-creds.git
```

__OR__

Install the gimme-aws-creds package if you have already cloned the source:

```bash
python3 setup.py install
```

__OR__

Build the docker image locally:

```bash
docker build -t gimme-aws-creds .
```

To make it easier you can also create an alias for the gimme-aws-creds command with docker:

```bash
# make sure you have the "~/.okta_aws_login_config" locally first!
touch ~/.okta_aws_login_config && \
Expand All @@ -56,16 +63,19 @@ alias gimme-aws-creds="docker run -it --rm \
-v ~/.okta_aws_login_config:/root/.okta_aws_login_config \
gimme-aws-creds"
```

With this config, you will be able to run further commands seamlessly!

## Configuration

To set-up the configuration run:

```bash
gimme-aws-creds --action-configure
```

You can also set up different Okta configuration profiles, this useful if you have multiple Okta accounts or environments you need credentials for. You can use the configuration wizard or run:

```bash
gimme-aws-creds --action-configure --profile profileName
```
Expand All @@ -77,15 +87,19 @@ A configuration wizard will prompt you to enter the necessary configuration para
- okta_auth_server - [Okta API Authorization Server](https://help.okta.com/en/prev/Content/Topics/Security/API_Access.htm) used for OpenID Connect authentication for gimme-creds-lambda
- client_id - OAuth client ID for gimme-creds-lambda
- gimme_creds_server
- URL for gimme-creds-lambda
- 'internal' for direct interaction with the Okta APIs (`OKTA_API_KEY` environment variable required)
- 'appurl' to set an aws application link url. This setting removes the need of an OKTA API key.
- URL for gimme-creds-lambda
- 'internal' for direct interaction with the Okta APIs (`OKTA_API_KEY` environment variable required)
- 'appurl' to set an aws application link url. This setting removes the need of an OKTA API key.
- write_aws_creds - True or False - If True, the AWS credentials will be written to `~/.aws/credentials` otherwise it will be written to stdout.
- cred_profile - If writing to the AWS cred file, this sets the name of the AWS credential profile. The reserved word 'role' will use the name component of the role arn as the profile name. i.e. arn:aws:iam::123456789012:role/okta-1234-role becomes section [okta-1234-role] in the aws credentials file
- cred_profile - If writing to the AWS cred file, this sets the name of the AWS credential profile.
- The reserved word `role` will use the name component of the role arn as the profile name. i.e. arn:aws:iam::123456789012:role/okta-1234-role becomes section [okta-1234-role] in the aws credentials file
- The reserved word `acc-role` will use the name component of the role arn prepended with account number to avoid collisions, i.e. arn:aws:iam::123456789012:role/okta-1234-role becomes section [123456789012-okta-1234-role] in the aws credentials file
- If set to `default` then the temp creds will be stored in the default profile
- Note: if there are multiple roles, and `default` is selected it will be overwritten multiple times and last role wins. The same happens when `role` is selected and you have many accounts with the same role names. Consider using `acc-role` if this happens.
- aws_appname - This is optional. The Okta AWS App name, which has the role you want to assume.
- aws_rolename - This is optional. The ARN of the role you want temporary AWS credentials for. The reserved word 'all' can be used to get and store credentials for every role the user is permissioned for.
- aws_default_duration = This is optional. Lifetime for temporary credentials, in seconds. Defaults to 1 hour (3600)
- app_url - If using 'appurl' setting for gimme_creds_server, this sets the url to the aws application configured in Okta. It is typically something like https://something.okta[preview].com/home/amazon_aws/app_instance_id/something
- app_url - If using 'appurl' setting for gimme_creds_server, this sets the url to the aws application configured in Okta. It is typically something like <https://something.okta[preview].com/home/amazon_aws/app_instance_id/something>
- okta_username - use this username to authenticate
- preferred_mfa_type - automatically select a particular device when prompted for MFA:
- push - Okta Verify App push
Expand All @@ -95,26 +109,29 @@ A configuration wizard will prompt you to enter the necessary configuration para
- sms - OTP via SMS message
- resolve_aws_alias - y or n. If yes, gimme-aws-creds will try to resolve AWS account ids with respective alias names (default: n). This option can also be set interactively in the command line using `-r` or `--resolve` parameter
- remember_device - y or n. If yes, the MFA device will be remembered by Okta service for a limited time. This option can also be set interactively in the command line using `-m` or `--remember-device`
- output_format - `json` or `export`, determines default credential output format, can be also specified by `--output-format FORMAT` and `-o FORMAT`.
- output_format - `json` or `export`, determines default credential output format, can be also specified by `--output-format FORMAT` and `-o FORMAT`.

## Configuration File

The config file follows a [configfile](https://docs.python.org/3/library/configparser.html) format.
By default, it is located in $HOME/.okta_aws_login_config

Example file:
`

```ini
[myprofile]
client_id = myclient_id
`
```

Configurations can inherit from other configurations to share common configuration parameters.
`

```ini
[my-base-profile]
client_id = myclient_id
[myprofile]
inherit = my-base-profile
aws_rolename = my-role
`
```

## Usage

Expand Down Expand Up @@ -150,7 +167,7 @@ You can automate the environnement variable creation by running `$(gimme-aws-cre
You can run a specific configuration profile with the `--profile` parameter:

```bash
$ ./gimme-aws-creds --profile profileName
./gimme-aws-creds --profile profileName
```

The username and password you are prompted for are the ones you login to Okta with. You can predefine your username by setting the `OKTA_USERNAME` environment variable or using the `-u username` parameter.
Expand All @@ -162,9 +179,10 @@ If all goes well you will get your temporary AWS access, secret key and token, t
You can always run `gimme-aws-creds --help` for all the available options.

Alternatively, you can overwrite values in the config section with environment variables for instances where say you may want to change the duration of your token.
A list of values of to change with environment variables are:
A list of values of to change with environment variables are:

- `AWS_DEFAULT_DURATION` - corresponds to `aws_default_duration` configuration
- `AWS_SHARED_CREDENTIALS_FILE` - file to write credentials to, points to `~/.aws/credentials` by default
- `AWS_SHARED_CREDENTIALS_FILE` - file to write credentials to, points to `~/.aws/credentials` by default
- `GIMME_AWS_CREDS_CLIENT_ID` - corresponds to `client_id` configuration
- `GIMME_AWS_CREDS_CRED_PROFILE` - corresponds to `cred_profile` configuration
- `GIMME_AWS_CREDS_OUTPUT_FORMAT` - corresponds to `output_format` configuration and `--output-format` CLI option
Expand All @@ -179,16 +197,20 @@ Example: `GIMME_AWS_CREDS_CLIENT_ID='foobar' AWS_DEFAULT_DURATION=12345 gimme-aw
For changing variables outside of this, you'd need to create a separate profile altogether with `gimme-aws-creds --action-configure --profile profileName`

### Viewing Profiles

`gimme-aws-creds --action-list-profiles` will go to your okta config file and print out all profiles created and their settings.

### Viewing roles

`gimme-aws-creds --action-list-roles` will print all available roles to STDOUT without retrieving their credentials.

### Generate credentials as json

`gimme-aws-creds -o json` will print out credentials in JSON format - 1 entry per line

### Store credentials from json
`gimme-aws-creds --action-store-json-creds` will store JSON formatted credentials from `stdin` to

`gimme-aws-creds --action-store-json-creds` will store JSON formatted credentials from `stdin` to
aws credentials file, eg: `gimme-aws-creds -o json | gimme-aws-creds --action-store-json-creds`.
Data can be modified by scripts on the way.

Expand Down Expand Up @@ -248,16 +270,18 @@ Actually it has only been tested with USB U2F keys & yubikeys.
You can run all the unit tests using nosetests. Most of the tests are mocked.

```bash
$ nosetests --verbosity=2 tests/
nosetests --verbosity=2 tests/
```

## Maintenance

This project is maintained by [Ann Wallace](https://github.com/anners), [Eric Pierce](https://github.com/epierce), and [Justin Wiley](https://github.com/sector95).

## Thanks and Credit

I came across [okta_aws_login](https://github.com/nimbusscale/okta_aws_login) written by Joe Keegan, when I was searching for a CLI tool that generates AWS tokens via Okta. Unfortunately it hasn't been updated since 2015 and didn't seem to work with the current Okta version. But there was still some great code I was able to reuse under the MIT license for gimme-aws-creds. I have noted in the comments where I used his code, to make sure he receives proper credit.

## Etc.
## Etc

[Okta's Java tool](https://github.com/oktadeveloper/okta-aws-cli-assume-role)

Expand All @@ -266,6 +290,7 @@ I came across [okta_aws_login](https://github.com/nimbusscale/okta_aws_login) wr
## [Contributing](https://github.com/Nike-Inc/gimme-aws-creds/blob/master/CONTRIBUTING.md)

## License

Gimme AWS Creds is released under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0)

[license]:LICENSE
Expand Down
5 changes: 3 additions & 2 deletions gimme_aws_creds/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ def update_config_file(self):
client_id = OAuth Client id for the gimme-creds-server
okta_auth_server = Server ID for the OAuth authorization server used by gimme-creds-server
write_aws_creds = Option to write creds to ~/.aws/credentials
cred_profile = Use DEFAULT or Role as the profile in ~/.aws/credentials
cred_profile = Use DEFAULT or Role-based name as the profile in ~/.aws/credentials
aws_appname = (optional) Okta AWS App Name
aws_rolename = (optional) Okta Role ARN
okta_username = Okta username
Expand Down Expand Up @@ -400,14 +400,15 @@ def _get_cred_profile(self, default_entry):
ui.default.message(
"The AWS credential profile defines which profile is used to store the temp AWS creds.\n"
"If set to 'role' then a new profile will be created matching the role name assumed by the user.\n"
"If set to 'acc-role' then a new profile will be created matching the role name assumed by the user, but prefixed with account number to avoid collisions.\n"
"If set to 'default' then the temp creds will be stored in the default profile\n"
"If set to any other value, the name of the profile will match that value."
)

cred_profile = self._get_user_input(
"AWS Credential Profile", default_entry)

if cred_profile.lower() in ['default', 'role']:
if cred_profile.lower() in ['default', 'role', 'acc-role']:
cred_profile = cred_profile.lower()

return cred_profile
Expand Down
25 changes: 18 additions & 7 deletions gimme_aws_creds/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class GimmeAWSCreds(object):
client_id = OAuth Client id for the gimme-creds-server
okta_auth_server = Server ID for the OAuth authorization server used by gimme-creds-server
write_aws_creds = Option to write creds to ~/.aws/credentials
cred_profile = Use DEFAULT or Role as the profile in ~/.aws/credentials
cred_profile = Use DEFAULT or Role-based name as the profile in ~/.aws/credentials
aws_appname = (optional) Okta AWS App Name
aws_rolename = (optional) AWS Role ARN. 'ALL' will retrieve all roles, can be a CSV for multiple roles.
okta_username = (optional) Okta User Name
Expand Down Expand Up @@ -286,6 +286,15 @@ def _get_aws_account_info(okta_org_url, okta_api_key, username):

return app_list

@staticmethod
def _get_account_and_rolename_from_arn(arn):
""" Extracts account number and role name from role arn string """
matches = re.match(r"arn:(aws|aws-cn|aws-us-gov):iam:.*:(?P<acc>\d+):role/(?P<role>\S+)", arn)
return {
'account': matches.group('acc'),
'role': matches.group('role')
}

def _choose_app(self, aws_info):
""" gets a list of available apps and
ask the user to select the app they want
Expand Down Expand Up @@ -676,23 +685,25 @@ def prepare_data(self, role, generate_credentials=False):
else:
self.ui.error('Failed to generate credentials for {} due to {}'.format(role.role, ex))

deriv_profname = re.sub('arn:(aws|aws-cn|aws-us-gov):iam:.*/', '', role.role)
naming_data = self._get_account_and_rolename_from_arn(role.role)
# set the profile name
# Note if there are multiple roles, and 'default' is
# selected it will be overwritten multiple times and last role
# wins.
# Note if there are multiple roles
# it will be overwritten multiple times and last role wins.
if self.conf_dict['cred_profile'].lower() == 'default':
profile_name = 'default'
elif self.conf_dict['cred_profile'].lower() == 'role':
profile_name = deriv_profname
profile_name = naming_data['role']
elif self.conf_dict['cred_profile'].lower() == 'acc-role':
profile_name = '-'.join([naming_data['account'],
naming_data['role']])
else:
profile_name = self.conf_dict['cred_profile']

return {
'shared_credentials_file': self.AWS_CONFIG,
'profile': {
'name': profile_name,
'derived_name': deriv_profname,
'derived_name': naming_data['role'],
'config_name': self.conf_dict.get('cred_profile', ''),
},
'role': {
Expand Down
9 changes: 9 additions & 0 deletions tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,12 @@ def test_get_partition_unkown(self):
creds = GimmeAWSCreds()

self.assertRaises(errors.GimmeAWSCredsExitBase, creds._get_partition_from_saml_acs, 'https://signin.amazonaws-foo.com/saml')

def test_arn_to_account_and_role_name(self):
creds = GimmeAWSCreds()
arn = "arn:aws:iam::123456789012:role/okta-1234-role"
self.assertEqual(creds._get_account_and_rolename_from_arn(arn),
{
'account': '123456789012',
'role': 'okta-1234-role'
})

0 comments on commit 09e6c32

Please sign in to comment.