{"id":2379,"date":"2021-09-04T19:50:01","date_gmt":"2021-09-04T19:50:01","guid":{"rendered":"https:\/\/dft.wiki\/?p=2379"},"modified":"2026-04-21T13:32:46","modified_gmt":"2026-04-21T17:32:46","slug":"hardening-openssh-with-2fa-on-ubuntu-20-04","status":"publish","type":"post","link":"https:\/\/dft.wiki\/?p=2379","title":{"rendered":"Hardening OpenSSH with 2FA on Ubuntu"},"content":{"rendered":"<p>This setup will require the Google Authenticator app to create the OTP (One-Time-Password).<\/p>\n<ul>\n<li>Android [<a href=\"https:\/\/play.google.com\/store\/apps\/details?id=com.google.android.apps.authenticator2\">Link<\/a>]<\/li>\n<li>iOS [<a href=\"https:\/\/itunes.apple.com\/us\/app\/google-authenticator\/id388497605\">Link<\/a>]<\/li>\n<\/ul>\n<pre>sudo apt install libpam-google-authenticator -y\r\nsudo nano \/etc\/pam.d\/sshd<\/pre>\n<p>Append the configuration:<\/p>\n<pre>auth required pam_google_authenticator.so <strong>nullok<\/strong><\/pre>\n<p>The last piece of the configuration above (<strong>nullok<\/strong>) allows users to log in if they did not set up the 2FA yet.<\/p>\n<p>Edit the OpenSSH-Server configuration:<\/p>\n<pre>sudo nano \/etc\/ssh\/sshd_config<\/pre>\n<p>And change these options:<\/p>\n<pre>ChallengeResponseAuthentication yes\r\nPasswordAuthentication no\r\nAuthenticationMethods publickey,keyboard-interactive<\/pre>\n<p>Make sure that you have access with an SSH-Key before setting the password option to <strong>no<\/strong>.<\/p>\n<p>Enable 2FA for<strong> sudo<\/strong> (do NOT combine the 2FA for SSH + SUDO, use one or other):<\/p>\n<pre>sudo nano \/etc\/pam.d\/common-auth<\/pre>\n<p>Append:<\/p>\n<pre>auth required pam_google_authenticator.so <strong>nullok<\/strong>\r\nauth required pam_permit.so<\/pre>\n<p>Now, execute the authenticator to generate the QR-Code for the current user.<\/p>\n<pre>google-authenticator<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-2380 aligncenter\" src=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2021\/09\/Screenshot_2021-09-04_15-27-26.png\" alt=\"\" width=\"701\" height=\"1188\" srcset=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2021\/09\/Screenshot_2021-09-04_15-27-26.png 701w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2021\/09\/Screenshot_2021-09-04_15-27-26-177x300.png 177w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2021\/09\/Screenshot_2021-09-04_15-27-26-604x1024.png 604w\" sizes=\"auto, (max-width: 701px) 100vw, 701px\" \/><\/p>\n<p>Now, restart the SSH service, then try to log in using another terminal to prevent being locked out.<\/p>\n<pre>sudo systemctl restart sshd.service<\/pre>\n<p>Note: a new file <strong>~\/.google_authenticator<\/strong> was created. It can be backed up or even copied to other servers that you want to have access to with the same token.<\/p>\n<p>After all the users have set up their Google-Authenticator, remember to edit <strong>\/etc\/pam.d\/sshd<\/strong> and remove the <strong>nullok<\/strong>, leaving the configuration as follows:<\/p>\n<pre>auth required pam_google_authenticator.so<\/pre>\n<p>To recover access to a user account, read the first line of the configuration file as root and type it in the user&#8217;s Google Authenticator app:<\/p>\n<pre>head -n 1 \/home\/<strong>user<\/strong>\/.google_authenticator<\/pre>\n<p>If it is not possible, just delete the configuration file and make sure the <strong>nullok<\/strong> option is still enabled. In this case, the user will be able to log in without the 2FA and create a new QR code for the 2FA:<\/p>\n<pre>google-authenticator <strong>-t -d -f -r <span class=\"token number\">3<\/span> -R <span class=\"token number\">30<\/span> -W<\/strong><\/pre>\n<p>Note: all the additional arguments <strong>-t -d -f -r <span class=\"token number\">3<\/span> -R <span class=\"token number\">30<\/span> -W<\/strong> are optional. They can be used to set it up in a non-interactive mode.<\/p>\n<p>To force a user to set up its 2FA on the log on, create the following file in the user&#8217;s home directory:<\/p>\n<pre>sudo nano \/home\/<strong>user<\/strong>\/.bash_login<\/pre>\n<p>Then, paste the following content:<\/p>\n<pre>#!\/bin\/bash\r\nFILE=~\/.google_authenticator\r\nif [ ! -f \"$FILE\" ]; then\r\necho \"\"\r\necho \"|-------------------------------------------------------------------------------------------------|\"\r\necho \"| Download the Google Authenticator app on your smartphone and scan the following QR code: |\"\r\necho \"|-------------------------------------------------------------------------------------------------|\"\r\necho \"\"\r\ngoogle-authenticator -t -d -f -r 3 -R 30 -W\r\nfi<\/pre>\n<p>To have this script placed in the home directory of every new user upon creation copy it to the following directory:<\/p>\n<pre>sudo cp .bash_login \/etc\/skel\/.bash_login<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>This setup will require the Google Authenticator app to create the OTP (One-Time-Password). Android [Link] [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4,6],"tags":[],"class_list":["post-2379","post","type-post","status-publish","format-standard","hentry","category-linux","category-raspberry-pi"],"_links":{"self":[{"href":"https:\/\/dft.wiki\/index.php?rest_route=\/wp\/v2\/posts\/2379","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dft.wiki\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dft.wiki\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dft.wiki\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/dft.wiki\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2379"}],"version-history":[{"count":7,"href":"https:\/\/dft.wiki\/index.php?rest_route=\/wp\/v2\/posts\/2379\/revisions"}],"predecessor-version":[{"id":5480,"href":"https:\/\/dft.wiki\/index.php?rest_route=\/wp\/v2\/posts\/2379\/revisions\/5480"}],"wp:attachment":[{"href":"https:\/\/dft.wiki\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2379"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dft.wiki\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2379"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dft.wiki\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2379"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}