Enable 2FA for TrueNAS Core
The 2FA in TrueNAS Core uses pam_oath.so
module, supports Two-factor time based (TOTP) SSH authentication.
Setup
Enable 2FA
-
Go to Credentials => 2FA
-
Click on Enable Two-Factor Authentication
-
Click on Show QR, use Authy to record it. This is token for root account.
-
Save
Test GUI
Use another browser login with user name, password, and pin code (Authy generated).
Make sure it is working.
Enable SSH
-
Go to Credentials => 2FA
-
Select Enable Two-Factor Auth for SSH
-
Save
Enable root login
-
Go to System Settings => Services
-
Select Configure button, which is a pencil icon
-
Check Log in as Root with Password
-
Save
Test root login with 2FA
Use terminal
$ ssh host.example.com
Password:
One-time password (OATH) for 'root':
Linux truenas.bx.net 5.10.70+truenas #1 SMP Wed Nov 3 18:30:34 UTC 2021 x86_64
Test root login successful.
Disable root login
-
Go to System Settings => Services
-
Select Configure button, which is a pencil icon
-
Uncheck Log in as Root with Password
-
Save
Setup for normal user
After enable 2FA, normal user can not login, got error in /var/log/auth.log
as below:
error: PAM: User not known to the underlying authentication module for ...
Use these steps to enable 2FA for user.
Note: If you lost SSH connection, the root shell can be accessed from GUI, System Settings => Shell
Generate a random code
# head -10 /dev/urandom | md5sum | cut -b 1-30
15ad027b56c81672214f4659ffb432
Get oath configuration file name
The usersfile name can be found using following command:
# grep oath /etc/pam.d/sshd
auth required pam_oath.so usersfile=/etc/users.oath window=0
Update /etc/users.oath
Setup the oath seed in /etc/users.oath
:
HOTP/T30/6 user - 15ad027b56c81672214f4659ffb432
Install oathtool
Use another linux server, such as ubuntu server:
ubuntu# apt install oathtool
I chose another server, because TrueNAS server is not fully customized debian server, better don't change it structure and packages.
Test pin code for SSH
Open another terminal, and run following command, and run second command in the linux server when prompting OATH code.
$ ssh host.example.com
Password:
One-time password (OATH) for 'user':
Now, quickly run following command,
ubuntu# oathtool --totp -v 15ad027b56c81672214f4659ffb432
960776
Input OATH code in SSH login terminal. The code should be accepted.
Get Base32 secret
In the previous ubuntu server, install qrencode
package
ubuntu# apt install qrencode
Run following command to collect Base32 secret:
ubuntu# oathtool --totp -v 15ad027b56c81672214f4659ffb432
Hex secret: 15ad027b56c81672214f4659ffb432
Base32 secret: CWWQE62WZALHEIKPIZM77NBS
...
329770
Generate QR code
qrencode -t ansiutf8 "otpauth://totp/user@host.example.com?secret=CWWQE62WZALHEIKPIZM77NBS"
Save into Authy
Use Authy scan QR code, then type in TrueNAS in textbox to search icon, then save it.
Persistent change
As TrueNAS is a fully customized OS, it has startup process to regenerate /etc/users.oath
file, results only root id stays.
In order to overcome this issue, create a startup command in System Settings => Advanced => Init/Shutdown Scripts, add following command:
Name: Append oath codes
When: POSTINIT
Command:
echo "HOTP/T30/6\t<user_name>\t-\t<user_code>" >> /etc/users.oath
Note: There are many ways to archive this, such as backup users.oath files you created, and restore it. I just chose the most easy and maintenance free way.
TrueNAS GUI
I could not find any place to setup in TrueNAS GUI for user, and the user id I created in TrueNAS can not login to GUI at all. In fact, TrueNAS doesn't support normal user login to GUI.
Possible enhancements
There are the limitations of pam_oath.o
implemenation
Only one usersfile
Only one usersfile can be specified in pam_oath.o
, there are some suggestions, such as:
- Enhance source code to allow
pam_oath.o
accepts%h
as usersfile parameter's value to point to user's home directory.
Missing entry allowed
If the user is not in usersfile, then they can not login, this makes administrator very busy.
I like one example implementation as below:
WARNING: I didn't test the following codes which downloaed from Two-Factor Authentication with OTP (Admin Guide), just for reference.
- Create a group called
otpusers
, the users are not in this group do not require 2FA. This implemented in PAM
auth [success=2 default=ignore] pam_succeed_if.so uid = 0 # skip 2 lines for root
auth [success=1 default=ignore] pam_succeed_if.so user notingroup otpusers # ignore users not yet in otpusers
auth requisite pam_oath.so usersfile=/var/security/auth/users.oath window=20 # accept one of 20 consecutive keys
(in case clocks of user and server are out of sync)
```
* Create profile script to check whether the user is in `otpusers` group, if not, create oath code and allow user save it.
*WARNING: The below script that I copied from Internet got syntax error, and I didn't test it as well.*
`/etc/profile.d/create_secret.sh`:
/bin/bash
RRZK, 2015-12-10 (CO)
OATH_FILE="/var/security/auth/users.oath"
OTPGROUP="otpusers"
ME=$(/usr/bin/whoami)
ME=${PAM_USER}
HOST=${HOSTNAME}
RET=0
/usr/bin/id -Gn ${ME}|/bin/grep ${OTPGROUP} >/dev/null 2>&1
RET=$?
if [ ! ${ME} = "root" ] && [ ${RET} -ne 0 ]; then
Disable CTRL-C
trap '' 2
/bin/echo -e "
Hello ${ME}
I will generate a TOTP (time based) OATH Secret for you...
"
generate secret
/bin/echo "... generating secret"
SECRET=$(/usr/bin/head -10 /dev/urandom | /usr/bin/sha512sum | /bin/cut -b 19-50)
generate base32 secret
/bin/echo "... generating base32 secret"
BASE32=$(/usr/bin/oathtool --totp -v ${SECRET}|/bin/grep 'Base32'|/bin/awk '{print $NF}')
generate qrcode
/bin/echo "... generating qrcode"
/usr/bin/qrencode -l H -v 1 --background=FFFFFF -o ${ME}_oath.png "otpauth://totp/${ME}@${HOST}?secret=${BASE32}"
insert secret in oath database
/bin/echo "... adding secret to oath database"
/bin/echo "... adding user to otpuser group"
TMPFILE=$(/bin/mktemp ) || exit 1
/bin/echo -e "HOTP/T30/6\t${ME}\t-\t${SECRET}" > $TMPFILE
/usr/bin/sudo -u root /usr/local/sbin/add_secret.sh ${TMPFILE} ${OTPGROUP} ${ME}
/bin/rm -f TMPFILE
/bin/echo "... finished"
echo "Secret: ${SECRET}
BASE32 Secret:${BASE32}" > ${ME}_oath.dat
/bin/echo "
Your Secret is: ${SECRET}
Your BASE32 Secret is ${BASE32}
Your QR-Code is: ${ME}_oath.png
Enter your secret in your OTP Token (enter BASE32 without the trailing '=')
or
Display this file and scan it with your OTP Token APP. (X11Forward only)
"
/bin/echo "To display your QR-Code, press
read INPUT
if [ "$INPUT" = "d" ]; then
/usr/bin/display ${ME}_oath.png
fi
logout
fi
* Then add oath code into *usersfile*.
*WARNING: The below script that I copied from Internet got syntax error, and I didn't test it as well.*
`/usr/local/sbin/add_secret.sh`:
/bin/bash
RRZK, 2015-12-10 (CO)
OATH_FILE=/var/security/auth/users.oath
TMPFILE=$1
OTPGROUP=$2
USER=$3
/bin/cat ${TMPFILE} >> ${OATH_FILE}
/usr/sbin/usermod -a -G ${OTPGROUP} ${USER}
exit 0
## References
[pam_oath](https://wiki.archlinux.org/title/Pam_oath)
[Two-factor time based (TOTP) SSH authentication with pam_oath and Google Authenticator](https://spod.cx/blog/two-factor-ssh-auth-with-pam_oath-google-authenticator.shtml)
[How to Create QR Codes From the Linux Command Line](https://www.cloudsavvyit.com/8382/how-to-create-qr-codes-from-the-linux-command-line/)
[How to generate a QR Code for Google Authenticator that correctly shows Issuer displayed above the OTP?](https://stackoverflow.com/questions/34520928/how-to-generate-a-qr-code-for-google-authenticator-that-correctly-shows-issuer-d)
[Enable user to login to webui](https://www.truenas.com/community/threads/unable-to-login-to-gui-with-non-admin-root-user.19921/)
[Two-Factor Authentication with OTP (Admin Guide)](https://hpc-wiki.info/hpc/Admin_Guide_Two-Factor_Authentication_with_OTP)
[sshd: How to enable PAM authentication for specific users under](https://serverfault.com/questions/222637/sshd-how-to-enable-pam-authentication-for-specific-users-under)