class Cumulus::SecurityGroups::SecurityGroupConfig

Public: An object representing configuration for a security group

Attributes

description[R]
inbound[R]
includes[R]
name[R]
outbound[R]
tags[R]
vpc_id[R]

Public Class Methods

new(name, vpc_id, json = nil) click to toggle source

Public: Constructor.

name - the name of the security group vpc_id - the id of the vpc the security group belongs in json - a hash containing the JSON configuration for the security group

# File lib/security/models/SecurityGroupConfig.rb, line 28
def initialize(name, vpc_id, json = nil)
  @name = name
  @vpc_id = vpc_id
  if !json.nil?
    @description = if !json["description"].nil? then json["description"] else "" end
    @tags = if !json["tags"].nil? then json["tags"] else {} end


    includes = (json["rules"]["includes"] || []).map { |rule| Loader.rule(rule) }
    inbound_includes = includes.reduce([]) { |sofar, inc| sofar + (inc["inbound"] || []) }.flatten.compact
    outbound_includes = includes.reduce([]) { |sofar, inc| sofar + (inc["outbound"] || []) }.flatten.compact

    combined_inbound = (json["rules"]["inbound"] || []) + inbound_includes
    @inbound = combined_inbound.map(&RuleConfig.method(:expand_ports)).flatten

    combined_outbound = (json["rules"]["outbound"] || []) + outbound_includes
    @outbound = if !json["rules"]["outbound"].nil?
      combined_outbound.map(&RuleConfig.method(:expand_ports)).flatten
    else
      if Configuration.instance.security.outbound_default_all_allowed
        [RuleConfig.allow_all]
      else
        outbound_includes
      end
    end
  end
end

Public Instance Methods

diff(aws) click to toggle source

Public: Produce an array of the differences between this local configuration and the configuration in AWS

aws - the aws resource

Returns an array of the SecurityGroupDiffs that were found

# File lib/security/models/SecurityGroupConfig.rb, line 62
def diff(aws)
  diffs = []

  if @description != aws.description
    diffs << SecurityGroupDiff.new(SecurityGroupChange::DESCRIPTION, aws, self)
  end

  if @tags != Hash[aws.tags.map { |t| [t.key, t.value] }]
    diffs << SecurityGroupDiff.new(SecurityGroupChange::TAGS, aws, self)
  end

  inbound_diffs = diff_rules(@inbound, aws.ip_permissions)
  if !inbound_diffs.empty?
    diffs << SecurityGroupDiff.inbound(aws, self, inbound_diffs)
  end

  outbound_diffs = diff_rules(@outbound, aws.ip_permissions_egress)
  if !outbound_diffs.empty?
    diffs << SecurityGroupDiff.outbound(aws, self, outbound_diffs)
  end

  diffs
end
populate!(aws) click to toggle source

Public: Populate this SecurityGroupConfig from an AWS resource

aws - the aws resource

# File lib/security/models/SecurityGroupConfig.rb, line 89
def populate!(aws)
  @vpc_id = aws.vpc_id
  @description = aws.description
  @tags = Hash[aws.tags.map { |t| [t.key, t.value] }]
  @inbound = combine_rules(aws.ip_permissions.map { |rule| RuleConfig.from_aws(rule) })
  @outbound = combine_rules(aws.ip_permissions_egress.map { |rule| RuleConfig.from_aws(rule) })
end
pretty_json() click to toggle source

Public: Get the config as a prettified JSON string.

Returns the JSON string

# File lib/security/models/SecurityGroupConfig.rb, line 100
def pretty_json
  JSON.pretty_generate({
    "description" => @description,
    "tags" => @tags,
    "rules" => {
      "inbound" => @inbound.map(&:hash),
      "outbound" => @outbound.map(&:hash),
    }
  }.reject { |k, v| v.nil? })
end

Private Instance Methods

combine_rules(rules) click to toggle source

Internal: Combine rules that have the same ports and security groups to create the compact version used by cumulus config.

rules - an array of the rules to combine

Returns an array of compact rules

# File lib/security/models/SecurityGroupConfig.rb, line 141
def combine_rules(rules)
  # separate out icmp rules
  all_rules = rules.map(&RuleMigration.method(:from_rule_config))
  icmp_rules = all_rules.select { |rule| rule.protocol == "icmp" }

  # first we find the ones that have the same protocol and port
  other_rules = all_rules.reject { |rule| rule.protocol == "icmp" }.group_by do |rule|
    [rule.protocol, rule.ports]
  # next, we combine the matching rules together
  end.flat_map do |_, matches|
    if matches.size == 1
      matches
    else
      matches[1..-1].inject(matches[0]) { |prev, cur| prev.combine_allowed(cur) }
    end
  # now, try to find ones that have the same groups of allowed entities and protocol
  end.group_by do |rule|
    [rule.protocol, rule.security_groups, rule.subnets]
  # finally, we'll combine ports for the matches
  end.flat_map do |_, matches|
    if matches.size == 1
      matches
    else
      matches[1..-1].inject(matches[0]) { |prev, cur| prev.combine_ports(cur) }
    end
  end

  icmp_rules + other_rules
end
diff_rules(local_rules, aws_rules) click to toggle source

Internal: Determine changes in rules

local_rules - the rules defined locally aws_rules - the rules in AWS

Returns an array of RuleDiffs that represent differences between local and AWS configuration

# File lib/security/models/SecurityGroupConfig.rb, line 119
def diff_rules(local_rules, aws_rules)
  diffs = []

  # get the aws config into a format that mirrors cumulus so we can compare
  aws = aws_rules.map do |rule|
    RuleConfig.from_aws(rule)
  end
  aws_hashes = aws.flat_map(&:hash)
  local_hashes = local_rules.flat_map(&:hash)

  diffs << local_hashes.reject { |i| aws_hashes.include?(i) }.map { |l| RuleDiff.added(RuleConfig.new(l)) }
  diffs << aws_hashes.reject { |a| local_hashes.include?(a) }.map { |a| RuleDiff.removed(RuleConfig.new(a)) }

  diffs.flatten
end