Ansible Role for managing an opiniated Alpine Router
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Simon Cornet a36cc2ac3b
All checks were successful
ci/woodpecker/push/linting Pipeline was successful
docs: improve readme
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-05 17:29:21 +02:00
.woodpecker chore(package): update cr.simoncor.net/dockerhub/davidanson/markdownlint-cli2 docker tag to v0.23.3 2026-09-20 05:50:52 +00:00
defaults feat(firewall): rate limit icmp on router 2026-09-21 09:56:25 +02:00
handlers feat: remove static routes 2026-02-17 18:09:55 +01:00
meta chore: fix galaxy meta information 2026-02-16 14:50:40 +01:00
roles ci: migrate from gitlab ci to woodpecker 2026-05-15 14:13:51 +02:00
tasks feat(routing): set loose reverse path filtering (rp_filter=2) 2026-09-30 10:11:06 +02:00
templates fix: incomign rule template 2026-09-21 10:13:09 +02:00
.ansible-lint fix: exclude only sops yml files from lint 2026-07-14 16:23:57 +02:00
.editorconfig chore: add .editorconfig and .gitattributes 2026-05-27 14:24:02 +02:00
.gitattributes chore: add .editorconfig and .gitattributes 2026-05-27 14:24:02 +02:00
.gitignore chore: add basic role layout for ans-router 2026-02-16 14:15:02 +01:00
.markdownlint-cli2.jsonc ci: migrate from gitlab ci to woodpecker 2026-05-15 14:13:51 +02:00
.sops.yaml fix: correct sops regex to ya?ml 2026-07-14 16:02:04 +02:00
.yamllint chore: exclude .yml files from lint configs 2026-07-14 16:17:40 +02:00
AGENTS.md chore: restructure tasks - install, routing, firewall, performance 2026-02-16 14:17:09 +01:00
ansible.cfg chore: sync linting and config files from common 2026-07-14 11:33:15 +02:00
playbook.yaml chore(playbook): run galaxy install only on first host 2026-08-13 08:49:04 +02:00
readme.md docs: improve readme 2026-10-05 17:29:21 +02:00
renovate.json chore: add basic role layout for ans-router 2026-02-16 14:15:02 +01:00

Ansible Role: Router

Configures an Alpine Linux host as a router: IPv4 forwarding, an nftables firewall with NAT (masquerade and port forwards), conntrack sizing and network performance tuning.

Requirements

  • Alpine Linux: packages are installed with apk (nftables).
  • Collections community.general and ansible.posix (modprobe, apk, sysctl).
  • Two interfaces: a WAN and a LAN interface.

Dependencies

None (dependencies: [] in meta/main.yaml). roles/requirements.yml only fetches this role itself from https://git.simoncor.net/ansible/router.git.

Variables

Variable Required Default Description
wan_interface Yes eth0 WAN network interface
lan_interface Yes eth1 LAN network interface
nat_port_forwards No [] List of port forwards from the WAN to hosts on the LAN
incoming_firewall_rules No [] List of extra rules that allow traffic to the router itself
icmp_rate_limit_enabled No true Rate limit ICMP and ICMPv6 on the WAN; when false all ICMP is accepted
icmp_rate_limit_rate No "50" Accepted ICMP packets per second on the WAN
icmp_rate_limit_burst No "5" ICMP burst size in packets
wireguard_enabled No true Allow forwarding for interfaces named wt* (WireGuard) incl. exit node
dhcp_enable No false Accept DHCP requests (UDP 67) on the LAN interface

nat_port_forwards

Key Required Default Description
name Yes Description, used as a comment in the ruleset
dst Yes LAN destination IP address
port Yes Port on the WAN, DNAT'ed to the same port on dst
protocol No tcp Protocol

incoming_firewall_rules

Key Required Default Description
name Yes Description, used as a comment in the ruleset
source Yes Source address or CIDR
port Yes Destination port on the router
protocol No tcp Protocol
interface No lan_interface Interface the traffic arrives on

Example

wan_interface: "eth0"
lan_interface: "eth1"

icmp_rate_limit_enabled: true
icmp_rate_limit_rate: "50"
icmp_rate_limit_burst: "5"

wireguard_enabled: true
dhcp_enable: false

incoming_firewall_rules:
  - name: "allow ssh from lan"
    source: "192.168.1.0/24"
    protocol: "tcp"
    port: 22
  - name: "allow dns from lan"
    source: "192.168.1.0/24"
    protocol: "udp"
    port: 53

nat_port_forwards:
  - name: "forward https to web server"
    dst: "192.168.1.10"
    port: 443
    protocol: "tcp"

Usage

Run playbook.yaml through Semaphore. The playbook first runs ansible-galaxy install -f -r roles/requirements.yml on localhost, then includes the router role. It runs with serial: 1, so routers are configured one at a time.

Operational notes

  • The role imports three task files in this order: routing.yaml, firewall.yaml, performance.yaml. None of the tasks carry tags.
  • Routing: installs nftables, enables IPv4 forwarding, disables ICMP redirects and source routing, sets loose reverse path filtering (rp_filter=2). Settings are written to /etc/sysctl.conf.
  • Firewall: renders /etc/nftables.nft (mode 0600) and restarts nftables on change. Input and forward policies are drop, output is accept. LAN to WAN is forwarded and masqueraded. The ruleset is flushed on load.
  • Conntrack: loads nf_conntrack (also at boot), sets hashsize=16384, nf_conntrack_max=16384 and timeouts for established (3600) and generic (120) connections.
  • Performance: larger socket buffers, BBR congestion control with the fq qdisc, ECN and a few other TCP settings.
  • The role does not enable the nftables service at boot; it only restarts it when the ruleset changes.