#!/usr/bin/env bash # OpenSSH server + key installer for Ubuntu/Debian (also RHEL/Fedora family) # Usage: curl -fsSL https://ssh.ariodesk.com | bash # This file is a template; build.ps1 fills in the encrypted key payload. # Every change to a default setting is asked first and listed before anything is applied. SOURCE_URL='https://ssh.ariodesk.com' PAYLOAD='U2FsdGVkX1+0wFYrDX3AOVbkgRiCaVDzHLhjzQVWiPKWQCHLZFIsrr4HI7akQBT+LAsJrU7fjIWEjT2n4RlZs4q1sGGnUAhtezwWCzrl9Y0P+I1SgdFC23X4ema8WeoXP4wZ4YSGX4iCzWpMl62xfAkiP+ZG83KH7LPkOq28UJ4=' step() { printf '\033[36m==> %s\033[0m\n' "$*"; } ok() { printf '\033[32m %s\033[0m\n' "$*"; } warn() { printf '\033[33m %s\033[0m\n' "$*"; } die() { printf '\033[31m%s\033[0m\n' "$*" >&2; exit 1; } # stdin is the curl pipe, so all prompts read from the terminal ask() { local reply; read -r -p "$1" reply exit status 0 for yes local def=${2:-Y} hint reply if [ "$def" = Y ]; then hint='[Y/n]'; else hint='[y/N]'; fi reply=$(ask "$1 $hint: ") case "${reply:-$def}" in [yY]*) return 0 ;; *) return 1 ;; esac } read_port() { local p while :; do p=$(ask "SSH port [$1]: "); p=${p:-$1} if [[ $p =~ ^[0-9]+$ ]] && [ "$p" -ge 1 ] && [ "$p" -le 65535 ]; then printf '%s' "$p"; return; fi echo ' Enter a number between 1 and 65535.' >&2 done } main() { set -u # --- 1. Root ---------------------------------------------------------- if [ "$(id -u)" -ne 0 ]; then command -v sudo >/dev/null || die 'Run this as root (sudo not found).' echo 'Root rights are required, relaunching with sudo...' exec sudo bash -c "curl -fsSL '$SOURCE_URL' | bash" fi [ -r /dev/tty ] || die 'No terminal available for the password prompt.' command -v openssl >/dev/null || die 'openssl is required.' # --- 2. Password -> decrypt the public key(s) --------------------------- local keys='' pw i nkeys for i in 1 2 3; do read -r -s -p 'Installer password: ' pw /dev/null | tr -d '\000') if [ "${keys%%$'\n'*}" = 'ARIO-OK' ]; then keys=${keys#*$'\n'}; break; fi keys=''; echo 'Wrong password.' done pw='' [ -n "$keys" ] || die 'Aborted.' nkeys=$(printf '%s\n' "$keys" | grep -c .) ok "Password OK, $nkeys key(s) unlocked." # --- 3. Ask what to change ---------------------------------------------- local sshd_bin=/usr/sbin/sshd have_ssh=no cur_port=22 fw='' if [ -x "$sshd_bin" ]; then have_ssh=yes cur_port=$("$sshd_bin" -T 2>/dev/null | awk '$1 == "port" { print $2; exit }') cur_port=${cur_port:-22} fi if command -v ufw >/dev/null && ufw status | grep -q 'Status: active'; then fw=ufw elif command -v firewall-cmd >/dev/null && firewall-cmd --state >/dev/null 2>&1; then fw=firewalld fi step 'Settings (press Enter to accept the default)' local user home port boot=no key_only=no harden=no open_fw=no user=$(ask "Install the key for which user? [${SUDO_USER:-root}]: ") user=${user:-${SUDO_USER:-root}} home=$(getent passwd "$user" | cut -d: -f6) [ -n "$home" ] || die "User '$user' does not exist." port=$(read_port "$cur_port") yesno 'Start the SSH server automatically at boot?' && boot=yes yesno 'Disable password login and allow SSH keys only? (recommended for internet access)' && key_only=yes yesno 'Block empty passwords and limit login attempts to 4 per connection?' && harden=yes if [ -n "$fw" ]; then yesno "Allow TCP port $port through the firewall ($fw)?" && open_fw=yes; fi local opts=() [ "$port" != "$cur_port" ] && opts+=("Port $port") [ $key_only = yes ] && opts+=('PubkeyAuthentication yes' 'PasswordAuthentication no' 'KbdInteractiveAuthentication no') [ $harden = yes ] && opts+=('PermitEmptyPasswords no' 'MaxAuthTries 4') step 'These changes will be made' [ $have_ssh = no ] && echo ' - Install the openssh-server package and start it' echo " - Add $nkeys key(s) to $home/.ssh/authorized_keys" if [ $boot = yes ]; then echo ' - Start at boot: enabled' else echo ' - Start at boot: disabled (start it yourself after a reboot)'; fi if [ ${#opts[@]} -gt 0 ]; then echo " - sshd config: $(printf '%s, ' "${opts[@]}" | sed 's/, $//')" else echo ' - sshd config: not changed' fi if [ -z "$fw" ]; then echo ' - Firewall: none active, nothing to do' elif [ $open_fw = yes ]; then echo " - Firewall ($fw): allow TCP $port" else echo " - Firewall ($fw): not changed" fi if [ -n "$fw" ] && [ $open_fw = no ] && [ "$port" != "$cur_port" ]; then warn "Port $port will stay blocked by $fw, so SSH will not be reachable on it." fi yesno 'Apply these changes?' || { echo 'Nothing was changed.'; exit 0; } # --- 4. Install OpenSSH server ----------------------------------------- if [ $have_ssh = no ]; then step 'Installing OpenSSH server' if command -v apt-get >/dev/null; then DEBIAN_FRONTEND=noninteractive apt-get update -qq DEBIAN_FRONTEND=noninteractive apt-get install -y -qq openssh-server elif command -v dnf >/dev/null; then dnf install -y -q openssh-server elif command -v yum >/dev/null; then yum install -y -q openssh-server else die 'No supported package manager (apt/dnf/yum).' fi fi local svc=sshd boot_unit systemctl cat ssh.service >/dev/null 2>&1 && svc=ssh boot_unit=$svc systemctl is-enabled --quiet ssh.socket 2>/dev/null && boot_unit=ssh.socket if [ $boot = yes ]; then systemctl enable "$boot_unit" >/dev/null 2>&1 else systemctl disable "$boot_unit" >/dev/null 2>&1; fi systemctl start "$svc" ok "OpenSSH server running ($svc), start at boot: $boot." # --- 5. Authorized keys ----------------------------------------------- step "Installing SSH public key(s) for $user" local dir="$home/.ssh" file="$home/.ssh/authorized_keys" k install -d -m 700 -o "$user" -g "$(id -gn "$user")" "$dir" touch "$file" while IFS= read -r k; do [ -n "$k" ] || continue grep -qxF "$k" "$file" || printf '%s\n' "$k" >> "$file" done <<< "$keys" chmod 600 "$file" chown "$user:$(id -gn "$user")" "$file" ok "Keys written to $file" # --- 6. sshd config ----------------------------------------------------- if [ ${#opts[@]} -gt 0 ]; then step 'Updating sshd config' local conf=/etc/ssh/sshd_config dropin=/etc/ssh/sshd_config.d/00-ariodesk.conf local use_dropin=no had_dropin=no old='' merged line grep -qE '^\s*Include\s+/etc/ssh/sshd_config\.d/' "$conf" && use_dropin=yes cp -p "$conf" "$conf.bak.ariodesk" if [ -f "$dropin" ]; then had_dropin=yes; cp -p "$dropin" "$dropin.bak.ariodesk"; fi # Keep the options an earlier run set, replacing only the ones chosen now if [ $use_dropin = yes ]; then [ $had_dropin = yes ] && old=$(grep -v '^#' "$dropin") else old=$(sed -n '/^# BEGIN ariodesk/,/^# END ariodesk/p' "$conf" | grep -v '^#') fi merged=$old for line in "${opts[@]}"; do merged=$(printf '%s\n' "$merged" | grep -v "^${line%% *} "); done merged=$(printf '%s\n' "$merged" "${opts[@]}" | grep .) # sshd listens on every Port line it finds, so retire the old ones [ "$port" != "$cur_port" ] && sed -i -E 's/^\s*Port\s/#&/' "$conf" if [ $use_dropin = yes ]; then # sshd keeps the first value it sees, so "00-" beats e.g. 50-cloud-init.conf printf '# Managed by the ssh.ariodesk.com installer\n%s\n' "$merged" > "$dropin" else { echo '# BEGIN ariodesk (managed by the ssh.ariodesk.com installer)' printf '%s\n' "$merged" echo '# END ariodesk' sed '/^# BEGIN ariodesk/,/^# END ariodesk/d' "$conf"; } > "$conf.tmp" cat "$conf.tmp" > "$conf" && rm -f "$conf.tmp" fi if ! "$sshd_bin" -t 2>/tmp/sshd-test.log; then cp -p "$conf.bak.ariodesk" "$conf" if [ $had_dropin = yes ]; then cp -p "$dropin.bak.ariodesk" "$dropin"; else rm -f "$dropin"; fi die "sshd config test failed, original restored: $(cat /tmp/sshd-test.log)" fi if [ "$port" != 22 ] && command -v semanage >/dev/null; then semanage port -a -t ssh_port_t -p tcp "$port" 2>/dev/null || semanage port -m -t ssh_port_t -p tcp "$port" 2>/dev/null || true fi if systemctl is-active --quiet ssh.socket; then systemctl daemon-reload; systemctl restart ssh.socket; systemctl try-restart "$svc" else systemctl restart "$svc" fi ok "sshd config updated: $(printf '%s, ' "${opts[@]}" | sed 's/, $//')" fi # --- 7. Firewall ------------------------------------------------------ if [ $open_fw = yes ]; then case $fw in ufw) ufw allow "$port/tcp" >/dev/null && ok "ufw: allowed $port/tcp" ;; firewalld) firewall-cmd --permanent --add-port="$port/tcp" >/dev/null && firewall-cmd --reload >/dev/null && ok "firewalld: allowed $port/tcp" ;; esac fi # --- Done ------------------------------------------------------------- echo printf '\033[32mOpenSSH server is ready.\033[0m\n' echo " Host IPs : $(hostname -I 2>/dev/null)" echo " Connect : ssh $user@ -p $port" } # Nothing runs until the whole script has been downloaded main "$@"