CONFIGURATION
Variables by use case
Change the variables below to describe the firewall. Each example maps to files currently present in this repository.
Give an interface an address
Put node-specific addresses in host_vars/fw1/network.yml and host_vars/fw2/network.yml. The role writes systemd-networkd configuration from these values.
network_interfaces:
- name: eth0
address: 10.10.10.3
netmask: 255.255.255.0
gateway: 10.10.10.1
- name: eth1
address: 10.20.10.3
netmask: 255.255.255.0
Change the address, mask and gateway for the target node. Keep the interface names identical on both HA nodes unless the templates and inventory explicitly account for the difference.
Define reusable firewall objects
Use group_vars/grp1/nftables/10_objects.yml. Hosts receive names, host groups combine them, and services define protocol and ports.
nftables_hosts:
web: 10.20.10.7
nftables_host_groups:
public_web:
hosts:
- web
nftables_services:
web:
protocols:
tcp: [80, 443]
The name public_web can then be used by a forward rule. Do not put an IP directly in every rule when the same destination is reused.
Publish a service
Publishing requires two files. 40_nat.yml translates the public packet. 30_forward.yml authorizes the translated packet.
nftables_nat_rules:
- chain: prerouting
source_interface: wan
protocol: tcp
ports: [443]
destination_host: web
destination_port: 443
action: dnat
nftables_forward_rules:
- chain: forward
source_interface: wan
destination_interface: lan
destination_group: public_web
service: web
protocol: tcp
action: accept
Adding DNAT without the forward rule does not publish the service. Adding the forward rule without DNAT accepts a packet that still has no backend destination.
Allow outbound NAT
Masquerade belongs in 40_nat.yml; forwarding permission belongs in 30_forward.yml.
nftables_nat_rules:
- chain: postrouting
source_network: 10.20.10.0/24
destination_interface: wan
action: masquerade
nftables_forward_rules:
- chain: forward
source_interface: lan
destination_interface: wan
source_network: 10.20.10.0/24
action: accept
Add a WireGuard peer
Peers are defined in group_vars/grp1/wireguard.yml. Use one unique address per peer and restrict allowed_ips to that peer.
wireguard_interfaces:
- name: wg0
address: 10.100.0.1/27
listen_port: 51820
peers:
- public_key: PEER_PUBLIC_KEY
allowed_ips:
- 10.100.0.2/32
persistent_keepalive: 25
Keep private keys in Vault. Check the result with wg show and verify that the peer handshake timestamp changes.
Enable OSPF on a node
Shared enablement belongs in group_vars/grp1/frr.yml; router identity and protocol configuration belong in host_vars/fw1/frr.yml.
frr_enabled: true
frr_daemons:
- zebra
- ospfd
frr_configs:
ospf: |
router ospf
ospf router-id 10.10.10.3
network 10.10.10.0/24 area 0
passive-interface eth1
Use vtysh -c "show ip ospf neighbor" to verify adjacency and ip route to verify kernel installation.
Configure HA
Pair-wide VIPs and passwords belong in group_vars/grp1/keepalived.yml. Priority and peer addresses belong in each host file.
keepalived_enabled: true
keepalived_virtual_ip: 10.20.10.2
keepalived_ha_interface: eth1
keepalived_node_id: fw1
keepalived_priority: 110
keepalived_ha_address: 10.20.10.3
keepalived_peer_ha_address: 10.20.10.4
Only the preferred node should have the higher priority. Validate with systemctl status keepalived and ip addr show.
Validate before applying
ansible-inventory -i inventory.yml --host fw1
ansible-playbook -i inventory.yml site.yml --syntax-check
ansible-playbook -i inventory.yml site.yml --check --diff
ansible-playbook -i inventory.yml site.yml