aws_cli_config_parser 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: cd3589f780327c218d9bc8bf20273c00ad3730999d1484083282eacb9eb3b94b
4
+ data.tar.gz: 49bab64edb2769d217642b212a5267e6b7c8b3a89502ebbcf1f4f7a9f2174dfb
5
+ SHA512:
6
+ metadata.gz: bb59817770089eb5c3ec25c6e31935af3f9ba6ca6b9b06b9075ad0eda4c9b9cb31a6713c7fc48bd57990b927b347c8c2fbb98746b4c41494fd3ad7a729c6ed6a
7
+ data.tar.gz: b1f0c10fa51f5697a589ee9428248e10b4a609e3aaffb41be9e268b2f1b07cb9bf3e25fd3efc6bdb0268a78a3e94f1610734261098eeb1914040d164fbece32f
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ pkg/
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2021-05-08
4
+
5
+ - First release: parses all files in the AWS CLI configuration folder and merges information.
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in aws_cli_config_parser.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+
10
+ gem "minitest", "~> 5.0"
data/Gemfile.lock ADDED
@@ -0,0 +1,21 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ aws_cli_config_parser (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ minitest (5.14.4)
10
+ rake (13.0.3)
11
+
12
+ PLATFORMS
13
+ x86_64-linux-musl
14
+
15
+ DEPENDENCIES
16
+ aws_cli_config_parser!
17
+ minitest (~> 5.0)
18
+ rake (~> 13.0)
19
+
20
+ BUNDLED WITH
21
+ 2.2.16
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 TODO: Write your name
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.
data/README.md ADDED
@@ -0,0 +1,134 @@
1
+ # AWS CLI Configuration Parser
2
+
3
+ This Ruby gem provides a tool to parse profile settings and secrets from AWS CLI configuration files, including the cached credentials from STS AssumeRole calls. This is often useful when using the AWS CLI with roles that require an MFA code. After authenticating successfully with an MFA code temporary session credentials are cached in your `~/.aws` folder. You'll often need to pass these temporary credentials to other tools such as Docker containers. This gem parses the files in your `~/.aws` folder and merges all information allowing you to retrieve any credential or setting.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'aws_cli_config_parser'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle install
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install aws_cli_config_parser
20
+
21
+
22
+ ## Usage
23
+
24
+ With a file tree like this:
25
+
26
+ ```
27
+ ~/.aws/
28
+ ├── cli
29
+ │   └── cache
30
+ │   ├── 1a2b3c4d5etc.json
31
+ ├── config
32
+ └── credentials
33
+ ```
34
+
35
+ **~/.aws/config**
36
+ ```
37
+ [default]
38
+ region = eu-west-1
39
+
40
+ [profile admin]
41
+ role_arn = arn:aws:iam::222200002222:role/SomeRole
42
+ source_profile = default
43
+ role_session_name = session_name
44
+ region = eu-central-1
45
+ ```
46
+
47
+ **~/.aws/credentials**
48
+ ```
49
+ [default]
50
+ aws_access_key_id = AKID1111000011110000
51
+ aws_secret_access_key = SECRET1111000011110000111100001111000011
52
+ ```
53
+
54
+ **~/.aws/cli/cache/1a2b3c4d5etc.json**
55
+ ```
56
+ {
57
+ "Credentials": {
58
+ "AccessKeyId": "AKID2222000022220000",
59
+ "SecretAccessKey": "SECRET2222000022220000222200002222000022",
60
+ "SessionToken": "SESSIONTOKEN222200002222000022220000222200002222000022220000etc",
61
+ "Expiration": "<some timestamp in the future>"
62
+ },
63
+ "AssumedRoleUser": {
64
+ "AssumedRoleId": "ARLID2222000022220000:session_name",
65
+ "Arn": "arn:aws:sts::222200002222:assumed-role/SomeRole/session_name"
66
+ },
67
+ ...
68
+ }
69
+ ```
70
+
71
+ You can obtain any individual configuration value like this:
72
+
73
+ ```ruby
74
+ profiles = AwsCliConfigParser.parse
75
+ # => #<AwsCliConfigParser::Profiles:0x000055b0526261e8>
76
+
77
+ default = profiles.get('default')
78
+ # => #<AwsCliConfigParser::Profile:0x000055b052654ea8>
79
+
80
+ default.get('region')
81
+ # => "eu-west-1"
82
+ default.get('aws_access_key_id')
83
+ # => "AKID1111000011110000"
84
+ default.get('aws_secret_access_key')
85
+ # => "SECRET1111000011110000111100001111000011"
86
+
87
+ admin = profiles.get('admin')
88
+ # => #<AwsCliConfigParser::Profile:0x000055b052644b98>
89
+
90
+ admin.get('region')
91
+ # => "eu-central-1"
92
+ admin.get('role_arn')
93
+ # => "arn:aws:iam::222200002222:role/SomeRole"
94
+ admin.get('aws_access_key_id')
95
+ # => "AKID2222000022220000"
96
+ admin.get('aws_secret_access_key')
97
+ # => "SECRET2222000022220000222200002222000022"
98
+ admin.get('aws_session_token')
99
+ # => "SESSIONTOKEN222200002222000022220000222200002222000022220000etc"
100
+ ```
101
+
102
+ Or if you prefer using hashes:
103
+
104
+ ```ruby
105
+ AwsCliConfigParser.parse.to_h == {
106
+ 'default' => {
107
+ 'region' => 'eu-west-1',
108
+ 'aws_access_key_id' => 'AKID1111000011110000',
109
+ 'aws_secret_access_key' => 'SECRET1111000011110000111100001111000011'
110
+ },
111
+ 'admin' => {
112
+ 'region' => 'eu-central-1',
113
+ 'role_arn' => 'arn:aws:iam::222200002222:role/SomeRole',
114
+ 'source_profile' => 'default',
115
+ 'role_session_name' => 'session_name',
116
+ 'aws_access_key_id' => 'AKID2222000022220000',
117
+ 'aws_secret_access_key' => 'SECRET2222000022220000222200002222000022',
118
+ 'aws_session_token' => 'SESSIONTOKEN222200002222000022220000222200002222000022220000etc',
119
+ }
120
+ }
121
+ # => true
122
+ ```
123
+
124
+ If you have your AWS CLI configuration directory somewhere other than the default you can tell the parser where to look for it:
125
+
126
+ ```ruby
127
+ AwsCliConfigParser.parse(aws_directory: '/somewhere/else/.my-aws-folder')
128
+ # => ...
129
+ ```
130
+
131
+
132
+ ## License
133
+
134
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList["test/**/*_test.rb"]
10
+ end
11
+
12
+ task default: :test
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/aws_cli_config_parser/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'aws_cli_config_parser'
7
+ spec.version = AwsCliConfigParser::VERSION
8
+ spec.authors = ['brunze']
9
+
10
+ spec.summary = 'Parses profile settings from AWS CLI configuration files.'
11
+ spec.description = <<~DESCRIPTION.strip
12
+ Parses profile settings and secrets from AWS CLI configuration files,
13
+ including the cached credentials from AssumeRole such as when using MFA.
14
+ DESCRIPTION
15
+ spec.homepage = 'https://github.com/brunze/aws_cli_config_parser'
16
+ spec.license = 'MIT'
17
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.3.0')
18
+
19
+ spec.metadata['homepage_uri'] = spec.homepage
20
+ spec.metadata['source_code_uri'] = spec.homepage
21
+ spec.metadata['changelog_uri'] = 'https://github.com/brunze/aws_cli_config_parser/CHANGELOG.md'
22
+
23
+ # Specify which files should be added to the gem when it is released.
24
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
25
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
26
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
27
+ end
28
+ spec.bindir = 'bin'
29
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
30
+ spec.require_paths = ['lib']
31
+
32
+ # Uncomment to register a new dependency of your gem
33
+ # spec.add_dependency 'example-gem', '~> 1.0'
34
+
35
+ # For more information and examples about making a new gem, checkout our
36
+ # guide at: https://bundler.io/guides/creating_gem.html
37
+ end
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "aws_cli_config_parser"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require "irb"
15
+ IRB.start(__FILE__)
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AwsCliConfigParser; end
4
+
5
+ require 'aws_cli_config_parser/version'
6
+ require 'aws_cli_config_parser/profiles'
7
+ require 'aws_cli_config_parser/cached_credential'
8
+ require 'pathname'
9
+
10
+ module AwsCliConfigParser
11
+ using Refined::Arrays
12
+
13
+ def self.parse(
14
+ aws_directory: '~/.aws',
15
+ config_file_name: 'config',
16
+ credentials_file_name: 'credentials',
17
+ cli_cache_directory: './cli/cache',
18
+ cached_credential_file_name_pattern: /\A\h{40}\.json\z/,
19
+ now: lambda{ Time.now.utc }
20
+ )
21
+ aws_directory = Pathname(aws_directory).expand_path.realpath
22
+
23
+ config_files = [
24
+ aws_directory.join(config_file_name),
25
+ aws_directory.join(credentials_file_name),
26
+ ]
27
+ .select{ |path| path.file? && path.readable_real? }
28
+
29
+ cli_cache_directory = aws_directory.join(cli_cache_directory)
30
+
31
+ cached_credential_files = if cli_cache_directory.directory? && cli_cache_directory.readable_real?
32
+ cli_cache_directory
33
+ .each_child
34
+ .select{ |path| path.basename.to_s.match(cached_credential_file_name_pattern) }
35
+ .select{ |path| path.file? && path.readable_real? }
36
+ else
37
+ []
38
+ end
39
+
40
+ parse_files(
41
+ configs: config_files,
42
+ cached_credentials: cached_credential_files,
43
+ now: now,
44
+ )
45
+ end
46
+
47
+ def self.parse_files configs: [], cached_credentials: [], now: lambda{ Time.now.utc }
48
+ configs
49
+ .map(&Profiles.method(:from_io))
50
+ .reduce(:merge!)
51
+ &.merge_credentials!(
52
+ cached_credentials
53
+ .map(&CachedCredential.method(:from_io))
54
+ .reject{ |credential| credential.expired?(now: now) }
55
+ ) || []
56
+ end
57
+
58
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'date'
5
+
6
+ class AwsCliConfigParser::CachedCredential
7
+
8
+ def initialize assumed_role, expiration_date, configuration
9
+ @assumed_role = assumed_role or raise TypeError
10
+ @expiration_date = expiration_date.to_time
11
+ @configuration = configuration.to_hash
12
+ end
13
+
14
+ attr_reader :assumed_role, :expiration_date, :configuration
15
+
16
+ def self.from_io io, parse_json: JSON.method(:parse), parse_date: DateTime.method(:parse)
17
+ json = parse_json[io.read]
18
+
19
+ assumed_role = json.dig('AssumedRoleUser', 'Arn') or return nil
20
+ assumed_role = assumed_role.match(%r|arn:aws:sts:\w*:(\d{12}):assumed-role/(.+?)/?\w*/?$|)&.captures or return nil
21
+
22
+ expiration_date = json.dig('Credentials', 'Expiration') or return nil
23
+ expiration_date = parse_date[expiration_date]
24
+
25
+ new(
26
+ assumed_role,
27
+ expiration_date,
28
+ 'aws_access_key_id' => json.dig('Credentials', 'AccessKeyId'),
29
+ 'aws_secret_access_key' => json.dig('Credentials', 'SecretAccessKey'),
30
+ 'aws_session_token' => json.dig('Credentials', 'SessionToken'),
31
+ )
32
+ end
33
+
34
+ def expired? now: lambda{ Time.now.utc }
35
+ @expiration_date <= now.call
36
+ end
37
+
38
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AwsCliConfigParser::Profile
4
+
5
+ def initialize name, configuration
6
+ @name = name.to_str ; raise ArgumentError if @name.empty?
7
+ @configuration = configuration
8
+ end
9
+
10
+ attr_reader :name, :configuration
11
+
12
+ def role
13
+ @configuration['role_arn'].to_s.match(%r|arn:aws:iam:\w*:(\d{12}):role/(.+?)/?$|)&.captures
14
+ end
15
+
16
+ def merge! other
17
+ raise TypeError unless other.is_a?(self.class)
18
+
19
+ @configuration.merge!(other.configuration)
20
+
21
+ self
22
+ end
23
+
24
+ def merge_credential! credential
25
+ @configuration.merge!(credential.configuration)
26
+ end
27
+
28
+ def get key
29
+ @configuration[key]
30
+ end
31
+
32
+ def to_h
33
+ @configuration.to_h
34
+ end
35
+
36
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'aws_cli_config_parser/profile'
4
+ require 'aws_cli_config_parser/refined/arrays'
5
+
6
+ class AwsCliConfigParser::Profiles
7
+ using ::AwsCliConfigParser::Refined::Arrays
8
+
9
+ def initialize profiles
10
+ @profiles = profiles.each.to_a or raise TypeError
11
+ end
12
+
13
+ def self.from_io io
14
+ new(
15
+ io.each_line.with_object([]) do |line, profiles|
16
+ case line
17
+ when /^ *\[(?:profile +)?(.+)\] *\n$/ then $1.strip! ; ; profiles.push({ name: $1 })
18
+ when /^ *(\w+) *= *(.+) *\n?$/ then $1.strip! ; $2.strip! ; profiles.last.store($1, $2)
19
+ else next
20
+ end
21
+ end
22
+ .map do |pairs|
23
+ ::AwsCliConfigParser::Profile.new(pairs.delete(:name), pairs)
24
+ end
25
+ )
26
+ end
27
+
28
+ def merge! other
29
+ raise TypeError unless other.is_a?(self.class)
30
+
31
+ @profiles = [
32
+ *@profiles,
33
+ *other.instance_variable_get(:@profiles),
34
+ ]
35
+ .group_by(&:name)
36
+ .map do |(_name, profiles)|
37
+ profiles.reduce(:merge!)
38
+ end
39
+
40
+ self
41
+ end
42
+
43
+ def merge_credentials! credentials
44
+ credentials = credentials.to_a.index_by(&:assumed_role)
45
+
46
+ @profiles.each do |profile|
47
+ profile.merge_credential!(credentials[profile.role]) if credentials.has_key?(profile.role)
48
+ end
49
+
50
+ self
51
+ end
52
+
53
+ def get name
54
+ @profiles.find{ |profile| profile.name == name }
55
+ end
56
+
57
+ def to_h
58
+ @profiles.map{ |profile| [profile.name, profile.to_h] }.to_h
59
+ end
60
+
61
+ end
@@ -0,0 +1,13 @@
1
+ module AwsCliConfigParser
2
+ module Refined
3
+ module Arrays
4
+ refine Array do
5
+
6
+ def index_by
7
+ each_with_object({}){ |value, index| index[yield(value)] = value }
8
+ end
9
+
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AwsCliConfigParser
4
+ VERSION = "0.1.0"
5
+ end
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: aws_cli_config_parser
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - brunze
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-05-08 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: |-
14
+ Parses profile settings and secrets from AWS CLI configuration files,
15
+ including the cached credentials from AssumeRole such as when using MFA.
16
+ email:
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - ".gitignore"
22
+ - CHANGELOG.md
23
+ - Gemfile
24
+ - Gemfile.lock
25
+ - LICENSE.txt
26
+ - README.md
27
+ - Rakefile
28
+ - aws_cli_config_parser.gemspec
29
+ - bin/console
30
+ - lib/aws_cli_config_parser.rb
31
+ - lib/aws_cli_config_parser/cached_credential.rb
32
+ - lib/aws_cli_config_parser/profile.rb
33
+ - lib/aws_cli_config_parser/profiles.rb
34
+ - lib/aws_cli_config_parser/refined/arrays.rb
35
+ - lib/aws_cli_config_parser/version.rb
36
+ homepage: https://github.com/brunze/aws_cli_config_parser
37
+ licenses:
38
+ - MIT
39
+ metadata:
40
+ homepage_uri: https://github.com/brunze/aws_cli_config_parser
41
+ source_code_uri: https://github.com/brunze/aws_cli_config_parser
42
+ changelog_uri: https://github.com/brunze/aws_cli_config_parser/CHANGELOG.md
43
+ post_install_message:
44
+ rdoc_options: []
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: 2.3.0
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ requirements: []
58
+ rubygems_version: 3.2.17
59
+ signing_key:
60
+ specification_version: 4
61
+ summary: Parses profile settings from AWS CLI configuration files.
62
+ test_files: []