Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Zorono/0cc865097d3e6548d39adb0e74b95048 to your computer and use it in GitHub Desktop.
Save Zorono/0cc865097d3e6548d39adb0e74b95048 to your computer and use it in GitHub Desktop.
#!/bin/bash
#
# Author: Peter Maloney
# License: GPLv2
backed_up=false
now=$(date +%s)
file=/etc/ssh/sshd_config
support_applications=()
pubkeyonly=false
update=false
dry_run=false
while [ "$#" != 0 ]; do
if [ "$1" = "-f" ]; then
file="$2"
shift
elif [ "$1" = "-n" -o "$1" = "--dry-run" ]; then
dry_run=true
elif [ "$1" = "-u" ]; then
update=true
elif [ "$1" = "-a" ]; then
shift
support_applications=($(echo "$1" | tr ',' ' '))
elif [ "$1" = "-p" ]; then
pubkeyonly=true
else
echo "ERROR: unknown option: $1"
exit 1
fi
shift
done
do_backup() {
if [ "$backed_up" = false ]; then
echo "INFO: doing backup"
cp "${file}" "${file}.backup.${now}"
backed_up=true
fi
}
# for each of these, comment out the line in the config
# regex must match a whole line with "^" and "$" added by this function.
disable_config() {
regex="$1"
# DSA is 1024 bits... obsolete long ago
#HostKey /etc/ssh/ssh_host_dsa_key
# ecdsa uses NIST... weak prng
#HostKey /etc/ssh/ssh_host_ecdsa_key
line=$(grep -E "^${regex}$" "$file")
if [ -n "$line" ]; then
if [ "$dry_run" = true ]; then
echo "INFO: (DRY RUN) disabling: $line"
else
do_backup "$file"
echo "INFO: disabling: $line"
sed -i -r "s|^(${regex})$|#\1|" "$file"
fi
else
echo "INFO: config matching regex \"$regex\" is already disabled"
fi
}
# Append the config if it isn't already in config. If it's there but different value, warn.
append_config() {
line="$1"
first_col=$(awk '{print $1}' <<< "$line")
if ! grep -Eq "^[ \t]*${first_col}" "$file"; then
if [ "$dry_run" = true ]; then
echo "INFO: (DRY RUN) modifying config... adding $line"
else
do_backup "$file"
echo "INFO: modifying config... adding $line"
echo "$line" >> "$file"
fi
elif [ "${first_col}" != "HostKey" ]; then
old_content=$(grep -E "^[ \t]*${first_col}" "$file")
if [ -n "$old_content" -a "$old_content" != "$line" ]; then
if [ "$update" = true ]; then
if [ "$dry_run" = true ]; then
echo "INFO: (DRY RUN) modifying config... updating $line"
else
do_backup "$file"
echo "INFO: modifying config... updating $line"
sed -i "s|${old_content}|$line|" "$file"
fi
else
echo "WARNING: old config has the config option but content does not match (use -u to update anyway):"
echo "- $old_content"
echo "+ $line"
return 1
fi
fi
echo "INFO: config is already set: $line"
fi
}
# example versions seen by ssh -V
# Ubuntu 12.04.5 LTS
# OpenSSH_5.9p1 Debian-5ubuntu1.10, OpenSSL 1.0.1 14 Mar 2012
# Ubuntu 14.04.5 LTS
# OpenSSH_6.6.1p1 Ubuntu-2ubuntu2.8, OpenSSL 1.0.1f 6 Jan 2014
# Ubuntu 16.04.2 LTS
# OpenSSH_7.2p2 Ubuntu-4ubuntu2.2, OpenSSL 1.0.2g 1 Mar 2016
match_version() {
local version=$(ssh -V 2>&1 | awk '{print $1}' | grep -Eo "[0-9]+\.[0-9]+")
local major="${version%.*}"
local minor="${version#*.}"
if [ "$major" -gt "7" ]; then
echo "7.3"
elif [ "$major" -ge "7" ]; then
if [ "$minor" -ge "3" ]; then
echo "7.3"
elif [ "$minor" -ge "2" ]; then
echo "7.2"
else
echo "6.5"
fi
elif [ "$major" -ge "6" ]; then
if [ "$minor" -ge "5" ]; then
echo "6.5"
else
echo "5.9"
fi
else
echo "5.9"
fi
}
restart_sshd() {
echo "INFO: restarting sshd"
if [ -e /etc/init.d/sshd -o -e /lib/systemd/system/sshd.service ]; then
service sshd restart
elif [ -e /etc/init.d/ssh -o -e /lib/systemd/system/ssh.service ]; then
service ssh restart
else
service ssh restart || service sshd restart
fi
return $?
}
# =======================================================================================
# Main
# =======================================================================================
support_version=$(match_version)
echo "INFO: supporting version $support_version"
# ===========================
# handling applications
# ===========================
array_to_csv() {
str=$(echo "$@" | tr ' ' '\n' | sort -u | xargs echo | tr ' ' ',')
if [ -n "$str" ]; then
echo ",${str}"
fi
}
more_kex=()
more_macs=()
for application in "${support_applications[@]}"; do
if [ "$application" = "paramiko" ]; then
more_kex+=("diffie-hellman-group-exchange-sha256")
more_macs+=("hmac-sha2-512")
elif [ "$application" = "jsch" ]; then
more_kex+=("diffie-hellman-group-exchange-sha256")
more_macs+=("hmac-sha2-256")
fi
done
more_kex_str=$(array_to_csv "${more_kex[@]}")
more_macs_str=$(array_to_csv "${more_macs[@]}")
echo "INFO: supporting applications = ${support_applications[@]}, with more_kex_str = \"${more_kex_str}\", more_macs_str = \"${more_macs_str}\""
# ===========================
# applying config
# ===========================
disable_config "HostKey .*ssh_host_dsa_key"
disable_config "HostKey .*ssh_host_ecdsa_key"
# at least one of these is required or ssh automatically enables all, including the insecure DSA (except with 7.2 and HostKeyAlgorithms set)
append_config "HostKey /etc/ssh/ssh_host_rsa_key"
if [ "$pubkeyonly" = true ]; then
append_config "PasswordAuthentication no"
append_config "ChallengeResponseAuthentication no"
append_config "PubkeyAuthentication yes"
# NOTE: we aren't changing "UsePAM" which has some influence on password auth, and "PermitRootLogin" which ought to be either no or prohibit-password (which can be pubkey related), but we don't check
fi
# our goal is:
# - enable the new good stuff from the newest versions
# - enable the minimal stuff to be compatible with 6.5 clients
# - enable the 5.9 and older stuff only on server versions that old
if [ "$support_version" = "7.3" ]; then
#on newer machines (7.3) (still compatible with 6.5, but not 5.9):
append_config "KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group14-sha256${more_kex_str}"
append_config "Ciphers aes256-gcm@openssh.com,aes256-ctr,chacha20-poly1305@openssh.com"
append_config "MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com${more_macs_str}"
append_config "HostKeyAlgorithms ssh-ed25519-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com,ssh-ed25519,ssh-rsa"
elif [ "$support_version" = "7.2" ]; then
#on newer machines (7.2) (still compatible with 6.5, but not 5.9):
append_config "KexAlgorithms curve25519-sha256@libssh.org${more_kex_str}"
append_config "Ciphers aes256-gcm@openssh.com,aes256-ctr,chacha20-poly1305@openssh.com"
append_config "MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com${more_macs_str}"
append_config "HostKeyAlgorithms ssh-ed25519-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com,ssh-ed25519,ssh-rsa"
elif [ "$support_version" = "6.5" ]; then
#on older machines (6.5):
append_config "KexAlgorithms curve25519-sha256@libssh.org${more_kex_str}"
append_config "Ciphers aes256-gcm@openssh.com,aes256-ctr,chacha20-poly1305@openssh.com"
append_config "MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com${more_macs_str}"
elif [ "$support_version" = "5.9" ]; then
#on older machines (5.9) (has ssh-audit warnings):
append_config "KexAlgorithms diffie-hellman-group14-sha1${more_kex_str}"
append_config "Ciphers aes256-ctr"
append_config "MACs hmac-sha2-256,hmac-sha2-512${more_macs_str}"
else
echo "ERROR: invalid support version: $support_version"
fi
if [ "$backed_up" = true ]; then
if ! restart_sshd; then
echo "ERROR: sshd failed with new config... reverting to backup"
mv "${file}" "${file}.failed.${now}"
mv "${file}.backup.${now}" "${file}"
if ! restart_sshd; then
echo "ERROR: uh oh... old config doesn't work either"
fi
fi
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment