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