├── .gitignore ├── LICENSE ├── README.md ├── awscli_keyring ├── __init__.py ├── commands.py ├── persistence.py └── session.py ├── setup.cfg └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | dist 3 | *.egg-info 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Samuel Cochran 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AWS CLI Keyring 2 | 3 | AWS command line credentials from OS X Keychain, or anything else [Keyring](https://pypi.python.org/pypi/keyring) supports. 4 | 5 | ## Usage 6 | 7 | Install with pip/setuptools: 8 | 9 | ``` 10 | $ pip install awscli-keyring 11 | ``` 12 | 13 | Turn it on and add some credentials: 14 | 15 | ``` 16 | $ aws configure set plugins.keyring awscli_keyring 17 | $ aws keyring add 18 | AWS Access Key ID [None]: ... 19 | AWS Secret Acess Key [None]: ... 20 | $ aws ec2 describe-instances 21 | { ... } 22 | ``` 23 | 24 | This will change your config like: 25 | 26 | ``` 27 | # ~/.aws/config 28 | [plugins] 29 | keyring = awscli_keyring 30 | 31 | [default] 32 | keyring = true 33 | ``` 34 | 35 | If you already have credentials, it will use them as defaults, but will not remove them from your configuration. We encourage you to rotate your credentials, put the new ones in keyring and remove the old ones. 36 | 37 | You can also add keyring credentials for different profiles: 38 | 39 | ``` 40 | $ aws --profile work keyring add 41 | AWS Access Key ID [None]: ... 42 | AWS Secret Acess Key [None]: ... 43 | $ aws --profile work ec2 describe-instances 44 | { ... } 45 | ``` 46 | 47 | Which adds the keyring flag to the profile configuration: 48 | 49 | ``` 50 | # ~/.aws/config 51 | # ... 52 | 53 | [profile work] 54 | keyring = true 55 | ``` 56 | 57 | If you need to see the credentials, or use other command line tools that require credentials in your env, use show. Adding `--export` will prefix each line with `export`. 58 | 59 | ``` 60 | $ aws keyring show 61 | AWS_ACCESS_KEY_ID="..." 62 | AWS_SECRET_ACCESS_KEY="..." 63 | 64 | $ eval "$(aws keyring show --export)" 65 | 66 | $ env | grep AWS 67 | AWS_ACCESS_KEY_ID="..." 68 | AWS_SECRET_ACCESS_KEY="..." 69 | 70 | # now do whatever... then close your shell. 71 | ``` 72 | -------------------------------------------------------------------------------- /awscli_keyring/__init__.py: -------------------------------------------------------------------------------- 1 | from . import commands, session 2 | 3 | def awscli_initialize(events): 4 | events.register("building-command-table.main", commands.build_command_table) 5 | events.register("session-initialized", session.initialized) 6 | -------------------------------------------------------------------------------- /awscli_keyring/commands.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | 4 | from botocore.compat import OrderedDict 5 | 6 | from awscli.customizations.commands import BasicCommand 7 | try: 8 | from awscli.customizations.configure.writer import ConfigFileWriter 9 | except ImportError: 10 | from awscli.customizations.configure import ConfigFileWriter 11 | 12 | from . import persistence 13 | 14 | def build_command_table(command_table, session, **kwargs): 15 | command_table["keyring"] = KeyringCommand(session) 16 | 17 | """Add credentials for a profile to keyring""" 18 | class AddCommand(BasicCommand): 19 | NAME = "add" 20 | 21 | DESCRIPTION = "Add credentials for the current profile to keyring" 22 | 23 | SYNOPSIS = "add [secret]" 24 | 25 | ARG_TABLE = [ 26 | {"name": "key", "positional_arg": True, "nargs": "?", "action": "store", "help_text": "AWS_ACCESS_KEY_ID; if omitted you will be asked to enter the secret using a password prompt."}, 27 | {"name": "secret", "positional_arg": True, "nargs": "?", "action": "store", "help_text": "AWS_SECRET_ACCESS_KEY; if omitted you will be asked to enter the secret using a password prompt."}, 28 | ] 29 | 30 | def _run_main(self, parsed_args, parsed_globals): 31 | current_key = None 32 | current_secret = None 33 | masked_current_secret = None 34 | if self._session._credentials: 35 | current_key = self._session._credentials.access_key 36 | current_secret = self._session._credentials.secret_key 37 | if current_secret is not None: 38 | masked_current_secret = "*" * (len(current_secret) - 4) + current_secret[-4:] 39 | 40 | key = parsed_args.key 41 | if key is None: 42 | import getpass 43 | key = getpass.getpass("AWS Access Key ID [%s]: " % current_key) 44 | if key is None or key == "": 45 | key = current_key 46 | 47 | secret = parsed_args.secret 48 | if secret is None: 49 | import getpass 50 | secret = getpass.getpass("AWS Secret Access Key [%s]: " % masked_current_secret) 51 | if secret is None or secret == "": 52 | secret = current_secret 53 | 54 | profile = self._session.profile 55 | if profile is None: 56 | profile = "default" 57 | config_section = "default" 58 | else: 59 | config_section = "profile {0}".format(profile) 60 | 61 | persistence.set_credentials(profile, key, secret) 62 | 63 | config_update = {"__section__": config_section, "keyring": "true"} 64 | config_filename = os.path.expanduser(self._session.get_config_variable("config_file")) 65 | 66 | config_writer = ConfigFileWriter() 67 | config_writer.update_config(config_update, config_filename) 68 | 69 | return 0 70 | 71 | class ShowCommand(BasicCommand): 72 | NAME = "show" 73 | 74 | DESCRIPTION = "Show credentials for current profile like environment variables\n\nUseful for sourcing in a shell or using as a wrapper for command line programs which expect credentials in environment variables." 75 | 76 | ARG_TABLE = [ 77 | {"name": "export", "action": "store_true", "help_text": "Prefix variables with \"export \""}, 78 | ] 79 | 80 | EXAMPLES = "Command::\n\n aws keyring show\n\nOutput::\n\n AWS_ACCESS_KEY_ID=\"ABC...\"\n AWS_SECRET_ACCESS_KEY=\"123...\"" 81 | 82 | def _run_main(self, parsed_args, global_args): 83 | export = "" 84 | if parsed_args.export: 85 | export = "export " 86 | 87 | if self._session._credentials: 88 | print('{export}AWS_ACCESS_KEY_ID="{value}"'.format(export=export, value=self._session._credentials.access_key)) 89 | print('{export}AWS_SECRET_ACCESS_KEY="{value}"'.format(export=export, value=self._session._credentials.secret_key)) 90 | if getattr(self._session._credentials, "token", None) is not None: 91 | print('{export}AWS_SESSION_TOKEN="{value}"'.format(export=export, value=self._session._credentials.token)) 92 | return 0 93 | else: 94 | sys.stderr.write('There are no credentials to show.\n') 95 | return 1 96 | 97 | """Keyring commands""" 98 | class KeyringCommand(BasicCommand): 99 | NAME = "keyring" 100 | 101 | DESCRIPTION = "keyring management" 102 | 103 | SUBCOMMANDS = [ 104 | {"name": "add", "command_class": AddCommand}, 105 | {"name": "show", "command_class": ShowCommand}, 106 | ] 107 | 108 | def _run_main(self, parsed_args, parsed_globals): 109 | self._display_help(parsed_args, parsed_globals) 110 | return 1 111 | -------------------------------------------------------------------------------- /awscli_keyring/persistence.py: -------------------------------------------------------------------------------- 1 | import keyring 2 | 3 | def get_credentials(profile): 4 | key = keyring.get_password("awscli:key", profile) 5 | secret = keyring.get_password("awscli:secret", profile) 6 | 7 | return (key, secret) 8 | 9 | def set_credentials(profile, key, secret): 10 | keyring.set_password("awscli:key", profile, key) 11 | keyring.set_password("awscli:secret", profile, secret) 12 | -------------------------------------------------------------------------------- /awscli_keyring/session.py: -------------------------------------------------------------------------------- 1 | from botocore.exceptions import ConfigNotFound, ProfileNotFound 2 | 3 | from . import persistence 4 | 5 | def cast_bool(value): 6 | return type(value) == type("") and value.lower() in ('1', 'yes', 'true', 'on') 7 | 8 | def initialized(session, **kwargs): 9 | session.session_var_map["keyring"] = ("keyring", None, False, cast_bool) 10 | 11 | try: 12 | if session.get_config_variable("keyring") != False: 13 | if session.profile is not None: 14 | profile = session.profile 15 | else: 16 | profile = "default" 17 | 18 | key, secret = persistence.get_credentials(profile) 19 | 20 | session.set_credentials(key, secret) 21 | 22 | except (ConfigNotFound, ProfileNotFound): 23 | pass 24 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = README.md 3 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | 5 | from setuptools import setup, find_packages 6 | 7 | setup( 8 | name='awscli-keyring', 9 | version='0.1.3', 10 | description='AWS command line credentials from OS X Keychain and other keyrings.', 11 | long_description='AWS command line credentials from OS X Keychain and other keyrings.', 12 | author='Samuel Cochran', 13 | author_email='sj26@sj26.com', 14 | url='https://github.com/sj26/awscli-keyring', 15 | packages=find_packages(exclude=['tests*']), 16 | install_requires=[ 17 | 'awscli', 18 | 'keyring', 19 | ], 20 | license="MIT", 21 | classifiers=[ 22 | 'Development Status :: 2 - Pre-Alpha', 23 | 'Intended Audience :: Developers', 24 | 'Intended Audience :: System Administrators', 25 | 'Natural Language :: English', 26 | 'License :: OSI Approved :: MIT License', 27 | 'Programming Language :: Python', 28 | ], 29 | ) 30 | --------------------------------------------------------------------------------