Skip to content

Commit ae4c3d1

Browse files
Merge branch 'develop' of https://github.com/CactuseSecurity/firewall-orchestrator into puppeteer
2 parents 7f0296a + 7681ade commit ae4c3d1

File tree

16 files changed

+603
-39
lines changed

16 files changed

+603
-39
lines changed

roles/database/files/sql/idempotent/fworch-texts.sql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,8 @@ INSERT INTO txt VALUES ('PagerPagesize', 'German', 'Seitengrösse');
283283
INSERT INTO txt VALUES ('PagerPagesize', 'English', 'Page size');
284284
INSERT INTO txt VALUES ('PagerSubmit', 'German', 'Speichern');
285285
INSERT INTO txt VALUES ('PagerSubmit', 'English', 'Save');
286+
INSERT INTO txt VALUES ('order_by', 'German', 'Sortieren nach');
287+
INSERT INTO txt VALUES ('order_by', 'English', 'Order by');
286288

287289
-- (re)login
288290
INSERT INTO txt VALUES ('login', 'German', 'Anmelden');

roles/importer/files/importer/fortiadom5ff/fmgr_gw_networking.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,12 @@ def normalize_network_data(native_config, normalized_config, mgm_details):
6262
normalized_config['routing'].sort(key=getRouteDestination,reverse=True)
6363

6464
for interface in native_config['interfaces_per_device/' + full_vdom_name]:
65-
if interface['ipv6']['ip6-address']!='::/0':
65+
if 'ipv6' in interface and 'ip6-address' in interface['ipv6'] and interface['ipv6']['ip6-address']!='::/0':
6666
ipv6, netmask_bits = interface['ipv6']['ip6-address'].split('/')
6767
normIfV6 = Interface(dev_id, interface['name'], IPAddress(ipv6), netmask_bits, ip_version=6)
6868
normalized_config['interfaces'].append(normIfV6)
6969

70-
if interface['ip']!=['0.0.0.0','0.0.0.0']:
70+
if 'ip' in interface and interface['ip']!=['0.0.0.0','0.0.0.0']:
7171
ipv4 = IPAddress(interface['ip'][0])
7272
netmask_bits = IPAddress(interface['ip'][1]).netmask_bits()
7373
normIfV4 = Interface(dev_id, interface['name'], ipv4, netmask_bits, ip_version=4)

roles/importer/files/importer/fortiadom5ff/fmgr_network.py

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,22 @@ def normalize_nwobjects(full_config, config2import, import_id, nw_obj_types, jwt
103103
obj.update({'control_id': import_id})
104104
nw_objects.append(obj)
105105

106+
# dynamic objects have different return structure
107+
if 'response' in full_config['nw_obj_global_firewall/internet-service-basic'][0] and 'results' in full_config['nw_obj_global_firewall/internet-service-basic'][0]['response']:
108+
for obj_orig in full_config['nw_obj_global_firewall/internet-service-basic'][0]['response']['results']:
109+
if 'name' in obj_orig and 'q_origin_key' in obj_orig:
110+
obj = {
111+
'obj_name': obj_orig['name'],
112+
'obj_typ': 'network',
113+
'obj_ip': '0.0.0.0/0',
114+
'obj_uid': 'q_origin_key_' + str(obj_orig['q_origin_key']),
115+
'control_id': import_id,
116+
'obj_zone': 'global'
117+
}
118+
nw_objects.append(obj)
119+
else:
120+
logger.warning("internet service objects return format broken")
121+
106122
# finally add "Original" network object for natting
107123
original_obj_name = 'Original'
108124
original_obj_uid = 'Original'
@@ -189,7 +205,11 @@ def get_first_ip_of_destination(obj_ref, config2import):
189205

190206
for obj in config2import['network_objects']:
191207
if 'obj_uid' in obj and obj['obj_uid']==obj_ref:
192-
return obj['obj_ip']
208+
if 'obj_type' in obj and obj['obj_type']=='group':
209+
if 'obj_member_refs' in obj and list_delimiter in obj['obj_member_refs']:
210+
return get_first_ip_of_destination(obj['obj_member_refs'].split(list_delimiter)[0], config2import)
211+
elif 'obj_ip' in obj:
212+
return obj['obj_ip']
193213
logger.warning('src nat behind interface: found no IP info for destination object ' + obj_ref)
194214
return None
195215

@@ -210,13 +230,14 @@ def resolve_raw_objects (obj_name_string_list, delimiter, obj_dict, name_key, ui
210230
if rule_type is not None:
211231
if obj_type == 'network':
212232
if 'v4' in rule_type and 'global' in rule_type:
213-
object_tables = [obj_dict['nw_obj_global_firewall/address'], obj_dict['nw_obj_global_firewall/addrgrp']]
233+
object_tables = [obj_dict['nw_obj_global_firewall/address'], obj_dict['nw_obj_global_firewall/addrgrp'], obj_dict['nw_obj_global_firewall/internet-service-basic'][0]['response']['results']]
214234
elif 'v6' in rule_type and 'global' in rule_type:
215235
object_tables = [obj_dict['nw_obj_global_firewall/address6'], obj_dict['nw_obj_global_firewall/addrgrp6']]
216236
elif 'v4' in rule_type and 'adom' in rule_type:
217237
object_tables = [obj_dict['nw_obj_adom_firewall/address'], obj_dict['nw_obj_adom_firewall/addrgrp'], \
218238
obj_dict['nw_obj_global_firewall/address'], obj_dict['nw_obj_global_firewall/addrgrp'], \
219-
obj_dict['nw_obj_adom_firewall/vip'] ]
239+
obj_dict['nw_obj_adom_firewall/vip'], obj_dict['nw_obj_adom_system/external-resource'], \
240+
obj_dict['nw_obj_global_firewall/internet-service-basic'][0]['response']['results'] ]
220241
elif 'v6' in rule_type and 'adom' in rule_type:
221242
object_tables = [obj_dict['nw_obj_adom_firewall/address6'], obj_dict['nw_obj_adom_firewall/addrgrp6'], \
222243
obj_dict['nw_obj_global_firewall/address6'], obj_dict['nw_obj_global_firewall/addrgrp6']]
@@ -235,7 +256,13 @@ def resolve_raw_objects (obj_name_string_list, delimiter, obj_dict, name_key, ui
235256
else:
236257
for obj in tab:
237258
if obj[name_key] == el:
238-
ref_list.append(obj[uid_key])
259+
if uid_key in obj:
260+
ref_list.append(obj[uid_key])
261+
# in case of internet-service-object we find no uid field, but custom q_origin_key_
262+
elif 'q_origin_key' in obj:
263+
ref_list.append('q_origin_key_' + str(obj['q_origin_key']))
264+
else:
265+
logger.error('found object without expected uid')
239266
break_flag = True
240267
found = True
241268
break

roles/importer/files/importer/fortiadom5ff/fmgr_rule.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ def normalize_access_rules(full_config, config2import, import_id, mgm_details={}
184184
rule['rule_svc'] = extend_string_list(rule['rule_svc'], rule_orig, 'service', list_delimiter, jwt=jwt, import_id=import_id)
185185
rule['rule_src'] = extend_string_list(rule['rule_src'], rule_orig, 'srcaddr6', list_delimiter, jwt=jwt, import_id=import_id)
186186
rule['rule_dst'] = extend_string_list(rule['rule_dst'], rule_orig, 'dstaddr6', list_delimiter, jwt=jwt, import_id=import_id)
187+
rule['rule_src'] = extend_string_list(rule['rule_src'], rule_orig, 'internet-service-src-name', list_delimiter, jwt=jwt, import_id=import_id)
187188

188189
if len(rule_orig['srcintf'])>0:
189190
src_obj_zone = fmgr_zone.add_zone_if_missing (config2import, rule_orig['srcintf'][0], import_id)
@@ -194,6 +195,8 @@ def normalize_access_rules(full_config, config2import, import_id, mgm_details={}
194195

195196
if 'srcaddr-negate' in rule_orig:
196197
rule.update({ 'rule_src_neg': rule_orig['srcaddr-negate']=='disable'})
198+
elif 'internet-service-src-negate' in rule_orig:
199+
rule.update({ 'rule_src_neg': rule_orig['internet-service-src-negate']=='disable'})
197200
if 'dstaddr-negate' in rule_orig:
198201
rule.update({ 'rule_dst_neg': rule_orig['dstaddr-negate']=='disable'})
199202
if 'service-negate' in rule_orig:
@@ -411,9 +414,9 @@ def handle_combined_nat_rule(rule, rule_orig, config2import, nat_rule_number, im
411414
if hideInterface is not None:
412415
obj_name = 'hide_IF_ip_' + str(hideInterface) + '_' + str(destination_interface_ip)
413416
obj_comment = 'FWO auto-generated dummy object for source nat'
414-
if type(ipaddress.ip_address(str(destination_interface_ip))) is ipaddress.IPv6Address:
417+
if destination_interface_ip is not None and type(ipaddress.ip_address(str(destination_interface_ip))) is ipaddress.IPv6Address:
415418
HideNatIp = str(destination_interface_ip) + '/128'
416-
elif type(ipaddress.ip_address(str(destination_interface_ip))) is ipaddress.IPv4Address:
419+
elif destination_interface_ip is not None and type(ipaddress.ip_address(str(destination_interface_ip))) is ipaddress.IPv4Address:
417420
HideNatIp = str(destination_interface_ip) + '/32'
418421
else:
419422
HideNatIp = '0.0.0.0/32'

roles/importer/files/importer/fortiadom5ff/fwcommon.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
scope = ['global', 'adom']
1616
nw_obj_types = ['firewall/address', 'firewall/address6', 'firewall/addrgrp',
17-
'firewall/addrgrp6', 'firewall/ippool', 'firewall/vip']
17+
'firewall/addrgrp6', 'firewall/ippool', 'firewall/vip', 'system/external-resource']
1818
svc_obj_types = ['application/list', 'application/group', 'application/categories',
1919
'application/custom', 'firewall/service/custom', 'firewall/service/group']
2020

@@ -107,6 +107,7 @@ def get_config(config2import, full_config, current_import_id, mgm_details, limit
107107

108108

109109
def getObjects(sid, fm_api_url, raw_config, adom_name, limit, scope, nw_obj_types, svc_obj_types):
110+
logger = getFwoLogger()
110111
# get those objects that exist globally and on adom level
111112
for s in scope:
112113
# get network objects:
@@ -137,6 +138,35 @@ def getObjects(sid, fm_api_url, raw_config, adom_name, limit, scope, nw_obj_type
137138
adom_scope = s
138139
fmgr_getter.update_config_with_fortinet_api_call(
139140
raw_config, sid, fm_api_url, "/pm/config/"+adom_scope+"/obj/" + object_type, "user_obj_" + s + "_" + object_type, limit=limit)
141+
142+
# get one arbitrary device and vdom to get dynamic objects
143+
# they are equal across all adoms, vdoms, devices
144+
devices = fmgr_getter.fortinet_api_call(sid, fm_api_url, '/dvmdb/adom/' + adom_name + '/device')
145+
if len(devices)>0 and 'name' in devices[0] and 'vdom' in devices[0] and 'name' in devices[0]['vdom'][0]:
146+
arbitraryDevice = devices[0]['name']
147+
arbitraryVdom = devices[0]['vdom'][0]['name']
148+
else:
149+
logger.error('no device or vdom info for adom: ' + adom_name)
150+
151+
# get dynamic objects
152+
payload = {
153+
'params': [
154+
{
155+
'data': {
156+
'action': 'get',
157+
'resource': '/api/v2/monitor/firewall/internet-service-basic?vdom=' + arbitraryVdom,
158+
'target': [
159+
'adom/' + adom_name + '/device/' + arbitraryDevice
160+
]
161+
}
162+
}
163+
]
164+
}
165+
fmgr_getter.update_config_with_fortinet_api_call(
166+
raw_config, sid, fm_api_url, "sys/proxy/json", "nw_obj_global_firewall/internet-service-basic", limit=limit, payload=payload, method='exec')
167+
168+
169+
140170

141171

142172
# def getZones(sid, fm_api_url, raw_config, adom_name, limit, debug_level):
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System.Net;
2+
using NetTools;
3+
using FWO.Basics;
4+
5+
namespace FWO.Basics.Comparer
6+
{
7+
public class IPAdressComparer : IComparer<IPAddress>
8+
{
9+
public int Compare(IPAddress? x, IPAddress? y)
10+
{
11+
if(x is null || y is null)
12+
{
13+
return 0;
14+
}
15+
16+
int compareIPFamiliesResult = IpOperations.CompareIpFamilies(x, y);
17+
18+
if (compareIPFamiliesResult != 0)
19+
{
20+
return compareIPFamiliesResult;
21+
}
22+
23+
return IpOperations.CompareIpValues(x, y);
24+
}
25+
}
26+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
using System.Numerics;
2+
using NetTools;
3+
4+
namespace FWO.Basics.Comparer
5+
{
6+
public class IPAddressRangeComparer : IComparer<IPAddressRange>
7+
{
8+
public int Compare(IPAddressRange? x, IPAddressRange? y)
9+
{
10+
if(x is null || y is null)
11+
{
12+
return 0;
13+
}
14+
15+
IPAdressComparer iPAdressComparer = new ();
16+
int compareIPAddressResult = iPAdressComparer.Compare(x.Begin, y.Begin);
17+
18+
if (compareIPAddressResult != 0)
19+
{
20+
return compareIPAddressResult;
21+
}
22+
23+
BigInteger xRangeSize = GetIPRangeSize(x);
24+
BigInteger yRangeSize = GetIPRangeSize(y);
25+
26+
if(xRangeSize < yRangeSize)
27+
{
28+
return -1;
29+
}
30+
31+
if(xRangeSize > yRangeSize)
32+
{
33+
return 1;
34+
}
35+
36+
return 0;
37+
}
38+
39+
public BigInteger GetIPRangeSize(IPAddressRange range)
40+
{
41+
byte[] startBytes = range.Begin.GetAddressBytes();
42+
byte[] endBytes = range.End.GetAddressBytes();
43+
44+
// prevents overflow problem
45+
byte[] startBytesPadded = startBytes.Reverse().Concat(new byte[] { 0 }).ToArray();
46+
byte[] endBytesPadded = endBytes.Reverse().Concat(new byte[] { 0 }).ToArray();
47+
48+
BigInteger startValue = new BigInteger(startBytesPadded);
49+
BigInteger endValue = new BigInteger(endBytesPadded);
50+
51+
return endValue - startValue;
52+
}
53+
}
54+
}

roles/lib/files/FWO.Basics/IpOperations.cs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,5 +193,93 @@ private static string GetIPv4SubnetMask(int prefixLength)
193193
uint[] bytes = [(mask >> 24) & 0xff, (mask >> 16) & 0xff, (mask >> 8) & 0xff, mask & 0xff];
194194
return string.Join(".", bytes);
195195
}
196+
197+
/// <summary>
198+
/// Compares the values of two IP adresses byte by byte.
199+
/// </summary>
200+
public static int CompareIpValues(IPAddress ip1, IPAddress ip2)
201+
{
202+
var ip1Bytes = ip1.GetAddressBytes();
203+
var ip2Bytes = ip2.GetAddressBytes();
204+
205+
for (int i = 0; i < ip1Bytes.Length; i++)
206+
{
207+
if (ip1Bytes[i] < ip2Bytes[i])
208+
{
209+
return -1;
210+
}
211+
if (ip1Bytes[i] > ip2Bytes[i])
212+
{
213+
return 1;
214+
}
215+
}
216+
217+
return 0;
218+
}
219+
220+
/// <summary>
221+
/// Compares to strings that may contain subnet masks by their format and value.
222+
/// </summary>
223+
public static int CompareSubnetMasks(string subnetMask1, string subnetMask2)
224+
{
225+
if (subnetMask1.StartsWith(@"\")) subnetMask1 = subnetMask1.Substring(1);
226+
if (subnetMask2.StartsWith(@"\")) subnetMask2 = subnetMask2.Substring(1);
227+
228+
if(subnetMask1 != subnetMask2)
229+
{
230+
// first without subnet masks
231+
if (subnetMask1 == "") return -1;
232+
if (subnetMask2 == "") return 1;
233+
234+
// then cidr
235+
int subnet1CIDR;
236+
int subnet2CIDR;
237+
bool subnet1IsInt = int.TryParse(subnetMask1, out subnet1CIDR);
238+
bool subnet2IsInt = int.TryParse(subnetMask2, out subnet2CIDR);
239+
if (subnet1IsInt && subnet2IsInt)
240+
{
241+
if (subnet1CIDR < subnet2CIDR) return -1;
242+
if (subnet1CIDR > subnet2CIDR) return 1;
243+
}
244+
if (subnet1IsInt) return -1;
245+
if (subnet2IsInt) return 1;
246+
247+
248+
IPAddress subnet1IP;
249+
IPAddress subnet2IP;
250+
bool subnet1IsIp = IPAddress.TryParse(subnetMask1, out subnet1IP);
251+
bool subnet2IsIp = IPAddress.TryParse(subnetMask2, out subnet2IP);
252+
if (subnet1IsIp && subnet2IsIp)
253+
{
254+
// if both in ip format order by value
255+
int compareIpValuesResult = CompareIpValues(subnet1IP, subnet2IP);
256+
if (compareIpValuesResult != 0) return compareIpValuesResult;
257+
}
258+
259+
// if one is ip format it should come before the unhandled case
260+
if (subnet1IsIp) return -1;
261+
if (subnet2IsIp) return 1;
262+
}
263+
264+
// if nothing fits just treat as they were the same
265+
return 0;
266+
}
267+
268+
/// <summary>
269+
/// Compares two IPAdress objects by their family (IPv4 or IPv6).
270+
/// </summary>
271+
public static int CompareIpFamilies(IPAddress ip1, IPAddress ip2)
272+
{
273+
if (ip1.AddressFamily == AddressFamily.InterNetwork && ip2.AddressFamily == AddressFamily.InterNetworkV6 )
274+
{
275+
return -1;
276+
}
277+
if (ip1.AddressFamily == AddressFamily.InterNetworkV6 && ip2.AddressFamily == AddressFamily.InterNetwork )
278+
{
279+
return 1;
280+
}
281+
282+
return 0;
283+
}
196284
}
197285
}

roles/lib/files/FWO.Basics/StringExtensionsIp.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,17 @@ public static string ReplaceAll(this string input, IEnumerable<string> values, s
219219

220220
return input;
221221
}
222+
/// <summary>
223+
/// Parses a string to an IPAdress object and a string that represents the subnet mask (empty if there is no subnet mask).
224+
/// </summary>
225+
public static (IPAddress, string) ToIPAdressAndSubnetMask(this string str)
226+
{
227+
IPAddress ipAdress = IPAddress.Parse(str.StripOffNetmask());
228+
string subnetMask = "";
229+
var hasSubnetMask = str.TryGetNetmask(out subnetMask);
230+
231+
return (ipAdress, hasSubnetMask ? subnetMask.Substring(1) : "");
232+
}
233+
222234
}
223235
}
224-

0 commit comments

Comments
 (0)