Class: SetSecurityGroups

Inherits:
Command show all
Defined in:
lib/maws/commands/set-security-groups.rb

Instance Attribute Summary

Attributes inherited from Command

#connection, #maws

Instance Method Summary collapse

Methods inherited from Command

#add_generic_options, #add_specific_options, #initialize, #instances, #process_options, #verify_configs

Constructor Details

This class inherits a constructor from Command

Instance Method Details

#authorize_ec2_security_group_rule(group_id, from_port, to_port, protocol, rule) ⇒ Object



192
193
194
195
196
197
# File 'lib/maws/commands/set-security-groups.rb', line 192

def authorize_ec2_security_group_rule(group_id, from_port, to_port, protocol, rule)
  params = {:from_port  => from_port,
            :to_port    => to_port,
            :protocol   => protocol}.merge(rule)
  connection.ec2.modify_security_group(:authorize, :ingress, group_id, params)
end

#clear_out_ec2_security_group(name, description) ⇒ Object

EC2 ###



171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/maws/commands/set-security-groups.rb', line 171

def clear_out_ec2_security_group(name, description)
  description[:aws_perms].each { |permission|
    group_id = description[:group_id]

    revoke_params = if permission[:group_id]
      # this is a group rule
      {:groups => {permission[:owner] => permission[:group_id]}}
    else
      {:cidr_ip => permission[:cidr_ips]}
    end

    revoke_params[:protocol] = permission[:protocol]
    revoke_params[:from_port] = permission[:from_port]
    revoke_params[:to_port] = permission[:to_port]

    info "        revoking #{revoke_params.inspect}"
    connection.ec2.modify_security_group(:revoke, :ingress,
      group_id, revoke_params);
  }
end

#clear_out_rds_security_group(name, description) ⇒ Object

RDS ###



203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/maws/commands/set-security-groups.rb', line 203

def clear_out_rds_security_group(name, description)
  description[:ec2_security_groups].each {|group_permission|
    info "        revoking from #{group_permission[:owner_id]}/#{group_permission[:name]}"
    safe_revoke_rds_security_group(name,
                                     :ec2_security_group_owner => group_permission[:owner_id],
                                     :ec2_security_group_name  => group_permission[:name])
  }

  description[:ip_ranges].each {|ip_permission|
    info "        revoking from #{ip_permission[:cidrip].inspect}"
    safe_revoke_rds_security_group(name, :cidrip => ip_permission[:cidrip])
  }
end

#clear_out_security_group(name, definition) ⇒ Object



111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/maws/commands/set-security-groups.rb', line 111

def clear_out_security_group(name, definition)
  info "    clearing out #{name}"
  description =  definition[:description]
  return unless description && !description.empty?


  if definition[:service] == :ec2
    clear_out_ec2_security_group(name, description)
  elsif definition[:service] == :rds
    clear_out_rds_security_group(name, description)
  end
end

#create_security_group(name, definition) ⇒ Object



101
102
103
104
105
106
107
108
109
# File 'lib/maws/commands/set-security-groups.rb', line 101

def create_security_group(name, definition)
  info "CREATING #{name}"

  if definition[:service] == :ec2
    connection.ec2.create_security_group(name, name) # description same as name
  elsif definition[:service] == :rds
    connection.rds.create_db_security_group(name, name)
  end
end

#descriptionObject



4
5
6
# File 'lib/maws/commands/set-security-groups.rb', line 4

def description
  "set-security-groups - create or update security groups from security rules configuration"
end

#resolve_owner_and_group_id_for_group_definition(group_definition) ⇒ Object

Raises:



252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
# File 'lib/maws/commands/set-security-groups.rb', line 252

def resolve_owner_and_group_id_for_group_definition(group_definition)
  # 'owner_id/group_name' => ['owner_id', 'group_name'] OR 'group_name' => ['group_name']
  owner_id_and_group = group_definition.split('/')

  group = owner_id_and_group.pop
  owner_id = owner_id_and_group.pop

  group_id = if group =~ /sg-/
    group
  else
    # look up name
    definition = @security_group_definitions.values.find {|definition| definition[:group_name] == group}

    definition[:group_id] if definition
  end

  raise BadSecurityRule.new("Can't find group id (sg-...) for group name #{group}") unless group_id

  # use this account for owner id
  owner_id = @security_group_definitions.values.first[:owner_id] unless owner_id

  [owner_id, group_id]
end

#resolve_owner_and_group_id_for_role_definition(role_name) ⇒ Object



241
242
243
244
245
246
247
248
249
250
# File 'lib/maws/commands/set-security-groups.rb', line 241

def resolve_owner_and_group_id_for_role_definition(role_name)
  definition = @security_group_definitions.values.find {|definition| definition[:role] == role_name}
  if definition
    p [definition[:owner_id], definition[:group_id]]
    [definition[:owner_id], definition[:group_id]]
  else
    security_group_name = "#{@config.profile.name}-#{role_name}"
    raise BadSecurityRule.new("Can't find security group '#{security_group_name}' for role '#{role_name}'")
  end
end

#run!Object



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/maws/commands/set-security-groups.rb', line 15

def run!
  rules = @config.security_rules
  if rules.empty?
    info "No security rules to create security groups from"
    return
  end

  @security_group_definitions = {}
  rules.keys.map {|rules_set_name|
    security_group_name = "#{@config.profile.name}-#{rules_set_name}"
    service = if rules_set_name == 'ec2_default'
      security_group_name = 'ec2_default'
      :ec2
    elsif rules_set_name == 'rds_default'
      security_group_name = 'rds_default'
      :rds
    else
      @config.combined[rules_set_name].service
    end

    @security_group_definitions[security_group_name] = {
            :role => rules_set_name,
            :rules => rules[rules_set_name],
            :service => service.to_sym}
  }

  update_definitions_with_descriptions(@security_group_definitions)


  @security_group_definitions.each { |name, definition|
    create_security_group(name, definition) unless definition[:description]
  }

  update_definitions_with_descriptions(@security_group_definitions)
  begin
    update_definitions_rules_with_real_ids(@security_group_definitions)
  rescue BadSecurityRule => err
    info "Bad security rule: #{err.message}"
    info "No AWS security rules will be updated."
    return
  end

  @security_group_definitions.each { |name, definition|
    info "#{definition[:service]} security group #{name}"

    clear_out_security_group(name, definition)

    # rds takes a while to propagate. this code handles errors with retries, but this sleep keeps the noise down
    if definition[:service] == :rds
      info "...waiting for revoke to take effect..."
      sleep 20
    end

    set_security_group(name, definition)

    info "done\n\n"
  }
end

#safe_authorize_rds_security_group_rule(name, params) ⇒ Object



319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
# File 'lib/maws/commands/set-security-groups.rb', line 319

def safe_authorize_rds_security_group_rule(name, params)
  rds_logger = connection.rds.logger
  connection.rds.logger = NullLogger.new

  tries = 4

  loop do
    if tries <= 0
      info "!!!!!! FAILED TO AUTHORIZE: #{name} #{params.inspect} !!!!!!"
      return
    end

    begin
      connection.rds.authorize_db_security_group_ingress(name, params)
      info "            (succesfully authorized)"
      return
    rescue Exception => e
      if e.message =~ /AuthorizationAlreadyExists/
        info "            (authorization already exists. probably means it is still being revoked. retrying...)"
        sleep 10
        tries -= 1

      elsif e.message =~ /InvalidDBSecurityGroupState: Cannot authorize an authorization that is in the revoking state/
        info "            (currently revoking - will be revoked shortly. will retry authorizing then)"
        sleep 10
        tries -= 1

      elsif e.message =~ /InvalidDBSecurityGroupState: Cannot authorize an authorization that is in the authorizing state/
        info "            (already authorizing. waiting and retrying to confirm...)"
        sleep 10
        tries -= 1

      else
        sleep 1
        tries -= 1
      end
    end
  end
ensure
  connection.rds.logger = rds_logger
end

#safe_revoke_rds_security_group(name, params) ⇒ Object



277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
# File 'lib/maws/commands/set-security-groups.rb', line 277

def safe_revoke_rds_security_group(name, params)
  rds_logger = connection.rds.logger
  connection.rds.logger = NullLogger.new

  tries = 4

  loop do
    if tries <= 0
      info "!!!!!! FAILED TO REVOKE: #{name} #{params.inspect} !!!!!!"
      return
    end

    begin
      connection.rds.revoke_db_security_group_ingress(name, params)
      info "            (succesfully revoked)"
      return
    rescue Exception => e
      if e.message =~ /AuthorizationNotFound/
        info "            (not authorized. nothing to do here)"
        return

      elsif e.message =~ /InvalidDBSecurityGroupState: Cannot revoke an authorization that is in the revoking state/
        info "            (already revoking - will be revoked shortly)"
        sleep 10
        tries -= 1

      elsif e.message =~ /InvalidDBSecurityGroupState: Cannot revoke an authorization that is in the authorizing state/
        info "            (not yet finished authorizing. waiting and retrying...)"
        sleep 10
        tries -= 1

      else
        sleep 1
        tries -= 1
      end
    end
  end
ensure
  connection.rds.logger = rds_logger
end

#set_security_group(name, definition) ⇒ Object



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/maws/commands/set-security-groups.rb', line 124

def set_security_group(name, definition)
  info "    adding security rules to #{name}"
  service = definition[:service]

  definition[:rules].each { |rule|
    rule.port ||= 0
    rule.port_from ||= (rule.port)
    rule.port_to ||= (rule.port)

    protocol_description = service == :ec2 ? "#{rule.protocol}:#{rule.port_from}-#{rule.port_to}" : ""

    if rule.group
      info "        allow from #{rule.owner_id}/#{rule.group_id} (#{rule.group}) #{protocol_description}"
      if service == :ec2
        authorize_ec2_security_group_rule(definition[:group_id], rule.port_from, rule.port_to, rule.protocol,
                                          :groups => {rule.owner_id => rule.group_id})
      else
        safe_authorize_rds_security_group_rule(definition[:group_id],
                                          :ec2_security_group_owner => rule.owner_id,
                                          :ec2_security_group_name => rule.group_name)
      end
    elsif rule.role
      info "        allow from #{rule.owner_id}/#{rule.group_id} (role '#{rule.role}') #{protocol_description}"
      if service == :ec2
        authorize_ec2_security_group_rule(definition[:group_id], rule.port_from, rule.port_to, rule.protocol,
                                          :groups => {rule.owner_id => rule.group_id})
      else
        safe_authorize_rds_security_group_rule(definition[:group_id],
                                          :ec2_security_group_owner => rule.owner_id,
                                          :ec2_security_group_name => rule.group_id)
      end
    elsif rule.cidr
      info "        allow from #{rule.cidr.inspect} #{protocol_description}"
      if service == :ec2
        authorize_ec2_security_group_rule(definition[:group_id], rule.port_from, rule.port_to, rule.protocol,
                                          :cidr_ips => [rule.cidr].flatten)
      else
        [rule.cidr].flatten.each { |cidr|
          safe_authorize_rds_security_group_rule(definition[:group_id], :cidrip => cidr)
        }
      end
    end
  }
end

#update_definitions_rules_with_real_ids(definitions) ⇒ Object



218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/maws/commands/set-security-groups.rb', line 218

def update_definitions_rules_with_real_ids(definitions)
  definitions.each { |name, definition|
    definition[:rules].each { |rule|
      next if rule.cidr

      owner_id, group_id = if rule.role
        resolve_owner_and_group_id_for_role_definition(rule.role)
      elsif rule.group
        resolve_owner_and_group_id_for_group_definition(rule.group)
      end

      unless owner_id && group_id
        raise BadSecurityRule.new("Can't resolve security group id and owner for rule #{rule.inspect}}")
      else
        rule.owner_id = owner_id
        rule.group_id = group_id
      end
    }
  }

end

#update_definitions_with_descriptions(security_group_definitions) ⇒ Object

sets :description, :group_id, :group_name and :owner_id for each definition



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/maws/commands/set-security-groups.rb', line 75

def update_definitions_with_descriptions(security_group_definitions)
  ec2_sg_descriptions = connection.ec2.describe_security_groups
  rds_sg_descriptions = connection.rds.describe_db_security_groups

  # attach existing descriptions (if they exist) to sec group definition
  security_group_definitions.each {|name, definition|
    if definition[:service] == :ec2
      definition[:description] = ec2_sg_descriptions.find {|description| description[:aws_group_name] == name }
      if definition[:description]
        definition[:group_id] = definition[:description][:group_id]
        definition[:group_name] = definition[:description][:aws_group_name]
        definition[:owner_id] = definition[:description][:aws_owner]
      end
    elsif definition[:service] == :rds
      definition[:description] = rds_sg_descriptions.find {|description| description[:name] == name }
      if definition[:description]
        definition[:group_id] = definition[:description][:name]
        definition[:group_name] = definition[:description][:name]
        definition[:owner_id] = definition[:description][:owner_id]
      end
    end
  }
end

#verify_optionsObject

override from command to ignore selection



9
10
11
12
13
# File 'lib/maws/commands/set-security-groups.rb', line 9

def verify_options
  if @config.command_line.region.blank?
    Trollop::die "Region must be specified in command line options OR as 'default_region' in #{@config.config.paths.config}"
  end
end