sous-chefs/firewall

Raw rules not persisted with default config

Opened this issue Β· 4 comments

πŸ‘» Brief Description

Using raw 'SOME_RULE' does not persist rules with Debian/Ubuntu. /etc/iptables/rules.v4 is created but it is not enforced. Setup fails kitchen test and I have to manually do iptables-restore (a hack in code does not work). I have tested this on both a KVM system (I thought it was libvirt) and a Docker system (I thought it was Docker) but I have tried putting the firewall recipe directives both before and after those installs, and those are common use cases anywayβ€” they should not interfere with the managed firewall.

πŸ₯ž Cookbook version

depends 'firewall', '~>2.7.1'

πŸ‘©β€πŸ³ Chef-Infra Version

16.10.17 (pinned for stability)

Version of chef-client in your environment.

🎩 Platform details

vagrant@virt-generic-debian10:~$ uname -a
Linux virt-generic-debian10 4.19.0-16-amd64 #1 SMP Debian 4.19.181-1 (2021-03-19) x86_64 GNU/Linux
vagrant@virt-generic-ubuntu2004:~$ uname -a
Linux virt-generic-ubuntu2004 5.4.0-74-generic #83-Ubuntu SMP Sat May 8 02:35:39 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

Host is Ubuntu 21.04 running Kitchen and vagrant libvirt provider (because we use KVM in prod).

Steps To Reproduce

my_cookbook::firewall.rb

include_recipe "firewall"

ssh_port = node[:my_cookbook][:sshd][:port]
ssh_interface = node[:my_cookbook][:sshd][:interface]

common_rules = [
  '-P INPUT DROP',
  '-P FORWARD DROP',
  '-P OUTPUT ACCEPT',
  "-A INPUT -i lo -m comment --comment \"allow loopback\" -j ACCEPT",
  "-A INPUT -p icmp -m icmp --icmp-type any -j DROP",
  "-A INPUT -i #{ssh_interface} -p tcp -m tcp -m multiport --dports #{ssh_port} -m state --state NEW,ESTABLISHED -m comment --comment ssh -j ACCEPT",
  "-A OUTPUT -p tcp -m tcp -m multiport --dports #{ssh_port} -m state --state RELATED,ESTABLISHED -m comment --comment ssh -j ACCEPT",
  "-A OUTPUT -o lo -j ACCEPT",
]

service "ufw" do
  action :stop
end

service "ufw" do
  action :disable
end

firewall "default" do
  action :install
end

i = 1
common_rules.each do |rule|
  firewall_rule "common_rules" do
    raw      rule
    position i
  end
  i += 1
end

attributes::firewall.rb

default[:my_cookbook][:sshd][:port] = 22
default[:my_cookbook][:sshd][:interface] = 'eth0'

default['firewall']['ubuntu_iptables'] = true
default['firewall']['redhat7_iptables'] = true
default['firewall']['allow_loopback'] = true
default['firewall']['allow_icmp'] = false
default['firewall']['allow_ssh'] = false
default['firewall']['ipv6_enabled'] = false

integration::firewall_test.rb

common_rules = [
  '-P INPUT DROP',
  '-P FORWARD DROP',
  '-P OUTPUT ACCEPT',
  '-A INPUT -i lo -m comment --comment "allow loopback" -j ACCEPT',
  '-A INPUT -p icmp -m icmp --icmp-type any -j DROP',
  '-A INPUT -i eth0 -p tcp -m tcp -m multiport --dports 22 -m state --state NEW,ESTABLISHED -m comment --comment ssh -j ACCEPT',
  '-A OUTPUT -o lo -j ACCEPT',
]

unwanted_rules = [
  '-A INPUT -p tcp -m tcp -m multiport --dports 22 -m comment --comment "allow world to ssh" -j ACCEPT'
]

control 'any node' do
  describe iptables do
    common_rules.each do |expected|
      it { should have_rule(expected) }
    end
    unwanted_rules.each do |unwanted|
      it { should_not have_rule(unwanted) }
    end
  end

  describe service('ufw') do
    it { should_not be_enabled }
    it { should_not be_running }
  end

  describe service('netfilter-persistent') do
    it { should be_enabled }
    it { should be_running }
  end

  impact 1.0
  title 'any node firewall basic rules'
  desc 'iptables is not configured properly'
end

kvm.rb

%w{ qemu-kvm libvirt-daemon-system virtinst virt-manager virt-viewer bridge-utils cloud-image-utils libguestfs-tools ifupdown screen }.each do |pkg|
  package pkg do
    action :install
  end
end
# [snip - there's a lot more here I will add if necessary but trying to MVP here]

docker.rb

docker_service 'default' do
  action [ :create, :start ]
end

.kitchen.yml

---
driver:
  name: vagrant
  provider: libvirt
provisioner:
  name: chef_zero
  product_name: chef
  product_version: 16.10.17
  always_update_cookbooks: true
  chef_license: accept
verifier:
  name: inspec
platforms:
  - name: generic/debian10
  - name: generic/ubuntu2004
suites:
  - name: example
    run_list:
      - recipe[my_cookbook::kvm]
      - recipe[my_cookbook::docker]
      - recipe[my_cookbook::firewall]
    verifier:
      inspec_tests:
        - test/integration/firewall
    attributes:
      my_cookbook:
        sshd: 
          port: 22 # https://serverfault.com/a/746699/182753
          interface: eth0

πŸš“ Expected behavior

The firewall is configured with the expected rules and passes all tests.

βž• Actual behavior

Profile: tests from {:path=>"/home/celiyah/Code/my_cookbook/chef/test/integration/firewall"} (tests from {:path=>".home.celiyah.Code.my_cookbook.chef.test.integration.firewall"})
Version: (not specified)
Target:  ssh://vagrant@192.168.121.104:22

  Γ—  any node: any node firewall basic rules (5 failed)
     Γ—  Iptables is expected to have rule "-P INPUT DROP"
     expected Iptables to have rule "-P INPUT DROP"
     βœ”  Iptables is expected to have rule "-P FORWARD DROP"
     βœ”  Iptables is expected to have rule "-P OUTPUT ACCEPT"
     Γ—  Iptables is expected to have rule "-A INPUT -i lo -m comment --comment \"allow loopback\" -j ACCEPT"
     expected Iptables to have rule "-A INPUT -i lo -m comment --comment \"allow loopback\" -j ACCEPT"
     Γ—  Iptables is expected to have rule "-A INPUT -p icmp -m icmp --icmp-type any -j DROP"
     expected Iptables to have rule "-A INPUT -p icmp -m icmp --icmp-type any -j DROP"
     Γ—  Iptables is expected to have rule "-A INPUT -i eth0 -p tcp -m tcp -m multiport --dports 22 -m state --state NEW,ESTABLISHED -m comment --comment ssh -j ACCEPT"
     expected Iptables to have rule "-A INPUT -i eth0 -p tcp -m tcp -m multiport --dports 22 -m state --state NEW,ESTABLISHED -m comment --comment ssh -j ACCEPT"
     Γ—  Iptables is expected to have rule "-A OUTPUT -o lo -j ACCEPT"
     expected Iptables to have rule "-A OUTPUT -o lo -j ACCEPT"
     βœ”  Iptables is expected not to have rule "-A INPUT -p tcp -m tcp -m multiport --dports 22 -m comment --comment \"allow world to ssh\" -j ACCEPT"
     βœ”  Service ufw is expected not to be enabled
     βœ”  Service ufw is expected not to be running
     βœ”  Service netfilter-persistent is expected to be enabled
     βœ”  Service netfilter-persistent is expected to be running


Profile Summary: 5 successful controls, 1 control failure, 0 controls skipped
Test Summary: 54 successful, 5 failures, 0 skipped

Add any other context about the problem here. e.g. related issues or existing pull requests.

You probably will notice I've contributed before :) not above submitting a PR but I do not understand what is happening here. UFW is disabled as expected. netfilter-persistent is enabled as expected. :onfire:

vagrant@virt-generic-debian10:~$ sudo iptables -S
-P INPUT ACCEPT
-P FORWARD DROP
-P OUTPUT ACCEPT
-N DOCKER
-N DOCKER-ISOLATION-STAGE-1
-N DOCKER-USER
-N DOCKER-ISOLATION-STAGE-2
-A FORWARD -j DOCKER-USER
-A FORWARD -j DOCKER-ISOLATION-STAGE-1
-A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -o docker0 -j DOCKER
-A FORWARD -i docker0 ! -o docker0 -j ACCEPT
-A FORWARD -i docker0 -o docker0 -j ACCEPT
-A DOCKER-ISOLATION-STAGE-1 -i docker0 ! -o docker0 -j DOCKER-ISOLATION-STAGE-2
-A DOCKER-ISOLATION-STAGE-1 -j RETURN
-A DOCKER-USER -j RETURN
-A DOCKER-ISOLATION-STAGE-2 -o docker0 -j DROP
-A DOCKER-ISOLATION-STAGE-2 -j RETURN

Didn't know where to fit this in above. This is on a Docker host; the libvirt host has the libvirt rules. But in both cases these should be gone, and the firewall cookbook rules in their place.

I really recommend you use the iptables cookbooks instead of this cookbook as it works much better with iptables.

I had tried it in a branch, but they don't support the raw rules. Let me give it another shotβ€” thanks, @ramereth

@chaim1221 FWIW we're trying to get that cookbook good enough to eventually be pulled into as a native resource