{"id":995,"date":"2026-08-28T23:52:45","date_gmt":"2026-08-28T23:52:45","guid":{"rendered":"https:\/\/virtualserversvps.com\/blog\/?p=995"},"modified":"2026-10-16T22:03:07","modified_gmt":"2026-10-16T22:03:07","slug":"ssh-security-best-practices-vps","status":"publish","type":"post","link":"https:\/\/virtualserversvps.com\/blog\/ssh-security-best-practices-vps\/","title":{"rendered":"Hardening SSH on a VPS: Bastion Architecture, Key Attribution and Revocation That Scales"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">SSH is the single most attacked service on any internet-facing VPS. Every public IPv4 address receives thousands of authentication attempts per day, and most of those attempts target <code>root<\/code> with a short password list. Hardening SSH properly is not about setting <code>PasswordAuthentication no<\/code> and calling it done \u2014 it is about building an access architecture that scales, that lets you attribute every login to a human, and that lets you revoke access in seconds when someone leaves. This guide covers the model and the measurement commands behind it.<\/p>\n\n\n<p class=\"wp-block-paragraph\">If you are still choosing a host, review the <a href=\"https:\/\/virtualserversvps.com\/\">VPS comparison table on our main site<\/a> \u2014 provider differences in IPv6 support, console access and snapshot tooling directly affect how you recover a VPS when SSH is misconfigured. Once you have a box, the rest of this article is about making that access plane defensible.<\/p>\n\n\n<h2 class=\"wp-block-heading\">Start by Measuring What Is Actually Hitting Your SSH Port<\/h2>\n\n\n<p class=\"wp-block-paragraph\">Before changing a single line of <code>sshd_config<\/code>, quantify the noise. This tells you whether you are facing distributed credential stuffing, a targeted campaign, or simple background scanning.<\/p>\n\n\n<pre class=\"wp-block-code\"><code>journalctl -u ssh --since \"24 hours ago\" | grep -c \"Failed password\"\nss -tn state established '( sport = :22 )'\nauth={}; zgrep -h \"Failed password\" \/var\/log\/auth.log* 2>\/dev\/null | awk '{print $1}' | sort | uniq -c | sort -rn | head<\/code><\/pre>\n\n\n<p class=\"wp-block-paragraph\">The first command gives you a 24-hour failure count. The second shows who is legitimately connected right now. The third ranks source IPs. If you see tens of thousands of failures from diverse networks, that is automated scanning and rate limiting plus key-only auth will eliminate essentially all of it.<\/p>\n\n\n<p class=\"wp-block-paragraph\">Install <code>sshd<\/code> connection accounting so you always know the source: set <code>LogLevel VERBOSE<\/code> to record key fingerprints per login. Without that, all sessions look identical and you cannot attribute activity to a person or a key.<\/p>\n\n\n<h2 class=\"wp-block-heading\">Move From Passwords to Keys, Then From Keys to Certificates<\/h2>\n\n\n<p class=\"wp-block-paragraph\">Password authentication on a public VPS is indefensible. Keys are the baseline, but a fleet of long-lived keys pasted into <code>authorized_keys<\/code> files is its own problem: you cannot tell which key belongs to whom, and revocation means editing every host. A bastion with short-lived, signed certificates solves attribution and revocation at once.<\/p>\n\n\n<ul class=\"wp-block-list\"><li>Password auth: disabled <code>PasswordAuthentication no<\/code> and <code>PermitRootLogin prohibit-password<\/code><\/li>\n<li>Host keys: rotate to Ed25519, remove any RSA keys below 3072 bits<\/li>\n<li>Per-user keys: one key per person, never shared, with a comment naming the owner<\/li>\n<li>Certificates: sign user keys against a CA with a short validity window, typically 8 to 12 hours<\/li><\/ul>\n\n\n<pre class=\"wp-block-code\"><code>ssh-keygen -t ed25519 -a 64 -C \"alice@example 2026-01\" -f ~\/.ssh\/id_ed25519_alice\nssh-keygen -s \/etc\/ssh\/ca_user -I alice -n alice,deploy -V +12h ~\/.ssh\/id_ed25519_alice.pub<\/code><\/pre>\n\n\n<p class=\"wp-block-paragraph\">The <code>-V +12h<\/code> flag means the certificate is worthless after twelve hours. An attacker who exfiltrates a laptop gains nothing. Revocation becomes a matter of not re-signing, and you can maintain a KRL (key revocation list) for the rare stolen-key case: <code>ssh-keygen -k -f \/etc\/ssh\/revoked_keys<\/code>.<\/p>\n\n\n<h2 class=\"wp-block-heading\">Bastion Patterns: Avoid the Single Point of Failure<\/h2>\n\n\n<p class=\"wp-block-paragraph\">A single bastion is a comfort until it goes down. Design for two paths: a redundant bastion pair behind a floating IP, plus an out-of-band provider console as the break-glass path. Record the console credentials in your password manager and test the console quarterly \u2014 a recovery path you have never used is not a recovery path.<\/p>\n\n\n<p class=\"wp-block-paragraph\">On the bastion itself, run <code>sshd -T<\/code> to dump the effective configuration and diff it against your intent. Configuration drift on SSH is common because operators edit live files. Capture a golden <code>sshd_config.d\/*.conf<\/code> set in version control and enforce it with a configuration-drift checker.<\/p>\n\n\n<h2 class=\"wp-block-heading\">Harden the Daemon Without Breaking Automation<\/h2>\n\n\n<p class=\"wp-block-paragraph\">Aggressive hardening often breaks deployment scripts. Tighten deliberately and test each change against a non-root automation user before rollout.<\/p>\n\n\n<ul class=\"wp-block-list\"><li><code>PermitRootLogin no<\/code> \u2014 force named accounts with sudo<\/li>\n<li><code>MaxAuthTries 3<\/code> and <code>LoginGraceTime 20<\/code> to shrink the brute-force window<\/li>\n<li><code>AllowUsers<\/code> or an <code>AllowGroups ssh-users<\/code> group to whitelist principals<\/li>\n<li><code>KexAlgorithms<\/code>, <code>Ciphers<\/code> and <code>MACs<\/code> trimmed to modern suites<\/li>\n<li><code>ClientAliveInterval 300<\/code> with <code>ClientAliveCountMax 2<\/code> to reap dead sessions<\/li><\/ul>\n\n\n<pre class=\"wp-block-code\"><code>sshd -T | sort > \/tmp\/effective.txt\ndiff <(sort \/etc\/ssh\/sshd_config.golden) \/tmp\/effective.txt<\/code><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Pair this with <code>fail2ban<\/code> or nftables rate limiting on port 22 as a first-line filter, but treat it as noise reduction, not security. The real gate is key and certificate authentication with a restricted principal list.<\/p>\n\n\n<h2 class=\"wp-block-heading\">Port Knocking, Non-Standard Ports and Why They Barely Matter<\/h2>\n\n\n<p class=\"wp-block-paragraph\">Moving SSH to a non-standard port cuts log volume because dumb scanners only hit 22, but it provides no cryptographic protection and costs you clarity. Port knocking adds a shared secret but also adds a failure mode that locks you out of your own server. If you use either, document it in the runbook and never rely on it as the sole control.<\/p>\n\n\n<p class=\"wp-block-paragraph\">The measurable win from obscurity is smaller log files and less fail2ban churn. The measurable risk is an operator who forgets the port during an incident. Prefer certificate auth on port 22 with rate limiting; the log volume is the only real cost.<\/p>\n\n\n<h2 class=\"wp-block-heading\">Monitoring, Alerting and Access Review<\/h2>\n\n\n<p class=\"wp-block-paragraph\">Security controls decay silently. Build three signals that you actually page on: a login from a new source ASN, a login using a key outside your CA, and more than three failures for a known account in an hour. Feed these into whatever alerting you already run and treat anomalies as incidents.<\/p>\n\n\n<pre class=\"wp-block-code\"><code>last -ai | head -20\njournalctl -u ssh --since today | grep -E \"Accepted|Failed\" | tail -50<\/code><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Run a quarterly access review: list every active certificate, its principal and its expiry, and remove anything stale. This is the same discipline behind our <a href=\"https:\/\/virtualserversvps.com\/\">VPS performance tuning guides<\/a> \u2014 measure, tighten, verify, repeat. Access control is a process, not a one-time config drop.<\/p>\n\n\n<h2 class=\"wp-block-heading\">Putting It Together<\/h2>\n\n\n<p class=\"wp-block-paragraph\">A defensible SSH setup on a VPS has three layers: network filtering to cut noise, certificate-based authentication for attribution and fast revocation, and instrumentation so you can see both attacks and legitimate access. Disable passwords, run a CA, keep certificates short-lived, and always maintain a tested console break-glass path.<\/p>\n\n\n<p class=\"wp-block-paragraph\">Every change should be verifiable with a command. If you cannot answer \"who logged in, with which key, from where, and when did that key expire\" in under a minute, the architecture needs work. Start with key-only auth today, add the CA this week, and rehearse the console recovery path before you need it.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>SSH is the single most attacked service on any internet-facing VPS. Every public IPv4 address receives thousands of authentication attempts per day, and most of those attempts target root with&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"iawp_total_views":3,"footnotes":""},"categories":[4],"tags":[],"class_list":["post-995","post","type-post","status-publish","format-standard","hentry","category-security-compliance"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v26.1 (Yoast SEO v26.1) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Hardening SSH on a VPS: Bastion Architecture, Key Attribution and Revocation That Scales - Virtual Servers VPS Blog<\/title>\n<meta name=\"description\" content=\"SSH is the single most attacked service on any internet-facing VPS. Every public IPv4 address receives thousands of authentication attempts per day, and\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/virtualserversvps.com\/blog\/ssh-security-best-practices-vps\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Hardening SSH on a VPS: Bastion Architecture, Key Attribution and Revocation That Scales\" \/>\n<meta property=\"og:description\" content=\"Hardening SSH on a VPS: Bastion Architecture, Key Attribution and Revocation That Scales\" \/>\n<meta property=\"og:url\" content=\"https:\/\/virtualserversvps.com\/blog\/ssh-security-best-practices-vps\/\" \/>\n<meta property=\"og:site_name\" content=\"Virtual Servers VPS Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-08-28T23:52:45+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-10-16T22:03:07+00:00\" \/>\n<meta name=\"author\" content=\"Virtual-Servers-Vps-Editor\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Virtual-Servers-Vps-Editor\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"3 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/ssh-security-best-practices-vps\/\",\"url\":\"https:\/\/virtualserversvps.com\/blog\/ssh-security-best-practices-vps\/\",\"name\":\"Hardening SSH on a VPS: Bastion Architecture, Key Attribution and Revocation That Scales - Virtual Servers VPS Blog\",\"isPartOf\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#website\"},\"datePublished\":\"2026-08-28T23:52:45+00:00\",\"dateModified\":\"2026-10-16T22:03:07+00:00\",\"author\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0\"},\"description\":\"SSH is the single most attacked service on any internet-facing VPS. Every public IPv4 address receives thousands of authentication attempts per day, and\",\"breadcrumb\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/ssh-security-best-practices-vps\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/virtualserversvps.com\/blog\/ssh-security-best-practices-vps\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/ssh-security-best-practices-vps\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/virtualserversvps.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Hardening SSH on a VPS: Bastion Architecture, Key Attribution and Revocation That Scales\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#website\",\"url\":\"https:\/\/virtualserversvps.com\/blog\/\",\"name\":\"Virtual Servers VPS Blog\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/virtualserversvps.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0\",\"name\":\"Virtual-Servers-Vps-Editor\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g\",\"caption\":\"Virtual-Servers-Vps-Editor\"},\"sameAs\":[\"https:\/\/virtualserversvps.com\/blog\"],\"url\":\"https:\/\/virtualserversvps.com\/blog\/author\/virtualserversvps\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Hardening SSH on a VPS: Bastion Architecture, Key Attribution and Revocation That Scales - Virtual Servers VPS Blog","description":"SSH is the single most attacked service on any internet-facing VPS. Every public IPv4 address receives thousands of authentication attempts per day, and","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/virtualserversvps.com\/blog\/ssh-security-best-practices-vps\/","og_locale":"en_US","og_type":"article","og_title":"Hardening SSH on a VPS: Bastion Architecture, Key Attribution and Revocation That Scales","og_description":"Hardening SSH on a VPS: Bastion Architecture, Key Attribution and Revocation That Scales","og_url":"https:\/\/virtualserversvps.com\/blog\/ssh-security-best-practices-vps\/","og_site_name":"Virtual Servers VPS Blog","article_published_time":"2026-08-28T23:52:45+00:00","article_modified_time":"2026-10-16T22:03:07+00:00","author":"Virtual-Servers-Vps-Editor","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Virtual-Servers-Vps-Editor","Est. reading time":"3 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/virtualserversvps.com\/blog\/ssh-security-best-practices-vps\/","url":"https:\/\/virtualserversvps.com\/blog\/ssh-security-best-practices-vps\/","name":"Hardening SSH on a VPS: Bastion Architecture, Key Attribution and Revocation That Scales - Virtual Servers VPS Blog","isPartOf":{"@id":"https:\/\/virtualserversvps.com\/blog\/#website"},"datePublished":"2026-08-28T23:52:45+00:00","dateModified":"2026-10-16T22:03:07+00:00","author":{"@id":"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0"},"description":"SSH is the single most attacked service on any internet-facing VPS. Every public IPv4 address receives thousands of authentication attempts per day, and","breadcrumb":{"@id":"https:\/\/virtualserversvps.com\/blog\/ssh-security-best-practices-vps\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/virtualserversvps.com\/blog\/ssh-security-best-practices-vps\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/virtualserversvps.com\/blog\/ssh-security-best-practices-vps\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/virtualserversvps.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Hardening SSH on a VPS: Bastion Architecture, Key Attribution and Revocation That Scales"}]},{"@type":"WebSite","@id":"https:\/\/virtualserversvps.com\/blog\/#website","url":"https:\/\/virtualserversvps.com\/blog\/","name":"Virtual Servers VPS Blog","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/virtualserversvps.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0","name":"Virtual-Servers-Vps-Editor","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g","caption":"Virtual-Servers-Vps-Editor"},"sameAs":["https:\/\/virtualserversvps.com\/blog"],"url":"https:\/\/virtualserversvps.com\/blog\/author\/virtualserversvps\/"}]}},"_links":{"self":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts\/995","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/comments?post=995"}],"version-history":[{"count":11,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts\/995\/revisions"}],"predecessor-version":[{"id":1414,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts\/995\/revisions\/1414"}],"wp:attachment":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/media?parent=995"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/categories?post=995"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/tags?post=995"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}