Skip to content

Commit 7f7a87b

Browse files
committed
Add support for IPv6 in security groups
1 parent 58ab34a commit 7f7a87b

File tree

4 files changed

+738
-266
lines changed

4 files changed

+738
-266
lines changed

app/messages/validators/security_group_rule_validator.rb

+12-7
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@ def validate(record)
3030
add_rule_error("protocol must be 'tcp', 'udp', 'icmp', or 'all'", record, index) unless valid_protocol(rule[:protocol])
3131

3232
if valid_destination_type(rule[:destination], record, index)
33-
rules = rule[:destination].split(',', -1)
34-
add_rule_error("maximum destinations per rule exceeded - must be under #{MAX_DESTINATIONS_PER_RULE}", record, index) unless rules.length <= MAX_DESTINATIONS_PER_RULE
33+
destinations = rule[:destination].split(',', -1)
34+
add_rule_error("maximum destinations per rule exceeded - must be under #{MAX_DESTINATIONS_PER_RULE}", record, index) unless destinations.length <= MAX_DESTINATIONS_PER_RULE
3535

36-
rules.each do |d|
36+
destinations.each do |d|
3737
validate_destination(d, record, index)
3838
end
3939
end
@@ -142,12 +142,17 @@ def validate_destination(destination, record, index)
142142
add_rule_error(error_message, record, index) unless CloudController::RuleValidator.parse_ip(address_list.first)
143143

144144
elsif address_list.length == 2
145-
ipv4s = CloudController::RuleValidator.parse_ip(address_list)
146-
return add_rule_error('destination IP address range is invalid', record, index) unless ipv4s
145+
ips = CloudController::RuleValidator.parse_ip(address_list)
146+
return add_rule_error('destination IP address range is invalid', record, index) unless ips
147+
148+
sorted_ips = if ips.first.is_a?(NetAddr::IPv4)
149+
NetAddr.sort_IPv4(ips)
150+
else
151+
NetAddr.sort_IPv6(ips)
152+
end
147153

148-
sorted_ipv4s = NetAddr.sort_IPv4(ipv4s)
149154
reversed_range_error = 'beginning of IP address range is numerically greater than the end of its range (range endpoints are inverted)'
150-
add_rule_error(reversed_range_error, record, index) unless ipv4s.first == sorted_ipv4s.first
155+
add_rule_error(reversed_range_error, record, index) unless ips.first == sorted_ips.first
151156

152157
else
153158
add_rule_error(error_message, record, index)

lib/cloud_controller/config_schemas/base/api_schema.rb

+3-1
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,9 @@ class ApiSchema < VCAP::Config
389389
},
390390

391391
update_metric_tags_on_rename: bool,
392-
app_instance_stopping_state: bool
392+
app_instance_stopping_state: bool,
393+
394+
optional(:enable_ipv6) => bool
393395
}
394396
end
395397
# rubocop:enable Metrics/BlockLength

lib/cloud_controller/rule_validator.rb

+61-14
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,16 @@ def self.validate_destination(destination)
5050
return true if parse_ip(address_list.first)
5151

5252
elsif address_list.length == 2
53-
ipv4s = parse_ip(address_list)
54-
return false if ipv4s.nil?
53+
ips = parse_ip(address_list)
54+
return false if ips.nil?
5555

56-
sorted_ipv4s = NetAddr.sort_IPv4(ipv4s)
57-
return true if ipv4s.first == sorted_ipv4s.first
56+
sorted_ips = if ips.first.is_a?(NetAddr::IPv4)
57+
NetAddr.sort_IPv4(ips)
58+
else
59+
NetAddr.sort_IPv6(ips)
60+
end
61+
62+
return true if ips.first == sorted_ips.first
5863
end
5964

6065
false
@@ -65,19 +70,14 @@ def self.validate_boolean(bool)
6570
end
6671

6772
def self.parse_ip(val)
68-
if val.is_a?(Array)
69-
val.map do |ip|
70-
NetAddr::IPv4.parse(ip)
71-
end
72-
else
73-
NetAddr::IPv4Net.parse(val)
74-
end
75-
rescue NetAddr::ValidationError
76-
nil
73+
ipv4 = parse_ipv4(val)
74+
75+
ipv6 = parse_ipv6(val) if !ipv4 && config.get(:enable_ipv6)
76+
77+
ipv4 || ipv6
7778
end
7879

7980
def self.comma_delimited_destinations_enabled?
80-
config = VCAP::CloudController::Config.config
8181
config.get(:security_groups, :enable_comma_delimited_destinations)
8282
end
8383

@@ -92,7 +92,17 @@ def self.no_leading_zeros(destination)
9292
no_zeros
9393
end
9494

95+
private_class_method def self.config
96+
VCAP::CloudController::Config.config
97+
end
98+
9599
private_class_method def self.no_leading_zeros_in_address(address)
100+
return no_leading_zeros_in_ipv4_address(address) if address.include?('.')
101+
102+
no_leading_zeros_in_ipv6_address(address) if address.include?(':')
103+
end
104+
105+
private_class_method def self.no_leading_zeros_in_ipv4_address(address)
96106
address.split('.') do |octet|
97107
if octet.start_with?('0') && octet.length > 1
98108
octet_parts = octet.split('/')
@@ -104,5 +114,42 @@ def self.no_leading_zeros(destination)
104114

105115
true
106116
end
117+
118+
private_class_method def self.no_leading_zeros_in_ipv6_address(address)
119+
address.split(':').each do |segment|
120+
next unless segment.start_with?('0') && segment.length > 1
121+
122+
segment_parts = segment.split('/')
123+
return false if segment_parts.length < 2
124+
125+
return false if segment_parts[0].length > 1 && segment_parts[0].start_with?('0')
126+
end
127+
128+
true
129+
end
130+
131+
private_class_method def self.parse_ipv4(val)
132+
if val.is_a?(Array)
133+
val.map do |ip|
134+
NetAddr::IPv4.parse(ip)
135+
end
136+
else
137+
NetAddr::IPv4Net.parse(val)
138+
end
139+
rescue NetAddr::ValidationError
140+
nil
141+
end
142+
143+
private_class_method def self.parse_ipv6(val)
144+
if val.is_a?(Array)
145+
val.map do |ip|
146+
NetAddr::IPv6.parse(ip)
147+
end
148+
else
149+
NetAddr::IPv6Net.parse(val)
150+
end
151+
rescue NetAddr::ValidationError
152+
nil
153+
end
107154
end
108155
end

0 commit comments

Comments
 (0)