#!/usr/bin/env bash # # Env vars # # SSH config paths SSH_CFG_DIR="/etc/ssh" SSH_USER_CA="$SSH_CFG_DIR/ssh_user_ca.pub" SSH_HOST_KEY="$SSH_CFG_DIR/ssh_host_ed25519_key" SSH_HOST_PUBLIC_KEY="$SSH_HOST_KEY.pub" SSH_HOST_CERT="$SSH_HOST_KEY-cert.pub" GREEN_CHECK="\e[32m✔\e[0m" RED_X="\e[31m✗\e[0m" YELLOW_BANG="\e[33m!\e[0m" # # Function Definition # # This test loads the sshd config to see what values actually get parsed. ssh_config_val() { local field="$1" local val if [[ -z "$field" ]]; then echo "usage: ssh_config_val " >&2 return 2 fi echo $(sshd -T 2>/dev/null | grep -i "^$field " | head -1 | awk '{print $2}') } prompt_user() { local title="\e[1m${1:-Title}\e[0m" local prompt="${2:-Prompt for the user}" full_prompt_msg="$title: $prompt" echo -n -e "$YELLOW_BANG $full_prompt_msg" read -p " (y/n) " -n 1 -r echo } update_prompt() { local icon="$1" local msg="${2:-$full_prompt_msg}" # \e[1A: Move up one line # \r: Move to start of line # \e[K: Clear to end of line echo -en "\e[1A\r\e[K" if [[ "$msg" != "$full_prompt_msg" ]]; then echo -e "$icon $msg" else echo -e "$icon $msg $REPLY" fi } auto_update_prompt() { if [[ $REPLY =~ ^[Yy]$ ]]; then update_prompt $GREEN_CHECK elif [[ $REPLY =~ ^[Nn]$ ]]; then update_prompt $RED_X fi } sign_host_cert() { local if="eth0" local IP_ADDRESS=$(ip -4 addr show dev $if | awk '/inet /{print $2}' | cut -d/ -f1) && \ local hostname=$(hostname -s) && \ step ssh certificate --host --sign \ --principal "$hostname" \ --principal "$hostname.john-stream.com" \ --principal "$IP_ADDRESS" \ --provisioner admin \ "$hostname" "$SSH_HOST_PUBLIC_KEY" } check_ssh_config_files() { row_success() { local key="$1" local path="$2" local perms=$(stat -c '%a' "$path") printf "%-17b %-20s %-6s %s\n" " $GREEN_CHECK" "$key" "$perms" "$path" } row_missing() { local key="$1" local path="$2" printf "%-15b %-20s %-6s %s\n" " $YELLOW_BANG" "$key" "-" "$path (missing)" } row_unconfigured() { local key="$1" printf "%-17b %-20s %-6s %s\n" " $RED_X" "$key" "-" "(not configured)" } get_key_status() { local path="$1" if [[ -z "$path" ]]; then echo "unconfigured" elif [[ ! -e "$path" ]]; then echo "missing" else echo "success" fi } row_process() { local key="$1" path=$(ssh_config_val "$key") status=$(get_key_status "$path") case "$status" in success) row_success "$key" "$path" ;; missing) row_missing "$key" "$path" ;; unconfigured) row_unconfigured "$key" ;; esac } printf "%-6s %-20s %-6s %s\n" "STATUS" "KEY" "PERMS" "PATH" row_process "hostkey" row_process "hostcertificate" case "$status" in missing) prompt_user "SSH Host" "SSH host cert missing. Sign the ssh host cert?" if [[ $REPLY =~ ^[Yy]$ ]]; then sign_host_cert update_prompt $GREEN_CHECK "Signed ssh host cert" else update_prompt $RED_X fi ;; esac row_process "trustedusercakeys" case "$status" in missing) prompt_user "User CA" "Created the trusted keys file?" if [[ $REPLY =~ ^[Yy]$ ]]; then (step ssh config --roots > "$path") update_prompt $GREEN_CHECK "Created public key file for SSH user CA" else update_prompt $RED_X fi ;; esac } ssh_fingerprint() { local field="$1" if [[ -z "$field" ]]; then echo "usage: ssh_fingerprint " >&2 return 2 fi local cfg_path=$(ssh_config_val $field) if [[ -z "$cfg_path" ]]; then echo "error: sshd field '$field' not found or empty" >&2 return 1 fi if [[ ! -r "$cfg_path" ]]; then echo "error: file not readable: $cfg_path" >&2 return 1 fi ssh-keygen -lf "$cfg_path" | awk '{ print $2 }' } check_cert_config() { local base_dir="/etc/ssh/sshd_config.d" local cfg_path="$base_dir/${1:-certs.conf}" install_cert_config() { mkdir -p $(dirname $cfg_path) cat < $cfg_path TrustedUserCAKeys $SSH_USER_CA HostKey $SSH_HOST_KEY HostCertificate $SSH_HOST_CERT EOF } if [[ ! -e $cfg_path ]]; then prompt_user "sshd config" "Do you want to configure sshd?" if [[ $REPLY =~ ^[Yy]$ ]]; then install_cert_config update_prompt $GREEN_CHECK "Configured sshd" fi fi restart_sshd } restart_sshd() { if ! systemctl is-active --quiet sshd; then prompt_user "sshd.service" "sshd.service is not active. Restart?" if [[ $REPLY =~ ^[Yy]$ ]]; then systemctl restart sshd local sshd_pid=$(systemctl show --property MainPID --value sshd) update_prompt $GREEN_CHECK "Restarted sshd.service on PID: $sshd_pid" fi else local sshd_pid=$(systemctl show --property MainPID --value sshd) echo -e "$GREEN_CHECK sshd.service is active on PID: $sshd_pid" fi } # Run Process check_cert_config "certs.conf" echo check_ssh_config_files echo echo "Host key fingerprint" ssh_fingerprint hostkey