diff --git a/.gitignore b/.gitignore index 26f2096..9481a50 100644 --- a/.gitignore +++ b/.gitignore @@ -16,7 +16,7 @@ __pycache__ # Log-Dateien *.log - +config.xml # Temporäre Dateien *.tmp *.swp diff --git a/README.md b/README.md index 8c580ff..f07017e 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,8 @@ # opnsense-helper -- assign and enable lan / phy interfaces! - ***Not enabled in the opnsense api*** -- create vlans, vlan-interfaces - ***Not enabled in the opnsense api*** -- setup dhcp -- uses the opnsense backend via shh - ***much faster than the frontend api!!!*** -- all configctl commands -- all pluginctl commands -- all opnsense scripts -## Some of the docs are currently outdated +- create, assign and enable lan / phy interfaces and all the other stuff that is ***Not enabled*** in the opnsense api +- use the config_manager to apply all your configs in runtime at once +- uses the opnsense backend via shh + ## install ## pip @@ -15,10 +11,13 @@ pip install opnsense-helper ``` ## usage > you can run the provided snippets directly by pulling the [example file](https://github.com/the-pod-shop/opnsense-helper/blob/main/python/examples/add_vlans.py) + +### required variables * import the package and define the needed variables for the main class ```python -from opnsense_helper.classes import Opnsense_Helper +from opnsense_helper.opnsense_helper import Opnsense_Helper +from opnsense_helper.config_manager.config_manager import Vlan, Dhcpd, Interface host= "192.168.1.103" auth={ @@ -28,6 +27,9 @@ auth={ temp_path="./config.xml" helper=Opnsense_Helper(host=host,ssh_auth=auth,temp_path=temp_path, init=True) ``` +### config_manager +> add or change existing modules +> - currently supports vlans, dhcpd, interfaces and soon routes, as well as firewall rules - create the objects of the modules you want to set ```python vlans=[ @@ -58,6 +60,55 @@ helper.set("vlans",vlans) helper.save(temp_path) #helper.remove_items() ``` +### scripts and commands +> - you can run every script fron `/usr/local/opnsense/scripts/` +> - you can use every `pluginctl` and `configctl` commands +> - use ` ` +> - besides command, argument may be required based on the method + +```python + helper.scripts.system.run("status") + helper.scripts.routes.run("show_routes") + + helper.commands.pluginctl.run("ipv4") + helper.commands.pluginctl.run("service", "dhcpd status") + helper.commands.pluginctl.run("config", "dhcp") +``` +#### Result +```bash +$ /usr/local/opnsense/scripts/system/status.php* +{"CrashReporter":{"statusCode":2,"message":"No problems were detected.","logLocation":"\/crash_reporter.php","timestamp":"0"},"Firewall":{"statusCode":-1,"message":"There were error(s) loading the rules: \/tmp\/rules.debug:25: syntax error - The line in question reads [25]: set loginterface \n","logLocation":"\/ui\/diagnostics\/log\/core\/firewall","timestamp":1731025409}} + +$ /usr/local/opnsense/scripts/routes/show_routes.py* +destination gateway flags nhop# mtu netif expire +ipv4 default 192.168.0.1 UGS 5 1500 vtnet0 +ipv4 localhost link#4 UH 2 16384 lo0 +ipv4 192.168.0.1 link#1 UHS 4 1500 vtnet0 +ipv4 192.168.1.0/24 link#1 U 1 1500 vtnet0 +ipv4 192.168.1.1 link#1 UHS 4 1500 vtnet0 +ipv4 192.168.1.103 link#1 UHS 3 16384 lo0 +ipv4 200.1.0.0/24 link#2 U 6 1500 vtnet1 +ipv4 200.1.0.1 link#2 UHS 7 16384 lo0 +ipv6 localhost link#4 UHS 1 16384 lo0 +ipv6 fe80::%lo0/64 link#4 U 3 16384 lo0 +ipv6 fe80::1%lo0 link#4 UHS 2 16384 lo0 + +$ pluginctl -4 +{ + "address": null, + "network": null, + "bits": null, + "device": null, + "interface": null +} + +$ pluginctl -s dhcpd status +dhcpd is running as pid 16072. + +$ pluginctl -c dhcp +Starting DHCPv4 service...done. +``` + ### Frontend Api - you can download the config.xml and add vlans via api @@ -95,29 +146,29 @@ def using_api(): | interfaces | list[dict] | {id: str, descr: str, enable: int, ipaddr: str, subnet: str, type: str, virtual: bool, spoofmac: str, interface: str} | | dhcp | list[dict] | {id: str, enable: str, ddnsdomainalgorithm: str, range: {from: str, _to: str}} | -#### Manual steps +#### config_manager manual usage * pull the config.xml from the firewall via ssh ```python -helper.get_conf(conf_path) +helper.config_manager.get_conf(conf_path) ``` * initialize the the Opnsense_Helper-class and parse the config.xml ```python -helper.initialize() +helper.config_manager.initialize() ``` - add the items ```python -helper.add_Items("vlans",vlans) +helper.config_manager.add_Items("vlans",vlans) ``` * save the configuration as xml and copy it back to the firewall > this will also reconfigure your vlans for you, if you have any ```python -helper.save(output) -helper.put_file(output,conf_path) -helper.close_con() +helper.config_manager.save(output) +helper.config_manager.put_file(output,conf_path) +helper.config_manager.close_con() ``` diff --git a/pyproject.toml.old b/pyproject.toml.old deleted file mode 100644 index 4e4ff57..0000000 --- a/pyproject.toml.old +++ /dev/null @@ -1,18 +0,0 @@ -[project] -name = "opnsense_helper" -version = "0.1.1" -authors = [ - { name="ji.podhdead", email="ji-podhead@podshop.com" }, -] -description = "assign lan interfaces, create vlans, vlan-interfaces and setup dhcp in a single script." -readme = "README.md" -requires-python = ">=3.8" -classifiers = [ - "Programming Language :: Python :: 3", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", -] - -[project.urls] -Homepage = "https://github.com/the-pod-shop/opnsense-helper/" -Issues = "https://github.com/the-pod-shop/opnsense-helper/issues" diff --git a/python/build/lib/opnsense_helper/opnsense_helper.py b/python/build/lib/opnsense_helper/opnsense_helper.py index 34c5296..91bd585 100644 --- a/python/build/lib/opnsense_helper/opnsense_helper.py +++ b/python/build/lib/opnsense_helper/opnsense_helper.py @@ -1,111 +1,16 @@ -import xml.etree.ElementTree as ET -import paramiko -from .utils import parseChild,update_xml_file,aliases, get_element, getRes - - -class Interface: - def __init__(self, name, parent): - self.descr = parseChild(parent, "descr") - self.enable = parseChild(parent, "enable") - self.ipaddr = parseChild(parent, "ipaddr") - self.subnet = parseChild(parent, "subnet") - self.type = parseChild(parent, "type") - self.virtual = parseChild(parent, "virtual") - self.spoofmac=parseChild(parent, "spoofmac") - self.attr=None - -class Vlan: - def __init__(self,name, parent): - self.parentinterface =parseChild(parent, "if") - self.tag = parseChild(parent,"tag") - self.pcp = parseChild(parent,"pcp") - self.proto = parseChild(parent,"proto") - self.descr = parseChild(parent,"descr") - self.vlanif = parseChild(parent,"vlanif") - self.attr=None - -class Dhcpd: - def __init__(self, name, parent): - self.enable = parseChild(parent, "enable") - self.ddnsdomainalgorithm = parseChild(parent, "ddnsdomainalgorithm") - self.range={ - "_from":"", - "_to":"" - } - self.attr=None - - -class OpennsenseHelper(): - def __init__(self, filepath, method=None, user=None, passw=None, host=None, api_key=None, api_secret=None): - self.filepath=filepath - self.tree = ET.parse(filepath) - self.root = self.tree.getroot() - self.objects={ - "vlans":{}, - "dhcpd":{}, - "interfaces":{} - } - - if(method=="ssh"): - self.ssh = paramiko.SSHClient() - self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - self.ssh.connect(host, username=user, password=passw) - self.sftp = self.ssh.open_sftp() - #self.ssh .open_sftp - if(method=="http"): - self.url = host - self.api_key = api_key - self.api_secret = api_secret - self.host=host - - def initialize(self): - self.get_all("dhcpd") - self.get_all("vlans") - self.get_all("interfaces") - def save(self,output): - update_xml_file(self.objects["dhcpd"],self.root,"dhcpd") - update_xml_file(self.objects["interfaces"],self.root,"interfaces") - update_xml_file(self.objects["vlans"],self.root,"vlans") - with open(output, 'w') as f: - f.write(ET.tostring(self.root, encoding='unicode', method='xml')) - - def get_all(self,element): - print(f''' ----------------------------- - {element}''') - for parent in self.root.findall(element): - for key in parent: - if element== "dhcpd": - child = Dhcpd(key.tag,parent) - elif element== "interfaces": - child = Interface(key.tag,parent) - elif element== "vlans": - child = Vlan(key.tag,parent) - - child=get_element(parent, key.tag, child) - if element== "dhcpd": - _range={"_from": parent.find(key.tag).find("range/from"), - "_to":parent.find(key.tag).find("range/to")} - if _range["_from"] is not None: - _range["_from"]=_range["_from"].text - _range["_to"]=_range["_to"].text - child.range =_range - child.attr=key.attrib if key.attrib is not None else None - print(child.attr) - self.objects[element][key.tag]=child.__dict__ - print(f'''{key.tag} : {child.__dict__} - -------------------''') - - return(self.objects[element]) - def close_con(self): - self.sftp.close() - self.ssh.close() - def put_file(self, _from,_to): - self.sftp.put(_from, _to) - def get_file(self,_from,_to): - self.sftp.get(_from,_to) - def get_backup(self,_from,_to): - command = 'core/firmware/status' - timeout = 5 - getRes(self.host, command, self.api_key, self.api_secret, timeout) - +from opnsense_helper.config_manager.config_manager import Interface, Vlan, Dhcpd, Config_Manager +from opnsense_helper.utils.baseclass import Base_Class +from opnsense_helper.commands.commands import Commands +from opnsense_helper.scripts.scripts import Scripts +class Opnsense_Helper(): + def __init__(self, host=None, ssh_auth=None, api_auth=None, conf_path="/conf/config.xml", temp_path="./config.xml", verbose=False, init_config_manager=True): + + base_class = Base_Class(host, ssh_auth, api_auth, conf_path, temp_path, verbose) + self.config_manager=Config_Manager(base_class) + self.commands=Commands(base_class) + self.scripts=Scripts(base_class) + + if init_config_manager: + self.config_manager.get_conf() + self.config_manager.initialize() diff --git a/python/config.xml b/python/config.xml deleted file mode 100644 index ff15a01..0000000 --- a/python/config.xml +++ /dev/null @@ -1,870 +0,0 @@ - - opnsense - - - Increase UFS read-ahead speeds to match the state of hard drives and NCQ. - vfs.read_max - default - - - Set the ephemeral port range to be lower. - net.inet.ip.portrange.first - default - - - Drop packets to closed TCP ports without returning a RST - net.inet.tcp.blackhole - default - - - Do not send ICMP port unreachable messages for closed UDP ports - net.inet.udp.blackhole - default - - - Randomize the ID field in IP packets - net.inet.ip.random_id - default - - - - Source routing is another way for an attacker to try to reach non-routable addresses behind your box. - It can also be used to probe for information about your internal networks. These functions come enabled - as part of the standard FreeBSD core system. - - net.inet.ip.sourceroute - default - - - - Source routing is another way for an attacker to try to reach non-routable addresses behind your box. - It can also be used to probe for information about your internal networks. These functions come enabled - as part of the standard FreeBSD core system. - - net.inet.ip.accept_sourceroute - default - - - - This option turns off the logging of redirect packets because there is no limit and this could fill - up your logs consuming your whole hard drive. - - net.inet.icmp.log_redirect - default - - - Drop SYN-FIN packets (breaks RFC1379, but nobody uses it anyway) - net.inet.tcp.drop_synfin - default - - - Enable sending IPv6 redirects - net.inet6.ip6.redirect - default - - - Enable privacy settings for IPv6 (RFC 4941) - net.inet6.ip6.use_tempaddr - default - - - Prefer privacy addresses and use them over the normal addresses - net.inet6.ip6.prefer_tempaddr - default - - - Generate SYN cookies for outbound SYN-ACK packets - net.inet.tcp.syncookies - default - - - Maximum incoming/outgoing TCP datagram size (receive) - net.inet.tcp.recvspace - default - - - Maximum incoming/outgoing TCP datagram size (send) - net.inet.tcp.sendspace - default - - - Do not delay ACK to try and piggyback it onto a data packet - net.inet.tcp.delayed_ack - default - - - Maximum outgoing UDP datagram size - net.inet.udp.maxdgram - default - - - Handling of non-IP packets which are not passed to pfil (see if_bridge(4)) - net.link.bridge.pfil_onlyip - default - - - Set to 1 to additionally filter on the physical interface for locally destined packets - net.link.bridge.pfil_local_phys - default - - - Set to 0 to disable filtering on the incoming and outgoing member interfaces. - net.link.bridge.pfil_member - default - - - Set to 1 to enable filtering on the bridge interface - net.link.bridge.pfil_bridge - default - - - Allow unprivileged access to tap(4) device nodes - net.link.tap.user_open - default - - - Randomize PID's (see src/sys/kern/kern_fork.c: sysctl_kern_randompid()) - kern.randompid - default - - - Disable CTRL+ALT+Delete reboot from keyboard. - hw.syscons.kbd_reboot - default - - - Enable TCP extended debugging - net.inet.tcp.log_debug - default - - - Set ICMP Limits - net.inet.icmp.icmplim - default - - - TCP Offload Engine - net.inet.tcp.tso - default - - - UDP Checksums - net.inet.udp.checksum - default - - - Maximum socket buffer size - kern.ipc.maxsockbuf - default - - - Page Table Isolation (Meltdown mitigation, requires reboot.) - vm.pmap.pti - default - - - Disable Indirect Branch Restricted Speculation (Spectre V2 mitigation) - hw.ibrs_disable - default - - - Hide processes running as other groups - security.bsd.see_other_gids - default - - - Hide processes running as other users - security.bsd.see_other_uids - default - - - Enable/disable sending of ICMP redirects in response to IP packets for which a better, - and for the sender directly reachable, route and next hop is known. - - net.inet.ip.redirect - default - - - - Redirect attacks are the purposeful mass-issuing of ICMP type 5 packets. In a normal network, redirects - to the end stations should not be required. This option enables the NIC to drop all inbound ICMP redirect - packets without returning a response. - - net.inet.icmp.drop_redirect - 1 - - - Maximum outgoing UDP datagram size - net.local.dgram.maxdgram - default - - - - - - 115200 - serial - video - normal - OPNsense - localdomain - 1 - - admins - System Administrators - system - 1999 - 0 - page-all - - - root - System Administrator - system - admins - $2y$10$YRVoF4SgskIsrXOvOQjGieB9XqHPRra9R7d80B3BZdbY/j21TwBfS - 0 - - - ejl4fIU9yfNk+gaQmPk/rqIa15f1yX1snIKgcIEl2QNoJwhbekraWIE0ANRYceh9hey5IFGzlf3da4yJ - $6$$eEqYlri6WGRI581O1XJHX12nF77O23/2yaj.Rw0GpSCBAD/ueJ3jC8MlDUCxMxOA7kL2wtbz6MYlGkt6DGoM30 - - - - 2000 - 2000 - Etc/UTC - 0.opnsense.pool.ntp.org 1.opnsense.pool.ntp.org 2.opnsense.pool.ntp.org 3.opnsense.pool.ntp.org - - https - 6709124c5ca91 - - - - - - yes - 1 - 1 - 1 - 1 - 1 - 1 - - hadp - hadp - hadp - - monthly - - 1 - 1 - - admins - 1 - - - - - - - enabled - 1 - 1 - - -1 - -1 - - - - - - - - - 192.168.0.1 - - 1dhcplo018127.0.0.1Loopbacknone1vtnet1124200.1.0.1routervlan0.1124200.0.1.100:00:00:01:00:01vlan1vlan0.2224200.0.2.100:00:00:01:00:02vlan2vlan0.3324200.0.3.100:00:00:01:00:03vlan3192.168.1.3192.168.1.1261200.1.0.2200.1.0.2hmac-md1200.0.1.1200.0.1.100hmac-md1200.0.2.1200.0.2.100hmac-md1200.0.3.1200.0.3.100hmac-md - - - public - - - - automatic - - - - - pass - inet - Default allow LAN to any rule - lan - - lan - - - - - - - pass - inet6 - Default allow LAN IPv6 to any rule - lan - - lan - - - - - - - - - ICMP - icmp - ICMP - - - - TCP - tcp - Generic TCP - - - - HTTP - http - Generic HTTP - - / - - 200 - - - - HTTPS - https - Generic HTTPS - - / - - 200 - - - - SMTP - send - Generic SMTP - - - 220 * - - - - - 0.opnsense.pool.ntp.org - - - system_information-container:00000000-col3:show,services_status-container:00000001-col4:show,gateways-container:00000002-col4:show,interface_list-container:00000003-col4:show - 2 - - - root@192.168.1.102 - /api/interfaces/vlan_settings/delItem/99e38e60-6baa-402c-b94e-a79bd45a52ce made changes - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - v9 - - - - 0 - - 1800 - 15 - - - - - - - - - 0 - 0 - 0 - wan - 192.168.0.0/16,10.0.0.0/8,172.16.0.0/12 - - - W0D23 - 4 - - - - - - - 0 - 0 - 0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0 - 127.0.0.1 - 8000 - - - - - 0 - - 4000 - - - 0 - - - - - - - - - - 0 - 120 - 120 - 127.0.0.1 - 25 - - - 0 - auto - 1 - - - - - 0 - root - 2lu9p2wj7Kv3LN1rWUlZNKszWRsOK - 2812 - - - 5 - 1 - - - 0 - root@localhost.local - 0 - - - - - - - 1 - $HOST - - system - - - - 300 - 30 -
- - - - 55c9207a-e2b9-4843-b316-0bf8c00d7f3f,8857acf7-7139-418d-9e3e-d655f227cbd1,71b90893-9bfe-49be-a0d8-bff6454bb91f,937c2cb2-bd88-464e-8002-8dc1a73e780a - - - - - 1 - RootFs - - filesystem - - - / - 300 - 30 -
- - - - 53434a4e-1140-4edd-953e-d168547abe81 - - - - - 0 - carp_status_change - - custom - - - /usr/local/opnsense/scripts/OPNsense/Monit/carp_status - 300 - 30 -
- - - - b8f28cff-ddd0-496d-857e-9d2552eb35f6 - - - - - 0 - gateway_alert - - custom - - - /usr/local/opnsense/scripts/OPNsense/Monit/gateway_alert - 300 - 30 -
- - - - 507e9ea4-394b-4437-ad52-4df7d7fcafb0 - - - - - Ping - NetworkPing - failed ping - alert - - - - NetworkLink - NetworkInterface - failed link - alert - - - - NetworkSaturation - NetworkInterface - saturation is greater than 75% - alert - - - - MemoryUsage - SystemResource - memory usage is greater than 75% - alert - - - - CPUUsage - SystemResource - cpu usage is greater than 75% - alert - - - - LoadAvg1 - SystemResource - loadavg (1min) is greater than 2 - alert - - - - LoadAvg5 - SystemResource - loadavg (5min) is greater than 1.5 - alert - - - - LoadAvg15 - SystemResource - loadavg (15min) is greater than 1 - alert - - - - SpaceUsage - SpaceUsage - space usage is greater than 75% - alert - - - - ChangedStatus - ProgramStatus - changed status - alert - - - - NonZeroStatus - ProgramStatus - status != 0 - alert - - - - - - - - - - - - - - 0 - LAN_GW - Interface LAN Gateway - lan - inet - 192.168.1.1 - 0 - 0 - 1 - - - - 255 - 1 - - - - - - - - - - - 0 - LAN_GW_2 - Interface LAN Gateway - lan - inet - 192.168.0.1 - 1 - 0 - 1 - - - - 255 - 1 - - - - - - - - - - - - - 1 - - - - - - - - - - - 1 - 53 - - - - - - - - - - - - - - transparent - - - - - - - - - - - - - - - - - - - - - - 1 - 0 - - 0.0.0.0/8,10.0.0.0/8,100.64.0.0/10,169.254.0.0/16,172.16.0.0/12,192.0.2.0/24,192.168.0.0/16,198.18.0.0/15,198.51.100.0/24,203.0.113.0/24,233.252.0.0/24,::1/128,2001:db8::/32,fc00::/8,fd00::/8,fe80::/10 - - - - - - - - - - - - - - - - - - allow - - - 0 - - - - - - -
- - - - - - - - - - - - - - - - 0 - - - - - - - - - - - - - - vtnet101vlan1vlan0.1vtnet102vlan2vlan0.2vtnet103vlan3vlan0.3 - - - - - - - - - - - - - - - - - - - - - - 6709124c5ca91 - Web GUI TLS certificate - LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUhIakNDQlFhZ0F3SUJBZ0lVTHVUQ25TSDZTTGdCUEpBdkxOSnR2b1lydnNVd0RRWUpLb1pJaHZjTkFRRUwKQlFBd2dZa3hIVEFiQmdOVkJBTU1GRTlRVG5ObGJuTmxMbXh2WTJGc1pHOXRZV2x1TVFzd0NRWURWUVFHRXdKTwpUREVWTUJNR0ExVUVDQXdNV25WcFpDMUliMnhzWVc1a01SVXdFd1lEVlFRSERBeE5hV1JrWld4b1lYSnVhWE14CkxUQXJCZ05WQkFvTUpFOVFUbk5sYm5ObElITmxiR1l0YzJsbmJtVmtJSGRsWWlCalpYSjBhV1pwWTJGMFpUQWUKRncweU5ERXdNVEV4TVRVMk1qSmFGdzB5TlRFeE1USXhNVFUyTWpKYU1JR0pNUjB3R3dZRFZRUUREQlJQVUU1egpaVzV6WlM1c2IyTmhiR1J2YldGcGJqRUxNQWtHQTFVRUJoTUNUa3d4RlRBVEJnTlZCQWdNREZwMWFXUXRTRzlzCmJHRnVaREVWTUJNR0ExVUVCd3dNVFdsa1pHVnNhR0Z5Ym1sek1TMHdLd1lEVlFRS0RDUlBVRTV6Wlc1elpTQnoKWld4bUxYTnBaMjVsWkNCM1pXSWdZMlZ5ZEdsbWFXTmhkR1V3Z2dJaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQwpEd0F3Z2dJS0FvSUNBUURKWjVncXVRdHlBNXVta1NrMy8wN0RRUmlSSU0yUmRtNVE4R2NEcEVodVBuYjVObU8yClM3Qjh5NzRNNGRsbWNQV0N4Ukw4bGRWNmZjcGo0UnVIRTdFK25nc2pISzZLV1pLSVhjTWtia2NPN0s5ZndQak4KaVBEcUdqTlV1Z1UvRldmU2VMVElneUxXM0VWaU9TSHlZVkZsc29QWkdvd3d6QXNQSTBFNFJHSFozRUhvNXB6NAp3am9LQUYwN1ZSTVlhWUlxcEkvdUdldnFzZjdNN0ZIV28yeS9tUEhuUzA1WHlnbFRlYzZzNXpsWWM0a09mei9vCmpzL1FYSHdMZVFBYXJVTUdkazA2dGVISGtNV1RvREoxMFNxNm02ZFpNSk1CekQ0aWZxbWsvNGNZUjlydmFKVnkKd0Z2S0x1MGxxcDZQdHhjR1pSVG5vSVFldTM0RXloSmtvTk9YZHk3OFVzOS9UaThPUEZyNGlNYVR6Y1YwSVppUApJRTdRcDlKL1IrQTM0V1hlUFpUZWoxN3pGNDh2OElHeU1rYVNvbWNwNGlGQ1d2S0g5VXVRa2trTWF4dkJLNTUzClpjdmtIUW5NYzk5TkhzVHJadjJtQkl2MkhtanovQ0pqSW9XbEpRaFV2cEVaUzI2RC80ZURoMUpKbiszUTh2MGYKeVBUTjc2aUw1OGt4K201NFM2S3loMms1Z0JIUjhmcENaYUtpR201aEJPOXZlV0NWVGlkK3lycm1SM0FBcWxJeQpkcE9naExqdExIbGkxV2NDQWZkbzhlVUdXN2piYmUyR2U5VTV4MmxscGE1b2JpVnp1TVBucVIzblgwZHJKOGZwCjA2VEVaaHFOWEFnRGRJclVqcWh0SGFIWUtWZllSaytSSHNJZEVlaEJWZkxqREFiUDY1R3UrK3VkalFJREFRQUIKbzRJQmVqQ0NBWFl3Q1FZRFZSMFRCQUl3QURBUkJnbGdoa2dCaHZoQ0FRRUVCQU1DQmtBd05BWUpZSVpJQVliNApRZ0VOQkNjV0pVOVFUbk5sYm5ObElFZGxibVZ5WVhSbFpDQlRaWEoyWlhJZ1EyVnlkR2xtYVdOaGRHVXdIUVlEClZSME9CQllFRkxnMlJLWlRXTnBxaVY2bmFrSmVtYm1oWFFpbU1JR3pCZ05WSFNNRWdhc3dnYWloZ1kra2dZd3cKZ1lreEhUQWJCZ05WQkFNTUZFOVFUbk5sYm5ObExteHZZMkZzWkc5dFlXbHVNUXN3Q1FZRFZRUUdFd0pPVERFVgpNQk1HQTFVRUNBd01XblZwWkMxSWIyeHNZVzVrTVJVd0V3WURWUVFIREF4TmFXUmtaV3hvWVhKdWFYTXhMVEFyCkJnTlZCQW9NSkU5UVRuTmxibk5sSUhObGJHWXRjMmxuYm1Wa0lIZGxZaUJqWlhKMGFXWnBZMkYwWllJVUx1VEMKblNINlNMZ0JQSkF2TE5KdHZvWXJ2c1V3SFFZRFZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQ0FJQwpNQXNHQTFVZER3UUVBd0lGb0RBZkJnTlZIUkVFR0RBV2doUlBVRTV6Wlc1elpTNXNiMk5oYkdSdmJXRnBiakFOCkJna3Foa2lHOXcwQkFRc0ZBQU9DQWdFQVlqcnBTcU1Sbk1may94YWNvdnlPSURneGhvZXp6OUE4cXVXbnFQOGwKVFNOM2FFVDcvUkt0MGZFMUcrZGhNbGFaTm9jeTljem5OR2pKVjkrVytiYXh6aTNHNTQwbk91K3lteDhyNkw2bwpQNE5mVXUvM284MHhtbmZQN2ZkSVV3RXY2RG1KQUdmZi91d2t6M0ExbXVqeGRrVVZFQ0NDMU9tNmJ1T2o3UE0yCk5MUml2VFM0bXFUbkY1RXZTV3FzNVJSVGNPSllHZjcxZElRMW0wS3FyeG1VT0kzck9JeUpyNnZXNk8wRmhqcmcKMmRabW53K0F3TmEyVEVhdDBMTUtjSWM3NTY4RExCMzN1YzA0bGRRV0hOS2FrVU9LZ3dFcDRZWTltTngwemw3Vgo3cS9LUy9HQUtTK3NRVnUyY0FscmZzYUoybmVud3FTYVpEYnZZTDF6NHU3SXlzUXhCRlljbUJ5aUVJb1VZQ2JaCnd2MFZaWFVUVXRNbnVPcytUUFlXSWZTRm50U3VZSzhGWWRZb3cvdy9tRXBic25lSUtYeWFFS1N5NVMxS1Y3RVoKc1JnNGtJRklROE1uOW5Mc1dwREhrdGZJUk5VUnNYaG9uR0FscHFqNDgxMWQraU41OUIzTGJwQ056OWFpNnBqbgozSDd1VHFaQ1ZIVEU3L1RpQzFPS2kyMVVmaG9za1pkUmxQK3pQTENkWFhwS0kvazdaNENPcDRhd2lwWW9QMjE0CmhkcndhOE9ST3NwZFMrQS95bGptOXdCbTIvOTRORWNyQzYya0ZrdTg0VExOdlVWa0lRL2RkbXBueHBRT05KTGwKR3Vka0RlQkFpOUJoWTIvL1dyS2FqOWxESEhVVWljL2k3Nm9oK011NnVueTRnTW52UlgwVElmSWx2WkdyOXJ2eQpGNHM9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K - LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUpRZ0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQ1N3d2dna29BZ0VBQW9JQ0FRREpaNWdxdVF0eUE1dW0Ka1NrMy8wN0RRUmlSSU0yUmRtNVE4R2NEcEVodVBuYjVObU8yUzdCOHk3NE00ZGxtY1BXQ3hSTDhsZFY2ZmNwago0UnVIRTdFK25nc2pISzZLV1pLSVhjTWtia2NPN0s5ZndQak5pUERxR2pOVXVnVS9GV2ZTZUxUSWd5TFczRVZpCk9TSHlZVkZsc29QWkdvd3d6QXNQSTBFNFJHSFozRUhvNXB6NHdqb0tBRjA3VlJNWWFZSXFwSS91R2V2cXNmN00KN0ZIV28yeS9tUEhuUzA1WHlnbFRlYzZzNXpsWWM0a09mei9vanMvUVhId0xlUUFhclVNR2RrMDZ0ZUhIa01XVApvREoxMFNxNm02ZFpNSk1CekQ0aWZxbWsvNGNZUjlydmFKVnl3RnZLTHUwbHFwNlB0eGNHWlJUbm9JUWV1MzRFCnloSmtvTk9YZHk3OFVzOS9UaThPUEZyNGlNYVR6Y1YwSVppUElFN1FwOUovUitBMzRXWGVQWlRlajE3ekY0OHYKOElHeU1rYVNvbWNwNGlGQ1d2S0g5VXVRa2trTWF4dkJLNTUzWmN2a0hRbk1jOTlOSHNUclp2Mm1CSXYySG1qegovQ0pqSW9XbEpRaFV2cEVaUzI2RC80ZURoMUpKbiszUTh2MGZ5UFRONzZpTDU4a3grbTU0UzZLeWgyazVnQkhSCjhmcENaYUtpR201aEJPOXZlV0NWVGlkK3lycm1SM0FBcWxJeWRwT2doTGp0TEhsaTFXY0NBZmRvOGVVR1c3amIKYmUyR2U5VTV4MmxscGE1b2JpVnp1TVBucVIzblgwZHJKOGZwMDZURVpocU5YQWdEZElyVWpxaHRIYUhZS1ZmWQpSaytSSHNJZEVlaEJWZkxqREFiUDY1R3UrK3VkalFJREFRQUJBb0lDQUZFRHBNZjZxMG52ZlpyZzVVMnJHd21iCms4QTlDN09waWZKdzRWOHJwQjhFYldNTnA4cGFZK0d2S3dHUGo1MlBicEp0cDVlR3hkcUI0dm1PUVQ1eWcyZUcKdDBBbWQyY2JaTUFKeDBkT1BMTWFMZWs4ZkZoZzcxZWJ0bTRzYlpQVWpuQ3hNVGMrMkxMSTQvZ0F0MUVDS0hYWgord05IbElERWN1Wmg3Z2d1S1pZeTdTZzhLNFE4ekkvZFdhTldNMW9zaHJtVWZIandZRitGdmRLOGRLYjhFZkI0CkcwWkltbmpod2lLYjZyRE9TZGowa0hzT1lLeXhWWktnd1J6MFRVQnQxWG94K0JyWHNyeE4xY3Z1TXA0RngwUk0KL0dINkVnZzFHZ0ZTSmZkQytocWdQTWIwc0xqNSszSTJzaG5tcWVWeFpvZWgvV2kydnE2R0NNandaN21tbjdXawo2QTZ3cVRlREFwcHZqeGNlN2FwOVEzMXFzVWl4NGpFenVVSmJzd3NCTTBVQmhTc3VIcGZVR1lONmtURnFGSWpBCmhwdE84VnNkUGpUUEx6WmRNTmFuZjFxOVh2TWxGTHhTanAwUGZjc2owcGdSNzg5NXhkWGRmODVhWHhCbzZKbGwKZ2hwblgzUTJyNDgwdEhyNTBWTmdqYTA2WDR2T291bGVtcHdhZ09NbzB0OXVQUm0wMExmMUNlQkxaUE15cFY5cgpzRXNZSndhZExuYzZUbG9kQmd6WkRXSzVKZ3pTSGFidkUwS0dpK2tNblFpV0lURklwYW1Yc0c4MWs4bjNDSkdXCll2aElrQmVsd3BOUUh4Z2xOcHQ5MVc2eTB4Rzh1VFI0YjFJVi9TL2doTzJIbzVTOWVjZ3RaV3YzT1JXZzhneGcKak1wYW1YMXZEakVBSEsrZVZhWVpBb0lCQVFEckZsSEJSaExYZnoxUDF0VWFSZXFVdVBUd1dqMFhkclp3K1RZaQpLdW9vYU9hVmxyMnFhaE4yZnlvd2U3NURpejJXUjFtV1pxZmFMUnhSU1FtT24rSzR2TzY5VlV3aHE0dXVvdUcxCkxacWlGT0w0V2J6SGZQeUdPa1BCbzZnQlJHOEtXU045bXpCSFdiOEtOaHBWY1JLMWN2cmJNanVsY2xDN21jTG4KcU1LeG9FR2M3MDR2TUVWZEt5RWxjVHl1NjF4SlpHUTVCU0ZmbXV4TjVYS3FQdEliaTZzRCtHeVJ1d1AvblRtTApCRjRmdzg4b0Y0cnIrK2c2U1hMalBRbXp5VVFqcDZsK2FiRngwdCtZUmphQmF3RGFieDUzaHd0TkdxV0t5NWl3CldFMUhudEtoZ2RwVXEwRitvVysyQkduU2k0eUhocHh1V0ZTVDFWbmFEYU1veUVpM0FvSUJBUURiVWplZmJLT0YKd2M1Z3g3MUtybFBheG05NmNCRGMzVWNLY2lLZjFzSzh0dWtlK0RLeEZUWWxuNWZjTjZKZUhlamdaSFBHTjJOKwprRTBUbHFTbDhEK2U3ZEszRUF0UkdmRW5DWEZ2U2dSYXBNVHZtbEtIVmFLbUsxbHBNT0ZrTER1VWhkTVorM01RCnZ4UzlxR3lTUkliSHFtSWEyb3NnV3p6RUppU1FYUTNBSFNsQVNEajArUE4xZkJJbUNlWjN3NVhtOHg3TXliOGQKV3N3QzNWYzh4K0k2SUR2ZksvQUpyMVJtUHJRdEo1RmJtTWJmbXlpRzY4dXI2YnozOTNxWkl6Mkh2cmZUckVVeAp0aEVLc2hiMkwyTlIybjB6cndGZTFQL3d4WXB3aU1xY001aWRoNUFKU0pKYytIZndoTlFKSndpZXpVcDdkdEhsCm82UlpETGgrc3QvYkFvSUJBUURlbldnVnliR1Y5NXVydXhoWnBOT2F1ZnJZajl0Q0dOeHk1c3RvVjVNZGFSalIKbHNsTVV0N1RXMkFzUVVITzNGQVp1cjhQT1c3Qm4rNHMyeWo5c2xJVDluVnRQUlMyM2FlTVhCbTRZOU1Hb1JpOQpiTnpqVzJ3Vi81NmlBOFBZVDJHSFB4bm9tOGlBQ0hCcnp3Zm8yUWFLWkxOQVZyVTgycCt3eHM1V2FPelZINWlDCmIrcHRyUnhDT2RGTmo0bVRhclVkbXI5OWIycEhDb0d5MGhCZlB0WGYxOGErSDhWUHl2VGlYWlJjSEdxMVVjc2QKZ0EzQm9lNHJJOXB6YlIxKzdweGoxeVVkM0NCNFZPUC9ZcDNDdC8wNjIvYlI0VVBHWS9GWEdhMGhjTHpka1h5TQpERmNmem54YTJhemJoQnBoa2x3bENrZWdCbTB4QU52eExQRFFjYmpKQW9JQkFBYUZ1bXUrVWxxZStlRTg3Mjh5CklnSkdsbmdncncreWNQTnBrNGdIWGdFNHUxUVZjNFI2cWdLU2JYUzZIdFVIdEs0L3JvVzlqZkxzbmE0d2M3TVAKb3dTNTgrTkgwZDVXbENwaUNJS1R3KzlpS1EwckhMc3ptOE92VkZoaGdNSjI0a3EzZENDNHJxSlgzR2tMZ2pjVApYS0xCZVd0dEJtZ3U3bTZ5NTdGM1l2cE1vN1pxcjI3eG5HUHBEYTBkN0VHeGlscTA3anlPMzJVakZ5QWFyd1o2CnQwcWRQTXFWUnUweXpxSy95S0h6SkVxQ0Q2QUV3RDdLOG1LcGI5bzcvT1JpL09oMWpvZjMvcExNNUZMYUd2NjEKU0JIZUdvMFg3a1dBdGgvVjhCa0txR1BlMFBJV1h2MDNzRXFkaFF1ZEY2VXlDUHFZQmxnSG41Umh1bmgvZlZrZgpQck1DZ2dFQWIzd1hEUTRNTjFrUnFpNEIrSW51a0k3Q2MyYzVUSTZUVXBxNkpXWGZzcWtabHN1eFVTK21yTUFWCmVtMEdMQmFlV0NEQzJvMnp2NFZTUFZnV3N4SGdCKzNLOXV6K0dranpmN1F4MjFuSmZOUzJjcWFlMTdtVVBvUE8KcU41UmxpT1VzME9mZU5yRk1SQStpYUFoK2VtYUc4NjdYT3RQc3MzUmExVUVBZlY0dmdLWHFxN21BYi8rcXRUcwowNzdYdUEzN1IyQ2FMdXNXNFdEVFNneElnQkZ5L1BIQXJvb3MyZjluSHVZWmtkWlF3VTFrb2hoVXI5ZEdQblg4CkNFdUlqYjE5U1pqKzl5T25sWlNGNlpHZ203d3pNL254S3FYMFg5NkJ6MUxwT1FSdXJUZmhkNTFyYUJFMk85L1YKSE03ZGNUQ3FmL3lrSWVKTzM3WnMzNFF4T1plOUNRPT0KLS0tLS1FTkQgUFJJVkFURSBLRVktLS0tLQo= - - \ No newline at end of file diff --git a/python/examples/example.py b/python/examples/example.py index 70f2e59..8cd0c0c 100644 --- a/python/examples/example.py +++ b/python/examples/example.py @@ -1,4 +1,5 @@ -from opnsense_helper.classes import Opnsense_Helper, Vlan, Dhcpd, Interface +from opnsense_helper.opnsense_helper import Opnsense_Helper +from opnsense_helper.config_manager.config_manager import Vlan, Dhcpd, Interface temp_path="./config.xml" host= "192.168.1.103" def backend(): @@ -27,14 +28,19 @@ def backend(): "passw":"opnsense", } - helper=Opnsense_Helper(host=host,ssh_auth=auth,temp_path=temp_path, init=True) - helper.set("interfaces",interfaces) - helper.set("dhcpd",dhcp) - helper.set("vlans",vlans) - helper.save(temp_path) - # helper.remove_items() + helper=Opnsense_Helper(host=host,ssh_auth=auth,temp_path=temp_path, init_config_manager=True) + helper.config_manager.set("interfaces",interfaces) + helper.config_manager.set("dhcpd",dhcp) + helper.config_manager.set("vlans",vlans) + helper.config_manager.save(temp_path) + helper.scripts.system.run("status") + helper.scripts.routes.run("show_routes") + + helper.commands.pluginctl.run("ipv4") + helper.commands.pluginctl.run("service", "dhcpd status") + helper.commands.pluginctl.run("config", "dhcp") def using_api(): diff --git a/python/opnsense_helper/README.md b/python/opnsense_helper/README.md deleted file mode 100644 index 03984e1..0000000 --- a/python/opnsense_helper/README.md +++ /dev/null @@ -1,111 +0,0 @@ -# opnsense-helper -assign lan interfaces, create vlans, vlan-interfaces and setup dhcp in a single script using the opnsense backend. -## install -## pip -```bash -pip install opnsense-helper -``` -## usage -- before you create Vlan-Interfaces you need to add them first using the add_vlan method -- rn i try to find out how to reconfigure the vlans using the backend, but this is on todo list - -```python -ffrom opnsense_helper.classes import Opnsense_Helper -output="./config.xml" -conf_path="/conf/config.xml" -host= "192.168.1.103" -vlans=[ -{"id":"vlan1",'parent': 'vtnet1', 'tag': '1', 'pcp': '0', 'proto': None, 'descr': 'vlan1', 'vlanif': 'vlan0.1'}, -{"id":"vlan2",'parent': 'vtnet1', 'tag': '2', 'pcp': '0', 'proto': None, 'descr': 'vlan2', 'vlanif': 'vlan0.2'}, -{"id":"vlan3",'parent': 'vtnet1', 'tag': '3', 'pcp': '0', 'proto': None, 'descr': 'vlan3', 'vlanif': 'vlan0.3'} - -] -dhcp=[ -{"id":"opt2",'enable': '1', 'ddnsdomainalgorithm': 'hmac-md', "range":{'from': '200.0.3.10', '_to': '200.0.3.100'}}, -{"id":"opt3",'enable': '1', 'ddnsdomainalgorithm': 'hmac-md', "range":{'from': '200.0.4.10', '_to': '200.0.4.100'}} -] -interfaces=[ -{"id":"opt1",'descr': 'router', 'enable': '1', 'ipaddr': None, 'subnet': None, 'type': None, 'virtual': None, 'spoofmac': '00:00:00:00:02:01',"interface":"vtnet1"}, -{"id":"opt2",'descr': 'vlan1', 'enable': '1', 'ipaddr': '200.0.3.1', 'subnet': '24', 'type': None, 'virtual': None, 'spoofmac': '00:00:00:00:00:01',"interface":"vlan0.1"}, -{"id":"opt3",'descr': 'vlan2', 'enable': '1', 'ipaddr': '200.0.4.1', 'subnet': '24', 'type': None, 'virtual': None, 'spoofmac': '00:00:00:00:00:02', "interface":"vlan0.2"}, -{"id":"opt4",'descr': 'vlan3', 'enable': '1', 'ipaddr': '200.0.5.1', 'subnet': '24', 'type': None, 'virtual': None, 'spoofmac': '00:00:00:00:00:03', "interface":"vlan0.3"} - -] -auth={ -"user":"root", -"passw":"opnsense", -} -helper=Opnsense_Helper(host=host,ssh_auth=auth,filepath=output, verbose=False) -#helper.set_vlans(vlans) -helper.get_conf(conf_path) -helper.initialize() -helper.add_Items("interfaces",interfaces) -helper.add_Items("dhcpd",dhcp) -helper.add_Items("vlans",vlans) -helper.save(output) -helper.put_file(output,conf_path) -helper.close_con() -``` - -### Frontend Api -```python -def using_api(): - vlans_api=[ - {'if': 'vtnet1', 'tag': '1', 'pcp': '0', 'proto': None, 'descr': 'vlan1', 'vlanif': 'vlan0.1'}, - {'if': 'vtnet1', 'tag': '2', 'pcp': '0', 'proto': None, 'descr': 'vlan2', 'vlanif': 'vlan0.2'} - ] - api_auth={ - "api_key" :'ejl4fIU9yfNk+gaQmPk/rqIa15f1yX1snIKgcIEl2QNoJwhbekraWIE0ANRYceh9hey5IFGzlf3da4yJ', - "api_secret":'5JVVGoatPbaAA+FozLDQY92/T6sRlmKD1+aRNl/YI8KA9/0TNiTDboLveqvd9FU8wFeDo3D3DY5wrUtF', - "ssl": True, - "verify": False - } - helper=Opnsense_Helper(host=host,api_auth=api_auth,filepath=output, verbose=False) - helper.vlans_api(vlans_api,"add") -``` -### Variables -#### Opnsense_Helper -(host=host,ssh_auth=auth,filepath=output, verbose=False) -| var | type | description | -| --- | --- | --- | -| host| str | ip or hostname | -| auth | dict {user: str, passw: str} | SSH authentication dictionary | - - - -#### Module Args -| var | type | elements | -| --- | --- | --- | -| vlans | list[dict] | {id: str, parent: str, tag: int, pcp: str, proto: str } | None, descr: str, vlanif: str} | -| interfaces | list[dict] | {id: str, descr: str, enable: int, ipaddr: str, subnet: str, type: str, virtual: bool, spoofmac: str, interface: str} | -| dhcp | list[dict] | {id: str, enable: str, ddnsdomainalgorithm: str, range: {from: str, _to: str}} | - -## roadmap -- add routes -- firewall roules - -### contribute -- clone, or fork `git@github.com:the-pod-shop/opnsense-helper.git` -- build when made changes -- make sure to use the right user -```bash -cd python -python setup.py bdist_wheel \ -&& pip install --upgrade . \ -&& python3 -m pip install --upgrade build #--force -``` -- you can also use the build.sh script -- create pull request - -## motivation -- i couldnt find a single repo/collection/terraform provider/api that let me assign and enable lan interfaces -- i decided to create one mself -- opnsense api does not let me do it, it just replies with: controller not found -- but /conf/config.xml has the answer. -- however for phisical interfaces its the god damn conf.rc -- my opnsense runs in a vm, so it really doesnt matter for me -- i just add the interfaces via libvirt and all i need to do is to enable them, given the /conf/config.xml method -xml has the answer. -- however for phisical interfaces its the god damn conf.rc -- my opnsense runs in a vm, so it really doesnt matter for me -- i just add the interfaces via libvirt and all i need to do is to enable them, given the /conf/config.xml method diff --git a/python/opnsense_helper/commands/commands.py b/python/opnsense_helper/commands/commands.py new file mode 100644 index 0000000..91fd6b8 --- /dev/null +++ b/python/opnsense_helper/commands/commands.py @@ -0,0 +1,92 @@ +from opnsense_helper.utils.exec_class import Exec_Class +class Commands(): + def __init__(self,base): + + self.reconfigure=reconfigure(base) + self.configctl=configctl(base) + self.pluginctl=pluginctl(base) +class reconfigure(Exec_Class): + def __init__(self, base): + print(base.__dict__) + super(Exec_Class).__init__() + if base is not None: + self.__dict__.update(base.__dict__) + + self.commands={ + "vlans": {"command":'/usr/local/opnsense/scripts/interfaces/reconfigure_vlans.php',"argument":None,"flags":[]}, + "interfaces": {"command":"configctl interface reconfigure", "flags":[] } +} + +class configctl(Exec_Class): + def __init__(self, base): + super(Exec_Class).__init__() + if base is not None: + self.__dict__.update(base.__dict__) + + self.commands={ + # optional arguments: + # -h, --help show this help message and exit + # -m execute multiple arguments at once + # -e use as event handler, execute command on receiving input + # -d detach the execution of the command and return immediately + # -q run quietly by muting standard output + # -w W wait specified amount of seconds for socket to become available + # -t T threshold between events, wait this interval before executing commands, combine input into single events + "auth":{"command":"configctl auth", "argument":None,"flags":[]}, + "captiveportal":{"command":"configctl captiveportal", "argument":None,"flags":[]}, + "configd":{"command":"configctl configd", "argument":None,"flags":[]}, + "cron":{"command":"configctl cron", "argument":None,"flags":[]}, + "dhcpd":{"command":"configctl dhcpd", "argument":None,"flags":[]}, + "dhcpd6":{"command":"configctl dhcpd6", "argument":None,"flags":[]}, + "dns":{"command":"configctl dns", "argument":None,"flags":[]}, + "filter":{"command":"configctl filter", "argument":None,"flags":[]}, + "firmware":{"command":"configctl firmware", "argument":None,"flags":[]}, + "health":{"command":"configctl health", "argument":None,"flags":[]}, + "ids":{"command":"configctl ids", "argument":None,"flags":[]}, + "interface":{"command":"configctl interface", "argument":None,"flags":[]}, + "ipfw":{"command":"configctl ipfw", "argument":None,"flags":[]}, + "ipsec":{"command":"configctl ipsec", "argument":None,"flags":[]}, + "kea":{"command":"configctl kea", "argument":None,"flags":[]}, + "monit":{"command":"configctl monit", "argument":None,"flags":[]}, + "netflow":{"command":"configctl netflow", "argument":None,"flags":[]}, + "openssh":{"command":"configctl openssh", "argument":None,"flags":[]}, + "openvpn":{"command":"configctl openvpn", "argument":None,"flags":[]}, + "syslog":{"command":"configctl syslog", "argument":None,"flags":[]}, + "system":{"command":"configctl system", "argument":None,"flags":[]}, + "template":{"command":"configctl template", "argument":None,"flags":[]}, + "unbound":{"command":"configctl unbound", "argument":None,"flags":[]}, + "webgui":{"command":"configctl webgui", "argument":None,"flags":[]}, + "wireguard":{"command":"configctl wireguard", "argument":None,"flags":[]}, + "zfs":{"command":"configctl zfs", "argument":None,"flags":[]}, + } +class pluginctl(Exec_Class): + def __init__(self, base): + super(Exec_Class).__init__() + if base is not None: + self.__dict__.update(base.__dict__) + + self.commands={ + # -4 IPv4 address mode, return primary address of interface + # -c configure mode (default), executes plugin [_configure] hook + # -D ifconfig mode, lists available devices + # -d device mode, lists registered devices + # -f flush config property (raw, e.g. system.firmware.plugins) + # -g get config property (raw, e.g. system.firmware.plugins) + # -h show this help text and exit + # -I information mode, lists registered device statistics + # -i invoke dynamic interface registration + # -r run mode (e.g. command) + # -S service metadata dump + # -s service mode (e.g. myservice restart) + "ipv4":{"argument":None,"command":"pluginctl -4"}, + "config":{"argument":None, "flags":[],"command":"pluginctl -c"}, + "ifconfig":{"argument":None,"command":"pluginctl -D"}, + "device_info":{"argument":None,"command":"pluginctl -d"}, + "flush":{"argument":None,"command":"pluginctl -f"}, + "get":{"argument":None, "flags":[],"command":"pluginctl -g"}, + "info":{"argument":None, "flags":[],"command":"pluginctl -I" }, + "if_reg":{"argument":None,"command":"pluginctl -i"}, + "run":{"argument":None,"command":"pluginctl -r"}, + "service_dump":{"argument":None,"command":"pluginctl -S"}, + "service":{"argument":None, "flags":[],"command":"pluginctl -s"} # stop start resta, rt +} \ No newline at end of file diff --git a/python/opnsense_helper/classes.py b/python/opnsense_helper/config_manager/config_manager.py similarity index 89% rename from python/opnsense_helper/classes.py rename to python/opnsense_helper/config_manager/config_manager.py index 83b1271..2c962a0 100644 --- a/python/opnsense_helper/classes.py +++ b/python/opnsense_helper/config_manager/config_manager.py @@ -1,14 +1,7 @@ -import paramiko -from lxml import etree import xml.etree.ElementTree as ET -import logging from xmldiff import main, formatting -from opnsense_helper.utils import parseChild, update_xml_file, aliases, reconfigure_vlans -from opnsense_helper.frontend_utils import api_get,api_post - -{"id":"router",'descr': 'router', 'enable': '1', 'ipaddr': '200.1.0.1','spoofmac': '00:00:00:01:00:01',"interface":"vtnet1"}, - +from opnsense_helper.utils.utils import parseChild, update_xml_file class Interface: """ ***Creates a Interface object.*** @@ -110,37 +103,16 @@ def initialize(self, parent): self._range["_from"]=self._range["_from"].text self._range["_to"]=self._range["_to"].text -class Opnsense_Helper(): - def __init__(self, host=None, ssh_auth=None, api_auth=None, conf_path="/conf/config.xml", temp_path="./config.xml", verbose=False, init=True): - if(verbose): - self.logging=logging.basicConfig(level=logging.DEBUG) - self.objects={ - "vlans":{}, - "dhcpd":{}, - "interfaces":{} - } - self.url = host - self.conf_path=conf_path - - if(ssh_auth!=None): - self.temp_path= temp_path - self.ssh = paramiko.SSHClient() - self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - self.ssh.connect(host, username=ssh_auth["user"], password=ssh_auth["passw"]) - self.sftp = self.ssh.open_sftp() - - if(api_auth!=None): - self.api_key = api_auth["api_key"] - self.api_secret = api_auth["api_secret"] - self.ssl=api_auth["ssl"] - self.verify=api_auth["verify"] - self.host=host +class Config_Manager(): + + def __init__(self, base, init): + super().__init__() + if base is not None: + self.__dict__.update(base.__dict__) if init: self.get_conf() self.initialize() - def close(self): - self.close_con() def get_dif(self): @@ -185,6 +157,7 @@ def save(self,output,put=True): print("saving ifs") update_xml_file(self.objects["interfaces"],self.root,"interfaces") if len(self.objects["vlans"]) > 0: + print("saving vlans") update_xml_file(self.objects["vlans"],self.root,"vlans") with open(output, 'w') as f: @@ -241,7 +214,9 @@ def get_all(self,element): def close_con(self): self.sftp.close() self.ssh.close() - + # def reconfigure(self,type): + + def put_file(self, _from=None,_to=None): """ Apply the configuration. @@ -260,8 +235,14 @@ def put_file(self, _from=None,_to=None): b=_to if _to is not None else self.conf_path self.sftp.put(a, b) if len(self.objects["vlans"]) > 0: - reconfigure_vlans(self) - + self.commands.reconfigure.run("vlans") + if len(self.objects["interfaces"]) > 0: + for interface,values in self.objects["interfaces"].items(): + # check if interface is no vlan interface, but a phyInterface + phy=values["interface"] + if phy!= None and "vtnet" in phy: + if values["enable"] == "1" and len(phy)>0: + self.commands.reconfigure.run("interfaces", values["interface"]) def remove_items(self,type, items, init=True, apply=True): if init: self.get_conf() diff --git a/python/opnsense_helper/opnsense_helper.py b/python/opnsense_helper/opnsense_helper.py new file mode 100644 index 0000000..999a3e4 --- /dev/null +++ b/python/opnsense_helper/opnsense_helper.py @@ -0,0 +1,9 @@ + +from opnsense_helper.utils.baseclass import Base_Class + +class Opnsense_Helper(): + def __init__(self, host=None, ssh_auth=None, api_auth=None, conf_path="/conf/config.xml", temp_path="./config.xml", verbose=False, init_config_manager=True): + base_class = Base_Class(host, ssh_auth, api_auth, conf_path, temp_path, verbose) + self.config_manager=base_class.config_manager + self.commands=base_class.commands + self.scripts=base_class.scripts diff --git a/python/opnsense_helper/scripts/scripts.py b/python/opnsense_helper/scripts/scripts.py new file mode 100644 index 0000000..7adc0b4 --- /dev/null +++ b/python/opnsense_helper/scripts/scripts.py @@ -0,0 +1,357 @@ + +from opnsense_helper.utils.exec_class import Exec_Class +Exec_Class +scripts_folder="/usr/local/opnsense/scripts/" + +class Scripts(): + def __init__(self,base): + self.unbound=unbound(base) + self.system=system(base) + self.syslog=syslog(base) + self.suricata=suricata(base) + self.shell=shell(base) + self.shaper=shaper(base) + self.routes=routes(base) + self.openvpn=openvpn(base) + self.openssh=openssh(base) + self.netflow=netflow(base) + self.ipsec=ipsec(base) + self.interfaces=interfaces(base) + self.health=health(base) + self.firmware=firmware(base) + self.filter=filter(base) + self.dns=dns(base) + self.dhcp=dhcp(base) + self.auth=auth(base) + self.Wireguard=Wireguard(base) + +class unbound(Exec_Class): + def __init__(self, base): + super(Exec_Class).__init__() + if base is not None: + self.__dict__.update(base.__dict__) + + self.commands={ + "wrapper":{ "command":scripts_folder+"ubound/wrapper.py*","flags":[]}, + "stats":{ "command":scripts_folder+"ubound/stats.py*","flags":[]}, + "start":{ "command":scripts_folder+"ubound/start.sh*","flags":[]}, + "restore_db":{ "command":scripts_folder+"ubound/restore_db.py*","flags":[]}, + "logger":{ "command":scripts_folder+"ubound/logger.py*","flags":[]}, + "check":{ "command":scripts_folder+"ubound/check.sh*","flags":[]}, + "cache":{ "command":scripts_folder+"ubound/cache.sh*","flags":[]}, + "blocklists":{ "command":scripts_folder+"ubound/blocklists.py*","flags":[]}, + "blocklists":{ "command":scripts_folder+"ubound/blocklists/","flags":[]}, + } +class system(Exec_Class): + def __init__(self, base): + super(Exec_Class).__init__() + if base is not None: + self.__dict__.update(base.__dict__) + + self.commands={ + "trigger_config_changed_events":{"command":scripts_folder+"system/trigger_config_changed_events.py*", "flags":[]}, + "temperature":{"command":scripts_folder+"system/temperature.sh*", "flags":[]}, + "sysctl":{"command":scripts_folder+"system/sysctl.py*", "flags":[]}, + "status":{"command":scripts_folder+"system/status.php*", "flags":[]}, + "ssl_ciphers":{"command":scripts_folder+"system/ssl_ciphers.py*", "flags":[]}, + "rrd_pfstate_info":{"command":scripts_folder+"system/rrd_pfstate_info.py*", "flags":[]}, + "rfc5246_cipher_suites":{"command":scripts_folder+"system/rfc5246_cipher_suites.csv", "flags":[]}, + "remote_backup":{"command":scripts_folder+"system/remote_backup.php*", "flags":[]}, + "nameservers":{"command":scripts_folder+"system/nameservers.php*", "flags":[]}, + "certctl":{"command":scripts_folder+"system/certctl.py*", "flags":[]}, + "activity":{"command":scripts_folder+"system/activity.py*", "flags":[]}, + } +class syslog(Exec_Class): + def __init__(self, base): + super(Exec_Class).__init__() + if base is not None: + self.__dict__.update(base.__dict__) + + self.commands={ + "queryLog":{"command":scripts_folder+"syslog/ queryLog.py*", "flags":[]}, + "logformats":{"command":scripts_folder+"syslog/ logformats/", "flags":[]}, + "log_archive":{"command":scripts_folder+"syslog/ log_archive*", "flags":[]}, + "lockout_handler":{"command":scripts_folder+"syslog/ lockout_handler*", "flags":[]}, + "list_applications":{"command":scripts_folder+"syslog/ list_applications.php*", "flags":[]}, + "generate_certs":{"command":scripts_folder+"syslog/ generate_certs*", "flags":[]}, + "clearlog":{"command":scripts_folder+"syslog/ clearlog.php*", "flags":[]}, + } +class suricata(Exec_Class): + def __init__(self, base): + super(Exec_Class).__init__() + if base is not None: + self.__dict__.update(base.__dict__) + + self.commands={ + "setup":{"command":scripts_folder+"suricata/setup.sh*", "flags":[]}, + "rule-updater":{"command":scripts_folder+"suricata/rule-updater.py*", "flags":[]}, + "queryInstalledRules":{"command":scripts_folder+"suricata/queryInstalledRules.py*", "flags":[]}, + "queryAlertLog":{"command":scripts_folder+"suricata/queryAlertLog.py*", "flags":[]}, + "metadata":{"command":scripts_folder+"suricata/metadata/", "flags":[]}, + "listRuleMetadata":{"command":scripts_folder+"suricata/listRuleMetadata.py*", "flags":[]}, + "listInstallableRulesets":{"command":scripts_folder+"suricata/listInstallableRulesets.py*", "flags":[]}, + "listAlertLogs":{"command":scripts_folder+"suricata/listAlertLogs.py*", "flags":[]}, + "lib":{"command":scripts_folder+"suricata/lib/", "flags":[]}, + "installRules":{"command":scripts_folder+"suricata/installRules.py*", "flags":[]}, + "dropAlertLog":{"command":scripts_folder+"suricata/dropAlertLog.py*", "flags":[]}, + "__init__":{"command":scripts_folder+"suricata/__init__.py*", "flags":[]}, + } +class shell(Exec_Class): + def __init__(self, base): + super(Exec_Class).__init__() + if base is not None: + self.__dict__.update(base.__dict__) + + self.commands={ + "setports":{"command":scripts_folder+"shell/setports.php*","flags":[]}, + "setaddr":{"command":scripts_folder+"shell/setaddr.php*","flags":[]}, + "restore":{"command":scripts_folder+"shell/restore.sh*","flags":[]}, + "reboot":{"command":scripts_folder+"shell/reboot.php*","flags":[]}, + "ping":{"command":scripts_folder+"shell/ping.php*","flags":[]}, + "password":{"command":scripts_folder+"shell/password.php*","flags":[]}, + "halt":{"command":scripts_folder+"shell/halt.php*","flags":[]}, + "firmware":{"command":scripts_folder+"shell/firmware.sh*","flags":[]}, + "defaults":{"command":scripts_folder+"shell/defaults.php*","flags":[]}, + "banner":{"command":scripts_folder+"shell/banner.php*","flags":[]}, + } +class shaper(Exec_Class): + def __init__(self, base): + super(Exec_Class).__init__() + if base is not None: + self.__dict__.update(base.__dict__) + + self.commands={ + "update_tables":{"command":scripts_folder+"shaper/update_tables*","flags":[]}, + "lib":{"command":scripts_folder+"shaper/lib/","flags":[]}, + "dummynet_stats":{"command":scripts_folder+"shaper/dummynet_stats.py*","flags":[]}, + } +class routes(Exec_Class): + def __init__(self, base): + super(Exec_Class).__init__() + if base is not None: + self.__dict__.update(base.__dict__) + + self.commands={ + "show_routes":{"command":scripts_folder+"routes/show_routes.py*","flags":[]}, + "gateways":{"command":scripts_folder+"routes/gateways.php*","flags":[]}, + "gateway_watcher":{"command":scripts_folder+"routes/gateway_watcher.php*","flags":[]}, + "gateway_status":{"command":scripts_folder+"routes/gateway_status.php*","flags":[]}, + "del_route":{"command":scripts_folder+"routes/del_route.py*","flags":[]}, + } +class openvpn(Exec_Class): + def __init__(self, base): + super(Exec_Class).__init__() + if base is not None: + self.__dict__.update(base.__dict__) + + self.commands={ + "user_pass_verify":{"command":scripts_folder+"openvpn/user_pass_verify.php*", "flags":[]}, + "tls_verify":{"command":scripts_folder+"openvpn/tls_verify.php*", "flags":[]}, + "ovpn_status":{"command":scripts_folder+"openvpn/ovpn_status.py*", "flags":[]}, + "ovpn_service_control":{"command":scripts_folder+"openvpn/ovpn_service_control.php*", "flags":[]}, + "ovpn_event":{"command":scripts_folder+"openvpn/ovpn_event.py*", "flags":[]}, + "kill_session":{"command":scripts_folder+"openvpn/kill_session.py*", "flags":[]}, + "client_disconnect":{"command":scripts_folder+"openvpn/client_disconnect.sh*", "flags":[]}, + "client_connect":{"command":scripts_folder+"openvpn/client_connect.php*", "flags":[]}, + } +class openssh(Exec_Class): + def __init__(self, base): + super(Exec_Class).__init__() + if base is not None: + self.__dict__.update(base.__dict__) + + self.commands={ + "ssh_query":{"command":scripts_folder+"openssh/ssh_query.py*","flags":[]} + } + +class netflow(Exec_Class): + def __init__(self, base): + super(Exec_Class).__init__() + if base is not None: + self.__dict__.update(base.__dict__) + + self.commands={ + "lib":{"command":scripts_folder+"netflow/lib/","flags":[]}, + "get_top_usage":{"command":scripts_folder+"netflow/get_top_usage.py*","flags":[]}, + "get_timeseries":{"command":scripts_folder+"netflow/get_timeseries.py*","flags":[]}, + "flush_all":{"command":scripts_folder+"netflow/flush_all.sh*","flags":[]}, + "flowd_aggregate_metadata":{"command":scripts_folder+"netflow/flowd_aggregate_metadata.py*","flags":[]}, + "flowd_aggregate":{"command":scripts_folder+"netflow/flowd_aggregate.py*","flags":[]}, + "flowctl_stats":{"command":scripts_folder+"netflow/flowctl_stats.py*","flags":[]}, + "export_details":{"command":scripts_folder+"netflow/export_details.py*","flags":[]}, + "dump_log":{"command":scripts_folder+"netflow/dump_log.py*","flags":[]}, + } +class ipsec(Exec_Class): + def __init__(self, base): + super(Exec_Class).__init__() + if base is not None: + self.__dict__.update(base.__dict__) + + self.commands={ + "updown_event":{"command":scripts_folder+"ipsec/updown_event.py*","flags":[]}, + "spddelete":{"command":scripts_folder+"ipsec/spddelete.py*","flags":[]}, + "saddelete":{"command":scripts_folder+"ipsec/saddelete.py*","flags":[]}, + "list_status":{"command":scripts_folder+"ipsec/list_status.py*","flags":[]}, + "list_spd":{"command":scripts_folder+"ipsec/list_spd.py*","flags":[]}, + "list_sad":{"command":scripts_folder+"ipsec/list_sad.py*","flags":[]}, + "list_leases":{"command":scripts_folder+"ipsec/list_leases.py*","flags":[]}, + "lib":{"command":scripts_folder+"ipsec/lib/","flags":[]}, + "get_legacy_vti.":{"command":scripts_folder+"ipsec/get_legacy_vti.php*","flags":[]}, + "disconnect":{"command":scripts_folder+"ipsec/disconnect.py*","flags":[]}, + "connect":{"command":scripts_folder+"ipsec/connect.py*","flags":[]}, + } +class interfaces(Exec_Class): + def __init__(self, base): + super(Exec_Class).__init__() + if base is not None: + self.__dict__.update(base.__dict__) + + self.commands={ + "traffic_top":{"command": scripts_folder+"interfaces/traffic_top.py*", "flags":[]}, + "traffic_stats":{"command": scripts_folder+"interfaces/traffic_stats.php*", "flags":[]}, + "traceroute":{"command": scripts_folder+"interfaces/traceroute.py*", "flags":[]}, + "rtsold_resolvconf":{"command": scripts_folder+"interfaces/rtsold_resolvconf.sh*", "flags":[]}, + "reconfigure_vlans":{"command": scripts_folder+"interfaces/reconfigure_vlans.php*", "flags":[]}, + "reconfigure_vips":{"command": scripts_folder+"interfaces/reconfigure_vips.php*", "flags":[]}, + "reconfigure_neighbors":{"command": scripts_folder+"interfaces/reconfigure_neighbors.php*", "flags":[]}, + "reconfigure_laggs":{"command": scripts_folder+"interfaces/reconfigure_laggs.php*", "flags":[]}, + "ppp-uptime":{"command": scripts_folder+"interfaces/ppp-uptime.sh*", "flags":[]}, + "ppp-rename":{"command": scripts_folder+"interfaces/ppp-rename.sh*", "flags":[]}, + "ppp-linkup":{"command": scripts_folder+"interfaces/ppp-linkup.sh*", "flags":[]}, + "ppp-linkdown":{"command": scripts_folder+"interfaces/ppp-linkdown.sh*", "flags":[]}, + "portprobe":{"command": scripts_folder+"interfaces/portprobe.py*", "flags":[]}, + "ping":{"command": scripts_folder+"interfaces/ping.py*", "flags":[]}, + "mpd":{"command": scripts_folder+"interfaces/mpd.script*", "flags":[]}, + "macinfo":{"command": scripts_folder+"interfaces/macinfo.py*", "flags":[]}, + "list_sockstat":{"command": scripts_folder+"interfaces/list_sockstat.py*", "flags":[]}, + "list_ndp":{"command": scripts_folder+"interfaces/list_ndp.py*", "flags":[]}, + "list_macdb":{"command": scripts_folder+"interfaces/list_macdb.py*", "flags":[]}, + "list_arp":{"command": scripts_folder+"interfaces/list_arp.py*", "flags":[]}, + "ifctl":{"command": scripts_folder+"interfaces/ifctl.sh*", "flags":[]}, + "dhclient":{"command": scripts_folder+"interfaces/dhclient-script*", "flags":[]}, + "carp_set_status":{"command": scripts_folder+"interfaces/carp_set_status.php*", "flags":[]}, + "carp_global_status":{"command": scripts_folder+"interfaces/carp_global_status.php*", "flags":[]}, + "capture":{"command": scripts_folder+"interfaces/capture.py*", "flags":[]}, + } +class health(Exec_Class): + def __init__(self, base): + super(Exec_Class).__init__() + if base is not None: + self.__dict__.update(base.__dict__) + + self.commands={ + "listReports":{"command":scripts_folder+"health/listReports.py*","flags":[]}, + "flush_rrd":{"command":scripts_folder+"health/flush_rrd.py*","flags":[]}, + "fetchData":{"command":scripts_folder+"health/fetchData.py*","flags":[]}, + } +class firmware(Exec_Class): + def __init__(self, base): + super(Exec_Class).__init__() + if base is not None: + self.__dict__.update(base.__dict__) + + self.commands={ + "upgrade":{"command":scripts_folder+"firmware/upgrade.sh*","flags":[]}, + "update":{"command":scripts_folder+"firmware/update.sh*","flags":[]}, + "unlock":{"command":scripts_folder+"firmware/unlock.sh*","flags":[]}, + "sync.subr":{"command":scripts_folder+"firmware/sync.subr.sh*","flags":[]}, + "sync":{"command":scripts_folder+"firmware/sync.sh*","flags":[]}, + "security":{"command":scripts_folder+"firmware/security.sh*","flags":[]}, + "running":{"command":scripts_folder+"firmware/running.sh*","flags":[]}, + "resync":{"command":scripts_folder+"firmware/resync.sh*","flags":[]}, + "remove":{"command":scripts_folder+"firmware/remove.sh*","flags":[]}, + "reinstall":{"command":scripts_folder+"firmware/reinstall.sh*","flags":[]}, + "register.":{"command":scripts_folder+"firmware/register.php*","flags":[]}, + "reboot":{"command":scripts_folder+"firmware/reboot.sh*","flags":[]}, + "read":{"command":scripts_folder+"firmware/read.sh*","flags":[]}, + "query":{"command":scripts_folder+"firmware/query.sh*","flags":[]}, + "product.":{"command":scripts_folder+"firmware/product.php*","flags":[]}, + "plugin":{"command":scripts_folder+"firmware/plugin.sh*","flags":[]}, + "lock":{"command":scripts_folder+"firmware/lock.sh*","flags":[]}, + "license":{"command":scripts_folder+"firmware/license.sh*","flags":[]}, + "launcher":{"command":scripts_folder+"firmware/launcher.sh*","flags":[]}, + "latest.":{"command":scripts_folder+"firmware/latest.php*","flags":[]}, + "install":{"command":scripts_folder+"firmware/install.sh*","flags":[]}, + "health":{"command":scripts_folder+"firmware/health.sh*","flags":[]}, + "connection":{"command":scripts_folder+"firmware/connection.sh*","flags":[]}, + "check":{"command":scripts_folder+"firmware/check.sh*","flags":[]}, + "changelog":{"command":scripts_folder+"firmware/changelog.sh*","flags":[]}, + } +class filter(Exec_Class): + def __init__(self, base): + super(Exec_Class).__init__() + if base is not None: + self.__dict__.update(base.__dict__) + + self.commands={ + "update_tables":{"commands":scripts_folder+"filter/update_tables.py*","flags":[]}, + "update_bogons":{"commands":scripts_folder+"filter/update_bogons.sh*","flags":[]}, + "run_unittests":{"commands":scripts_folder+"filter/run_unittests.py*","flags":[]}, + "rule_stats":{"commands":scripts_folder+"filter/rule_stats.py*","flags":[]}, + "rollback_timer.":{"commands":scripts_folder+"filter/rollback_timer.php*","flags":[]}, + "rollback_cancel.":{"commands":scripts_folder+"filter/rollback_cancel.php*","flags":[]}, + "read_log":{"commands":scripts_folder+"filter/read_log.py*","flags":[]}, + "pftop":{"commands":scripts_folder+"filter/pftop.py*","flags":[]}, + "pftablecount":{"commands":scripts_folder+"filter/pftablecount.py*","flags":[]}, + "pfstatistics":{"commands":scripts_folder+"filter/pfstatistics.py*","flags":[]}, + "list_tables":{"commands":scripts_folder+"filter/list_tables.py*","flags":[]}, + "list_table":{"commands":scripts_folder+"filter/list_table.py*","flags":[]}, + "list_states":{"commands":scripts_folder+"filter/list_states.py*","flags":[]}, + "list_rule_ids":{"commands":scripts_folder+"filter/list_rule_ids.py*","flags":[]}, + "list_pfsync":{"commands":scripts_folder+"filter/list_pfsync.py*","flags":[]}, + "list_osfp":{"commands":scripts_folder+"filter/list_osfp.py*","flags":[]}, + "kill_table":{"commands":scripts_folder+"filter/kill_table.py*","flags":[]}, + "kill_states":{"commands":scripts_folder+"filter/kill_states.py*","flags":[]}, + "find_table_references":{"commands":scripts_folder+"filter/find_table_references.py*","flags":[]}, + "download_geoip":{"commands":scripts_folder+"filter/download_geoip.py*","flags":[]}, + "delete_table":{"commands":scripts_folder+"filter/delete_table.py*","flags":[]}, + } +class dns(Exec_Class): + def __init__(self, base): + super(Exec_Class).__init__() + if base is not None: + self.__dict__.update(base.__dict__) + + self.commands={ + "query_dns":{"commands":scripts_folder+"dns/query_dns.py*","flags":[]} + } + +class dhcp(Exec_Class): + def __init__(self, base): + super(Exec_Class).__init__() + if base is not None: + self.__dict__.update(base.__dict__) + + self.commands={ + "unbound_watche":{"command":scripts_folder+"dhcp/unbound_watcher.py*","flags":[]}, + "prefixe":{"command":scripts_folder+"dhcp/prefixes.sh*","flags":[]}, + "prefixes":{"command":scripts_folder+"dhcp/prefixes.php*","flags":[]}, + "get_leases":{"command":scripts_folder+"dhcp/get_leases6.py*","flags":[]}, + "get_lease":{"command":scripts_folder+"dhcp/get_leases.py*","flags":[]}, + "get_kea_lease":{"command":scripts_folder+"dhcp/get_kea_leases.py*","flags":[]}, + "dnsmasq_watche":{"command":scripts_folder+"dhcp/dnsmasq_watcher.py*","flags":[]}, + "cleanup_leases6":{"command":scripts_folder+"dhcp/cleanup_leases6.php*","flags":[]}, + "cleanup_leases4":{"command":scripts_folder+"dhcp/cleanup_leases4.php*","flags":[]}, + } +class auth(Exec_Class): + def __init__(self, base): + super(Exec_Class).__init__() + if base is not None: + self.__dict__.update(base.__dict__) + + self.commands={ + "list_group_members":{"command":scripts_folder+"auth/list_group_members.php*","flags":[]}, + "add_user":{"command":scripts_folder+"auth/add_user.php*","flags":[]}, + } +class Wireguard(Exec_Class): + def __init__(self, base): + super(Exec_Class).__init__() + if base is not None: + self.__dict__.update(base.__dict__) + + self.commands={ + "wg_show":{"command":scripts_folder+"wireguard/wg_show.py*","flags":[]}, + "wg-service-control":{"command":scripts_folder+"wireguard/wg-service-control.php*","flags":[]}, + "reresolve-dns":{"command":scripts_folder+"wireguard/reresolve-dns.py*","flags":[]}, + "gen_keypair":{"command":scripts_folder+"wireguard/gen_keypair.py*","flags":[]}, + } \ No newline at end of file diff --git a/python/opnsense_helper/utils.py b/python/opnsense_helper/utils.py index 75f857e..6c50621 100644 --- a/python/opnsense_helper/utils.py +++ b/python/opnsense_helper/utils.py @@ -4,71 +4,7 @@ import xml.etree.ElementTree as ET import requests import os -reconfigure={ - "vlans": {"command":'/usr/local/opnsense/scripts/interfaces/reconfigure_vlans.php'}, - "interfaces": {"command":"configctl interface reconfigure", "flags":[] } -} -configctl={ -# optional arguments: -# -h, --help show this help message and exit -# -m execute multiple arguments at once -# -e use as event handler, execute command on receiving input -# -d detach the execution of the command and return immediately -# -q run quietly by muting standard output -# -w W wait specified amount of seconds for socket to become available -# -t T threshold between events, wait this interval before executing commands, combine input into single events -"auth":{"command":"configctl auth", "argument":None,"flags":[]}, -"captiveportal":{"command":"configctl captiveportal", "argument":None,"flags":[]}, -"configd":{"command":"configctl configd", "argument":None,"flags":[]}, -"cron":{"command":"configctl cron", "argument":None,"flags":[]}, -"dhcpd":{"command":"configctl dhcpd", "argument":None,"flags":[]}, -"dhcpd6":{"command":"configctl dhcpd6", "argument":None,"flags":[]}, -"dns":{"command":"configctl dns", "argument":None,"flags":[]}, -"filter":{"command":"configctl filter", "argument":None,"flags":[]}, -"firmware":{"command":"configctl firmware", "argument":None,"flags":[]}, -"health":{"command":"configctl health", "argument":None,"flags":[]}, -"ids":{"command":"configctl ids", "argument":None,"flags":[]}, -"interface":{"command":"configctl interface", "argument":None,"flags":[]}, -"ipfw":{"command":"configctl ipfw", "argument":None,"flags":[]}, -"ipsec":{"command":"configctl ipsec", "argument":None,"flags":[]}, -"kea":{"command":"configctl kea", "argument":None,"flags":[]}, -"monit":{"command":"configctl monit", "argument":None,"flags":[]}, -"netflow":{"command":"configctl netflow", "argument":None,"flags":[]}, -"openssh":{"command":"configctl openssh", "argument":None,"flags":[]}, -"openvpn":{"command":"configctl openvpn", "argument":None,"flags":[]}, -"syslog":{"command":"configctl syslog", "argument":None,"flags":[]}, -"system":{"command":"configctl system", "argument":None,"flags":[]}, -"template":{"command":"configctl template", "argument":None,"flags":[]}, -"unbound":{"command":"configctl unbound", "argument":None,"flags":[]}, -"webgui":{"command":"configctl webgui", "argument":None,"flags":[]}, -"wireguard":{"command":"configctl wireguard", "argument":None,"flags":[]}, -"zfs":{"command":"configctl zfs", "argument":None,"flags":[]}, -} -pluginctl={ - # -4 IPv4 address mode, return primary address of interface - # -c configure mode (default), executes plugin [_configure] hook - # -D ifconfig mode, lists available devices - # -d device mode, lists registered devices - # -f flush config property (raw, e.g. system.firmware.plugins) - # -g get config property (raw, e.g. system.firmware.plugins) - # -h show this help text and exit - # -I information mode, lists registered device statistics - # -i invoke dynamic interface registration - # -r run mode (e.g. command) - # -S service metadata dump - # -s service mode (e.g. myservice restart) - "ipv4":{"argument":None,"command":"pluginctl -4"}, - "config":{"argument":None, "flags":[],"command":"pluginctl -c"}, - "ifconfig":{"argument":None,"command":"pluginctl -D"}, - "device_info":{"argument":None,"command":"pluginctl -d"}, - "flush":{"argument":None,"command":"pluginctl -f"}, - "get":{"argument":None, "flags":[],"command":"pluginctl -g"}, - "info":{"argument":None, "flags":[],"command":"pluginctl -I" }, - "if_reg":{"argument":None,"command":"pluginctl -i"}, - "run":{"argument":None,"command":"pluginctl -r"}, - "service_dump":{"argument":None,"command":"pluginctl -S"}, - "service":{"argument":None, "flags":[],"command":"pluginctl -s"} # stop start resta, rt -} + aliases={ "parent": "if", "_from":"from", diff --git a/python/opnsense_helper/utils/baseclass.py b/python/opnsense_helper/utils/baseclass.py new file mode 100644 index 0000000..c0466d8 --- /dev/null +++ b/python/opnsense_helper/utils/baseclass.py @@ -0,0 +1,53 @@ +import logging +import paramiko +from opnsense_helper.utils.exec_class import Exec_Class +from opnsense_helper.commands.commands import Commands +from opnsense_helper.scripts.scripts import Scripts +from opnsense_helper.config_manager.config_manager import Interface, Vlan, Dhcpd, Config_Manager + +class Base_Class(): + def __init__(self, host=None, ssh_auth=None, api_auth=None, conf_path="/conf/config.xml", temp_path="./config.xml", verbose=False, init_config_manager=True): + if(verbose): + self.logging=logging.basicConfig(level=logging.DEBUG) + self.objects={ + "vlans":{}, + "dhcpd":{}, + "interfaces":{} + } + self.url = host + self.conf_path=conf_path + self.temp_path= temp_path + + + if(ssh_auth!=None): + self.ssh = paramiko.SSHClient() + self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + self.ssh.connect(host, username=ssh_auth["user"], password=ssh_auth["passw"]) + self.sftp = self.ssh.open_sftp() + + if(api_auth!=None): + self.api_key = api_auth["api_key"] + self.api_secret = api_auth["api_secret"] + self.ssl=api_auth["ssl"] + self.verify=api_auth["verify"] + self.host=host + exec_class = Exec_Class(self) + self.commands=Commands(exec_class) + self.scripts=Scripts(exec_class) + self.config_manager=Config_Manager(self, init=True) + + if init_config_manager: + self.config_manager.get_conf() + self.config_manager.initialize() + + def close(self): + self.close_con() + + def close_con(self): + self.sftp.close() + self.ssh.close() + + def run(self, command): + res=exec(self, self.commands[command]) + print( res) + return res \ No newline at end of file diff --git a/python/opnsense_helper/utils/exec_class.py b/python/opnsense_helper/utils/exec_class.py new file mode 100644 index 0000000..2e178bf --- /dev/null +++ b/python/opnsense_helper/utils/exec_class.py @@ -0,0 +1,10 @@ +from opnsense_helper.utils.utils import exec +class Exec_Class(): + def __init__(self, base): + self.base=base + def run(self, command,argument=None,flags=[]): + command=self.commands[command] + command["argument"]=argument + command["flags"]=flags + res=exec(self.base,command ) + return res \ No newline at end of file diff --git a/python/opnsense_helper/frontend_utils.py b/python/opnsense_helper/utils/frontend_utils.py similarity index 62% rename from python/opnsense_helper/frontend_utils.py rename to python/opnsense_helper/utils/frontend_utils.py index 9fd9e2e..9e85eda 100644 --- a/python/opnsense_helper/frontend_utils.py +++ b/python/opnsense_helper/utils/frontend_utils.py @@ -5,6 +5,44 @@ import requests import os +# deprecated +def get_backup(helper,output=None): + """ + this function is currently deprecated + it was used to get the config file using the api + i left it in however if someone wants to fetch a certain backup + i need to implement the backup selection though + """ + command = 'core/backup/download/this' + backup=api_get(helper,command) + helper.root=backup + print(ET.tostring(backup, encoding='unicode')) + # if output != None: + # path=output + # else: + # path = self.temp_path + # with open(path, 'w') as f: + # f.write(ET.tostring(backup, encoding='unicode', method='xml')) + + +# deprecated +def vlans_api(helper,vlans, command="add"): + if command == "add": + for value in vlans: + print("---------add vlan-----------------") + print(value) + payload={"vlan":value} + r=api_post(helper,"interfaces/vlan_settings/addItem",payload) + print(r) + r=api_post(helper,"interfaces/vlan_settings/reconfigure",{}) + print(r) + elif command == "set": + for value in vlans: + print("---------set vlan-----------------") + payload={"vlan":value} + r=api_post(helper,"interfaces/vlan_settings/set",payload) + print(r) + def ping(helper): response = os.system(f"ping -c 1 {helper.host}") diff --git a/python/opnsense_helper/utils/utils.py b/python/opnsense_helper/utils/utils.py new file mode 100644 index 0000000..d8422a5 --- /dev/null +++ b/python/opnsense_helper/utils/utils.py @@ -0,0 +1,95 @@ +import xml.etree.ElementTree as ET +import os + +aliases={ +"parent": "if", +"_from":"from", +"_to":"to", +"interface":"if", +"_range":"range" +} + +def parseChild(child, tag): + result=child.find(tag) + element=result.text if result is not None else None + return element + +def ping(helper): + response = os.system(f"ping -c 1 {helper.host}") + if response == 0: + print(f"IP {helper.host} is reachable") + return 0 + else: return 1 +def format_flags(flags_array): + return ' '.join(map(str, flags_array)) +def exec(helper, obj): + flags= format_flags(obj["flags"]) if obj["flags"] is not [] else "" + command=f"""{obj["command"]} {obj["argument"]if obj["argument"] is not None else ""} {flags}""" + stdin, stdout, stderr = helper.ssh.exec_command(command) + output = stdout.read().decode('utf-8') + print("$ "+command) + print(output) + error = stderr.read().decode('utf-8') + if error: + print(f"Fehler: {error}") + + +def get_element(root,id, obj): + for x in root.findall(id): + for key in obj.__dict__.keys(): +# if isinstance(obj.__dict__[key], dict): + if(key!="_to"and key!="_from"): + + if key in aliases.keys(): + key2 = aliases[key] + y=parseChild(x, key2) + else: + y=parseChild(x, key) + setattr(obj, key, y) + return obj + +def recoursion(e, value): + for key2, value2 in value.items(): + if(key2!="attr"): + if key2 in aliases.keys(): + key2 = aliases[key2] + x=ET.SubElement(e, key2) + if isinstance(value2, dict): + recoursion(x,value2) + else: + x.text = value2 + +def update_xml_file(objects,root,type): + el = root.find(type) + el.clear() + for key, value in objects.items(): + if(type=="vlans"): + key="vlan" + if value["attr"] is not {}: + e = ET.SubElement(el, key,value["attr"]) + else: + e=ET.SubElement(el,key) + recoursion(e,value) + +# deprecated due to a logic fail +def get_child(root,element, id, keys): + elements=[] + for parent in root.findall(element): + child= {} + for y in keys: + child[y]=None + for x in parent.findall(id): + for key in keys: + child[key]=parseChild(x, key) + elements.append(child) + return elements + +def replace(item): + Data = {} + #data='{"vlan": {"descr": "example2", "if": "vtnet1", "tag": 110, "pcp": 0, "vlanif": "vlan0.110"}}' + for key, value in item.items(): + if key in aliases.keys(): + Data[aliases[key]] = value + else: + Data[key] = value + return Data diff --git a/python/setup.py b/python/setup.py index e2a9a37..08008ed 100644 --- a/python/setup.py +++ b/python/setup.py @@ -2,13 +2,13 @@ setup( name='opnsense_helper', - version='0.1.10', + version='0.1.20', description='assign lan interfaces, create vlans, vlan-interfaces and setup dhcp in a single script.', url='https://github.com/the-pod-shop/opnsense-helper/', author='ji-podhdead', author_email='ji.podhdead@proton.me', license='BSD 2-clause', - packages=['opnsense_helper'], + packages=['opnsense_helper', "opnsense_helper.utils", "opnsense_helper.scripts", "opnsense_helper.commands", "opnsense_helper.config_manager"], install_requires=['paramiko', ], classifiers=[