{"id":2494,"date":"2021-10-23T00:50:01","date_gmt":"2021-10-23T00:50:01","guid":{"rendered":"https:\/\/dft.wiki\/?p=2494"},"modified":"2026-04-21T13:32:34","modified_gmt":"2026-04-21T17:32:34","slug":"2fa-with-yubikey-on-ubuntu-20-04","status":"publish","type":"post","link":"https:\/\/dft.wiki\/?p=2494","title":{"rendered":"2FA with Yubikey on Ubuntu"},"content":{"rendered":"<p>In this post, I will cover the usages of a Yubikey for:<\/p>\n<ul>\n<li>Protect local sudo command,<\/li>\n<li>Protect local gnome login screen,<\/li>\n<li>Protect local tty screen,<\/li>\n<li>Protect remote ssh connections.<\/li>\n<\/ul>\n<hr \/>\n<p><strong>PROTECTING LOCAL SUDO<\/strong><\/p>\n<pre>sudo apt update &amp;&amp; sudo apt upgrade -y\r\nsudo apt install libpam-u2f -y\r\nmkdir -p ~\/.config\/Yubico\r\npamu2fcfg &gt; ~\/.config\/Yubico\/u2f_keys\r\nsudo nano \/etc\/pam.d\/sudo<\/pre>\n<p>Add the line in bold after the mentioned line:<\/p>\n<pre>@include common-auth\r\n<strong>auth required pam_u2f.so<\/strong><\/pre>\n<p>It is complete. Try to use the <strong>sudo<\/strong> command with and without the Yubikey connected.<\/p>\n<p>The Yubikey will flash asking to touch its button to allow the command to go through.<\/p>\n<hr \/>\n<p><strong>PROTECTING LOCAL GNOME LOGIN<\/strong><\/p>\n<pre>sudo nano \/etc\/pam.d\/gdm-password<\/pre>\n<p>Add the line in bold after the mentioned line:<\/p>\n<pre>@include common-auth\r\n<strong>auth required pam_u2f.so<\/strong><\/pre>\n<hr \/>\n<p><strong>PROTECTING LOCAL TTY SCREEN<\/strong><\/p>\n<pre>sudo nano \/etc\/pam.d\/login<\/pre>\n<p>Add the line in bold after the mentioned line:<\/p>\n<pre>@include common-auth\r\n<strong>auth required pam_u2f.so<\/strong><\/pre>\n<hr \/>\n<p><strong>PROTECTING REMOTE SSH<\/strong><\/p>\n<pre>sudo apt update &amp;&amp; sudo apt upgrade -y\r\nsudo add-apt-repository ppa:yubico\/stable\r\nsudo apt install libpam-yubico -y\r\nsudo nano \/etc\/ssh\/yubikeys<\/pre>\n<p>Add one line for each user followed by the first 12 characters of its own Yubikey:<\/p>\n<pre>usera:f8v6d6f687d6\r\nuserb:df98b67d7b68:adf1n98b7kf5\r\nuserc:edf8n98b7nf6<\/pre>\n<p>Create API credentials at the Yubikey website.\u00a0Go to <a href=\"https:\/\/upgrade.yubico.com\/getapikey\" target=\"_blank\" rel=\"noopener\">https:\/\/upgrade.yubico.com\/getapikey<\/a>.<\/p>\n<p>Edit the PAM configuration file for the SSH server:<\/p>\n<pre>sudo nano \/etc\/pam.d\/sshd<\/pre>\n<p>Add the following line at the top of the document replacing the <span style=\"color: #ff0000;\"><strong>ID<\/strong><\/span> and <span style=\"color: #ff0000;\"><strong>KEY<\/strong><\/span> from the API:<\/p>\n<pre>auth required pam_yubico.so id=<span style=\"color: #ff0000;\"><strong>ID<\/strong><\/span> key=<span style=\"color: #ff0000;\"><strong>KEY<\/strong><\/span> mode=client debug authfile=\/etc\/ssh\/yubikeys<\/pre>\n<p>The most popular <strong>keyboard-interactive<\/strong> options are:<\/p>\n<ul>\n<li><strong>required<\/strong> (OTP and Password have to be met),<\/li>\n<li><strong>sufficient <\/strong>(OTP or Password have to be met).<\/li>\n<\/ul>\n<p>Then edit the file:<\/p>\n<pre>sudo nano \/etc\/ssh\/sshd_config<\/pre>\n<p>And make sure both of the following configurations are enabled:<\/p>\n<pre>ChallengeResponseAuthentication yes\r\nUsePAM yes\r\nAuthenticationMethods publickey,keyboard-interactive<\/pre>\n<p>It requires both methods to be met:<\/p>\n<ul>\n<li><strong>publickey<\/strong> (SSH-Key),<\/li>\n<li>and <strong>keyboard-interactive<\/strong> (what comes out of the PAM).<\/li>\n<\/ul>\n<p>To allow one specific user to connections only with ssh-key append:<\/p>\n<pre>Match User userd\r\n    AuthenticationMethods publickey<\/pre>\n<p>Restart the service:<\/p>\n<pre>sudo systemctl restart ssh<\/pre>\n<hr \/>\n<p><strong>PROTECTING REMOTE SUDO<\/strong><\/p>\n<pre>sudo nano \/etc\/pam.d\/sudo<\/pre>\n<p>Add the following line at the top of the document replacing the <span style=\"color: #ff0000;\"><strong>ID<\/strong><\/span> and <span style=\"color: #ff0000;\"><strong>KEY<\/strong><\/span> from the API:<\/p>\n<pre>auth required pam_yubico.so id=<span style=\"color: #ff0000;\"><strong>ID<\/strong><\/span> key=<span style=\"color: #ff0000;\"><strong>KEY<\/strong><\/span> mode=client authfile=\/etc\/ssh\/yubikeys<\/pre>\n<hr \/>\n<p><strong>PROTECTING REMOTE RASPBERRY PI&#8217;s SSH<\/strong><\/p>\n<p>Following the same steps to protect the SSH you may realize that the repo below does not exist for RPi:<\/p>\n<pre>sudo add-apt-repository ppa:yubico\/stable<\/pre>\n<p>But do not let it stop you. It will work the same way.<\/p>\n<hr \/>\n<p><strong>USING ANSIBLE WITH 2FA<\/strong><\/p>\n<p>There are many workaround solutions that simply create bypass conditions for the Ansible, such as allowing root from a certain address or network:<\/p>\n<pre>Match Address 10.1.1.0\/24,192.168.111.222\r\n  PermitRootLogin yes<\/pre>\n<p>Or to suppress the 2FA for a specific user that only Ansible uses:<\/p>\n<pre>Match User ansible_user\r\n  AuthenticationMethods publickey<\/pre>\n<p>But the best way for making the 2FA prompt to the Ansible that will enter the OTP is inserting the following configuration to the <strong>\/etc\/ansible\/ansible.cfg<\/strong>:<\/p>\n<pre>[ssh_connection]\r\nssh_args = -C -o ControlMaster=auto -o ControlPersist=60s -o PreferredAuthentications=publickey,keyboard-interactive<\/pre>\n<p>Note: Ansible executes the requests to many servers in parallel (to improve performance) for each module execution, so consider serializing the execution giving it an order to follow to prevent overlapping of the OTP prompts (if more than one server requires OTP).<\/p>\n<pre>  serial: 1\r\n  order: <strong>inventory<\/strong><\/pre>\n<p>The order can be: inventory, reverse_inventory, sorted, reverse_sorted, or shuffle<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this post, I will cover the usages of a Yubikey for: Protect local sudo [&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-2494","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\/2494","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=2494"}],"version-history":[{"count":13,"href":"https:\/\/dft.wiki\/index.php?rest_route=\/wp\/v2\/posts\/2494\/revisions"}],"predecessor-version":[{"id":5479,"href":"https:\/\/dft.wiki\/index.php?rest_route=\/wp\/v2\/posts\/2494\/revisions\/5479"}],"wp:attachment":[{"href":"https:\/\/dft.wiki\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2494"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dft.wiki\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2494"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dft.wiki\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2494"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}