environments-list-builder 0.1.8 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
}
|