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.