How to Disable Weak SSH Algorithms in Linux with a Shell Script

Secure Shell (SSH) is a foundational tool for secure remote administration of Linux systems. However, default configurations may include legacy or deprecated cryptographic algorithms that can expose servers to modern attack vectors. In high-security environments, especially where compliance and regulatory standards are enforced, it’s critical to harden the SSH configuration. This post explores a shell script that automates the process on how to disable weak SSH algorithms in Linux, ensuring alignment with best practices in system hardening.

Background

Cryptographic agility is essential for maintaining secure communication channels in any IT infrastructure. Over time, cryptographic algorithms can become obsolete due to advances in computing power and cryptanalysis techniques. Protocols like SSH, which offer multiple ciphers, key exchange (Kex) algorithms, and message authentication codes (MACs), require periodic auditing and adjustment to remove weaker options.

For Managed Service Providers (MSPs) and IT professionals, this maintenance can be tedious and error-prone, particularly at scale. This script was developed to automate the secure configuration of sshd_config, providing a streamlined method to disable weak SSH algorithms in Linux using shell scripting. It backs up the existing configuration, applies hardened settings, and safely reloads the SSH service.

The Script

#!/usr/bin/env bash

# Description: Disables weak SSH algorithms.
# By using this script, you indicate your acceptance of the following legal terms as well as our Terms of Use at https://www.ninjaone.com/terms-of-use.
# Ownership Rights: NinjaOne owns and will continue to own all right, title, and interest in and to the script (including the copyright). NinjaOne is giving you a limited license to use the script in accordance with these legal terms. 
# Use Limitation: You may only use the script for your legitimate personal or internal business purposes, and you may not share the script with another party. 
# Republication Prohibition: Under no circumstances are you permitted to re-publish the script in any script library or website belonging to or under the control of any other software provider. 
# Warranty Disclaimer: The script is provided “as is” and “as available”, without warranty of any kind. NinjaOne makes no promise or guarantee that the script will be free from defects or that it will meet your specific needs or expectations. 
# Assumption of Risk: Your use of the script is at your own risk. You acknowledge that there are certain inherent risks in using the script, and you understand and assume each of those risks. 
# Waiver and Release: You will not hold NinjaOne responsible for any adverse or unintended consequences resulting from your use of the script, and you waive any legal or equitable rights or remedies you may have against NinjaOne relating to your use of the script. 
# EULA: If you are a NinjaOne customer, your use of the script is subject to the End User License Agreement applicable to you (EULA).
#
# Release Notes: Initial Release
#
# Disables weak SSH algorithms.
#
# Links:
#  https://infosec.mozilla.org/guidelines/openssh
#  https://man.openbsd.org/sshd_config#KexAlgorithms
#  https://man.openbsd.org/sshd_config#Ciphers
#  https://man.openbsd.org/sshd_config#MACs
#  https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar2.pdf

_Ciphers='[email protected],[email protected],[email protected],aes256-ctr,aes192-ctr,aes128-ctr'
_KexAlgorithms='[email protected],ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group-exchange-sha256'
_MACs='[email protected],[email protected],[email protected],hmac-sha2-512,hmac-sha2-256,[email protected]'

_HostKeys=("HostKey /etc/ssh/ssh_host_ed25519_key" "HostKey /etc/ssh/ssh_host_rsa_key" "HostKey /etc/ssh/ssh_host_ecdsa_key")

_sshd_config="/etc/ssh/sshd_config"

_restoreBackedUpConfig="false"

if [[ -n $restoreBackedUpConfig && $restoreBackedUpConfig == "true" ]]; then
    _restoreBackedUpConfig="true"
fi

# Logs an error message and exits with the specified exit code
die() {
    local _ret="${2:-1}"
    echo "$1" >&2
    exit "${_ret}"
}

backup_file() {
    # Source file to backup
    local file_source=$1
    # Get number of backups to keep from argument or default to 3
    local backup_keep_count=${2:-3}

    local file_source_dir
    local file_source_file
    local file_backup
    local backup_files
    file_backup="${file_source}_$(date +%Y-%m-%d_%H-%M-%S).backup"
    if [[ -f "${file_source}" ]]; then
        echo "[Info] Backing up $file_source to $file_backup"
        cp "${file_source}" "${file_backup}"
    fi

    # Remove the oldest backup file if there are more than 3 backups
    backup_keep_count="+$((backup_keep_count + 1))"

    # Get the list of backup files
    echo "[Info] Finding backup files..."
    file_source_dir=$(dirname "${file_source}")
    file_source_file=$(basename "${file_source}")
    backup_files=$(find "$file_source_dir" -name "${file_source_file}_*.backup" -printf '%T+ %p\n' 2>/dev/null | sort -r | tail -n "${backup_keep_count}" | cut -d' ' -f2-)
    # Loop through each backup file and remove it
    for backup_file in $backup_files; do
        echo "[Info] Removing $backup_file"
        rm -f "$backup_file"
    done
}

restart_sshd() {
    # Check that this system is running systemd-based
    _type=$(
        # Get the type of init system
        file /sbin/init 2>/dev/null | awk -F/ '{print $NF}' 2>/dev/null
    )
    if [[ "${_type}" == "systemd" ]] && [[ -n "$(command -v systemctl)" ]]; then
        echo "[Info] Reloading ${sshd_service} service..."
        # Find the sshd service
        sshd_service=$(
            # Get the ssh service, if two are found use the first one. Likely the first one is a symlink to the actual service file.
            systemctl list-unit-files | grep -E "^(sshd|ssh|openssh-server)\.service" | awk -F' ' '{print $1}' | head -n 1
        )
        if [[ -z "${sshd_service}" ]]; then
            die "[Error] sshd service is not available. Please install it and try again" 1
        fi
        # Check that ssh service is enabled
        if systemctl is-enabled "${sshd_service}" >/dev/null; then
            echo "[Info] ${sshd_service} is enabled"
        else
            die "[Info] ${sshd_service} is not enabled. When enabled and started, PermitEmptyPasswords will be set to no" 0
        fi
        # Check that ssh service is running
        if systemctl is-active "${sshd_service}" >/dev/null; then
            echo "[Info] ${sshd_service} is running"
            # Reload sshd.service
            if systemctl reload "${sshd_service}"; then
                echo "[Info] sshd service configuration reloaded"
            else
                die "[Error] Failed to reload ${sshd_service}" 1
            fi
        else
            echo "[Info] ${sshd_service} is not running"
        fi
    else
        echo "[Info] Restarting sshd service..."
        # Check that the service command is available
        if ! [ "$(command -v service)" ]; then
            die "[Error] The service command is not available. Is this an initd type system (e.g. SysV)?" 1
        fi
        # Find the sshd service
        sshd_service=$(
            # Get the list of services
            service --status-all | awk -F' ' '{print $NF}' | grep sshd
        )
        if [[ -z "${sshd_service}" ]]; then
            sshd_service=$(
                # Get the list of services
                service --status-all | awk -F' ' '{print $NF}' | grep ssh
            )
            if [[ -z "${sshd_service}" ]]; then
                die "[Error] sshd service is not available. Please install it and try again" 1
            fi
        fi
        # Restart sshd service
        if service "${sshd_service}" restart; then
            echo "[Info] sshd service restarted"
        else
            die "[Error] Failed to restart sshd service" 1
        fi
    fi
}

# Check that we are running as root
if [[ $EUID -ne 0 ]]; then
    die "[Error] This script must be run as root" 1
fi

# Check if sshd is installed
if ! command -v sshd &>/dev/null; then
    die "[Error] sshd is not installed" 1
fi

# Get the version of OpenSSH Server
_sshd_version=$(sshd -V 2>&1 | grep OpenSSH | awk '{print $1}' | cut -d '_' -f 2 | sed 's/,//g')
_sshd_version_major=$(echo "${_sshd_version}" | cut -d '.' -f 1)
_sshd_version_minor=$(echo "${_sshd_version}" | cut -d '.' -f 2)

# Check if the sshd version is less than 8.8
if [[ "${_sshd_version_major}" -lt 6 ]] || [[ "${_sshd_version_major}" -eq 6 ]] && [[ "${_sshd_version_minor}" -lt 7 ]]; then
    die "[Error] This script is only supported on OpenSSH Server 6.7 and above: Current version is ${_sshd_version}" 1
fi

if [[ $_restoreBackedUpConfig == "true" ]]; then
    # Get backup files
    backup_dir=$(dirname "${_sshd_config}")
    backup_file=$(basename "${_sshd_config}")
    backup_file_to_restore=$(find "${backup_dir}" -name "${backup_file}_*.backup" -printf "%-.22T+ %M %n %-8u %-8g %8s %Tx %.8TX %p\n" | sort | cut -f 2- -d ' ' | tail -n 1 | awk '{print $8}')

    if [ -f "${backup_file_to_restore}" ]; then
        echo "[Info] Restoring ${backup_file_to_restore}"
        cp "${backup_file_to_restore}" "${_sshd_config}" || die "[Error] Failed to restore ${backup_file}" 1
        echo "[Info] Successfully restored ${backup_file}"
    else
        echo "[Info] No backup file found, nothing to restore"
    fi

    exit 0
fi

# Check if the sshd_config file exists
if [[ -f "${_sshd_config}" ]]; then
    # Make a backup of the sshd_config file
    backup_file "${_sshd_config}"

    # Set KexAlgorithms
    if grep -q "^KexAlgorithms" /etc/ssh/sshd_config; then
        sed -i -e "s/^KexAlgorithms.*/KexAlgorithms ${_KexAlgorithms}/g" /etc/ssh/sshd_config
        echo "[Info] KexAlgorithms set to ${_KexAlgorithms}"
    else
        echo "KexAlgorithms ${_KexAlgorithms}" >>/etc/ssh/sshd_config
        echo "[Info] Added KexAlgorithms: ${_KexAlgorithms}"
    fi

    # Set Ciphers
    if grep -q "^Ciphers" /etc/ssh/sshd_config; then
        sed -i -e "s/^Ciphers.*/Ciphers ${_Ciphers}/g" /etc/ssh/sshd_config
        echo "[Info] Ciphers set to ${_Ciphers}"
    else
        echo "Ciphers ${_Ciphers}" >>/etc/ssh/sshd_config
        echo "[Info] Added Ciphers: ${_Ciphers}"
    fi

    # Set MACs
    if grep -q "^MACs" /etc/ssh/sshd_config; then
        sed -i -e "s/^MACs.*/MACs ${_MACs}/g" /etc/ssh/sshd_config
        echo "[Info] MACs set to ${_MACs}"
    else
        echo "MACs ${_MACs}" >>/etc/ssh/sshd_config
        echo "[Info] Added MACs: ${_MACs}"
    fi

    # Remove old HostKey entries
    if grep -q "^HostKey" /etc/ssh/sshd_config; then
        sed -i '/^HostKey/d' /etc/ssh/sshd_config
        echo "[Info] Removed old HostKey entries"
    fi

    # Add new HostKey entries
    for _HostKey in "${_HostKeys[@]}"; do
        # Display the HostKey after the last /
        _displayName=${_HostKey##*/}
        if grep -q "^${_HostKey}" /etc/ssh/sshd_config; then
            echo "[Info] HostKey ${_displayName} already exists"
        else
            echo "${_HostKey}" >>/etc/ssh/sshd_config
            echo "[Info] Added HostKey ${_HostKey} to /etc/ssh/sshd_config"
        fi
    done

    restart_sshd
else
    die "[Error] The sshd_config file does not exist" 1
fi

 

Detailed Breakdown

Let’s unpack the key components and logic of this script.

1. Secure Configuration Values

The script defines hardened cryptographic settings:

bash

CopyEdit

_Ciphers=’[email protected],…’

_KexAlgorithms=’[email protected],…’

_MACs=’[email protected],…’

These are drawn from vetted sources such as Mozilla’s OpenSSH guidelines and NIST publications.

2. Backup Mechanism

Before making any changes, the script performs a timestamped backup of the current SSH configuration file:

bash

CopyEdit

backup_file “/etc/ssh/sshd_config”

It keeps the three most recent backups and cleans up older ones.

3. Configuration Hardening

It either replaces or appends secure values for Ciphers, KexAlgorithms, and MACs in /etc/ssh/sshd_config. It also replaces existing HostKey entries with explicitly defined secure ones.

4. Service Validation and Reload

Depending on whether the system uses systemd or traditional init (SysV), the script detects the correct SSH service and reloads or restarts it as needed:

bash

CopyEdit

systemctl reload sshd

# or

service sshd restart

5. Restore Functionality

If a restore is requested (restoreBackedUpConfig=true), it locates the latest backup and restores it, then exits safely.

Potential Use Cases

Case Study: Healthcare MSP Compliance

A healthcare-focused MSP needs to comply with HIPAA, which mandates strong encryption practices. Rather than manually auditing each client server, the MSP deploys this shell script through their remote monitoring and management (RMM) platform to all client endpoints. Within minutes, weak SSH algorithms are disabled and compliance is achieved without manual intervention.

Comparisons

Manual Configuration

Manually editing /etc/ssh/sshd_config requires significant expertise and is error-prone, especially when managing multiple systems.

Ansible/Chef/Puppet

These tools provide similar automation but often involve steeper learning curves and infrastructure setup. The shell script is simpler and self-contained, making it ideal for lightweight use cases.

GUI Tools

Some GUI security tools offer SSH hardening, but they can lack the transparency and auditability that scripting provides.

FAQs

Q: What SSH versions does this script support?

A: OpenSSH Server 6.7 and above. The script exits with an error if the version is unsupported.

Q: What happens if the SSH service fails to restart?

A: The script reports an error and exits, ensuring no silent failures.

Q: Can I undo changes?

A: Yes. Set restoreBackedUpConfig=true to revert to the latest backup.

Q: Will this log me out of my current SSH session?

A: No, since the script reloads the configuration. However, caution is advised when running remotely.

Implications

Applying this script reduces the attack surface of your Linux systems significantly. Weak cryptographic options are a common exploitation target in automated attacks and advanced persistent threats (APTs). In regulatory contexts such as PCI-DSS, HIPAA, and ISO 27001, hardening SSH is often a mandatory control. Failure to do so may result in non-compliance and security breaches.

Recommendations

  • Test in a lab before deploying across production systems.
  • Use version control for tracking configuration changes.
  • Pair with centralized logging to monitor SSH access and service status.
  • Schedule periodic audits to ensure configurations remain current with cryptographic standards.
  • Always ensure physical or alternate remote access when deploying SSH changes in bulk.

Final Thoughts

Disabling weak SSH algorithms in Linux with a shell script like this provides a reliable, scalable solution for system hardening. It’s especially powerful when integrated into endpoint management platforms such as NinjaOne. With NinjaOne, IT professionals can deploy scripts across environments, validate configurations, and monitor results from a single pane of glass—transforming security best practices into automated, enforceable policies.

By leveraging simple yet effective scripts like this, NinjaOne users can stay ahead of evolving cybersecurity threats while maintaining operational efficiency.

Next Steps

Building an efficient and effective IT team requires a centralized solution that acts as your core service delivery tool. NinjaOne enables IT teams to monitor, manage, secure, and support all their devices, wherever they are, without the need for complex on-premises infrastructure.

Learn more about NinjaOne Remote Script Deployment, check out a live tour, or start your free trial of the NinjaOne platform.

Categories:

You might also like

×

See NinjaOne in action!

By submitting this form, I accept NinjaOne's privacy policy.

NinjaOne Terms & Conditions

By clicking the “I Accept” button below, you indicate your acceptance of the following legal terms as well as our Terms of Use:

  • Ownership Rights: NinjaOne owns and will continue to own all right, title, and interest in and to the script (including the copyright). NinjaOne is giving you a limited license to use the script in accordance with these legal terms.
  • Use Limitation: You may only use the script for your legitimate personal or internal business purposes, and you may not share the script with another party.
  • Republication Prohibition: Under no circumstances are you permitted to re-publish the script in any script library belonging to or under the control of any other software provider.
  • Warranty Disclaimer: The script is provided “as is” and “as available”, without warranty of any kind. NinjaOne makes no promise or guarantee that the script will be free from defects or that it will meet your specific needs or expectations.
  • Assumption of Risk: Your use of the script is at your own risk. You acknowledge that there are certain inherent risks in using the script, and you understand and assume each of those risks.
  • Waiver and Release: You will not hold NinjaOne responsible for any adverse or unintended consequences resulting from your use of the script, and you waive any legal or equitable rights or remedies you may have against NinjaOne relating to your use of the script.
  • EULA: If you are a NinjaOne customer, your use of the script is subject to the End User License Agreement applicable to you (EULA).