SSH
SSH¶
Proxmox - SSH¶
As SSH is our mostly used tool, we need to make it safe.
Keep SSH up to date¶
Please consult the OpenSSH pages to ascertain the most recent version and any security concerns that have been identified. You can update it from your repository or build it yourself.
Hardening – SSH Key pairs¶
OpenSSH server can impose restrictions on a per-key basis. You can control access for machine-to-machine sessions, as well as allow non-sudo users to set restrictions for their own user account.
SSH-keys¶
Use SSH keys (ed25519) certificates and not passwords
SSH Authentication Keys
Prior to implementing firewall adjustments, we generate SSH keys for the Proxmox server. We can do it command line:
Or we can use the following shell script that will generate the keys, configuration for passwordless SSH access to a remote server.#!/bin/bash
###############################################################################
# Script Name: ssh_key_setup.sh
# Description: Automates SSH key pair generation and configuration for
# passwordless SSH access to a remote server.
#
# Usage: ./ssh_key_setup.sh <host_name> <ip_address> <username>
#
# Arguments:
# <host_name> - Name of the remote server.
# <ip_address> - IP address of the remote server.
# <username> - Username for SSH access on the remote server.
#
# Exit Codes:
# 0 - Success
# 1 - Invalid arguments or parameter validation failed
# 2 - Directory or file creation failed
# 3 - SSH key generation failed
# 4 - Failed to copy SSH key to remote server
#
# Notes:
# - Assumes `ssh-copy-id` is available on the system.
# - SSH key is generated without a passphrase for automation.
###############################################################################
# Function to validate an IP address
valid_ip() {
local ip=$1
local stat=1
if [[ $ip =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
IFS='.' read -r -a octets <<< "$ip"
if [[ ${octets[0]} -le 255 && ${octets[1]} -le 255 && \
${octets[2]} -le 255 && ${octets[3]} -le 255 ]]; then
stat=0
fi
fi
return $stat
}
# Ensure the script is run with the correct number of parameters
if [ "$#" -ne 3 ]; then
echo "Usage: $0 <host_name> <ip_address> <username>"
exit 1
fi
# Assign parameters to variables
host_name=$1
ip_address=$2
user=$3
local_user=$USER
# Validate the IP address
if ! valid_ip "$ip_address"; then
echo "Error: Invalid IP address format: $ip_address"
exit 1
fi
# Define and create the SSH config directory if it doesn't exist
config_directory="/home/${local_user}/.ssh/include.d/${host_name}"
if [ ! -d "$config_directory" ]; then
echo "Creating SSH config directory: $config_directory..."
mkdir -p "$config_directory" || { echo "Error: Failed to create directory"; exit 2; }
fi
# Set ACL permissions on the directory
setfacl -d -m u::rw,g::-,o::- "$config_directory" || {
echo "Error: Failed to set ACL on $config_directory"; exit 2;
}
# Generate SSH key pair
ssh-keygen -t ed25519 -f "${config_directory}/${host_name}" -N "" || {
echo "Error: SSH key generation failed"; exit 3;
}
# Copy the SSH key to the remote server
echo "Copying SSH public key to ${user}@${ip_address}..."
ssh-copy-id -i "${config_directory}/${host_name}.pub" "${user}@${ip_address}" || {
echo "Error: Failed to copy SSH key"; exit 4;
}
# Create SSH config file
cat <<EOL > "${config_directory}/config"
Host ${host_name}
HostName ${ip_address}
User ${user}
IdentityFile ${config_directory}/${host_name}
EOL
# Append Include directive to ~/.ssh/config if not already present
ssh_config_file="/home/${local_user}/.ssh/config"
touch "$ssh_config_file"
chmod 600 "$ssh_config_file"
if ! grep -Fxq "Include ${config_directory}/config" "$ssh_config_file"; then
echo "Adding 'Include ${config_directory}/config' to $ssh_config_file..."
echo "Include ${config_directory}/config" | cat - "$ssh_config_file" > temp_file && mv temp_file "$ssh_config_file" || {
echo "Error: Failed to update SSH config"; exit 2;
}
fi
# Test SSH connection
echo "Testing SSH connection to ${host_name}..."
ssh "${host_name}" || {
echo "Error: Failed to connect to ${host_name}"; exit 1;
}
echo "SSH setup complete: Key created, config updated, and connection tested."
Using keys¶
If the script is used the public key will be added by ssh-copy-id to authorized_keys, otherwise begin by opening your .ssh/authorized_keys file in nano. Since these configurations apply on a per-key basis, you’ll need to edit each individual key within each individual authorized_keys file that you want them to apply to, for all users on your system. Usually you will only need to edit one key/file, but this is worth considering if you have a complex multi-user system.
The following restriction options are available:
- no-agent-forwarding: Disable SSH agent forwarding.
- no-port-forwarding: Disable SSH port forwarding.
- no-pty: Disable the ability to allocate a TTY (i.e. start a shell).
- no-user-rc: Prevent execution of the ~/.ssh/rc file.
- no-X11-forwarding: Disable X11 display forwarding.
Hardening in general¶
The exact hardening configuration that is most suitable for your own server depends heavily on your own threat model and risk threshold. However, the configuration you’ll use in this step is a general secure configuration that will suit the majority of servers.
We will not edit the main OpenSSH configuration file in /etc/ssh/sshd_config but will use additional configuration files in /etc/ssh/sshd_config.d to set the majority of the hardening options. Before continuing, it is a good idea to create a backup of your existing configuration file so that you can restore it in the unlikely event that something goes wrong. It alos protects it from sshd_config being overwritten by updates.
Before doing anything, make a copy
Additional ssh settings¶
We are creating /etc/ssh/sshd_config.d/ssh_changes.conf and we can properly document the chnages we make.
As can be seen from line 1 we leave Port 22 open. Any security audtior will claim that the SSH port should be changed but we cannot do this on the Proxmox servers as port 22 on Proxmox does also all communication via port 22. Changing the port will break corosync, HA live migrates etc.
Port 22
AddressFamily inet
ListenAddress 0.0.0.0
#-----------------------------------------------------------------------------------#
# Logging
SyslogFacility AUTH
LogLevel VERBOSE
#-----------------------------------------------------------------------------------#
#-----------------------------------------------------------------------------------#
# Login restrictions
#-----------------------------------------------------------------------------------#
LoginGraceTime 30
PermitRootLogin prohibit-password
StrictModes yes
MaxAuthTries 3
MaxSessions 2
PasswordAuthentication yes
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
HostbasedAuthentication no
IgnoreRhosts yes
PermitEmptyPasswords no
ChallengeResponseAuthentication no
UsePAM yes
AllowAgentForwarding no
AllowTcpForwarding no
GatewayPorts no
X11Forwarding no
#X11DisplayOffset 10
#X11UseLocalhost yes
PermitTTY yes
PrintMotd no
PrintLastLog yes
#UsePrivilegeSeparation sandbox
PermitUserEnvironment no
TCPKeepAlive no
#-----------------------------------------------------------------------------------#
# Enable compression
#-----------------------------------------------------------------------------------#
Compression no
ClientAliveInterval 300
ClientAliveCountMax 2
#UseDNS no
PidFile /var/run/sshd.pid
MaxStartups 10:30:100
PermitTunnel no
#ChrootDirectory none
VersionAddendum none
#-----------------------------------------------------------------------------------#
# Display a banner before authenticating to the system. Use your own /etc/issue.net file
#-----------------------------------------------------------------------------------#
Banner /etc/issue.net
# Allow only specific groups to access SSH
#AllowGroups ssh-users
#-----------------------------------------------------------------------------------#
# Allowing one or more system users to login over SSH
#-----------------------------------------------------------------------------------#
AllowUsers root nimda tkhrl
After you done something, test the config
This command will execute the OpenSSH server in an extended test mode, thereby validating the entire configuration file and displaying the effective configuration values.
Edit the config -file you created
When you edit your configuration file, some options can be commented out by using a single character (#) at the beginning of the line. To turn on the option, remove the hash. Look at the sshd_config for examples.
Test the syntax for success
Test mode. Only check the validity of the configuration file and sanity of the keys.
No output means a valid configuration. If issues, you will have output describing the syntax error.
Extended test mode. Check the validity of the configuration file, output the effective configuration to stdout, and then exit sshd -T.
Reload and Apply
Firewall Modifications for SSH on Proxmox¶
Configuring the firewall for SSH is straightforward and could be managed entirely through the web interface. Keep in mind that different firewalls offer varying features, so the following is a high-level description of the adjustments that need to be made:
- SSH was enabled on the LAN interface exclusively.
- Root user login was disabled to enhance security.
- Password-based login was disabled, requiring more secure authentication methods.
- A new user account was created, which required administrative privileges for SSH access.
- The SSH authentication key was uploaded to ensure secure access.
These changes successfully enable SSH access to the firewall, setting the stage for more secure operations.
Configuring SSH with .ssh/config¶
To streamline SSH commands using configuration files, you can create specific config files for each server if you did not use the ssh script, like this:
Config File Name: pve
# Configuration file for Proxmox VE
Host pve
HostName 192.168.167.127
User luke
ProxyJump firewall
IdentityFile ~/.ssh/pve
Changes to: ~/.ssh/config
With these configurations in place, connecting to the Proxmox server simplifies to a single command: