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

  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 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@
    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@
  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 ::
    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/
    #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/ (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". 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 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
    # Skip Google Authenticator if logging in from the local network.
    # auth [success=1 default=ignore] accessfile=/etc/security/access-sshd-local.conf
    # Google Authenticator 2-step verification.
    #auth       requisite
    # Standard Un*x authentication.
    #auth       include      common-auth
    # Disallow non-root logins when /etc/nologin exists.
    account    required
    # 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
    # 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
    # Print the status of the user's mailbox upon successful login.
    session    optional standard noenv
    # Set up user limits from /etc/security/limits.conf.
    session    required
    # Set up SELinux capabilities (need modified pam)
    # session    required multiple
    # Standard Un*x password updating.
    password   include      common-password
    auth       required     /usr/lib/security/
  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 ( 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:


Good stuff, you might find 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?


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


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


@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