File: security_group.rb

package info (click to toggle)
ruby-aws-sdk 1.66.0-1
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 6,808 kB
  • ctags: 4,854
  • sloc: ruby: 28,354; makefile: 7
file content (482 lines) | stat: -rw-r--r-- 15,661 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
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
317
318
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
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
# Copyright 2011-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
#     http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.

module AWS
  class EC2

    # Represents a security group in EC2.
    #
    # @attr_reader [String] description The short informal description
    #   given when the group was created.
    #
    # @attr_reader [String] name The name of the security group.
    #
    # @attr_reader [String] owner_id The security group owner's id.
    #
    # @attr_reader [String,nil] vpc_id If this is a VPC security group,
    #   vpc_id is the ID of the VPC this group was created in.
    #   Returns false otherwise.
    #
    class SecurityGroup < Resource

      autoload :IpPermission, 'aws/ec2/security_group/ip_permission'
      autoload :IpPermissionCollection, 'aws/ec2/security_group/ip_permission_collection'
      autoload :IngressIpPermissionCollection, 'aws/ec2/security_group/ip_permission_collection'
      autoload :EgressIpPermissionCollection, 'aws/ec2/security_group/ip_permission_collection'

      include TaggedItem

      def initialize security_group_id, options = {}
        @security_group_id = security_group_id
        super
      end

      # @return [String]
      attr_reader :security_group_id

      alias_method :group_id, :security_group_id

      alias_method :id, :security_group_id

      attribute :name, :from => :group_name, :static => true

      attribute :owner_id, :static => true

      attribute :vpc_id, :static => true

      attribute :description, :from => :group_description, :static => true

      attribute :ip_permissions_list, :from => :ip_permissions

      attribute :ip_permissions_list_egress, :from => :ip_permissions_egress

      populates_from(:describe_security_groups) do |resp|
        resp[:security_group_index][id]
      end

      # @return [InstanceCollection] Returns an instance collection that will
      #   only enumerate instances in this security group.
      def instances
        instances = InstanceCollection.new(:config => config)
        if vpc?
          instances.filter('instance.group-id', [group_id])
        else
          instances.filter('group-id', [group_id])
        end
      end

      # @return [Boolean] True if the security group exists.
      def exists?
        client.describe_security_groups(:filters => [
          { :name => "group-id", :values => [id] }
        ]).security_group_index.key?(id)
      end

      # Returns true if this security group is a VPC security group and
      # not an EC2 security group.  VPC security groups belong to a VPC
      # subnet and can have egress rules.
      # @return [Boolean] Returns true if this is a VPC security group and
      #   false if this is an EC2 security group.
      def vpc?
        vpc_id ? true : false
      end

      # @return [VPC,nil] Returns the VPC this security group belongs to,
      #   or nil if this is not a VPC security group.
      def vpc
        if vpc_id
          VPC.new(vpc_id, :config => config)
        end
      end

      # @return [SecurityGroup::IngressIpPermissionCollection] Returns a
      #   collection of {IpPermission} objects that represents all of
      #   the (ingress) permissions this security group has
      #   authorizations for.
      def ingress_ip_permissions
        IngressIpPermissionCollection.new(self, :config => config)
      end
      alias_method :ip_permissions, :ingress_ip_permissions

      # @return [SecurityGroup::EgressIpPermissionCollection] Returns a
      #   collection of {IpPermission} objects that represents all of
      #   the egress permissions this security group has authorizations for.
      def egress_ip_permissions
        EgressIpPermissionCollection.new(self, :config => config)
      end

      # Adds ingress rules for ICMP pings.  Defaults to 0.0.0.0/0 for
      # the list of allowed IP ranges the ping can come from.
      #
      #   security_group.allow_ping # anyone can ping servers in this group
      #
      #   # only allow ping from a particular address
      #   security_group.allow_ping('123.123.123.123/0')
      #
      # @param [String] sources One or more IP ranges to allow ping from.
      #   Defaults to 0.0.0.0/0
      #
      # @return [nil]
      #
      def allow_ping *sources
        sources << '0.0.0.0/0' if sources.empty?
        authorize_ingress('icmp', -1, *sources)
      end

      # Removes ingress rules for ICMP pings.  Defaults to 0.0.0.0/0 for
      # the list of IP ranges to revoke.
      #
      # @param [String] sources One or more IP ranges to disallow ping from.
      #   Defaults to 0.0.0.0/0
      #
      # @return [nil]
      #
      def disallow_ping *sources
        sources << '0.0.0.0/0' if sources.empty?
        revoke_ingress('icmp', -1, *sources)
      end

      # Add an ingress rules to this security group.
      # Ingress rules permit inbound traffic over a given protocol for
      # a given port range from one or more source ip addresses.
      #
      # This example grants the whole internet (0.0.0.0/0) access to port 80
      # over TCP (HTTP web traffic).
      #
      #     security_group.authorize_ingress(:tcp, 80)
      #
      # You can specify port ranges as well:
      #
      #     # ftp
      #     security_group.authorize_ingress(:tcp, 20..21)
      #
      # ## Sources
      #
      # Security groups accept ingress trafic from:
      #
      # * CIDR IP addresses
      # * security groups
      # * load balancers
      #
      # ### Ip Addresses
      #
      # In the following example allow incoming SSH from a list of
      # IP address ranges.
      #
      #     security_group.authorize_ingress(:tcp, 22,
      #       '111.111.111.111/0', '222.222.222.222/0')
      #
      # ### Security Groups
      #
      # To autohrize ingress traffic from all EC2 instance in another
      # security group, just pass the security group:
      #
      #     web = security_groups.create('webservers')
      #     db = security_groups.create('database')
      #     db.authorize_ingress(:tcp, 3306, web)
      #
      # You can also pass a hash of security group details instead of
      # a {SecurityGroup} object.
      #
      #     # by security group name
      #     sg.authorize_ingress(:tcp, 80, { :group_name => 'other-group' })
      #
      #     # by security group id
      #     sg.authorize_ingress(:tcp, 80, { :group_id => 'sg-1234567' })
      #
      # If the security group belongs to a different account, just make
      # sure it has the correct owner ID populated:
      #
      #     not_my_sg = SecurityGroup.new('sg-1234567', :owner_id => 'abcxyz123')
      #     my_sg.authorize_ingress(:tcp, 80, not_my_sg)
      #
      # You can do the same with a hash as well (with either `:group_id`
      # or `:group_name`):
      #
      #     sg.authorize_ingress(:tcp, 21..22, { :group_id => 'sg-id', :user_id => 'abcxyz123' })
      #
      # ### Load Balancers
      #
      # If you use ELB to manage load balancers, then you need to add
      # ingress permissions to the security groups they route traffic into.
      # You can do this by passing the {ELB::LoadBalancer} into
      # authorize_ingress:
      #
      #     load_balancer = AWS::ELB.new.load_balancers['web-load-balancer']
      #
      #     sg.authorize_ingress(:tcp, 80, load_balancer)
      #
      # ### Multiple Sources
      #
      # You can provide multiple sources each time you call authorize
      # ingress, and you can mix and match the source types:
      #
      #     sg.authorize_ingress(:tcp, 80, other_sg, '1.2.3.4/0', load_balancer)
      #
      # @param [String, Symbol] protocol Should be :tcp, :udp or :icmp
      #   or the string equivalent.
      #
      # @param [Integer, Range] ports The port (or port range) to allow
      #   traffic through.  You can pass a single integer (like 80)
      #   or a range (like 20..21).
      #
      # @param [Mixed] sources One or more CIDR IP addresses,
      #   security groups, or load balancers.  Security groups
      #   can be specified as hashes.
      #
      #   A security group hash must provide either `:group_id` or
      #   `:group_name` for the security group.  If the security group
      #   does not belong to you aws account then you must also
      #   provide `:user_id` (which can be an AWS account ID or alias).
      #
      # @return [nil]
      #
      def authorize_ingress protocol, ports, *sources
        client.authorize_security_group_ingress(
          :group_id => id,
          :ip_permissions => [ingress_opts(protocol, ports, sources)]
        )
        nil
      end

      # Revokes an ingress (inbound) ip permission.  This is the inverse
      # operation to {#authorize_ingress}.  See {#authorize_ingress}
      # for param and option documentation.
      #
      # @see #authorize_ingress
      #
      # @return [nil]
      #
      def revoke_ingress protocol, ports, *sources
        client.revoke_security_group_ingress(
          :group_id => id,
          :ip_permissions => [ingress_opts(protocol, ports, sources)]
        )
        nil
      end

      # Authorize egress (outbound) traffic for a VPC security group.
      #
      #     # allow traffic for all protocols/ports from the given sources
      #     security_group.authorize_egress('10.0.0.0/16', '10.0.0.1/16')
      #
      #     # allow tcp traffic outband via port 80
      #     security_group.authorize_egress('10.0.0.0/16',
      #       :protocol => :tcp, :ports => 80..80)
      #
      # @note Calling this method on a non-VPC security group raises an error.
      #
      # @overload authorize_egress(*sources, options = {})
      #
      #   @param [Mixed] sources One or more CIDR IP addresses,
      #     security groups or load balancers.  See {#authorize_ingress}
      #     for more information on accepted formats for sources.
      #
      #   @param [Hash] options
      #
      #   @option options [Symbol] :protocol (:any) The protocol name or number
      #     to authorize egress traffic for.  For a complete list of protocols
      #     see: [protocol-numbers.xml](http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xml)
      #
      #   @option options [Range<Integer>,Integer] :ports (nil) An optional
      #     port or range of ports.  This option is required depending on
      #     the protocol.
      #
      # @return [nil]
      #
      def authorize_egress *sources
        client.authorize_security_group_egress(
          :group_id => id,
          :ip_permissions => [egress_opts(sources)])
        nil
      end

      # Revokes an egress (outound) ip permission.  This is the inverse
      # operation to {#authorize_egress}.  See {#authorize_egress}
      # for param and option documentation.
      #
      # @see #authorize_egress
      #
      # @return [nil]
      #
      def revoke_egress *sources
        client.revoke_security_group_egress(
          :group_id => id,
          :ip_permissions => [egress_opts(sources)])
        nil
      end

      # Deletes this security group.
      #
      # If you attempt to delete a security group that contains
      # instances, or attempt to delete a security group that is referenced
      # by another security group, an error is raised. For example, if
      # security group B has a rule that allows access from security
      # group A, security group A cannot be deleted until the rule is
      # removed.
      # @return [nil]
      def delete
        client.delete_security_group(:group_id => id)
        nil
      end

      # @api private
      def <=> other
        self.id <=> other.id
      end

      # @api private
      def resource_type
        'security-group'
      end

      # @api private
      def inflected_name
        "group"
      end

      # @api private
      def self.describe_call_name
        :describe_security_groups
      end
      def describe_call_name; self.class.describe_call_name; end

      # @api private
      protected
      def ingress_opts protocol, ports, sources

        opts = {}
        opts[:ip_protocol] = protocol == :any ? '-1' : protocol.to_s.downcase

        unless ports.is_a?(Range)
          ports = Array(ports)
        end
        opts[:from_port] = ports.first.to_i
        opts[:to_port] = ports.last.to_i

        ips, groups = parse_sources(sources)

        opts[:ip_ranges] = ips unless ips.empty?
        opts[:user_id_group_pairs] = groups unless groups.empty?

        opts

      end

      # @api private
      protected
      def egress_opts args
        ensure_vpc do

          last = args.last

          if last.is_a?(Hash) and (last.key?(:protocol) or last.key?(:ports))
            # hashes at the end of egress methods could be a hash intedned
            # to be a source, like:
            #
            #     { :group_id => ..., :user_id => ... }
            #
            options = args.pop
          else
            options = {}
          end

          opts = {}

          opts[:ip_protocol] = [nil,:any, '-1'].include?(options[:protocol]) ?
            '-1' : options[:protocol].to_s.downcase

          if options[:ports]
            if options[:ports].is_a?(Range)
              ports = options[:ports]
            else
              ports = Array(options[:ports])
            end

            opts[:from_port] = ports.first.to_i
            opts[:to_port] = ports.last.to_i
          end

          ips, groups = parse_sources(args)

          opts[:ip_ranges] = ips unless ips.empty?
          opts[:user_id_group_pairs] = groups unless groups.empty?

          opts

        end
      end

      # @api private
      protected
      def parse_sources sources

        ips = []
        groups = []

        sources.each do |source|
          case source

          when String
            ips << { :cidr_ip => source }

          when SecurityGroup
            groups << { :group_id => source.id, :user_id => source.owner_id }

          when ELB::LoadBalancer
            groups << source.source_security_group

          when Hash

            # group name or id required
            unless source.has_key?(:group_id) or source.has_key?(:group_name)
              raise ArgumentError, 'invalid ip permission hash, ' +
                'must provide :group_id or :group_name'
            end

            # prevent typos
            unless source.keys - [:group_id, :group_name, :user_id] == []
              raise ArgumentError, 'invalid ip permission hash, ' +
                'only accepts the following keys, :group_id, :group_name, :user_id'
            end

            groups << source

          else
            raise ArgumentError, 'invalid ingress ip permission, ' +
              'expected CIDR IP address or SecurityGroup'
          end
        end

        ips << { :cidr_ip => '0.0.0.0/0' } if ips.empty? and groups.empty?

        [ips, groups]

      end

      # @api private
      protected
      def ensure_vpc &block
        raise 'operation permitted for VPC security groups only' unless vpc?
        yield
      end

      # @api private
      protected
      def find_in_response(resp)
        resp.security_group_index[id]
      end

    end
  end
end