HOWTO: OpenSSH with MFA on OpenWrt 19.07.x using Google Authenticator

I've just spent a few hours trying to establish two-factor authentication for OpenSSH on my OpenWrt x86 router (v19.07.06). It finally works, but it's been a bit bumpy road, worth documenting for the future reference.

Some helpful prior-art resources I've come across:

These are the steps I've gone through to make it work. Say, the router IP is the default 192.168.1.1:

  1. Move the OpenWrt's built-in Dropbear SSH to LAN only and away from port 22 (e.g., 20022). This can be done in Luci at http://192.168.1.1/cgi-bin/luci/admin/system/admin/dropbear. If anything goes wrong with OpenSSH, you still should be able to log in from your local network using Dropbear:

    Luci Dropbear

  2. Log into the router using Dropbear and install the openssh-server-pam and google-authenticator-libpam packages:

    ssh -p 20022 root@192.168.1.1
    opkg update
    opkg install google-authenticator-libpam openssh-server-pam
    
  3. Set up OpenSSH public/private key authentication. This is no different from a typical non-MFA scenario, I've just followed this excellent guide.

  4. Restart the OpenSSH service (service sshd restart from the stil-open Dropbear session of step 2) and test that you can connect to OpenSSH as root on port 22, from some other host:

    ssh root@192.168.1.1
    
  5. Run google-authenticator (in the open Dropbear session) and enroll in MFA. Follow this guide precisely, starting from "Run the initialization app" and stopping at "Step 2 — Configuring OpenSSH".

    You can use Google Authenticator, Microsoft Authenticator or any other MFA app that implements the standard Time-based One-time Password Algorithm (TOTP - RFC 6238).

  6. Edit /etc/ssh/sshd_config (with nano /etc/ssh/sshd_config) to make these scattered changes:

    • PermitRootLogin yes
    • PubkeyAuthentication yes
    • ChallengeResponseAuthentication yes
    • UsePAM yes
    • AuthenticationMethods publickey,keyboard-interactive

    Here is a working version of /etc/ssh/sshd_config:

    #       $OpenBSD: sshd_config,v 1.103 2018/04/09 20:41:22 tj Exp $
    
    # This is the sshd server system-wide configuration file.  See
    # sshd_config(5) for more information.
    
    # This sshd was compiled with PATH=/usr/bin:/bin:/usr/sbin:/sbin
    
    # The strategy used for options in the default sshd_config shipped with
    # OpenSSH is to specify options with their default value where
    # possible, but leave them commented.  Uncommented options override the
    # default value.
    
    Port 22
    #AddressFamily any
    #ListenAddress 0.0.0.0
    #ListenAddress ::
    
    HostKey /etc/ssh/ssh_host_rsa_key
    HostKey /etc/ssh/ssh_host_ecdsa_key
    HostKey /etc/ssh/ssh_host_ed25519_key
    
    # Ciphers and keying
    #RekeyLimit default none
    
    # Logging
    #SyslogFacility AUTH
    #LogLevel INFO
    
    # Authentication:
    
    #LoginGraceTime 2m
    PermitRootLogin yes
    #StrictModes yes
    #MaxAuthTries 6
    #MaxSessions 10
    
    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
    
    #AuthorizedPrincipalsFile none
    
    #AuthorizedKeysCommand none
    #AuthorizedKeysCommandUser nobody
    
    # For this to work you will also need host keys in /etc/ssh/ssh_known_hosts
    #HostbasedAuthentication no
    # Change to yes if you don't trust ~/.ssh/known_hosts for
    # HostbasedAuthentication
    #IgnoreUserKnownHosts no
    # Don't read the user's ~/.rhosts and ~/.shosts files
    #IgnoreRhosts yes
    
    # To disable tunneled clear text passwords, change to no here!
    #PasswordAuthentication yes
    #PermitEmptyPasswords no
    
    # Change to no to disable s/key passwords
    ChallengeResponseAuthentication yes
    
    # Kerberos options
    #KerberosAuthentication no
    #KerberosOrLocalPasswd yes
    #KerberosTicketCleanup yes
    #KerberosGetAFSToken no
    
    # GSSAPI options
    #GSSAPIAuthentication no
    #GSSAPICleanupCredentials yes
    
    # Set this to 'yes' to enable PAM authentication, account processing,
    # and session processing. If this is enabled, PAM authentication will
    # be allowed through the ChallengeResponseAuthentication and
    # PasswordAuthentication.  Depending on your PAM configuration,
    # PAM authentication via ChallengeResponseAuthentication may bypass
    # the setting of "PermitRootLogin without-password".
    # If you just want the PAM account and session checks to run without
    # PAM authentication, then enable this but set PasswordAuthentication
    # and ChallengeResponseAuthentication to 'no'.
    UsePAM yes
    
    AuthenticationMethods publickey,keyboard-interactive
    
    #AllowAgentForwarding yes
    #AllowTcpForwarding yes
    #GatewayPorts no
    #X11Forwarding no
    #X11DisplayOffset 10
    #X11UseLocalhost yes
    #PermitTTY yes
    #PrintMotd yes
    #PrintLastLog yes
    #TCPKeepAlive yes
    #PermitUserEnvironment no
    #Compression delayed
    #ClientAliveInterval 0
    #ClientAliveCountMax 3
    #UseDNS no
    #PidFile /var/run/sshd.pid
    #MaxStartups 10:30:100
    #PermitTunnel no
    #ChrootDirectory none
    #VersionAddendum none
    
    # no default banner path
    #Banner none
    
    # override default of no subsystems
    Subsystem       sftp    /usr/lib/sftp-server
    
    # Example of overriding settings on a per-user basis
    #Match User anoncvs
    #       X11Forwarding no
    #       AllowTcpForwarding no
    #       PermitTTY no
    #       ForceCommand cvs server
    
  7. Edit /etc/pam.d/sshd (with nano /etc/pam.d/sshd) to make these changes:

    • #auth include common-auth (must be commented out)
    • auth required /usr/lib/security/pam_google_authenticator.so (append at the very end of the file)

    This is where I had a major roadblock. All the guides I've linked above suggested using "auth required pam_google_authenticator.so". However, at least in OpenWrt 19.07, pam.d tries to load this plugin from /lib/security and that fails, because the current google-authenticator-libpam package installs pam_google_authenticator.so into /usr/lib/security. Once I figured that out, the MFA authentication started working.

    Here is a working version of /etc/pam.d/sshd:

    # PAM configuration for the Secure Shell service
    
    # Read environment variables from /etc/environment and
    # /etc/security/pam_env.conf.
    auth       required     pam_env.so
    
    # Skip Google Authenticator if logging in from the local network.
    # auth [success=1 default=ignore] pam_access.so accessfile=/etc/security/access-sshd-local.conf
    # Google Authenticator 2-step verification.
    #auth       requisite    pam_google_authenticator.so
    
    # Standard Un*x authentication.
    #auth       include      common-auth
    
    # Disallow non-root logins when /etc/nologin exists.
    account    required     pam_nologin.so
    
    # Uncomment and edit /etc/security/access.conf if you need to set complex
    # access limits that are hard to express in sshd_config.
    # account    required     pam_access.so
    
    # Standard Un*x authorization.
    account    include      common-account
    
    # Standard Un*x session setup and teardown.
    session    include      common-session
    
    # Print the message of the day upon successful login.
    session    optional     pam_motd.so
    
    # Print the status of the user's mailbox upon successful login.
    session    optional     pam_mail.so standard noenv
    
    # Set up user limits from /etc/security/limits.conf.
    session    required     pam_limits.so
    
    # Set up SELinux capabilities (need modified pam)
    # session    required     pam_selinux.so multiple
    
    # Standard Un*x password updating.
    password   include      common-password
    
    auth       required     /usr/lib/security/pam_google_authenticator.so
    
  8. Now restart sshd (service sshd restart) and try to connect to it from another machine. You should be prompted for a one-time MFA password.

  9. Configure your Firewall trafic rules (http://192.168.1.1/cgi-bin/luci/admin/network/firewall/rules) if you want to be able to connect to OpenSSH from the Internet.

Congrats, you've hardened your OpenWrt OpenSSH root access with two-factor authentication. A few final points:

  • The OTP codes are time-based. My x86 router has an RTC clock, so the MFA should work even if the router is offline.
  • OpenWrt automatically syncs time using NTP, so as long as the router is online, the MFA still should work.
  • Otherwise, if the router is offline and there's no RTC, you should still have an option to connect from the LAN using Dropbear on port 20022.

Oh, and if you have a spare fanless Mini PC and thinking of turning it into a OpenWrt router, you may want to check out my other post:

9 Likes

Good stuff, you might find https://github.com/Strykar/openwrt_packages interesting.

1 Like

Thanks for writing this up and sharing your experiences with others. I have to ask tho, what kind of threats are you facing on your local network (which couldn't be mitigated by the use of private keys and/or limiting the access to ssh for select IPs/ranges) so that you have to set up MFA?

3 Likes

I have to ask tho, what kind of threats are you facing on your local network

@stangri, this is not for connecting from my local network, IIUIC. I've opened OpenSSH on port 22 on my WAN interface, so I can connect to my home network when I'm on the go. I mostly need it for RDC'ing into my desktop PC, and I use SSH tunnels for that (here's a great read). It works well and I prefer that to a full-blown VPN. I also have an option to switch it to port 443 and do SSH tunneling for when I'm on some locked-down WiFi networks where only HTTP/HTTPS traffic is allowed.

As to accessing it from the local network only, I think the bult-in Dropbear SSH with key-based authentication is good enough.

@bobafetthotmail What do you think about adding this to the wiki?

1 Like

Added https://openwrt.org/docs/guide-user/services/ssh/ssh.mfa.auth

@Strykar thanks for taking the time to share your experience!

2 Likes

@bobafetthotmail, thanks for converting my how-to into the Wiki! Would I be able to edit it there directly if I needed to amend anything? I'm a nitpicker :slight_smile:

It's a wiki, anyone with an account can edit most pages.

yes you can register to the wiki and edit that page (or most others too).

EDIT: Due to spambot activity, registration process is now "send a PM to tmomas with your nickname and email" and he will create the account manually.

1 Like