Disabling IPv6 vs. sshd

Hi,

When I’m not using IPv6, I usually take care of disabling it. I’m currently writing a little Ansible playbook to disable/enable IPv6 on Rocky Linux 8. On a basic system, this usually involves reconfiguring SSH and Postfix accordingly.

In the vanilly configuration, /etc/ssh/sshd_config sports the following lines:

#AddressFamily any
#ListenAddress 0.0.0.0

Now with IPv6 disabled, I usually take care of changing these two so they look like this:

AddressFamily inet
ListenAddress 0.0.0.0

And here comes the question. When disabling IPv6, is it sufficient to run a simple systemctl reload sshd? Or is it necessary to call a systemctl restart sshd?

Cheers,

Niki

systemctl status sshd.service reveals the “Loaded” (and optional “Drop-In”) unit file(s).
By default that is /usr/lib/systemd/system/sshd.service, which in turn has lines:

ExecStart=/usr/sbin/sshd -D $OPTIONS
ExecReload=/bin/kill -HUP $MAINPID

The ‘reload’ sends SIGHUP? What does sshd do on HUP?

# man sshd | grep -1 HUP
     fied in the configuration file.  sshd rereads its configuration file when
     it receives a hangup signal, SIGHUP, by executing itself with the name
     and options it was started with, e.g. /usr/sbin/sshd.

Sounds like the reload is sufficient, but restart is probably ok too.


I’d set the task that modifies file to notify handler (if the task is in a role) and the handler could
look like:

- name: Restart SSHD
  ansible.builtin.systemd:
    name:  sshd.service
    state: reloaded

The option ‘state’ of ‘systemd’ module:

# ansible-doc systemd | grep -A6 -- "- state"
- state
        'started'/'stopped' are idempotent actions that will not run
        commands unless necessary. 'restarted' will always bounce the
        unit. 'reloaded' will always reload.
        choices: [reloaded, restarted, started, stopped]
        default: null
        type: str

Alternatively, the handler could use module ‘service’ and be slightly more generic:

# ansible-doc service | head -7
> ANSIBLE.BUILTIN.SERVICE    (/usr/lib/python3.11/site-packages/ansible/modules/service.py)

        Controls services on remote hosts. Supported init systems
        include BSD init, OpenRC, SysV, Solaris SMF, systemd, upstart.
        This module acts as a proxy to the underlying service manager
        module. While all arguments will be passed to the underlying
        module, not all modules support the same arguments. This

How did you disable the IPv6?

In the early days it was possible to blacklist the ipv6 kernel module, etc, but more recently that has been too harsh; something depends (or did) on it. These days I have mere ipv6.method ignore on NetworkManager connections.

For example, the rhel-system-roles.network sets that with:

network_connections:
- name: myconn
  ip:
    auto6: no

Each interface still has the linklocal inet6 fe80::* address and services do listen, so IPv6 is not totally off.
One could/should complement that by blocking IPv6 at the firewall too.

1 Like

Again, thanks very much for your very detailed answer.

Since our last exchange I’ve learnt to manage roles, so I defined a disable_ipv6 role and then a distinct enable_ipv6 role to eventually reverse that operation.

Here’s disable_ipv6/tasks/main.yml:

---
- name: Disable IPv6 in the kernel parameters
  ansible.posix.sysctl:
    name: "{{ item }}"
    value: "1"
    reload: false
  loop:
    - net.ipv6.conf.all.disable_ipv6
    - net.ipv6.conf.default.disable_ipv6
    - net.ipv6.conf.lo.disable_ipv6
  notify: Reload kernel parameters

- name: Configure SSH for IPv4 only
  ansible.builtin.replace:
    path: /etc/ssh/sshd_config
    regexp: "{{item.from}}"
    replace: "{{item.to}}"
  with_items:
    - { from: "^#AddressFamily any", to: "AddressFamily inet" }
    - { from: "^#ListenAddress 0.0.0.0", to: "ListenAddress 0.0.0.0" }
  notify: Reload SSH configuration

And here’s the corresponding handlers/main.yml:

---
- name: Reload kernel parameters
  ansible.builtin.command:
    cmd: sysctl --system

- name: Reload SSH configuration
  ansible.builtin.service:
    name: sshd
    state: reloaded

Looks good so far. :slightly_smiling_face:

Wow, I looked at the disable thing and noticed to my embarrasment that I’ve been “stuck in the old ways” :flushed:

NM in el7 had the ipv6.method ignore. I’ve missed that NM already in el8 has ipv6.method disabled.
A vital difference is that NM actually does the equivalent of sysctl net.ipv6.conf.XX.disable_ipv6=1 when it brings up interface XX.

I did try nmcli con mod XX ipv6.method disabled but that did not modify net.ipv6.conf.XX.disable_ipv6. Nevertheless, after reboot it had value ‘1’.

Even the rhel-system-roles.network has started to support that in 2021: https://bugzilla.redhat.com/show_bug.cgi?id=1939711
I have inventories to update.


Current rhel-system-roles package does have role rhel-system-roles.sshd. The package contains README.md for each role, but online copy seems elusive. This: GitHub - willshersystems/ansible-sshd: Ansible role to configure the OpenSSH server daemon is apparently the upstream. So, a play could have:

- hosts: all
  tasks:
  - name: "Configure sshd"
    include_role:
      name: rhel-system-roles.sshd
    vars:
      # sshd_skip_defaults: true
      sshd:
        ListenAddress:
        - "0.0.0.0"
        AddressFamily: "inet"

I’m not sure whether the ‘sshd_skip_defaults’ is required. If it is, then the config requires the rest of the settings too. That upstream version does have a “reload” handler (except for platforms that do not support it).

I usually test a play with options -C --diff (check mode and show how files would change).


Another detail in that upstream code, and in el9 version of sshd is the use of “drop-in” config dir.
The main config file starts with:

Include /etc/ssh/sshd_config.d/*.conf

That is, one can primarily override the main file with a file in /etc/ssh/sshd_config.d/ without touching the main file at all.

1 Like