Search code examples
salt-project

IPtables management with SaltStack


I'm trying to configure a flexible iptables management solution with SaltStack, but I find it harder than I thought it would be.

My main requirement: to be able to have a pillar where I keep a list of IPs, which should be whitelisted for SSH access on all minions. This list of IPs will of course change every now and then: some IPs get added, some IPs are removed. The problem that I'm facing is with the removed IPs - when I remove them from the pillar file, SaltStack doesn't remove the actual whitelisting from the minions.

The only workaround I could find, was to create a new key named "removed-ips" and whenever I want to remove an IP, I would add it there. The second for loop will then remove it. Of course, this is a really nasty workaround, is there a better way of doing it?

/srv/pillar/iptables-default.sls:

iptables-default:
  whitelisted-ips:
    - '55.55.55.55'
    - '66.66.66.66'
    - '77.77.77.77'
  removed-ips:
    - '88.88.88.88'

/srv/salt/iptables-default.sls:

{% for ip in salt['pillar.get']('iptables-default:whitelisted-ips') %}
Whitelist OSF IP {{ip}} for SSH access:
  iptables.append:
    - table: filter
    - family: ipv4
    - chain: INPUT
    - jump: ACCEPT
    - match: state
    - connstate: NEW
    - source: '{{ ip }}'
    - dport: 22
    - proto: tcp
    - save: True
{% endfor %}

{% for ip in salt['pillar.get']('iptables-default:removed-ips') %}
Remove old IPs that are not needed anymore:
  iptables.delete:
    - table: filter
    - family: ipv4
    - chain: INPUT
    - jump: ACCEPT
    - match: state
    - connstate: NEW
    - source: {{ ip }}
    - dport: 22
    - proto: tcp
    - save: True
{% endfor %}

Solution

  • Rather than using salt's iptables states, I like to manage /etc/iptables/rules.v4 and v6, like this:

    firewall-ipv4:
      pkg.installed:
        - pkgs:
          - iptables
          - iptables-persistent
      file.managed:
        - name: /etc/iptables/rules.v4
        - source: salt://firewall/files/rules.jinja
        - template: jinja
        - context:
            slspath: {{ slspath }}
            family: ipv4
      cmd.wait:
        - name: iptables-restore rules.v4
        - cwd: /etc/iptables
        - order: last
        - watch: 
          - file: firewall-ipv4
    
    {{ similar for v6... }}
    

    Where rules.jinja generates the ruleset from pillar. The benefit of this method is that it does the Right Thing when pillar rules are removed, without requiring a flush (i.e. a change) on every highstate. The downside is that it won't notice and revert manual changes to the firewall from the local machine.

    I have a formula using the technique here. Ignore the readme note about compatibility issues, it works fine on current salt. Or did last time I checked.