Ansible sysctl tries to reload ipv6, even though ipv6 is disabled

2.6k views Asked by At

Consider this:

I have a RHEL8 server where ipv6 is disabled. I'm running some ansible scripts for security compliance.

Ansible check looks like this :

- name: "SCORED | 3.1.1 | PATCH | Ensure IP forwarding is disabled"
  block:
  - name: "SCORED | 3.1.1 | PATCH | Ensure IP forwarding is disabled | Disable IPv4 forwarding"
    sysctl:
        name: '{{ item.name }}'
        value: '{{ item.value }}'
        state: present
        reload: yes
        ignoreerrors: yes
    with_items:
        - { name: net.ipv4.ip_forward, value: 0 }
        - { name: net.ipv4.route.flush, value: 1}

    notify:
        - sysctl flush ipv4 route table

  - name: "SCORED | 3.1.1 | PATCH | Ensure IP forwarding is disabled | Disable IPv6 forwarding"
    sysctl:
        name: '{{ item.name }}'
        value: '{{ item.value }}'
        state: present
        reload: yes
        ignoreerrors: yes
    with_items:
        - { name: net.ipv6.conf.all.forwarding, value: 0 }
        - { name: net.ipv6.route.flush, value: 1}
    when:
        - rhel8cis_ipv6_required
    notify:
        - sysctl flush ipv6 route table
  when:
    - not rhel8cis_is_router
    - rhel8cis_rule_3_1_1
  tags:
    - level1
    - sysctl
    - patch
    - rule_3.1.1

Variable

rhel8cis_ipv6_required is set to false.

The handlers that are being called look like this:

- name: sysctl flush ipv4 route table
  become: yes
  sysctl:
    name: net.ipv4.route.flush
    value: 1
    sysctl_set: yes
  when: ansible_virtualization_type != "docker"

- name: sysctl flush ipv6 route table
  become: yes
  sysctl:
    name: net.ipv6.route.flush
    value: 1
    sysctl_set: yes
  when: ansible_virtualization_type != "docker"

The output of the playbook looks like this:

TASK [RHEL8_CIS : SCORED | 3.1.1 | PATCH | Ensure IP forwarding is disabled | Disable IPv4 forwarding] ************************************************************************************************************************************** ok: [alrha001.acc.vlkintern.nl] => (item={'name': 'net.ipv4.ip_forward', 'value': 0}) changed: [alrha001.acc.vlkintern.nl] => (item={'name': 'net.ipv4.route.flush', 'value': 1})

TASK [RHEL8_CIS : SCORED | 3.1.1 | PATCH | Ensure IP forwarding is disabled | Disable IPv6 forwarding] ************************************************************************************************************************************** skipping: [alrha001.acc.vlkintern.nl] => (item={'name': 'net.ipv6.conf.all.forwarding', 'value': 0}) skipping: [alrha001.acc.vlkintern.nl] => (item={'name': 'net.ipv6.route.flush', 'value': 1})

RUNNING HANDLER [RHEL8_CIS : sysctl flush ipv4 route table] ********************************************************************************************************************************************************************************* [WARNING]: The value 1 (type int) in a string field was converted to '1' (type string). If this does not look like what you expect, quote the entire value to ensure it does not change.

fatal: [alrha001.acc.vlkintern.nl]: FAILED! => {"changed": false, "msg": "Failed to reload sysctl: fs.suid_dumpable = 0\nkernel.randomize_va_space = 2\nnet.ipv4.conf.all.forwarding = 0\nnet.ipv4.conf.all.send_redirects = 0\nnet.ipv4.conf.default.send_redirects = 0\nnet.ipv4.conf.all.accept_source_route = 0\nnet.ipv4.conf.default.accept_source_route = 0\nnet.ipv4.conf.all.accept_redirects = 0\nnet.ipv4.conf.default.accept_redirects = 0\nnet.ipv4.conf.all.secure_redirects = 0\nnet.ipv4.conf.default.secure_redirects = 0\nnet.ipv4.conf.all.log_martians = 1\nnet.ipv4.conf.default.log_martians = 1\nnet.ipv4.icmp_echo_ignore_broadcasts = 1\nnet.ipv4.icmp_ignore_bogus_error_responses = 1\nnet.ipv4.conf.all.rp_filter = 1\nnet.ipv4.conf.default.rp_filter = 1\nnet.ipv4.tcp_syncookies = 1\nnet.ipv4.route.flush = 1\nnet.ipv4.ip_forward = 0\nsysctl: cannot stat /proc/sys/net/ipv6/route/flush: No such file or directory\nsysctl: cannot stat /proc/sys/net/ipv6/conf/all/forwarding: No such file or directory\n"}

And this is where I'm completely confused. As you can see, the handler for ipv4 is executed. All variables shown in the fatal output pertain to ipv4. Yet, at the end, the handler complains that it cannot find ipv6 files. Which is correct, because on this server ipv6 is not enabled.

What is wrong here ?

2

There are 2 answers

2
Liam Kelly On

From what I can see you are asking the IPv6 settings to be modified; however, beacuse you do not have IPv6 enabled those settings do not exist. Take a look at the IPv6 flush statement:

- name: sysctl flush ipv6 route table
  become: yes
  sysctl:
    name: net.ipv6.route.flush
    value: 1
    sysctl_set: yes
  when: ansible_virtualization_type != "docker"

Under the hood, sysctl performs this action by using the special file /proc/sys/net/ipv6/route/flush in procfs. This file, as well as everything in /proc/sys/net/ipv6/, does not exist unless IPv6 is enabled. I think your other IPv6 statements are failing for the same reason, they are trying to access parameters in /proc/sys/net/ipv6/ that do not exist.

0
jursetto On

It's likely you have an ipv6 key in your /etc/sysctl.conf, maybe from before this remediation, or caused by it earlier. That will normally be ignored, but it happens to cause a failure here due to a combination of factors.

The ansible sysctl module always tries to update a sysctl file on disk, by default /etc/sysctl.conf. It has no provision for a temporary change. Here the handler is trying to make a point-in-time change (route flush), but it will incorrectly make a permanent change by writing this update to /etc/sysctl.conf. The permanent change causes the file to be reloaded (because reload: defaults to yes) and the sysctl module then fails because it is very strict about every key working. So it's detecting your unrelated ipv6 key and failing.

In short, this is a handler bug. Change the handler to use the sysctl command directly and it should work.

- name: sysctl flush ipv4 route
  command: sysctl -w net.ipv4.route.flush=1
  ...

It's also possible to use ignoreerrors: yes with the sysctl module so that the handler doesn't crash, or even reload: no. But that still makes the change permanent, so the proper solution is to manually issue the sysctl. In fact, Red Hat explicitly says you can't use this flush key in /etc/sysctl.conf. (Link behind their paywall, unfortunately.)

For the same reason, you might need to set ignoreerrors: yes if you ever write a playbook that actually needs to modify /etc/sysctl.conf, and if you could have an invalid key in there.