Securing SSH and remote access
Listen to this lesson
This episode is a study companion for CompTIA Linux+ XK0-006 and is not produced by or endorsed by CompTIA.
Why this matters
SSH is how you reach every server you administer, and it is the service most continuously attacked on the public internet — a machine with port 22 open will see brute-force attempts within minutes of coming online.
It is also the service where a configuration mistake locks you out of the box you are configuring. Every change in this lesson has the same rule attached: keep a second session open until you have proved the new settings work.
The lesson
The daemon and its configuration
sshd — the Secure Shell daemon — listens for connections, authenticates them, and starts sessions. Configuration is /etc/ssh/sshd_config (the server); /etc/ssh/ssh_config is the client side, and editing the wrong one is a common waste of an afternoon.
sshd -t # TEST the configuration — do this every time
sshd -T # print the effective config, all defaults resolved
systemctl reload sshd # apply without dropping existing sessions
systemctl restart sshd
sshd -t before reloading, every time. It parses the file and reports errors. Reload with a broken config and the daemon may refuse to start, at which point your existing session is the only thing standing between you and rescue media.
reload rather than restart. Reload re-reads the configuration while leaving current connections alone; restart drops them. On a machine you are connected to, that difference is the difference between a change and an outage.
Keys instead of passwords
Password authentication is guessable and repeatable. Key authentication uses a keypair: the public half goes on the server, the private half never leaves your machine.
ssh-keygen -t ed25519 -C "alice@laptop" # ed25519: modern, short, fast
ssh-keygen -t rsa -b 4096 # if ed25519 is unsupported
ssh-copy-id alice@server # install the public key
ssh alice@server
ssh-copy-id appends your public key to ~/.ssh/authorized_keys on the server and fixes the permissions, which matters because SSH refuses keys whose permissions are too open:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chmod 600 ~/.ssh/id_ed25519
A key that silently does not work, with the server logging "Authentication refused: bad ownership or modes", is nearly always this. It is a deliberate check, not a bug — a world-readable private key is not a secret.
Once keys work, turn passwords off:
PasswordAuthentication no
PubkeyAuthentication yes
KbdInteractiveAuthentication no
That single change removes brute-forcing as a threat entirely: there is nothing to guess.
Root login
PermitRootLogin no # the correct setting
PermitRootLogin prohibit-password # keys only, no password
PermitRootLogin without-password # the old name for the same thing
PermitRootLogin no is the default answer. Root is the one username every attacker knows exists, so permitting it directly gives them half the credential for free. Log in as yourself and escalate with sudo, which also preserves attribution — root logging in tells you nothing about who it was.
prohibit-password is the compromise where automation genuinely needs root over SSH: keys work, passwords do not.
Restricting who may connect
AllowUsers alice bob deploy
AllowUsers alice@10.0.0.0/8
AllowGroups sshusers admins
DenyUsers baduser
AllowUsers and AllowGroups are allowlists, and that is their value: once either is present, everyone not named is refused regardless of their password or key. AllowGroups sshusers is the maintainable form — control access by adding and removing group members rather than editing the config each time.
The user@host form restricts by origin as well, so a service account can only connect from the machines that legitimately need it.
Deny rules are evaluated before allow rules, but relying on DenyUsers alone means anyone you forget to list is permitted. Prefer the allowlist.
Other hardening settings
Port 2222 # non-standard port: reduces log noise, not risk
Protocol 2 # SSHv1 is long dead
X11Forwarding no
AllowTcpForwarding no # if tunnelling is not needed
PermitEmptyPasswords no
MaxAuthTries 3
ClientAliveInterval 300
ClientAliveCountMax 2
LoginGraceTime 30
Disabling X forwarding matters more than it sounds. X11 forwarding lets a remote application draw on your local display — and the X protocol has weak isolation, so a compromised server with your forwarded display can read your keystrokes and capture your other windows. Turn it off on servers unless somebody genuinely needs a GUI.
Changing the port is noise reduction, not security. It stops the undirected scanning that fills your logs; it does not stop anyone who scans ports, which is everyone who matters. Do it for the log volume, not for protection, and never in place of key authentication.
ClientAliveInterval and ClientAliveCountMax drop sessions that have gone away, which tidies up half-dead connections holding files open.
SSH tunnelling
SSH can forward arbitrary TCP over its encrypted channel.
# LOCAL forward: reach a remote service as though it were local
ssh -L 8080:localhost:80 user@server
# your :8080 → server's :80
# REMOTE forward: expose a local service on the remote host
ssh -R 9000:localhost:3000 user@server
# DYNAMIC: a SOCKS proxy on your machine
ssh -D 1080 user@server
ssh -J bastion.example.com user@internal # jump through a bastion host
Local forwarding is how you reach a database bound to 127.0.0.1 on a remote server without exposing it to the network — the correct way to administer a database that should never listen publicly.
It cuts both ways, which is why AllowTcpForwarding no exists: a user with SSH access and forwarding enabled can tunnel out of a network, bypassing egress controls entirely. Remote forwarding with GatewayPorts yes can expose an internal service to the world. On a locked-down bastion, turn forwarding off.
-J (ProxyJump) is the clean way through a bastion: it connects to the internal host through the bastion without your traffic being decrypted there.
SSH agent
An encrypted private key needs its passphrase every time. The agent holds the decrypted key in memory so you type it once.
eval $(ssh-agent) # start it
ssh-add ~/.ssh/id_ed25519 # add a key
ssh-add -l # list loaded keys
ssh-add -D # remove all
ssh-add -t 3600 ~/.ssh/id_ed25519 # expire after an hour
Agent forwarding (ssh -A) lets a remote host use your local keys to authenticate onwards. It is convenient and it is a genuine risk: anyone with root on that intermediate host can use your agent socket to authenticate as you, anywhere your keys are accepted, for as long as you are connected. Prefer ProxyJump to agent forwarding — it achieves the same result without exposing the agent.
SFTP, and what to avoid
SFTP is file transfer over the SSH channel — encrypted, authenticated, and using the same port and credentials.
sftp user@server
scp file user@server:/path/ # simple copy over SSH
rsync -avz -e ssh dir/ user@server:/path/ # efficient sync
Restricting a user to SFTP only, with no shell:
Match Group sftpusers
ChrootDirectory /srv/sftp/%u
ForceCommand internal-sftp
AllowTcpForwarding no
The services to remove rather than configure:
Telnet — a remote shell in plaintext. Usernames, passwords and every keystroke cross the network readable by anyone in the path. There is no secure mode. SSH replaced it thirty years ago.
FTP — file transfer, also plaintext credentials. FTPS and SFTP are the encrypted alternatives; plain FTP should not exist on a modern network.
TFTP — Trivial FTP. No authentication at all, over UDP. It has one legitimate use — serving boot images to PXE clients, which is why it survives — and it should be reachable only from the provisioning network and never from anywhere else.
The general rule: anything that carries credentials in plaintext is not configurable into safety. Remove the package rather than hardening it.
ss -tulnp | grep -E ':(21|23|69)\b' # is anything still listening?
On the exam
-
sshd -tvalidates the config; reload rather than restart to avoid dropping sessions. -
PermitRootLogin no, and usesudo— it also preserves attribution. - Key authentication removes brute-forcing entirely; then set
PasswordAuthentication no. - SSH refuses keys with permissions that are too open:
700on~/.ssh,600on the key andauthorized_keys. -
AllowUsers/AllowGroupsare allowlists — once set, unlisted users are refused. - Changing the port reduces log noise, not risk.
-
-Llocal forward,-Rremote forward,-DSOCKS proxy,-Jjump host. - Agent forwarding exposes your agent to root on the intermediate host; prefer
ProxyJump. - Telnet, FTP and TFTP send credentials in the clear (TFTP has none at all). Remove them; TFTP's one valid use is PXE.
Practise what you just read
1. You have edited sshd_config on a remote server. What is the safest way to apply it?
Select one
Show answer
D. sshd -t parses the file and reports errors without applying it, which catches the typo that would otherwise stop sshd from starting. reload re-reads the configuration without dropping established sessions, where restart drops them. Keep the existing session open and prove a NEW one works before closing it -- otherwise a mistake means a console trip.
2. Key authentication fails and the server log records "Authentication refused: bad ownership or modes". What is wrong?
Select one
Show answer
C. sshd refuses to trust a key that others could have modified, so it requires 700 on ~/.ssh, 600 on authorized_keys and the private key, and a home directory that is not group- or world-writable. The client is told only "permission denied", which is why the server's log is where the actual reason lives.
3. Does moving SSH from port 22 to port 2222 improve security?
Select one
Show answer
A. It removes most of the background brute-force traffic, which makes the logs readable, and that is a genuine operational benefit. It stops no one who scans the host, and a port above 1024 is one an unprivileged process could bind if sshd were ever stopped. Key-only authentication is the control that actually matters.
5 more questions on this objective are part of the full course.
Hands-on labs
Part of the free CompTIA Linux+ XK0-006 course — 48 lessons and 82 hands-on labs.