From 9c8f075fb17b502f4dab4c9b87422a1b48f04363 Mon Sep 17 00:00:00 2001 From: Lumpiasty Date: Thu, 14 May 2026 01:15:54 +0200 Subject: [PATCH] feat(ansible): add internet access for dlink --- ansible/playbooks/dlink-init.yml | 7 ++ ansible/roles/openwrt/tasks/firewall.yml | 40 +++++++---- ansible/roles/openwrt/tasks/network.yml | 73 ++++++++++++++++++--- ansible/roles/routeros/tasks/addressing.yml | 6 ++ ansible/roles/routeros/tasks/base.yml | 7 ++ ansible/roles/routeros/tasks/firewall.yml | 32 +++++++++ 6 files changed, 142 insertions(+), 23 deletions(-) diff --git a/ansible/playbooks/dlink-init.yml b/ansible/playbooks/dlink-init.yml index ae38f5f..77e60c1 100644 --- a/ansible/playbooks/dlink-init.yml +++ b/ansible/playbooks/dlink-init.yml @@ -17,6 +17,13 @@ vars: ansible_host: "192.168.1.1" ansible_user: root + # Role defaults are not loaded when importing role task files directly. + # These must mirror roles/openwrt/defaults/main.yml. + openwrt_mgmt_ip: 192.168.255.11 + openwrt_mgmt_prefix: 24 + openwrt_mgmt_gateway: 192.168.255.10 + openwrt_dns_servers: + - 192.168.0.1 tasks: - name: Verify connectivity diff --git a/ansible/roles/openwrt/tasks/firewall.yml b/ansible/roles/openwrt/tasks/firewall.yml index e94ceb1..420f7e9 100644 --- a/ansible/roles/openwrt/tasks/firewall.yml +++ b/ansible/roles/openwrt/tasks/firewall.yml @@ -1,19 +1,24 @@ --- -# This device is a pure AP — no routing, no NAT, no internet-facing interface. +# This device is a pure AP — no routing, no NAT. # # Zones: -# mgmt — management interface (192.168.255.11) -# input: ACCEPT (SSH, ping reachable from MGMT network) -# forward: REJECT (nothing routes through mgmt) +# mgmt — management interface (192.168.255.11) +# input: ACCEPT (SSH, ping reachable from MGMT network) +# forward: REJECT (nothing routes through mgmt) # -# lan — client bridge (eth0.2, LAN ports) -# input: REJECT (clients cannot SSH into the AP itself) -# forward: ACCEPT (traffic passes through to MikroTik for firewalling) +# lan — client bridge (eth0.2, LAN ports) +# input: REJECT (clients cannot SSH into the AP itself) +# forward: ACCEPT (traffic passes through to MikroTik for firewalling) # -# 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) +# 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) +# +# uplink — internet uplink via MikroTik vlan6 (192.168.6.2/24) +# input: REJECT (no inbound connections from internet side) +# output: ACCEPT (AP itself initiates outbound — opkg, NTP, etc.) +# forward: REJECT (AP does not route client traffic through uplink) # # No forwarding rules between zones — all inter-zone policy is on MikroTik. @@ -52,10 +57,17 @@ option output 'ACCEPT' option forward 'ACCEPT' + config zone + option name 'uplink' + list network 'uplink' + option input 'REJECT' + option output 'ACCEPT' + option forward 'REJECT' + config rule - option name 'Allow-ICMP-mgmt' - option src 'mgmt' - option proto 'icmp' + option name 'Allow-ICMPv6-uplink' + option src 'uplink' + option proto 'icmpv6' option target 'ACCEPT' notify: Reload firewall diff --git a/ansible/roles/openwrt/tasks/network.yml b/ansible/roles/openwrt/tasks/network.yml index 3249cc0..30e5cc5 100644 --- a/ansible/roles/openwrt/tasks/network.yml +++ b/ansible/roles/openwrt/tasks/network.yml @@ -1,17 +1,19 @@ --- -# Network layout: +# Network layout: # MikroTik ether3 ↔ dlink WAN port (switch0 port4) -# MikroTik sends MGMT traffic untagged, vlan2 (LAN) and vlan5 (IOT) tagged. +# MikroTik sends MGMT traffic untagged, vlan2/vlan5/vlan6 tagged. # # switch0 VLAN table: -# VLAN 1 (MGMT): CPU(6) tagged, WAN(4) untagged → eth0.1 → mgmt -# VLAN 2 (LAN): CPU(6) tagged, WAN(4) tagged, LAN1-4(0-3) untagged → eth0.2 → br-lan → lan -# VLAN 5 (IOT): CPU(6) tagged, WAN(4) tagged → eth0.5 → br-iot → iot +# VLAN 1 (MGMT): CPU(6) tagged, WAN(4) untagged → eth0.1 → mgmt +# VLAN 2 (LAN): CPU(6) tagged, WAN(4) tagged, LAN1-4(0-3) untagged → eth0.2 → br-lan → lan +# VLAN 5 (IOT): CPU(6) tagged, WAN(4) tagged → eth0.5 → br-iot → iot +# VLAN 6 (UPLINK): CPU(6) tagged, WAN(4) tagged → eth0.6 → uplink # # Interfaces: -# mgmt — static 192.168.255.11/24 on eth0.1, management -# lan — bridge (br-lan) on eth0.2, LAN clients via LAN ports -# iot — bridge (br-iot) on eth0.5, IoT clients via wifi only +# mgmt — static 192.168.255.11/24 on eth0.1, management +# lan — bridge (br-lan) on eth0.2, LAN clients via LAN ports +# iot — bridge (br-iot) on eth0.5, IoT clients via wifi only +# uplink — static 192.168.6.2/24 + 2001:470:61a3:600::2/64 on eth0.6, internet access for opkg - name: Configure network community.openwrt.uci: @@ -55,6 +57,13 @@ option description 'iot' option ports '4t 6t' + config switch_vlan + option device 'switch0' + option vlan '6' + option vid '6' + option description 'uplink' + option ports '4t 6t' + config device option name 'br-lan' option type 'bridge' @@ -64,9 +73,46 @@ option device 'eth0.1' option proto 'static' option ipaddr '{{ openwrt_mgmt_ip }}/{{ openwrt_mgmt_prefix }}' - option gateway '{{ openwrt_mgmt_gateway }}' option dns '{{ openwrt_dns_servers | join(" ") }}' + # Policy routing for mgmt interface. + # + # Without this, replies to traffic destined for 192.168.255.11 (mgmt IP) + # would be sent via the default route (eth0.6/uplink, src 192.168.6.2) + # instead of back through eth0.1. This is because mgmt clients (e.g. PCs + # on 192.168.0.0/24) are not on the directly connected 192.168.255.0/24 + # subnet — they reach 192.168.255.11 via MikroTik routing, so the kernel + # has no connected route matching the reply destination and falls back to + # the default route, causing asymmetric routing. + # + # ip4table cannot be used here — it generates rules matching only the + # interface IP (from 192.168.255.11) and destination (to 192.168.255.11/24), + # not the source subnet needed for return traffic from arbitrary clients. + # Instead we manually add a rule matching any traffic sourced from the mgmt + # subnet and a default route in table 100 via the MikroTik mgmt gateway. + # Same-subnet traffic (src and dst both in 192.168.255.0/24) must stay in + # main table so replies go directly out eth0.1 without being redirected. + # Priority 500 ensures this fires before the catch-all rule below (1000). + config rule + option src '192.168.255.0/24' + option dest '192.168.255.0/24' + option lookup 'main' + option priority '500' + + # All other traffic sourced from 192.168.255.0/24 (i.e. replies to clients + # outside this subnet, routed via MikroTik) uses table 100 which has a + # default route back via eth0.1 to prevent asymmetric routing. + config rule + option src '192.168.255.0/24' + option lookup '100' + option priority '1000' + + config route + option table '100' + option interface 'mgmt' + option target '0.0.0.0/0' + option gateway '{{ openwrt_mgmt_gateway }}' + config interface 'lan' option device 'br-lan' option proto 'none' @@ -80,6 +126,15 @@ option device 'br-iot' option proto 'none' + config interface 'uplink' + option device 'eth0.6' + option proto 'static' + option ipaddr '192.168.6.2/24' + option gateway '192.168.6.1' + option dns '192.168.6.1' + option ip6addr '2001:470:61a3:600::2/64' + option ip6gw '2001:470:61a3:600::1' + notify: Reload network - name: Commit network config diff --git a/ansible/roles/routeros/tasks/addressing.yml b/ansible/roles/routeros/tasks/addressing.yml index 4b5fb2f..7cdc6b3 100644 --- a/ansible/roles/routeros/tasks/addressing.yml +++ b/ansible/roles/routeros/tasks/addressing.yml @@ -27,6 +27,9 @@ - address: 192.168.5.1/24 interface: vlan5 network: 192.168.5.0 + - address: 192.168.6.1/24 + interface: vlan6 + network: 192.168.6.0 handle_absent_entries: remove handle_entries_content: remove_as_much_as_possible @@ -48,5 +51,8 @@ - address: ::ffff:ffff:ffff:ffff/64 from-pool: pool1 interface: vlan5 + - address: 2001:470:61a3:600::1/64 + advertise: false + interface: vlan6 handle_absent_entries: remove handle_entries_content: remove_as_much_as_possible diff --git a/ansible/roles/routeros/tasks/base.yml b/ansible/roles/routeros/tasks/base.yml index 5cfe7bb..84c2351 100644 --- a/ansible/roles/routeros/tasks/base.yml +++ b/ansible/roles/routeros/tasks/base.yml @@ -29,6 +29,10 @@ comment: IOT interface: bridge1 vlan-id: 5 + - name: vlan6 + comment: OPENWRT UPLINK + interface: bridge1 + vlan-id: 6 handle_absent_entries: remove handle_entries_content: remove_as_much_as_possible @@ -97,6 +101,9 @@ - bridge: bridge1 tagged: bridge1,ether3 vlan-ids: 5 + - bridge: bridge1 + tagged: bridge1,ether3 + vlan-ids: 6 - bridge: bridge1 tagged: sfp-sfpplus2 untagged: ether10 diff --git a/ansible/roles/routeros/tasks/firewall.yml b/ansible/roles/routeros/tasks/firewall.yml index 421f9d7..03ecdfa 100644 --- a/ansible/roles/routeros/tasks/firewall.yml +++ b/ansible/roles/routeros/tasks/firewall.yml @@ -58,6 +58,11 @@ comment: Allow from IOT to internet only in-interface: vlan5 out-interface-list: wan + - action: accept + chain: forward + comment: Allow from OPENWRT UPLINK to internet only + in-interface: vlan6 + out-interface-list: wan - action: accept chain: forward comment: Allow from dockers to everywhere @@ -152,6 +157,17 @@ dst-port: 53 in-interface: vlan5 protocol: tcp + - action: accept + chain: input + comment: Allow DNS from OPENWRT UPLINK + dst-port: 53 + in-interface: vlan6 + protocol: udp + - action: accept + chain: input + dst-port: 53 + in-interface: vlan6 + protocol: tcp - action: accept chain: input comment: Allow BGP from SRV @@ -389,6 +405,11 @@ comment: Allow from IOT to internet only in-interface: vlan5 out-interface-list: wan + - action: accept + chain: forward + comment: Allow from OPENWRT UPLINK to internet only + in-interface: vlan6 + out-interface-list: wan - action: accept chain: forward comment: Allow from dockers to everywhere @@ -477,6 +498,17 @@ dst-port: 53 in-interface: vlan5 protocol: tcp + - action: accept + chain: input + comment: Allow DNS from OPENWRT UPLINK + dst-port: 53 + in-interface: vlan6 + protocol: udp + - action: accept + chain: input + dst-port: 53 + in-interface: vlan6 + protocol: tcp - action: accept chain: input comment: Allow BGP from SRV