370 Configure pf

Use case

Use the role vbotka.freebsd.pf to configure pf.

Tree

shell> tree .
.
├── ansible.cfg
├── host_vars
│   └── iocage_04.yml
├── iocage.ini
└── pb.yml

Synopsis

  • The Ansible controller connects to the iocage host iocage_04 at IP 10.1.0.29 configured in /etc/rc.conf:

    cloned_interfaces="bridge0"
    create_args_bridge0="addm igb0"
    ifconfig_bridge0="inet 10.1.0.29/24"
    ifconfig_igb0="up -tso -vlanhwtso"
    
  • In the playbook pb.yml for iocage_04, configure:

    • blacklistd, fail2ban, and sshguard

    • NAT

    • log all blocked

    • pass from localnet to any

Requirements

Notes

  • TBD

Note

vbotka.freebsd_pf is the role freebsd_pf in the namespace vbotka.

See also

ansible.cfg

[defaults]
callback_result_format = yaml
display_skipped_hosts = False
gathering = explicit
interpreter_python = auto_silent
log_path = /var/log/ansible.log
deprecation_warnings = False

[connection]
pipelining = true

Inventory iocage.ini

iocage_04 ansible_host=10.1.0.29

[iocage]
iocage_04

[iocage:vars]
ansible_user=admin
ansible_become=true
ansible_python_interpreter=auto_silent

host_vars

host_vars/iocage_04.yml
pf_install: false
pf_blacklistd: true
pf_fail2ban: true
pf_sshguard: true

pf_enable: true
pf_blacklistd_enable: true
pf_fail2ban_enable: true
pf_sshguard_enable: true

pf_log_enable: true
pf_backup_conf: true

# /etc/pf.conf
pf_type: default2
pf_ext_if: bridge0
pf_log_all_blocked: log
pf_pass_icmp_types: [echoreq, unreach]
pf_pass_icmp6_types: [echoreq, unreach]
pf_local_net: 10.1.0.0/24

pf_macros:
  ext_if: "{{ pf_ext_if }}"
  localnet: "{{ pf_local_net }}"
  logall: "{{ pf_log_all_blocked }}"
  icmp_types: "{{ pf_pass_icmp_types }}"
  icmp6_types: "{{ pf_pass_icmp6_types }}"

# pf blocks
pf_options:
  - set skip on lo0
  - set block-policy return
  - set loginterface $ext_if

pf_tables:
  - table <sshabuse> persist

pf_normalization:
  - scrub in on $ext_if all fragment reassemble

pf_translation:
  - nat on $ext_if from $localnet to any -> ($ext_if)

pf_filtering:
  - antispoof for $ext_if
  - "{{ pf_anchors }}"
  - block $logall all
  - pass inet proto icmp icmp-type $icmp_types
  - pass inet6 proto icmp6 icmp6-type $icmp6_types
  - pass from { self, $localnet } to any keep state

# blacklistd
pf_blacklistd_conf_local:
  - {adr: ssh, type: stream, proto: '*', owner: '*', name: '*', nfail: '3', disable: 24h}
  - {adr: ftp, type: stream, proto: '*', owner: '*', name: '*', nfail: '3', disable: 24h}
  - {adr: smtp, type: stream, proto: '*', owner: '*', name: '*', nfail: '3', disable: 24h}
  - {adr: smtps, type: stream, proto: '*', owner: '*', name: '*', nfail: '3', disable: 24h}
  - {adr: submission, type: stream, proto: '*', owner: '*', name: '*', nfail: '3', disable: 24h}
  - {adr: '*', type: '*', proto: '*', owner: '*', name: '*', nfail: '3', disable: '60'}
pf_blacklistd_flags: '-r'
pf_blacklistd_rcconf:
  - {name: blacklistd_flags, value: "{{ pf_blacklistd_flags }}"}

Playbook pb.yml

- name: Test role vbotka.freebsd.pf
  hosts: iocage
  gather_facts: true

  roles:
    - vbotka.freebsd.pf

Playbook output - Install packages

(env) > ansible-playbook -i iocage.ini \
                         -t pf_packages -e pf_install=true \
                         pb.yml
PLAY [Test role vbotka.freebsd.pf] *********************************************

TASK [Gathering Facts] *********************************************************
ok: [iocage_04]

TASK [vbotka.freebsd.pf : Packages: Install packages.] *************************
ok: [iocage_04]

PLAY RECAP *********************************************************************
iocage_04                  : ok=2    changed=0    unreachable=0    failed=0    skipped=2    rescued=0    ignored=0   

Playbook output - Configure pf

Starting and restarting the firewall breaks active SSH connections (see the handlers for details). Consequently, both the start and reload handlers can fail to complete cleanly, causing the SSH session to go stale. Therefore, configure the rules first before enabling the service:

(env) > ansible-playbook -i iocage.ini -e pf_enable=false pb.yml
PLAY [Test role vbotka.freebsd.pf] *********************************************

TASK [Gathering Facts] *********************************************************
ok: [iocage_04]

TASK [vbotka.freebsd.pf : Rcconf-blacklistd: Stat /etc/rc.d/blacklistd] ********
ok: [iocage_04]

TASK [vbotka.freebsd.pf : Blacklistd: Backup /etc/blacklistd.conf.orig] ********
ok: [iocage_04]

TASK [vbotka.freebsd.pf : Blacklistd: Configure /etc/blacklistd.conf] **********
ok: [iocage_04]

TASK [vbotka.freebsd.pf : Rcconf-fail2ban: Stat /usr/local/etc/rc.d/fail2ban] ***
ok: [iocage_04]

TASK [vbotka.freebsd.pf : Fail2ban: Configure /usr/local/etc/fail2ban/fail2ban.local] ***
ok: [iocage_04]

TASK [vbotka.freebsd.pf : Fail2ban: Configure /usr/local/etc/fail2ban/jail.local] ***
ok: [iocage_04]

TASK [vbotka.freebsd.pf : Rcconf-sshguard: Stat /usr/local/etc/rc.d/sshguard] ***
ok: [iocage_04]

TASK [vbotka.freebsd.pf : Configure sshguard.conf] *****************************
ok: [iocage_04] => (item={'key': 'BACKEND', 'value': '/usr/local/libexec/sshg-fw-pf'})
ok: [iocage_04] => (item={'key': 'WHITELIST_FILE', 'value': '/usr/local/etc/sshguard.whitelist'})

TASK [vbotka.freebsd.pf : Pfconf: Configure and validate rules using template default2-pf.conf.j2] ***
ok: [iocage_04]

TASK [vbotka.freebsd.pf : Rcconf-blacklistd: Enable and start blacklistd.] *****
ok: [iocage_04]

TASK [vbotka.freebsd.pf : Rcconf-blacklistd: Configure blacklistd.] ************
ok: [iocage_04] => (item={'name': 'blacklistd_flags', 'value': '-r'})

TASK [vbotka.freebsd.pf : Rcconf-fail2ban: Enable and start fail2ban.] *********
ok: [iocage_04]

TASK [vbotka.freebsd.pf : Rcconf-fail2ban: Configure fail2ban.] ****************
ok: [iocage_04] => (item={'name': 'fail2ban_flags', 'value': ''})

TASK [vbotka.freebsd.pf : Rcconf-sshguard: Enable and start sshguard.] *********
ok: [iocage_04]

TASK [vbotka.freebsd.pf : Rcconf-pf: Disable and stop pf.] *********************
changed: [iocage_04]

TASK [vbotka.freebsd.pf : Rcconf-pflog: Enable and start pflog.] ***************
ok: [iocage_04]

RUNNING HANDLER [vbotka.freebsd.pf : Disable and stop pf] **********************
changed: [iocage_04]

PLAY RECAP *********************************************************************
iocage_04                  : ok=18   changed=2    unreachable=0    failed=0    skipped=39   rescued=0    ignored=0   

Playbook output - Enable pf

(env) > ansible-playbook -i iocage.ini -t pf_rcconf_pf pb.yml
PLAY [Test role vbotka.freebsd.pf] *********************************************

TASK [Gathering Facts] *********************************************************
ok: [iocage_04]

TASK [vbotka.freebsd.pf : Rcconf-pf: Enable and start pf.] *********************
changed: [iocage_04]

RUNNING HANDLER [vbotka.freebsd.pf : Start pf] *********************************
changed: [iocage_04]

PLAY RECAP *********************************************************************
iocage_04                  : ok=3    changed=2    unreachable=0    failed=0    skipped=2    rescued=0    ignored=0   

Result

pf status

shell> ssh admin@10.1.0.29 sudo service pf status
Status: Disabled for 0 days 00:00:04          Debug: Urgent

Interface Stats for bridge0           IPv4             IPv6
  Bytes In                               0                0
  Bytes Out                              0                0
  Packets In
    Passed                          233387              987
    Blocked                            394                0
  Packets Out
    Passed                               0                0
    Blocked                         174948                0

State Table                          Total             Rate
  current entries                      164               
  searches                          426626       106656.5/s
  inserts                             8549         2137.2/s
  removals                            8385         2096.2/s
Counters
  match                              12205         3051.2/s
  bad-offset                             0            0.0/s
  fragment                               0            0.0/s
  short                                  0            0.0/s
  normalize                              0            0.0/s
  memory                                 0            0.0/s
  bad-timestamp                          0            0.0/s
  congestion                             0            0.0/s
  ip-option                            107           26.8/s
  proto-cksum                          872          218.0/s
  state-mismatch                         6            1.5/s
  state-insert                           0            0.0/s
  state-limit                            0            0.0/s
  src-limit                              0            0.0/s
  synproxy                               0            0.0/s
  map-failed                             0            0.0/s
  translate                              0            0.0/s

/etc/pf.conf

shell> ssh admin@10.1.0.29 cat /etc/pf.conf
# Ansible managed
# template: default2-pf.conf.j2

# MACROS
ext_if = "bridge0"
localnet = "10.1.0.0/24"
logall = "log"
icmp_types = "{ echoreq, unreach }"
icmp6_types = "{ echoreq, unreach }"

# TABLES
table <sshabuse> persist

# OPTIONS
set skip on lo0
set block-policy return
set loginterface $ext_if

# NORMALIZATION
scrub in on $ext_if all fragment reassemble

# TRANSLATION
nat on $ext_if from $localnet to any -> ($ext_if)

# FILTERING
antispoof for $ext_if
anchor "blacklistd/*" in on $ext_if
anchor "f2b/*"
block $logall all
pass inet proto icmp icmp-type $icmp_types
pass inet6 proto icmp6 icmp6-type $icmp6_types
pass from { self, $localnet } to any keep state

# EOF