class MU::Cloud::CloudFormation::FirewallRule

A firewall ruleset as configured in {MU::Config::BasketofKittens::firewall_rules}

Attributes

cfm_name[R]
cfm_template[R]
cloud_id[R]
config[R]
mu_name[R]

Public Class Methods

new(mommacat: nil, kitten_cfg: nil, mu_name: nil, cloud_id: nil) click to toggle source

@param mommacat [MU::MommaCat]: A {MU::Mommacat} object containing the deploy of which this resource is/will be a member. @param kitten_cfg [Hash]: The fully parsed and resolved {MU::Config} resource descriptor as defined in {MU::Config::BasketofKittens::firewall_rules}

# File modules/mu/providers/cloudformation/firewall_rule.rb, line 35
def initialize(mommacat: nil, kitten_cfg: nil, mu_name: nil, cloud_id: nil)
  @deploy = mommacat
  @config = kitten_cfg
  @cloud_id ||= cloud_id
  if !mu_name.nil?
    @mu_name = mu_name
  elsif @config['scrub_mu_isms']
    @mu_name = @config['name']
  else
    if !@vpc.nil?
      @mu_name = @deploy.getResourceName(@config['name'], need_unique_string: true)
    else
      @mu_name = @deploy.getResourceName(@config['name'])
    end
  end

end
validateConfig(acl, config) click to toggle source

Cloud-specific pre-processing of {MU::Config::BasketofKittens::firewall_rules}, bare and unvalidated. @param acl [Hash]: The resource to process and validate @param config [MU::Config]: The overall deployment config of which this resource is a member @return [Boolean]: True if validation succeeded, False otherwise

# File modules/mu/providers/cloudformation/firewall_rule.rb, line 138
def self.validateConfig(acl, config)
  # Just use the AWS implemention
  MU::Cloud.resourceClass("AWS", "FirewallRule").validateConfig(acl, config)
end

Private Class Methods

cleanup(*args) click to toggle source

Placeholder. This is a NOOP for CloudFormation, which doesn't build resources directly.

# File modules/mu/providers/cloudformation/firewall_rule.rb, line 285
def self.cleanup(*args)
  MU.log "cleanup() not implemented for CloudFormation layer", MU::DEBUG
  nil
end
find(*args) click to toggle source

Placeholder. This is a NOOP for CloudFormation, which doesn't build resources directly.

# File modules/mu/providers/cloudformation/firewall_rule.rb, line 279
def self.find(*args)
  MU.log "find() not implemented for CloudFormation layer", MU::DEBUG
  nil
end
isGlobal?() click to toggle source

Does this resource type exist as a global (cloud-wide) artifact, or is it localized to a region/zone? @return [Boolean]

# File modules/mu/providers/cloudformation/firewall_rule.rb, line 300
def self.isGlobal?
  MU::Cloud.resourceClass("AWS", "FirewallRule").isGlobal?
end
schema(config) click to toggle source

Cloud-specific configuration properties. @param config [MU::Config]: The calling MU::Config object @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource

# File modules/mu/providers/cloudformation/firewall_rule.rb, line 293
def self.schema(config)
  MU::Cloud.resourceClass("AWS", "FirewallRule").schema(config)
end

Public Instance Methods

addRule(hosts, proto: "tcp", port: nil, egress: false, port_range: "0-65535" ) click to toggle source

Insert a rule into an existing security group.

@param hosts [Array<String>]: An array of CIDR network addresses to which this rule will apply. @param proto [String]: One of “tcp,” “udp,” or “icmp” @param port [Integer]: A port number. Only valid with udp or tcp. @param egress [Boolean]: Whether this is an egress ruleset, instead of ingress. @param port_range [String]: A port range descriptor (e.g. 0-65535). Only valid with udp or tcp. @return [void]

# File modules/mu/providers/cloudformation/firewall_rule.rb, line 96
def addRule(hosts,
            proto: "tcp",
            port: nil,
            egress: false,
            port_range: "0-65535"
)
  rule = Hash.new
  rule["proto"] = proto
  if hosts.is_a?(String)
    rule["hosts"] = [hosts]
  else
    rule["hosts"] = hosts
  end
  if port != nil
    port = port.to_s if !port.is_a?(String)
    rule["port"] = port
  else
    rule["port_range"] = port_range
  end
  ec2_rule = convertToEc2([rule])

  begin
    if egress
      MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).authorize_security_group_egress(
          group_id: @cloud_id,
          ip_permissions: ec2_rule
      )
    else
      MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).authorize_security_group_ingress(
          group_id: @cloud_id,
          ip_permissions: ec2_rule
      )
    end
  rescue Aws::EC2::Errors::InvalidPermissionDuplicate => e
    MU.log "Attempt to add duplicate rule to #{@cloud_id}", MU::DEBUG, details: ec2_rule
  end
end
create() click to toggle source

Populate @cfm_template with a resource description for this security group in CloudFormation language.

# File modules/mu/providers/cloudformation/firewall_rule.rb, line 55
        def create
          @cfm_name, @cfm_template = MU::Cloud::CloudFormation.cloudFormationBase(self.class.cfg_name, self, tags: @config['tags'], scrub_mu_isms: @config['scrub_mu_isms']) if @cfm_template.nil?
          MU::Cloud::CloudFormation.setCloudFormationProp(@cfm_template[@cfm_name], "GroupDescription", @mu_name)

          if !@config['vpc'].nil? and !@config['vpc']['vpc_id'].nil?
            MU::Cloud::CloudFormation.setCloudFormationProp(@cfm_template[@cfm_name], "VpcId", @config['vpc']['vpc_id'])
          elsif @dependencies.has_key?("vpc") and !@config["vpc"]["vpc_name"].nil? and @dependencies["vpc"].has_key?(@config["vpc"]["vpc_name"])
            MU::Cloud::CloudFormation.setCloudFormationProp(@cfm_template[@cfm_name], "DependsOn", @dependencies["vpc"][@config["vpc"]["vpc_name"]].cloudobj.cfm_name)
            MU::Cloud::CloudFormation.setCloudFormationProp(@cfm_template[@cfm_name], "VpcId", { "Ref" => @dependencies["vpc"][@config["vpc"]["vpc_name"]].cloudobj.cfm_name })
          end
          egress = false
          egress = true if !@cfm_template[@cfm_name]["VpcId"].nil?
          # XXX the egress logic here is a crude hack, this really needs to be
          # done at config level
          setRules(
            @config['rules'],
            add_to_self: @config['self_referencing'],
            ingress: true,
            egress: egress
          )
#          pp @cfm_template
        end
groom() click to toggle source

Called by {MU::Deploy#createResources}

# File modules/mu/providers/cloudformation/firewall_rule.rb, line 79
def groom
  create
end
notify() click to toggle source

Log metadata about this ruleset to the currently running deployment

# File modules/mu/providers/cloudformation/firewall_rule.rb, line 84
def notify
  {}
end

Private Instance Methods

convertToEc2(rules) click to toggle source

Convert our config languages description of firewall rules into Amazon's. This rule structure is as defined in MU::Config.

# File modules/mu/providers/cloudformation/firewall_rule.rb, line 191
        def convertToEc2(rules)
          ec2_rules = []
          if rules != nil
            rules.each { |rule|
              ec2_rule = Hash.new
              rule['proto'] = "tcp" if rule['proto'].nil? or rule['proto'].empty?
              ec2_rule[:ip_protocol] = rule['proto']

              p_start = nil
              p_end = nil
              if rule['port_range']
                p_start, p_end = rule['port_range'].split(/\s*-\s*/)
              elsif rule['port']
                p_start = rule['port']
                p_end = rule['port']
              elsif rule['proto'] != "icmp"
                raise MuError, "Can't create a TCP or UDP security group rule without specifying ports: #{rule}"
              end
              if rule['proto'] != "icmp"
                if p_start.nil? or p_end.nil?
                  raise MuError, "Got nil ports out of rule #{rule}"
                end
                ec2_rule[:from_port] = p_start.to_i
                ec2_rule[:to_port] = p_end.to_i
              else
                ec2_rule[:from_port] = -1
                ec2_rule[:to_port] = -1
              end

              if (!defined? rule['hosts'] or !rule['hosts'].is_a?(Array)) and
                  (!defined? rule['sgs'] or !rule['sgs'].is_a?(Array)) and
                  (!defined? rule['lbs'] or !rule['lbs'].is_a?(Array))
                raise MuError, "One of 'hosts', 'sgs', or 'lbs' in rules provided to createEc2SG must be an array."
              end
              ec2_rule[:ip_ranges] = []
              ec2_rule[:user_id_group_pairs] = []

              if !rule['hosts'].nil?
                rule['hosts'].each { |cidr|
                  next if cidr.nil? # XXX where is that coming from?
                  cidr = cidr + "/32" if cidr.match(/^\d+\.\d+\.\d+\.\d+$/)
                  ec2_rule[:ip_ranges] << {cidr_ip: cidr}
                }
              end

              if !rule['lbs'].nil?
# XXX This is a dopey place for this, dependencies() should be doing our legwork
                rule['lbs'].each { |lb_name|
                  if @dependencies.has_key?("loadbalancer") and @dependencies["loadbalancer"].has_key?(lb_name)
#                    MU::Cloud::CloudFormation.setCloudFormationProp(@cfm_template[@cfm_name], "DependsOn", @dependencies["loadbalancer"][lb_name].cloudobj.cfm_name)
                  end
                }
              end

              if !rule['sgs'].nil?
                rule['sgs'].each { |sg_name|
# XXX This is a dopey place for this, dependencies() should be doing our legwork
                  if @dependencies.has_key?("firewall_rule") and @dependencies["firewall_rule"].has_key?(sg_name)
#                    MU::Cloud::CloudFormation.setCloudFormationProp(@cfm_template[@cfm_name], "DependsOn", @dependencies["firewall_rule"][sg_name].cloudobj.cfm_name)
                  end
                }
              end

              if !ec2_rule[:user_id_group_pairs].nil? and
                  ec2_rule[:user_id_group_pairs].size > 0 and
                  !ec2_rule[:ip_ranges].nil? and
                  ec2_rule[:ip_ranges].size > 0
                MU.log "Cannot specify ip_ranges and user_id_group_pairs", MU::ERR
                raise MuError, "Cannot specify ip_ranges and user_id_group_pairs"
              end

              ec2_rule.delete(:ip_ranges) if ec2_rule[:ip_ranges].size == 0
              ec2_rule.delete(:user_id_group_pairs) if ec2_rule[:user_id_group_pairs].size == 0

              if !ec2_rule[:user_id_group_pairs].nil? and
                  ec2_rule[:user_id_group_pairs].size > 0
                ec2_rule.delete(:ip_ranges)
              elsif !ec2_rule[:ip_ranges].nil? and
                  ec2_rule[:ip_ranges].size > 0
                ec2_rule.delete(:user_id_group_pairs)
              end
              ec2_rules << ec2_rule
            }
          end
          return ec2_rules
        end
setRules(rules, add_to_self: false, ingress: true, egress: false) click to toggle source

Manufacture an EC2 security group. The second parameter, rules, is an “ingress_rules” structure parsed and validated by MU::Config.

# File modules/mu/providers/cloudformation/firewall_rule.rb, line 149
def setRules(rules, add_to_self: false, ingress: true, egress: false)
  return if rules.nil? or rules.size == 0

  if add_to_self
    rules.each { |rule|
      if rule['sgs'].nil?
        new_rule = rule.clone
        new_rule.delete('hosts')
        rule['sgs'] = Array.new if rule['sgs'].nil?
        rule['sgs'] << { "Ref" => @cfm_name }
      end
    }
  end

  ec2_rules = convertToEc2(rules)

  # Creating an empty security group is ok, so don't freak out if we get
  # a null rule list.
  if !ec2_rules.nil?
    ec2_rules.each { |rule|
      next if rule.nil? or rule[:ip_ranges].nil? # XXX whaaat
      rule[:ip_ranges].each { |cidr|
        MU::Cloud::CloudFormation.setCloudFormationProp(
          @cfm_template[@cfm_name],
          "SecurityGroupIngress",
          {
            "IpProtocol" => rule[:ip_protocol],
            "FromPort" => rule[:from_port],
            "ToPort" => rule[:to_port],
            "CidrIp" => cidr[:cidr_ip]
          }
        )
      }
    }
  end

end