From a7ac6c586d54f7146419e90752f704cda4d13ed7 Mon Sep 17 00:00:00 2001 From: "Dustin C. Hatch" Date: Fri, 23 Mar 2018 10:14:46 -0500 Subject: [PATCH] dch-gw: Initial commit The *dch-gw* role, and the corresponding `dch-gw.yml` playbook, apply all of the necessary configuration to the edge router on my home network. --- dch-gw.yml | 4 + group_vars/dch-gw/dch-network.yml | 64 +++++++++ group_vars/dch-gw/dhcpd.yml | 126 ++++++++++++++++++ group_vars/dch-gw/radvd.yml | 11 ++ hosts | 11 ++ roles/dch-gw/defaults/main.yml | 1 + roles/dch-gw/files/ipv4-nat.nft | 8 ++ .../dch-gw/files/outside-address.dhcpcd-hook | 28 ++++ roles/dch-gw/handlers/main.yml | 2 + roles/dch-gw/tasks/main.yml | 39 ++++++ roles/dch-gw/templates/masquerade.nft.j2 | 5 + roles/dch-gw/templates/port-forwards.nft.j2 | 45 +++++++ 12 files changed, 344 insertions(+) create mode 100644 dch-gw.yml create mode 100644 group_vars/dch-gw/dch-network.yml create mode 100644 group_vars/dch-gw/dhcpd.yml create mode 100644 group_vars/dch-gw/radvd.yml create mode 100644 roles/dch-gw/defaults/main.yml create mode 100644 roles/dch-gw/files/ipv4-nat.nft create mode 100644 roles/dch-gw/files/outside-address.dhcpcd-hook create mode 100644 roles/dch-gw/handlers/main.yml create mode 100644 roles/dch-gw/tasks/main.yml create mode 100644 roles/dch-gw/templates/masquerade.nft.j2 create mode 100644 roles/dch-gw/templates/port-forwards.nft.j2 diff --git a/dch-gw.yml b/dch-gw.yml new file mode 100644 index 0000000..42f2fd7 --- /dev/null +++ b/dch-gw.yml @@ -0,0 +1,4 @@ +- hosts: dch-gw + roles: + - nftables + - dch-gw diff --git a/group_vars/dch-gw/dch-network.yml b/group_vars/dch-gw/dch-network.yml new file mode 100644 index 0000000..a5cab49 --- /dev/null +++ b/group_vars/dch-gw/dch-network.yml @@ -0,0 +1,64 @@ +dch_networks: + jazz: + description: Legacy network + vlan_id: 1 + ipv4_address: 172.31.0.0/27 + router_iface: vlan1 + dns_search: + - pyrocufflink.jazz + dns_servers: + - fd99:8dc7:6528::10:1 + - fd99:8dc7:6528::100:1 + dns_servers_v4: + - 172.31.0.4 + - 172.31.0.10 + sla_id: 1 + + blue: + description: pyrocufflink.blue AD domain members only + vlan_id: 30 + ipv4_address: 172.30.0.0/26 + router_iface: vlan30 + sla_id: 0 + + red: + description: Non-domain member machines + vlan_id: 101 + ipv4_address: 172.31.1.1/24 + router_iface: vlan101 + sla_id: 101 + + guest: + description: Guest Wi-Fi + vlan_id: 100 + ipv4_address: 172.24.100.0/24 + router_iface: vlan100 + + dmz: + description: DMZ + vlan_id: 254 + router_iface: vlan254 + + +nat_port_forwards: +- protocol: tcp + port: http + destination: 172.31.0.6 +- protocol: tcp + port: https + destination: 172.31.0.6 +- protocol: tcp + port: ssh + destination: 172.31.0.5 +- protocol: tcp + port: rsync + destination: 172.31.0.5 +- protocol: udp + port: 16881-16999 + destination: 172.31.0.5 +- protocol: udp + port: isakmp + destination: 172.31.0.2 +- protocol: udp + port: ipsec-nat-t + destination: 172.31.0.2 diff --git a/group_vars/dch-gw/dhcpd.yml b/group_vars/dch-gw/dhcpd.yml new file mode 100644 index 0000000..556e4ca --- /dev/null +++ b/group_vars/dch-gw/dhcpd.yml @@ -0,0 +1,126 @@ +dhcp_ddns: true + +dhcp_subnets: +# pyrocufflink.jazz +- address: 172.31.0.1/27 + pools: + - start: 172.31.0.11 + end: 172.31.0.30 + routers: + - 172.31.0.1 + dns_servers: + - 172.31.0.4 + - 172.31.0.10 + domain_name: pyrocufflink.jazz + domain_search: pyrocufflink.jazz + ntp_servers: + - 172.31.0.4 + - 172.31.0.10 + +# pyrocufflink.blue +- address: 172.30.0.0/26 + pools: + - start: 172.30.0.11 + end: 172.30.0.59 + routers: + - 172.30.0.1 + dns_servers: + - 172.30.0.4 + domain_name: pyrocufflink.blue + domain_search: pyrocufflink.blue + ntp_servers: + - 172.30.0.10 + +# pyrocufflink.red +- address: 172.31.1.0/24 + pools: + - start: 172.31.1.2 + end: 172.31.1.254 + routers: + - 172.31.1.1 + dns_servers: + - 172.31.0.4 + - 172.31.0.10 + domain_name: pyrocufflink.red + domain_search: pyrocufflink.red pyrocufflink.blue pyrocufflink.jazz + ntp_servers: + - 172.31.0.4 + - 172.31.0.10 + dynamic_hostnames: true + default_lease: 3600 + max_lease: 28800 + +# tachyglossus.net (Guest) +- address: 172.24.100.0/24 + pools: + - start: 172.24.100.2 + end: 172.24.100.254 + routers: + - 172.24.100.1 + dns_servers: + - 208.67.222.222 + - 208.67.220.220 + ntp_servers: + - 0.fedora.pool.ntp.org + - 1.fedora.pool.ntp.org + - 2.fedora.pool.ntp.org + - 3.fedora.pool.ntp.org + + +dhcp_reservations: +# pyrocufflink.jazz +- host: odette.pyrocufflink.jazz + ip_addr: 172.31.0.2 + mac_addr: 52:54:00:a5:8f:59 +- host: GSS108E.pyrocufflink.jazz + ip_addr: 172.31.0.3 + mac_addr: c0:ff:d4:c9:80:a4 +- host: tyrande.pyrocufflink.jazz + ip_addr: 172.31.0.4 + mac_addr: 52:54:00:8d:58:c9 +- host: caithe.pyrocufflink.jazz + ip_addr: 172.31.0.5 + mac_addr: 52:54:00:a0:22:a0 +- host: myala.pyrocufflink.jazz + ip_addr: 172.31.0.6 + mac_addr: 52:54:00:49:55:9a +- host: Downstairs-AP.pyrocufflink.jazz + ip_addr: 172.31.0.7 + mac_addr: 80:2a:a8:90:ed:d6 +- host: jaina.pyrocufflink.jazz + ip_addr: 172.31.0.8 + mac_addr: 08:62:66:2b:a6:eb +- host: Upstairs-AP.pyrocufflink.jazz + ip_addr: 172.31.0.9 + mac_addr: f0:9f:c2:cb:b9:b0 +- host: malfurion.pyrocufflink.jazz + ip_addr: 172.31.0.10 + mac_addr: 52:54:00:aa:5c:01 + +# pyrocufflink.blue +- host: dc0.pyrocufflink.blue + ip_addr: 172.30.0.10 + mac_addr: 52:54:00:e0:fa:f9 +- host: dc1.pyrocufflink.blue + ip_addr: 172.30.0.9 + mac_addr: b8:27:eb:0d:db:19 +- host: dns0.pyrocufflink.blue + ip_addr: 172.30.0.4 + mac_addr: 52:54:00:b8:8b:64 + + +dhcp_ddns_keys: +- name: dhcp-ddns + secret: +0zVSpY8oFrxl2F1qB8tT2HMgbuD31JurL9w4zilNCg= + +dhcp_ddns_zones: +- zone: pyrocufflink.jazz + primary: 172.31.0.4 +- zone: 0.31.0.172.in-addr.arpa + primary: 172.31.0.4 +- zone: pyrocufflink.red + primary: 172.30.0.4 + key: dhcp-ddns +- zone: 1.31.172.in-addr.arpa + primary: 172.30.0.4 + key: dhcp-ddns diff --git a/group_vars/dch-gw/radvd.yml b/group_vars/dch-gw/radvd.yml new file mode 100644 index 0000000..631e1c8 --- /dev/null +++ b/group_vars/dch-gw/radvd.yml @@ -0,0 +1,11 @@ +radvd_interfaces: +- interface: '{{ dch_networks.jazz.router_iface }}' + prefix: '::/64' + rdnss: '{{ dch_networks.jazz.dns_servers }}' + dnssl: '{{ dch_networks.jazz.dns_search }}' + +- interface: '{{ dch_networks.blue.router_iface }}' + prefix: '::/64' + +- interface: '{{ dch_networks.red.router_iface }}' + prefix: '::/64' diff --git a/hosts b/hosts index cfb8330..13e42c0 100644 --- a/hosts +++ b/hosts @@ -15,3 +15,14 @@ dns0.pyrocufflink.blue ansible_host=2605:6000:3ccc:fb00::4:1 [named-server:children] pyrocufflink-dns + +[dch-gw] + +[dhcpcd:children] +dch-gw + +[dhcpd:children] +dch-gw + +[radvd:children] +dch-gw diff --git a/roles/dch-gw/defaults/main.yml b/roles/dch-gw/defaults/main.yml new file mode 100644 index 0000000..af62751 --- /dev/null +++ b/roles/dch-gw/defaults/main.yml @@ -0,0 +1 @@ +nat_port_forwards: [] diff --git a/roles/dch-gw/files/ipv4-nat.nft b/roles/dch-gw/files/ipv4-nat.nft new file mode 100644 index 0000000..db5652c --- /dev/null +++ b/roles/dch-gw/files/ipv4-nat.nft @@ -0,0 +1,8 @@ +#! /usr/sbin/nft -f + +table nat { + chain prerouting { type nat hook prerouting priority -100; } + chain input { type nat hook input priority 100; } + chain output { type nat hook output priority -100; } + chain postrouting { type nat hook postrouting priority 100; } +} diff --git a/roles/dch-gw/files/outside-address.dhcpcd-hook b/roles/dch-gw/files/outside-address.dhcpcd-hook new file mode 100644 index 0000000..4661da5 --- /dev/null +++ b/roles/dch-gw/files/outside-address.dhcpcd-hook @@ -0,0 +1,28 @@ +# vim: set ft=sh : + +( +RULESET=/var/lib/dhcpcd/outside-address.ruleset + + +reload_nftables() { + systemctl reload nftables +} + + +write_ruleset() { + install -d "${RULESET%/*}" + printf 'define outside_address = %s\n' "${new_ip_address}" \ + > "${RULESET}" +} + + +if [ -n "${new_ip_address}" ]; then + if [ ! -f "${ruleset}" ]; then + write_ruleset + reload_nftables + elif [ "${new_ip_address}" != "${old_ip_address}" ]; then + write_ruleset + reload_nftables + fi +fi +) diff --git a/roles/dch-gw/handlers/main.yml b/roles/dch-gw/handlers/main.yml new file mode 100644 index 0000000..16be5ce --- /dev/null +++ b/roles/dch-gw/handlers/main.yml @@ -0,0 +1,2 @@ +- name: rebind dhcp leases + command: dhcpcd -n diff --git a/roles/dch-gw/tasks/main.yml b/roles/dch-gw/tasks/main.yml new file mode 100644 index 0000000..84ab9cc --- /dev/null +++ b/roles/dch-gw/tasks/main.yml @@ -0,0 +1,39 @@ +- name: ensure outside-address dhcpcd hook is installed + copy: + src=outside-address.dhcpcd-hook + dest=/usr/libexec/dhcpcd-hooks/10-outside-address + mode=0444 + notify: rebind dhcp leases +- meta: flush_handlers + +- name: ensure ipv4 forwarding is enabled + sysctl: + name=net.ipv4.conf.all.forwarding + value=1 + sysctl_file=/etc/sysctl.d/ip-forwarding.conf + state=present +- name: ensure ipv6 forwarding is enabled + sysctl: + name=net.ipv6.conf.all.forwarding + value=1 + sysctl_file=/etc/sysctl.d/ip-forwarding.conf + state=present + +- name: ensure ipv4 nat rules are configured + copy: + src=ipv4-nat.nft + dest=/etc/nftables/ruleset.d/10_ipv4-nat.nft + mode=0644 + notify: reload nftables +- name: ensure port forwards are configured + template: + src=port-forwards.nft.j2 + dest=/etc/nftables/ruleset.d/70_port-forwards.nft + mode=0644 + notify: reload nftables +- name: ensure ip masquerading is configured + template: + src=masquerade.nft.j2 + dest=/etc/nftables/ruleset.d/90_masquerade.nft + mode=0644 + notify: reload nftables diff --git a/roles/dch-gw/templates/masquerade.nft.j2 b/roles/dch-gw/templates/masquerade.nft.j2 new file mode 100644 index 0000000..082f787 --- /dev/null +++ b/roles/dch-gw/templates/masquerade.nft.j2 @@ -0,0 +1,5 @@ +table ip nat { + chain postrouting { + oif {{ ansible_default_ipv4.interface }} masquerade + } +} diff --git a/roles/dch-gw/templates/port-forwards.nft.j2 b/roles/dch-gw/templates/port-forwards.nft.j2 new file mode 100644 index 0000000..567a039 --- /dev/null +++ b/roles/dch-gw/templates/port-forwards.nft.j2 @@ -0,0 +1,45 @@ +{# vim: set sw=4 ts=4 sts=4 et : #} +include "/var/lib/dhcpcd/outside-address.ruleset" + +table ip nat { + set inside_networks { + type ipv4_addr + flags interval + elements = { +{% for name, network in dch_networks|dictsort if network.ipv4_address is defined %} + {{ network.ipv4_address }}, +{% endfor %} + } + } + + map tcp_forward { + type inet_service: ipv4_addr + flags interval + elements = { +{% for item in nat_port_forwards if item.protocol|d('tcp') == 'tcp' %} + {{ item.port }}: {{ item.destination }}, +{% endfor %} + } + } + + map udp_forward { + type inet_service: ipv4_addr + flags interval + elements = { +{% for item in nat_port_forwards if item.protocol|d('tcp') == 'udp' %} + {{ item.port }}: {{ item.destination }}, +{% endfor %} + } + } + + chain prerouting { + ip daddr $outside_address dnat tcp dport map @tcp_forward + ip daddr $outside_address dnat udp dport map @udp_forward + } + + chain postrouting { +{% for item in nat_port_forwards %} + ip saddr @inside_networks ip daddr {{ item.destination }} {{ item.protocol|d('tcp') }} dport {{ item.port }} masquerade +{% endfor %} + } +}