Skip to content

Commit 942a396

Browse files
committed
Added server firewalls
1 parent 6578276 commit 942a396

1 file changed

Lines changed: 166 additions & 0 deletions

File tree

lib/softlayer/ServerFirewall.rb

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
#--
2+
# Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved.
3+
#
4+
# For licensing information see the LICENSE.md file in the project root.
5+
#++
6+
7+
module SoftLayer
8+
9+
##
10+
# The ServerFirewall class represents a firewall in the
11+
# SoftLayer environment that exists in a 1 to 1 relationship
12+
# with a particular server (either Bare Metal or Virtual).
13+
#
14+
# This is also called a "Shared Firewall" in some documentation
15+
#
16+
# Instances of this class rougly correspond to instances of the
17+
# SoftLayer_Network_Component_Firewall service entity
18+
#
19+
class ServerFirewall < SoftLayer::ModelBase
20+
include ::SoftLayer::DynamicAttribute
21+
22+
##
23+
# :attr_reader:
24+
# The firewall rules assigned to this firewall. These rules will
25+
# be read from the network API every time you ask for the value
26+
# of this property. To change the rules on the server use the
27+
# asymmetric method change_rules!
28+
sl_dynamic_attr :rules do |firewall_rules|
29+
firewall_rules.should_update? do
30+
# firewall rules update every time you ask for them.
31+
return true
32+
end
33+
34+
firewall_rules.to_update do
35+
rules_data = self.service.object_mask(self.class.default_rules_mask).getRules()
36+
37+
# For some reason (at the time of this writing) the object mask is not
38+
# applied to the rules properly. (this has been reported as a bug to the
39+
# proper development team). This extra step does filtering that should
40+
# have been done by the object mask.
41+
rules_keys = self.class.default_rules_mask_keys
42+
new_rules = rules_data.inject([]) do |new_rules, current_rule|
43+
new_rule = current_rule.delete_if { |key, value| !(rules_keys.include? key) }
44+
new_rules << new_rule
45+
end
46+
47+
new_rules.sort { |lhs, rhs| lhs['orderValue'] <=> rhs['orderValue'] }
48+
end
49+
end
50+
51+
##
52+
# :attr_reader:
53+
# The server that this firewall is attached to. The result may be
54+
# either a bare metal or virtual server.
55+
#
56+
sl_dynamic_attr :protected_server do |protected_server|
57+
protected_server.should_update? do
58+
@protected_server == nil
59+
end
60+
61+
protected_server.to_update do
62+
if has_sl_property?('networkComponent')
63+
@protected_server = SoftLayer::BareMetalServer.server_with_id(self['networkComponent']['downlinkComponent']['hardwareId'], :client => softlayer_client)
64+
end
65+
66+
if has_sl_property?('guestNetworkComponent')
67+
@protected_server = SoftLayer::VirtualServer.server_with_id(self['guestNetworkComponent']['guest']['id'], :client => softlayer_client)
68+
end
69+
70+
@protected_server
71+
end
72+
end
73+
74+
def initialize(client, network_hash)
75+
super(client, network_hash)
76+
@protected_server = nil
77+
end
78+
79+
##
80+
# Change the set of rules for the firewall.
81+
# The rules_data parameter should be an array of hashes where
82+
# each hash gives the conditions of the rule. The keys of the
83+
# hashes should be entries from the array returned by
84+
# SoftLayer::ServerFirewall.default_rules_mask_keys
85+
#
86+
# *NOTE!* The rules themselves have an "orderValue" property.
87+
# It is this property, and *not* the order that the rules are
88+
# found in the rules_data array, which will determine in which
89+
# order the firewall applies it's rules to incomming traffic.
90+
#
91+
# *NOTE!* Changes to the rules are not applied immediately
92+
# on the server side. Instead, they are enqueued by the
93+
# firewall update service and updated periodically. A typical
94+
# update will take about one minute to apply, but times may vary
95+
# depending on the system load and other circumstances.
96+
def change_rules!(rules_data)
97+
change_object = {
98+
"networkComponentFirewallId" => self.id,
99+
"rules" => rules_data
100+
}
101+
102+
self.softlayer_client[:Network_Firewall_Update_Request].createObject(change_object)
103+
end
104+
105+
106+
##
107+
# Locate and return all the server firewalls in the environment.
108+
#
109+
# These are a bit trick to track down. The strategy we take here is
110+
# to look at the account and find all the VLANs that do NOT have their
111+
# "dedicatedFirewallFlag" set.
112+
#
113+
# With the list of VLANs in hand we check each to see if it has an
114+
# firewallNetworkComponents (corresponding to bare metal servers) or
115+
# firewallGuestNetworkComponents (corresponding to virtual servers) that
116+
# have a status of "allow_edit". Each such component is a firewall
117+
# interface on the VLAN with rules that the customer can edit.
118+
#
119+
# The collection of all those VLANs becomes the set of firewalls
120+
# for the account.
121+
#
122+
def self.find_firewalls(client)
123+
softlayer_client = client || Client.default_client
124+
raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client
125+
126+
# Note that the dedicatedFirewallFlag is actually an integer and not a boolean
127+
# so we compare it against 0
128+
shared_vlans_filter = SoftLayer::ObjectFilter.new() { |filter|
129+
filter.accept("networkVlans.dedicatedFirewallFlag").when_it is(0)
130+
}
131+
132+
bare_metal_firewalls_data = []
133+
virtual_firewalls_data = []
134+
135+
shared_vlans = softlayer_client[:Account].object_mask("mask[firewallNetworkComponents[id,status,networkComponent[downlinkComponent[hardwareId]]],firewallGuestNetworkComponents[id,status,guestNetworkComponent[id,guest.id]]]").object_filter(shared_vlans_filter).getNetworkVlans
136+
shared_vlans.each do |vlan_data|
137+
bare_metal_firewalls_data.concat vlan_data["firewallNetworkComponents"].select { |network_component| network_component['status'] == 'allow_edit'}
138+
virtual_firewalls_data.concat vlan_data["firewallGuestNetworkComponents"].select { |network_component| network_component['status'] == 'allow_edit'}
139+
end
140+
141+
bare_metal_firewalls = bare_metal_firewalls_data.collect { |bare_metal_firewall_data|
142+
self.new(softlayer_client, bare_metal_firewall_data)
143+
}
144+
145+
virtual_server_firewalls = virtual_firewalls_data.collect { |virtual_firewall_data|
146+
self.new(softlayer_client, virtual_firewall_data)
147+
}
148+
149+
return bare_metal_firewalls + virtual_server_firewalls
150+
end
151+
152+
def service
153+
self.softlayer_client[:Network_Component_Firewall].object_with_id(self.id)
154+
end
155+
156+
private
157+
158+
def self.default_rules_mask
159+
return { "mask" => default_rules_mask_keys }.to_sl_object_mask
160+
end
161+
162+
def self.default_rules_mask_keys
163+
['orderValue','action','destinationIpAddress','destinationIpSubnetMask',"protocol","destinationPortRangeStart","destinationPortRangeEnd",'sourceIpAddress',"sourceIpSubnetMask","version"]
164+
end
165+
end # ServerFirewall class
166+
end # SoftLayer module

0 commit comments

Comments
 (0)