subiam 1.2.1

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
+ SHA1:
3
+ metadata.gz: 5f32814fa2db9f3e157767104fe4da0752b40120
4
+ data.tar.gz: 677bf6d1e8fdbf6b0a9e13e7527add9ad8f8b69d
5
+ SHA512:
6
+ metadata.gz: e3cc7e53244ab488c971c4f7ff583db303889cb76e951e2254174642e3a16d9663f7a009569e595101b397293fa4e9e3835236b2c055f2d4880f9f17c6ddd5f9
7
+ data.tar.gz: 6c74f7af2951620201979b7f16551724c95c8df036b47f76f8896ad0845310c7beb257b6c8ca1ad214c5f8da9017efab3989486bec2208800d9d51b8e20f3f00
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ test.rb
16
+ IAMfile
17
+ *.iam
18
+ account.csv
19
+ *.json
data/.rspec ADDED
@@ -0,0 +1,4 @@
1
+ --require rspec/instafail
2
+ --format RSpec::Instafail
3
+ --colour
4
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in subiam.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,48 @@
1
+ Copyright (c) 2016 GREE, Inc.
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ Subiam is forked from Miam, which is published with the same license.
25
+ Here is the original copyright notice for Miam:
26
+
27
+ Copyright (c) 2014 Genki Sugawara
28
+
29
+ MIT License
30
+
31
+ Permission is hereby granted, free of charge, to any person obtaining
32
+ a copy of this software and associated documentation files (the
33
+ "Software"), to deal in the Software without restriction, including
34
+ without limitation the rights to use, copy, modify, merge, publish,
35
+ distribute, sublicense, and/or sell copies of the Software, and to
36
+ permit persons to whom the Software is furnished to do so, subject to
37
+ the following conditions:
38
+
39
+ The above copyright notice and this permission notice shall be
40
+ included in all copies or substantial portions of the Software.
41
+
42
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
43
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
44
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
45
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
46
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
47
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
48
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,408 @@
1
+ # Subiam
2
+
3
+ Subiam is a tool to manage IAM.
4
+
5
+ It defines the state of IAM using DSL, and updates IAM according to DSL.
6
+
7
+ It's forked from Miam. Miam is designed to manage all IAM entities in the AWS account. Subiam is not so. Subiam is designed to manage sub part of IAM entities in the AWS account. For example around MySQL instances / around web servers / around lambda functions / around monitoring systems.
8
+
9
+ **Notice**
10
+ * `>= 1.2.0`
11
+ * Add helper methods: `arn_policy_by_aws`, `arn_policy_by_current_account`
12
+
13
+ * `>= 1.1.0`
14
+ * Rename `require` DSL command to `import` to avoid override Kernel#require
15
+ * Allow Symbols alternative to Strings at Hash keys. It's a bit easy to write!
16
+
17
+ * `>= 1.0.0`
18
+ * Forked from miam
19
+ * Required to specify `target` in DSL or json
20
+ * `instance_profile` also follow target (bug fix)
21
+ * don't delete top level entity (user, group, role, instance_profile) by default. Use the `--enable-delete` option.
22
+
23
+ ## Installation
24
+
25
+ Add this line to your application's Gemfile:
26
+
27
+ ```ruby
28
+ gem 'subiam'
29
+ ```
30
+
31
+ And then execute:
32
+
33
+ $ bundle
34
+
35
+ Or install it yourself as:
36
+
37
+ $ gem install subiam
38
+
39
+ ## Usage
40
+
41
+ ```sh
42
+ export AWS_ACCESS_KEY_ID='...'
43
+ export AWS_SECRET_ACCESS_KEY='...'
44
+ export AWS_REGION='us-east-1'
45
+ vi subiam-xxx.rb
46
+ subiam -a --dry-run subiam-xxx.rb
47
+ subiam -a subiam-xxx.rb
48
+ ```
49
+
50
+ ## Help
51
+
52
+ ```
53
+ Usage: subiam [options]
54
+ -p, --profile PROFILE_NAME
55
+ --credentials-path PATH
56
+ -k, --access-key ACCESS_KEY
57
+ -s, --secret-key SECRET_KEY
58
+ -r, --region REGION
59
+ -a, --apply
60
+ -f, --file FILE
61
+ --dry-run
62
+ --account-output FILE
63
+ -e, --export
64
+ -o, --output FILE
65
+ --split
66
+ --split-more
67
+ --format=FORMAT
68
+ --export-concurrency N
69
+ --ignore-login-profile
70
+ --no-color
71
+ --no-progress
72
+ --debug
73
+ --enable-delete
74
+ ```
75
+
76
+ ## IAM definition files example
77
+ subiam_mytool.rb
78
+
79
+ ```ruby
80
+ import 'subiam_ec2_assume_role_attrs.rb'
81
+
82
+ target /^mytool/ # required!!!
83
+
84
+ role 'mytool', path: '/' do
85
+ context.version = '2012-10-17'
86
+
87
+ include_template 'ec2-assume-role-attrs'
88
+
89
+ instance_profiles(
90
+ 'mytool'
91
+ )
92
+
93
+ policy 'mytool-role-policy' do
94
+ {
95
+ Version: context.version,
96
+ Statement: [
97
+ {
98
+ Effect: "Allow",
99
+ Action: [
100
+ "ec2:DescribeInstances",
101
+ "ec2:DescribeVpcs"
102
+ ],
103
+ Resource: [
104
+ "*"
105
+ ]
106
+ },
107
+ {
108
+ Effect: "Allow",
109
+ Action: [
110
+ "route53:Get*",
111
+ "route53:List*",
112
+ "route53:ChangeResourceRecordSets*"
113
+ ],
114
+ Resource: [
115
+ "*"
116
+ ]
117
+ },
118
+ ],
119
+ }
120
+ end
121
+ end
122
+
123
+ instance_profile 'mytool', path: '/'
124
+
125
+ ```
126
+
127
+ subiam_ec2_assume_role_attrs.rb
128
+
129
+ ```ruby
130
+ template "ec2-assume-role-attrs" do
131
+ assume_role_policy_document do
132
+ {
133
+ Version: context.version,
134
+ Statement: [
135
+ {
136
+ Sid: "",
137
+ Effect: "Allow",
138
+ Principal: {Service: "ec2.amazonaws.com"},
139
+ Action: "sts:AssumeRole",
140
+ },
141
+ ],
142
+ }
143
+ end
144
+ end
145
+ ```
146
+
147
+
148
+ ## Use management policy
149
+
150
+ ```ruby
151
+ user "foo", path: '/' do
152
+ attached_managed_policies(
153
+ 'arn:aws:iam::0123456789:policy/MyPolicy',
154
+
155
+ arn_policy_by_current_account("MyPolicy2"),
156
+ # == "arn:aws:iam::0123456789:policy/MyPolicy2'
157
+
158
+ arn_policy_by_aws("AdministratorAccess")
159
+ # == 'arn:aws:iam::aws:policy/AdministratorAccess'
160
+ )
161
+ end
162
+ ```
163
+
164
+
165
+ ---
166
+ old examples (but works if add `target`)
167
+
168
+ ```ruby
169
+ import 'other/iamfile'
170
+
171
+ target /^monitoring-/
172
+
173
+ user "monitoring-bob", :path => "/monitoring-user/" do
174
+ login_profile :password_reset_required=>true
175
+
176
+ groups(
177
+ "Admin"
178
+ )
179
+
180
+ policy "bob-policy" do
181
+ {"Version"=>"2012-10-17",
182
+ "Statement"=>
183
+ [{"Action"=>
184
+ ["s3:Get*",
185
+ "s3:List*"],
186
+ "Effect"=>"Allow",
187
+ "Resource"=>"*"}]}
188
+ end
189
+
190
+ attached_managed_policies(
191
+ # attached_managed_policy
192
+ )
193
+ end
194
+
195
+ user "mary", :path => "/staff/" do
196
+ # login_profile :password_reset_required=>true
197
+
198
+ groups(
199
+ # no group
200
+ )
201
+
202
+ policy "s3-readonly" do
203
+ {"Version"=>"2012-10-17",
204
+ "Statement"=>
205
+ [{"Action"=>
206
+ ["s3:Get*",
207
+ "s3:List*"],
208
+ "Effect"=>"Allow",
209
+ "Resource"=>"*"}]}
210
+ end
211
+
212
+ policy "route53-readonly" do
213
+ {"Version"=>"2012-10-17",
214
+ "Statement"=>
215
+ [{"Action"=>
216
+ ["route53:Get*",
217
+ "route53:List*"],
218
+ "Effect"=>"Allow",
219
+ "Resource"=>"*"}]}
220
+ end
221
+
222
+ attached_managed_policies(
223
+ "arn:aws:iam::aws:policy/AdministratorAccess",
224
+ "arn:aws:iam::123456789012:policy/my_policy"
225
+ )
226
+ end
227
+
228
+ group "Admin", :path => "/admin/" do
229
+ policy "Admin" do
230
+ {"Statement"=>[{"Effect"=>"Allow", "Action"=>"*", "Resource"=>"*"}]}
231
+ end
232
+ end
233
+
234
+ role "S3", :path => "/" do
235
+ instance_profiles(
236
+ "S3"
237
+ )
238
+
239
+ assume_role_policy_document do
240
+ {"Version"=>"2012-10-17",
241
+ "Statement"=>
242
+ [{"Sid"=>"",
243
+ "Effect"=>"Allow",
244
+ "Principal"=>{"Service"=>"ec2.amazonaws.com"},
245
+ "Action"=>"sts:AssumeRole"}]}
246
+ end
247
+
248
+ policy "S3-role-policy" do
249
+ {"Version"=>"2012-10-17",
250
+ "Statement"=>[{"Effect"=>"Allow", "Action"=>"*", "Resource"=>"*"}]}
251
+ end
252
+ end
253
+
254
+ instance_profile "S3", :path => "/"
255
+ ```
256
+
257
+ ## Rename
258
+
259
+ ```ruby
260
+ import 'other/iamfile'
261
+
262
+ user "bob2", :path => "/developer/", :renamed_from => "bob" do
263
+ # ...
264
+ end
265
+
266
+ group "Admin2", :path => "/admin/". :renamed_from => "Admin" do
267
+ # ...
268
+ end
269
+ ```
270
+
271
+ ## Managed Policy attach/detach
272
+
273
+ ```ruby
274
+ user "bob", :path => "/developer/" do
275
+ login_profile :password_reset_required=>true
276
+
277
+ groups(
278
+ "Admin"
279
+ )
280
+
281
+ policy "bob-policy" do
282
+ # ...
283
+ end
284
+
285
+ attached_managed_policies(
286
+ "arn:aws:iam::aws:policy/AmazonElastiCacheReadOnlyAccess"
287
+ )
288
+ end
289
+ ```
290
+
291
+ ## Custom Managed Policy
292
+
293
+ ```ruby
294
+ managed_policy "my-policy", :path=>"/" do
295
+ {"Version"=>"2012-10-17",
296
+ "Statement"=>
297
+ [{"Effect"=>"Allow", "Action"=>"directconnect:Describe*", "Resource"=>"*"}]}
298
+ end
299
+
300
+ user "bob", :path => "/developer/" do
301
+ login_profile :password_reset_required=>true
302
+
303
+ groups(
304
+ "Admin"
305
+ )
306
+
307
+ policy "bob-policy" do
308
+ # ...
309
+ end
310
+
311
+ attached_managed_policies(
312
+ "arn:aws:iam::123456789012:policy/my-policy"
313
+ )
314
+ end
315
+ ```
316
+
317
+ ## Use JSON
318
+
319
+ ```sh
320
+ $ subiam -e -o iam.json
321
+ ᗧ 100%
322
+ Export IAM to `iam.json`
323
+
324
+ $ cat iam.json
325
+ {
326
+ "users": {
327
+ "bob": {
328
+ "path": "/",
329
+ "groups": [
330
+ "Admin"
331
+ ],
332
+ "policies": {
333
+ ...
334
+
335
+ $ subiam -a -f iam.json --dry-run
336
+ Apply `iam.json` to IAM (dry-run)
337
+ ᗧ 100%
338
+ No change
339
+ ```
340
+
341
+ ## Use Template
342
+
343
+ ```ruby
344
+ template "common-policy" do
345
+ policy "my-policy" do
346
+ {"Version"=>context.version,
347
+ "Statement"=>
348
+ [{"Action"=>
349
+ ["s3:Get*",
350
+ "s3:List*"],
351
+ "Effect"=>"Allow",
352
+ "Resource"=>"*"}]}
353
+ end
354
+ end
355
+
356
+ template "common-role-attrs" do
357
+ assume_role_policy_document do
358
+ {"Version"=>context.version,
359
+ "Statement"=>
360
+ [{"Sid"=>"",
361
+ "Effect"=>"Allow",
362
+ "Principal"=>{"Service"=>"ec2.amazonaws.com"},
363
+ "Action"=>"sts:AssumeRole"}]}
364
+ end
365
+ end
366
+
367
+ user "bob", :path => "/developer/" do
368
+ login_profile :password_reset_required=>true
369
+
370
+ groups(
371
+ "Admin"
372
+ )
373
+
374
+ include_template "common-policy", version: "2012-10-17"
375
+ end
376
+
377
+ user "mary", :path => "/staff/" do
378
+ # login_profile :password_reset_required=>true
379
+
380
+ groups(
381
+ # no group
382
+ )
383
+
384
+ context.version = "2012-10-17"
385
+ include_template "common-policy"
386
+
387
+ attached_managed_policies(
388
+ "arn:aws:iam::aws:policy/AdministratorAccess",
389
+ "arn:aws:iam::123456789012:policy/my_policy"
390
+ )
391
+ end
392
+
393
+ role "S3", :path => "/" do
394
+ instance_profiles(
395
+ "S3"
396
+ )
397
+
398
+ include_template "common-role-attrs"
399
+
400
+ policy "S3-role-policy" do
401
+ {"Version"=>"2012-10-17",
402
+ "Statement"=>[{"Effect"=>"Allow", "Action"=>"*", "Resource"=>"*"}]}
403
+ end
404
+ end
405
+ ```
406
+
407
+ ## Similar tools
408
+ * [Codenize.tools](http://codenize.tools/)
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new('spec')
5
+ task :default => :spec
data/bin/subiam ADDED
@@ -0,0 +1,189 @@
1
+ #!/usr/bin/env ruby
2
+ $: << File.expand_path("#{File.dirname __FILE__}/../lib")
3
+ require 'rubygems'
4
+ require 'subiam'
5
+ require 'fileutils'
6
+ require 'optparse'
7
+
8
+ Version = Subiam::VERSION
9
+ DEFAULT_FILENAME = 'IAMfile'
10
+
11
+ mode = nil
12
+ file = DEFAULT_FILENAME
13
+ output_file = '-'
14
+ account_output = 'account.csv'
15
+ split = false
16
+ MAGIC_COMMENT = <<-EOS
17
+ # -*- mode: ruby -*-
18
+ # vi: set ft=ruby :
19
+ EOS
20
+
21
+ options = {
22
+ :dry_run => false,
23
+ :format => :ruby,
24
+ :color => true,
25
+ :debug => false,
26
+ }
27
+
28
+ options[:password_manager] = Subiam::PasswordManager.new(account_output, options)
29
+
30
+ ARGV.options do |opt|
31
+ begin
32
+ access_key = nil
33
+ secret_key = nil
34
+ region = nil
35
+ profile_name = nil
36
+ credentials_path = nil
37
+ format_passed = false
38
+
39
+ opt.on('-p', '--profile PROFILE_NAME') {|v| profile_name = v }
40
+ opt.on('' , '--credentials-path PATH') {|v| credentials_path = v }
41
+ opt.on('-k', '--access-key ACCESS_KEY') {|v| access_key = v }
42
+ opt.on('-s', '--secret-key SECRET_KEY') {|v| secret_key = v }
43
+ opt.on('-r', '--region REGION') {|v| region = v }
44
+ opt.on('-a', '--apply') { mode = :apply }
45
+ opt.on('-f', '--file FILE') {|v| file = v }
46
+ opt.on('' , '--dry-run') { options[:dry_run] = true }
47
+ opt.on('' , '--account-output FILE') {|v| options[:password_manager] = Subiam::PasswordManager.new(v, options) }
48
+ opt.on('-e', '--export') { mode = :export }
49
+ opt.on('-o', '--output FILE') {|v| output_file = v }
50
+ opt.on('' , '--split') { split = true }
51
+ opt.on('' , '--split-more') { split = :more }
52
+ opt.on('', '--format=FORMAT', [:ruby, :json]) {|v| format_passed = true; options[:format] = v }
53
+ opt.on('' , '--export-concurrency N', Integer) {|v| options[:export_concurrency] = v }
54
+ opt.on('' , '--ignore-login-profile') { options[:ignore_login_profile] = true }
55
+ opt.on('' , '--no-color') { options[:color] = false }
56
+ opt.on('' , '--no-progress') { options[:no_progress] = true }
57
+ opt.on('' , '--debug') { options[:debug] = true }
58
+ opt.on('' , '--enable-delete') { options[:enable_delete] = true }
59
+ opt.parse!
60
+
61
+ aws_opts = {}
62
+
63
+ if access_key and secret_key
64
+ aws_opts.update(
65
+ :access_key_id => access_key,
66
+ :secret_access_key => secret_key
67
+ )
68
+ elsif profile_name or credentials_path
69
+ credentials_opts = {}
70
+ credentials_opts[:profile_name] = profile_name if profile_name
71
+ credentials_opts[:path] = credentials_path if credentials_path
72
+ credentials = Aws::SharedCredentials.new(credentials_opts)
73
+ aws_opts[:credentials] = credentials
74
+ elsif (access_key and !secret_key) or (!access_key and secret_key) or mode.nil?
75
+ puts opt.help
76
+ exit 1
77
+ end
78
+
79
+ aws_opts[:region] = region if region
80
+ Aws.config.update(aws_opts)
81
+
82
+ if not format_passed and [file, output_file].any? {|i| i =~ /\.json\z/ }
83
+ options[:format] = :json
84
+ end
85
+ rescue => e
86
+ $stderr.puts("[ERROR] #{e.message}")
87
+ exit 1
88
+ end
89
+ end
90
+
91
+ String.colorize = options[:color]
92
+
93
+ if options[:debug]
94
+ Aws.config.update(
95
+ :http_wire_trace => true,
96
+ :logger => Subiam::Logger.instance
97
+ )
98
+ end
99
+
100
+ begin
101
+ logger = Subiam::Logger.instance
102
+ logger.set_debug(options[:debug])
103
+ client = Subiam::Client.new(options)
104
+
105
+ case mode
106
+ when :export
107
+ if split
108
+ logger.info('Export IAM')
109
+ output_file = DEFAULT_FILENAME if output_file == '-'
110
+ requires = []
111
+
112
+ client.export(:split_more => (split == :more), :convert => (options[:format] == :ruby)) do |args|
113
+ type, dsl = args.values_at(:type, :dsl)
114
+ next if dsl.empty?
115
+
116
+ type = type.to_s
117
+ dir = File.dirname(output_file)
118
+
119
+ if split == :more
120
+ name = args[:name]
121
+ dir = File.join(dir, type)
122
+ FileUtils.mkdir_p(dir)
123
+ iam_filename = "#{name}.iam"
124
+ iam_file = File.join(dir, iam_filename)
125
+ requires << File.join(type, iam_filename)
126
+ else
127
+ iam_filename = "#{type}.iam"
128
+ iam_file = File.join(dir, iam_filename)
129
+ requires << iam_filename
130
+ end
131
+
132
+ if options[:format] == :json
133
+ iam_file << '.json'
134
+ end
135
+
136
+ logger.info(" write `#{iam_file}`")
137
+
138
+ open(iam_file, 'wb') do |f|
139
+ f.puts MAGIC_COMMENT if options[:format] == :ruby
140
+ f.puts dsl
141
+ end
142
+ end
143
+
144
+ if options[:format] == :ruby
145
+ logger.info(" write `#{output_file}`")
146
+
147
+ open(output_file, 'wb') do |f|
148
+ f.puts MAGIC_COMMENT
149
+
150
+ requires.each do |iam_file|
151
+ f.puts "require '#{iam_file}'"
152
+ end
153
+ end
154
+ end
155
+ else
156
+ exported = client.export(:convert => (options[:format] == :ruby))
157
+
158
+ if output_file == '-'
159
+ logger.info('# Export IAM')
160
+ puts exported
161
+ else
162
+ logger.info("Export IAM to `#{output_file}`")
163
+ open(output_file, 'wb') do |f|
164
+ f.puts MAGIC_COMMENT if options[:format] == :ruby
165
+ f.puts exported
166
+ end
167
+ end
168
+ end
169
+ when :apply
170
+ unless File.exist?(file)
171
+ raise "No IAMfile found (looking for: #{file})"
172
+ end
173
+
174
+ msg = "Apply `#{file}` to IAM"
175
+ msg << ' (dry-run)' if options[:dry_run]
176
+ logger.info(msg)
177
+
178
+ updated = client.apply(file)
179
+
180
+ logger.info('No change'.intense_blue) unless updated
181
+ end
182
+ rescue => e
183
+ if options[:debug]
184
+ raise e
185
+ else
186
+ $stderr.puts("[ERROR] #{e.message}".red)
187
+ exit 1
188
+ end
189
+ end