Add support for AWS VPC Network ACLs

This commit is contained in:
Alex Coomans 2013-12-18 22:57:32 -07:00
parent 13090e998b
commit 9fc03381dc
21 changed files with 1285 additions and 0 deletions

View File

@ -251,6 +251,12 @@ module Fog
"sg-#{Fog::Mock.random_hex(8)}"
end
def self.network_acl_id
"acl-#{Fog::Mock.random_hex(8)}"
end
def self.network_acl_association_id
"aclassoc-#{Fog::Mock.random_hex(8)}"
end
def self.network_interface_id
"eni-#{Fog::Mock.random_hex(8)}"
end

View File

@ -24,6 +24,8 @@ module Fog
collection :internet_gateways
model :key_pair
collection :key_pairs
model :network_acl
collection :network_acls
model :network_interface
collection :network_interfaces
model :route_table
@ -60,6 +62,8 @@ module Fog
request :create_internet_gateway
request :create_image
request :create_key_pair
request :create_network_acl
request :create_network_acl_entry
request :create_network_interface
request :create_placement_group
request :create_route
@ -76,6 +80,8 @@ module Fog
request :delete_dhcp_options
request :delete_internet_gateway
request :delete_key_pair
request :delete_network_acl
request :delete_network_acl_entry
request :delete_network_interface
request :delete_security_group
request :delete_placement_group
@ -98,6 +104,7 @@ module Fog
request :describe_reserved_instances
request :describe_instance_status
request :describe_key_pairs
request :describe_network_acls
request :describe_network_interface_attribute
request :describe_network_interfaces
request :describe_route_tables
@ -131,6 +138,8 @@ module Fog
request :purchase_reserved_instances_offering
request :reboot_instances
request :release_address
request :replace_network_acl_association
request :replace_network_acl_entry
request :register_image
request :request_spot_instances
request :reset_network_interface_attribute
@ -214,6 +223,7 @@ module Fog
'ipPermissions' => [],
},
},
:network_acls => {},
:network_interfaces => {},
:snapshots => {},
:volumes => {},

View File

@ -0,0 +1,170 @@
require 'fog/core/model'
module Fog
module Compute
class AWS
class NetworkAcl < Fog::Model
ICMP = 1
TCP = 6
UDP = 17
identity :network_acl_id, :aliases => 'networkAclId'
attribute :vpc_id, :aliases => 'vpcId'
attribute :default
attribute :entries, :aliases => 'entrySet'
attribute :associations, :aliases => 'associationSet'
attribute :tags, :aliases => 'tagSet'
# Add an inbound rule, shortcut method for #add_rule
def add_inbound_rule(rule_number, protocol, rule_action, cidr_block, options = {})
add_rule(rule_number, protocol, rule_action, cidr_block, false, options)
end
# Add an outbound rule, shortcut method for #add_rule
def add_outbound_rule(rule_number, protocol, rule_action, cidr_block, options = {})
add_rule(rule_number, protocol, rule_action, cidr_block, true, options)
end
# Add a new rule
#
# network_acl.add_rule(100, Fog::Compute::AWS::NetworkAcl::TCP, 'allow', '0.0.0.0/0', true, 'PortRange.From' => 22, 'PortRange.To' => 22)
#
# ==== Parameters
# * rule_number<~Integer> - The rule number for the entry, between 100 and 32766
# * protocol<~Integer> - The IP protocol to which the rule applies. You can use -1 to mean all protocols.
# * rule_action<~String> - Allows or denies traffic that matches the rule. (either allow or deny)
# * cidr_block<~String> - The CIDR range to allow or deny
# * egress<~Boolean> - Indicates whether this rule applies to egress traffic from the subnet (true) or ingress traffic to the subnet (false).
# * options<~Hash>:
# * 'Icmp.Code' - ICMP code, required if protocol is 1
# * 'Icmp.Type' - ICMP type, required if protocol is 1
# * 'PortRange.From' - The first port in the range, required if protocol is 6 (TCP) or 17 (UDP)
# * 'PortRange.To' - The last port in the range, required if protocol is 6 (TCP) or 17 (UDP)
#
# ==== Returns
#
# True or false depending on the result
#
def add_rule(rule_number, protocol, rule_action, cidr_block, egress, options = {})
requires :network_acl_id
service.create_network_acl_entry(network_acl_id, rule_number, protocol, rule_action, cidr_block, egress, options)
true
end
# Remove an inbound rule, shortcut method for #remove_rule
def remove_inbound_rule(rule_number)
remove_rule(rule_number, false)
end
# Remove an outbound rule, shortcut method for #remove_rule
def remove_outbound_rule(rule_number)
remove_rule(rule_number, true)
end
# Update a specific rule number
#
# network_acl.remove_rule(100, true)
#
# ==== Parameters
# * rule_number<~Integer> - The rule number for the entry, between 100 and 32766
# * egress<~Boolean> - Indicates whether this rule applies to egress traffic from the subnet (true) or ingress traffic to the subnet (false).
#
# ==== Returns
#
# True or false depending on the result
#
def remove_rule(rule_number, egress)
requires :network_acl_id
service.delete_network_acl_entry(network_acl_id, rule_number, egress)
true
end
# Update an inbound rule, shortcut method for #update_rule
def update_inbound_rule(rule_number, protocol, rule_action, cidr_block, options = {})
update_rule(rule_number, protocol, rule_action, cidr_block, false, options)
end
# Update an outbound rule, shortcut method for #update_rule
def update_outbound_rule(rule_number, protocol, rule_action, cidr_block, options = {})
update_rule(rule_number, protocol, rule_action, cidr_block, true, options)
end
# Update a specific rule number
#
# network_acl.update_rule(100, Fog::Compute::AWS::NetworkAcl::TCP, 'allow', '0.0.0.0/0', true, 'PortRange.From' => 22, 'PortRange.To' => 22)
#
# ==== Parameters
# * rule_number<~Integer> - The rule number for the entry, between 100 and 32766
# * protocol<~Integer> - The IP protocol to which the rule applies. You can use -1 to mean all protocols.
# * rule_action<~String> - Allows or denies traffic that matches the rule. (either allow or deny)
# * cidr_block<~String> - The CIDR range to allow or deny
# * egress<~Boolean> - Indicates whether this rule applies to egress traffic from the subnet (true) or ingress traffic to the subnet (false).
# * options<~Hash>:
# * 'Icmp.Code' - ICMP code, required if protocol is 1
# * 'Icmp.Type' - ICMP type, required if protocol is 1
# * 'PortRange.From' - The first port in the range, required if protocol is 6 (TCP) or 17 (UDP)
# * 'PortRange.To' - The last port in the range, required if protocol is 6 (TCP) or 17 (UDP)
#
# ==== Returns
#
# True or false depending on the result
#
def update_rule(rule_number, protocol, rule_action, cidr_block, egress, options = {})
requires :network_acl_id
service.replace_network_acl_entry(network_acl_id, rule_number, protocol, rule_action, cidr_block, egress, options)
true
end
# Associate a subnet with this network ACL
#
# network_acl.associate_with(subnet)
#
# ==== Parameters
# * subnet<~Subnet> - Subnet object to associate with this network ACL
#
# ==== Returns
#
# True or false depending on the result
#
def associate_with(subnet)
requires :network_acl_id
# We have to manually find out the network ACL the subnet is currently associated with
old_id = service.network_acls.all('association.subnet-id' => subnet.subnet_id).first.associations.detect { |a| a['subnetId'] == subnet.subnet_id }['networkAclAssociationId']
service.replace_network_acl_association(old_id, network_acl_id)
true
end
# Removes an existing network ACL
#
# network_acl.destroy
#
# ==== Returns
#
# True or false depending on the result
#
def destroy
requires :network_acl_id
service.delete_network_acl(network_acl_id)
true
end
# Create a network ACL
#
# >> g = AWS.network_acls.new(:vpc_id => 'vpc-abcdefgh')
# >> g.save
def save
requires :vpc_id
data = service.create_network_acl(vpc_id).body['networkAcl']
merge_attributes(data)
true
end
end
end
end
end

View File

@ -0,0 +1,138 @@
require 'fog/core/collection'
require 'fog/aws/models/compute/network_acl'
module Fog
module Compute
class AWS
class NetworkAcls < Fog::Collection
attribute :filters
model Fog::Compute::AWS::NetworkAcl
# Creates a new network ACL
#
# AWS.network_acls.new
#
# ==== Returns
#
# Returns the details of the new network ACL
#
#>> <Fog::Compute::AWS::NetworkAcl
# network_acl_id=nil,
# vpc_id=nil,
# default=nil,
# entries=nil,
# associations=nil,
# tags=nil
# >
#
def initialize(attributes)
self.filters ||= {}
super
end
# Returns an array of all network ACLs that have been created
#
# AWS.network_acls.all
#
# ==== Returns
#
# Returns an array of all network ACLs
#
#>> AWS.network_acls.all
# <Fog::AWS::Compute::NetworkAcls
# filters={}
# [
# <Fog::Compute::AWS::NetworkAcl
# network_acl_id="acl-abcdefgh",
# vpc_id="vpc-abcdefgh",
# default=true,
# entries=[
# {
# "icmpTypeCode" => {},
# "portRange" => {},
# "ruleNumber" => 32767,
# "protocol" => -1,
# "ruleAction" => "deny",
# "egress" => false,
# "cidrBlock" => "0.0.0.0/0"
# },
# {
# "icmpTypeCode" => {},
# "portRange" => {},
# "ruleNumber" => 32767,
# "protocol" => -1,
# "ruleAction" => "deny",
# "egress" => true,
# "cidrBlock" => "0.0.0.0/0"
# }
# ],
# associations=[
# {
# "networkAclAssociationId" => "aclassoc-abcdefgh",
# "networkAclId" => "acl-abcdefgh",
# "subnetId" => "subnet-abcdefgh"
# }
# ],
# tags={}
# >
# ]
# >
#
def all(filters = filters)
self.filters = filters
data = service.describe_network_acls(filters).body
load(data['networkAclSet'])
end
# Used to retrieve a network interface
# network interface id is required to get any information
#
# You can run the following command to get the details:
# AWS.network_interfaces.get("eni-11223344")
#
# ==== Returns
#
#>> AWS.network_acls.get("acl-abcdefgh")
# <Fog::Compute::AWS::NetworkAcl
# network_acl_id="acl-abcdefgh",
# vpc_id="vpc-abcdefgh",
# default=true,
# entries=[
# {
# "icmpTypeCode" => {},
# "portRange" => {},
# "ruleNumber" => 32767,
# "protocol" => -1,
# "ruleAction" => "deny",
# "egress" => false,
# "cidrBlock" => "0.0.0.0/0"
# },
# {
# "icmpTypeCode" => {},
# "portRange" => {},
# "ruleNumber" => 32767,
# "protocol" => -1,
# "ruleAction" => "deny",
# "egress" => true,
# "cidrBlock" => "0.0.0.0/0"
# }
# ],
# associations=[
# {
# "networkAclAssociationId" => "aclassoc-abcdefgh",
# "networkAclId" => "acl-abcdefgh",
# "subnetId" => "subnet-abcdefgh"
# }
# ],
# tags={}
# >
def get(nacl_id)
self.class.new(:service => service).all('network-acl-id' => nacl_id).first if nacl_id
end
end
end
end
end

View File

@ -0,0 +1,28 @@
module Fog
module Parsers
module Compute
module AWS
require 'fog/aws/parsers/compute/network_acl_parser'
class CreateNetworkAcl < NetworkAclParser
def reset
super
@response = { 'networkAcl' => {} }
end
def end_element(name)
case name
when 'requestId'
@response[name] = value
when 'networkAcl'
@response['networkAcl'] = @network_acl
reset_nacl
else
super
end
end
end
end
end
end
end

View File

@ -0,0 +1,42 @@
module Fog
module Parsers
module Compute
module AWS
require 'fog/aws/parsers/compute/network_acl_parser'
class DescribeNetworkAcls < NetworkAclParser
def reset
super
@response = { 'networkAclSet' => [] }
@item_level = 0
end
def start_element(name, attrs = [])
super
case name
when 'item'
@item_level += 1
end
end
def end_element(name)
case name
when 'requestId'
@response[name] = value
when 'item'
@item_level -= 1
if @item_level == 0
@response['networkAclSet'] << @network_acl
reset_nacl
else
super
end
else
super
end
end
end
end
end
end
end

View File

@ -0,0 +1,105 @@
module Fog
module Parsers
module Compute
module AWS
class NetworkAclParser < Fog::Parsers::Base
def reset_nacl
@network_acl = { 'associationSet' => [], 'entrySet' => [], 'tagSet' => {} }
@association = {}
@entry = { 'icmpTypeCode' => {}, 'portRange' => {} }
@tag = {}
@in_entry_set = false
@in_association_set = false
@in_tag_set = false
@in_port_range = false
@in_icmp_type_code = false
end
def reset
reset_nacl
end
def start_element(name, attrs = [])
super
case name
when 'entrySet'
@in_entry_set = true
when 'associationSet'
@in_association_set = true
when 'tagSet'
@in_tag_set = true
when 'portRange'
@in_port_range = true
when 'icmpTypeCode'
@in_icmp_type_code = true
end
end
def end_element(name)
if @in_entry_set
if @in_port_range
case name
when 'portRange'
@in_port_range = false
when 'from', 'to'
@entry['portRange'][name] = value.to_i
end
elsif @in_icmp_type_code
case name
when 'icmpTypeCode'
@in_icmp_type_code = false
when 'code', 'type'
@entry['icmpTypeCode'][name] = value.to_i
end
else
case name
when 'entrySet'
@in_entry_set = false
when 'item'
@network_acl['entrySet'] << @entry
@entry = { 'icmpTypeCode' => {}, 'portRange' => {} }
when 'ruleNumber', 'protocol'
@entry[name] = value.to_i
when 'ruleAction', 'cidrBlock'
@entry[name] = value
when 'egress'
@entry[name] = value == 'true'
end
end
elsif @in_association_set
case name
when 'associationSet'
@in_association_set = false
when 'item'
@network_acl['associationSet'] << @association
@association = {}
when 'networkAclAssociationId', 'networkAclId', 'subnetId'
@association[name] = value
end
elsif @in_tag_set
case name
when 'tagSet'
@in_tag_set = false
when 'item'
@network_acl['tagSet'][@tag['key']] = @tag['value']
@tag = {}
when 'key', 'value'
@tag[name] = value
end
else
case name
when 'networkAclId', 'vpcId'
@network_acl[name] = value
when 'default'
@network_acl[name] = value == 'true'
end
end
end
end
end
end
end
end

View File

@ -0,0 +1,20 @@
module Fog
module Parsers
module Compute
module AWS
class ReplaceNetworkAclAssociation < Fog::Parsers::Base
def reset
@response = {}
end
def end_element(name)
case name
when 'requestId', 'newAssociationId'
@response[name] = value
end
end
end
end
end
end
end

View File

@ -0,0 +1,105 @@
module Fog
module Compute
class AWS
class Real
require 'fog/aws/parsers/compute/create_network_acl'
# Creates a network ACL
#
# ==== Parameters
# * vpcId<~String> - The ID of the VPC to create this network ACL under
#
# === Returns
# * response<~Excon::Response>:
# * body<~Hash>:
# * 'requestId'<~String> - Id of request
# * 'networkAcl'<~Array>: - The network ACL
# * 'networkAclId'<~String> - The ID of the network ACL
# * 'vpcId'<~String> - The ID of the VPC for the network ACL
# * 'default'<~Boolean> - Indicates whether this is the default network ACL for the VPC
# * 'entrySet'<~Array>: - A list of entries (rules) in the network ACL
# * 'ruleNumber'<~Integer> - The rule number for the entry. ACL entries are processed in ascending order by rule number
# * 'protocol'<~Integer> - The protocol. A value of -1 means all protocols
# * 'ruleAction'<~String> - Indicates whether to allow or deny the traffic that matches the rule
# * 'egress'<~Boolean> - Indicates whether the rule is an egress rule (applied to traffic leaving the subnet)
# * 'cidrBlock'<~String> - The network range to allow or deny, in CIDR notation
# * 'icmpTypeCode'<~Hash> - ICMP protocol: The ICMP type and code
# * 'code'<~Integer> - The ICMP code. A value of -1 means all codes for the specified ICMP type
# * 'type'<~Integer> - The ICMP type. A value of -1 means all types
# * 'portRange'<~Hash> - TCP or UDP protocols: The range of ports the rule applies to
# * 'from'<~Integer> - The first port in the range
# * 'to'<~Integer> - The last port in the range
# * 'associationSet'<~Array>: - A list of associations between the network ACL and subnets
# * 'networkAclAssociationId'<~String> - The ID of the association
# * 'networkAclId'<~String> - The ID of the network ACL
# * 'subnetId'<~String> - The ID of the subnet
# * 'tagSet'<~Array>: - Tags assigned to the resource.
# * 'key'<~String> - Tag's key
# * 'value'<~String> - Tag's value
#
# {Amazon API Reference}[http://docs.aws.amazon.com/AWSEC2/latest/APIReference/ApiReference-query-CreateNetworkAcl.html]
def create_network_acl(vpcId, options = {})
request({
'Action' => 'CreateNetworkAcl',
'VpcId' => vpcId,
:parser => Fog::Parsers::Compute::AWS::CreateNetworkAcl.new
}.merge!(options))
end
end
class Mock
def create_network_acl(vpcId, options = {})
response = Excon::Response.new
if vpcId
id = Fog::AWS::Mock.network_acl_id
unless self.data[:vpcs].detect { |s| s['vpcId'] == vpcId }
raise Fog::Compute::AWS::Error.new("Unknown VPC '#{vpcId}' specified")
end
data = {
'networkAclId' => id,
'vpcId' => vpcId,
'default' => false,
'entrySet' => [
{
'icmpTypeCode' => {},
'portRange' => {},
'ruleNumber' => 32767,
'protocol' => -1,
'ruleAction' => "deny",
'egress' => true,
'cidrBlock' => "0.0.0.0/0",
},
{
'icmpTypeCode' => {},
'portRange' => {},
'ruleNumber' => 32767,
'protocol' => -1,
'ruleAction' => "deny",
'egress' => false,
'cidrBlock' => "0.0.0.0/0",
},
],
'associationSet' => [],
'tagSet' => {},
}
self.data[:network_acls][id] = data
response.body = {
'requestId' => Fog::AWS::Mock.request_id,
'networkAcl' => data
}
else
response.status = 400
response.body = {
'Code' => 'InvalidParameterValue',
'Message' => "Invalid value '' for subnetId"
}
end
response
end
end
end
end
end

View File

@ -0,0 +1,80 @@
module Fog
module Compute
class AWS
class Real
require 'fog/aws/parsers/compute/basic'
# Creates a Network ACL entry
#
# ==== Parameters
# * network_acl_id<~String> - The ID of the ACL to add this entry to
# * rule_number<~Integer> - The rule number for the entry, between 100 and 32766
# * protocol<~Integer> - The IP protocol to which the rule applies. You can use -1 to mean all protocols.
# * rule_action<~String> - Allows or denies traffic that matches the rule. (either allow or deny)
# * cidr_block<~String> - The CIDR range to allow or deny
# * egress<~Boolean> - Indicates whether this rule applies to egress traffic from the subnet (true) or ingress traffic to the subnet (false).
# * options<~Hash>:
# * 'Icmp.Code' - ICMP code, required if protocol is 1
# * 'Icmp.Type' - ICMP type, required if protocol is 1
# * 'PortRange.From' - The first port in the range, required if protocol is 6 (TCP) or 17 (UDP)
# * 'PortRange.To' - The last port in the range, required if protocol is 6 (TCP) or 17 (UDP)
#
# === Returns
# * response<~Excon::Response>:
# * body<~Hash>:
# * 'requestId'<~String> - Id of request
# * 'return'<~Boolean> - Returns true if the request succeeds.
#
# {Amazon API Reference}[http://docs.aws.amazon.com/AWSEC2/latest/APIReference/ApiReference-query-CreateNetworkAclEntry.html]
def create_network_acl_entry(network_acl_id, rule_number, protocol, rule_action, cidr_block, egress, options = {})
request({
'Action' => 'CreateNetworkAclEntry',
'NetworkAclId' => network_acl_id,
'RuleNumber' => rule_number,
'Protocol' => protocol,
'RuleAction' => rule_action,
'Egress' => egress,
'CidrBlock' => cidr_block,
:parser => Fog::Parsers::Compute::AWS::Basic.new
}.merge!(options))
end
end
class Mock
def create_network_acl_entry(network_acl_id, rule_number, protocol, rule_action, cidr_block, egress, options = {})
response = Excon::Response.new
if self.data[:network_acls][network_acl_id]
if self.data[:network_acls][network_acl_id]['entrySet'].detect { |r| r['ruleNumber'] == rule_number && r['egress'] == egress }
raise Fog::Compute::AWS::Error.new("Already a rule with that number")
end
data = {
'ruleNumber' => rule_number,
'protocol' => protocol,
'ruleAction' => rule_action,
'egress' => egress,
'cidrBlock' => cidr_block,
'icmpTypeCode' => {},
'portRange' => {}
}
data['icmpTypeCode']['code'] = options['Icmp.Code'] if options['Icmp.Code']
data['icmpTypeCode']['type'] = options['Icmp.Type'] if options['Icmp.Type']
data['portRange']['from'] = options['PortRange.From'] if options['PortRange.From']
data['portRange']['to'] = options['PortRange.To'] if options['PortRange.To']
self.data[:network_acls][network_acl_id]['entrySet'] << data
response.status = 200
response.body = {
'requestId' => Fog::AWS::Mock.request_id,
'return' => true
}
response
else
raise Fog::Compute::AWS::NotFound.new("The network ACL '#{network_acl_id}' does not exist")
end
end
end
end
end
end

View File

@ -56,6 +56,17 @@ module Fog
'tagSet' => {}
})
# Add this subnet to the default network ACL
accid = Fog::AWS::Mock.network_acl_association_id
default_nacl = self.data[:network_acls].values.detect { |nacl| nacl['vpcId'] == vpcId && nacl['default'] }
if default_nacl
default_nacl['associationSet'] << {
'networkAclAssociationId' => accid,
'networkAclId' => default_nacl['networkAclId'],
'subnetId' => self.data[:subnets].last['subnetId'],
}
end
response.body = {
'requestId' => Fog::AWS::Mock.request_id,
'subnetSet' => self.data[:subnets]

View File

@ -66,6 +66,12 @@ module Fog
assoc = add_route_association(default_route.id, nil, true)
route_table["associationSet"].push(assoc)
# Create a default network ACL
default_nacl = self.network_acls.new(:vpc_id => vpc_id)
default_nacl.save
# Manually override since Amazon doesn't let you create a default one
self.data[:network_acls][default_nacl.network_acl_id]['default'] = true
response.body = {
'requestId' => Fog::AWS::Mock.request_id,
'vpcSet' => self.data[:vpcs]

View File

@ -0,0 +1,52 @@
module Fog
module Compute
class AWS
class Real
require 'fog/aws/parsers/compute/basic'
# Deletes a network ACL.
#
# ==== Parameters
# * network_acl_id<~String> - The ID of the network ACL you want to delete.
#
# === Returns
# * response<~Excon::Response>:
# * body<~Hash>:
# * 'requestId'<~String> - Id of request
# * 'return'<~Boolean> - Returns true if the request succeeds.
#
# {Amazon API Reference}[http://docs.aws.amazon.com/AWSEC2/latest/APIReference/ApiReference-query-DeleteNetworkAcl.html]
def delete_network_acl(network_acl_id)
request(
'Action' => 'DeleteNetworkAcl',
'NetworkAclId' => network_acl_id,
:parser => Fog::Parsers::Compute::AWS::Basic.new
)
end
end
class Mock
def delete_network_acl(network_acl_id)
response = Excon::Response.new
if self.data[:network_acls][network_acl_id]
if self.data[:network_acls][network_acl_id]['associationSet'].any?
raise Fog::Compute::AWS::Error.new("ACL is in use")
end
self.data[:network_acls].delete(network_acl_id)
response.status = 200
response.body = {
'requestId' => Fog::AWS::Mock.request_id,
'return' => true
}
response
else
raise Fog::Compute::AWS::NotFound.new("The network ACL '#{network_acl_id}' does not exist")
end
end
end
end
end
end

View File

@ -0,0 +1,55 @@
module Fog
module Compute
class AWS
class Real
require 'fog/aws/parsers/compute/basic'
# Deletes a network ACL entry
#
# ==== Parameters
# * network_acl_id<~String> - The ID of the network ACL
# * rule_number<~Integer> - The rule number of the entry to delete.
# * egress<~Boolean> - Indicates whether the rule is an egress rule (true) or ingress rule (false)
#
# === Returns
# * response<~Excon::Response>:
# * body<~Hash>:
# * 'requestId'<~String> - Id of request
# * 'return'<~Boolean> - Returns true if the request succeeds.
#
# {Amazon API Reference}[http://docs.aws.amazon.com/AWSEC2/latest/APIReference/ApiReference-query-DeleteNetworkAclEntry.html]
def delete_network_acl_entry(network_acl_id, rule_number, egress)
request(
'Action' => 'DeleteNetworkAclEntry',
'NetworkAclId' => network_acl_id,
'RuleNumber' => rule_number,
'Egress' => egress,
:parser => Fog::Parsers::Compute::AWS::Basic.new
)
end
end
class Mock
def delete_network_acl_entry(network_acl_id, rule_number, egress)
response = Excon::Response.new
if self.data[:network_acls][network_acl_id]
if self.data[:network_acls][network_acl_id]['entrySet'].detect { |r| r['ruleNumber'] == rule_number && r['egress'] == egress }
self.data[:network_acls][network_acl_id]['entrySet'].delete_if { |r| r['ruleNumber'] == rule_number && r['egress'] == egress }
else
raise Fog::Compute::AWS::Error.new("No rule with that number and egress value")
end
response.status = 200
response.body = {
'requestId' => Fog::AWS::Mock.request_id,
'return' => true
}
response
else
raise Fog::Compute::AWS::NotFound.new("The network ACL '#{network_acl_id}' does not exist")
end
end
end
end
end
end

View File

@ -36,6 +36,10 @@ module Fog
response.status = 200
self.data[:vpcs].reject! { |v| v['vpcId'] == vpc_id }
# Delete the default network ACL
network_acl_id = self.network_acls.all('vpc-id' => vpc_id, 'default' => true).first.network_acl_id
self.data[:network_acls].delete(network_acl_id)
response.body = {
'requestId' => Fog::AWS::Mock.request_id,
'return' => true

View File

@ -0,0 +1,104 @@
module Fog
module Compute
class AWS
class Real
require 'fog/aws/parsers/compute/describe_network_acls'
# Describe all or specified network ACLs
#
# ==== Parameters
# * filters<~Hash> - List of filters to limit results with
#
# === Returns
# * response<~Excon::Response>:
# * body<~Hash>:
# * 'requestId'<~String> - Id of request
# * 'networkAclSet'<~Array>: - A list of network ACLs
# * 'networkAclId'<~String> - The ID of the network ACL
# * 'vpcId'<~String> - The ID of the VPC for the network ACL
# * 'default'<~Boolean> - Indicates whether this is the default network ACL for the VPC
# * 'entrySet'<~Array>: - A list of entries (rules) in the network ACL
# * 'ruleNumber'<~Integer> - The rule number for the entry. ACL entries are processed in ascending order by rule number
# * 'protocol'<~Integer> - The protocol. A value of -1 means all protocols
# * 'ruleAction'<~String> - Indicates whether to allow or deny the traffic that matches the rule
# * 'egress'<~Boolean> - Indicates whether the rule is an egress rule (applied to traffic leaving the subnet)
# * 'cidrBlock'<~String> - The network range to allow or deny, in CIDR notation
# * 'icmpTypeCode'<~Hash> - ICMP protocol: The ICMP type and code
# * 'code'<~Integer> - The ICMP code. A value of -1 means all codes for the specified ICMP type
# * 'type'<~Integer> - The ICMP type. A value of -1 means all types
# * 'portRange'<~Hash> - TCP or UDP protocols: The range of ports the rule applies to
# * 'from'<~Integer> - The first port in the range
# * 'to'<~Integer> - The last port in the range
# * 'associationSet'<~Array>: - A list of associations between the network ACL and subnets
# * 'networkAclAssociationId'<~String> - The ID of the association
# * 'networkAclId'<~String> - The ID of the network ACL
# * 'subnetId'<~String> - The ID of the subnet
# * 'tagSet'<~Array>: - Tags assigned to the resource.
# * 'key'<~String> - Tag's key
# * 'value'<~String> - Tag's value
#
# {Amazon API Reference}[http://docs.aws.amazon.com/AWSEC2/latest/APIReference/ApiReference-query-DescribeNetworkAcls.html]
def describe_network_acls(filters = {})
params = Fog::AWS.indexed_filters(filters)
request({
'Action' => 'DescribeNetworkAcls',
:idempotent => true,
:parser => Fog::Parsers::Compute::AWS::DescribeNetworkAcls.new
}.merge!(params))
end
end
class Mock
def describe_network_acls(filters = {})
response = Excon::Response.new
network_acls = self.data[:network_acls].values
aliases = {
'vpc-id' => 'vpcId',
'network-acl-id' => 'networkAclId',
'default' => 'default',
}
association_aliases = {
'association-id' => 'networkAclAssociationId',
'network-acl-id' => 'networkAclId',
'subnet-id' => 'subnetId',
}
entry_aliases = {
'cidr' => 'cidrBlock',
'egress' => 'egress',
'rule-action' => 'ruleAction',
'rule-number' => 'ruleNumber',
'protocol' => 'protocol',
'egress' => 'egress',
}
for filter_key, filter_value in filters
filter_key = filter_key.to_s
if association_key = filter_key.split('association.')[1]
aliased_key = association_aliases[association_key]
network_acls = network_acls.reject{|nacl| !nacl['associationSet'].detect {|association| [*filter_value].include?(association[aliased_key])}}
elsif entry_key = filter_key.split('entry.icmp.')[1]
network_acls = network_acls.reject{|nacl| !nacl['entrySet'].detect {|association| [*filter_value].include?(association['icmpTypeCode'][entry_key])}}
elsif entry_key = filter_key.split('entry.port-range.')[1]
network_acls = network_acls.reject{|nacl| !nacl['entrySet'].detect {|association| [*filter_value].include?(association['portRange'][entry_key])}}
elsif entry_key = filter_key.split('entry.')[1]
aliased_key = entry_aliases[entry_key]
network_acls = network_acls.reject{|nacl| !nacl['entrySet'].detect {|association| [*filter_value].include?(association[aliased_key])}}
else
aliased_key = aliases[filter_key]
network_acls = network_acls.reject{|nacl| ![*filter_value].include?(nacl[aliased_key])}
end
end
response.status = 200
response.body = {
'requestId' => Fog::AWS::Mock.request_id,
'networkAclSet' => network_acls
}
response
end
end
end
end
end

View File

@ -0,0 +1,66 @@
module Fog
module Compute
class AWS
class Real
require 'fog/aws/parsers/compute/replace_network_acl_association'
# Replace the network ACL for a subnet with a
#
# ==== Parameters
# * association_id<~String> - The ID of the current association between the original network ACL and the subnet
# * network_acl_id<~String> - The ID of the network ACL
#
# === Returns
# * response<~Excon::Response>:
# * body<~Hash>:
# * 'requestId'<~String> - Id of request
# * 'return'<~Boolean> - Returns true if the request succeeds.
#
# {Amazon API Reference}[http://docs.aws.amazon.com/AWSEC2/latest/APIReference/ApiReference-query-ReplaceNetworkAclAssociation.html]
def replace_network_acl_association(association_id, network_acl_id)
request({
'Action' => 'ReplaceNetworkAclAssociation',
'AssociationId' => association_id,
'NetworkAclId' => network_acl_id,
:parser => Fog::Parsers::Compute::AWS::ReplaceNetworkAclAssociation.new
})
end
end
class Mock
def replace_network_acl_association(association_id, network_acl_id)
response = Excon::Response.new
if self.data[:network_acls][network_acl_id]
# find the old assoc
old_nacl = self.data[:network_acls].values.detect do |n|
n['associationSet'].detect { |assoc| assoc['networkAclAssociationId'] == association_id }
end
unless old_nacl
raise Fog::Compute::AWS::Error.new("Invalid association_id #{association_id}")
end
subnet_id = old_nacl['associationSet'].detect { |assoc| assoc['networkAclAssociationId'] == association_id }['subnetId']
old_nacl['associationSet'].delete_if { |assoc| assoc['networkAclAssociationId'] == association_id }
id = Fog::AWS::Mock.network_acl_association_id
self.data[:network_acls][network_acl_id]['associationSet'] << {
'networkAclAssociationId' => id,
'networkAclId' => network_acl_id,
'subnetId' => subnet_id,
}
response.status = 200
response.body = {
'requestId' => Fog::AWS::Mock.request_id,
'newAssociationId' => id
}
response
else
raise Fog::Compute::AWS::NotFound.new("The network ACL '#{network_acl_id}' does not exist")
end
end
end
end
end
end

View File

@ -0,0 +1,81 @@
module Fog
module Compute
class AWS
class Real
require 'fog/aws/parsers/compute/basic'
# Replaces a Network ACL entry with the same rule number
#
# ==== Parameters
# * network_acl_id<~String> - The ID of the ACL to add this entry to
# * rule_number<~Integer> - The rule number for the entry, between 100 and 32766
# * protocol<~Integer> - The IP protocol to which the rule applies. You can use -1 to mean all protocols.
# * rule_action<~String> - Allows or denies traffic that matches the rule. (either allow or deny)
# * cidr_block<~String> - The CIDR range to allow or deny
# * egress<~Boolean> - Indicates whether this rule applies to egress traffic from the subnet (true) or ingress traffic to the subnet (false).
# * options<~Hash>:
# * 'Icmp.Code' - ICMP code, required if protocol is 1
# * 'Icmp.Type' - ICMP type, required if protocol is 1
# * 'PortRange.From' - The first port in the range, required if protocol is 6 (TCP) or 17 (UDP)
# * 'PortRange.To' - The last port in the range, required if protocol is 6 (TCP) or 17 (UDP)
#
# === Returns
# * response<~Excon::Response>:
# * body<~Hash>:
# * 'requestId'<~String> - Id of request
# * 'return'<~Boolean> - Returns true if the request succeeds.
#
# {Amazon API Reference}[http://docs.aws.amazon.com/AWSEC2/latest/APIReference/ApiReference-query-ReplaceNetworkAclEntry.html]
def replace_network_acl_entry(network_acl_id, rule_number, protocol, rule_action, cidr_block, egress, options = {})
request({
'Action' => 'ReplaceNetworkAclEntry',
'NetworkAclId' => network_acl_id,
'RuleNumber' => rule_number,
'Protocol' => protocol,
'RuleAction' => rule_action,
'Egress' => egress,
'CidrBlock' => cidr_block,
:parser => Fog::Parsers::Compute::AWS::Basic.new
}.merge!(options))
end
end
class Mock
def replace_network_acl_entry(network_acl_id, rule_number, protocol, rule_action, cidr_block, egress, options = {})
response = Excon::Response.new
if self.data[:network_acls][network_acl_id]
unless self.data[:network_acls][network_acl_id]['entrySet'].detect { |r| r['ruleNumber'] == rule_number && r['egress'] == egress }
raise Fog::Compute::AWS::Error.new("No rule with that number")
end
self.data[:network_acls][network_acl_id]['entrySet'].delete_if { |r| r['ruleNumber'] == rule_number && r['egress'] == egress }
data = {
'ruleNumber' => rule_number,
'protocol' => protocol,
'ruleAction' => rule_action,
'egress' => egress,
'cidrBlock' => cidr_block,
'icmpTypeCode' => {},
'portRange' => {}
}
data['icmpTypeCode']['code'] = options['Icmp.Code'] if options['Icmp.Code']
data['icmpTypeCode']['type'] = options['Icmp.Type'] if options['Icmp.Type']
data['portRange']['from'] = options['PortRange.From'] if options['PortRange.From']
data['portRange']['to'] = options['PortRange.To'] if options['PortRange.To']
self.data[:network_acls][network_acl_id]['entrySet'] << data
response.status = 200
response.body = {
'requestId' => Fog::AWS::Mock.request_id,
'return' => true
}
response
else
raise Fog::Compute::AWS::NotFound.new("The network ACL '#{network_acl_id}' does not exist")
end
end
end
end
end
end

View File

@ -0,0 +1,109 @@
Shindo.tests("Fog::Compute[:aws] | network_acl", ['aws']) do
@vpc = Fog::Compute[:aws].vpcs.create('cidr_block' => '10.0.10.0/24')
@subnet = Fog::Compute[:aws].subnets.create('vpc_id' => @vpc.id, 'cidr_block' => '10.0.10.16/28')
model_tests(Fog::Compute[:aws].network_acls, { :vpc_id => @vpc.id }, true)
tests("associate_with") do
@new_nacl = Fog::Compute[:aws].network_acls.create(:vpc_id => @vpc.id)
@default_nacl = Fog::Compute[:aws].network_acls.all('vpc-id' => @vpc.id, 'default' => true).first
test("associate_with new_nacl") do
@new_nacl.associate_with(@subnet)
end
@new_nacl.reload
test("associate_with correctly updates new_nacl") do
@new_nacl.associations.collect { |a| a['subnetId'] } == [@subnet.subnet_id]
end
@default_nacl.associate_with(@subnet)
@new_nacl.reload
@default_nacl.reload
test("associate_with correctly updates new_nacl after removal") do
@new_nacl.associations.collect { |a| a['subnetId'] } == []
end
test("associate_with correctly updates default_nacl after removal") do
@default_nacl.associations.collect { |a| a['subnetId'] } == [@subnet.subnet_id]
end
@new_nacl.destroy
end
tests("add_rule and remove_rule") do
@new_nacl = Fog::Compute[:aws].network_acls.create(:vpc_id => @vpc.id)
default_rules = @new_nacl.entries.dup
test("add a new inbound rule") do
@new_nacl.add_inbound_rule(100, Fog::Compute::AWS::NetworkAcl::TCP, 'allow', '0.0.0.0/0', 'PortRange.From' => 22, 'PortRange.To' => 22)
@new_nacl.reload
(@new_nacl.entries - default_rules) == [{
"icmpTypeCode" => {},
"portRange" => {
"from" => 22,
"to" => 22
},
"ruleNumber" => 100,
"protocol" => 6,
"ruleAction" => "allow",
"egress" => false,
"cidrBlock" => "0.0.0.0/0"
}]
end
test("remove inbound rule") do
@new_nacl.remove_inbound_rule(100)
@new_nacl.reload
@new_nacl.entries == default_rules
end
test("add a new outbound rule") do
@new_nacl.add_outbound_rule(100, Fog::Compute::AWS::NetworkAcl::TCP, 'allow', '0.0.0.0/0', 'PortRange.From' => 22, 'PortRange.To' => 22)
@new_nacl.reload
(@new_nacl.entries - default_rules) == [{
"icmpTypeCode" => {},
"portRange" => {
"from" => 22,
"to" => 22
},
"ruleNumber" => 100,
"protocol" => 6,
"ruleAction" => "allow",
"egress" => true,
"cidrBlock" => "0.0.0.0/0"
}]
end
test("remove outbound rule") do
@new_nacl.remove_outbound_rule(100)
@new_nacl.reload
@new_nacl.entries == default_rules
end
test("update rule") do
@new_nacl.add_inbound_rule(100, Fog::Compute::AWS::NetworkAcl::TCP, 'allow', '0.0.0.0/0', 'PortRange.From' => 22, 'PortRange.To' => 22)
@new_nacl.update_inbound_rule(100, Fog::Compute::AWS::NetworkAcl::TCP, 'allow', '10.0.0.0/8', 'PortRange.From' => 22, 'PortRange.To' => 22)
@new_nacl.reload
(@new_nacl.entries - default_rules) == [{
"icmpTypeCode" => {},
"portRange" => {
"from" => 22,
"to" => 22
},
"ruleNumber" => 100,
"protocol" => 6,
"ruleAction" => "allow",
"egress" => false,
"cidrBlock" => "10.0.0.0/8"
}]
end
@new_nacl.destroy
end
@subnet.destroy
@vpc.destroy
end

View File

@ -0,0 +1,7 @@
Shindo.tests("Fog::Compute[:aws] | network_acls", ['aws']) do
@vpc = Fog::Compute[:aws].vpcs.create('cidr_block' => '10.0.10.0/24')
collection_tests(Fog::Compute[:aws].network_acls, { :vpc_id => @vpc.id }, true)
@vpc.destroy
end

View File

@ -0,0 +1,86 @@
Shindo.tests('Fog::Compute[:aws] | network acl requests', ['aws']) do
@network_acl_format = {
'networkAclId' => String,
'vpcId' => String,
'default' => Fog::Boolean,
'entrySet' => [{
'ruleNumber' => Integer,
'protocol' => Integer,
'ruleAction' => String,
'egress' => Fog::Boolean,
'cidrBlock' => String,
'icmpTypeCode' => {
'code' => Fog::Nullable::Integer,
'type' => Fog::Nullable::Integer
},
'portRange' => {
'from' => Fog::Nullable::Integer,
'to' => Fog::Nullable::Integer
}
}],
'associationSet' => Array,
'tagSet' => Hash
}
@network_acls_format = {
'requestId' => String,
'networkAclSet' => [ @network_acl_format ]
}
@network_acl_replace_association = {
'requestId' => String,
'newAssociationId' => String
}
tests('success') do
@vpc = Fog::Compute[:aws].vpcs.create('cidr_block' => '10.0.10.0/24')
@subnet = Fog::Compute[:aws].subnets.create('vpc_id' => @vpc.id, 'cidr_block' => '10.0.10.16/28')
@network_acl = nil
# Describe network interfaces
tests('#describe_network_acls').formats(@network_acls_format) do
Fog::Compute[:aws].describe_network_acls.body
end
tests('#create_network_acl').formats(@network_acl_format) do
data = Fog::Compute[:aws].create_network_acl(@vpc.id).body
@network_acl = data['networkAcl']
data['networkAcl']
end
tests("#create_network_acl_entry").formats(AWS::Compute::Formats::BASIC) do
Fog::Compute[:aws].create_network_acl_entry(@network_acl['networkAclId'], 100, 6, 'allow', '0.0.0.0/8', false, 'PortRange.From' => 22, 'PortRange.To' => 22).body
end
tests("#replace_network_acl_entry").formats(AWS::Compute::Formats::BASIC) do
Fog::Compute[:aws].replace_network_acl_entry(@network_acl['networkAclId'], 100, 6, 'deny', '0.0.0.0/8', false, 'PortRange.From' => 22, 'PortRange.To' => 22).body
end
tests("#delete_network_acl_entry").formats(AWS::Compute::Formats::BASIC) do
Fog::Compute[:aws].delete_network_acl_entry(@network_acl['networkAclId'], 100, false).body
end
default_acl = Fog::Compute[:aws].describe_network_acls('vpc-id' => @vpc.id, 'default' => true).body['networkAclSet'].first
@assoc_id = default_acl['associationSet'].first['networkAclAssociationId']
tests("#replace_network_acl_association").formats(@network_acl_replace_association) do
data = Fog::Compute[:aws].replace_network_acl_association(@assoc_id, @network_acl['networkAclId']).body
@assoc_id = data['newAssociationId']
data
end
tests("#replace_network_acl_association").formats(@network_acl_replace_association) do
Fog::Compute[:aws].replace_network_acl_association(@assoc_id, default_acl['networkAclId']).body
end
tests('#delete_network_acl').formats(AWS::Compute::Formats::BASIC) do
Fog::Compute[:aws].delete_network_acl(@network_acl['networkAclId']).body
end
# Clean up
@subnet.destroy
@vpc.destroy
end
end