Two Factor Authentication (2FA)

From Mage
Revision as of 09:00, 24 June 2019 by Bret (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Setting an Arch server for two-factor authentication is pretty straightforward.



  • OpenSSH is already installed and running.
  • Your server's IP address is - change this to your IP
  • You want to connect on port 454647 - change the port number to something high to avoid the lazier scans, between 30,000-65,535
  • Your username is foo - probably change this from the example

Install Packages

pacman -S libpam-google-authenticator

package details - libpam-google-authenticator

Edit config files

vi /etc/pam.d/sshd

# Disable remote root
auth      required

# Require two-factor authentication
auth      required

# But don't allow password attempts
#auth      include   system-remote-login

account   include   system-remote-login
password  include   system-remote-login
session   include   system-remote-login

# Log authenticator fails in pam_tally2
# Lock for 5m (300s) after 3 attempt fails
auth      required deny=3 unlock_time=300 onerr=succeed
account   required

vi /etc/ssh/sshd_config

#  --- Hardened sshd config w/comments ---
# See sshd_config(5) for more information.

# Change from the default port value (22)
Port 454647

# Manually designate the addresses to listen on

# Disable SSH version 1
Protocol 2

# Limit HostKeys for protocol version 2 in order of preference
HostKey /etc/ssh/ssh_host_ed25519_key
HostKey /etc/ssh/ssh_host_rsa_key

# Limit key exchange algorithms to more secure versions

# Limit our cipher suite to more secure versions

# Limit our message authentication codes to more secure versions

# Logging

PermitRootLogin no

# Strict adherence to file permissions (~/.ssh/)
StrictModes yes

# 30s to login (2FA timeout), 3 tries to login, only 1 unauth connection at a time
LoginGraceTime 30
MaxAuthTries 3
MaxStartups 1

# Specifies the maximum number of open shell, login or subsystem (e.g. sftp) sessions permitted per network connection.
MaxSessions 3

# Specifies whether public key authentication is allowed.
PubkeyAuthentication yes

# The default is to check both .ssh/authorized_keys and .ssh/authorized_keys2
# but this is overridden so installations will only check .ssh/authorized_keys
AuthorizedKeysFile      .ssh/authorized_keys

# Disable host-based authentication, like .rhost it's not rock solid.
HostbasedAuthentication no

# Don't read the user's ~/.rhosts and ~/.shosts files
IgnoreRhosts yes

# Disable password authentication
PasswordAuthentication no
PermitEmptyPasswords no

# Allow keyboard-interactive so we can ask for our 2FA
ChallengeResponseAuthentication yes

# Order matters for AuthenticationMethods. Ask for the pubkey first, then go interactive.
# The comma is important as well, as it asks for pubkey AND 2FA. A space would be either/or.
AuthenticationMethods publickey,keyboard-interactive:pam

# Allow pam use so the 2FA library can be loaded
UsePAM yes

# Do not allow SSH agent key management
AllowAgentForwarding no

# Disable SSH tunnels
PermitTunnel no

# Do not allow TCP port forwarding by default
AllowTcpForwarding no

# Do not forward buggy X11
X11Forwarding no

# Disable SSH binding local port forwardings to the loopback address
GatewayPorts no

# Specifies whether pty(4) allocation is permitted. Default yes
PermitTTY yes

# Specifies whether sshd should print /etc/motd when a user logs in interactively.
PrintMotd no   # set it in pam

# Print the date and time of last user login
PrintLastLog yes

# Specifies whether the system should send TCP keepalive messages to the other side.
# Default is yes, lets host notice network outage/infinite hangs.
TCPKeepAlive yes

# Specifies whether ~/.ssh/environment and environment= options 
# in ~/.ssh/authorized_keys are processed by sshd. Default = no
PermitUserEnvironment no

# enable compression only after authentication
Compression delayed

# idle timeout interval
ClientAliveInterval 300
ClientAliveCountMax 0

# Force sshd to use DNS
UseDNS yes

# Warning banner path
Banner /etc/

# Log sftp level file access (read/write/etc.) that would not be easily logged otherwise.
Subsystem       sftp    /usr/lib/ssh/sftp-server -f AUTHPRIV -l INFO

# Setup only one user able to connect
AllowUsers foo
# Allow only that user to do tunneling
Match User foo
AllowTcpForwarding yes
PermitTunnel yes

Generate secret key

Adding the key into FreeOTP

For each user you want to connect via 2FA, run the google-authenticator selecting a time-based key that disallows code reuse.

┌(foo@server)─(05:32 AM Sat Jun 16)─(~)
└> google-authenticator -t -d
Warning: pasting the following URL into your browser exposes the OTP secret to Google:
Failed to use libqrencode to show QR code visually for scanning.
Consider typing the OTP secret into your app manually.
Your new secret key is: LJ6V5LANKTMP527CRIDPXJLKHQ
Enter code from app (-1 to skip): -1
Code confirmation skipped
Your verification code is 723310
Your emergency scratch codes are:

Do you want me to update your "/home/foo/.google_authenticator" file? (y/n) y

By default, a new token is generated every 30 seconds by the mobile app.
In order to compensate for possible time-skew between the client and the server,
we allow an extra token before and after the current time. This allows for a
time skew of up to 30 seconds between authentication server and client. If you
experience problems with poor time synchronization, you can increase the window
from its default size of 3 permitted codes (one previous code, the current
code, the next code) to 17 permitted codes (the 8 previous codes, the current
code, and the 8 next codes). This will permit for a time skew of up to 4 minutes
between client and server.
Do you want to do so? (y/n) n

If the computer that you are logging into isn't hardened against brute-force
login attempts, you can enable rate-limiting for the authentication module.
By default, this limits attackers to no more than 3 login attempts every 30s.
Do you want to enable rate-limiting? (y/n) y


Copy the generated secret key listed and save a scratch key if you really want to. Just log in locally, lazy. Install FreeOTP on the device of your choice (thanks Red Hat!) and accept the default options while adding a key.

Restart the service

sudo systemctl restart sshd

Connecting your clients


┌(foo@bar)─(05:33 AM Sun Jun 17)─(~)
└> ssh -p 454647 foo@

This is your warning. Remember, I warned you.

Verification code: 
Last login: Sun Jun 17 05:40:48 2018 from bar.localdomain.local

┌(foo@server)─(05:33 AM Sun Jun 17)─(~)


Connect via PuTTY as you normally would. You'll be prompted for a verification code once the public key is verified:

Putty tfa.png


Locked Accounts

  • Make sure the account isn't locked on the server
┌(foo@server)─(05:37 AM Sun Jun 17)─(~)
└> sudo pam_tally2 -u foo
Login           Failures Latest failure     From
foo                 3    06/17/18 05:59:36  bar.localdomain.local

If it is, reset the account's tally

┌(foo@server)─(05:37 AM Sun Jun 17)─(~)
└> sudo pam_tally2 -u foo --reset
Login           Failures Latest failure     From
foo                 3    06/17/18 05:59:36  bar.localdomain.local

┌(foo@server)─(05:37 AM Sun Jun 17)─(~)
└> sudo pam_tally2 -u foo
Login           Failures Latest failure     From
foo                 0