diff --git a/host_vars/opnsense.homelan b/host_vars/opnsense.homelan
new file mode 100644
index 0000000..1ed9c2a
--- /dev/null
+++ b/host_vars/opnsense.homelan
@@ -0,0 +1,59 @@
+---
+opnsense_api_key: "uTaG84sB3dhZtBgTxXV3oXh0aD5dhsR8xxfRYjP6SeAhmYM055dvo89BlfrpLTE01Rwwm7FVZLwZiTiS"
+opnsense_api_secret: "nrFEVYPBBcuan53v+BBOwUWaBGMEt03+mbU2SZq0q0K90CX2w1h18fsFBiLCNa91yEFQZUSyuWPlx4Yp"
+opnsense_ssl_verify: false
+
+opnsense_openvpn_static_keys:
+ - name: MCI-VPN-TLS-CRYPT
+ mode: crypt
+ key: |
+ -----BEGIN OpenVPN Static key V1-----
+ a68d0dba3e2efe7e83c3887cc7fcb759
+ c86a6b4984ea7ffa230dafe1fb4e9ca7
+ 66f3667fe75fd8d368b9a0aae7024603
+ f88cf00baf1eab326b222b4321e9cddd
+ 8b287265c3ee2d0053193dea1dbdda75
+ e869c7d7d580c0b42421bb5a1af22f80
+ 4d058aebc2cf1d8c83cc88948807fbf9
+ 3bfc122cab4a650f258f7a57716daa95
+ da3898acfd80481b9d81e9b3d07aeea0
+ 11b6c6508324ef89c6812028c345f274
+ f6a51c669c49295e07e8385435f713c0
+ 5eaa46b3ccb69888a5b477df79675d66
+ 365c4ce5f7684984980731d5eacd0a1a
+ 77a9449533076c098cde36c45e54342e
+ 27a920724f64eddeb16dcef134adb4b9
+ e0bcaa4ade9ebf2c45d679bd016f7f2e
+ -----END OpenVPN Static key V1-----
+
+opnsense_openvpn_clients:
+ - name: MCI-VPN
+ enabled: false
+ remote:
+ - vpn.mci.edu:1194
+ - 193.171.232.106:1194
+ - 193.171.232.107:1194
+ - "[2001:628:2260:850::106]:1194"
+ - "[2001:628:2260:850::107]:1194"
+ protocol: udp
+ mode: tap
+ ca: MCI-VPN-CA # import CA cert in WebUI first: System → Trust → Authorities
+ certificate: majanetschek # import client cert+key in WebUI first: System → Trust → Certificates
+ key: "MCI-VPN-TLS-CRYPT [crypt (Encrypt and authenticate all control channel packets)]" # workaround: oxlorg.opnsense uses endswith() matching
+ authentication: SHA384
+ verify_remote_certificate: true
+ username: "majanetschek"
+ password: "1234"
+ keepalive_interval: "20"
+ keepalive_timeout: "60"
+ renegotiate_time: "3600"
+ log_level: 1
+ # NOTE: The following options must be set manually in OPNsense WebUI
+ # under VPN → OpenVPN → Clients → Edit → Custom options:
+ # ncp-disable
+ # tls-version-min 1.2
+ # tls-cipher TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384
+ # cipher AES-256-GCM
+ # remote-cert-ku e0
+ # reneg-bytes 104857600
+ # reneg-pkts 100000
diff --git a/majanetschek-vpn.mci.edu.ovpn b/majanetschek-vpn.mci.edu.ovpn
new file mode 100644
index 0000000..364cc13
--- /dev/null
+++ b/majanetschek-vpn.mci.edu.ovpn
@@ -0,0 +1,119 @@
+#openvpn für majanetschek
+tls-client
+remote-cert-tls server
+allow-recursive-routing
+ifconfig-nowarn
+ncp-disable
+dev tap
+proto udp
+auth-user-pass
+client
+
+cipher AES-256-GCM
+auth SHA384
+
+verb 1
+
+keepalive 20 60
+
+reneg-bytes 104857600
+reneg-pkts 100000
+reneg-sec 3600
+
+
+persist-key
+
+tls-version-min 1.2
+tls-cipher TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384
+
+remote-cert-ku e0
+
+
+-----BEGIN CERTIFICATE-----
+MIIC6zCCAkygAwIBAgIULeThdNwAHGD0XQggppFxVDgd+3QwCgYIKoZIzj0EAwMw
+RjEoMCYGA1UECgwfTUNJIE1hbmFnZW1lbnQgQ2VudGVyIElubnNicnVjazEaMBgG
+A1UEAwwRTUNJIFZQTiBSb290IENBIDEwHhcNMjAwOTAyMDgyODQxWhcNMzAwODMx
+MDgyODQxWjBGMSgwJgYDVQQKDB9NQ0kgTWFuYWdlbWVudCBDZW50ZXIgSW5uc2Jy
+dWNrMRowGAYDVQQDDBFNQ0kgVlBOIFJvb3QgQ0EgMTCBmzAQBgcqhkjOPQIBBgUr
+gQQAIwOBhgAEALqR4Lihw47A96tXIO9x/emm0bVyeWg1nrzsKHrsFM/bxmNkGlp+
+x+yxkcWLkHdHFKqwxu9EZjNQZY14BYnSRjE6AdnPTemChMZuD/QVWhj1QIXtSB32
+TaE+hdGPcNuysAWSGCeMCk/ZKPa6Uyf9f+lqNReQBGpb/385Zlt+PVlElit9o4HU
+MIHRMBIGA1UdEwEB/wQIMAYBAf8CAQEwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQW
+BBRIxhLvChAQ19yDSwlXtgV5O831JTAsBgNVHREEJTAjgQt2cG5AbWNpLmVkdYYU
+aHR0cHM6Ly92cG4ubWNpLmVkdS8wXgYDVR0eAQH/BFQwUqBQMC6kLDAqMSgwJgYD
+VQQKDB9NQ0kgTWFuYWdlbWVudCBDZW50ZXIgSW5uc2JydWNrMA6CDHZwbjEubWNp
+LmVkdTAOggx2cG4yLm1jaS5lZHUwCgYIKoZIzj0EAwMDgYwAMIGIAkIBmFMTiKaf
+gVzp8lKs9uQ/pq8ms2JUCjt2QcBuK0QyUG2fMvC1bBge+j7HYYSVsxx7fm+Lr0/y
+3ZToJaNJryMSdGUCQgFcHuzWXQM95c1ZSM/horpQvdrOxx9JQXn1BibZe3o1RWIa
+xypwPnGHV/KnikIzrHUCcFVB2/jqXPCeZNSvKCTDPQ==
+-----END CERTIFICATE-----
+
+
+-----BEGIN CERTIFICATE-----
+MIICnTCCAf+gAwIBAgIUJ6zCEdrglrqL/8qnj5gaqQTF15EwCgYIKoZIzj0EAwMw
+RjEoMCYGA1UECgwfTUNJIE1hbmFnZW1lbnQgQ2VudGVyIElubnNicnVjazEaMBgG
+A1UEAwwRTUNJIFZQTiBSb290IENBIDEwHhcNMjAwOTA0MTAxNzA0WhcNMzAwOTAy
+MTAxNzA0WjBaMSgwJgYDVQQKDB9NQ0kgTWFuYWdlbWVudCBDZW50ZXIgSW5uc2Jy
+dWNrMRcwFQYDVQQLDA5NQ0kgVlBOIENsaWVudDEVMBMGA1UEAwwMbWFqYW5ldHNj
+aGVrMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE9WwWxvk44Lh07ivf2kEnM7HJ2daC
+fjK/v9xBBIRA4nEJrv8nU//2Cw+EXDO6Ewrua4rOnZ2qosV0wJKhbpfnk5XYbquK
+ACePOXnZ4teFnEf1ToJhBbOzuDHKH0FDqpZlo4GZMIGWMB8GA1UdIwQYMBaAFEjG
+Eu8KEBDX3INLCVe2BXk7zfUlMCwGA1UdEgQlMCOBC3ZwbkBtY2kuZWR1hhRodHRw
+czovL3Zwbi5tY2kuZWR1LzAOBgNVHQ8BAf8EBAMCBeAwFgYDVR0lAQH/BAwwCgYI
+KwYBBQUHAwIwHQYDVR0OBBYEFBth2m1Rp0i8qlHhciG8cLSmSZqYMAoGCCqGSM49
+BAMDA4GLADCBhwJCASGvkGCOVQ1HfYMVwrBJYLqV/XTqwIW+bFPvaRFLjjSBB7V7
+Q32ziQNmRzS6Yh8KVdYEtibrHfw2zalAra6OZyi3AkEIcCj2iizYDuNFyf47B3tP
+bmWDWlXMdoVjRewDpcPDyy8BqlcHny9WOLcPwl1YqFeHzVgamglTDDMawBi5QgDU
+4g==
+-----END CERTIFICATE-----
+
+
+-----BEGIN EC PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-256-CBC,A1D526070600C0577D6F6683D9E530A6
+
+2DPAkQov+AMjUNw6FFC2QOSrGjJqXeZ4IC0xzkWwPbP/z8GyVBZhUJbKTxnwOVoo
+TJWJ7BRoujRFKiV+RS42zl5UKH23BwouLZOVgZU6BXLuW6kPJy+ocHE2pDM+k3rT
+quGrfZFmGMMFB+va9g2dI6ZY1FPxnCdYu6qsRmeubDbIZlAQ3+e/mB//IxxWSoCd
+ULSYsFCTcB5AQTF1sQTjIu3wzM7pAVFwqPKynTjiChc=
+-----END EC PRIVATE KEY-----
+
+
+#
+# 2048 bit OpenVPN static key
+#
+-----BEGIN OpenVPN Static key V1-----
+a68d0dba3e2efe7e83c3887cc7fcb759
+c86a6b4984ea7ffa230dafe1fb4e9ca7
+66f3667fe75fd8d368b9a0aae7024603
+f88cf00baf1eab326b222b4321e9cddd
+8b287265c3ee2d0053193dea1dbdda75
+e869c7d7d580c0b42421bb5a1af22f80
+4d058aebc2cf1d8c83cc88948807fbf9
+3bfc122cab4a650f258f7a57716daa95
+da3898acfd80481b9d81e9b3d07aeea0
+11b6c6508324ef89c6812028c345f274
+f6a51c669c49295e07e8385435f713c0
+5eaa46b3ccb69888a5b477df79675d66
+365c4ce5f7684984980731d5eacd0a1a
+77a9449533076c098cde36c45e54342e
+27a920724f64eddeb16dcef134adb4b9
+e0bcaa4ade9ebf2c45d679bd016f7f2e
+-----END OpenVPN Static key V1-----
+
+
+
+ remote vpn.mci.edu 1194 udp
+
+
+ remote 193.171.232.106 1194 udp
+
+
+ remote 193.171.232.107 1194 udp
+
+
+ remote 2001:628:2260:850::106 1194 udp
+
+
+ remote 2001:628:2260:850::107 1194 udp
+
diff --git a/opnsense b/opnsense
new file mode 100644
index 0000000..d78004a
--- /dev/null
+++ b/opnsense
@@ -0,0 +1,5 @@
+
+localhost ansible_connection=local ansible_python_interpreter="{{ansible_playbook_python}}" host_ip_addr="10.61.1.20" host_base_domain="localhost"
+
+[opnsense]
+opnsense.homelan ansible_host=10.61.0.240 ansible_connection=local
diff --git a/opnsense.yml b/opnsense.yml
new file mode 100644
index 0000000..f970515
--- /dev/null
+++ b/opnsense.yml
@@ -0,0 +1,209 @@
+---
+- hosts: opnsense
+ gather_facts: false
+ roles:
+ - role: opnsense
+ vars:
+ opnsense_dhcp_interfaces:
+ - lan
+ - opt2
+ - opt3
+
+ opnsense_self_address: 10.61.0.240 # OPNsense LAN IP
+ opnsense_lan_interface: lan
+ opnsense_dmz_interface: opt1
+ opnsense_guest_interface: opt2
+ opnsense_iot_interface: opt3
+
+ opnsense_iot_internet_allowed:
+ - 192.168.20.10 # smart TV
+ - 192.168.20.11 # printer
+
+ # --- VLANs ---
+ opnsense_vlans:
+ - interface: vtnet0
+ tag: 1
+ description: LAN
+ - interface: vtnet0
+ tag: 10
+ description: DMZ
+ - interface: vtnet0
+ tag: 11
+ description: GUEST
+ - interface: vtnet0
+ tag: 20
+ description: IoT
+
+ # --- Unbound ---
+ opnsense_unbound_hosts:
+ - hostname: airq-livingroom
+ domain: homelan
+ description: AirQ Luftsensor
+ records:
+ - value: 10.63.1.177
+ aliases:
+ - alias: api
+ - domain: homelan.matzman.net
+
+ - hostname: ha
+ domain: homelan
+ description: Home Assistant
+ records:
+ - value: 10.61.1.75
+ - type: AAAA
+ value: "fd30:882:6101:1::75"
+ aliases:
+ - domain: homelan.matzman.net
+ - alias: mqtt
+ - alias: mqtt
+ domain: homelan.matzman.net
+
+ - hostname: ha
+ domain: iot.homelan
+ description: Home Assistant IoT VLAN
+ records:
+ - value: 10.63.1.75
+ - type: AAAA
+ value: "fd30:882:6120:1::75"
+ aliases:
+ - domain: iot.homelan.matzman.net
+ - alias: mqtt
+ - alias: mqtt
+ domain: iot.homelan.matzman.net
+
+ - hostname: helios4
+ domain: iot.homelan
+ description: Home Assistant IoT VLAN
+ records:
+ - value: 10.61.1.190
+ - type: AAAA
+ value: "fd30:882:6101:1::190"
+ aliases:
+ - domain: iot.homelan.matzman.net
+ - alias: mqtt
+ - alias: mqtt
+ domain: iot.homelan.matzman.net
+
+ - hostname: octopi
+ domain: homelan
+ description: OctoPi Raspi
+ records:
+ - value: 10.63.1.70
+ - type: AAAA
+ value: "fd30:882:6120:1::70"
+ aliases:
+ - alias: octoprint
+
+ - hostname: opnsense
+ domain: homelan
+ description: OPNsense Router
+ records:
+ - value: 10.61.0.250
+ - type: AAAA
+ value: "fd30:0882:6101::250"
+ aliases:
+ - alias: pfsense
+ - alias: ntp
+
+ - hostname: opnsense
+ domain: iot.homelan
+ description: OPNsense Router IoT VLAN
+ records:
+ - value: 10.63.0.250
+ - type: AAAA
+ value: "fd30:0882:6120::250"
+ aliases:
+ - alias: pfsense
+ - alias: ntp
+
+ - hostname: prusa-core-one
+ domain: homelan
+ description: Prusa Core One 3D-Printer
+ records:
+ - value: 10.63.1.84
+
+ - hostname: vhost0
+ domain: homelan
+ description: vhost0 Server
+ records:
+ - value: 10.61.0.100
+ - type: AAAA
+ value: "fd30:0882:6101::100"
+ aliases:
+ - alias: vhost0
+ domain: homelan.matzman.net
+ - alias: nas3
+
+ - hostname: vhost1
+ domain: homelan
+ description: vhost1 Server
+ records:
+ - value: 10.61.0.110
+ - type: AAAA
+ value: "fd30:0882:6101::110"
+ aliases:
+ - alias: vhost1
+ domain: homelan.matzman.net
+ - alias: comic-utils
+ - alias: homepage
+ - alias: komf
+ - alias: komga
+ - alias: uptime
+ - alias: traefik
+ - alias: jdownloader
+ - alias: transmission
+ - alias: lanraragi
+ - alias: influxdb
+ - alias: influxdb
+ domain: homelan.matzman.net
+ - alias: nas0
+
+ - hostname: vhost1
+ domain: iot.homelan
+ description: vhost1 Server IoT VLAN
+ records:
+ - value: 10.63.0.110
+ - type: AAAA
+ value: "fd30:0882:6120::110"
+ aliases:
+ - alias: vhost1
+ domain: iot.homelan.matzman.net
+ - alias: influxdb
+ - alias: influxdb
+ domain: iot.homelan.matzman.net
+
+ - hostname: vserver0
+ domain: wg0.homelan
+ description: vserver0 wg0 interface
+ records:
+ - value: 10.71.1.4
+
+ opnsense_dhcp_subnets:
+ - subnet: "192.168.1.0/24"
+ pools:
+ - "192.168.1.100 - 192.168.1.200"
+ gateway: [192.168.1.1]
+ dns: [192.168.1.1]
+ domain: homelan
+ description: LAN
+
+ # --- WireGuard ---
+ opnsense_wireguard_enabled: false
+
+ opnsense_wireguard_servers:
+ - name: wg0
+ private_key: "MGChy8PrvKTHWC7nzvLwuHQP/uxhNA5FUhLm5NTgfE8="
+ port: 51820
+ allowed_ips:
+ - 10.0.0.1/24
+ peers: []
+ enabled: true
+
+ opnsense_wireguard_peers:
+ - name: client1
+ public_key: "NjmYVK0BWDhQzaODqqoWHNgdtPQyNMJwhCJyEingln8="
+ allowed_ips:
+ - 10.0.0.2/32
+ servers: [wg0]
+ keepalive: 25
+ enabled: true
diff --git a/roles/opnsense/defaults/main.yml b/roles/opnsense/defaults/main.yml
new file mode 100644
index 0000000..43deec4
--- /dev/null
+++ b/roles/opnsense/defaults/main.yml
@@ -0,0 +1,166 @@
+#SPDX-License-Identifier: MIT-0
+---
+# --- Connection ---
+# opnsense_firewall defaults to ansible_host from inventory
+opnsense_firewall: "{{ ansible_host }}"
+opnsense_api_key: "" # set in Ansible Vault
+opnsense_api_secret: "" # set in Ansible Vault
+opnsense_ssl_verify: true
+opnsense_api_port: 443
+
+# --- System settings ---
+opnsense_hostname: opnsense
+opnsense_domain: homelan
+opnsense_timezone: Europe/Vienna
+opnsense_dns_servers:
+ - 1.1.1.1
+ - 8.8.8.8
+
+# --- Gateways ---
+# opnsense_gateways:
+# - name: WAN_GW
+# interface: wan
+# gateway: 192.168.1.1
+# ip_protocol: inet
+# default_gw: true
+opnsense_gateways: []
+
+# --- VLANs ---
+# opnsense_vlans:
+# - interface: vtnet0
+# tag: 10
+# description: LAN
+opnsense_vlans: []
+
+# --- Network topology ---
+# OPNsense interface names as assigned in WebUI (Interfaces → Assignments)
+opnsense_lan_interface: lan
+opnsense_dmz_interface: opt1
+opnsense_guest_interface: opt2
+opnsense_iot_interface: opt3
+
+# OPNsense's own IP address — used as DNS target in firewall rules
+# Set this to OPNsense's LAN IP (e.g. 192.168.1.1)
+opnsense_self_address: ""
+
+# IoT hosts allowed internet access — list of IPs for iot_internet_allowed alias
+opnsense_iot_internet_allowed: []
+
+# --- Firewall aliases ---
+# opnsense_aliases:
+# - name: rfc1918
+# type: network
+# content:
+# - 10.0.0.0/8
+# - 172.16.0.0/12
+# - 192.168.0.0/16
+# description: Private networks
+opnsense_aliases: []
+
+# --- Firewall rules ---
+# opnsense_rules:
+# - description: Allow LAN to internet
+# interface: [lan]
+# direction: in
+# action: pass
+# source_net: lan
+# destination_net: any
+# protocol: any
+# enabled: true
+opnsense_rules: []
+
+# --- NAT (outbound / masquerade) ---
+# opnsense_nat_source_rules:
+# - description: Masquerade LAN to WAN
+# interface: wan
+# source_net: 192.168.0.0/24
+# target: interface
+# enabled: true
+opnsense_nat_source_rules: []
+
+# --- DHCP server ---
+opnsense_dhcp_enabled: false
+opnsense_dhcp_interfaces: []
+opnsense_dhcp_fw_rules: false
+
+# opnsense_dhcp_subnets:
+# - subnet: 192.168.1.0/24
+# pools:
+# - 192.168.1.100 - 192.168.1.200
+# gateway: [192.168.1.1]
+# dns: [192.168.1.1]
+# domain: homelan
+# description: LAN
+opnsense_dhcp_subnets: []
+
+# opnsense_dhcp_reservations:
+# - ip: 192.168.1.10
+# mac: "aa:bb:cc:dd:ee:ff"
+# hostname: myhost
+# subnet: 192.168.1.0/24
+# description: My host
+opnsense_dhcp_reservations: []
+
+# --- Unbound DNS resolver ---
+opnsense_unbound_enabled: true
+opnsense_unbound_port: 53
+opnsense_unbound_dnssec: true
+opnsense_unbound_register_dhcp_leases: false
+opnsense_unbound_register_dhcp_static_mappings: false
+opnsense_unbound_register_system_records: true
+
+# Host overrides — aliases are sub-properties of each host entry.
+# alias.domain defaults to the parent host's domain if not set.
+# alias.target is auto-set to hostname.domain of the parent host.
+# opnsense_unbound_hosts:
+# - hostname: vhost0
+# domain: homelan
+# records:
+# - value: 10.61.0.100 # type defaults to A
+# - type: AAAA
+# value: "fd30:882:6101:1::1"
+# aliases:
+# - alias: web
+# - alias: api
+# - hostname: vhost1
+# domain: homelan
+# records:
+# - value: 10.61.0.110
+opnsense_unbound_hosts: []
+
+# Selective forwards — leave empty to resolve everything from root servers
+# opnsense_unbound_forwards:
+# - domain: homelan
+# target: 10.61.0.1
+# description: Local domain
+# - domain: internal.example.com
+# target: 192.168.1.53
+# description: Internal DNS
+opnsense_unbound_forwards: []
+
+# --- OpenVPN clients ---
+opnsense_openvpn_static_keys: []
+opnsense_openvpn_clients: []
+
+# --- WireGuard ---
+opnsense_wireguard_enabled: true
+
+# opnsense_wireguard_servers:
+# - name: wg0
+# private_key: "{{ vault_wg_private_key }}"
+# port: 51820
+# allowed_ips:
+# - 10.0.0.1/24
+# peers: []
+# enabled: true
+opnsense_wireguard_servers: []
+
+# opnsense_wireguard_peers:
+# - name: client1
+# public_key: "CLIENT_PUBLIC_KEY"
+# allowed_ips:
+# - 10.0.0.2/32
+# servers: [wg0]
+# keepalive: 25
+# enabled: true
+opnsense_wireguard_peers: []
diff --git a/roles/opnsense/tasks/dhcp.yml b/roles/opnsense/tasks/dhcp.yml
new file mode 100644
index 0000000..2fd72a0
--- /dev/null
+++ b/roles/opnsense/tasks/dhcp.yml
@@ -0,0 +1,46 @@
+---
+- name: Configure DHCP general settings
+ oxlorg.opnsense.dhcp_general:
+ firewall: "{{ opnsense_firewall }}"
+ api_key: "{{ opnsense_api_key }}"
+ api_secret: "{{ opnsense_api_secret }}"
+ ssl_verify: "{{ opnsense_ssl_verify }}"
+ api_port: "{{ opnsense_api_port }}"
+ enabled: "{{ opnsense_dhcp_enabled }}"
+ interfaces: "{{ opnsense_dhcp_interfaces }}"
+ fw_rules: "{{ opnsense_dhcp_fw_rules }}"
+
+- name: Configure DHCP subnets
+ oxlorg.opnsense.dhcp_subnet:
+ firewall: "{{ opnsense_firewall }}"
+ api_key: "{{ opnsense_api_key }}"
+ api_secret: "{{ opnsense_api_secret }}"
+ ssl_verify: "{{ opnsense_ssl_verify }}"
+ api_port: "{{ opnsense_api_port }}"
+ match_fields: [subnet]
+ subnet: "{{ item.subnet }}"
+ auto_options: false
+ pools: "{{ item.pools | default([]) }}"
+ gateway: "{{ item.gateway | default([]) }}"
+ dns: "{{ item.dns | default([]) }}"
+ domain: "{{ item.domain | default('') }}"
+ domain_search: "{{ item.domain_search | default([]) }}"
+ ntp_servers: "{{ item.ntp_servers | default([]) }}"
+ description: "{{ item.description | default(omit) }}"
+ state: "{{ item.state | default('present') }}"
+ loop: "{{ opnsense_dhcp_subnets }}"
+
+- name: Configure DHCP reservations
+ oxlorg.opnsense.dhcp_reservation:
+ firewall: "{{ opnsense_firewall }}"
+ api_key: "{{ opnsense_api_key }}"
+ api_secret: "{{ opnsense_api_secret }}"
+ ssl_verify: "{{ opnsense_ssl_verify }}"
+ api_port: "{{ opnsense_api_port }}"
+ ip: "{{ item.ip }}"
+ mac: "{{ item.mac }}"
+ subnet: "{{ item.subnet | default(omit) }}"
+ hostname: "{{ item.hostname | default(omit) }}"
+ description: "{{ item.description | default(omit) }}"
+ state: "{{ item.state | default('present') }}"
+ loop: "{{ opnsense_dhcp_reservations }}"
diff --git a/roles/opnsense/tasks/firewall-aliases.yml b/roles/opnsense/tasks/firewall-aliases.yml
new file mode 100644
index 0000000..690fa4d
--- /dev/null
+++ b/roles/opnsense/tasks/firewall-aliases.yml
@@ -0,0 +1,15 @@
+---
+- name: Configure firewall aliases
+ oxlorg.opnsense.alias:
+ firewall: "{{ opnsense_firewall }}"
+ api_key: "{{ opnsense_api_key }}"
+ api_secret: "{{ opnsense_api_secret }}"
+ ssl_verify: "{{ opnsense_ssl_verify }}"
+ api_port: "{{ opnsense_api_port }}"
+ name: "{{ item.name }}"
+ type: "{{ item.type | default('host') }}"
+ content: "{{ item.content | default([]) }}"
+ description: "{{ item.description | default(omit) }}"
+ enabled: "{{ item.enabled | default(true) }}"
+ state: "{{ item.state | default('present') }}"
+ loop: "{{ opnsense_aliases }}"
diff --git a/roles/opnsense/tasks/firewall-defaults.yml b/roles/opnsense/tasks/firewall-defaults.yml
new file mode 100644
index 0000000..64528d3
--- /dev/null
+++ b/roles/opnsense/tasks/firewall-defaults.yml
@@ -0,0 +1,251 @@
+---
+# Default aliases
+
+- name: Configure default firewall aliases
+ oxlorg.opnsense.alias:
+ firewall: "{{ opnsense_firewall }}"
+ api_key: "{{ opnsense_api_key }}"
+ api_secret: "{{ opnsense_api_secret }}"
+ ssl_verify: "{{ opnsense_ssl_verify }}"
+ api_port: "{{ opnsense_api_port }}"
+ name: "{{ item.name }}"
+ type: "{{ item.type }}"
+ content: "{{ item.content | default([]) }}"
+ description: "{{ item.description | default(omit) }}"
+ enabled: "{{ item.enabled | default(true) }}"
+ state: present
+ loop:
+ - name: rfc1918
+ type: network
+ content:
+ - 10.0.0.0/8
+ - 172.16.0.0/12
+ - 192.168.0.0/16
+ description: Private IPv4 networks
+ - name: iot_internet_allowed
+ type: host
+ content: "{{ opnsense_iot_internet_allowed }}"
+ description: IoT hosts allowed internet access
+
+# LAN rules
+
+- name: LAN - allow all
+ oxlorg.opnsense.rule:
+ firewall: "{{ opnsense_firewall }}"
+ api_key: "{{ opnsense_api_key }}"
+ api_secret: "{{ opnsense_api_secret }}"
+ ssl_verify: "{{ opnsense_ssl_verify }}"
+ api_port: "{{ opnsense_api_port }}"
+ match_fields: [description]
+ description: LAN - allow all
+ sequence: 100
+ action: pass
+ interface: ["{{ opnsense_lan_interface }}"]
+ direction: in
+ source_net: "{{ opnsense_lan_interface }}"
+ destination_net: any
+ protocol: any
+
+# GUEST rules
+
+- name: GUEST - allow DNS to OPNsense
+ oxlorg.opnsense.rule:
+ firewall: "{{ opnsense_firewall }}"
+ api_key: "{{ opnsense_api_key }}"
+ api_secret: "{{ opnsense_api_secret }}"
+ ssl_verify: "{{ opnsense_ssl_verify }}"
+ api_port: "{{ opnsense_api_port }}"
+ match_fields: [description]
+ description: GUEST - allow DNS to OPNsense
+ sequence: 100
+ action: pass
+ interface: ["{{ opnsense_guest_interface }}"]
+ direction: in
+ source_net: "{{ opnsense_guest_interface }}"
+ destination_net: "{{ opnsense_self_address }}"
+ destination_port: "53"
+ protocol: TCP/UDP
+
+- name: GUEST - block private networks
+ oxlorg.opnsense.rule:
+ firewall: "{{ opnsense_firewall }}"
+ api_key: "{{ opnsense_api_key }}"
+ api_secret: "{{ opnsense_api_secret }}"
+ ssl_verify: "{{ opnsense_ssl_verify }}"
+ api_port: "{{ opnsense_api_port }}"
+ match_fields: [description]
+ description: GUEST - block private networks
+ sequence: 200
+ action: block
+ interface: ["{{ opnsense_guest_interface }}"]
+ direction: in
+ source_net: "{{ opnsense_guest_interface }}"
+ destination_net: rfc1918
+
+- name: GUEST - allow internet
+ oxlorg.opnsense.rule:
+ firewall: "{{ opnsense_firewall }}"
+ api_key: "{{ opnsense_api_key }}"
+ api_secret: "{{ opnsense_api_secret }}"
+ ssl_verify: "{{ opnsense_ssl_verify }}"
+ api_port: "{{ opnsense_api_port }}"
+ match_fields: [description]
+ description: GUEST - allow internet
+ sequence: 300
+ action: pass
+ interface: ["{{ opnsense_guest_interface }}"]
+ direction: in
+ source_net: "{{ opnsense_guest_interface }}"
+ destination_net: any
+
+# IoT rules
+
+- name: IoT - allow DNS to OPNsense
+ oxlorg.opnsense.rule:
+ firewall: "{{ opnsense_firewall }}"
+ api_key: "{{ opnsense_api_key }}"
+ api_secret: "{{ opnsense_api_secret }}"
+ ssl_verify: "{{ opnsense_ssl_verify }}"
+ api_port: "{{ opnsense_api_port }}"
+ match_fields: [description]
+ description: IoT - allow DNS to OPNsense
+ sequence: 100
+ action: pass
+ interface: ["{{ opnsense_iot_interface }}"]
+ direction: in
+ source_net: "{{ opnsense_iot_interface }}"
+ destination_net: "{{ opnsense_self_address }}"
+ destination_port: "53"
+ protocol: TCP/UDP
+
+- name: IoT - block private networks
+ oxlorg.opnsense.rule:
+ firewall: "{{ opnsense_firewall }}"
+ api_key: "{{ opnsense_api_key }}"
+ api_secret: "{{ opnsense_api_secret }}"
+ ssl_verify: "{{ opnsense_ssl_verify }}"
+ api_port: "{{ opnsense_api_port }}"
+ match_fields: [description]
+ description: IoT - block private networks
+ sequence: 200
+ action: block
+ interface: ["{{ opnsense_iot_interface }}"]
+ direction: in
+ source_net: "{{ opnsense_iot_interface }}"
+ destination_net: rfc1918
+
+- name: IoT - allow internet for selected hosts
+ oxlorg.opnsense.rule:
+ firewall: "{{ opnsense_firewall }}"
+ api_key: "{{ opnsense_api_key }}"
+ api_secret: "{{ opnsense_api_secret }}"
+ ssl_verify: "{{ opnsense_ssl_verify }}"
+ api_port: "{{ opnsense_api_port }}"
+ match_fields: [description]
+ description: IoT - allow internet for selected hosts
+ sequence: 300
+ action: pass
+ interface: ["{{ opnsense_iot_interface }}"]
+ direction: in
+ source_net: iot_internet_allowed
+ destination_net: any
+
+- name: IoT - block all
+ oxlorg.opnsense.rule:
+ firewall: "{{ opnsense_firewall }}"
+ api_key: "{{ opnsense_api_key }}"
+ api_secret: "{{ opnsense_api_secret }}"
+ ssl_verify: "{{ opnsense_ssl_verify }}"
+ api_port: "{{ opnsense_api_port }}"
+ match_fields: [description]
+ description: IoT - block all
+ sequence: 400
+ action: block
+ interface: ["{{ opnsense_iot_interface }}"]
+ direction: in
+ source_net: any
+ destination_net: any
+
+# DMZ rules
+
+- name: DMZ - allow DNS to OPNsense
+ oxlorg.opnsense.rule:
+ firewall: "{{ opnsense_firewall }}"
+ api_key: "{{ opnsense_api_key }}"
+ api_secret: "{{ opnsense_api_secret }}"
+ ssl_verify: "{{ opnsense_ssl_verify }}"
+ api_port: "{{ opnsense_api_port }}"
+ match_fields: [description]
+ description: DMZ - allow DNS to OPNsense
+ sequence: 100
+ action: pass
+ interface: ["{{ opnsense_dmz_interface }}"]
+ direction: in
+ source_net: "{{ opnsense_dmz_interface }}"
+ destination_net: "{{ opnsense_self_address }}"
+ destination_port: "53"
+ protocol: TCP/UDP
+
+- name: DMZ - block LAN access
+ oxlorg.opnsense.rule:
+ firewall: "{{ opnsense_firewall }}"
+ api_key: "{{ opnsense_api_key }}"
+ api_secret: "{{ opnsense_api_secret }}"
+ ssl_verify: "{{ opnsense_ssl_verify }}"
+ api_port: "{{ opnsense_api_port }}"
+ match_fields: [description]
+ description: DMZ - block LAN access
+ sequence: 200
+ action: block
+ interface: ["{{ opnsense_dmz_interface }}"]
+ direction: in
+ source_net: "{{ opnsense_dmz_interface }}"
+ destination_net: "{{ opnsense_lan_interface }}"
+
+- name: DMZ - block GUEST access
+ oxlorg.opnsense.rule:
+ firewall: "{{ opnsense_firewall }}"
+ api_key: "{{ opnsense_api_key }}"
+ api_secret: "{{ opnsense_api_secret }}"
+ ssl_verify: "{{ opnsense_ssl_verify }}"
+ api_port: "{{ opnsense_api_port }}"
+ match_fields: [description]
+ description: DMZ - block GUEST access
+ sequence: 210
+ action: block
+ interface: ["{{ opnsense_dmz_interface }}"]
+ direction: in
+ source_net: "{{ opnsense_dmz_interface }}"
+ destination_net: "{{ opnsense_guest_interface }}"
+
+- name: DMZ - block IoT access
+ oxlorg.opnsense.rule:
+ firewall: "{{ opnsense_firewall }}"
+ api_key: "{{ opnsense_api_key }}"
+ api_secret: "{{ opnsense_api_secret }}"
+ ssl_verify: "{{ opnsense_ssl_verify }}"
+ api_port: "{{ opnsense_api_port }}"
+ match_fields: [description]
+ description: DMZ - block IoT access
+ sequence: 220
+ action: block
+ interface: ["{{ opnsense_dmz_interface }}"]
+ direction: in
+ source_net: "{{ opnsense_dmz_interface }}"
+ destination_net: "{{ opnsense_iot_interface }}"
+
+- name: DMZ - allow internet
+ oxlorg.opnsense.rule:
+ firewall: "{{ opnsense_firewall }}"
+ api_key: "{{ opnsense_api_key }}"
+ api_secret: "{{ opnsense_api_secret }}"
+ ssl_verify: "{{ opnsense_ssl_verify }}"
+ api_port: "{{ opnsense_api_port }}"
+ match_fields: [description]
+ description: DMZ - allow internet
+ sequence: 300
+ action: pass
+ interface: ["{{ opnsense_dmz_interface }}"]
+ direction: in
+ source_net: "{{ opnsense_dmz_interface }}"
+ destination_net: any
diff --git a/roles/opnsense/tasks/firewall-rules.yml b/roles/opnsense/tasks/firewall-rules.yml
new file mode 100644
index 0000000..7ad8c17
--- /dev/null
+++ b/roles/opnsense/tasks/firewall-rules.yml
@@ -0,0 +1,27 @@
+---
+- name: Configure firewall rules
+ oxlorg.opnsense.rule:
+ firewall: "{{ opnsense_firewall }}"
+ api_key: "{{ opnsense_api_key }}"
+ api_secret: "{{ opnsense_api_secret }}"
+ ssl_verify: "{{ opnsense_ssl_verify }}"
+ api_port: "{{ opnsense_api_port }}"
+ match_fields: "{{ item.match_fields | default(['description']) }}"
+ description: "{{ item.description }}"
+ sequence: "{{ item.sequence | default(1) }}"
+ action: "{{ item.action | default('pass') }}"
+ quick: "{{ item.quick | default(true) }}"
+ interface: "{{ item.interface | default(['lan']) }}"
+ direction: "{{ item.direction | default('in') }}"
+ ip_protocol: "{{ item.ip_protocol | default('inet') }}"
+ protocol: "{{ item.protocol | default('any') }}"
+ source_net: "{{ item.source_net | default('any') }}"
+ source_port: "{{ item.source_port | default(omit) }}"
+ source_invert: "{{ item.source_invert | default(false) }}"
+ destination_net: "{{ item.destination_net | default('any') }}"
+ destination_port: "{{ item.destination_port | default(omit) }}"
+ destination_invert: "{{ item.destination_invert | default(false) }}"
+ log: "{{ item.log | default(true) }}"
+ enabled: "{{ item.enabled | default(true) }}"
+ state: "{{ item.state | default('present') }}"
+ loop: "{{ opnsense_rules }}"
diff --git a/roles/opnsense/tasks/interfaces.yml b/roles/opnsense/tasks/interfaces.yml
new file mode 100644
index 0000000..b854d3f
--- /dev/null
+++ b/roles/opnsense/tasks/interfaces.yml
@@ -0,0 +1,36 @@
+---
+# NOTE: Interface assignment is not available via the OPNsense API and cannot
+# be automated with this role. After VLANs are created by this task, you must
+# manually assign them in the WebUI under Interfaces → Assignments, then
+# configure each interface (IP address, description) under Interfaces → [name].
+# This only needs to be done once during initial setup.
+
+- name: Configure VLANs
+ oxlorg.opnsense.interface_vlan:
+ firewall: "{{ opnsense_firewall }}"
+ api_key: "{{ opnsense_api_key }}"
+ api_secret: "{{ opnsense_api_secret }}"
+ ssl_verify: "{{ opnsense_ssl_verify }}"
+ api_port: "{{ opnsense_api_port }}"
+ interface: "{{ item.interface }}"
+ tag: "{{ item.tag }}"
+ description: "{{ item.description | default(omit) }}"
+ state: "{{ item.state | default('present') }}"
+ loop: "{{ opnsense_vlans }}"
+
+- name: Configure gateways
+ oxlorg.opnsense.gateway:
+ firewall: "{{ opnsense_firewall }}"
+ api_key: "{{ opnsense_api_key }}"
+ api_secret: "{{ opnsense_api_secret }}"
+ ssl_verify: "{{ opnsense_ssl_verify }}"
+ api_port: "{{ opnsense_api_port }}"
+ name: "{{ item.name }}"
+ interface: "{{ item.interface }}"
+ gateway: "{{ item.gateway }}"
+ ip_protocol: "{{ item.ip_protocol | default('inet') }}"
+ default_gw: "{{ item.default_gw | default(false) }}"
+ far_gw: "{{ item.far_gw | default(false) }}"
+ monitor_disable: "{{ item.monitor_disable | default(false) }}"
+ state: "{{ item.state | default('present') }}"
+ loop: "{{ opnsense_gateways }}"
diff --git a/roles/opnsense/tasks/main.yml b/roles/opnsense/tasks/main.yml
new file mode 100644
index 0000000..ce7617d
--- /dev/null
+++ b/roles/opnsense/tasks/main.yml
@@ -0,0 +1,66 @@
+#SPDX-License-Identifier: MIT-0
+#
+# OPNsense role — manages OPNsense via oxlorg.opnsense REST API (no SSH)
+# Collection: oxlorg.opnsense, tested with OPNsense 26.1.2
+#
+# Inventory: opnsense file, ansible_connection=local (API-based, no SSH)
+# Playbook: opnsense.yml
+#
+# DNS: Unbound (independent resolver, selective forwards supported)
+# DHCP: Kea (dnsmasq disabled)
+# VPN: WireGuard + OpenVPN clients
+#
+# Known API limitations in OPNsense 26.1.2 — must be set manually in WebUI:
+# - System general settings (hostname, timezone, DNS)
+# - Interface assignment and IP configuration
+# - Outbound NAT mode (set to Hybrid manually)
+# - Config backup restore
+# - OpenVPN custom options (ncp-disable, tls-cipher, etc.)
+# - CA and client certificates (System → Trust)
+#
+# Firewall design:
+# - LAN: full access
+# - GUEST: DNS to OPNsense, internet only, isolated from other VLANs
+# - IoT: DNS to OPNsense, internet only for hosts in iot_internet_allowed alias
+# - DMZ: DNS to OPNsense, internet access, isolated from LAN/GUEST/IoT
+#
+---
+- name: Configure OPNsense system settings
+ ansible.builtin.import_tasks: system.yml
+ tags: [opnsense-system]
+
+- name: Configure OPNsense interfaces
+ ansible.builtin.import_tasks: interfaces.yml
+ tags: [opnsense-interfaces]
+
+- name: Configure OPNsense default firewall rules
+ ansible.builtin.import_tasks: firewall-defaults.yml
+ tags: [opnsense-firewall-defaults, opnsense-firewall]
+
+- name: Configure OPNsense firewall aliases
+ ansible.builtin.import_tasks: firewall-aliases.yml
+ tags: [opnsense-aliases, opnsense-firewall]
+
+- name: Configure OPNsense firewall rules
+ ansible.builtin.import_tasks: firewall-rules.yml
+ tags: [opnsense-rules, opnsense-firewall]
+
+- name: Configure OPNsense NAT
+ ansible.builtin.import_tasks: nat.yml
+ tags: [opnsense-nat, opnsense-firewall]
+
+- name: Configure OPNsense DHCP
+ ansible.builtin.import_tasks: dhcp.yml
+ tags: [opnsense-dhcp]
+
+- name: Configure OPNsense Unbound DNS
+ ansible.builtin.import_tasks: unbound.yml
+ tags: [opnsense-unbound, opnsense-dns]
+
+- name: Configure OPNsense OpenVPN clients
+ ansible.builtin.import_tasks: openvpn-clients.yml
+ tags: [opnsense-openvpn]
+
+- name: Configure OPNsense WireGuard
+ ansible.builtin.import_tasks: wireguard.yml
+ tags: [opnsense-wireguard]
diff --git a/roles/opnsense/tasks/nat.yml b/roles/opnsense/tasks/nat.yml
new file mode 100644
index 0000000..127d338
--- /dev/null
+++ b/roles/opnsense/tasks/nat.yml
@@ -0,0 +1,29 @@
+---
+# NOTE: Outbound NAT mode (automatic/hybrid/manual) is not available via the
+# API. Set it manually once in the WebUI under Firewall → NAT → Outbound → Mode.
+# Recommended: Hybrid — allows automatic rules plus the custom rules below.
+
+- name: Configure outbound NAT rules
+ oxlorg.opnsense.nat_source:
+ firewall: "{{ opnsense_firewall }}"
+ api_key: "{{ opnsense_api_key }}"
+ api_secret: "{{ opnsense_api_secret }}"
+ ssl_verify: "{{ opnsense_ssl_verify }}"
+ api_port: "{{ opnsense_api_port }}"
+ match_fields: "{{ item.match_fields | default(['description']) }}"
+ description: "{{ item.description }}"
+ sequence: "{{ item.sequence | default(1) }}"
+ interface: "{{ item.interface }}"
+ target: "{{ item.target }}"
+ target_port: "{{ item.target_port | default(omit) }}"
+ no_nat: "{{ item.no_nat | default(false) }}"
+ ip_protocol: "{{ item.ip_protocol | default('inet') }}"
+ protocol: "{{ item.protocol | default('any') }}"
+ source_net: "{{ item.source_net | default('any') }}"
+ source_port: "{{ item.source_port | default(omit) }}"
+ destination_net: "{{ item.destination_net | default('any') }}"
+ destination_port: "{{ item.destination_port | default(omit) }}"
+ log: "{{ item.log | default(false) }}"
+ enabled: "{{ item.enabled | default(true) }}"
+ state: "{{ item.state | default('present') }}"
+ loop: "{{ opnsense_nat_source_rules }}"
diff --git a/roles/opnsense/tasks/openvpn-clients.yml b/roles/opnsense/tasks/openvpn-clients.yml
new file mode 100644
index 0000000..ccc2876
--- /dev/null
+++ b/roles/opnsense/tasks/openvpn-clients.yml
@@ -0,0 +1,64 @@
+---
+# OpenVPN client instances and TLS static keys.
+#
+# Prerequisites — must be done manually in OPNsense WebUI before running:
+# - Import CA certificates: System → Trust → Authorities
+# - Import client certificates: System → Trust → Certificates
+# (includes the private key — decrypt it first if encrypted)
+#
+# After running — set manually in WebUI per client under
+# VPN → OpenVPN → Clients → Edit → Custom options:
+# ncp-disable
+# tls-version-min 1.2
+# tls-cipher
+# cipher
+# reneg-bytes
+# reneg-pkts
+#
+# Known limitation: oxlorg.opnsense resolves the tls-crypt key by matching
+# the END of the API value string, which has the format:
+# " [ ()]"
+# Since the name is at the beginning, matching by name alone does not work.
+# Workaround: pass the full value string as the key parameter, e.g.:
+# key: "MCI-VPN-TLS-CRYPT [crypt (Encrypt and authenticate all control channel packets)]"
+
+- name: Configure OpenVPN static (tls-crypt) keys
+ oxlorg.opnsense.openvpn_static_key:
+ firewall: "{{ opnsense_firewall }}"
+ api_key: "{{ opnsense_api_key }}"
+ api_secret: "{{ opnsense_api_secret }}"
+ ssl_verify: "{{ opnsense_ssl_verify }}"
+ api_port: "{{ opnsense_api_port }}"
+ name: "{{ item.name }}"
+ mode: "{{ item.mode | default('crypt') }}"
+ key: "{{ item.key }}"
+ state: "{{ item.state | default('present') }}"
+ loop: "{{ opnsense_openvpn_static_keys }}"
+ no_log: true
+
+- name: Configure OpenVPN clients
+ oxlorg.opnsense.openvpn_client:
+ firewall: "{{ opnsense_firewall }}"
+ api_key: "{{ opnsense_api_key }}"
+ api_secret: "{{ opnsense_api_secret }}"
+ ssl_verify: "{{ opnsense_ssl_verify }}"
+ api_port: "{{ opnsense_api_port }}"
+ name: "{{ item.name }}"
+ remote: "{{ item.remote }}"
+ protocol: "{{ item.protocol | default('udp') }}"
+ mode: "{{ item.mode | default('tun') }}"
+ ca: "{{ item.ca | default(omit) }}"
+ certificate: "{{ item.certificate | default(omit) }}"
+ key: "{{ item.key | default(omit) }}"
+ authentication: "{{ item.authentication | default(omit) }}"
+ verify_remote_certificate: "{{ item.verify_remote_certificate | default(false) }}"
+ username: "{{ item.username | default(omit) }}"
+ password: "{{ item.password | default(omit) }}"
+ keepalive_interval: "{{ item.keepalive_interval | default(omit) }}"
+ keepalive_timeout: "{{ item.keepalive_timeout | default(omit) }}"
+ renegotiate_time: "{{ item.renegotiate_time | default(omit) }}"
+ log_level: "{{ item.log_level | default(3) }}"
+ enabled: "{{ item.enabled | default(true) }}"
+ state: "{{ item.state | default('present') }}"
+ loop: "{{ opnsense_openvpn_clients }}"
+ no_log: true
diff --git a/roles/opnsense/tasks/system.yml b/roles/opnsense/tasks/system.yml
new file mode 100644
index 0000000..35c66ca
--- /dev/null
+++ b/roles/opnsense/tasks/system.yml
@@ -0,0 +1,13 @@
+---
+# NOTE: OPNsense does not expose general system settings (hostname, domain,
+# timezone, DNS) via the API. These must be configured manually via the WebUI
+# under System → Settings → General.
+
+- name: Disable dnsmasq (using Unbound for DNS and Kea for DHCP)
+ oxlorg.opnsense.dnsmasq_general:
+ firewall: "{{ opnsense_firewall }}"
+ api_key: "{{ opnsense_api_key }}"
+ api_secret: "{{ opnsense_api_secret }}"
+ ssl_verify: "{{ opnsense_ssl_verify }}"
+ api_port: "{{ opnsense_api_port }}"
+ enabled: false
diff --git a/roles/opnsense/tasks/unbound.yml b/roles/opnsense/tasks/unbound.yml
new file mode 100644
index 0000000..aa5951d
--- /dev/null
+++ b/roles/opnsense/tasks/unbound.yml
@@ -0,0 +1,67 @@
+---
+- name: Configure Unbound general settings
+ oxlorg.opnsense.unbound_general:
+ firewall: "{{ opnsense_firewall }}"
+ api_key: "{{ opnsense_api_key }}"
+ api_secret: "{{ opnsense_api_secret }}"
+ ssl_verify: "{{ opnsense_ssl_verify }}"
+ api_port: "{{ opnsense_api_port }}"
+ enabled: "{{ opnsense_unbound_enabled }}"
+ port: "{{ opnsense_unbound_port }}"
+ dnssec: "{{ opnsense_unbound_dnssec }}"
+ register_dhcp_leases: "{{ opnsense_unbound_register_dhcp_leases }}"
+ register_dhcp_static_mappings: "{{ opnsense_unbound_register_dhcp_static_mappings }}"
+ register_system_records: "{{ opnsense_unbound_register_system_records }}"
+
+- name: Configure Unbound host overrides
+ oxlorg.opnsense.unbound_host:
+ firewall: "{{ opnsense_firewall }}"
+ api_key: "{{ opnsense_api_key }}"
+ api_secret: "{{ opnsense_api_secret }}"
+ ssl_verify: "{{ opnsense_ssl_verify }}"
+ api_port: "{{ opnsense_api_port }}"
+ match_fields: "{{ item[1].match_fields | default(['hostname', 'domain', 'record_type']) }}"
+ hostname: "{{ item[0].hostname }}"
+ domain: "{{ item[0].domain }}"
+ record_type: "{{ item[1].type | default('A') }}"
+ value: "{{ item[1].value }}"
+ description: "{{ item[0].description | default(omit) }}"
+ enabled: "{{ item[0].enabled | default(true) }}"
+ state: "{{ item[1].state | default('present') }}"
+ loop: "{{ opnsense_unbound_hosts | subelements('records') }}"
+
+- name: Configure Unbound host override aliases
+ oxlorg.opnsense.unbound_host_alias:
+ firewall: "{{ opnsense_firewall }}"
+ api_key: "{{ opnsense_api_key }}"
+ api_secret: "{{ opnsense_api_secret }}"
+ ssl_verify: "{{ opnsense_ssl_verify }}"
+ api_port: "{{ opnsense_api_port }}"
+ match_fields: "{{ item[1].match_fields | default(['alias', 'domain']) }}"
+ alias: "{{ item[1].alias | default(item[0].hostname) }}"
+ domain: "{{ item[1].domain | default(item[0].domain) }}"
+ target: "{{ item[0].hostname }}.{{ item[0].domain }}"
+ description: "{{ item[1].description | default(omit) }}"
+ state: "{{ item[1].state | default('present') }}"
+ loop: "{{ opnsense_unbound_hosts | subelements('aliases', skip_missing=True) }}"
+ tags: debug01
+
+- debug:
+ msg: "{{ opnsense_unbound_hosts | subelements('aliases', skip_missing=True) }}"
+ tags: debug02
+
+- name: Configure Unbound selective forwards
+ oxlorg.opnsense.unbound_forward:
+ firewall: "{{ opnsense_firewall }}"
+ api_key: "{{ opnsense_api_key }}"
+ api_secret: "{{ opnsense_api_secret }}"
+ ssl_verify: "{{ opnsense_ssl_verify }}"
+ api_port: "{{ opnsense_api_port }}"
+ domain: "{{ item.domain }}"
+ target: "{{ item.target }}"
+ port: "{{ item.port | default(53) }}"
+ forward_tcp: "{{ item.forward_tcp | default(false) }}"
+ description: "{{ item.description | default(omit) }}"
+ enabled: "{{ item.enabled | default(true) }}"
+ state: "{{ item.state | default('present') }}"
+ loop: "{{ opnsense_unbound_forwards }}"
diff --git a/roles/opnsense/tasks/wireguard.yml b/roles/opnsense/tasks/wireguard.yml
new file mode 100644
index 0000000..dffb2e7
--- /dev/null
+++ b/roles/opnsense/tasks/wireguard.yml
@@ -0,0 +1,47 @@
+---
+- name: Enable WireGuard
+ oxlorg.opnsense.wireguard_general:
+ firewall: "{{ opnsense_firewall }}"
+ api_key: "{{ opnsense_api_key }}"
+ api_secret: "{{ opnsense_api_secret }}"
+ ssl_verify: "{{ opnsense_ssl_verify }}"
+ api_port: "{{ opnsense_api_port }}"
+ enabled: "{{ opnsense_wireguard_enabled }}"
+
+- name: Configure WireGuard servers
+ oxlorg.opnsense.wireguard_server:
+ firewall: "{{ opnsense_firewall }}"
+ api_key: "{{ opnsense_api_key }}"
+ api_secret: "{{ opnsense_api_secret }}"
+ ssl_verify: "{{ opnsense_ssl_verify }}"
+ api_port: "{{ opnsense_api_port }}"
+ name: "{{ item.name }}"
+ private_key: "{{ item.private_key }}"
+ port: "{{ item.port | default(51820) }}"
+ allowed_ips: "{{ item.allowed_ips }}"
+ mtu: "{{ item.mtu | default(1420) }}"
+ dns_servers: "{{ item.dns_servers | default([]) }}"
+ disable_routes: "{{ item.disable_routes | default(false) }}"
+ peers: "{{ item.peers | default([]) }}"
+ enabled: "{{ item.enabled | default(true) }}"
+ state: "{{ item.state | default('present') }}"
+ loop: "{{ opnsense_wireguard_servers }}"
+
+- name: Configure WireGuard peers
+ oxlorg.opnsense.wireguard_peer:
+ firewall: "{{ opnsense_firewall }}"
+ api_key: "{{ opnsense_api_key }}"
+ api_secret: "{{ opnsense_api_secret }}"
+ ssl_verify: "{{ opnsense_ssl_verify }}"
+ api_port: "{{ opnsense_api_port }}"
+ name: "{{ item.name }}"
+ public_key: "{{ item.public_key }}"
+ psk: "{{ item.psk | default(omit) }}"
+ allowed_ips: "{{ item.allowed_ips }}"
+ server: "{{ item.server | default(omit) }}"
+ port: "{{ item.port | default(omit) }}"
+ keepalive: "{{ item.keepalive | default(omit) }}"
+ servers: "{{ item.servers | default([]) }}"
+ enabled: "{{ item.enabled | default(true) }}"
+ state: "{{ item.state | default('present') }}"
+ loop: "{{ opnsense_wireguard_peers }}"
diff --git a/test02.yml b/test02.yml
new file mode 100644
index 0000000..d21cc51
--- /dev/null
+++ b/test02.yml
@@ -0,0 +1,12 @@
+---
+
+- hosts: opnsense.homelan
+ connection: local # execute modules on controller
+ gather_facts: false
+ tasks:
+ - name: Example
+ oxlorg.opnsense.alias:
+ firewall: "{{ ansible_host }}" # or use a per-host variable to store the FQDN..
+ api_key: "{{ firewall.key }}"
+ api_secret: "{{ firewall.secret }}"
+
diff --git a/testing02 b/testing02
new file mode 100644
index 0000000..6ac57cf
--- /dev/null
+++ b/testing02
@@ -0,0 +1,5 @@
+
+localhost ansible_connection=local ansible_python_interpreter="{{ansible_playbook_python}}" host_ip_addr="10.61.1.20" host_base_domain="localhost"
+
+[vhosts]
+vserver1.matzman.net ansible_connection=ssh ansible_user=root host_ip_addr="185.242.113.24" host_base_domain="vserver1.matzman.net"
diff --git a/tmp b/tmp
new file mode 100644
index 0000000..f3ee5ac
--- /dev/null
+++ b/tmp
@@ -0,0 +1,1435 @@
+
+PLAY [opnsense] ****************************************************************
+
+TASK [opnsense : debug] ********************************************************
+ok: [opnsense.homelan] => {
+ "msg": [
+ [
+ {
+ "aliases": [
+ {
+ "alias": "api"
+ },
+ {
+ "domain": "homelan.matzman.net"
+ }
+ ],
+ "description": "AirQ Luftsensor",
+ "domain": "homelan",
+ "hostname": "airq-livingroom",
+ "records": [
+ {
+ "value": "10.63.1.177"
+ }
+ ]
+ },
+ {
+ "alias": "api"
+ }
+ ],
+ [
+ {
+ "aliases": [
+ {
+ "alias": "api"
+ },
+ {
+ "domain": "homelan.matzman.net"
+ }
+ ],
+ "description": "AirQ Luftsensor",
+ "domain": "homelan",
+ "hostname": "airq-livingroom",
+ "records": [
+ {
+ "value": "10.63.1.177"
+ }
+ ]
+ },
+ {
+ "domain": "homelan.matzman.net"
+ }
+ ],
+ [
+ {
+ "aliases": [
+ {
+ "domain": "homelan.matzman.net"
+ },
+ {
+ "alias": "mqtt"
+ },
+ {
+ "alias": "mqtt",
+ "domain": "homelan.matzman.net"
+ }
+ ],
+ "description": "Home Assistant",
+ "domain": "homelan",
+ "hostname": "ha",
+ "records": [
+ {
+ "value": "10.61.1.75"
+ },
+ {
+ "type": "AAAA",
+ "value": "fd30:882:6101:1::75"
+ }
+ ]
+ },
+ {
+ "domain": "homelan.matzman.net"
+ }
+ ],
+ [
+ {
+ "aliases": [
+ {
+ "domain": "homelan.matzman.net"
+ },
+ {
+ "alias": "mqtt"
+ },
+ {
+ "alias": "mqtt",
+ "domain": "homelan.matzman.net"
+ }
+ ],
+ "description": "Home Assistant",
+ "domain": "homelan",
+ "hostname": "ha",
+ "records": [
+ {
+ "value": "10.61.1.75"
+ },
+ {
+ "type": "AAAA",
+ "value": "fd30:882:6101:1::75"
+ }
+ ]
+ },
+ {
+ "alias": "mqtt"
+ }
+ ],
+ [
+ {
+ "aliases": [
+ {
+ "domain": "homelan.matzman.net"
+ },
+ {
+ "alias": "mqtt"
+ },
+ {
+ "alias": "mqtt",
+ "domain": "homelan.matzman.net"
+ }
+ ],
+ "description": "Home Assistant",
+ "domain": "homelan",
+ "hostname": "ha",
+ "records": [
+ {
+ "value": "10.61.1.75"
+ },
+ {
+ "type": "AAAA",
+ "value": "fd30:882:6101:1::75"
+ }
+ ]
+ },
+ {
+ "alias": "mqtt",
+ "domain": "homelan.matzman.net"
+ }
+ ],
+ [
+ {
+ "aliases": [
+ {
+ "domain": "iot.homelan.matzman.net"
+ },
+ {
+ "alias": "mqtt"
+ },
+ {
+ "alias": "mqtt",
+ "domain": "iot.homelan.matzman.net"
+ }
+ ],
+ "description": "Home Assistant IoT VLAN",
+ "domain": "iot.homelan",
+ "hostname": "ha",
+ "records": [
+ {
+ "value": "10.63.1.75"
+ },
+ {
+ "type": "AAAA",
+ "value": "fd30:882:6120:1::75"
+ }
+ ]
+ },
+ {
+ "domain": "iot.homelan.matzman.net"
+ }
+ ],
+ [
+ {
+ "aliases": [
+ {
+ "domain": "iot.homelan.matzman.net"
+ },
+ {
+ "alias": "mqtt"
+ },
+ {
+ "alias": "mqtt",
+ "domain": "iot.homelan.matzman.net"
+ }
+ ],
+ "description": "Home Assistant IoT VLAN",
+ "domain": "iot.homelan",
+ "hostname": "ha",
+ "records": [
+ {
+ "value": "10.63.1.75"
+ },
+ {
+ "type": "AAAA",
+ "value": "fd30:882:6120:1::75"
+ }
+ ]
+ },
+ {
+ "alias": "mqtt"
+ }
+ ],
+ [
+ {
+ "aliases": [
+ {
+ "domain": "iot.homelan.matzman.net"
+ },
+ {
+ "alias": "mqtt"
+ },
+ {
+ "alias": "mqtt",
+ "domain": "iot.homelan.matzman.net"
+ }
+ ],
+ "description": "Home Assistant IoT VLAN",
+ "domain": "iot.homelan",
+ "hostname": "ha",
+ "records": [
+ {
+ "value": "10.63.1.75"
+ },
+ {
+ "type": "AAAA",
+ "value": "fd30:882:6120:1::75"
+ }
+ ]
+ },
+ {
+ "alias": "mqtt",
+ "domain": "iot.homelan.matzman.net"
+ }
+ ],
+ [
+ {
+ "aliases": [
+ {
+ "domain": "iot.homelan.matzman.net"
+ },
+ {
+ "alias": "mqtt"
+ },
+ {
+ "alias": "mqtt",
+ "domain": "iot.homelan.matzman.net"
+ }
+ ],
+ "description": "Home Assistant IoT VLAN",
+ "domain": "iot.homelan",
+ "hostname": "helios4",
+ "records": [
+ {
+ "value": "10.61.1.190"
+ },
+ {
+ "type": "AAAA",
+ "value": "fd30:882:6101:1::190"
+ }
+ ]
+ },
+ {
+ "domain": "iot.homelan.matzman.net"
+ }
+ ],
+ [
+ {
+ "aliases": [
+ {
+ "domain": "iot.homelan.matzman.net"
+ },
+ {
+ "alias": "mqtt"
+ },
+ {
+ "alias": "mqtt",
+ "domain": "iot.homelan.matzman.net"
+ }
+ ],
+ "description": "Home Assistant IoT VLAN",
+ "domain": "iot.homelan",
+ "hostname": "helios4",
+ "records": [
+ {
+ "value": "10.61.1.190"
+ },
+ {
+ "type": "AAAA",
+ "value": "fd30:882:6101:1::190"
+ }
+ ]
+ },
+ {
+ "alias": "mqtt"
+ }
+ ],
+ [
+ {
+ "aliases": [
+ {
+ "domain": "iot.homelan.matzman.net"
+ },
+ {
+ "alias": "mqtt"
+ },
+ {
+ "alias": "mqtt",
+ "domain": "iot.homelan.matzman.net"
+ }
+ ],
+ "description": "Home Assistant IoT VLAN",
+ "domain": "iot.homelan",
+ "hostname": "helios4",
+ "records": [
+ {
+ "value": "10.61.1.190"
+ },
+ {
+ "type": "AAAA",
+ "value": "fd30:882:6101:1::190"
+ }
+ ]
+ },
+ {
+ "alias": "mqtt",
+ "domain": "iot.homelan.matzman.net"
+ }
+ ],
+ [
+ {
+ "aliases": [
+ {
+ "alias": "octoprint"
+ }
+ ],
+ "description": "OctoPi Raspi",
+ "domain": "homelan",
+ "hostname": "octopi",
+ "records": [
+ {
+ "value": "10.63.1.70"
+ },
+ {
+ "type": "AAAA",
+ "value": "fd30:882:6120:1::70"
+ }
+ ]
+ },
+ {
+ "alias": "octoprint"
+ }
+ ],
+ [
+ {
+ "aliases": [
+ {
+ "alias": "pfsense"
+ },
+ {
+ "alias": "ntp"
+ }
+ ],
+ "description": "OPNsense Router",
+ "domain": "homelan",
+ "hostname": "opnsense",
+ "records": [
+ {
+ "value": "10.61.0.250"
+ },
+ {
+ "type": "AAAA",
+ "value": "fd30:0882:6101::250"
+ }
+ ]
+ },
+ {
+ "alias": "pfsense"
+ }
+ ],
+ [
+ {
+ "aliases": [
+ {
+ "alias": "pfsense"
+ },
+ {
+ "alias": "ntp"
+ }
+ ],
+ "description": "OPNsense Router",
+ "domain": "homelan",
+ "hostname": "opnsense",
+ "records": [
+ {
+ "value": "10.61.0.250"
+ },
+ {
+ "type": "AAAA",
+ "value": "fd30:0882:6101::250"
+ }
+ ]
+ },
+ {
+ "alias": "ntp"
+ }
+ ],
+ [
+ {
+ "aliases": [
+ {
+ "alias": "pfsense"
+ },
+ {
+ "alias": "ntp"
+ }
+ ],
+ "description": "OPNsense Router IoT VLAN",
+ "domain": "iot.homelan",
+ "hostname": "opnsense",
+ "records": [
+ {
+ "value": "10.63.0.250"
+ },
+ {
+ "type": "AAAA",
+ "value": "fd30:0882:6120::250"
+ }
+ ]
+ },
+ {
+ "alias": "pfsense"
+ }
+ ],
+ [
+ {
+ "aliases": [
+ {
+ "alias": "pfsense"
+ },
+ {
+ "alias": "ntp"
+ }
+ ],
+ "description": "OPNsense Router IoT VLAN",
+ "domain": "iot.homelan",
+ "hostname": "opnsense",
+ "records": [
+ {
+ "value": "10.63.0.250"
+ },
+ {
+ "type": "AAAA",
+ "value": "fd30:0882:6120::250"
+ }
+ ]
+ },
+ {
+ "alias": "ntp"
+ }
+ ],
+ [
+ {
+ "aliases": [
+ {
+ "alias": "vhost0",
+ "domain": "homelan.matzman.net"
+ },
+ {
+ "alias": "nas3"
+ }
+ ],
+ "description": "vhost0 Server",
+ "domain": "homelan",
+ "hostname": "vhost0",
+ "records": [
+ {
+ "value": "10.61.0.100"
+ },
+ {
+ "type": "AAAA",
+ "value": "fd30:0882:6101::100"
+ }
+ ]
+ },
+ {
+ "alias": "vhost0",
+ "domain": "homelan.matzman.net"
+ }
+ ],
+ [
+ {
+ "aliases": [
+ {
+ "alias": "vhost0",
+ "domain": "homelan.matzman.net"
+ },
+ {
+ "alias": "nas3"
+ }
+ ],
+ "description": "vhost0 Server",
+ "domain": "homelan",
+ "hostname": "vhost0",
+ "records": [
+ {
+ "value": "10.61.0.100"
+ },
+ {
+ "type": "AAAA",
+ "value": "fd30:0882:6101::100"
+ }
+ ]
+ },
+ {
+ "alias": "nas3"
+ }
+ ],
+ [
+ {
+ "aliases": [
+ {
+ "alias": "vhost1",
+ "domain": "homelan.matzman.net"
+ },
+ {
+ "alias": "comic-utils"
+ },
+ {
+ "alias": "homepage"
+ },
+ {
+ "alias": "komf"
+ },
+ {
+ "alias": "komga"
+ },
+ {
+ "alias": "uptime"
+ },
+ {
+ "alias": "traefik"
+ },
+ {
+ "alias": "jdownloader"
+ },
+ {
+ "alias": "transmission"
+ },
+ {
+ "alias": "lanraragi"
+ },
+ {
+ "alias": "influxdb"
+ },
+ {
+ "alias": "influxdb",
+ "domain": "homelan.matzman.net"
+ },
+ {
+ "alias": "nas0"
+ }
+ ],
+ "description": "vhost1 Server",
+ "domain": "homelan",
+ "hostname": "vhost1",
+ "records": [
+ {
+ "value": "10.61.0.110"
+ },
+ {
+ "type": "AAAA",
+ "value": "fd30:0882:6101::110"
+ }
+ ]
+ },
+ {
+ "alias": "vhost1",
+ "domain": "homelan.matzman.net"
+ }
+ ],
+ [
+ {
+ "aliases": [
+ {
+ "alias": "vhost1",
+ "domain": "homelan.matzman.net"
+ },
+ {
+ "alias": "comic-utils"
+ },
+ {
+ "alias": "homepage"
+ },
+ {
+ "alias": "komf"
+ },
+ {
+ "alias": "komga"
+ },
+ {
+ "alias": "uptime"
+ },
+ {
+ "alias": "traefik"
+ },
+ {
+ "alias": "jdownloader"
+ },
+ {
+ "alias": "transmission"
+ },
+ {
+ "alias": "lanraragi"
+ },
+ {
+ "alias": "influxdb"
+ },
+ {
+ "alias": "influxdb",
+ "domain": "homelan.matzman.net"
+ },
+ {
+ "alias": "nas0"
+ }
+ ],
+ "description": "vhost1 Server",
+ "domain": "homelan",
+ "hostname": "vhost1",
+ "records": [
+ {
+ "value": "10.61.0.110"
+ },
+ {
+ "type": "AAAA",
+ "value": "fd30:0882:6101::110"
+ }
+ ]
+ },
+ {
+ "alias": "comic-utils"
+ }
+ ],
+ [
+ {
+ "aliases": [
+ {
+ "alias": "vhost1",
+ "domain": "homelan.matzman.net"
+ },
+ {
+ "alias": "comic-utils"
+ },
+ {
+ "alias": "homepage"
+ },
+ {
+ "alias": "komf"
+ },
+ {
+ "alias": "komga"
+ },
+ {
+ "alias": "uptime"
+ },
+ {
+ "alias": "traefik"
+ },
+ {
+ "alias": "jdownloader"
+ },
+ {
+ "alias": "transmission"
+ },
+ {
+ "alias": "lanraragi"
+ },
+ {
+ "alias": "influxdb"
+ },
+ {
+ "alias": "influxdb",
+ "domain": "homelan.matzman.net"
+ },
+ {
+ "alias": "nas0"
+ }
+ ],
+ "description": "vhost1 Server",
+ "domain": "homelan",
+ "hostname": "vhost1",
+ "records": [
+ {
+ "value": "10.61.0.110"
+ },
+ {
+ "type": "AAAA",
+ "value": "fd30:0882:6101::110"
+ }
+ ]
+ },
+ {
+ "alias": "homepage"
+ }
+ ],
+ [
+ {
+ "aliases": [
+ {
+ "alias": "vhost1",
+ "domain": "homelan.matzman.net"
+ },
+ {
+ "alias": "comic-utils"
+ },
+ {
+ "alias": "homepage"
+ },
+ {
+ "alias": "komf"
+ },
+ {
+ "alias": "komga"
+ },
+ {
+ "alias": "uptime"
+ },
+ {
+ "alias": "traefik"
+ },
+ {
+ "alias": "jdownloader"
+ },
+ {
+ "alias": "transmission"
+ },
+ {
+ "alias": "lanraragi"
+ },
+ {
+ "alias": "influxdb"
+ },
+ {
+ "alias": "influxdb",
+ "domain": "homelan.matzman.net"
+ },
+ {
+ "alias": "nas0"
+ }
+ ],
+ "description": "vhost1 Server",
+ "domain": "homelan",
+ "hostname": "vhost1",
+ "records": [
+ {
+ "value": "10.61.0.110"
+ },
+ {
+ "type": "AAAA",
+ "value": "fd30:0882:6101::110"
+ }
+ ]
+ },
+ {
+ "alias": "komf"
+ }
+ ],
+ [
+ {
+ "aliases": [
+ {
+ "alias": "vhost1",
+ "domain": "homelan.matzman.net"
+ },
+ {
+ "alias": "comic-utils"
+ },
+ {
+ "alias": "homepage"
+ },
+ {
+ "alias": "komf"
+ },
+ {
+ "alias": "komga"
+ },
+ {
+ "alias": "uptime"
+ },
+ {
+ "alias": "traefik"
+ },
+ {
+ "alias": "jdownloader"
+ },
+ {
+ "alias": "transmission"
+ },
+ {
+ "alias": "lanraragi"
+ },
+ {
+ "alias": "influxdb"
+ },
+ {
+ "alias": "influxdb",
+ "domain": "homelan.matzman.net"
+ },
+ {
+ "alias": "nas0"
+ }
+ ],
+ "description": "vhost1 Server",
+ "domain": "homelan",
+ "hostname": "vhost1",
+ "records": [
+ {
+ "value": "10.61.0.110"
+ },
+ {
+ "type": "AAAA",
+ "value": "fd30:0882:6101::110"
+ }
+ ]
+ },
+ {
+ "alias": "komga"
+ }
+ ],
+ [
+ {
+ "aliases": [
+ {
+ "alias": "vhost1",
+ "domain": "homelan.matzman.net"
+ },
+ {
+ "alias": "comic-utils"
+ },
+ {
+ "alias": "homepage"
+ },
+ {
+ "alias": "komf"
+ },
+ {
+ "alias": "komga"
+ },
+ {
+ "alias": "uptime"
+ },
+ {
+ "alias": "traefik"
+ },
+ {
+ "alias": "jdownloader"
+ },
+ {
+ "alias": "transmission"
+ },
+ {
+ "alias": "lanraragi"
+ },
+ {
+ "alias": "influxdb"
+ },
+ {
+ "alias": "influxdb",
+ "domain": "homelan.matzman.net"
+ },
+ {
+ "alias": "nas0"
+ }
+ ],
+ "description": "vhost1 Server",
+ "domain": "homelan",
+ "hostname": "vhost1",
+ "records": [
+ {
+ "value": "10.61.0.110"
+ },
+ {
+ "type": "AAAA",
+ "value": "fd30:0882:6101::110"
+ }
+ ]
+ },
+ {
+ "alias": "uptime"
+ }
+ ],
+ [
+ {
+ "aliases": [
+ {
+ "alias": "vhost1",
+ "domain": "homelan.matzman.net"
+ },
+ {
+ "alias": "comic-utils"
+ },
+ {
+ "alias": "homepage"
+ },
+ {
+ "alias": "komf"
+ },
+ {
+ "alias": "komga"
+ },
+ {
+ "alias": "uptime"
+ },
+ {
+ "alias": "traefik"
+ },
+ {
+ "alias": "jdownloader"
+ },
+ {
+ "alias": "transmission"
+ },
+ {
+ "alias": "lanraragi"
+ },
+ {
+ "alias": "influxdb"
+ },
+ {
+ "alias": "influxdb",
+ "domain": "homelan.matzman.net"
+ },
+ {
+ "alias": "nas0"
+ }
+ ],
+ "description": "vhost1 Server",
+ "domain": "homelan",
+ "hostname": "vhost1",
+ "records": [
+ {
+ "value": "10.61.0.110"
+ },
+ {
+ "type": "AAAA",
+ "value": "fd30:0882:6101::110"
+ }
+ ]
+ },
+ {
+ "alias": "traefik"
+ }
+ ],
+ [
+ {
+ "aliases": [
+ {
+ "alias": "vhost1",
+ "domain": "homelan.matzman.net"
+ },
+ {
+ "alias": "comic-utils"
+ },
+ {
+ "alias": "homepage"
+ },
+ {
+ "alias": "komf"
+ },
+ {
+ "alias": "komga"
+ },
+ {
+ "alias": "uptime"
+ },
+ {
+ "alias": "traefik"
+ },
+ {
+ "alias": "jdownloader"
+ },
+ {
+ "alias": "transmission"
+ },
+ {
+ "alias": "lanraragi"
+ },
+ {
+ "alias": "influxdb"
+ },
+ {
+ "alias": "influxdb",
+ "domain": "homelan.matzman.net"
+ },
+ {
+ "alias": "nas0"
+ }
+ ],
+ "description": "vhost1 Server",
+ "domain": "homelan",
+ "hostname": "vhost1",
+ "records": [
+ {
+ "value": "10.61.0.110"
+ },
+ {
+ "type": "AAAA",
+ "value": "fd30:0882:6101::110"
+ }
+ ]
+ },
+ {
+ "alias": "jdownloader"
+ }
+ ],
+ [
+ {
+ "aliases": [
+ {
+ "alias": "vhost1",
+ "domain": "homelan.matzman.net"
+ },
+ {
+ "alias": "comic-utils"
+ },
+ {
+ "alias": "homepage"
+ },
+ {
+ "alias": "komf"
+ },
+ {
+ "alias": "komga"
+ },
+ {
+ "alias": "uptime"
+ },
+ {
+ "alias": "traefik"
+ },
+ {
+ "alias": "jdownloader"
+ },
+ {
+ "alias": "transmission"
+ },
+ {
+ "alias": "lanraragi"
+ },
+ {
+ "alias": "influxdb"
+ },
+ {
+ "alias": "influxdb",
+ "domain": "homelan.matzman.net"
+ },
+ {
+ "alias": "nas0"
+ }
+ ],
+ "description": "vhost1 Server",
+ "domain": "homelan",
+ "hostname": "vhost1",
+ "records": [
+ {
+ "value": "10.61.0.110"
+ },
+ {
+ "type": "AAAA",
+ "value": "fd30:0882:6101::110"
+ }
+ ]
+ },
+ {
+ "alias": "transmission"
+ }
+ ],
+ [
+ {
+ "aliases": [
+ {
+ "alias": "vhost1",
+ "domain": "homelan.matzman.net"
+ },
+ {
+ "alias": "comic-utils"
+ },
+ {
+ "alias": "homepage"
+ },
+ {
+ "alias": "komf"
+ },
+ {
+ "alias": "komga"
+ },
+ {
+ "alias": "uptime"
+ },
+ {
+ "alias": "traefik"
+ },
+ {
+ "alias": "jdownloader"
+ },
+ {
+ "alias": "transmission"
+ },
+ {
+ "alias": "lanraragi"
+ },
+ {
+ "alias": "influxdb"
+ },
+ {
+ "alias": "influxdb",
+ "domain": "homelan.matzman.net"
+ },
+ {
+ "alias": "nas0"
+ }
+ ],
+ "description": "vhost1 Server",
+ "domain": "homelan",
+ "hostname": "vhost1",
+ "records": [
+ {
+ "value": "10.61.0.110"
+ },
+ {
+ "type": "AAAA",
+ "value": "fd30:0882:6101::110"
+ }
+ ]
+ },
+ {
+ "alias": "lanraragi"
+ }
+ ],
+ [
+ {
+ "aliases": [
+ {
+ "alias": "vhost1",
+ "domain": "homelan.matzman.net"
+ },
+ {
+ "alias": "comic-utils"
+ },
+ {
+ "alias": "homepage"
+ },
+ {
+ "alias": "komf"
+ },
+ {
+ "alias": "komga"
+ },
+ {
+ "alias": "uptime"
+ },
+ {
+ "alias": "traefik"
+ },
+ {
+ "alias": "jdownloader"
+ },
+ {
+ "alias": "transmission"
+ },
+ {
+ "alias": "lanraragi"
+ },
+ {
+ "alias": "influxdb"
+ },
+ {
+ "alias": "influxdb",
+ "domain": "homelan.matzman.net"
+ },
+ {
+ "alias": "nas0"
+ }
+ ],
+ "description": "vhost1 Server",
+ "domain": "homelan",
+ "hostname": "vhost1",
+ "records": [
+ {
+ "value": "10.61.0.110"
+ },
+ {
+ "type": "AAAA",
+ "value": "fd30:0882:6101::110"
+ }
+ ]
+ },
+ {
+ "alias": "influxdb"
+ }
+ ],
+ [
+ {
+ "aliases": [
+ {
+ "alias": "vhost1",
+ "domain": "homelan.matzman.net"
+ },
+ {
+ "alias": "comic-utils"
+ },
+ {
+ "alias": "homepage"
+ },
+ {
+ "alias": "komf"
+ },
+ {
+ "alias": "komga"
+ },
+ {
+ "alias": "uptime"
+ },
+ {
+ "alias": "traefik"
+ },
+ {
+ "alias": "jdownloader"
+ },
+ {
+ "alias": "transmission"
+ },
+ {
+ "alias": "lanraragi"
+ },
+ {
+ "alias": "influxdb"
+ },
+ {
+ "alias": "influxdb",
+ "domain": "homelan.matzman.net"
+ },
+ {
+ "alias": "nas0"
+ }
+ ],
+ "description": "vhost1 Server",
+ "domain": "homelan",
+ "hostname": "vhost1",
+ "records": [
+ {
+ "value": "10.61.0.110"
+ },
+ {
+ "type": "AAAA",
+ "value": "fd30:0882:6101::110"
+ }
+ ]
+ },
+ {
+ "alias": "influxdb",
+ "domain": "homelan.matzman.net"
+ }
+ ],
+ [
+ {
+ "aliases": [
+ {
+ "alias": "vhost1",
+ "domain": "homelan.matzman.net"
+ },
+ {
+ "alias": "comic-utils"
+ },
+ {
+ "alias": "homepage"
+ },
+ {
+ "alias": "komf"
+ },
+ {
+ "alias": "komga"
+ },
+ {
+ "alias": "uptime"
+ },
+ {
+ "alias": "traefik"
+ },
+ {
+ "alias": "jdownloader"
+ },
+ {
+ "alias": "transmission"
+ },
+ {
+ "alias": "lanraragi"
+ },
+ {
+ "alias": "influxdb"
+ },
+ {
+ "alias": "influxdb",
+ "domain": "homelan.matzman.net"
+ },
+ {
+ "alias": "nas0"
+ }
+ ],
+ "description": "vhost1 Server",
+ "domain": "homelan",
+ "hostname": "vhost1",
+ "records": [
+ {
+ "value": "10.61.0.110"
+ },
+ {
+ "type": "AAAA",
+ "value": "fd30:0882:6101::110"
+ }
+ ]
+ },
+ {
+ "alias": "nas0"
+ }
+ ],
+ [
+ {
+ "aliases": [
+ {
+ "alias": "vhost1",
+ "domain": "iot.homelan.matzman.net"
+ },
+ {
+ "alias": "influxdb"
+ },
+ {
+ "alias": "influxdb",
+ "domain": "iot.homelan.matzman.net"
+ }
+ ],
+ "description": "vhost1 Server IoT VLAN",
+ "domain": "iot.homelan",
+ "hostname": "vhost1",
+ "records": [
+ {
+ "value": "10.63.0.110"
+ },
+ {
+ "type": "AAAA",
+ "value": "fd30:0882:6120::110"
+ }
+ ]
+ },
+ {
+ "alias": "vhost1",
+ "domain": "iot.homelan.matzman.net"
+ }
+ ],
+ [
+ {
+ "aliases": [
+ {
+ "alias": "vhost1",
+ "domain": "iot.homelan.matzman.net"
+ },
+ {
+ "alias": "influxdb"
+ },
+ {
+ "alias": "influxdb",
+ "domain": "iot.homelan.matzman.net"
+ }
+ ],
+ "description": "vhost1 Server IoT VLAN",
+ "domain": "iot.homelan",
+ "hostname": "vhost1",
+ "records": [
+ {
+ "value": "10.63.0.110"
+ },
+ {
+ "type": "AAAA",
+ "value": "fd30:0882:6120::110"
+ }
+ ]
+ },
+ {
+ "alias": "influxdb"
+ }
+ ],
+ [
+ {
+ "aliases": [
+ {
+ "alias": "vhost1",
+ "domain": "iot.homelan.matzman.net"
+ },
+ {
+ "alias": "influxdb"
+ },
+ {
+ "alias": "influxdb",
+ "domain": "iot.homelan.matzman.net"
+ }
+ ],
+ "description": "vhost1 Server IoT VLAN",
+ "domain": "iot.homelan",
+ "hostname": "vhost1",
+ "records": [
+ {
+ "value": "10.63.0.110"
+ },
+ {
+ "type": "AAAA",
+ "value": "fd30:0882:6120::110"
+ }
+ ]
+ },
+ {
+ "alias": "influxdb",
+ "domain": "iot.homelan.matzman.net"
+ }
+ ]
+ ]
+}
+
+PLAY RECAP *********************************************************************
+opnsense.homelan : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
+