SFTP two-factor authentication: keyboard-interactive explained
SFTP two-factor authentication almost always works through an SSH mechanism called keyboard-interactive. The server sends one or more prompts, such as "Verification code:", and the client shows them to you and sends back your answers. Combined with a required public key, that gives you a genuine second factor: something you have (the key) and a time-based code from your phone. This post explains how keyboard-interactive works, how to set up OpenSSH to require a key and a code, and what to configure in an SFTP client.
How keyboard-interactive works
SSH supports several authentication methods. The familiar ones are password and publickey. Keyboard-interactive, defined in RFC 4256, is a generic question-and-answer exchange: the server sends a set of prompts, each marked as to whether the answer should be echoed on screen, and the client replies with the answers. The server can go round as many times as it likes.
Because the prompts are arbitrary text, the server can ask for anything. On Linux, OpenSSH hands keyboard-interactive to PAM, so whatever PAM modules are configured decide the questions. Add a TOTP module and one of the prompts becomes a six-digit code. TOTP itself is specified in RFC 6238: the code is derived from a shared secret and the current time, and changes every 30 seconds.
Requiring two methods with AuthenticationMethods
By default, any single successful method logs you in. OpenSSH's AuthenticationMethods directive changes that by listing methods that must all succeed, in order. To require a key and then a code:
AuthenticationMethods publickey,keyboard-interactive
The client offers its key first. The server accepts it as a partial success and says which methods can continue, and the client then completes keyboard-interactive. You can watch this by connecting with verbose output, ssh -v deploy@example.com, and reading the debug lines:
debug1: Authentications that can continue: publickey
debug1: Offering public key: /Users/you/.ssh/id_ed25519 ED25519 ...
debug1: Authentications that can continue: keyboard-interactive
The second "can continue" line, listing only keyboard-interactive, is the server saying the key was accepted and a code is still needed. The sshd_config manual page documents the directive in full.
Setting up key plus TOTP on Ubuntu or Debian
This is the common setup using the Google Authenticator PAM module, which works with any TOTP app. Keep a second SSH session open while you do it, so a mistake can't lock you out.
- Install the module:
sudo apt install libpam-google-authenticator - As the user who will log in, run
google-authenticator, answer yes to time-based tokens, and scan the QR code with your authenticator app. Save the emergency scratch codes somewhere safe. It also asks whether to disallow reuse of the same code; saying yes is more secure, and it matters for clients that open several connections, as explained below. - Add the module to
/etc/pam.d/sshd:
For key plus code without a password as well, comment out theauth required pam_google_authenticator.so@include common-authline in the same file. Otherwise PAM asks for the account password too. - In
/etc/ssh/sshd_config(or a file in/etc/ssh/sshd_config.d/), set:
Older OpenSSH releases call the first directiveKbdInteractiveAuthentication yes UsePAM yes AuthenticationMethods publickey,keyboard-interactiveChallengeResponseAuthentication. - Test the configuration, then restart:
sudo sshd -t && sudo systemctl restart ssh
Open a fresh connection from another terminal before closing the old one. You should be asked for a verification code after the key is accepted. If you need to roll this out gradually, adding nullok to the PAM line lets users who haven't set up a code yet log in without one; remove it once everyone is enrolled.
SFTP uses the same SSH authentication, so the same rules apply to sftp sessions and to chrooted SFTP-only users.
Password plus code instead of a key
If you keep @include common-auth and set AuthenticationMethods keyboard-interactive, PAM asks two questions over keyboard-interactive: the account password, then the code. It's still two factors, but a password is weaker than a key and can be phished along with the code. Prefer a key where you can.
Configuring two-factor SFTP in Wharf
Wharf supports both shapes. In the site editor, the authentication Method picker offers:
- SSH key + 2FA: for
publickey,keyboard-interactive. Wharf offers the key first, then answers the server's challenge. Import the key with Import Key…; it's copied into your Keychain once and used from there. - Interactive / 2FA: challenge-response only, for servers that ask everything through keyboard-interactive, including the password-then-code setup above.
When the server sends a prompt, Wharf shows its text exactly as the server wrote it, with the site's name as the heading, and hides what you type when the server asks for no echo. If the server asks two questions, you'll see two prompts in turn. Codes are answered at connect time and never saved.
A few practical points:
- Each connection authenticates separately. Wharf opens several connections to a site for parallel transfers, up to the Maximum connections value in the site editor's Advanced section (three by default). With 2FA, each new connection asks for a code. If your server disallows reusing a code, two connections in the same 30 seconds need two different codes. For a 2FA site, setting Maximum connections to 1 means one code per session.
- A dropped connection needs a new code. Reconnecting is a fresh login.
- Jump hosts can ask too. If a site is reached through a jump host that also requires 2FA, Wharf prompts for the bastion's code as well as the target's.
- Automations can't answer prompts. Wharf's Shortcuts actions refuse sites that need a two-factor code, with an error saying so, rather than hanging while waiting for an answer nobody can give. See the Shortcuts guide for what automations can do.
For the key part of the setup, the SSH key guide covers generating and importing a key on a Mac, and the support page explains why Wharf imports keys into the Keychain rather than reading ~/.ssh. The protocols Wharf supports are listed on the features page.
Troubleshooting
- No code prompt, and the login fails after the key. Check that
KbdInteractiveAuthenticationisyesand that no later file insshd_config.dsets it back tono.sudo sshd -T | grep -i kbdshows the effective value. - Asked for a password you didn't expect.
@include common-authis still active in/etc/pam.d/sshd. - Codes always rejected. TOTP depends on the clock. Check the server's time with
timedatectl, and make sure NTP is on. - Works in the terminal, not in a client. Make sure the client is set to use the key and keyboard-interactive, not just the key.