Skip to content

Commit 0549303

Browse files
fix: dealing with different address types in phase2 interfaces (#121)
1 parent 12a265f commit 0549303

6 files changed

Lines changed: 189 additions & 37 deletions

File tree

fortilib/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,7 @@ def get_fortigate_member_array(source: List, attrname="name") -> List[Dict]:
2929
)
3030

3131
return ret
32+
33+
34+
def remove_empty_dict_values(source: dict):
35+
return {k: v for k, v in source.items() if v}

fortilib/firewall.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ class FortigateFirewall:
6262
:ivar proxy_address_groups: List of :class:`fortilib.proxyaddressgroup.FortigateProxyAddressGroup` (default: [])
6363
:ivar proxy_policies: List of :class:`fortilib.proxypolicies.FortigateProxyPolicy` (default: [])
6464
:ivar all_addresses: List of :class:`fortilib.proxyaddresses.FortigateProxyAddress` and :class:`fortilib.address.FortigateAddress` (default: [])
65+
:ivar phase1_interfaces: List of :class:`fortilib.phase1interface.FortigatePhase1Interface` (default: [])
66+
:ivar phase2_interfaces: List of :class:`fortilib.phase2interface.FortigatePhase2Interface`` (default: [])
6567
"""
6668

6769
def __init__(self, name: str, fortigateapi: FortigateFirewallApi):

fortilib/phase2interface.py

Lines changed: 107 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import ipaddress
22

3+
from fortilib import remove_empty_dict_values
34
from fortilib.base import FortigateNamedObject
45

56

@@ -9,12 +10,24 @@ class FortigatePhase2KeylifeType:
910
BOTH = "both"
1011

1112

13+
class FortigatePhase2AddressType:
14+
IP = "ip"
15+
RANGE = "range"
16+
SUBNET = "subnet"
17+
18+
1219
class FortigatePhase2Interface(FortigateNamedObject):
1320
"""Fortigate object for phase2 interfaces.
1421
1522
:ivar phase1_name: Name of phase1
23+
:ivar src_start_ip: Source address e.g. 10.0.0.10
24+
:ivar src_end_ip: Source address e.g. 10.0.0.20
1625
:ivar src_subnet: Source subnet e.g. 10.0.0.0/8
26+
:ivar src_addr_type: Source address type e.g. subnet
27+
:ivar dst_start_ip: Destination address e.g. 192.168.100.10
28+
:ivar dst_end_ip: Destination address e.g. 192.168.100.20
1729
:ivar dst_subnet: Destination subnet e.g. 192.168.100.0/24
30+
:ivar dst_addr_type: Destination address type e.g. subnet
1831
:ivar dhgrp: Diffie-Hellman group e.g. 20
1932
:ivar auto_negotiate: Auto-negotiate enable/disable (default: "disable")
2033
:ivar keepalive: Keepalive enable/disable (default: "disable")
@@ -32,6 +45,16 @@ def __init__(self):
3245
self.phase1_name: str = ""
3346
self.dst_subnet: ipaddress.IPv4Network = None
3447
self.src_subnet: ipaddress.IPv4Network = None
48+
self.dst_start_ip: ipaddress.IPv4Address = None
49+
self.dst_end_ip: ipaddress.IPv4Address = None
50+
self.src_start_ip: ipaddress.IPv4Address = None
51+
self.src_end_ip: ipaddress.IPv4Address = None
52+
self.dst_addr_type: FortigatePhase2AddressType = (
53+
FortigatePhase2AddressType.SUBNET
54+
)
55+
self.src_addr_type: FortigatePhase2AddressType = (
56+
FortigatePhase2AddressType.SUBNET
57+
)
3558
self.dhgrp: str = ""
3659
self.auto_negotiate: str = "disable"
3760
self.keepalive: str = "disable"
@@ -58,18 +81,50 @@ def populate(self, object_data: dict):
5881
super().populate(object_data)
5982

6083
self.phase1_name = object_data.get("phase1name", self.phase1_name)
61-
self.dst_subnet = ipaddress.ip_network(
62-
"{}/{}".format(
63-
object_data.get("dst-subnet", "0.0.0.0/0").split()[0],
64-
object_data.get("dst-subnet", "0.0.0.0/0").split()[1],
65-
)
84+
self.dst_addr_type = object_data.get(
85+
"dst-addr-type", FortigatePhase2AddressType.SUBNET
6686
)
67-
self.src_subnet = ipaddress.ip_network(
68-
"{}/{}".format(
69-
object_data.get("src-subnet", "0.0.0.0/0").split()[0],
70-
object_data.get("src-subnet", "0.0.0.0/0").split()[1],
71-
)
87+
self.src_addr_type = object_data.get(
88+
"src-addr-type", FortigatePhase2AddressType.SUBNET
7289
)
90+
if self.dst_addr_type == "subnet":
91+
self.dst_subnet = ipaddress.ip_network(
92+
"{}/{}".format(
93+
object_data.get("dst-subnet", "0.0.0.0/0").split()[0],
94+
object_data.get("dst-subnet", "0.0.0.0/0").split()[1],
95+
)
96+
)
97+
98+
if self.src_addr_type == FortigatePhase2AddressType.SUBNET:
99+
self.src_subnet = ipaddress.ip_network(
100+
"{}/{}".format(
101+
object_data.get("src-subnet", "0.0.0.0/0").split()[0],
102+
object_data.get("src-subnet", "0.0.0.0/0").split()[1],
103+
)
104+
)
105+
if (
106+
self.dst_addr_type == FortigatePhase2AddressType.IP
107+
or self.dst_addr_type == FortigatePhase2AddressType.RANGE
108+
):
109+
self.dst_start_ip = ipaddress.ip_address(
110+
object_data.get("dst-start-ip", "0.0.0.0")
111+
)
112+
if self.dst_addr_type == FortigatePhase2AddressType.RANGE:
113+
self.dst_end_ip = ipaddress.ip_address(
114+
object_data.get("dst-end-ip", "0.0.0.0")
115+
)
116+
if (
117+
self.src_addr_type == FortigatePhase2AddressType.IP
118+
or self.src_addr_type == FortigatePhase2AddressType.RANGE
119+
):
120+
self.src_start_ip = ipaddress.ip_address(
121+
object_data.get("src-start-ip", "0.0.0.0")
122+
)
123+
if self.src_addr_type == FortigatePhase2AddressType.RANGE:
124+
self.src_end_ip = ipaddress.ip_address(
125+
object_data.get("src-end-ip", "0.0.0.0")
126+
)
127+
73128
self.dhgrp = object_data.get("dhgrp", self.dhgrp)
74129
self.auto_negotiate = object_data.get(
75130
"auto-negotiate", self.auto_negotiate
@@ -94,36 +149,54 @@ def render(self) -> dict:
94149
{
95150
"name": "vpn_phase2",
96151
"phase1name": "vpn_phase1",
152+
"dst-addr-type": "subnet"
97153
"dst-subnet": "192.168.100.0/24",
154+
"dst-start-ip": "192.168.100.1",
155+
"dst-end-ip": "192.168.100.128",
156+
"src-addr-type": "subnet"
98157
"src-subnet": "10.0.0.0/8",
158+
"src-start-ip": "10.0.0.1",
159+
"src-end-ip": "10.0.0.128",
99160
"dhgrp": "20",
100-
"pfs":"enable",
101-
"replay":"enable",
102-
"keepalive":"disable",
103-
"auto-negotiate":"enable",
104-
"keylifeseconds":14400,
105-
"keylifekbs":5120,
106-
"keylife-type":"seconds",
161+
"pfs": "enable",
162+
"replay": "enable",
163+
"keepalive": "disable",
164+
"auto-negotiate": "enable",
165+
"keylifeseconds": 14400,
166+
"keylifekbs": 5120,
167+
"keylife-type": "seconds",
107168
"proposal": "chacha20poly1305 aes256-sha512 aes256gcm",
108169
"comments": "",
109170
},
110171
"""
111-
return {
112-
"name": self.name,
113-
"phase1name": self.phase1_name,
114-
"dst-subnet": str(self.dst_subnet),
115-
"src-subnet": str(self.src_subnet),
116-
"dhgrp": self.dhgrp,
117-
"pfs": self.pfs,
118-
"replay": self.replay,
119-
"keepalive": self.keepalive,
120-
"auto-negotiate": self.auto_negotiate,
121-
"keylifeseconds": self.keylife_seconds,
122-
"keylifekbs": self.keylife_kbs,
123-
"keylife-type": self.keylife_type,
124-
"proposal": self.proposal,
125-
"comments": self.comment,
126-
}
172+
return remove_empty_dict_values(
173+
{
174+
"name": self.name,
175+
"phase1name": self.phase1_name,
176+
"dst-addr-type": self.dst_addr_type,
177+
"dst-subnet": str(self.dst_subnet) if self.dst_subnet else "",
178+
"dst-start-ip": (
179+
str(self.dst_start_ip) if self.dst_start_ip else ""
180+
),
181+
"dst-end-ip": str(self.dst_end_ip) if self.dst_end_ip else "",
182+
"src-addr-type": self.src_addr_type,
183+
"src-subnet": str(self.src_subnet) if self.src_subnet else "",
184+
"src-start-ip": (
185+
str(self.src_start_ip) if self.src_start_ip else ""
186+
),
187+
"src-end-ip": str(self.src_end_ip) if self.src_end_ip else "",
188+
"dhgrp": self.dhgrp,
189+
"pfs": self.pfs,
190+
"replay": self.replay,
191+
"keepalive": self.keepalive,
192+
"auto-negotiate": self.auto_negotiate,
193+
"keylifeseconds": self.keylife_seconds,
194+
"keylifekbs": self.keylife_kbs,
195+
"keylife-type": self.keylife_type,
196+
"proposal": self.proposal,
197+
"comments": self.comment,
198+
}
199+
)
127200

128201
def __repr__(self):
129-
return f"{self.__class__.__name__} {self.name} Phase1 Name: {self.phase1_name} SRC: {self.src_subnet} DST: {self.dst_subnet}"
202+
return f"{self.__class__.__name__} {self.name} Phase1 Name: {self.phase1_name}"

tests/data.py

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2556,5 +2556,47 @@
25562556
"dst-end-ip6": "::",
25572557
"dst-subnet": "192.168.100.0 255.255.255.0",
25582558
"dst-port": 0,
2559-
}
2559+
},
2560+
{
2561+
"name": "vpn_phase2_2",
2562+
"q_origin_key": "vpn_phase2_2",
2563+
"phase1name": "vpn_phase1",
2564+
"dhcp-ipsec": "disable",
2565+
"proposal": "chacha20poly1305 aes256gcm",
2566+
"pfs": "enable",
2567+
"ipv4-df": "disable",
2568+
"dhgrp": "20",
2569+
"replay": "enable",
2570+
"keepalive": "enable",
2571+
"auto-negotiate": "enable",
2572+
"add-route": "phase1",
2573+
"inbound-dscp-copy": "phase1",
2574+
"auto-discovery-sender": "phase1",
2575+
"auto-discovery-forwarder": "phase1",
2576+
"keylifeseconds": 14400,
2577+
"keylifekbs": 5120,
2578+
"keylife-type": "seconds",
2579+
"single-source": "disable",
2580+
"route-overlap": "use-new",
2581+
"encapsulation": "tunnel-mode",
2582+
"l2tp": "disable",
2583+
"comments": "test phase2",
2584+
"initiator-ts-narrow": "disable",
2585+
"diffserv": "disable",
2586+
"diffservcode": "000000",
2587+
"protocol": 0,
2588+
"src-name": "",
2589+
"src-name6": "",
2590+
"src-addr-type": "range",
2591+
"src-start-ip": "10.0.0.10",
2592+
"src-end-ip": "10.0.0.11",
2593+
"src-end-ip6": "::",
2594+
"src-port": 0,
2595+
"dst-name": "",
2596+
"dst-name6": "",
2597+
"dst-addr-type": "ip",
2598+
"dst-start-ip": "192.168.100.10",
2599+
"dst-end-ip6": "::",
2600+
"dst-port": 0,
2601+
},
25602602
]

tests/tests_fortilib_firewall_read/test_fortilib_firewall_phase2_interfaces.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ def test_firewall_base_phase2_interfaces(self):
1616
{
1717
"name": "vpn_phase2",
1818
"phase1name": "vpn_phase1",
19+
"dst-addr-type": "subnet",
1920
"dst-subnet": "192.168.100.0/24",
21+
"src-addr-type": "subnet",
2022
"src-subnet": "10.0.0.0/8",
2123
"dhgrp": "20",
2224
"pfs": "enable",
@@ -39,5 +41,31 @@ def test_firewall_base_phase2_interfaces(self):
3941

4042
self.assertEqual(
4143
str(phase2_interface),
42-
"FortigatePhase2Interface vpn_phase2 Phase1 Name: vpn_phase1 SRC: 10.0.0.0/8 DST: 192.168.100.0/24",
44+
"FortigatePhase2Interface vpn_phase2 Phase1 Name: vpn_phase1",
45+
)
46+
47+
phase2_interface: FortigatePhase1Interface = get_by(
48+
"name", "vpn_phase2_2", self.fw.phase2_interfaces
49+
)
50+
self.assertEqual(
51+
phase2_interface.render(),
52+
{
53+
"name": "vpn_phase2_2",
54+
"phase1name": "vpn_phase1",
55+
"dst-addr-type": "ip",
56+
"dst-start-ip": "192.168.100.10",
57+
"src-addr-type": "range",
58+
"src-start-ip": "10.0.0.10",
59+
"src-end-ip": "10.0.0.11",
60+
"dhgrp": "20",
61+
"pfs": "enable",
62+
"replay": "enable",
63+
"keepalive": "enable",
64+
"auto-negotiate": "enable",
65+
"keylifeseconds": 14400,
66+
"keylifekbs": 5120,
67+
"keylife-type": "seconds",
68+
"proposal": "chacha20poly1305 aes256gcm",
69+
"comments": "test phase2",
70+
},
4371
)

tests/tests_fortilib_firewall_write/test_fortilib_firewall_phase2_interface.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,16 @@ def test_firewall_base_phase2_interface_create(self):
2626
{
2727
"name": "vpn_phase2",
2828
"phase1name": "vpn_phase1",
29+
"dst-addr-type": "subnet",
2930
"dst-subnet": "192.168.100.0/24",
31+
"src-addr-type": "subnet",
3032
"src-subnet": "10.0.0.0/8",
3133
"dhgrp": "20",
3234
"pfs": "enable",
3335
"replay": "enable",
3436
"keepalive": "enable",
3537
"auto-negotiate": "disable",
3638
"keylifeseconds": 14400,
37-
"keylifekbs": None,
3839
"keylife-type": "seconds",
3940
"proposal": "chacha20poly1305 aes256gcm",
4041
"comments": "test phase2",
@@ -57,7 +58,9 @@ def test_firewall_base_phase2_interface_update(self):
5758
{
5859
"name": "vpn_phase2",
5960
"phase1name": "vpn_phase1",
61+
"dst-addr-type": "subnet",
6062
"dst-subnet": "192.168.200.0/24",
63+
"src-addr-type": "subnet",
6164
"src-subnet": "10.0.0.0/8",
6265
"dhgrp": "20",
6366
"pfs": "enable",

0 commit comments

Comments
 (0)