---
title: Enable SSH Warning Banner
description: Datadog, the leading service for cloud-scale monitoring.
breadcrumbs: Docs > Datadog Security > OOTB Rules > Enable SSH Warning Banner
---

> For the complete documentation index, see [llms.txt](https://docs.datadoghq.com/llms.txt).

# Enable SSH Warning Banner
 
## Description{% #description %}

To enable the warning banner and ensure it is consistent across the system, add or correct the following line in `/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf`:

```
Banner /etc/issue.net
```

Another section contains information on how to create an appropriate system-wide warning banner.

## Rationale{% #rationale %}

The warning message reinforces policy awareness during the logon process and facilitates possible legal action against attackers. Alternatively, systems whose ownership should not be obvious should ensure usage of a banner that does not provide easy attribution.

## Remediation{% #remediation %}

### Shell script{% #shell-script %}

The following script can be run on the host to remediate the issue.

```bash
#!/bin/bash

# Remediation is applicable only in certain platforms
if dpkg-query --show --showformat='${db:Status-Status}' 'linux-base' 2>/dev/null | grep -q '^installed$'; then

mkdir -p /etc/ssh/sshd_config.d
chmod 0700 /etc/ssh/sshd_config.d
touch /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf
chmod 0600 /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf
LC_ALL=C sed -i "/^\s*Banner\s\+/Id" "/etc/ssh/sshd_config"
LC_ALL=C sed -i "/^\s*Banner\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf
# sshd reads /etc/ssh/sshd_config.d through an Include line of /etc/ssh/sshd_config.
if grep -Eiqs '^\s*Include\s+/etc/ssh/sshd_config\.d/\*\.conf' /etc/ssh/sshd_config; then
    if [ -e "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" ] ; then
        LC_ALL=C sed -i "/^\s*Banner\s\+/Id" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf"
    else
        touch "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf"
        chmod 0644 "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf"
    fi
    # make sure file has newline at the end
    sed -i -e '$a\' "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf"

    cp "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak"
    # Insert at the beginning of the file
    printf '%s\n' "Banner /etc/issue.net" > "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf"
    cat "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf"
    # Clean up after ourselves.
    rm "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak"
else
    if [ -e "/etc/ssh/sshd_config" ] ; then
        LC_ALL=C sed -i "/^\s*Banner\s\+/Id" "/etc/ssh/sshd_config"
    else
        touch "/etc/ssh/sshd_config"
        chmod 0600 "/etc/ssh/sshd_config"
    fi
    # make sure file has newline at the end
    sed -i -e '$a\' "/etc/ssh/sshd_config"

    cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak"
    # Insert at the beginning of the file
    printf '%s\n' "Banner /etc/issue.net" > "/etc/ssh/sshd_config"
    cat "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config"
    # Clean up after ourselves.
    rm "/etc/ssh/sshd_config.bak"
fi

else
    >&2 echo 'Remediation is not applicable, nothing was done'
fi
```

### Ansible playbook{% #ansible-playbook %}

The following playbook can be run with Ansible to remediate the issue.

```go
- name: Gather the package facts
  package_facts:
    manager: auto
  tags:
  - CJIS-5.5.6
  - DISA-STIG-UBTU-22-255020
  - NIST-800-53-AC-17(a)
  - NIST-800-53-AC-8(a)
  - NIST-800-53-AC-8(c)
  - NIST-800-53-CM-6(a)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sshd_enable_warning_banner_net

- name: Enable SSH Warning Banner - Find if a Match block of /etc/ssh/sshd_config
    sets the parameter Banner
  ansible.builtin.find:
    paths:
    - /etc/ssh
    patterns: sshd_config
    contains: (?ims)^[ \t]*Match[ \t].*^[ \t]*Banner[ \t]
    read_whole_file: true
  register: _sshd_main_config_match_sets_parameter
  when: '"linux-base" in ansible_facts.packages'
  tags:
  - CJIS-5.5.6
  - DISA-STIG-UBTU-22-255020
  - NIST-800-53-AC-17(a)
  - NIST-800-53-AC-8(a)
  - NIST-800-53-AC-8(c)
  - NIST-800-53-CM-6(a)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sshd_enable_warning_banner_net

- name: Enable SSH Warning Banner - Remove the parameter Banner from the Match blocks
    of /etc/ssh/sshd_config
  ansible.builtin.replace:
    path: '{{ item.path }}'
    after: (?im)^[ \t]*Match[ \t]
    regexp: (?i)^[ \t]*Banner[ \t].*\n?
    replace: ''
  loop: '{{ _sshd_main_config_match_sets_parameter.files }}'
  when: '"linux-base" in ansible_facts.packages'
  tags:
  - CJIS-5.5.6
  - DISA-STIG-UBTU-22-255020
  - NIST-800-53-AC-17(a)
  - NIST-800-53-AC-8(a)
  - NIST-800-53-AC-8(c)
  - NIST-800-53-CM-6(a)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sshd_enable_warning_banner_net

- name: Enable SSH Warning Banner - Find the files of /etc/ssh/sshd_config.d where
    a Match block sets the parameter Banner
  ansible.builtin.find:
    paths:
    - /etc/ssh/sshd_config.d
    patterns: '*.conf'
    contains: (?ims)^[ \t]*Match[ \t].*^[ \t]*Banner[ \t]
    read_whole_file: true
  register: _sshd_config_dir_match_sets_parameter
  when: '"linux-base" in ansible_facts.packages'
  tags:
  - CJIS-5.5.6
  - DISA-STIG-UBTU-22-255020
  - NIST-800-53-AC-17(a)
  - NIST-800-53-AC-8(a)
  - NIST-800-53-AC-8(c)
  - NIST-800-53-CM-6(a)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sshd_enable_warning_banner_net

- name: Enable SSH Warning Banner - Remove the parameter Banner from the Match blocks
    of the files of /etc/ssh/sshd_config.d
  ansible.builtin.replace:
    path: '{{ item.path }}'
    after: (?im)^[ \t]*Match[ \t]
    regexp: (?i)^[ \t]*Banner[ \t].*\n?
    replace: ''
  loop: '{{ _sshd_config_dir_match_sets_parameter.files }}'
  when: '"linux-base" in ansible_facts.packages'
  tags:
  - CJIS-5.5.6
  - DISA-STIG-UBTU-22-255020
  - NIST-800-53-AC-17(a)
  - NIST-800-53-AC-8(a)
  - NIST-800-53-AC-8(c)
  - NIST-800-53-CM-6(a)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sshd_enable_warning_banner_net

- name: Enable SSH Warning Banner - Check if /etc/ssh/sshd_config includes /etc/ssh/sshd_config.d
  ansible.builtin.lineinfile:
    path: /etc/ssh/sshd_config
    regexp: (?i)^\s*Include\s+/etc/ssh/sshd_config\.d/\*\.conf
    state: absent
  check_mode: true
  changed_when: false
  failed_when: false
  register: _sshd_config_includes_config_dir
  when: '"linux-base" in ansible_facts.packages'
  tags:
  - CJIS-5.5.6
  - DISA-STIG-UBTU-22-255020
  - NIST-800-53-AC-17(a)
  - NIST-800-53-AC-8(a)
  - NIST-800-53-AC-8(c)
  - NIST-800-53-CM-6(a)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sshd_enable_warning_banner_net

- name: Enable SSH Warning Banner - Set the parameter Banner in /etc/ssh/sshd_config.d
  block:

  - name: Enable SSH Warning Banner - Check if the parameter Banner is configured
      in /etc/ssh/sshd_config
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      regexp: (?i)^\s*{{ "Banner"| regex_escape }}\s+
      state: absent
    check_mode: true
    changed_when: false
    register: _config_file_has_parameter

  - name: Enable SSH Warning Banner - Check if the parameter Banner is configured
      in /etc/ssh/sshd_config.d
    ansible.builtin.find:
      paths:
      - /etc/ssh/sshd_config.d
      contains: (?i)^\s*{{ "Banner"| regex_escape }}\s+
      patterns: '*.conf'
    register: _config_dir_has_parameter

  - name: Enable SSH Warning Banner - Count the lines that configure the parameter
      Banner in /etc/ssh/sshd_config.d
    ansible.builtin.lineinfile:
      path: '{{ item.path }}'
      regexp: (?i)^\s*{{ "Banner"| regex_escape }}\s+
      state: absent
    check_mode: true
    changed_when: false
    loop: '{{ _config_dir_has_parameter.files }}'
    register: _config_dir_parameter_lines

  - name: Enable SSH Warning Banner - Check if /etc/ssh/sshd_config exists
    ansible.builtin.stat:
      path: /etc/ssh/sshd_config
    register: _config_file_exists

  - name: Enable SSH Warning Banner - Check if the parameter Banner is configured
      correctly in /etc/ssh/sshd_config
    ansible.builtin.lineinfile:
      path: /etc/ssh/sshd_config
      regexp: (?i)^\s*{{ "Banner"| regex_escape }}\s+/etc/issue.net$
      state: absent
    check_mode: true
    changed_when: false
    register: _config_file_correctly
    when: _config_file_exists.stat.exists

  - name: Enable SSH Warning Banner - Check if the parameter Banner is configured
      correctly in /etc/ssh/sshd_config.d
    ansible.builtin.find:
      paths:
      - /etc/ssh/sshd_config.d
      contains: (?i)^\s*{{ "Banner"| regex_escape }}\s+/etc/issue.net$
      patterns: '*.conf'
    register: _config_dir_correctly

  - name: Enable SSH Warning Banner
    block:

    - name: Deduplicate values from /etc/ssh/sshd_config
      ansible.builtin.lineinfile:
        path: /etc/ssh/sshd_config
        create: false
        regexp: (?i)(?i)^\s*{{ "Banner"| regex_escape }}\s+
        mode: '0600'
        state: absent

    - name: Check if /etc/ssh/sshd_config.d exists
      ansible.builtin.stat:
        path: /etc/ssh/sshd_config.d
      register: _etc_ssh_sshd_config_d_exists

    - name: Check if the parameter Banner is present in /etc/ssh/sshd_config.d
      ansible.builtin.find:
        paths: /etc/ssh/sshd_config.d
        recurse: 'yes'
        follow: 'no'
        contains: (?i)^\s*{{ "Banner"| regex_escape }}\s+
        patterns: '*.conf'
      register: _etc_ssh_sshd_config_d_has_parameter
      when: _etc_ssh_sshd_config_d_exists.stat.isdir is defined and _etc_ssh_sshd_config_d_exists.stat.isdir

    - name: Remove parameter from files in /etc/ssh/sshd_config.d
      ansible.builtin.lineinfile:
        path: '{{ item.path }}'
        create: false
        regexp: (?i)(?i)^\s*{{ "Banner"| regex_escape }}\s+
        mode: '0600'
        state: absent
      with_items: '{{ _etc_ssh_sshd_config_d_has_parameter.files | default([]) }}'
      when: _etc_ssh_sshd_config_d_has_parameter.matched > 0

    - name: Insert correct line to /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf
      ansible.builtin.lineinfile:
        path: /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf
        create: true
        regexp: (?i)(?i)^\s*{{ "Banner"| regex_escape }}\s+
        mode: '0600'
        line: Banner /etc/issue.net
        state: present
        insertbefore: BOF
        validate: /usr/sbin/sshd -t -f %s
    when:
    - _config_file_correctly is not skipped
    - (_config_file_correctly.found == 0 and _config_dir_correctly.matched == 0) or
      ((_config_file_has_parameter.found | int) + (_config_dir_parameter_lines.results
      | map(attribute='found') | map('int') | sum)) != 1

  - name: Enable SSH Warning Banner - set file mode for /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf
    ansible.builtin.file:
      path: /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf
      mode: '0600'
      state: touch
      modification_time: preserve
      access_time: preserve
  when:
  - '"linux-base" in ansible_facts.packages'
  - (_sshd_config_includes_config_dir.found | default(0) | int) > 0
  tags:
  - CJIS-5.5.6
  - DISA-STIG-UBTU-22-255020
  - NIST-800-53-AC-17(a)
  - NIST-800-53-AC-8(a)
  - NIST-800-53-AC-8(c)
  - NIST-800-53-CM-6(a)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sshd_enable_warning_banner_net

- name: Enable SSH Warning Banner - Set the parameter Banner in /etc/ssh/sshd_config
  block:

  - name: Enable SSH Warning Banner - Find the files of /etc/ssh/sshd_config.d that
      set the parameter Banner
    ansible.builtin.find:
      paths:
      - /etc/ssh/sshd_config.d
      patterns: '*.conf'
      contains: (?i)^\s*{{ "Banner"| regex_escape }}\s+
    register: _sshd_config_dir_sets_parameter

  - name: Enable SSH Warning Banner - Remove the parameter Banner from the files of
      /etc/ssh/sshd_config.d
    ansible.builtin.lineinfile:
      path: '{{ item.path }}'
      regexp: (?i)^\s*{{ "Banner"| regex_escape }}\s+
      state: absent
    loop: '{{ _sshd_config_dir_sets_parameter.files }}'

  - name: Enable SSH Warning Banner
    block:

    - name: Check for duplicate values
      ansible.builtin.lineinfile:
        path: /etc/ssh/sshd_config
        create: true
        regexp: (?i)(?i)^\s*Banner\s+
        mode: '0600'
        state: absent
      check_mode: true
      changed_when: false
      register: dupes

    - name: Deduplicate values from /etc/ssh/sshd_config
      ansible.builtin.lineinfile:
        path: /etc/ssh/sshd_config
        create: true
        regexp: (?i)(?i)^\s*Banner\s+
        mode: '0600'
        state: absent
      when: dupes.found is defined and dupes.found > 1

    - name: Insert correct line to /etc/ssh/sshd_config
      ansible.builtin.lineinfile:
        path: /etc/ssh/sshd_config
        create: true
        regexp: (?i)(?i)^\s*Banner\s+
        mode: '0600'
        line: Banner /etc/issue.net
        state: present
        insertbefore: BOF
        validate: /usr/sbin/sshd -t -f %s
  when:
  - '"linux-base" in ansible_facts.packages'
  - (_sshd_config_includes_config_dir.found | default(0) | int) == 0
  tags:
  - CJIS-5.5.6
  - DISA-STIG-UBTU-22-255020
  - NIST-800-53-AC-17(a)
  - NIST-800-53-AC-8(a)
  - NIST-800-53-AC-8(c)
  - NIST-800-53-CM-6(a)
  - low_complexity
  - low_disruption
  - medium_severity
  - no_reboot_needed
  - restrict_strategy
  - sshd_enable_warning_banner_net
```
