{"id":51,"date":"2018-05-25T07:00:00","date_gmt":"2018-05-25T07:00:00","guid":{"rendered":"http:\/\/ssdnodes.billabailey.com\/2017\/08\/24\/tutorial-using-two-factor-authentication-to-protect-ssh-logins\/"},"modified":"2025-05-18T19:46:36","modified_gmt":"2025-05-18T19:46:36","slug":"ssh-two-factor-authentication-vps","status":"publish","type":"post","link":"https:\/\/www.ssdnodes.com\/blog\/ssh-two-factor-authentication-vps\/","title":{"rendered":"SSH two-factor authentication: How to enable on your VPS"},"content":{"rendered":"<p>Sometimes, you want to be doubly sure that no one can get access to your VPS but you. If two-factor authentication (2FA) can work for <a href=\"https:\/\/www.ssdnodes.com\/blog\/tutorial-enabling-two-factor-authentication-with-ssd-nodes\/\">a dashboard<\/a>, why not for a VPS? In this tutorial, we'll talk about enabling SSH two-factor authentication (2FA) on an Ubuntu 16.04 VPS, via Google Authenticator (or another 2FA app of your choice) so that you can put another layer of protection on your VPS.<\/p>\n<p>It's important to note that the most <em>common<\/em> and <em>recommended <\/em>method of securing SSH-based logins is <a href=\"https:\/\/www.ssdnodes.com\/blog\/tutorial-setting-up-and-securing-ssh-based-authentication\/\">SSH keys<\/a>, and we heartily recommend you use them on all your VPSs.<\/p>\n<p>For the sake of explaining how SSH two-factor authentication works, we'll start with using 2FA to protect an SSH configuration that uses passwords, not SSH keys. We'll follow that up with a recommendation on how you might use both SSH two-factor authentication <em>and<\/em> SSH keys for a rock-solid (and perhaps overkill) triple-factor SSH authentication.<\/p>\n<pre>Updated on May 25, 2018!<\/pre>\n<h2>Prerequisites<\/h2>\n<ul>\n<li>A VPS running Ubuntu 16.04<\/li>\n<li>A non-root, <code>sudo<\/code>-enabled user. If you only have a <code>root<\/code> user, see our <a href=\"https:\/\/www.ssdnodes.com\/blog\/tutorial-setting-up-and-securing-ssh-based-authentication\/#adding-a-new-user\">SSH tutorial<\/a> for details on creating new users.<\/li>\n<\/ul>\n<div class=\"cta-inline\"><\/div>\n<h2>Step 1. Installing Google Authenticator on the VPS<\/h2>\n<p>First off, you need to install the Google Authenticator program and run it as the user you're going to log in with\u2014preferably <em>not<\/em> the root account.<\/p>\n<pre><code class=\"language-shell hljs\"><span class=\"hljs-meta\">$<\/span><span class=\"bash\"> sudo apt-get install libpam-google-authenticator<\/span>\n<span class=\"hljs-meta\">$<\/span><span class=\"bash\"> google-authenticator<\/span>\n<\/code><\/pre>\n<p>You'll first be prompted as to whether or not you want to use time-based tokens. The other option is one-time tokens, which is probably not what you want. Type <code>y<\/code> to use time-based tokens.<\/p>\n<pre><code class=\"language-shell hljs\">Do you want authentication tokens to be time-based (y\/n) y\n<\/code><\/pre>\n<p>Once you confirm time-based tokens, you'll see a large amount of output on your terminal. This will include the QR code that you can use with Google Authenticator (or another similar program), or a secret key that you can enter in manually if your device can't scan QR codes.<\/p>\n<p>Once you've set up the token on your mobile device, you can take a look at the secret keys that the program generates, along with emergency scratch codes, which you can use once if you lose that device.<\/p>\n<p>There are a few more questions to answer\u2014the first of which is whether you want to save the Authenticator settings. Answer in the affirmative.<\/p>\n<pre><code class=\"hljs sql\"><span class=\"hljs-keyword\">Do<\/span> you want me <span class=\"hljs-keyword\">to<\/span> <span class=\"hljs-keyword\">update<\/span> your <span class=\"hljs-string\">\"\/home\/USER\/.google_authenticator\"<\/span> <span class=\"hljs-keyword\">file<\/span> (y\/n) y\n<\/code><\/pre>\n<p>The next option helps reduce the risk of certain attacks, so you'll want to use it as well.<\/p>\n<pre><code class=\"hljs sql\"><span class=\"hljs-keyword\">Do<\/span> you want <span class=\"hljs-keyword\">to<\/span> <span class=\"hljs-keyword\">disallow<\/span> multiple uses <span class=\"hljs-keyword\">of<\/span> the same <span class=\"hljs-keyword\">authentication<\/span>\ntoken? This restricts you <span class=\"hljs-keyword\">to<\/span> one login about every <span class=\"hljs-number\">30<\/span>s, but it increases\nyour chances <span class=\"hljs-keyword\">to<\/span> <span class=\"hljs-keyword\">notice<\/span> <span class=\"hljs-keyword\">or<\/span> even prevent man-<span class=\"hljs-keyword\">in<\/span>-the-middle attacks (y\/n) y\n<\/code><\/pre>\n<p>The next option, to help reduce the effects of time-skew, probably isn't necessary. If you do have problems later on, you can re-run this configuration and select <code>y<\/code> instead.<\/p>\n<pre><code class=\"hljs coffeescript\">By <span class=\"hljs-keyword\">default<\/span>, tokens are good <span class=\"hljs-keyword\">for<\/span> <span class=\"hljs-number\">30<\/span> seconds <span class=\"hljs-keyword\">and<\/span> <span class=\"hljs-keyword\">in<\/span> order to compensate <span class=\"hljs-keyword\">for<\/span>\npossible time-skew between the client <span class=\"hljs-keyword\">and<\/span> the server, we allow an extra\ntoken before <span class=\"hljs-keyword\">and<\/span> after the current time. If you experience problems with poor\ntime synchronization, you can increase the <span class=\"hljs-built_in\">window<\/span> <span class=\"hljs-keyword\">from<\/span> its <span class=\"hljs-keyword\">default<\/span>\nsize <span class=\"hljs-keyword\">of<\/span> <span class=\"hljs-number\">1<\/span>:<span class=\"hljs-number\">30<\/span>min to about <span class=\"hljs-number\">4<\/span>min. Do you want to <span class=\"hljs-keyword\">do<\/span> so (y\/n) n\n<\/code><\/pre>\n<p>The last option allows you to set a limit on how many times someone can attempt to use security tokens\u2014no more than 3 times every thirty seconds. This dramatically reduces the risk of brute force attacks.<\/p>\n<pre><code class=\"hljs cpp\">If the computer that you are logging into isn't hardened against brute-force\nlogin attempts, you can enable rate-limiting <span class=\"hljs-keyword\">for<\/span> the authentication <span class=\"hljs-keyword\">module<\/span>.\nBy <span class=\"hljs-keyword\">default<\/span>, <span class=\"hljs-keyword\">this<\/span> limits attackers to no more than <span class=\"hljs-number\">3<\/span> login attempts every <span class=\"hljs-number\">30<\/span>s.\nDo you want to enable rate-limiting (y\/n) y\n<\/code><\/pre>\n<h2>Step 2. Copy the code to your smartphone<\/h2>\n<p>Scroll up in your terminal, either with the mousewheel or <code>Page Up<\/code>, and take a look at the QR code, secret key, verification code, and emergency scratch codes. You might want to copy the text into a document for temporary safekeeping, just in case you lose the terminal output during this process.<\/p>\n<p>To add the code to your smartphone, open the Google Authenticator app and tap <code>Scan a barcode<\/code>. Give the app permission to take photos if it asks. Then, simply point your phone at the QR code in the terminal. You should hear a small beep, and you'll then start to see 6-digit codes that change every so often. You'll use those codes when logging in via SSH, so let's turn back to your server.<\/p>\n<h2>Step 3. Enable Google Authenticator in SSH<\/h2>\n<p>Before we go any further, it's best to work in multiple SSH connections in multiple terminals beyond this point. If you try this using only a single terminal and SSH connection, you might accidentally lock yourself out of your VPS due to misconfiguration in your SSH settings. By keeping one SSH connection active and using a second terminal to test these new settings, you can always make further tweaks or roll back to a setting that works.<\/p>\n<p><em>Note: In order for this particular configuration to work,\u00a0<\/em><code>PubkeyAuthentication<\/code> <em>must be set to<\/em> <code>no<\/code> <em>and<\/em> <code>PasswordAuthentication<\/code> <em>must be set to<\/em> <code>yes<\/code><em>. More on using 2FA plus SSH keys in the next step!<\/em><\/p>\n<p>In order to enable these codes via SSH, edit the <code>\/etc\/pam.d\/sshd<\/code> file and add the following line at the beginning.<\/p>\n<pre><code class=\"language-shell hljs\">auth required pam_google_authenticator.so\n<\/code><\/pre>\n<p>You then need to open <code>\/etc\/ssh\/sshd_config<\/code> and look for the <code>ChallengeResponseAuthentication<\/code> line. If you have one, you can simply change <code>no<\/code> to <code>yes<\/code>, and uncomment it (remove the <code>#<\/code>) if necessary. If you don't have this line, add the text below.<\/p>\n<p>Either, way the line in question should look like this:<\/p>\n<pre><code class=\"language-shell hljs\">ChallengeResponseAuthentication yes\n<\/code><\/pre>\n<p>Finally, you just need to restart sshd.<\/p>\n<pre><code class=\"language-shell hljs\"><span class=\"hljs-meta\">$<\/span><span class=\"bash\"> sudo systemctl restart sshd<\/span>\n<\/code><\/pre>\n<p>At this point, you can try connecting to your VPS via another terminal. You'll first be asked for your password, followed by the verification code. If all is working well, you'll be logged in!<\/p>\n<h2>Step 4 (optional). Reconfigure SSH to use both 2FA and SSH keys<\/h2>\n<p>Step 3 relied upon disabled SSH key authentication, and forced password authentication. SSH keys are a reliable way to improve SSH security, whereas 2FA is a bit of an unknown. So, let's figure out how to use both SSH keys and 2FA for <em><strong>maximum factors<\/strong><\/em><em>.<\/em><\/p>\n<p>First, hop back into your <code>\/etc\/pam.d\/sshd<\/code> file and comment out the <code>auth required pam_google_authenticator.so<\/code> we added earlier. It should look like this:<\/p>\n<p><code># auth required pam_google_authenticator.so<\/code><\/p>\n<p>Now, <em>above<\/em> that line, add another:<\/p>\n<p><code>auth [success=done new_authtok_reqd=done default=die] pam_google_authenticator.so nullok<\/code><\/p>\n<p>In your <code>\/etc\/ssh\/sshd_config<\/code> file, ensure that <code>PubkeyAuthentication<\/code> is set to <code>yes<\/code> and is <code>PasswordAuthentication<\/code> to <code>no<\/code>.<\/p>\n<p>In this same file, you also need to change the authentication methods that are used for SSH logins:<\/p>\n<pre class=\"literal-block\"><code>AuthenticationMethods publickey,keyboard-interactive<\/code><\/pre>\n<p>Finally, restart sshd again.<\/p>\n<p><code class=\"language-shell hljs\"><span class=\"hljs-meta\">$<\/span><span class=\"bash\"> sudo systemctl restart sshd<\/span><\/code><\/p>\n<p>Now, when you log into your VPS, you are asked first for the passphrase for your SSH key, followed by your 2FA code. Pretty cool!<\/p>\n<h2>Final thoughts on SSH two-factor authentication<\/h2>\n<p>As I mentioned before, SSH keys are often a superior option over 2FA when it comes to dealing with servers on a regular basis, but there are likely certain configurations where 2FA makes a lot of sense. Even better, it's easy to set up both. Yes, it's a bit of a pain to enter both a passphrase <em>and<\/em> get your phone out to get the 2FA code, but it's certainly less of a pain than dealing with a security breach.<\/p>\n<p>And, finally, don't be like me\u2014make sure your Num Lock is <strong>on<\/strong> before you drive yourself crazy trying to figure out why the verification codes don't work.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Easily enable SSH two-factor authentication to ensure that would-be attackers can&#8217;t easily access your VPS. Combine with SSH keys for even more security.<\/p>\n","protected":false},"author":20,"featured_media":1967,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[18,30],"tags":[],"class_list":["post-51","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devops","category-tutorials"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.ssdnodes.com\/wp-json\/wp\/v2\/posts\/51","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.ssdnodes.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.ssdnodes.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.ssdnodes.com\/wp-json\/wp\/v2\/users\/20"}],"replies":[{"embeddable":true,"href":"https:\/\/www.ssdnodes.com\/wp-json\/wp\/v2\/comments?post=51"}],"version-history":[{"count":3,"href":"https:\/\/www.ssdnodes.com\/wp-json\/wp\/v2\/posts\/51\/revisions"}],"predecessor-version":[{"id":13067,"href":"https:\/\/www.ssdnodes.com\/wp-json\/wp\/v2\/posts\/51\/revisions\/13067"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.ssdnodes.com\/wp-json\/wp\/v2\/media\/1967"}],"wp:attachment":[{"href":"https:\/\/www.ssdnodes.com\/wp-json\/wp\/v2\/media?parent=51"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.ssdnodes.com\/wp-json\/wp\/v2\/categories?post=51"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.ssdnodes.com\/wp-json\/wp\/v2\/tags?post=51"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}