Skip to content

Commit 42431a1

Browse files
committed
Added routines to bypass and route around firewalls. Added unit tests to ensure those routines must be used carefully
1 parent cecc3d2 commit 42431a1

10 files changed

Lines changed: 315 additions & 75 deletions

lib/softlayer/BareMetalServer.rb

Lines changed: 2 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -60,15 +60,7 @@ def cancel!(reason = :unneeded, comment = '')
6060
# you may have to use +SoftLayer_Hardware_Server+ as a type or service. That
6161
# service object is available thorugh the hardware_server_service method
6262
def service
63-
return softlayer_client[:Hardware].object_with_id(self.id)
64-
end
65-
66-
##
67-
# Returns the SoftLayer_Hardware_Server service for this bare metal server
68-
# This service is used less often than SoftLayer_Hardware, but may be required
69-
# for some operations
70-
def hardware_server_service
71-
self.softlayer_client[:Hardware_Server].object_with_id(self.id)
63+
return softlayer_client[:Hardware_Server].object_with_id(self.id)
7264
end
7365

7466
##
@@ -145,27 +137,6 @@ def firewall_port_speed
145137
return [max_group_speed, max_ungrouped_speed].max
146138
end
147139

148-
##
149-
# Change the current port speed of the server
150-
#
151-
# +new_speed+ is expressed Mbps and should be 0, 10, 100, or 1000.
152-
# Ports have a maximum speed that will limit the actual speed set
153-
# on the port.
154-
#
155-
# Set +public+ to +false+ in order to change the speed of the
156-
# primary private network interface.
157-
#
158-
def change_port_speed(new_speed, public = true)
159-
if public
160-
self.hardware_server_service.setPublicNetworkInterfaceSpeed(new_speed)
161-
else
162-
self.hardware_server_service.setPrivateNetworkInterfaceSpeed(new_speed)
163-
end
164-
165-
self.refresh_details()
166-
self
167-
end
168-
169140
##
170141
# Retrive the bare metal server with the given server ID from the
171142
# SoftLayer API
@@ -180,7 +151,7 @@ def self.server_with_id(server_id, options = {})
180151
softlayer_client = options[:client] || Client.default_client
181152
raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client
182153

183-
hardware_service = softlayer_client[:Hardware]
154+
hardware_service = softlayer_client[:Hardware_Server]
184155
hardware_service = hardware_service.object_mask(default_object_mask.to_sl_object_mask)
185156

186157
if options.has_key?(:object_mask)

lib/softlayer/Config.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,8 @@ def Config.globals_settings
7272

7373
def Config.environment_settings
7474
result = {}
75-
result[:username] = ENV["SL_USERNAME"] if ENV["SL_USERNAME"]
76-
result[:api_key] = ENV["SL_API_KEY"] if ENV["SL_API_KEY"]
75+
result[:username] = ENV['SL_USERNAME'] if ENV['SL_USERNAME']
76+
result[:api_key] = ENV['SL_API_KEY'] if ENV['SL_API_KEY']
7777
result
7878
end
7979

lib/softlayer/Server.rb

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ def reboot!(reboot_technique = :default_reboot)
9494
when :power_cycle
9595
self.service.rebootHard
9696
else
97-
raise RuntimeError, "Unrecognized reboot technique in SoftLayer::Server#reboot!}"
97+
raise ArgumentError, "Unrecognized reboot technique in SoftLayer::Server#reboot!}"
9898
end
9999
end
100100

@@ -117,7 +117,7 @@ def softlayer_properties(object_mask = nil)
117117
# Change the notes of the server
118118
# raises ArgumentError if you pass nil as the notes
119119
def notes=(new_notes)
120-
raise ArgumentError.new("The new notes cannot be nil") unless new_notes
120+
raise ArgumentError, "The new notes cannot be nil" unless new_notes
121121

122122
edit_template = {
123123
"notes" => new_notes
@@ -131,7 +131,7 @@ def notes=(new_notes)
131131
# Change the user metadata for the server.
132132
#
133133
def user_metadata=(new_metadata)
134-
raise ArgumentError.new("Cannot set user metadata to nil") unless new_metadata
134+
raise ArgumentError, "Cannot set user metadata to nil" unless new_metadata
135135
self.service.setUserMetadata([new_metadata])
136136
self.refresh_details()
137137
end
@@ -141,8 +141,8 @@ def user_metadata=(new_metadata)
141141
# Raises an ArgumentError if the new hostname is nil or empty
142142
#
143143
def set_hostname!(new_hostname)
144-
raise ArgumentError.new("The new hostname cannot be nil") unless new_hostname
145-
raise ArgumentError.new("The new hostname cannot be empty") if new_hostname.empty?
144+
raise ArgumentError, "The new hostname cannot be nil" unless new_hostname
145+
raise ArgumentError, "The new hostname cannot be empty" if new_hostname.empty?
146146

147147
edit_template = {
148148
"hostname" => new_hostname
@@ -159,8 +159,8 @@ def set_hostname!(new_hostname)
159159
# no further validation is done on the domain name
160160
#
161161
def set_domain!(new_domain)
162-
raise ArgumentError.new("The new hostname cannot be nil") unless new_domain
163-
raise ArgumentError.new("The new hostname cannot be empty") if new_domain.empty?
162+
raise ArgumentError, "The new hostname cannot be nil" unless new_domain
163+
raise ArgumentError, "The new hostname cannot be empty" if new_domain.empty?
164164

165165
edit_template = {
166166
"domain" => new_domain
@@ -188,11 +188,16 @@ def firewall_port_speed
188188
# on the port.
189189
#
190190
# Set +public+ to +false+ in order to change the speed of the
191-
# primary private network interface.
192-
#
193-
# This is an abstract method implemented by subclasses
191+
# private network interface.
194192
def change_port_speed(new_speed, public = true)
195-
raise "The abstract change_port_speed method of SoftLayer::Server was called. Subclasses should implement the method for their particular service"
193+
if public
194+
self.service.setPublicNetworkInterfaceSpeed(new_speed)
195+
else
196+
self.service.setPrivateNetworkInterfaceSpeed(new_speed)
197+
end
198+
199+
self.refresh_details()
200+
self
196201
end
197202

198203
##

lib/softlayer/ServerFirewall.rb

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,40 @@ def change_rules!(rules_data)
112112
self.softlayer_client[:Network_Firewall_Update_Request].createObject(change_object)
113113
end
114114

115+
##
116+
# This method asks the firewall to ignore its rule set and pass all traffic
117+
# through the firewall. Compare the behavior of this routine with
118+
# change_routing_bypass!
119+
#
120+
# It is important to note that changing the bypass to :bypass_firewall_rules
121+
# removes ALL the protection offered by the firewall. This routine should be
122+
# used with extreme discretion.
123+
#
124+
# Note that this routine queues a rule change and rule changes may take
125+
# time to process. The change will probably not take effect immediately
126+
#
127+
# The two symbols accepted as arguments by this routine are:
128+
# :apply_firewall_rules - The rules of the firewall are applied to traffic. This is the default operating mode of the firewall
129+
# :bypass_firewall_rules - The rules of the firewall are ignored. In this configuration the firewall provides no protection.
130+
#
131+
def change_rules_bypass!(bypass_symbol)
132+
change_object = {
133+
"networkComponentFirewallId" => self.id,
134+
"rules" => self.rules
135+
}
136+
137+
case bypass_symbol
138+
when :apply_firewall_rules
139+
change_object['bypassFlag'] = false
140+
self.softlayer_client[:Network_Firewall_Update_Request].createObject(change_object)
141+
when :bypass_firewall_rules
142+
change_object['bypassFlag'] = true
143+
self.softlayer_client[:Network_Firewall_Update_Request].createObject(change_object)
144+
else
145+
raise ArgumentError, "An invalid parameter was sent to #{__method__}. It accepts :apply_firewall_rules and :bypass_firewall_rules"
146+
end
147+
end
148+
115149
##
116150
# Locate and return all the server firewalls in the environment.
117151
#
@@ -128,7 +162,7 @@ def change_rules!(rules_data)
128162
# The collection of all those VLANs becomes the set of firewalls
129163
# for the account.
130164
#
131-
def self.find_firewalls(client)
165+
def self.find_firewalls(client = nil)
132166
softlayer_client = client || Client.default_client
133167
raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client
134168

lib/softlayer/ServerFirewallOrder.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class ServerFirewallOrder
1717
def initialize (server)
1818
@server = server
1919

20-
raise ArgumentError.new("Server does not have an active Public interface") if server.firewall_port_speed == 0
20+
raise ArgumentError, "Server does not have an active Public interface" if server.firewall_port_speed == 0
2121
end
2222

2323
##

lib/softlayer/VLANFirewall.rb

Lines changed: 80 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,78 @@ class VLANFirewall < SoftLayer::ModelBase
7070
# depending on the system load and other circumstances.
7171
def change_rules!(rules_data)
7272
change_object = {
73-
"firewallContextAccessControlListId" => self.id,
73+
"firewallContextAccessControlListId" => rules_ACL_id(),
7474
"rules" => rules_data
7575
}
7676

7777
self.softlayer_client[:Network_Firewall_Update_Request].createObject(change_object)
7878
end
7979

80+
##
81+
# This method asks the firewall to ignore its rule set and pass all traffic
82+
# through the firewall. Compare the behavior of this routine with
83+
# change_routing_bypass!
84+
#
85+
# It is important to note that changing the bypass to :bypass_firewall_rules
86+
# removes ALL the protection offered by the firewall. This routine should be
87+
# used with extreme discretion.
88+
#
89+
# Note that this routine queues a rule change and rule changes may take
90+
# time to process. The change will probably not take effect immediately
91+
#
92+
# The two symbols accepted as arguments by this routine are:
93+
# :apply_firewall_rules - The rules of the firewall are applied to traffic. This is the default operating mode of the firewall
94+
# :bypass_firewall_rules - The rules of the firewall are ignored. In this configuration the firewall provides no protection.
95+
#
96+
def change_rules_bypass!(bypass_symbol)
97+
change_object = {
98+
"firewallContextAccessControlListId" => rules_ACL_id(),
99+
"rules" => self.rules
100+
}
101+
102+
case bypass_symbol
103+
when :apply_firewall_rules
104+
change_object['bypassFlag'] = false
105+
self.softlayer_client[:Network_Firewall_Update_Request].createObject(change_object)
106+
when :bypass_firewall_rules
107+
change_object['bypassFlag'] = true
108+
self.softlayer_client[:Network_Firewall_Update_Request].createObject(change_object)
109+
else
110+
raise ArgumentError, "An invalid parameter was sent to #{__method__}. It accepts :apply_firewall_rules and :bypass_firewall_rules"
111+
end
112+
end
113+
114+
##
115+
# This method allows you to route traffic around the firewall
116+
# and directly to the servers it protects. Compare the behavior of this routine with
117+
# change_rules_bypass!
118+
#
119+
# It is important to note that changing the routing to :route_around_firewall
120+
# removes ALL the protection offered by the firewall. This routine should be
121+
# used with extreme discretion.
122+
#
123+
# Note that this routine constructs a transaction. The Routing change
124+
# may not happen immediately.
125+
#
126+
# The two symbols accepted as arguments by the routine are:
127+
# :route_through_firewall - Network traffic is sent through the firewall to the servers in the VLAN segment it protects. This is the usual operating mode of the firewall.
128+
# :route_around_firewall - Network traffic will be sent directly to the servers in the VLAN segment protected by this firewall. This means that the firewall will *NOT* be protecting those servers.
129+
#
130+
def change_routing_bypass!(routing_symbol)
131+
vlan_firewall_id = self['networkVlanFirewall']['id']
132+
133+
raise "Could not identify the device for a VLAN firewall" if !vlan_firewall_id
134+
135+
case routing_symbol
136+
when :route_through_firewall
137+
self.softlayer_client[:Network_Vlan_Firewall].object_with_id(vlan_firewall_id).updateRouteBypass(false)
138+
when :route_around_firewall
139+
self.softlayer_client[:Network_Vlan_Firewall].object_with_id(vlan_firewall_id).updateRouteBypass(true)
140+
else
141+
raise ArgumentError, "An invalid parameter was sent to #{__method__}. It accepts :route_through_firewall and :route_around_firewall"
142+
end
143+
end
144+
80145
##
81146
# Returns the name of the primary router the firewall is attached to.
82147
# This is often a "customer router" in one of the datacenters.
@@ -108,7 +173,7 @@ def high_availability?
108173
#
109174
# This list is obtained by asking the account for all the VLANs
110175
# it has that also have a networkVlanFirewall component.
111-
def self.find_firewalls(client)
176+
def self.find_firewalls(client = nil)
112177
softlayer_client = client || Client.default_client
113178
raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client
114179

@@ -117,8 +182,8 @@ def self.find_firewalls(client)
117182
filter.accept("networkVlans.networkVlanFirewall").when_it is_not_null
118183
}
119184

120-
vlan_firewalls = client[:Account].object_mask(vlan_firewall_mask).object_filter(vlan_firewall_filter).getNetworkVlans
121-
vlan_firewalls.collect { |firewall_data| SoftLayer::VLANFirewall.new(client, firewall_data)}
185+
vlan_firewalls = softlayer_client[:Account].object_mask(vlan_firewall_mask).object_filter(vlan_firewall_filter).getNetworkVlans
186+
vlan_firewalls.collect { |firewall_data| SoftLayer::VLANFirewall.new(softlayer_client, firewall_data)}
122187
end
123188

124189

@@ -157,7 +222,7 @@ def rules_ACL_id
157222
end
158223

159224
def self.vlan_firewall_mask
160-
return "mask[primaryRouter,highAvailabilityFirewallFlag,"+
225+
return "mask[primaryRouter,highAvailabilityFirewallFlag," +
161226
"firewallInterfaces.firewallContextAccessControlLists," +
162227
"networkVlanFirewall[id, datacenter, primaryIpAddress, firewallType, fullyQualifiedDomainName]]"
163228
end
@@ -167,7 +232,16 @@ def self.default_rules_mask
167232
end
168233

169234
def self.default_rules_mask_keys
170-
['orderValue','action','destinationIpAddress','destinationIpSubnetMask',"protocol","destinationPortRangeStart","destinationPortRangeEnd",'sourceIpAddress',"sourceIpSubnetMask","version"]
235+
['orderValue',
236+
'action',
237+
'destinationIpAddress',
238+
'destinationIpSubnetMask',
239+
'protocol',
240+
'destinationPortRangeStart',
241+
'destinationPortRangeEnd',
242+
'sourceIpAddress',
243+
'sourceIpSubnetMask',
244+
'version']
171245
end
172246
end # class Firewall
173247
end # module SoftLayer

lib/softlayer/VirtualServer.rb

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -65,27 +65,6 @@ class VirtualServer < Server
6565
end
6666
end
6767

68-
##
69-
# Change the current port speed of the server
70-
#
71-
# +new_speed+ is expressed Mbps and should be 0, 10, 100, or 1000.
72-
# Ports have a maximum speed that will limit the actual speed set
73-
# on the port.
74-
#
75-
# Set +public+ to +false+ in order to change the speed of the
76-
# primary private network interface.
77-
#
78-
def change_port_speed(new_speed, public = true)
79-
if public
80-
self.service.setPublicNetworkInterfaceSpeed(new_speed)
81-
else
82-
self.service.setPrivateNetworkInterfaceSpeed(new_speed)
83-
end
84-
85-
self.refresh_details()
86-
self
87-
end
88-
8968
##
9069
# IMMEDIATELY cancel this virtual server
9170
#

spec/BareMetalServer_spec.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,20 @@
2626
SoftLayer::BareMetalServer.new(mock_client, { "id" => 12345 })
2727
end
2828

29-
it "identifies itself with the SoftLayer_Hardware service" do
29+
it "identifies itself with the SoftLayer_Hardware_Server service" do
3030
service = sample_server.service
3131
expect(service.server_object_id).to eq(12345)
32-
expect(service.target.service_name).to eq "SoftLayer_Hardware"
32+
expect(service.target.service_name).to eq "SoftLayer_Hardware_Server"
3333
end
3434

3535
it_behaves_like "server with port speed" do
3636
let (:server) { sample_server }
3737
end
3838

39+
it_behaves_like "server with mutable hostname" do
40+
let (:server) { sample_server }
41+
end
42+
3943
it "can be cancelled" do
4044
mock_client = SoftLayer::Client.new(:username => "fakeuser", :api_key => "DEADBEEFBADF00D")
4145
allow(mock_client).to receive(:[]) do |service_name|

0 commit comments

Comments
 (0)