I use this role to configure my home router, a PC Engines APU2 running OpenBSD. Notable functionality includes:
- Dual-stack IPv4/IPv6
- Wired and wireless interfaces bridged using veb(4)
- Local DNS resolver with local records and domain blocking
- A pf configuration with NAT and simple firewall rules
This role is designed only for my personal use and contains various hardcoded assumptions and limitations. Use at your own risk.
The machine configured here has three NICs, em0
, em1
, and em2
, and
a wireless interface, athn0
. em0
is the egress, and em1
, em2
, and
athn0
are all connected via a veb(4) bridge with a vport(4) interface
named vport0
.
The target system must be prepared with a Python installation, SSH access, a suitable doas(1) configuration, and any other prerequisites required for use with Ansible.
hostname
: The router's hostname.search_domain
: FQDN to use for DNS search.root_email
: Email address to use for the root user and various aliases.smtpd_secrets
: Any variable definitions containing secrets for inclusion in/etc/smtpd.conf
. See the sample playbook below for an example.smtp_host
: SMTP host specification for smtp relay. See the example below and smtpd.conf(5).mail_from
: SMTPMAIL FROM
address to use when sending mail.root_from_addr
: "From" address to set in .mailrc for root user.wifi
: Wireless network configuration parameters. See hostname.if(5) and ifconfig(8).key
: WPA keynwid
: Network IDchan
: Channelenabled
: Whether to bring the interface up. Optional, defaults totrue
.
vport0
: Configuration parameters for the subnet assigned to the vport interface attached to the veb(4) bridge.ipv4
: IPv4 configuration parameters.addr
: The interface's IPv4 address.subnet
: IPv4 subnet address.netmask
: The subnet's netmask.broadcast_addr
: The subnet's broadcast address.cidr
: The subnet expressed in CIDR notation.
ipv6
: IPv6 configuration parameters.ula
: A unique local address for the vport0 interface conforming to RFC 4193.prefixlen
: The ULA subnet's prefix length.cidr
: The ULA subnet expressed in CIDR notation.
dhcpd
: dhcpd(8) configuration.dynamic
: Range definition for dynamically assigned IP addresses.start
: The lowest address in the range.end
: The highest address in the range.
static
: A list of static IP address assignment configurations. Each configuration requires the following variables:name
: Hostname.hw_addr
: MAC address.ip_addr
: IP address to assign.
static_port_ips
: A list of IP addresses for which static ports should be used in network address translation, required for some games to function properly.local_dns_records
: A list of configurations for DNS records to be served by unbound(8) on the local network. Parameters include:type
: A or AAAA.domain
: The domain for the record.addr
: The IP address for the record.ptr
: Whether to create a corresponding PTR record. Optional, defaults totrue
.
nameservers
: A list of IP addresses of upstream DNS servers thatunbound(8)
should forward requests to. An item may be specified as either a string or a dict. If a string is specified, it should be a plain IP address, and port 853 (for DNS over TLS) is assumed. If a dict is specified, it must have two keys:addr
: The server's IP address.port
: The server's port number.
blocked_domains
: A list of domains to block via unbound(8).
This playbook configures a machine named router.example.com with internal subnets at 192.168.1.0/24 and fdfd:64a6:2917::/64.
- hosts: servers
vars:
hostname: router.example.com
search_domain: example.com.
root_email: "user@example.com"
# mail_password should be stored in a vault.
smtpd_secrets: mymailauth:user@example.com:{{ mail_password }}
smtp_host: "smtp+tls://mymailauth@smtp.example.com:587"
mail_from: "user@example.com"
root_from_addr: "user@example.com"
wifi:
# Key should be stored in a vault.
key: "{{ my_wifi_password }}"
nwid: myssid
chan: 132
vport0:
ipv4:
addr: 192.168.1.1
subnet: 192.168.1.0
netmask: 255.255.255.0
broadcast_addr: 192.168.1.255
cidr: 192.168.1.0/24
ipv6:
# This should be a unique RFC 4193-compliant subnet.
ula: fdfd:64a6:2917::1
prefixlen: 64
cidr: fdfd:64a6:2917::/64
dhcpd:
# Hand out 192.168.1.50-192.168.1.100 (inclusive) as dynamic
# addresses.
dynamic:
start: 192.168.1.50
end: 192.168.1.100
# Assign some static addresses based on MAC addresses.
static:
- name: wireless-access-point
hw_addr: 28:10:89:8F:34:8E
ip_addr: 192.168.1.2
- name: desktop
hw_addr: 16:F5:E4:0B:19:3C
ip_addr: 192.168.1.3
# Don't rewrite the source port on packets when natting to desktop.
static_port_ips:
- 192.168.1.3
# Set up some local DNS records so we can find our router on the LAN.
local_dns_records:
- type: A
domain: router.example.com
addr: "{{ vport0.ipv4.addr }}"
- type: AAAA
domain: router.example.com
addr: "{{ vport0.ipv6.ula }}"
# Configure upstream DNS servers to which unbound should forward
# requests.
nameservers:
# Verbose format -- allows port to be specified.
- addr: 1.1.1.1
port: 853
# Simple format: port 853 is assumed.
- 8.8.8.8
- 2001:4860:4860::8888
# Tell unbound to refuse queries for certain domains.
blocked_domains:
- blocked.domain.example
- another-blocked-domain.example
roles:
- router
ISC
Benjamin Linskey contact@linskey.org