blob: b2afd71cba8f9590d73f293f4a0afd5bc189ebb0 [file] [log] [blame]
Ara Hayrabedian95bf20d2015-05-31 23:50:49 +04001from __future__ import print_function
2from __future__ import unicode_literals
3
4import argparse
Ara Hayrabedian95bf20d2015-05-31 23:50:49 +04005import os
Ara Hayrabedian02e8bdc2015-06-12 19:20:56 +04006
Anthony Sottile8be6f4c2016-08-22 14:38:49 -07007from six.moves import configparser
Ara Hayrabedian95bf20d2015-05-31 23:50:49 +04008
9
Daniel Roschkab0d4cdb2016-12-30 08:41:24 +010010def get_aws_credential_files_from_env():
11 """Extract credential file paths from environment variables."""
12 files = set()
Daniel Roschka3939aee2017-01-03 19:05:49 +010013 for env_var in (
14 'AWS_CONFIG_FILE', 'AWS_CREDENTIAL_FILE', 'AWS_SHARED_CREDENTIALS_FILE',
Anthony Sottile2a902e02017-07-12 18:35:24 -070015 'BOTO_CONFIG',
Daniel Roschka3939aee2017-01-03 19:05:49 +010016 ):
17 if env_var in os.environ:
Daniel Roschkab0d4cdb2016-12-30 08:41:24 +010018 files.add(os.environ[env_var])
Daniel Roschkab0d4cdb2016-12-30 08:41:24 +010019 return files
20
21
22def get_aws_secrets_from_env():
23 """Extract AWS secrets from environment variables."""
24 keys = set()
Daniel Roschka3939aee2017-01-03 19:05:49 +010025 for env_var in (
Anthony Sottile2a902e02017-07-12 18:35:24 -070026 'AWS_SECRET_ACCESS_KEY', 'AWS_SECURITY_TOKEN', 'AWS_SESSION_TOKEN',
Daniel Roschka3939aee2017-01-03 19:05:49 +010027 ):
28 if env_var in os.environ:
Daniel Roschkab0d4cdb2016-12-30 08:41:24 +010029 keys.add(os.environ[env_var])
Daniel Roschkab0d4cdb2016-12-30 08:41:24 +010030 return keys
31
32
33def get_aws_secrets_from_file(credentials_file):
34 """Extract AWS secrets from configuration files.
35
36 Read an ini-style configuration file and return a set with all found AWS
37 secret access keys.
Ara Hayrabedian95bf20d2015-05-31 23:50:49 +040038 """
39 aws_credentials_file_path = os.path.expanduser(credentials_file)
40 if not os.path.exists(aws_credentials_file_path):
Daniel Roschkab0d4cdb2016-12-30 08:41:24 +010041 return set()
Ara Hayrabedian95bf20d2015-05-31 23:50:49 +040042
Ara Hayrabedian3078aec2015-06-12 16:24:01 +040043 parser = configparser.ConfigParser()
Daniel Roschkab0d4cdb2016-12-30 08:41:24 +010044 try:
45 parser.read(aws_credentials_file_path)
46 except configparser.MissingSectionHeaderError:
47 return set()
Ara Hayrabedian95bf20d2015-05-31 23:50:49 +040048
49 keys = set()
50 for section in parser.sections():
Daniel Roschka3939aee2017-01-03 19:05:49 +010051 for var in (
52 'aws_secret_access_key', 'aws_security_token',
Anthony Sottile2a902e02017-07-12 18:35:24 -070053 'aws_session_token',
Daniel Roschka3939aee2017-01-03 19:05:49 +010054 ):
Daniel Roschkab0d4cdb2016-12-30 08:41:24 +010055 try:
56 keys.add(parser.get(section, var))
57 except configparser.NoOptionError:
58 pass
Ara Hayrabedian95bf20d2015-05-31 23:50:49 +040059 return keys
60
61
Dean Wilsona6665272015-10-28 05:13:37 +000062def check_file_for_aws_keys(filenames, keys):
Daniel Roschkab0d4cdb2016-12-30 08:41:24 +010063 """Check if files contain AWS secrets.
64
65 Return a list of all files containing AWS secrets and keys found, with all
66 but the first four characters obfuscated to ease debugging.
67 """
Dean Wilsona6665272015-10-28 05:13:37 +000068 bad_files = []
69
70 for filename in filenames:
71 with open(filename, 'r') as content:
72 text_body = content.read()
Daniel Roschkab0d4cdb2016-12-30 08:41:24 +010073 for key in keys:
74 # naively match the entire file, low chance of incorrect
75 # collision
76 if key in text_body:
Anthony Sottile5da199b2017-01-03 13:13:44 -080077 bad_files.append({
78 'filename': filename, 'key': key[:4] + '*' * 28,
79 })
Dean Wilsona6665272015-10-28 05:13:37 +000080 return bad_files
Ara Hayrabedian95bf20d2015-05-31 23:50:49 +040081
82
83def main(argv=None):
84 parser = argparse.ArgumentParser()
Daniel Roschkab0d4cdb2016-12-30 08:41:24 +010085 parser.add_argument('filenames', nargs='+', help='Filenames to run')
Ara Hayrabedian95bf20d2015-05-31 23:50:49 +040086 parser.add_argument(
Anthony Sottiled444ab82016-02-08 17:05:39 -080087 '--credentials-file',
Daniel Roschkab0d4cdb2016-12-30 08:41:24 +010088 dest='credential_files',
89 action='append',
Anthony Sottile5da199b2017-01-03 13:13:44 -080090 default=[
91 '~/.aws/config', '~/.aws/credentials', '/etc/boto.cfg', '~/.boto',
92 ],
Anthony Sottiled444ab82016-02-08 17:05:39 -080093 help=(
Daniel Roschkab0d4cdb2016-12-30 08:41:24 +010094 'Location of additional AWS credential files from which to get '
95 'secret keys from'
Anthony Sottile2a902e02017-07-12 18:35:24 -070096 ),
Ara Hayrabedian95bf20d2015-05-31 23:50:49 +040097 )
Mike Fiedler312e7212017-02-10 08:26:26 -050098 parser.add_argument(
99 '--allow-missing-credentials',
100 dest='allow_missing_credentials',
101 action='store_true',
Anthony Sottile2a902e02017-07-12 18:35:24 -0700102 help='Allow hook to pass when no credentials are detected.',
Mike Fiedler312e7212017-02-10 08:26:26 -0500103 )
Ara Hayrabedian95bf20d2015-05-31 23:50:49 +0400104 args = parser.parse_args(argv)
Daniel Roschkab0d4cdb2016-12-30 08:41:24 +0100105
106 credential_files = set(args.credential_files)
107
108 # Add the credentials files configured via environment variables to the set
109 # of files to to gather AWS secrets from.
110 credential_files |= get_aws_credential_files_from_env()
111
112 keys = set()
113 for credential_file in credential_files:
114 keys |= get_aws_secrets_from_file(credential_file)
115
116 # Secrets might be part of environment variables, so add such secrets to
117 # the set of keys.
118 keys |= get_aws_secrets_from_env()
119
Mike Fiedler312e7212017-02-10 08:26:26 -0500120 if not keys and args.allow_missing_credentials:
121 return 0
122
Ara Hayrabedian02e8bdc2015-06-12 19:20:56 +0400123 if not keys:
Daniel Roschka3939aee2017-01-03 19:05:49 +0100124 print(
125 'No AWS keys were found in the configured credential files and '
126 'environment variables.\nPlease ensure you have the correct '
Anthony Sottile2a902e02017-07-12 18:35:24 -0700127 'setting for --credentials-file',
Daniel Roschka3939aee2017-01-03 19:05:49 +0100128 )
Ara Hayrabedian02e8bdc2015-06-12 19:20:56 +0400129 return 2
Ara Hayrabedian95bf20d2015-05-31 23:50:49 +0400130
Dean Wilsona6665272015-10-28 05:13:37 +0000131 bad_filenames = check_file_for_aws_keys(args.filenames, keys)
132 if bad_filenames:
133 for bad_file in bad_filenames:
Daniel Roschka3939aee2017-01-03 19:05:49 +0100134 print('AWS secret found in {filename}: {key}'.format(**bad_file))
Dean Wilsona6665272015-10-28 05:13:37 +0000135 return 1
136 else:
137 return 0
Ara Hayrabedian95bf20d2015-05-31 23:50:49 +0400138
Anthony Sottile70e405e2016-11-30 09:56:42 -0800139
Ara Hayrabedian95bf20d2015-05-31 23:50:49 +0400140if __name__ == '__main__':
141 exit(main())