From 38f0aa699f12568adff2aac6285c2a59fc62b889 Mon Sep 17 00:00:00 2001 From: Lumpiasty Date: Wed, 13 May 2026 22:27:25 +0200 Subject: [PATCH] feat(ansible): add IoT VLAN 5 (192.168.5.0/24, szafa wifi) MikroTik: add vlan5 interface, bridge VLAN entry (ether3 tagged), IP 192.168.5.1/24, IPv6 from-pool, DHCP pool/server/network, firewall rules allowing IoT internet-only (IPv4 and IPv6), DNS input from vlan5. OpenWrt: add switch VLAN 5 (WAN+CPU tagged), br-iot bridge on eth0.5, iot interface, iot firewall zone (forward ACCEPT, input REJECT). Also remove ensure_order from all non-firewall api_modify tasks as RouterOS does not support move on those paths. --- ansible/roles/openwrt/tasks/firewall.yml | 20 +++++++--- ansible/roles/openwrt/tasks/wireless.yml | 44 +++++++++++++++++++++ ansible/roles/openwrt/vars/main.yml | 9 +++++ ansible/roles/routeros/tasks/addressing.yml | 8 +++- ansible/roles/routeros/tasks/base.yml | 30 ++++++++------ ansible/roles/routeros/tasks/containers.yml | 3 -- ansible/roles/routeros/tasks/firewall.yml | 32 +++++++++++++++ ansible/roles/routeros/tasks/routing.yml | 2 - ansible/roles/routeros/tasks/wan.yml | 3 -- 9 files changed, 124 insertions(+), 27 deletions(-) create mode 100644 ansible/roles/openwrt/tasks/wireless.yml create mode 100644 ansible/roles/openwrt/vars/main.yml diff --git a/ansible/roles/openwrt/tasks/firewall.yml b/ansible/roles/openwrt/tasks/firewall.yml index 0988d16..e94ceb1 100644 --- a/ansible/roles/openwrt/tasks/firewall.yml +++ b/ansible/roles/openwrt/tasks/firewall.yml @@ -6,13 +6,16 @@ # input: ACCEPT (SSH, ping reachable from MGMT network) # forward: REJECT (nothing routes through mgmt) # -# lan — client bridge (eth0.2, wireless clients) +# lan — client bridge (eth0.2, LAN ports) # input: REJECT (clients cannot SSH into the AP itself) -# forward: ACCEPT (client traffic passes through to MikroTik, -# which does all actual firewalling) +# forward: ACCEPT (traffic passes through to MikroTik for firewalling) # -# No forwarding rules between zones — traffic in/out of each zone goes -# directly to/from MikroTik over the trunk, not through this device. +# iot — IoT bridge (eth0.5, wifi only) +# input: REJECT (IoT devices cannot reach the AP itself) +# forward: ACCEPT (traffic passes through to MikroTik, which allows +# internet only and blocks all internal networks) +# +# No forwarding rules between zones — all inter-zone policy is on MikroTik. - name: Configure firewall community.openwrt.uci: @@ -42,6 +45,13 @@ option output 'ACCEPT' option forward 'ACCEPT' + config zone + option name 'iot' + list network 'iot' + option input 'REJECT' + option output 'ACCEPT' + option forward 'ACCEPT' + config rule option name 'Allow-ICMP-mgmt' option src 'mgmt' diff --git a/ansible/roles/openwrt/tasks/wireless.yml b/ansible/roles/openwrt/tasks/wireless.yml new file mode 100644 index 0000000..568af9a --- /dev/null +++ b/ansible/roles/openwrt/tasks/wireless.yml @@ -0,0 +1,44 @@ +--- +- name: Load IoT WiFi password from OpenBao + ansible.builtin.set_fact: + openwrt_iot_wifi_password: >- + {{ + lookup( + 'community.hashi_vault.vault_kv2_get', + openbao_fields.iot_wifi.path, + engine_mount_point=openbao_kv_mount + ).secret[openbao_fields.iot_wifi.password_key] + }} + no_log: true + +- name: Configure IoT WiFi interface (szafa, WPA2, network iot) + community.openwrt.uci: + command: section + config: wireless + type: wifi-iface + name: iot_radio0 + find: + device: radio0 + ssid: szafa + value: + device: radio0 + network: iot + mode: ap + ssid: szafa + encryption: psk2 + key: "{{ openwrt_iot_wifi_password }}" + disabled: '0' + replace: true + notify: Reload wireless + +- name: Enable radio0 + community.openwrt.uci: + command: set + key: wireless.radio0.disabled + value: '0' + notify: Reload wireless + +- name: Commit wireless config + community.openwrt.uci: + command: commit + key: wireless diff --git a/ansible/roles/openwrt/vars/main.yml b/ansible/roles/openwrt/vars/main.yml new file mode 100644 index 0000000..01b1e20 --- /dev/null +++ b/ansible/roles/openwrt/vars/main.yml @@ -0,0 +1,9 @@ +--- +# Secret references only; actual values are loaded from OpenBao/Vault at runtime. + +openbao_kv_mount: secret + +openbao_fields: + iot_wifi: + path: openwrt_iot_wifi + password_key: password diff --git a/ansible/roles/routeros/tasks/addressing.yml b/ansible/roles/routeros/tasks/addressing.yml index b3c5267..4b5fb2f 100644 --- a/ansible/roles/routeros/tasks/addressing.yml +++ b/ansible/roles/routeros/tasks/addressing.yml @@ -24,9 +24,11 @@ - address: 192.168.3.1/24 interface: vlan3 network: 192.168.3.0 + - address: 192.168.5.1/24 + interface: vlan5 + network: 192.168.5.0 handle_absent_entries: remove handle_entries_content: remove_as_much_as_possible - ensure_order: true - name: Configure IPv6 addresses community.routeros.api_modify: @@ -43,6 +45,8 @@ - address: 2001:470:61a3:100::1/64 advertise: false interface: vlan4 + - address: ::ffff:ffff:ffff:ffff/64 + from-pool: pool1 + interface: vlan5 handle_absent_entries: remove handle_entries_content: remove_as_much_as_possible - ensure_order: true diff --git a/ansible/roles/routeros/tasks/base.yml b/ansible/roles/routeros/tasks/base.yml index 32013ff..5cfe7bb 100644 --- a/ansible/roles/routeros/tasks/base.yml +++ b/ansible/roles/routeros/tasks/base.yml @@ -8,7 +8,6 @@ - name: dockers handle_absent_entries: remove handle_entries_content: remove_as_much_as_possible - ensure_order: true - name: Configure VLAN interfaces community.routeros.api_modify: @@ -26,9 +25,12 @@ comment: SERVER LAN interface: bridge1 vlan-id: 4 + - name: vlan5 + comment: IOT + interface: bridge1 + vlan-id: 5 handle_absent_entries: remove handle_entries_content: remove_as_much_as_possible - ensure_order: true - name: Configure interface lists community.routeros.api_modify: @@ -38,7 +40,6 @@ comment: contains interfaces facing internet handle_absent_entries: remove handle_entries_content: remove_as_much_as_possible - ensure_order: true - name: Configure interface list members community.routeros.api_modify: @@ -52,7 +53,6 @@ list: wan handle_absent_entries: remove handle_entries_content: remove_as_much_as_possible - ensure_order: true - name: Configure bridge ports community.routeros.api_modify: @@ -85,7 +85,6 @@ interface: ether11 handle_absent_entries: remove handle_entries_content: remove_as_much_as_possible - ensure_order: true - name: Configure bridge VLAN membership community.routeros.api_modify: @@ -95,6 +94,9 @@ tagged: sfp-sfpplus2,ether3 untagged: ether1,ether2,ether9 vlan-ids: 2 + - bridge: bridge1 + tagged: bridge1,ether3 + vlan-ids: 5 - bridge: bridge1 tagged: sfp-sfpplus2 untagged: ether10 @@ -104,7 +106,6 @@ vlan-ids: 4 handle_absent_entries: remove handle_entries_content: remove_as_much_as_possible - ensure_order: true - name: Configure IPv4 pools community.routeros.api_modify: @@ -116,9 +117,11 @@ - name: dhcp_pool1 ranges: 192.168.255.1-192.168.255.9,192.168.255.11-192.168.255.254 comment: MGMT DHCP pool + - name: dhcp_pool2 + ranges: 192.168.5.50-192.168.5.250 + comment: IOT DHCP pool handle_absent_entries: remove handle_entries_content: remove_as_much_as_possible - ensure_order: true - name: Configure DHCP servers community.routeros.api_modify: @@ -134,9 +137,13 @@ interface: bridge1 lease-time: 30m comment: MGMT + - name: dhcp3 + address-pool: dhcp_pool2 + interface: vlan5 + lease-time: 30m + comment: IOT handle_absent_entries: remove handle_entries_content: remove_as_much_as_possible - ensure_order: true - name: Configure DHCP networks community.routeros.api_modify: @@ -148,9 +155,11 @@ - address: 192.168.255.0/24 dns-none: true gateway: 192.168.255.10 + - address: 192.168.5.0/24 + dns-server: 192.168.5.1 + gateway: 192.168.5.1 handle_absent_entries: remove handle_entries_content: remove_as_much_as_possible - ensure_order: true # TODO: IPv6 pools are useful when we have dynamic prefix, but we don't # We can remove it now @@ -163,7 +172,6 @@ prefix-length: 64 handle_absent_entries: remove handle_entries_content: remove_as_much_as_possible - ensure_order: true - name: Configure DNS community.routeros.api_find_and_modify: @@ -195,7 +203,6 @@ type: internal handle_absent_entries: remove handle_entries_content: remove_as_much_as_possible - ensure_order: true - name: Configure UPnP global settings community.routeros.api_find_and_modify: @@ -217,7 +224,6 @@ type: internal handle_absent_entries: remove handle_entries_content: remove_as_much_as_possible - ensure_order: true - name: Configure IPv6 ND defaults community.routeros.api_find_and_modify: diff --git a/ansible/roles/routeros/tasks/containers.yml b/ansible/roles/routeros/tasks/containers.yml index 97d7d37..965e3d4 100644 --- a/ansible/roles/routeros/tasks/containers.yml +++ b/ansible/roles/routeros/tasks/containers.yml @@ -29,7 +29,6 @@ value: y handle_absent_entries: remove handle_entries_content: remove_as_much_as_possible - ensure_order: true - name: Configure container mounts community.routeros.api_modify: @@ -43,7 +42,6 @@ src: /tmp1/tailscale-root handle_absent_entries: remove handle_entries_content: remove_as_much_as_possible - ensure_order: true - name: Configure tailscale container community.routeros.api_modify: @@ -63,4 +61,3 @@ workdir: / handle_absent_entries: remove handle_entries_content: remove_as_much_as_possible - ensure_order: true diff --git a/ansible/roles/routeros/tasks/firewall.yml b/ansible/roles/routeros/tasks/firewall.yml index 47b5336..421f9d7 100644 --- a/ansible/roles/routeros/tasks/firewall.yml +++ b/ansible/roles/routeros/tasks/firewall.yml @@ -53,6 +53,11 @@ comment: Allow from SRV to CAM in-interface: vlan4 out-interface: vlan3 + - action: accept + chain: forward + comment: Allow from IOT to internet only + in-interface: vlan5 + out-interface-list: wan - action: accept chain: forward comment: Allow from dockers to everywhere @@ -136,6 +141,17 @@ dst-port: 53 in-interface: dockers protocol: tcp + - action: accept + chain: input + comment: Allow DNS from IOT + dst-port: 53 + in-interface: vlan5 + protocol: udp + - action: accept + chain: input + dst-port: 53 + in-interface: vlan5 + protocol: tcp - action: accept chain: input comment: Allow BGP from SRV @@ -368,6 +384,11 @@ comment: Allow from SRV to CAM in-interface: vlan4 out-interface: vlan3 + - action: accept + chain: forward + comment: Allow from IOT to internet only + in-interface: vlan5 + out-interface-list: wan - action: accept chain: forward comment: Allow from dockers to everywhere @@ -445,6 +466,17 @@ dst-port: 53 in-interface: dockers protocol: tcp + - action: accept + chain: input + comment: Allow DNS from IOT + dst-port: 53 + in-interface: vlan5 + protocol: udp + - action: accept + chain: input + dst-port: 53 + in-interface: vlan5 + protocol: tcp - action: accept chain: input comment: Allow BGP from SRV diff --git a/ansible/roles/routeros/tasks/routing.yml b/ansible/roles/routeros/tasks/routing.yml index 5c758f9..f0dff48 100644 --- a/ansible/roles/routeros/tasks/routing.yml +++ b/ansible/roles/routeros/tasks/routing.yml @@ -64,7 +64,6 @@ routing-table: main handle_absent_entries: remove handle_entries_content: remove_as_much_as_possible - ensure_order: true - name: Configure BGP templates community.routeros.api_modify: @@ -96,4 +95,3 @@ templates: klaster handle_absent_entries: remove handle_entries_content: remove_as_much_as_possible - ensure_order: true diff --git a/ansible/roles/routeros/tasks/wan.yml b/ansible/roles/routeros/tasks/wan.yml index 9400784..ebe4783 100644 --- a/ansible/roles/routeros/tasks/wan.yml +++ b/ansible/roles/routeros/tasks/wan.yml @@ -12,7 +12,6 @@ user: "{{ routeros_pppoe_username }}" handle_absent_entries: remove handle_entries_content: remove_as_much_as_possible - ensure_order: true - name: Configure 6to4 tunnel interface community.routeros.api_modify: @@ -25,7 +24,6 @@ remote-address: 216.66.80.162 handle_absent_entries: remove handle_entries_content: remove_as_much_as_possible - ensure_order: true - name: Configure veth interface for containers community.routeros.api_modify: @@ -41,4 +39,3 @@ comment: Tailscale container handle_absent_entries: remove handle_entries_content: remove_as_much_as_possible - ensure_order: true