environments-list-builder 0.1.8 → 0.1.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +35 -0
- data/.rakeTasks +7 -0
- data/.rspec +1 -0
- data/ChangeLog.md +4 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +187 -0
- data/LICENSE +201 -0
- data/LICENSE.txt +203 -0
- data/README.md +2 -0
- data/Rakefile +36 -0
- data/bin/environmentslistbuilder +34 -0
- data/environments-list-builder.gemspec +37 -0
- data/features/.gitkeep +0 -0
- data/features/manifestrepo-builder.feature +1 -0
- data/features/step_definitions/.gitkeep +0 -0
- data/features/step_definitions/manifestrepo-builder_steps.rb +1 -0
- data/lib/cicd/builder/environments-list.rb +50 -0
- data/lib/cicd/builder/environments-list/mixlib/build.rb +163 -0
- data/lib/cicd/builder/environments-list/mixlib/lib/mixins/no_commands.rb +309 -0
- data/lib/cicd/builder/environments-list/mixlib/lib/update_bucket_policy.rb +452 -0
- data/lib/cicd/builder/environments-list/mixlib/repo.rb +49 -0
- data/lib/cicd/builder/environments-list/mixlib/repo/artifactory.rb +49 -0
- data/lib/cicd/builder/environments-list/version.rb +10 -0
- data/openssl +0 -0
- data/spec/builder_spec.rb +8 -0
- data/spec/spec_helper.rb +4 -0
- data/tests/infinite-etag.rb +166 -0
- metadata +49 -9
@@ -0,0 +1,309 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'awesome_print'
|
3
|
+
require 'inifile'
|
4
|
+
|
5
|
+
module Amplify
|
6
|
+
module AWS
|
7
|
+
LOG_LEVELS = [:trace, :debug, :info, :step, :warn, :error, :fatal, :todo]
|
8
|
+
|
9
|
+
class CredentialsHash < Hash
|
10
|
+
def initialize
|
11
|
+
super()
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class IniFileCredentials < CredentialsHash
|
16
|
+
def initialize(inifile)
|
17
|
+
super()
|
18
|
+
self.[]=(:inifile, inifile)
|
19
|
+
end
|
20
|
+
def get_options
|
21
|
+
{
|
22
|
+
:inifile => self.[](:inifile),
|
23
|
+
:account => File.basename(self.[](:inifile)),
|
24
|
+
}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class ProfileCredentials < CredentialsHash
|
29
|
+
def initialize(profile)
|
30
|
+
super()
|
31
|
+
self.[]=(:profile, profile)
|
32
|
+
end
|
33
|
+
def get_options
|
34
|
+
{
|
35
|
+
:profile => self.[](:profile),
|
36
|
+
:account => self.[](:profile),
|
37
|
+
}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
module MixIns
|
42
|
+
module NoCommands
|
43
|
+
require 'dldinternet/mixlib/logging'
|
44
|
+
include DLDInternet::Mixlib::Logging
|
45
|
+
|
46
|
+
def validate_options
|
47
|
+
@config = @options.dup
|
48
|
+
if @config[:log_level]
|
49
|
+
log_level = @config[:log_level].to_sym
|
50
|
+
raise "Invalid log-level: #{log_level}" unless LOG_LEVELS.include?(log_level)
|
51
|
+
@config[:log_level] = log_level
|
52
|
+
end
|
53
|
+
@config[:log_level] ||= :info
|
54
|
+
end
|
55
|
+
|
56
|
+
def parse_options
|
57
|
+
validate_options
|
58
|
+
|
59
|
+
colormap = {
|
60
|
+
:trace => :blue,
|
61
|
+
:debug => :cyan,
|
62
|
+
:info => :green,
|
63
|
+
:step => :green,
|
64
|
+
:warn => :yellow,
|
65
|
+
:error => :red,
|
66
|
+
:fatal => :red,
|
67
|
+
:todo => :purple,
|
68
|
+
}
|
69
|
+
unless options[:color]
|
70
|
+
LOG_LEVELS.each do |l,_|
|
71
|
+
colormap[l] = :clear
|
72
|
+
end
|
73
|
+
end
|
74
|
+
lcs = ::Logging::ColorScheme.new( 'compiler', :levels => colormap)
|
75
|
+
scheme = lcs.scheme
|
76
|
+
if options[:color]
|
77
|
+
scheme['trace'] = "\e[38;5;33m"
|
78
|
+
scheme['fatal'] = "\e[38;5;89m"
|
79
|
+
scheme['todo'] = "\e[38;5;55m"
|
80
|
+
end
|
81
|
+
lcs.scheme scheme
|
82
|
+
@config[:log_opts] = lambda{|mlll| {
|
83
|
+
:pattern => "%#{mlll}l: %m %g\n",
|
84
|
+
:date_pattern => '%Y-%m-%d %H:%M:%S',
|
85
|
+
:color_scheme => 'compiler',
|
86
|
+
:trace => (@config[:trace].nil? ? false : @config[:trace]),
|
87
|
+
# [2014-06-30 Christo] DO NOT do this ... it needs to be a FixNum!!!!
|
88
|
+
# If you want to do ::Logging.init first then fine ... go ahead :)
|
89
|
+
# :level => @config[:log_level],
|
90
|
+
}
|
91
|
+
}
|
92
|
+
@logger = getLogger(@config)
|
93
|
+
|
94
|
+
if @options[:inifile]
|
95
|
+
load_credentials_inifile(@options[:inifile], 'global')
|
96
|
+
elsif @options[:profile]
|
97
|
+
unless File.directory?(File.expand_path('~/.aws'))
|
98
|
+
msg = 'Cannot load profile. ~/.aws does not exist!?'
|
99
|
+
@logger.error msg
|
100
|
+
raise msg
|
101
|
+
end
|
102
|
+
unless File.exists?(File.expand_path('~/.aws/config'))
|
103
|
+
msg = 'Cannot load profile. ~/.aws/config does not exist!?'
|
104
|
+
@logger.error msg
|
105
|
+
raise msg
|
106
|
+
end
|
107
|
+
unless File.exists?(File.expand_path('~/.aws/credentials'))
|
108
|
+
msg = 'Cannot load profile. ~/.aws/credentials does not exist!?'
|
109
|
+
@logger.error msg
|
110
|
+
raise msg
|
111
|
+
end
|
112
|
+
@logger.debug "Profile: #{@options[:profile]}"
|
113
|
+
load_credentials_profile(@options[:profile], {
|
114
|
+
'output' => 'AWS_DEFAULT_OUTPUT',
|
115
|
+
'region' => 'AWS_DEFAULT_REGION',
|
116
|
+
'aws_access_key_id' => 'AWS_ACCESS_KEY_ID',
|
117
|
+
'aws_secret_access_key' => 'AWS_SECRET_ACCESS_KEY',
|
118
|
+
})
|
119
|
+
end
|
120
|
+
|
121
|
+
if options[:debug]
|
122
|
+
@logger.info "Options:\n#{options.ai}"
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
def expand_options()
|
128
|
+
def _expand(opts,k,v,regex,rerun)
|
129
|
+
matches = v.match(regex)
|
130
|
+
if matches
|
131
|
+
var = matches[1]
|
132
|
+
if opts[var]
|
133
|
+
opts[k]=v.gsub(/\%\(#{var}\)/,opts[var]).gsub(/\%#{var}/,opts[var])
|
134
|
+
else
|
135
|
+
rerun[var] = 1
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
pending = nil
|
141
|
+
rerun = {}
|
142
|
+
begin
|
143
|
+
pending = rerun
|
144
|
+
rerun = {}
|
145
|
+
@options.to_hash.each{|k,v|
|
146
|
+
if v.to_s.match(/\%/)
|
147
|
+
_expand(@options,k,v,%r'[^\\]\%\((\w+)\)', rerun)
|
148
|
+
_expand(@options,k,v,%r'[^\\]\%(\w+)', rerun)
|
149
|
+
end
|
150
|
+
}
|
151
|
+
# Should break out the first time that we make no progress!
|
152
|
+
end while pending != rerun
|
153
|
+
end
|
154
|
+
|
155
|
+
def load_credentials_profile(profile, map=nil)
|
156
|
+
begin
|
157
|
+
if @saved_env and @saved_env.size > 0
|
158
|
+
@saved_env.each do |k,v|
|
159
|
+
ENV[k] = v
|
160
|
+
end
|
161
|
+
ENV.to_hash.each do |k,v|
|
162
|
+
unless @saved_env.has_key?(k)
|
163
|
+
ENV.delete(k)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
else
|
167
|
+
@saved_env = ENV.to_hash.dup
|
168
|
+
end
|
169
|
+
ini = load_inifile('~/.aws/config')
|
170
|
+
ini['default'].each{ |key,value|
|
171
|
+
k = if map[key.to_s]
|
172
|
+
map[key.to_s]
|
173
|
+
else
|
174
|
+
key.to_s
|
175
|
+
end
|
176
|
+
ENV[k]=value
|
177
|
+
}
|
178
|
+
ini["profile #{profile}"].each{ |key,value|
|
179
|
+
k = if map[key.to_s]
|
180
|
+
map[key.to_s]
|
181
|
+
else
|
182
|
+
key.to_s
|
183
|
+
end
|
184
|
+
ENV[k]=value
|
185
|
+
}
|
186
|
+
ini = load_inifile('~/.aws/credentials')
|
187
|
+
unless ini.sections.include?(profile)
|
188
|
+
logger.error "No credentials found for profile #{profile}"
|
189
|
+
exit 11
|
190
|
+
end
|
191
|
+
ini[profile].each{ |key,value|
|
192
|
+
k = if map[key.to_s]
|
193
|
+
map[key.to_s]
|
194
|
+
else
|
195
|
+
key.to_s
|
196
|
+
end
|
197
|
+
ENV[k]=value
|
198
|
+
}
|
199
|
+
expand_options()
|
200
|
+
rescue ::IniFile::Error => e
|
201
|
+
# noop
|
202
|
+
rescue SystemExit => e
|
203
|
+
raise e
|
204
|
+
rescue ::Exception => e
|
205
|
+
@logger.error "#{e.class.name} #{e.message}"
|
206
|
+
raise e
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def load_inifile(inifile)
|
211
|
+
inifile = File.expand_path(inifile)
|
212
|
+
unless File.exist?(inifile)
|
213
|
+
raise "#{inifile} not found!"
|
214
|
+
end
|
215
|
+
begin
|
216
|
+
ini = ::IniFile.load(inifile)
|
217
|
+
rescue ::Exception => e
|
218
|
+
@logger.error "#{e.class.name} #{e.message}"
|
219
|
+
raise e
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
def load_credentials_inifile(inifile, section='global', map=nil)
|
224
|
+
begin
|
225
|
+
ini = load_inifile(inifile)
|
226
|
+
ini[section].each{ |key,value|
|
227
|
+
#@options[key.to_s]=value
|
228
|
+
@logger.debug "#{key} '#{value}'"
|
229
|
+
ENV[key.to_s]=value.to_s
|
230
|
+
}
|
231
|
+
expand_options()
|
232
|
+
rescue ::IniFile::Error => e
|
233
|
+
# noop
|
234
|
+
rescue ::Exception => e
|
235
|
+
@logger.error "#{e.class.name} #{e.message}"
|
236
|
+
raise e
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
def check_missing_options(method,optionset)
|
241
|
+
parse_options
|
242
|
+
missing = []
|
243
|
+
optionset.each do |req|
|
244
|
+
case req.class.name
|
245
|
+
when /String|Symbol/
|
246
|
+
missing << req unless @options[req]
|
247
|
+
when /Array/
|
248
|
+
flag = false
|
249
|
+
req.each do |o|
|
250
|
+
if @options[o]
|
251
|
+
flag = true
|
252
|
+
break
|
253
|
+
end
|
254
|
+
end
|
255
|
+
missing << req.join('|') unless flag
|
256
|
+
else
|
257
|
+
logger.fatal "Internal: #{method}: Unsupported missing option type: #{req.class}"
|
258
|
+
exit 99
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
if missing.size > 0
|
263
|
+
logger.error "#{method}: Missing required options: #{missing.join(', ')}"
|
264
|
+
exit 1
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
def print_error(err)
|
269
|
+
|
270
|
+
logger.error err.to_s
|
271
|
+
|
272
|
+
end
|
273
|
+
|
274
|
+
def prepare_accounts
|
275
|
+
@accounts = []
|
276
|
+
if @options[:iniglob]
|
277
|
+
iniglob = File.expand_path(@options[:iniglob])
|
278
|
+
Dir.glob( iniglob) {| filename |
|
279
|
+
@accounts << IniFileCredentials.new(filename)
|
280
|
+
}
|
281
|
+
elsif @options[:inifile]
|
282
|
+
@accounts << IniFileCredentials.new(@options[:inifile]) unless (@options[:inifile].nil? or @options[:inifile].empty?)
|
283
|
+
end
|
284
|
+
|
285
|
+
if @options[:profile]
|
286
|
+
if @options[:profiles]
|
287
|
+
@options[:profiles] << @options[:profile]
|
288
|
+
else
|
289
|
+
@options[:profiles] = [@options[:profile]]
|
290
|
+
end
|
291
|
+
end
|
292
|
+
if @options[:profiles]
|
293
|
+
@options[:profiles].each {| profile |
|
294
|
+
@accounts << ProfileCredentials.new(profile)
|
295
|
+
}
|
296
|
+
end
|
297
|
+
unless @accounts.size > 0
|
298
|
+
parse_options
|
299
|
+
logger.fatal 'No options allow for account identification'
|
300
|
+
end
|
301
|
+
# Make the options mutable
|
302
|
+
unless @options.is_a?(Hash) or @options.is_a?(Hashie::Mash)
|
303
|
+
@options = Hashie::Mash.new(@options.to_hash)
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
309
|
+
end
|
@@ -0,0 +1,452 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'thor'
|
4
|
+
require 'awesome_print'
|
5
|
+
require 'inifile'
|
6
|
+
require 'rubygems'
|
7
|
+
require 'aws-sdk-core'
|
8
|
+
require 'json'
|
9
|
+
|
10
|
+
# =====================================================================================================================
|
11
|
+
# A little include path tom-foolery ...
|
12
|
+
path = File.dirname(__FILE__)
|
13
|
+
# Borrowing from "whiches" gem ...
|
14
|
+
cmd = File.basename(__FILE__, '.rb')
|
15
|
+
exes = []
|
16
|
+
exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
|
17
|
+
ENV['PATH'].split(File::PATH_SEPARATOR).each do |pth|
|
18
|
+
exts.each { |ext|
|
19
|
+
exe = File.join(pth, "#{cmd}#{ext}")
|
20
|
+
exes << exe if File.executable? exe
|
21
|
+
}
|
22
|
+
end
|
23
|
+
if exes.size > 0
|
24
|
+
path = File.dirname(exes[0])
|
25
|
+
end
|
26
|
+
|
27
|
+
# add_path = File.expand_path(File.join(path, '../../lib'))
|
28
|
+
# $:.unshift(add_path)
|
29
|
+
# add_path = File.expand_path(File.join(path, 'lib'))
|
30
|
+
add_path = path
|
31
|
+
$:.unshift(add_path)
|
32
|
+
|
33
|
+
# =====================================================================================================================
|
34
|
+
class UpdateBucketPolicy < Thor
|
35
|
+
KNOWN = %w(38.117.159.162/32 70.151.98.131/32 )
|
36
|
+
class_option :verbose, :type => :boolean
|
37
|
+
class_option :debug, :type => :boolean
|
38
|
+
class_option :log_level, :type => :string, :banner => 'Log level ([:trace, :debug, :info, :step, :warn, :error, :fatal, :todo])', :default => :step
|
39
|
+
class_option :bucket, :type => :string, :default => 'wgen-sto-artifacts'
|
40
|
+
class_option :bucketini, :type => :string
|
41
|
+
class_option :bucketprofile,:type => :string
|
42
|
+
class_option :basepolicy, :type => :string
|
43
|
+
class_option :policy, :type => :string
|
44
|
+
class_option :iniglob, :type => :string
|
45
|
+
class_option :inifile, :type => :string
|
46
|
+
class_option :profiles, :type => :array
|
47
|
+
class_option :color, :type => :boolean, :default => false
|
48
|
+
class_option :yes, :type => :boolean
|
49
|
+
attr_reader :accounts, :environments
|
50
|
+
attr_accessor :logger
|
51
|
+
|
52
|
+
no_commands do
|
53
|
+
|
54
|
+
require 'mixins/no_commands'
|
55
|
+
include Amplify::AWS::MixIns::NoCommands
|
56
|
+
|
57
|
+
def get_environments
|
58
|
+
logger.step 'Get environments ...'
|
59
|
+
|
60
|
+
@environments = {}
|
61
|
+
@nats = {}
|
62
|
+
@accounts.each do |acc|
|
63
|
+
@options.merge!(acc.get_options())
|
64
|
+
parse_options
|
65
|
+
get_environments_for_account
|
66
|
+
end
|
67
|
+
logger.info "Environments:\n"+@environments.ai
|
68
|
+
logger.info "NATs:\n"+@nats.ai
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
def get_environments_for_account
|
73
|
+
logger.step "Get environments for #{@options[:account]} ..."
|
74
|
+
|
75
|
+
config = @options
|
76
|
+
|
77
|
+
# noinspection RubyArgCount
|
78
|
+
cfn = Aws::CloudFormation::Client.new(retry_limit: 10)
|
79
|
+
|
80
|
+
stacks = []
|
81
|
+
resp = cfn.list_stacks()
|
82
|
+
|
83
|
+
stacks << resp[:stack_summaries]
|
84
|
+
while resp[:next_token]
|
85
|
+
resp = cfn.list_stacks(next_token: resp[:next_token])
|
86
|
+
logger.debug resp.size
|
87
|
+
stacks << resp[:stack_summaries]
|
88
|
+
end
|
89
|
+
stacks.flatten!
|
90
|
+
stacks = stacks.select{ |stack| stack[:stack_status].match(%r'^(UPDATE|CREATE).*?_COMPLETE$')}
|
91
|
+
logger.debug stacks.ai
|
92
|
+
|
93
|
+
stacks.each do |stack|
|
94
|
+
env = nil
|
95
|
+
resp = cfn.describe_stacks(stack_name: stack[:stack_id])
|
96
|
+
stck = resp[:stacks].shift
|
97
|
+
tags = stck[:tags]
|
98
|
+
tags.each do |tag|
|
99
|
+
if tag[:key] == 'EnvironmentName'
|
100
|
+
env = tag[:value]
|
101
|
+
break
|
102
|
+
end
|
103
|
+
end
|
104
|
+
unless env
|
105
|
+
env = stack[:stack_name]
|
106
|
+
end
|
107
|
+
@environments[env] ||= []
|
108
|
+
@environments[env] << stack[:stack_name]
|
109
|
+
|
110
|
+
resources = []
|
111
|
+
resp = cfn.describe_stack_resources(stack_name: stack[:stack_id], logical_resource_id: 'NATIPAddress')
|
112
|
+
resources << resp[:stack_resources]
|
113
|
+
resp = cfn.describe_stack_resources(stack_name: stack[:stack_id], logical_resource_id: 'BastionIPAddress')
|
114
|
+
resources << resp[:stack_resources]
|
115
|
+
resources.flatten!
|
116
|
+
logger.debug resources.ai
|
117
|
+
|
118
|
+
_nats = resources.map{ |r|
|
119
|
+
r.to_h[:physical_resource_id]
|
120
|
+
}
|
121
|
+
|
122
|
+
@nats[env] = @nats[env] ? [ @nats[env], _nats ].flatten : _nats
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
def get_eips_for_account
|
128
|
+
logger.step "Get EIPs for #{@options[:inifile]} ..."
|
129
|
+
|
130
|
+
# noinspection RubyArgCount
|
131
|
+
ec2 = Aws::EC2::Client.new(retry_limit: 10)
|
132
|
+
|
133
|
+
resp = ec2.describe_addresses()
|
134
|
+
|
135
|
+
@eips << resp[:addresses].map{ |a| a[:public_ip] }
|
136
|
+
@eips.flatten!
|
137
|
+
logger.debug @eips.ai
|
138
|
+
|
139
|
+
end
|
140
|
+
|
141
|
+
def get_eips
|
142
|
+
logger.step 'Get EIPs ...'
|
143
|
+
|
144
|
+
@eips = []
|
145
|
+
@accounts.each do |acc|
|
146
|
+
@options.merge!(acc.get_options())
|
147
|
+
parse_options
|
148
|
+
get_eips_for_account
|
149
|
+
end
|
150
|
+
@eips.uniq!
|
151
|
+
logger.info "EIPs:\n"+@eips.sort_by { |ip| ip.split(%r'[\./]').map{ |octet| octet.to_i} }.ai
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
def read_bucket_policy
|
156
|
+
|
157
|
+
parse_bucket_options(__method__, [ :policy, [:bucketini, :bucketprofile], :bucket ])
|
158
|
+
|
159
|
+
logger.step "Read the current policy for #{@options[:bucket]} ..."
|
160
|
+
|
161
|
+
# noinspection RubyArgCount
|
162
|
+
s3 = Aws::S3::Client.new()
|
163
|
+
|
164
|
+
resp = s3.get_bucket_policy(bucket: options[:bucket])
|
165
|
+
logger.debug resp.size
|
166
|
+
@policies = []
|
167
|
+
resp.each do |item|
|
168
|
+
json = item.policy.read
|
169
|
+
@policies << JSON.load(json)
|
170
|
+
|
171
|
+
logger.debug JSON.pretty_generate(@policies[-1], { indent: "\t", space: ' '})
|
172
|
+
end
|
173
|
+
|
174
|
+
logger.debug @policies.ai
|
175
|
+
|
176
|
+
json = ''
|
177
|
+
@policies.each do |pol|
|
178
|
+
json += JSON.pretty_generate(pol, { indent: "\t", space: ' '})
|
179
|
+
end
|
180
|
+
IO.write("#{options[:policy]}", json)
|
181
|
+
|
182
|
+
end
|
183
|
+
|
184
|
+
def inspect_bucket_policy
|
185
|
+
|
186
|
+
parse_bucket_options(__method__, [ :policy, [:bucketini, :bucketprofile], :bucket ])
|
187
|
+
|
188
|
+
logger.step "Inspect the policy for #{@options[:bucket]} ..."
|
189
|
+
|
190
|
+
config = @options.dup
|
191
|
+
tempfile =
|
192
|
+
@options[:policy] = Tempfile.new('policy').path+'.json'
|
193
|
+
read_bucket_policy()
|
194
|
+
@options = config
|
195
|
+
|
196
|
+
get_eips()
|
197
|
+
|
198
|
+
get_environments()
|
199
|
+
|
200
|
+
ips = [ @nats.values, @eips ].flatten.map{ |ip| "#{ip}/32"}
|
201
|
+
ips = [ ips, KNOWN ].flatten.uniq.select{ |ip| not ip.nil? }
|
202
|
+
ips = ips.sort_by { |ip| ip.split(%r'[\./]').map{ |octet| octet.to_i} }.uniq
|
203
|
+
logger.info "Valid IPs: \n"+ips.ai
|
204
|
+
|
205
|
+
json = ''
|
206
|
+
@policies.each do |pol|
|
207
|
+
unknown = 0
|
208
|
+
missing = 0
|
209
|
+
unadded = 0
|
210
|
+
if pol['Statement']
|
211
|
+
pol['Statement'].each do |smt|
|
212
|
+
logger.debug smt.ai
|
213
|
+
if smt['Condition'] and smt['Condition']['IpAddress'] and smt['Condition']['IpAddress']['aws:SourceIp']
|
214
|
+
smt['Condition']['IpAddress']['aws:SourceIp'].sort_by { |ip| ip.split(%r'[\./]').map{ |octet| octet.to_i} }.each do |entry|
|
215
|
+
unless ips.include?(entry)
|
216
|
+
logger.warn "#{entry} unknown"
|
217
|
+
unknown += 1
|
218
|
+
end
|
219
|
+
end
|
220
|
+
ips.each do |entry|
|
221
|
+
unless smt['Condition']['IpAddress']['aws:SourceIp'].include?(entry)
|
222
|
+
logger.info "#{entry} missing"
|
223
|
+
missing += 1
|
224
|
+
smt['Condition']['IpAddress']['aws:SourceIp'] << entry
|
225
|
+
end
|
226
|
+
end
|
227
|
+
if missing > 0
|
228
|
+
ips.each do |entry|
|
229
|
+
unless smt['Condition']['IpAddress']['aws:SourceIp'].include?(entry)
|
230
|
+
logger.warn "#{entry} missing"
|
231
|
+
unadded += 1
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
smt['Condition']['IpAddress']['aws:SourceIp'] = smt['Condition']['IpAddress']['aws:SourceIp'].sort_by { |ip| ip.split(%r'[\./]').map{ |octet| octet.to_i} }
|
236
|
+
end
|
237
|
+
end
|
238
|
+
else
|
239
|
+
logger.warn "Policy has no Statement: #{pol.ai}"
|
240
|
+
end
|
241
|
+
if missing+unknown > 0
|
242
|
+
logger.warn "Policy needs to be updated for #{unknown} unknown IPs, #{missing} missing and #{unadded} IPs which failed to add"
|
243
|
+
end
|
244
|
+
# require 'date_time'
|
245
|
+
logger.debug JSON.pretty_generate(pol, { indent: "\t", space: ' '})
|
246
|
+
pol['Id'] = "Policy-#{DateTime.now.strftime('%Y%m%dT%H%M%S')}"
|
247
|
+
json += JSON.pretty_generate(pol, { indent: "\t", space: ' '})
|
248
|
+
end
|
249
|
+
IO.write(options[:policy], json)
|
250
|
+
|
251
|
+
end
|
252
|
+
|
253
|
+
def update_bucket_policy
|
254
|
+
|
255
|
+
parse_bucket_options(__method__, [ :policy, [:bucketini, :bucketprofile], :bucket ])
|
256
|
+
|
257
|
+
logger.step "Update the current policy for #{@options[:bucket]} ..."
|
258
|
+
|
259
|
+
inspect_bucket_policy
|
260
|
+
|
261
|
+
write_bucket_policy
|
262
|
+
|
263
|
+
end
|
264
|
+
|
265
|
+
def write_bucket_policy
|
266
|
+
|
267
|
+
parse_bucket_options(__method__, [ :policy, [:bucketini, :bucketprofile], :bucket ])
|
268
|
+
|
269
|
+
logger.step "Write the new policy for #{@options[:bucket]} ..."
|
270
|
+
|
271
|
+
@options[:inifile] = @options[:bucketini]
|
272
|
+
parse_options
|
273
|
+
@logger.info options.ai
|
274
|
+
|
275
|
+
json = IO.read(options[:policy])
|
276
|
+
|
277
|
+
# noinspection RubyArgCount
|
278
|
+
s3 = Aws::S3::Client.new()
|
279
|
+
|
280
|
+
resp = s3.put_bucket_policy(
|
281
|
+
bucket: options[:bucket],
|
282
|
+
policy: json
|
283
|
+
)
|
284
|
+
logger.debug resp.ai
|
285
|
+
if resp.error
|
286
|
+
logger.error resp.error
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
def parse_bucket_options(method,optionset)
|
291
|
+
check_missing_options(method, optionset)
|
292
|
+
|
293
|
+
if @options[:bucketini]
|
294
|
+
@options[:inifile] = @options[:bucketini]
|
295
|
+
else
|
296
|
+
@options[:profile] = @options[:bucketprofile]
|
297
|
+
end
|
298
|
+
parse_options
|
299
|
+
@logger.info options.ai
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
def initialize(args = [], local_options = {}, config = {})
|
304
|
+
super(args,local_options,config)
|
305
|
+
end
|
306
|
+
|
307
|
+
desc 'read', 'read the policy for BUCKET'
|
308
|
+
def read()
|
309
|
+
prepare_accounts
|
310
|
+
|
311
|
+
read_bucket_policy
|
312
|
+
|
313
|
+
n = 1
|
314
|
+
@policies.each do |pol|
|
315
|
+
logger.step "#{n+=1}\n"+JSON.pretty_generate(pol, { indent: "\t", space: ' '})
|
316
|
+
end
|
317
|
+
|
318
|
+
0
|
319
|
+
end
|
320
|
+
|
321
|
+
desc 'inspect', 'inspect the policy for BUCKET'
|
322
|
+
def inspect()
|
323
|
+
prepare_accounts()
|
324
|
+
|
325
|
+
inspect_bucket_policy()
|
326
|
+
|
327
|
+
@policies.each do |pol|
|
328
|
+
logger.debug JSON.pretty_generate(pol, { indent: "\t", space: ' '})
|
329
|
+
end
|
330
|
+
0
|
331
|
+
end
|
332
|
+
|
333
|
+
desc 'update', 'update the policy for BUCKET'
|
334
|
+
def update()
|
335
|
+
prepare_accounts()
|
336
|
+
|
337
|
+
update_bucket_policy()
|
338
|
+
|
339
|
+
0
|
340
|
+
end
|
341
|
+
|
342
|
+
desc 'write', 'write the new POLICY to BUCKET'
|
343
|
+
def write()
|
344
|
+
prepare_accounts
|
345
|
+
@options[:inifile] = @options[:bucketini]
|
346
|
+
parse_options
|
347
|
+
@logger.info options.ai
|
348
|
+
|
349
|
+
write_bucket_policy
|
350
|
+
|
351
|
+
0
|
352
|
+
end
|
353
|
+
|
354
|
+
end
|
355
|
+
|
356
|
+
# =====================================================================================================================
|
357
|
+
# UpdateBucketPolicy.start(ARGV)
|
358
|
+
|
359
|
+
|
360
|
+
__END__
|
361
|
+
"aws:SourceIp": [
|
362
|
+
"54.84.38.255/32",
|
363
|
+
"54.86.79.230/32",
|
364
|
+
"54.84.206.23/32",
|
365
|
+
"54.172.63.6/32",
|
366
|
+
"54.86.33.222/32",
|
367
|
+
"38.117.159.162/32",
|
368
|
+
"70.151.98.131/32",
|
369
|
+
"54.208.252.192/32",
|
370
|
+
"54.86.6.21/32",
|
371
|
+
"54.86.156.100/32",
|
372
|
+
"54.209.155.115/32",
|
373
|
+
"107.23.90.79/32",
|
374
|
+
"54.172.109.59/32",
|
375
|
+
"54.209.93.210/32",
|
376
|
+
"54.88.205.140/32",
|
377
|
+
"54.209.196.100/32",
|
378
|
+
"54.84.6.106/32",
|
379
|
+
"54.165.122.109/32",
|
380
|
+
"107.21.36.141/32",
|
381
|
+
"54.86.150.196/32",
|
382
|
+
"54.209.17.121/32",
|
383
|
+
"54.84.31.228/32",
|
384
|
+
"54.88.2.227/32",
|
385
|
+
"54.85.80.166/32",
|
386
|
+
"54.86.31.209/32",
|
387
|
+
"50.19.114.108/32",
|
388
|
+
"54.85.159.92/32",
|
389
|
+
"54.85.129.109/32",
|
390
|
+
"54.165.86.129/32",
|
391
|
+
"54.165.114.103/32",
|
392
|
+
"54.172.8.125/32",
|
393
|
+
"54.86.144.175/32",
|
394
|
+
"107.23.57.237/32",
|
395
|
+
"54.165.231.248/32",
|
396
|
+
"54.165.228.106/32"
|
397
|
+
]
|
398
|
+
|
399
|
+
{
|
400
|
+
"Version": "2008-10-17",
|
401
|
+
"Id": "Policy13947375644752",
|
402
|
+
"Statement": [
|
403
|
+
{
|
404
|
+
"Sid": "Stmt13947375624982",
|
405
|
+
"Effect": "Allow",
|
406
|
+
"Principal": {
|
407
|
+
"AWS": "*"
|
408
|
+
},
|
409
|
+
"Action": "s3:*",
|
410
|
+
"Resource": "arn:aws:s3:::wgen-sto-artifacts/*",
|
411
|
+
"Condition": {
|
412
|
+
"IpAddress": {
|
413
|
+
"aws:SourceIp": [
|
414
|
+
"54.86.79.230/32",
|
415
|
+
"54.84.206.23/32",
|
416
|
+
"54.86.33.222/32",
|
417
|
+
"38.117.159.162/32",
|
418
|
+
"70.151.98.131/32",
|
419
|
+
"54.209.155.115/32",
|
420
|
+
"107.23.90.79/32",
|
421
|
+
"54.209.93.210/32",
|
422
|
+
"54.88.205.140/32",
|
423
|
+
"54.84.6.106/32",
|
424
|
+
"107.21.36.141/32",
|
425
|
+
"54.86.150.196/32",
|
426
|
+
"54.209.17.121/32",
|
427
|
+
"54.84.31.228/32",
|
428
|
+
"54.86.31.209/32",
|
429
|
+
"50.19.114.108/32",
|
430
|
+
"54.85.129.109/32",
|
431
|
+
"54.165.86.129/32",
|
432
|
+
"54.165.114.103/32",
|
433
|
+
"54.172.8.125/32",
|
434
|
+
"54.86.144.175/32",
|
435
|
+
"107.23.57.237/32",
|
436
|
+
"54.165.231.248/32",
|
437
|
+
"54.165.228.106/32"
|
438
|
+
]
|
439
|
+
}
|
440
|
+
}
|
441
|
+
},
|
442
|
+
{
|
443
|
+
"Sid": "2",
|
444
|
+
"Effect": "Allow",
|
445
|
+
"Principal": {
|
446
|
+
"AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity EXU2IEU8NKUSP"
|
447
|
+
},
|
448
|
+
"Action": "s3:GetObject",
|
449
|
+
"Resource": "arn:aws:s3:::wgen-sto-artifacts/*"
|
450
|
+
}
|
451
|
+
]
|
452
|
+
}
|