opnsense-api2 0.7.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- opnsense_api/__init__.py +5 -0
- opnsense_api/api/__init__.py +0 -0
- opnsense_api/api/acmeclient.py +71 -0
- opnsense_api/api/acmeclient_accounts_client.py +15 -0
- opnsense_api/api/acmeclient_certificates_client.py +29 -0
- opnsense_api/api/acmeclient_validations_client.py +15 -0
- opnsense_api/api/auth_group_client.py +18 -0
- opnsense_api/api/auth_user_client.py +22 -0
- opnsense_api/api/core_backup_client.py +80 -0
- opnsense_api/api/core_firmware_client.py +34 -0
- opnsense_api/api/firewall_alias_client.py +48 -0
- opnsense_api/api/firewall_category_client.py +20 -0
- opnsense_api/api/firewall_dnat_client.py +141 -0
- opnsense_api/api/firewall_filter_client.py +95 -0
- opnsense_api/api/firewall_one_to_one_client.py +54 -0
- opnsense_api/api/firewall_snat_client.py +40 -0
- opnsense_api/api/haproxy.py +588 -0
- opnsense_api/api/haproxy_authentik.py +1448 -0
- opnsense_api/api/haproxy_export_client.py +9 -0
- opnsense_api/api/haproxy_service_client.py +16 -0
- opnsense_api/api/haproxy_settings_client.py +223 -0
- opnsense_api/api/helper.py +18 -0
- opnsense_api/api/interfaces_overview_client.py +25 -0
- opnsense_api/api/interfaces_vlan_client.py +46 -0
- opnsense_api/api/kea_dhcpv4_client.py +107 -0
- opnsense_api/api/trust.py +29 -0
- opnsense_api/api/trust_ca_client.py +16 -0
- opnsense_api/api/trust_cert_client.py +14 -0
- opnsense_api/api/trust_crl_client.py +17 -0
- opnsense_api/api/unbound_client.py +204 -0
- opnsense_api/authentik.py +401 -0
- opnsense_api/base_client.py +120 -0
- opnsense_api/client.py +44 -0
- opnsense_api/pydantic/Account.py +35 -0
- opnsense_api/pydantic/Acl.py +240 -0
- opnsense_api/pydantic/Action.py +137 -0
- opnsense_api/pydantic/Alias.py +68 -0
- opnsense_api/pydantic/ApiUser.py +8 -0
- opnsense_api/pydantic/Backend.py +141 -0
- opnsense_api/pydantic/BaseObject.py +9 -0
- opnsense_api/pydantic/Ca.py +57 -0
- opnsense_api/pydantic/Category.py +15 -0
- opnsense_api/pydantic/Cert.py +81 -0
- opnsense_api/pydantic/CertBase.py +8 -0
- opnsense_api/pydantic/Certificate.py +45 -0
- opnsense_api/pydantic/ChangelogItem.py +7 -0
- opnsense_api/pydantic/DNATRule.py +53 -0
- opnsense_api/pydantic/ErrorFile.py +5 -0
- opnsense_api/pydantic/Fcgi.py +5 -0
- opnsense_api/pydantic/FirmwareInfo.py +19 -0
- opnsense_api/pydantic/Frontend.py +146 -0
- opnsense_api/pydantic/General.py +21 -0
- opnsense_api/pydantic/Group.py +19 -0
- opnsense_api/pydantic/InterfaceOverview.py +244 -0
- opnsense_api/pydantic/KeaDhcpv4.py +51 -0
- opnsense_api/pydantic/KeaDhcpv4Settings.py +35 -0
- opnsense_api/pydantic/Lua.py +24 -0
- opnsense_api/pydantic/OneToOneRule.py +38 -0
- opnsense_api/pydantic/Package.py +17 -0
- opnsense_api/pydantic/PluginDetail.py +5 -0
- opnsense_api/pydantic/Product.py +28 -0
- opnsense_api/pydantic/ProductCheck.py +25 -0
- opnsense_api/pydantic/Resolver.py +5 -0
- opnsense_api/pydantic/Response.py +5 -0
- opnsense_api/pydantic/Result.py +7 -0
- opnsense_api/pydantic/Rule.py +159 -0
- opnsense_api/pydantic/SNATRule.py +35 -0
- opnsense_api/pydantic/SearchRequest.py +14 -0
- opnsense_api/pydantic/SearchResult.py +90 -0
- opnsense_api/pydantic/Server.py +66 -0
- opnsense_api/pydantic/Status.py +7 -0
- opnsense_api/pydantic/Unbound.py +303 -0
- opnsense_api/pydantic/UpdateStatus.py +31 -0
- opnsense_api/pydantic/User.py +33 -0
- opnsense_api/pydantic/Validation.py +475 -0
- opnsense_api/pydantic/Vlan.py +33 -0
- opnsense_api/pydantic/__init__.py +0 -0
- opnsense_api/pydantic/pydantic_base.py +341 -0
- opnsense_api/pysense.py +195 -0
- opnsense_api/state/__init__.py +31 -0
- opnsense_api/state/handlers/__init__.py +16 -0
- opnsense_api/state/handlers/base_handler.py +259 -0
- opnsense_api/state/handlers/dhcp_reservation_handler.py +182 -0
- opnsense_api/state/handlers/dnat_handler.py +223 -0
- opnsense_api/state/handlers/dns_host_handler.py +166 -0
- opnsense_api/state/handlers/firewall_alias_handler.py +229 -0
- opnsense_api/state/handlers/firewall_rule_handler.py +269 -0
- opnsense_api/state/handlers/interface_handler.py +171 -0
- opnsense_api/state/handlers/one_to_one_nat_handler.py +178 -0
- opnsense_api/state/handlers/snat_handler.py +192 -0
- opnsense_api/state/handlers/vlan_handler.py +170 -0
- opnsense_api/state/state_manager.py +1724 -0
- opnsense_api2-0.7.2.dist-info/METADATA +20 -0
- opnsense_api2-0.7.2.dist-info/RECORD +97 -0
- opnsense_api2-0.7.2.dist-info/WHEEL +5 -0
- opnsense_api2-0.7.2.dist-info/entry_points.txt +2 -0
- opnsense_api2-0.7.2.dist-info/top_level.txt +1 -0
opnsense_api/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import time
|
|
2
|
+
from opnsense_api.api.acmeclient_accounts_client import AcmeclientAccountsClient
|
|
3
|
+
from opnsense_api.api.acmeclient_certificates_client import AcmeclientCertificatesClient
|
|
4
|
+
from opnsense_api.api.acmeclient_validations_client import AcmeclientValidationsClient
|
|
5
|
+
from opnsense_api.pydantic.Certificate import Certificate
|
|
6
|
+
from opnsense_api.pydantic.SearchRequest import SearchRequest
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Acmeclient(AcmeclientCertificatesClient, AcmeclientAccountsClient, AcmeclientValidationsClient):
|
|
10
|
+
|
|
11
|
+
def acmecient(self, domain, account_value, renew_interval=15) -> Certificate:
|
|
12
|
+
certificates = self.acmeclient_certificates_search(SearchRequest.search(domain))
|
|
13
|
+
for certificate in certificates.rows: # type: Certificate
|
|
14
|
+
if certificate.name == domain:
|
|
15
|
+
print("Warning certificate already exists")
|
|
16
|
+
return certificate
|
|
17
|
+
accounts = self.acmeclient_accounts_search()
|
|
18
|
+
account_uuid = None
|
|
19
|
+
if len(accounts.rows) == 1:
|
|
20
|
+
account_uuid = accounts.rows[0].uuid
|
|
21
|
+
for account in accounts.rows:
|
|
22
|
+
if account.name == account_value or account.uuid == account_value:
|
|
23
|
+
account_uuid = account.uuid
|
|
24
|
+
if account_uuid is None:
|
|
25
|
+
raise Exception("Account not found")
|
|
26
|
+
|
|
27
|
+
validations = self.acmeclient_validations_search()
|
|
28
|
+
validations_uuid = None
|
|
29
|
+
if len(validations.rows) == 1:
|
|
30
|
+
validations_uuid = validations.rows[0].uuid
|
|
31
|
+
if validations_uuid is None:
|
|
32
|
+
raise Exception("Validation not found")
|
|
33
|
+
|
|
34
|
+
item = Certificate(
|
|
35
|
+
enabled=True,
|
|
36
|
+
name=domain,
|
|
37
|
+
account=str(account_uuid),
|
|
38
|
+
validationMethod=str(validations_uuid),
|
|
39
|
+
keyLength=Certificate.CertificatesCertificateKeylengthEnum.KEY_4096,
|
|
40
|
+
ocsp=False,
|
|
41
|
+
autoRenewal=True,
|
|
42
|
+
renewInterval=renew_interval,
|
|
43
|
+
aliasmode=Certificate.CertificatesCertificateAliasmodeEnum.NONE
|
|
44
|
+
)
|
|
45
|
+
cert = self.acmeclient_certificates_add(item)
|
|
46
|
+
self.acmeclient_certificates_sign(cert.uuid)
|
|
47
|
+
return self.acmeclient_certificates_get(cert.uuid)
|
|
48
|
+
|
|
49
|
+
def acmeclient_wait_cert_issued(self, domain, timeout=10, interval_sleep=1) -> Certificate | None:
|
|
50
|
+
"""
|
|
51
|
+
The certRefId will be set after the certificate is issued. This can take a undefined amount of time.
|
|
52
|
+
(acme server rate limits for example). Only after the certificate is issued it can be connected to a service
|
|
53
|
+
|
|
54
|
+
:param interval_sleep: time to sleep in between attempts
|
|
55
|
+
:param timeout: time in seconds to wait for the certificate to be issued
|
|
56
|
+
:param domain:
|
|
57
|
+
:return: None on timeout else CertificateResponse
|
|
58
|
+
"""
|
|
59
|
+
start = time.time()
|
|
60
|
+
|
|
61
|
+
while time.time() - start < timeout:
|
|
62
|
+
certificates = self.acmeclient_certificates_search(domain)
|
|
63
|
+
for cert in certificates.rows:
|
|
64
|
+
if cert.name == domain:
|
|
65
|
+
if cert.certRefId is not None:
|
|
66
|
+
return cert
|
|
67
|
+
time.sleep(interval_sleep)
|
|
68
|
+
else:
|
|
69
|
+
print("Error: Unable to assign certificate due to certificate not issued in time")
|
|
70
|
+
return None
|
|
71
|
+
return certificate
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from opnsense_api.base_client import BaseClient
|
|
2
|
+
from opnsense_api.pydantic.Account import Account
|
|
3
|
+
from opnsense_api.pydantic.SearchRequest import SearchRequest
|
|
4
|
+
from opnsense_api.pydantic.SearchResult import AccountSearchResult
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AcmeclientAccountsClient(BaseClient):
|
|
8
|
+
|
|
9
|
+
def acmeclient_accounts_search(self, search: SearchRequest = None) -> AccountSearchResult:
|
|
10
|
+
s = search
|
|
11
|
+
if s is not None:
|
|
12
|
+
s = s.__dict__
|
|
13
|
+
data = self._post('acmeclient/accounts/search', s)
|
|
14
|
+
# print(data)
|
|
15
|
+
return AccountSearchResult.from_basic_dict(data, Account)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from opnsense_api.base_client import BaseClient
|
|
2
|
+
from opnsense_api.pydantic.Certificate import Certificate
|
|
3
|
+
from opnsense_api.pydantic.Response import Response
|
|
4
|
+
from opnsense_api.pydantic.Result import Result
|
|
5
|
+
from opnsense_api.pydantic.SearchRequest import SearchRequest
|
|
6
|
+
from opnsense_api.pydantic.SearchResult import BaseObjectSearchResult, CertificateSearchResult
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class AcmeclientCertificatesClient(BaseClient):
|
|
10
|
+
|
|
11
|
+
def acmeclient_certificates_get(self, uuid = None) -> Certificate:
|
|
12
|
+
data = self._get('acmeclient/certificates/get' + self._get_arg_formatter(uuid))
|
|
13
|
+
# print(data)
|
|
14
|
+
return Certificate.from_ui_dict(data)
|
|
15
|
+
|
|
16
|
+
def acmeclient_certificates_search(self, search: SearchRequest = None) -> CertificateSearchResult:
|
|
17
|
+
data = self._post('acmeclient/certificates/search', search)
|
|
18
|
+
# print(data)
|
|
19
|
+
return CertificateSearchResult.from_basic_dict(data, Certificate)
|
|
20
|
+
|
|
21
|
+
def acmeclient_certificates_add(self, item: Certificate):
|
|
22
|
+
data = self._post('acmeclient/certificates/add', item)
|
|
23
|
+
# print(data)
|
|
24
|
+
return Result.from_ui_dict(data)
|
|
25
|
+
|
|
26
|
+
def acmeclient_certificates_sign(self, uuid = None):
|
|
27
|
+
data = self._post('acmeclient/certificates/sign' + self._get_arg_formatter(uuid), '')
|
|
28
|
+
# print(data)
|
|
29
|
+
return Response.from_basic_dict(data)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from opnsense_api.base_client import BaseClient
|
|
2
|
+
from opnsense_api.pydantic.SearchRequest import SearchRequest
|
|
3
|
+
from opnsense_api.pydantic.SearchResult import BaseObjectSearchResult, ValidationSearchResult
|
|
4
|
+
from opnsense_api.pydantic.Validation import Validation
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AcmeclientValidationsClient(BaseClient):
|
|
8
|
+
|
|
9
|
+
def acmeclient_validations_search(self, search: SearchRequest = None) -> ValidationSearchResult:
|
|
10
|
+
s = search
|
|
11
|
+
if s is not None:
|
|
12
|
+
s = s.__dict__
|
|
13
|
+
data = self._post('acmeclient/validations/search', s)
|
|
14
|
+
# print(data)
|
|
15
|
+
return ValidationSearchResult.from_basic_dict(data, Validation)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from opnsense_api.base_client import BaseClient
|
|
2
|
+
from opnsense_api.pydantic.Group import Group
|
|
3
|
+
from opnsense_api.pydantic.SearchRequest import SearchRequest
|
|
4
|
+
from opnsense_api.pydantic.SearchResult import GroupSearchResult
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AuthGroup(BaseClient):
|
|
8
|
+
|
|
9
|
+
def auth_group_search(self, search: SearchRequest = None) -> GroupSearchResult:
|
|
10
|
+
if search is not None:
|
|
11
|
+
data = self._post('auth/group/search', search.__dict__)
|
|
12
|
+
else:
|
|
13
|
+
data = self._get('auth/group/search')
|
|
14
|
+
return GroupSearchResult.from_ui_dict(data, Group)
|
|
15
|
+
|
|
16
|
+
def auth_group_get(self, uuid: str = None) -> Group:
|
|
17
|
+
data = self._get('auth/group/get' + self._get_arg_formatter(uuid))
|
|
18
|
+
return Group.from_ui_dict(data)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from opnsense_api.base_client import BaseClient
|
|
2
|
+
from opnsense_api.pydantic.ApiUser import ApiUser
|
|
3
|
+
from opnsense_api.pydantic.SearchRequest import SearchRequest
|
|
4
|
+
from opnsense_api.pydantic.SearchResult import ApiUserSearchResult, UserSearchResult
|
|
5
|
+
from opnsense_api.pydantic.User import User
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AuthUser(BaseClient):
|
|
9
|
+
|
|
10
|
+
def auth_user_search_api_key(self, search: SearchRequest = None) -> ApiUserSearchResult:
|
|
11
|
+
if search is not None:
|
|
12
|
+
data = self._post('auth/user/search_api_key', search.__dict__)
|
|
13
|
+
else:
|
|
14
|
+
data = self._get('auth/user/search_api_key')
|
|
15
|
+
return ApiUserSearchResult.from_ui_dict(data, ApiUser)
|
|
16
|
+
|
|
17
|
+
def auth_user_search(self, search: SearchRequest = None) -> UserSearchResult:
|
|
18
|
+
if search is not None:
|
|
19
|
+
data = self._post('auth/user/search', search.__dict__)
|
|
20
|
+
else:
|
|
21
|
+
data = self._get('auth/user/search')
|
|
22
|
+
return UserSearchResult.from_ui_dict(data, User)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
from typing import List, Optional
|
|
2
|
+
|
|
3
|
+
from opnsense_api.base_client import BaseClient
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CoreBackupClient(BaseClient):
|
|
7
|
+
|
|
8
|
+
def core_backup_backups(self, host: str = 'this') -> dict:
|
|
9
|
+
"""List available backups for a host."""
|
|
10
|
+
return self._get(f'core/backup/backups/{host}')
|
|
11
|
+
|
|
12
|
+
def core_backup_providers(self) -> dict:
|
|
13
|
+
"""List available backup providers."""
|
|
14
|
+
return self._get('core/backup/providers')
|
|
15
|
+
|
|
16
|
+
def core_backup_download(self, host: str = 'this', backup: str = None) -> str:
|
|
17
|
+
"""Download a backup config as XML string.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
host: Backup provider host (default 'this' for local).
|
|
21
|
+
backup: Specific backup name/timestamp. If None, downloads current config.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
XML config content as string.
|
|
25
|
+
"""
|
|
26
|
+
endpoint = f'core/backup/download/{host}'
|
|
27
|
+
if backup:
|
|
28
|
+
endpoint += f'/{backup}'
|
|
29
|
+
return self._get(endpoint, raw=True)
|
|
30
|
+
|
|
31
|
+
def core_backup_download_to_file(self, path: str, host: str = 'this', backup: str = None) -> str:
|
|
32
|
+
"""Download a backup config and save it to a local file.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
path: Local file path to save the config to.
|
|
36
|
+
host: Backup provider host (default 'this' for local).
|
|
37
|
+
backup: Specific backup name/timestamp. If None, downloads current config.
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
The file path written to.
|
|
41
|
+
"""
|
|
42
|
+
content = self.core_backup_download(host=host, backup=backup)
|
|
43
|
+
with open(path, 'w') as f:
|
|
44
|
+
f.write(content)
|
|
45
|
+
return path
|
|
46
|
+
|
|
47
|
+
def core_backup_diff(self, host: str, backup1: str, backup2: str) -> str:
|
|
48
|
+
"""Get diff between two backups.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
host: Backup provider host.
|
|
52
|
+
backup1: First backup identifier.
|
|
53
|
+
backup2: Second backup identifier.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
Diff output as string.
|
|
57
|
+
"""
|
|
58
|
+
return self._get(f'core/backup/diff/{host}/{backup1}/{backup2}', raw=True)
|
|
59
|
+
|
|
60
|
+
def core_backup_delete(self, backup: str) -> dict:
|
|
61
|
+
"""Delete a backup.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
backup: Backup identifier to delete.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
API response dict.
|
|
68
|
+
"""
|
|
69
|
+
return self._post('core/backup/delete_backup', {'backup': backup})
|
|
70
|
+
|
|
71
|
+
def core_backup_revert(self, backup: str) -> dict:
|
|
72
|
+
"""Revert to a specific backup.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
backup: Backup identifier to revert to.
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
API response dict.
|
|
79
|
+
"""
|
|
80
|
+
return self._post('core/backup/revert_backup', {'backup': backup})
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import time
|
|
2
|
+
|
|
3
|
+
from opnsense_api.base_client import BaseClient
|
|
4
|
+
from opnsense_api.pydantic.FirmwareInfo import FirmwareInfo
|
|
5
|
+
from opnsense_api.pydantic.PluginDetail import PluginDetail
|
|
6
|
+
from opnsense_api.pydantic.Status import Status
|
|
7
|
+
from opnsense_api.pydantic.UpdateStatus import UpdateStatus
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class CoreFirmwareClient(BaseClient):
|
|
11
|
+
|
|
12
|
+
def core_firmware_status(self) -> UpdateStatus:
|
|
13
|
+
data = self._get('core/firmware/status')
|
|
14
|
+
return UpdateStatus(**data)
|
|
15
|
+
|
|
16
|
+
def core_firmware_info(self) -> FirmwareInfo:
|
|
17
|
+
data = self._get('core/firmware/info')
|
|
18
|
+
return FirmwareInfo(**data)
|
|
19
|
+
|
|
20
|
+
def core_firmware_install(self, package_name) -> Status:
|
|
21
|
+
data = self._post('core/firmware/install/'+package_name, '')
|
|
22
|
+
return Status(**data)
|
|
23
|
+
|
|
24
|
+
def core_firmware_detail(self, package_name) -> PluginDetail:
|
|
25
|
+
data = self._post('core/firmware/details/'+package_name, '')
|
|
26
|
+
return PluginDetail(**data)
|
|
27
|
+
|
|
28
|
+
def package_install_wait(self, package_name):
|
|
29
|
+
self.core_firmware_install(package_name)
|
|
30
|
+
info = self.core_firmware_info()
|
|
31
|
+
installed = info.is_installed(package_name)
|
|
32
|
+
while not installed:
|
|
33
|
+
time.sleep(5)
|
|
34
|
+
installed = info.is_installed(package_name)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from opnsense_api.base_client import BaseClient
|
|
2
|
+
from opnsense_api.pydantic.Alias import Alias
|
|
3
|
+
from opnsense_api.pydantic.Result import Result
|
|
4
|
+
from opnsense_api.pydantic.SearchResult import AliasSearchResult
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class FirewallAliasClient(BaseClient):
|
|
8
|
+
|
|
9
|
+
def firewall_alias_add_item(self, alias: Alias) -> Result:
|
|
10
|
+
data = self._post('firewall/alias/add_item', alias)
|
|
11
|
+
return Result(**data)
|
|
12
|
+
|
|
13
|
+
def firewall_alias_set_item(self, uuid: str, alias: Alias) -> Result:
|
|
14
|
+
data = self._post(f'firewall/alias/set_item/{uuid}', alias)
|
|
15
|
+
return Result(**data)
|
|
16
|
+
|
|
17
|
+
def firewall_alias_del_item(self, uuid: str) -> Result:
|
|
18
|
+
data = self._post(f'firewall/alias/del_item/{uuid}', '')
|
|
19
|
+
return Result(**data)
|
|
20
|
+
|
|
21
|
+
def firewall_alias_toggle_item(self, uuid: str, enabled: bool = None) -> Result:
|
|
22
|
+
endpoint = f'firewall/alias/toggle_item/{uuid}'
|
|
23
|
+
if enabled is not None:
|
|
24
|
+
endpoint += f'/{1 if enabled else 0}'
|
|
25
|
+
data = self._post(endpoint, '')
|
|
26
|
+
return Result(**data)
|
|
27
|
+
|
|
28
|
+
def firewall_alias_reconfigure(self) -> Result:
|
|
29
|
+
data = self._post('firewall/alias/reconfigure', '')
|
|
30
|
+
return Result(**data)
|
|
31
|
+
|
|
32
|
+
def firewall_alias_search_item(self) -> AliasSearchResult:
|
|
33
|
+
data = self._get('firewall/alias/search_item')
|
|
34
|
+
return AliasSearchResult.from_ui_dict(data, Alias)
|
|
35
|
+
|
|
36
|
+
def firewall_alias_get_item(self, uuid=None) -> Alias:
|
|
37
|
+
data = self._get('firewall/alias/get_item' + self._get_arg_formatter(uuid))
|
|
38
|
+
# print(data)
|
|
39
|
+
return Alias.from_ui_dict(data)
|
|
40
|
+
|
|
41
|
+
def firewall_alias_get(self) -> Alias:
|
|
42
|
+
"""
|
|
43
|
+
@todo
|
|
44
|
+
{"alias":{"geoip":{"url":""},"aliases":{"alias":{"3d6a0b47-4d09-41fb-a38a-bd0bf321dbb4":{"enabled":"1","name":"test","type":{"host":{"value":"Host(s)","selected":1},"network":{"value":"Network(s)","selected":0},"port":{"value":"Port(s)","selected":0},"url":{"value":"URL (IPs)","selected":0},"urltable":{"value":"URL Table (IPs)","selected":0},"urljson":{"value":"URL Table in JSON format (IPs)","selected":0},"geoip":{"value":"GeoIP","selected":0},"networkgroup":{"value":"Network group","selected":0},"mac":{"value":"MAC address","selected":0},"asn":{"value":"BGP ASN","selected":0},"dynipv6host":{"value":"Dynamic IPv6 Host","selected":0},"authgroup":{"value":"OpenVPN group","selected":0},"internal":{"value":"Internal (automatic)","selected":0},"external":{"value":"External (advanced)","selected":0}},"path_expression":"","proto":{"IPv4":{"value":"IPv4","selected":0},"IPv6":{"value":"IPv6","selected":0}},"interface":{"":{"value":"None","selected":1},"lan":{"value":"LAN","selected":0},"wan":{"value":"WAN","selected":0}},"counters":"0","updatefreq":"","content":{"127.0.0.1":{"value":"127.0.0.1","selected":1}},"password":"","username":"","authtype":{"":{"value":"None","selected":1},"Basic":{"value":"Basic","selected":0},"Bearer":{"value":"Bearer","selected":0},"Header":{"value":"Header","selected":0}},"expire":"","categories":{"b7e1b92a-764f-4024-a9c4-4caa2f0a5c4f":{"value":"Hosts","selected":0},"1fc36d4a-aa9e-4167-b1c9-3fe52b3d921b":{"value":"Routers","selected":0},"953f55fa-387b-4814-9c6e-2cfdd1af0869":{"value":"Switches","selected":0}},"current_items":"1","last_updated":"2025-12-09T14:24:53.729370","eval_nomatch":"0","eval_match":"0","in_block_p":"0","in_block_b":"0","in_pass_p":"0","in_pass_b":"0","out_block_p":"0","out_block_b":"0","out_pass_p":"0","out_pass_b":"0","description":""},"__wan_network":{"enabled":"1","name":"__wan_network","type":{"host":{"value":"Host(s)","selected":0},"network":{"value":"Network(s)","selected":0},"port":{"value":"Port(s)","selected":0},"url":{"value":"URL (IPs)","selected":0},"urltable":{"value":"URL Table (IPs)","selected":0},"urljson":{"value":"URL Table in JSON format (IPs)","selected":0},"geoip":{"value":"GeoIP","selected":0},"networkgroup":{"value":"Network group","selected":0},"mac":{"value":"MAC address","selected":0},"asn":{"value":"BGP ASN","selected":0},"dynipv6host":{"value":"Dynamic IPv6 Host","selected":0},"authgroup":{"value":"OpenVPN group","selected":0},"internal":{"value":"Internal (automatic)","selected":1},"external":{"value":"External (advanced)","selected":0}},"path_expression":"","proto":{"IPv4":{"value":"IPv4","selected":0},"IPv6":{"value":"IPv6","selected":0}},"interface":{"":{"value":"None","selected":1},"lan":{"value":"LAN","selected":0},"wan":{"value":"WAN","selected":0}},"counters":"","updatefreq":"","content":{"":{"value":"","selected":1}},"password":"","username":"","authtype":{"":{"value":"None","selected":1},"Basic":{"value":"Basic","selected":0},"Bearer":{"value":"Bearer","selected":0},"Header":{"value":"Header","selected":0}},"expire":"","categories":{"b7e1b92a-764f-4024-a9c4-4caa2f0a5c4f":{"value":"Hosts","selected":0},"1fc36d4a-aa9e-4167-b1c9-3fe52b3d921b":{"value":"Routers","selected":0},"953f55fa-387b-4814-9c6e-2cfdd1af0869":{"value":"Switches","selected":0}},"current_items":"1","last_updated":"","eval_nomatch":"0","eval_match":"0","in_block_p":"0","in_block_b":"0","in_pass_p":"0","in_pass_b":"0","out_block_p":"0","out_block_b":"0","out_pass_p":"0","out_pass_b":"0","description":"wan net"},"__lan_network":{"enabled":"1","name":"__lan_network","type":{"host":{"value":"Host(s)","selected":0},"network":{"value":"Network(s)","selected":0},"port":{"value":"Port(s)","selected":0},"url":{"value":"URL (IPs)","selected":0},"urltable":{"value":"URL Table (IPs)","selected":0},"urljson":{"value":"URL Table in JSON format (IPs)","selected":0},"geoip":{"value":"GeoIP","selected":0},"networkgroup":{"value":"Network group","selected":0},"mac":{"value":"MAC address","selected":0},"asn":{"value":"BGP ASN","selected":0},"dynipv6host":{"value":"Dynamic IPv6 Host","selected":0},"authgroup":{"value":"OpenVPN group","selected":0},"internal":{"value":"Internal (automatic)","selected":1},"external":{"value":"External (advanced)","selected":0}},"path_expression":"","proto":{"IPv4":{"value":"IPv4","selected":0},"IPv6":{"value":"IPv6","selected":0}},"interface":{"":{"value":"None","selected":1},"lan":{"value":"LAN","selected":0},"wan":{"value":"WAN","selected":0}},"counters":"","updatefreq":"","content":{"":{"value":"","selected":1}},"password":"","username":"","authtype":{"":{"value":"None","selected":1},"Basic":{"value":"Basic","selected":0},"Bearer":{"value":"Bearer","selected":0},"Header":{"value":"Header","selected":0}},"expire":"","categories":{"b7e1b92a-764f-4024-a9c4-4caa2f0a5c4f":{"value":"Hosts","selected":0},"1fc36d4a-aa9e-4167-b1c9-3fe52b3d921b":{"value":"Routers","selected":0},"953f55fa-387b-4814-9c6e-2cfdd1af0869":{"value":"Switches","selected":0}},"current_items":"1","last_updated":"","eval_nomatch":"0","eval_match":"0","in_block_p":"0","in_block_b":"0","in_pass_p":"0","in_pass_b":"0","out_block_p":"0","out_block_b":"0","out_pass_p":"0","out_pass_b":"0","description":"lan net"},"__lo0_network":{"enabled":"1","name":"__lo0_network","type":{"host":{"value":"Host(s)","selected":0},"network":{"value":"Network(s)","selected":0},"port":{"value":"Port(s)","selected":0},"url":{"value":"URL (IPs)","selected":0},"urltable":{"value":"URL Table (IPs)","selected":0},"urljson":{"value":"URL Table in JSON format (IPs)","selected":0},"geoip":{"value":"GeoIP","selected":0},"networkgroup":{"value":"Network group","selected":0},"mac":{"value":"MAC address","selected":0},"asn":{"value":"BGP ASN","selected":0},"dynipv6host":{"value":"Dynamic IPv6 Host","selected":0},"authgroup":{"value":"OpenVPN group","selected":0},"internal":{"value":"Internal (automatic)","selected":1},"external":{"value":"External (advanced)","selected":0}},"path_expression":"","proto":{"IPv4":{"value":"IPv4","selected":0},"IPv6":{"value":"IPv6","selected":0}},"interface":{"":{"value":"None","selected":1},"lan":{"value":"LAN","selected":0},"wan":{"value":"WAN","selected":0}},"counters":"","updatefreq":"","content":{"":{"value":"","selected":1}},"password":"","username":"","authtype":{"":{"value":"None","selected":1},"Basic":{"value":"Basic","selected":0},"Bearer":{"value":"Bearer","selected":0},"Header":{"value":"Header","selected":0}},"expire":"","categories":{"b7e1b92a-764f-4024-a9c4-4caa2f0a5c4f":{"value":"Hosts","selected":0},"1fc36d4a-aa9e-4167-b1c9-3fe52b3d921b":{"value":"Routers","selected":0},"953f55fa-387b-4814-9c6e-2cfdd1af0869":{"value":"Switches","selected":0}},"current_items":"2","last_updated":"","eval_nomatch":"0","eval_match":"0","in_block_p":"0","in_block_b":"0","in_pass_p":"0","in_pass_b":"0","out_block_p":"0","out_block_b":"0","out_pass_p":"0","out_pass_b":"0","description":"Loopback net"},"bogons":{"enabled":"1","name":"bogons","type":{"host":{"value":"Host(s)","selected":0},"network":{"value":"Network(s)","selected":0},"port":{"value":"Port(s)","selected":0},"url":{"value":"URL (IPs)","selected":0},"urltable":{"value":"URL Table (IPs)","selected":0},"urljson":{"value":"URL Table in JSON format (IPs)","selected":0},"geoip":{"value":"GeoIP","selected":0},"networkgroup":{"value":"Network group","selected":0},"mac":{"value":"MAC address","selected":0},"asn":{"value":"BGP ASN","selected":0},"dynipv6host":{"value":"Dynamic IPv6 Host","selected":0},"authgroup":{"value":"OpenVPN group","selected":0},"internal":{"value":"Internal (automatic)","selected":0},"external":{"value":"External (advanced)","selected":1}},"path_expression":"","proto":{"IPv4":{"value":"IPv4","selected":0},"IPv6":{"value":"IPv6","selected":0}},"interface":{"":{"value":"None","selected":1},"lan":{"value":"LAN","selected":0},"wan":{"value":"WAN","selected":0}},"counters":"","updatefreq":"","content":{"":{"value":"","selected":1}},"password":"","username":"","authtype":{"":{"value":"None","selected":1},"Basic":{"value":"Basic","selected":0},"Bearer":{"value":"Bearer","selected":0},"Header":{"value":"Header","selected":0}},"expire":"","categories":{"b7e1b92a-764f-4024-a9c4-4caa2f0a5c4f":{"value":"Hosts","selected":0},"1fc36d4a-aa9e-4167-b1c9-3fe52b3d921b":{"value":"Routers","selected":0},"953f55fa-387b-4814-9c6e-2cfdd1af0869":{"value":"Switches","selected":0}},"current_items":"2868","last_updated":"","eval_nomatch":"0","eval_match":"0","in_block_p":"0","in_block_b":"0","in_pass_p":"0","in_pass_b":"0","out_block_p":"0","out_block_b":"0","out_pass_p":"0","out_pass_b":"0","description":"bogon networks (internal)"},"bogonsv6":{"enabled":"1","name":"bogonsv6","type":{"host":{"value":"Host(s)","selected":0},"network":{"value":"Network(s)","selected":0},"port":{"value":"Port(s)","selected":0},"url":{"value":"URL (IPs)","selected":0},"urltable":{"value":"URL Table (IPs)","selected":0},"urljson":{"value":"URL Table in JSON format (IPs)","selected":0},"geoip":{"value":"GeoIP","selected":0},"networkgroup":{"value":"Network group","selected":0},"mac":{"value":"MAC address","selected":0},"asn":{"value":"BGP ASN","selected":0},"dynipv6host":{"value":"Dynamic IPv6 Host","selected":0},"authgroup":{"value":"OpenVPN group","selected":0},"internal":{"value":"Internal (automatic)","selected":0},"external":{"value":"External (advanced)","selected":1}},"path_expression":"","proto":{"IPv4":{"value":"IPv4","selected":0},"IPv6":{"value":"IPv6","selected":0}},"interface":{"":{"value":"None","selected":1},"lan":{"value":"LAN","selected":0},"wan":{"value":"WAN","selected":0}},"counters":"","updatefreq":"","content":{"":{"value":"","selected":1}},"password":"","username":"","authtype":{"":{"value":"None","selected":1},"Basic":{"value":"Basic","selected":0},"Bearer":{"value":"Bearer","selected":0},"Header":{"value":"Header","selected":0}},"expire":"","categories":{"b7e1b92a-764f-4024-a9c4-4caa2f0a5c4f":{"value":"Hosts","selected":0},"1fc36d4a-aa9e-4167-b1c9-3fe52b3d921b":{"value":"Routers","selected":0},"953f55fa-387b-4814-9c6e-2cfdd1af0869":{"value":"Switches","selected":0}},"current_items":"0","last_updated":"","eval_nomatch":"0","eval_match":"0","in_block_p":"0","in_block_b":"0","in_pass_p":"0","in_pass_b":"0","out_block_p":"0","out_block_b":"0","out_pass_p":"0","out_pass_b":"0","description":"bogon networks IPv6 (internal)"},"virusprot":{"enabled":"1","name":"virusprot","type":{"host":{"value":"Host(s)","selected":0},"network":{"value":"Network(s)","selected":0},"port":{"value":"Port(s)","selected":0},"url":{"value":"URL (IPs)","selected":0},"urltable":{"value":"URL Table (IPs)","selected":0},"urljson":{"value":"URL Table in JSON format (IPs)","selected":0},"geoip":{"value":"GeoIP","selected":0},"networkgroup":{"value":"Network group","selected":0},"mac":{"value":"MAC address","selected":0},"asn":{"value":"BGP ASN","selected":0},"dynipv6host":{"value":"Dynamic IPv6 Host","selected":0},"authgroup":{"value":"OpenVPN group","selected":0},"internal":{"value":"Internal (automatic)","selected":0},"external":{"value":"External (advanced)","selected":1}},"path_expression":"","proto":{"IPv4":{"value":"IPv4","selected":0},"IPv6":{"value":"IPv6","selected":0}},"interface":{"":{"value":"None","selected":1},"lan":{"value":"LAN","selected":0},"wan":{"value":"WAN","selected":0}},"counters":"","updatefreq":"","content":{"":{"value":"","selected":1}},"password":"","username":"","authtype":{"":{"value":"None","selected":1},"Basic":{"value":"Basic","selected":0},"Bearer":{"value":"Bearer","selected":0},"Header":{"value":"Header","selected":0}},"expire":"3600","categories":{"b7e1b92a-764f-4024-a9c4-4caa2f0a5c4f":{"value":"Hosts","selected":0},"1fc36d4a-aa9e-4167-b1c9-3fe52b3d921b":{"value":"Routers","selected":0},"953f55fa-387b-4814-9c6e-2cfdd1af0869":{"value":"Switches","selected":0}},"current_items":"0","last_updated":"","eval_nomatch":"4562325","eval_match":"0","in_block_p":"0","in_block_b":"0","in_pass_p":"0","in_pass_b":"0","out_block_p":"0","out_block_b":"0","out_pass_p":"0","out_pass_b":"0","description":"overload table for rate limiting (internal)"},"sshlockout":{"enabled":"1","name":"sshlockout","type":{"host":{"value":"Host(s)","selected":0},"network":{"value":"Network(s)","selected":0},"port":{"value":"Port(s)","selected":0},"url":{"value":"URL (IPs)","selected":0},"urltable":{"value":"URL Table (IPs)","selected":0},"urljson":{"value":"URL Table in JSON format (IPs)","selected":0},"geoip":{"value":"GeoIP","selected":0},"networkgroup":{"value":"Network group","selected":0},"mac":{"value":"MAC address","selected":0},"asn":{"value":"BGP ASN","selected":0},"dynipv6host":{"value":"Dynamic IPv6 Host","selected":0},"authgroup":{"value":"OpenVPN group","selected":0},"internal":{"value":"Internal (automatic)","selected":0},"external":{"value":"External (advanced)","selected":1}},"path_expression":"","proto":{"IPv4":{"value":"IPv4","selected":0},"IPv6":{"value":"IPv6","selected":0}},"interface":{"":{"value":"None","selected":1},"lan":{"value":"LAN","selected":0},"wan":{"value":"WAN","selected":0}},"counters":"","updatefreq":"","content":{"":{"value":"","selected":1}},"password":"","username":"","authtype":{"":{"value":"None","selected":1},"Basic":{"value":"Basic","selected":0},"Bearer":{"value":"Bearer","selected":0},"Header":{"value":"Header","selected":0}},"expire":"3600","categories":{"b7e1b92a-764f-4024-a9c4-4caa2f0a5c4f":{"value":"Hosts","selected":0},"1fc36d4a-aa9e-4167-b1c9-3fe52b3d921b":{"value":"Routers","selected":0},"953f55fa-387b-4814-9c6e-2cfdd1af0869":{"value":"Switches","selected":0}},"current_items":"0","last_updated":"","eval_nomatch":"1938974","eval_match":"0","in_block_p":"0","in_block_b":"0","in_pass_p":"0","in_pass_b":"0","out_block_p":"0","out_block_b":"0","out_pass_p":"0","out_pass_b":"0","description":"abuse lockout table (internal)"}}}}}
|
|
45
|
+
"""
|
|
46
|
+
data = self._get('firewall/alias/get')
|
|
47
|
+
# print(data)
|
|
48
|
+
return Alias.from_ui_dict(data)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from opnsense_api.base_client import BaseClient
|
|
2
|
+
from opnsense_api.pydantic.Category import Category
|
|
3
|
+
from opnsense_api.pydantic.Result import Result
|
|
4
|
+
from opnsense_api.pydantic.SearchResult import CategorySearchResult
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class FirewallCategoryClient(BaseClient):
|
|
8
|
+
|
|
9
|
+
def firewall_category_add_item(self, category: Category) -> Result:
|
|
10
|
+
data = self._post('firewall/category/add_item', category)
|
|
11
|
+
return Result(**data)
|
|
12
|
+
|
|
13
|
+
def firewall_category_search_item(self) -> CategorySearchResult:
|
|
14
|
+
data = self._get('firewall/category/search_item')
|
|
15
|
+
return CategorySearchResult.from_ui_dict(data, Category)
|
|
16
|
+
|
|
17
|
+
def firewall_category_get(self, uuid=None) -> Category:
|
|
18
|
+
data = self._get('firewall/category/get_item' + self._get_arg_formatter(uuid))
|
|
19
|
+
# print(data)
|
|
20
|
+
return Category.from_ui_dict(data)
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
from opnsense_api.base_client import BaseClient
|
|
2
|
+
from opnsense_api.pydantic.DNATRule import Rule as DNATRule
|
|
3
|
+
from opnsense_api.pydantic.Result import Result
|
|
4
|
+
from opnsense_api.pydantic.SearchRequest import SearchRequest
|
|
5
|
+
from opnsense_api.pydantic.SearchResult import DNATRuleSearchResult
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class FirewallDnatClient(BaseClient):
|
|
9
|
+
"""Client for OPNsense DNAT (Destination NAT / Port Forward) API."""
|
|
10
|
+
|
|
11
|
+
def firewall_dnat_search_rule(self, search: SearchRequest = None) -> DNATRuleSearchResult:
|
|
12
|
+
"""
|
|
13
|
+
Search DNAT (port forward) rules.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
search: Optional search parameters
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
DNATRuleSearchResult with matching rules
|
|
20
|
+
"""
|
|
21
|
+
data = self._search('firewall/d_nat/searchRule', search)
|
|
22
|
+
return DNATRuleSearchResult.from_ui_dict(data, DNATRule)
|
|
23
|
+
|
|
24
|
+
def firewall_dnat_get_rule(self, uuid: str = None) -> DNATRule:
|
|
25
|
+
"""
|
|
26
|
+
Get a specific DNAT rule by UUID.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
uuid: UUID of the rule (optional, returns template if None)
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
DNATRule entity
|
|
33
|
+
"""
|
|
34
|
+
data = self._get('firewall/d_nat/getRule' + self._get_arg_formatter(uuid))
|
|
35
|
+
return DNATRule.from_ui_dict(data)
|
|
36
|
+
|
|
37
|
+
def firewall_dnat_add_rule(self, rule: DNATRule) -> Result:
|
|
38
|
+
"""
|
|
39
|
+
Add a new DNAT (port forward) rule.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
rule: DNATRule entity to create
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
Result with UUID of created rule
|
|
46
|
+
"""
|
|
47
|
+
data = self._post('firewall/d_nat/addRule', rule)
|
|
48
|
+
return Result(**data)
|
|
49
|
+
|
|
50
|
+
def firewall_dnat_set_rule(self, uuid: str, rule: DNATRule) -> Result:
|
|
51
|
+
"""
|
|
52
|
+
Update an existing DNAT rule.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
uuid: UUID of the rule to update
|
|
56
|
+
rule: Updated DNATRule entity
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
Result object
|
|
60
|
+
"""
|
|
61
|
+
data = self._post(f'firewall/d_nat/setRule/{uuid}', rule)
|
|
62
|
+
return Result(**data)
|
|
63
|
+
|
|
64
|
+
def firewall_dnat_del_rule(self, uuid: str) -> Result:
|
|
65
|
+
"""
|
|
66
|
+
Delete a DNAT rule.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
uuid: UUID of the rule to delete
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
Result object
|
|
73
|
+
"""
|
|
74
|
+
data = self._post(f'firewall/d_nat/delRule/{uuid}', '')
|
|
75
|
+
return Result(**data)
|
|
76
|
+
|
|
77
|
+
def firewall_dnat_toggle_rule(self, uuid: str, disabled: bool = None) -> Result:
|
|
78
|
+
"""
|
|
79
|
+
Toggle a DNAT rule's enabled state.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
uuid: UUID of the rule to toggle
|
|
83
|
+
disabled: Optional explicit state (True=disabled, False=enabled)
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
Result object
|
|
87
|
+
"""
|
|
88
|
+
endpoint = f'firewall/d_nat/toggleRule/{uuid}'
|
|
89
|
+
if disabled is not None:
|
|
90
|
+
endpoint += f'/{1 if disabled else 0}'
|
|
91
|
+
data = self._post(endpoint, '')
|
|
92
|
+
return Result(**data)
|
|
93
|
+
|
|
94
|
+
def firewall_dnat_move_rule_before(self, selected_uuid: str, target_uuid: str) -> dict:
|
|
95
|
+
"""
|
|
96
|
+
Move a DNAT rule before another rule.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
selected_uuid: UUID of rule to move
|
|
100
|
+
target_uuid: UUID of target rule (selected will be placed before this)
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
API response dict
|
|
104
|
+
"""
|
|
105
|
+
return self._get(f'firewall/d_nat/moveRuleBefore/{selected_uuid},{target_uuid}')
|
|
106
|
+
|
|
107
|
+
def firewall_dnat_savepoint(self) -> dict:
|
|
108
|
+
"""
|
|
109
|
+
Create a savepoint for DNAT rules (for rollback).
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
Savepoint revision info
|
|
113
|
+
"""
|
|
114
|
+
return self._post('firewall/d_nat/savepoint', '')
|
|
115
|
+
|
|
116
|
+
def firewall_dnat_apply(self, rollback_revision: str = None) -> Result:
|
|
117
|
+
"""
|
|
118
|
+
Apply DNAT rule changes.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
rollback_revision: Optional rollback revision
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
Result object
|
|
125
|
+
"""
|
|
126
|
+
endpoint = 'firewall/d_nat/apply' + self._get_arg_formatter(rollback_revision)
|
|
127
|
+
data = self._post(endpoint, '')
|
|
128
|
+
return Result(**data)
|
|
129
|
+
|
|
130
|
+
def firewall_dnat_cancel_rollback(self, rollback_revision: str) -> Result:
|
|
131
|
+
"""
|
|
132
|
+
Cancel a pending DNAT rollback.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
rollback_revision: Rollback revision to cancel
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
Result object
|
|
139
|
+
"""
|
|
140
|
+
data = self._post(f'firewall/d_nat/cancelRollback/{rollback_revision}', '')
|
|
141
|
+
return Result(**data)
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from opnsense_api.base_client import BaseClient
|
|
4
|
+
from opnsense_api.pydantic.Rule import Rule
|
|
5
|
+
from opnsense_api.pydantic.Result import Result
|
|
6
|
+
from opnsense_api.pydantic.SearchRequest import SearchRequest
|
|
7
|
+
from opnsense_api.pydantic.SearchResult import RuleSearchResult
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class FirewallFilterClient(BaseClient):
|
|
11
|
+
|
|
12
|
+
def firewall_filter_search_rule(self, search: SearchRequest = None) -> RuleSearchResult:
|
|
13
|
+
data = self._search('firewall/filter/searchRule', search)
|
|
14
|
+
return RuleSearchResult.from_ui_dict(data, Rule)
|
|
15
|
+
|
|
16
|
+
def firewall_filter_search_rule_by_interface(self, interface: str) -> RuleSearchResult:
|
|
17
|
+
"""
|
|
18
|
+
Search firewall filter rules for a specific interface.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
interface: Interface identifier (e.g., 'lan', 'wan', 'opt1')
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
RuleSearchResult with rules for that interface
|
|
25
|
+
"""
|
|
26
|
+
data = self._post('firewall/filter/searchRule', {
|
|
27
|
+
'current': 1,
|
|
28
|
+
'rowCount': -1, # All rows
|
|
29
|
+
'sort': {},
|
|
30
|
+
'searchPhrase': '',
|
|
31
|
+
'interface': interface
|
|
32
|
+
})
|
|
33
|
+
return RuleSearchResult.from_ui_dict(data, Rule)
|
|
34
|
+
|
|
35
|
+
def firewall_filter_search_all_rules(self) -> List[Rule]:
|
|
36
|
+
"""
|
|
37
|
+
Fetch all firewall filter rules across all interfaces.
|
|
38
|
+
|
|
39
|
+
OPNsense filter rules are organized per interface, so this method:
|
|
40
|
+
1. Fetches all assigned interfaces
|
|
41
|
+
2. Searches for rules on each interface using the interface parameter
|
|
42
|
+
3. Combines results, removing duplicates by UUID
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
List of all Rule entities across all interfaces
|
|
46
|
+
"""
|
|
47
|
+
# Get all assigned interfaces
|
|
48
|
+
interfaces = self.interface_overview_export()
|
|
49
|
+
assigned = interfaces.get_assigned()
|
|
50
|
+
|
|
51
|
+
all_rules = {} # Use dict to deduplicate by UUID
|
|
52
|
+
|
|
53
|
+
for iface in assigned:
|
|
54
|
+
if not iface.identifier:
|
|
55
|
+
continue
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
result = self.firewall_filter_search_rule_by_interface(iface.identifier)
|
|
59
|
+
|
|
60
|
+
for rule in result.rows:
|
|
61
|
+
if rule.uuid and rule.uuid not in all_rules:
|
|
62
|
+
all_rules[rule.uuid] = rule
|
|
63
|
+
except Exception:
|
|
64
|
+
# Skip interfaces that fail (e.g., no rules configured)
|
|
65
|
+
continue
|
|
66
|
+
|
|
67
|
+
return list(all_rules.values())
|
|
68
|
+
|
|
69
|
+
def firewall_filter_get_rule(self, uuid: str = None) -> Rule:
|
|
70
|
+
data = self._get('firewall/filter/getRule' + self._get_arg_formatter(uuid))
|
|
71
|
+
return Rule.from_ui_dict(data)
|
|
72
|
+
|
|
73
|
+
def firewall_filter_add_rule(self, rule: Rule) -> Result:
|
|
74
|
+
data = self._post('firewall/filter/addRule', rule)
|
|
75
|
+
return Result(**data)
|
|
76
|
+
|
|
77
|
+
def firewall_filter_set_rule(self, uuid: str, rule: Rule) -> Result:
|
|
78
|
+
data = self._post(f'firewall/filter/setRule/{uuid}', rule)
|
|
79
|
+
return Result(**data)
|
|
80
|
+
|
|
81
|
+
def firewall_filter_del_rule(self, uuid: str) -> Result:
|
|
82
|
+
data = self._post(f'firewall/filter/delRule/{uuid}', '')
|
|
83
|
+
return Result(**data)
|
|
84
|
+
|
|
85
|
+
def firewall_filter_savepoint(self) -> dict:
|
|
86
|
+
return self._post('firewall/filter/savepoint', '')
|
|
87
|
+
|
|
88
|
+
def firewall_filter_apply(self, rollback_revision: str = None) -> Result:
|
|
89
|
+
endpoint = 'firewall/filter/apply' + self._get_arg_formatter(rollback_revision)
|
|
90
|
+
data = self._post(endpoint, '')
|
|
91
|
+
return Result(**data)
|
|
92
|
+
|
|
93
|
+
def firewall_filter_cancel_rollback(self, rollback_revision: str) -> Result:
|
|
94
|
+
data = self._post(f'firewall/filter/cancelRollback/{rollback_revision}', '')
|
|
95
|
+
return Result(**data)
|