Universal Second Factor U2F & FIDO: How can I secure my System/OS by using FIDO/U2F in order to do that ?
1. Motivation
This Tutorial will show you how you can activate FIDO/U2F in 60 seconds on your Ubuntu system.
FIDO/U2F is one of the most secure option which you can use in order to protect your system/OS and your privacy.
After applying the scripts you have to press the button on the Yubikey in order to prove a certain request, e.g. requesting a root shell or other administration commands.
This solution could be used to avoid an airport shutdown !
This solution requires
- A USB-Hardware-Key/Yubikey from Yubico (30€ to 50€)
- The OS Ubuntu 24.04 noble
- The files of the folder U2F_1, which contain the FIDO/U2F configuration
Why I have myself decided to go for a Yubikey? The answer is for security reasons. The key has no option to change the private key which is the Hardware-Key itself. From my point of view this is a big advantage compared to other USB-FIDO solutions, because it is not possible to manipulate the content of the key. All what you can do, is to define a few pins.
==> Secure by design ! (I am not working for Yubico)
2. Solution
We will review 2 scripts which will need to run in order to activate FIDO/U2F on our system.
Due to the fact that we are going to protect the system with a new Hardware, the Yubikey, we have to perform the necessary steps in a controlled way to make sure not to lockout ourself of the system ! It is a good approach to keep a root shell open as long as you have not proved that everything is working as expected, because than you have still the option to change what you want to change.
Script_1 ==> u2f_1__1_libs.sh
The script will assign and install the mandatory libraries for FIDO/U2F and the Yubikey.
Part_1 of Script_1 ==> Definition of some variables
{
#!/bin/bash
#
####################################################
# Version 1.1 #
# Date 2025-01-20 #
# Kay Schiller #
# Description: Configure FIDO/U2F for Ubuntu 24.04 #
# Part 1: Assign and load libs #
####################################################
#
FILE_3=u2f_keys
DIR_0="."
DIR_1=""
DIR_2=""
DIR_3="/home/$USER/.config/Yubico"
DIR_4="/etc/pam.d/U2F_0"
DIR_5="/etc/pam.d/U2F_1"
DIR_6="$DIR_0/u2f/U2F_0"
DIR_7="$DIR_0/u2f/U2F_1"
DIR_8="/etc/pam.d/"
DIR_9="/etc/Yubico"
USER_2=$USER
ERROR="0"
#
#------- Functions -------------------------------------------------
#
DFAULTCOLOR="\e[0m"
GREEN="\e[1;92m"
RED="\e[1;91m"
BLUE="\e[1;94m"
}
Part_2 of Script_1 ==> Loading the function STATUS_LAST_CMD
#
# Load Functions
#
. /home/$USER/Documents/USB/functions.sh
if [ $? = 0 ]
then
echo -e "${GREEN}Functios imported succesfully${DFAULTCOLOR}"
else
echo -e "${RED}ERROR: Functions were not imported, check path !${DFAULTCOLOR}"
exit
fi
functions.sh ==> STATUS_LAST_CMD
The script is using the function STATUS_LAST_CMD, which proves the Error-Level of a command.
Each command which is executed by a shell delivers the so called Error-Level. If the Error-Level=0 it means the previous command was executed sucessfully. In case the value is not equal to 0 it means an error occured during processing.
=> Using this simple function we can prove that the script was executed as we want to see it - successfully !
Part_3 of Script_1 ==> Plotting the content of some variables
echo "DIR_0=$DIR_0"
echo "DIR_1=$DIR_1"
echo "DIR_2=$DIR_2"
echo "DIR_3=$DIR_3"
echo "DIR_4=$DIR_4"
echo "DIR_5=$DIR_5"
echo "DIR_6=$DIR_6"
echo "DIR_7=$DIR_7"
echo "DIR_8=$DIR_8"
echo "DIR_9=$DIR_9"
Part_4 of Script_1 _==> Ask the user whether the system is connected to the internet
#
#ETHERNET = 0
#
#------------------------------------------------------------------
echo "Are you connected to the Internet ? [Y || y]"
ETHERNET=No
while read ETHERNET ;
do
if [[ $ETHERNET == "Y" ]] || [[ $ETHERNET == "y" ]]; then
echo "YES the cable is connected"
break
#
else
echo "NO, the cable is not connected ! Please connect it into the LAN"
fi
done
#------------------------------------------------------------------
Part_5 of Script_1 _==> Assign the Library/Repository and install it on the system
#------------------------------------------------------------------
#
echo "#######################################"
echo "### sudo apt install Yubikey U2F -y ###"
echo "#######################################"
# Add Yubico Repository
echo "############################################"
echo "### add-apt-repository ppa:yubico/stable ###"
echo "############################################"
sudo add-apt-repository ppa:yubico/stable
STATUS_LAST_CMD $?; STATUS="$?"; if [ "$STATUS" != "0" ]; then ERROR="1"; fi
#
echo "############################################"
echo "### sudo apt install pamu2fcfg ###"
echo "############################################"
sudo apt purge pamu2fcfg
#STATUS_LAST_CMD $?; STATUS="$?"; if [ "$STATUS" != "0" ]; then ERROR="1"; fi
sudo apt install pamu2fcfg
STATUS_LAST_CMD $?; STATUS="$?"; if [ "$STATUS" != "0" ]; then ERROR="1"; fi
# Install U2F libs for Yubico
echo
echo "############################################"
echo "### apt install libpam-u2f ###"
echo "############################################"
sudo apt purge libpam-u2f
#STATUS_LAST_CMD $?; STATUS="$?"; if [ "$STATUS" != "0" ]; then ERROR="1"; fi
sudo apt install libpam-u2f
STATUS_LAST_CMD $?; STATUS="$?"; if [ "$STATUS" != "0" ]; then ERROR="1"; fi
#------------------------------------------------------------------
echo -e "${BLUE}------------------------------------------------------------------${DFAULTCOLOR}"
echo ""
if [ "$ERROR" = "0" ]; then echo -e "${BLUE}Done !${DFAULTCOLOR}"; fi
if [ "$ERROR" != "0" ]; then echo -e "${RED}ERROR ! Something went wrong...${DFAULTCOLOR}"; fi
echo ""
echo -e "${BLUE}------------------------------------------------------------------${DFAULTCOLOR}"
Script_2 ==> u2f_1__2_pam.sh
The script will will configure the Yubikey for Ubuntu in order to enable FIDO/U2F.
It requires to change some existing files which are located at:
/etc/pam.d
In order to keep this script very simple it will generate two subfolders:
- /etc/pam.d/U2F_0
- /etc/pam.d/U2F_1
U2F_0 will contain a copy of the original files which were located at /etc/pam.d. (As backup)
U2F_1 will contain manipulated files in order to activate your Yubikey for FIDO/U2F of your Ubuntu.
Part_1 of Script_2 ==> Definition of some variables
#!/bin/bash
#
###############################################
# Version 1.1 #
# Date 2025-01-20 #
# Kay Schiller #
# Description: Configure FIDO/U2F for Ubuntu #
# Part 2: Copy Files A2B #
###############################################
#
FILE_3=u2f_keys
USER_1="$USER"
USER_2="$USER"
DIR_0="`pwd`"
DIR_1=""
DIR_2=""
DIR_3="/home/$USER_1/.config/Yubico"
DIR_4="/etc/pam.d/U2F_0"
DIR_5="/etc/pam.d/U2F_1"
DIR_6="$DIR_0/u2f/U2F_0"
DIR_7="$DIR_0/u2f/U2F_1"
DIR_8="/etc/pam.d/"
DIR_9="/etc/Yubico"
ERROR="0"
#
#------- Functions -------------------------------------------------
#
DFAULTCOLOR="\e[0m"
GREEN="\e[1;92m"
RED="\e[1;91m"
BLUE="\e[1;94m"
Part_2 of Script_2 ==> Loading the function STATUS_LAST_CMD
#
# Load Functions
#
. /home/$USER/Documents/USB/functions.sh
if [ $? = 0 ]
then
echo -e "${GREEN}Functios imported succesfully${DFAULTCOLOR}"
else
echo -e "${RED}ERROR: Functions were not imported, check path !${DFAULTCOLOR}"
exit
fi
We can find the description of the function under “Part_2 of Script 1”
Part_3 of Script_2 ==> Plotting the content of some variables
echo "DIR_0=$DIR_0"
echo "DIR_1=$DIR_1"
echo "DIR_2=$DIR_2"
echo "DIR_3=$DIR_3"
echo "DIR_4=$DIR_4"
echo "DIR_5=$DIR_5"
echo "DIR_6=$DIR_6"
echo "DIR_7=$DIR_7"
echo "DIR_8=$DIR_8"
echo "DIR_9=$DIR_9"
Part_4 of Script_2 ==> Ask the user whether the system is connected to the internet
#------------------------------------------------------------------
echo "Is the Ethernet cable connected ? [N || n]"
while read ETHERNET ;
do
if [[ $ETHERNET == "N" ]] || [[ $ETHERNET == "n" ]]; then
echo "No the cable is not connected anymore"
break
#
else
echo "Yes, the cable is still connected !"
fi
done
#
#------------------------------------------------------------------
Part_5 of Script_2 ==> Save the publik key of the Yubikey to right location
#------------------------------------------------------------------
echo
#read -p "Root shell available ? [ENTER]"
echo -e "${BLUE}Root shell available ? ${RED}[ENTER]${DFAULTCOLOR}"; read -p ""
echo
# Generate Yubico Keys for U2F
echo
echo "###############################################"
echo "### Generate Keys: $DIR_3 ###"
echo "###############################################"
mkdir -p $DIR_3
chmod 770 $DIR_3
#
echo "pamu2fcfg > ~/.config/Yubico/u2f_keys"
pamu2fcfg > "$DIR_3/u2f_keys"
STATUS_LAST_CMD $?; STATUS="$?"; if [ "$STATUS" != "0" ]; then ERROR="1"; fi
#
# Copy Yubikey to /etc/Yubico
#
sudo mkdir -p $DIR_9
echo "cp -f $DIR_3/* $DIR_9/."
echo ""
sudo rsync -av $DIR_3/* $DIR_9/.
STATUS_LAST_CMD $?; STATUS="$?"; if [ "$STATUS" != "0" ]; then ERROR="1"; fi
sudo chmod -Rf 660 $DIR_9
sudo chown -Rf root $DIR_9
sudo chgrp -Rf root $DIR_9
# End of Generate Yubico Keys for U2F
#------------------------------------------------------------------
Part_6 of Script_2 ==> Save the files of the location /etc/pam.d to /etc/pam.d/U2F_0 as backup
#------------------------------------------------------------------
# cp /etc/pam.d/. /etc/pam.d/U2F_0/.
#
echo
echo "###################################################"
echo "### Configure pam.d => U2F_0: $DIR_8/U2F_0 ###"
echo "###################################################"
if [ ! -d "$DIR_4" ]; then
sudo mkdir -p "$DIR_4"
pwd
sudo rsync -av $DIR_8/. $DIR_4/.
STATUS_LAST_CMD $?; STATUS="$?"; if [ "$STATUS" != "0" ]; then ERROR="1"; fi
fi
#------------------------------------------------------------------
# cp -f /home/$USER/Documents/USB/u2f/U2F_1/* /etc/pam.d/U2F_1/.
#
echo "######################################################"
echo "### cp -f ${DIR_7}/* ${DIR_5}/. ###"
echo "######################################################"
#
#DIR_0=/home/$USER/Documents/USB
#DIR_1=
#DIR_2=
#DIR_3=/home/$USER/.config/Yubico
#DIR_4=/etc/pam.d/U2F_0
#DIR_5=/etc/pam.d/U2F_1
#DIR_6=/home/$USER/Documents/USB/u2f/U2F_0
#DIR_7=/home/$USER/Documents/USB/u2f/U2F_1
#DIR_8=/etc/pam.d/
#DIR_9=/etc/Yubico
#
echo
sudo mkdir -p $DIR_5
echo "DIR_7=$DIR_7"
echo "DIR_5=$DIR_5"
echo
echo "rsync -av $DIR_7/. $DIR_5/."
echo
sudo rsync -av $DIR_7/. $DIR_5/.
STATUS_LAST_CMD $?; STATUS="$?"; if [ "$STATUS" != "0" ]; then ERROR="1"; fi
echo "chmod -Rf 644 $DIR_5"
sudo chmod -Rf 644 $DIR_5
echo "sudo chown -Rf root $DIR_5"
sudo chown -Rf root $DIR_5
echo "sudo chgrp -Rf root $DIR_5"
sudo chgrp -Rf root $DIR_5
#------------------------------------------------------------------
Part_7 of Script_2 ==> Save the files of the location /etc/pam.d/U2F_1 to /etc/pam.d in order to activate FIDO/U2F for the system
#------------------------------------------------------------------
#
# Configure pam.d using certain files of U2F_1 for: /etc/pam.d/
#
#------------------------------------------------------------------
echo "#################################################"
echo "### cp U2F_1 Files to /etc/pam.d ###"
echo "#################################################"
ls -al $DIR_8
#------------------------------------------------------------------
echo
echo "sudo cp -f $DIR_5/common-auth $DIR_8/."
sudo cp -f "$DIR_5/common-auth" $DIR_8/.
STATUS_LAST_CMD $?; STATUS="$?"; if [ "$STATUS" != "0" ]; then ERROR="1"; fi
echo
#------------------------------------------------------------------
#------------------------------------------------------------------
echo
echo "sudo cp -f $DIR_5/su $DIR_8/."
sudo cp -f "$DIR_5/su" $DIR_8/.
STATUS_LAST_CMD $?; STATUS="$?"; if [ "$STATUS" != "0" ]; then ERROR="1"; fi
echo
#------------------------------------------------------------------
#------------------------------------------------------------------
echo
echo "sudo cp -f $DIR_5/su-l $DIR_8/."
sudo cp -f "$DIR_5/su-l" $DIR_8/.
STATUS_LAST_CMD $?; STATUS="$?"; if [ "$STATUS" != "0" ]; then ERROR="1"; fi
echo
#------------------------------------------------------------------
#------------------------------------------------------------------
echo
echo "sudo cp -f $DIR_5/sudo $DIR_8/."
sudo cp -f "$DIR_5/sudo" $DIR_8/.
STATUS_LAST_CMD $?; STATUS="$?"; if [ "$STATUS" != "0" ]; then ERROR="1"; fi
echo
#------------------------------------------------------------------
#------------------------------------------------------------------
echo
echo "sudo cp -f $DIR_5/sudo-i $DIR_8/."
sudo cp -f "$DIR_5/sudo-i" $DIR_8/.
STATUS_LAST_CMD $?; STATUS="$?"; if [ "$STATUS" != "0" ]; then ERROR="1"; fi
echo
#------------------------------------------------------------------
# Cleaning...
# rm /etc/pam.d/USF_0/U2F_0, rm /etc/pam.d/USF_0/U2F_1 rm /etc/pam.d/USF_1/U2F_0, rm /etc/pam.d/USF_1/U2F_0
echo "rm -rf /etc/pam.d/USF_0/U2F_0"
sudo rm -rf /etc/pam.d/USF_0/U2F_0
echo
#------------------------------------------------------------------
echo -e "${BLUE}------------------------------------------------------------------${DFAULTCOLOR}"
echo ""
if [ "$ERROR" = "0" ]; then echo -e "${BLUE}Done !${DFAULTCOLOR}"; fi
if [ "$ERROR" != "0" ]; then echo -e "${RED}ERROR ! Something went wrong...${DFAULTCOLOR}"; fi
echo ""
echo -e "${BLUE}------------------------------------------------------------------${DFAULTCOLOR}"
Files of U2F_1 ==> FIDO/U2F Configuration
The folder /etc/pam.d/U2F_1 contains a bunch of files and those files configure U2F for our OS.
In the following I will list the files which I have currently configured for my system.
For the sake of completeness I will print the content of the files here but I will also attach the files at the very end of this tutorial.
[It looks like, it is not possible to attach files at this location…]
The command line between the # lines are showing the manipulation in order to configure FIDO/U2F.
Command Example_1
###########################################
auth sufficient pam_u2f.so cue
###########################################
This command configures the environment, that we have to press the button of the Yubikey without the need to enter a password in additon.
Command Example_2
################################################################
auth required pam_u2f.so authfile=/etc/Yubico/u2f_keys
################################################################
This command points to the file which is used to store the public key of the Yubikey. It is possilbe to store several keys of serveral users in one file.
In general it is recommend to have a second key available just in case we loose one of them.
/etc/pam.d/sudo
#%PAM-1.0
# Set up user limits from /etc/security/limits.conf.
session required pam_limits.so
session required pam_env.so readenv=1 user_readenv=0
session required pam_env.so readenv=1 envfile=/etc/default/locale user_readenv=0
###########################################
auth sufficient pam_u2f.so cue
###########################################
@include common-auth
################################################################
auth required pam_u2f.so authfile=/etc/Yubico/u2f_keys
################################################################
@include common-account
@include common-session-noninteractive
/etc/pam.d/sudo-i
#%PAM-1.0
# Set up user limits from /etc/security/limits.conf.
session required pam_limits.so
session required pam_env.so readenv=1 user_readenv=0
session required pam_env.so readenv=1 envfile=/etc/default/locale user_readenv=0
###########################################
auth sufficient pam_u2f.so cue
###########################################
@include common-auth
################################################################
auth required pam_u2f.so authfile=/etc/Yubico/u2f_keys
################################################################
@include common-account
@include common-session
/etc/pamm.d/su
#
# The PAM configuration file for the Shadow `su' service
#
# This allows root to su without passwords (normal operation)
auth sufficient pam_rootok.so
# Uncomment this to force users to be a member of group wheel
# before they can use `su'. You can also add "group=foo"
# to the end of this line if you want to use a group other
# than the default "wheel" (but this may have side effect of
# denying "root" user, unless she's a member of "foo" or explicitly
# permitted earlier by e.g. "sufficient pam_rootok.so").
# (Replaces the `SU_WHEEL_ONLY' option from login.defs)
# auth required pam_wheel.so
# Uncomment this if you want wheel members to be able to
# su without a password.
# auth sufficient pam_wheel.so trust
# Uncomment this if you want members of a specific group to not
# be allowed to use su at all.
# auth required pam_wheel.so deny group=nosu
# Uncomment and edit /etc/security/time.conf if you need to set
# time restrainst on su usage.
# (Replaces the `PORTTIME_CHECKS_ENAB' option from login.defs
# as well as /etc/porttime)
# account requisite pam_time.so
# This module parses environment configuration file(s)
# and also allows you to use an extended config
# file /etc/security/pam_env.conf.
#
# parsing /etc/environment needs "readenv=1"
session required pam_env.so readenv=1
# locale variables are also kept into /etc/default/locale in etch
# reading this file *in addition to /etc/environment* does not hurt
session required pam_env.so readenv=1 envfile=/etc/default/locale
# Defines the MAIL environment variable
# However, userdel also needs MAIL_DIR and MAIL_FILE variables
# in /etc/login.defs to make sure that removing a user
# also removes the user's mail spool file.
# See comments in /etc/login.defs
#
# "nopen" stands to avoid reporting new mail when su'ing to another user
session optional pam_mail.so nopen
# Sets up user limits according to /etc/security/limits.conf
# (Replaces the use of /etc/limits in old login)
session required pam_limits.so
# The standard Unix authentication modules, used with
# NIS (man nsswitch) as well as normal /etc/passwd and
# /etc/shadow entries.
@include common-auth
################################################################
auth required pam_u2f.so authfile=/etc/Yubico/u2f_keys
################################################################
@include common-account
@include common-session
/etc/pam.d/su-l
#%PAM-1.0
auth include su
account include su
password include su
session optional pam_keyinit.so force revoke
session include su
@include common-auth
################################################################
auth required pam_u2f.so authfile=/etc/Yubico/u2f_keys
################################################################
/etc/pam.d/runuser
#%PAM-1.0
auth sufficient pam_rootok.so
session optional pam_keyinit.so revoke
session required pam_limits.so
session required pam_unix.so
@include common-auth
################################################################
auth required pam_u2f.so authfile=/etc/Yubico/u2f_keys
################################################################
/etc/pam.d/runuser-l
#%PAM-1.0
auth include runuser
session optional pam_keyinit.so force revoke
session optional pam_systemd.so
session include runuser
@include common-auth
################################################################
auth required pam_u2f.so authfile=/etc/Yubico/u2f_keys
################################################################
/etc/pam.d/common-auth
#
# /etc/pam.d/common-auth - authentication settings common to all services
#
# This file is included from other service-specific PAM config files,
# and should contain a list of the authentication modules that define
# the central authentication scheme for use on the system
# (e.g., /etc/shadow, LDAP, Kerberos, etc.). The default is to use the
# traditional Unix authentication mechanisms.
#
# As of pam 1.0.1-6, this file is managed by pam-auth-update by default.
# To take advantage of this, it is recommended that you configure any
# local modules either before or after the default block, and use
# pam-auth-update to manage selection of other modules. See
# pam-auth-update(8) for details.
# here are the per-package modules (the "Primary" block)
auth [success=2 default=ignore] pam_unix.so nullok
auth [success=1 default=ignore] pam_sss.so use_first_pass
# here's the fallback if no module succeeds
auth requisite pam_deny.so
# prime the stack with a positive return value if there isn't one already;
# this avoids us returning an error just because nothing sets a success code
# since the modules above will each just jump around
auth sufficient pam_u2f.so cue
#auth required pam_permit.so
#########################################################################
auth required pam_u2f.so authfile=/etc/Yubico/u2f_keys cue
#########################################################################
# and here are more per-package modules (the "Additional" block)
auth optional pam_cap.so
# end of pam-auth-update config
/etc/pam.d/lightdm
#%PAM-1.0
auth requisite pam_nologin.so
auth sufficient pam_succeed_if.so user ingroup nopasswdlogin
@include common-auth
-auth optional pam_gnome_keyring.so
-auth optional pam_kwallet.so
-auth optional pam_kwallet5.so
#####################################################################
auth required pam_u2f.so authfile=/etc/Yubico/u2f_keys
#####################################################################
@include common-account
session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so close
#session required pam_loginuid.so
session required pam_limits.so
@include common-session
session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so open
-session optional pam_gnome_keyring.so auto_start
-session optional pam_kwallet.so auto_start
-session optional pam_kwallet5.so auto_start
session required pam_env.so readenv=1
session required pam_env.so readenv=1 user_readenv=1 envfile=/etc/default/locale
@include common-password
Location of the Scripts & Files in our System before we can start the execution
Scripts
/home/$USER/Documents/USB/u2f_1__1_libs.sh
/home/$USER/Documents/USB/u2f_1__2_pam.sh
/home/$USER/Documents/USB/functions.sh
Folder with the prepared files
/home/$USER/Documents/USB/u2f/U2F_1
/home/$USER/Documents/USB/u2f/U2F_1/sudo
/home/$USER/Documents/USB/u2f/U2F_1/sudo-i
/home/$USER/Documents/USB/u2f/U2F_1/su
/home/$USER/Documents/USB/u2f/U2F_1/su-l
/home/$USER/Documents/USB/u2f/U2F_1/runuser
/home/$USER/Documents/USB/u2f/U2F_1/runuser-l
/home/$USER/Documents/USB/u2f/U2F_1/common-auth
/home/$USER/Documents/USB/u2f/U2F_1/lightdm
FIDO/U2F Configuration, execution of the Scripts
Now it is time to use the existing scripts.
They will work in case you prepared the files as shown under the headline
“Folder with the prepared files” or you have downloaded the files and checked the shasum of the files.
The Scripts and Files of U2F_1
I wanted to provide the scripts below in order to avoid typos.
This is not allowed according to the rules in this forum.
Please skip this part and continue with the headline “Acknowledgement”
[Scripts.zip ]
shasum -a 256 Scripts.zip
e35601443914c864cf96f89fd937c0cd7a2ca072dbd157812e674dd2ba7ca9ad Scripts.zip

[U2f_1.zip]
shasum -a 256 'U2F_1.zip'
3e8336950b84c912a222d495da3df95f267c26c7f5d9c1bce51706025a461f53 U2F_1.zip

Execution of the Scripts
- Open a root shell and keep the shell open !
sudo -i
- Open a new terminal and execute the script_1
/home/$USER/Documents/USB/u2f_1__1_libs.sh
- Continue with the next script_2
/home/$USER/Documents/USB/u2f_1__2_pam.sh
- Test the FIDO/U2F configuration
Open a new Terminal and write the following command into it
sudo echo Hi!
In case FIDO/U2F was configured in the right way, then you have press the button of the Yubikey in order the get access to the sudo command.
=> now you could close the root shell
In case it was not configured in the right way, then the system will execute it with or without asking for a password.
=> you could use the root shell in order to copy the backup to /etc/pam.d
==> I used this option when I started to write this script.
===> Today the script works without any issues !
Acknowlegment
- A big thank you to the developer of the FIDO standard,
this was for sure hard work, hard hard work ! - In addition I want to thank the developer of the hardware which is secure by design.
This was also hard work, but fun as well! - And of course we have to thank the sponsors who enabled this solution.
THANK YOU !
Literature
Ubuntu Linux login guide (U2F)
Das Passwort ist tot – es leben die Passkeys
Zugangssicherheit: 2FA, MFA und FIDO2
How to get the best out of your Yubikey with GPG
Appendix
Some commands in case you are a beginner
-
- Open a Terminal ==> press
CTRL+ALT+tat the same time
- Open a Terminal ==> press
-
$USERis the name of the account who logged into the system.
We can check this with the command:whoami
The result ofwhoamiwill plot the account name
-
cd~ change directory, can be used to change to another directory
cd /home/$USER/Documents
-
ls -alrth~ will list the content of the current directory
-
mkdir~ can make a directory
mkdir /home/$USER/Documents/u2f
mkdir /home/$USER/Documents/u2f/U2F_1
-
- gnome-text-editor filename ~ will open the file filename or
generate filename in case it is not existing
gnome-text-editor /home/$USER/Documents/u2f/U2F_1/sudo
- gnome-text-editor filename ~ will open the file filename or
u2f_1__1_libs.sh
#!/bin/bash
#
####################################################
# Version 1.1 #
# Date 2025-01-20 #
# Kay Schiller #
# Description: Configure Fido/u2f for Ubuntu 24.04 #
# Part 1: Assign and load libs #
####################################################
#
FILE_3=u2f_keys
DIR_0="."
DIR_1=""
DIR_2=""
DIR_3="/home/$USER/.config/Yubico"
DIR_4="/etc/pam.d/U2F_0"
DIR_5="/etc/pam.d/U2F_1"
DIR_6="$DIR_0/u2f/U2F_0"
DIR_7="$DIR_0/u2f/U2F_1"
DIR_8="/etc/pam.d/"
DIR_9="/etc/Yubico"
USER_2=$USER
ERROR="0"
#
#------- Functions -------------------------------------------------
#
DFAULTCOLOR="\e[0m"
GREEN="\e[1;92m"
RED="\e[1;91m"
BLUE="\e[1;94m"
#
# Load Functions
#
. /home/$USER/Documents/USB/functions.sh
if [ $? = 0 ]
then
echo -e "${GREEN}Functios imported succesfully${DFAULTCOLOR}"
else
echo -e "${RED}ERROR: Functions were not imported, check path !${DFAULTCOLOR}"
exit
fi
#------------------------------------------------------------------
echo "DIR_0=$DIR_0"
echo "DIR_1=$DIR_1"
echo "DIR_2=$DIR_2"
echo "DIR_3=$DIR_3"
echo "DIR_4=$DIR_4"
echo "DIR_5=$DIR_5"
echo "DIR_6=$DIR_6"
echo "DIR_7=$DIR_7"
echo "DIR_8=$DIR_8"
echo "DIR_9=$DIR_9"
echo
#
#ETHERNET = 0
#
#------------------------------------------------------------------
echo "Are you connected to the Internet ? [Y || y]"
ETHERNET=No
while read ETHERNET ;
do
if [[ $ETHERNET == "Y" ]] || [[ $ETHERNET == "y" ]]; then
echo "YES the cable is connected"
break
#
else
echo "NO, the cable is not connected ! Please connect it into the LAN"
fi
done
#------------------------------------------------------------------
#
echo "#######################################"
echo "### sudo apt install Yubikey U2F -y ###"
echo "#######################################"
# Add Yubico Repository
echo "############################################"
echo "### add-apt-repository ppa:yubico/stable ###"
echo "############################################"
sudo add-apt-repository ppa:yubico/stable
STATUS_LAST_CMD $?; STATUS="$?"; if [ "$STATUS" != "0" ]; then ERROR="1"; fi
#
echo "############################################"
echo "### sudo apt install pamu2fcfg ###"
echo "############################################"
sudo apt purge pamu2fcfg
#STATUS_LAST_CMD $?; STATUS="$?"; if [ "$STATUS" != "0" ]; then ERROR="1"; fi
sudo apt install pamu2fcfg
STATUS_LAST_CMD $?; STATUS="$?"; if [ "$STATUS" != "0" ]; then ERROR="1"; fi
# Install U2F libs for Yubico
echo
echo "############################################"
echo "### apt install libpam-u2f ###"
echo "############################################"
sudo apt purge libpam-u2f
#STATUS_LAST_CMD $?; STATUS="$?"; if [ "$STATUS" != "0" ]; then ERROR="1"; fi
sudo apt install libpam-u2f
STATUS_LAST_CMD $?; STATUS="$?"; if [ "$STATUS" != "0" ]; then ERROR="1"; fi
#------------------------------------------------------------------
echo -e "${BLUE}------------------------------------------------------------------${DFAULTCOLOR}"
echo ""
if [ "$ERROR" = "0" ]; then echo -e "${BLUE}Done !${DFAULTCOLOR}"; fi
if [ "$ERROR" != "0" ]; then echo -e "${RED}ERROR ! Something went wrong...${DFAULTCOLOR}"; fi
echo ""
echo -e "${BLUE}------------------------------------------------------------------${DFAULTCOLOR}"
u2f_1__2_pam.sh
#!/bin/bash
#
###############################################
# Version 1.1 #
# Date 2025-01-20 #
# Kay Schiller #
# Description: Configure Fido/U2F for Ubuntu #
# Part 2: Copy Files A2B #
###############################################
#
FILE_3=u2f_keys
USER_1="$USER"
USER_2="$USER"
DIR_0="`pwd`"
DIR_1=""
DIR_2=""
DIR_3="/home/$USER_1/.config/Yubico"
DIR_4="/etc/pam.d/U2F_0"
DIR_5="/etc/pam.d/U2F_1"
DIR_6="$DIR_0/u2f/U2F_0"
DIR_7="$DIR_0/u2f/U2F_1"
DIR_8="/etc/pam.d/"
DIR_9="/etc/Yubico"
ERROR="0"
#
#------- Functions -------------------------------------------------
#
DFAULTCOLOR="\e[0m"
GREEN="\e[1;92m"
RED="\e[1;91m"
BLUE="\e[1;94m"
#
# Load Functions
#
. /home/$USER/Documents/USB/functions.sh
if [ $? = 0 ]
then
echo -e "${GREEN}Functios imported succesfully${DFAULTCOLOR}"
else
echo -e "${RED}ERROR: Functions were not imported, check path !${DFAULTCOLOR}"
exit
fi
#------------------------------------------------------------------
echo "DIR_0=$DIR_0"
echo "DIR_1=$DIR_1"
echo "DIR_2=$DIR_2"
echo "DIR_3=$DIR_3"
echo "DIR_4=$DIR_4"
echo "DIR_5=$DIR_5"
echo "DIR_6=$DIR_6"
echo "DIR_7=$DIR_7"
echo "DIR_8=$DIR_8"
echo "DIR_9=$DIR_9"
echo
#
#ETHERNET = 0
#
#------------------------------------------------------------------
echo "Is the Ethernet cable connected ? [N || n]"
while read ETHERNET ;
do
if [[ $ETHERNET == "N" ]] || [[ $ETHERNET == "n" ]]; then
echo "No the cable is not connected anymore"
break
#
else
echo "Yes, the cable is still connected !"
fi
done
#
#------------------------------------------------------------------
echo
#read -p "Root shell available ? [ENTER]"
echo -e "${BLUE}Root shell available ? ${RED}[ENTER]${DFAULTCOLOR}"; read -p ""
echo
# Generate Yubico Keys for U2F
echo
echo "###############################################"
echo "### Generate Keys: $DIR_3 ###"
echo "###############################################"
mkdir -p $DIR_3
chmod 770 $DIR_3
#
echo "pamu2fcfg > ~/.config/Yubico/u2f_keys"
pamu2fcfg > "$DIR_3/u2f_keys"
STATUS_LAST_CMD $?; STATUS="$?"; if [ "$STATUS" != "0" ]; then ERROR="1"; fi
#
# Copy Yubikey to /etc/Yubico
#
sudo mkdir -p $DIR_9
echo "cp -f $DIR_3/* $DIR_9/."
echo ""
sudo rsync -av $DIR_3/* $DIR_9/.
STATUS_LAST_CMD $?; STATUS="$?"; if [ "$STATUS" != "0" ]; then ERROR="1"; fi
sudo chmod -Rf 660 $DIR_9
sudo chown -Rf root $DIR_9
sudo chgrp -Rf root $DIR_9
# End of Generate Yubico Keys for U2F
#------------------------------------------------------------------
# cp /etc/pam.d/. /etc/pam.d/U2F_0/.
#
echo
echo "###################################################"
echo "### Configure pam.d => U2F_0: $DIR_8/U2F_0 ###"
echo "###################################################"
if [ ! -d "$DIR_4" ]; then
sudo mkdir -p "$DIR_4"
pwd
sudo rsync -av $DIR_8/. $DIR_4/.
STATUS_LAST_CMD $?; STATUS="$?"; if [ "$STATUS" != "0" ]; then ERROR="1"; fi
fi
#------------------------------------------------------------------
# cp -f /home/$USER/Documents/USB/u2f/U2F_1/* /etc/pam.d/U2F_1/.
#
echo "######################################################"
echo "### cp -f ${DIR_7}/* ${DIR_5}/. ###"
echo "######################################################"
#
#DIR_0=/home/$USER/Documents/USB
#DIR_1=
#DIR_2=
#DIR_3=/home/$USER/.config/Yubico
#DIR_4=/etc/pam.d/U2F_0
#DIR_5=/etc/pam.d/U2F_1
#DIR_6=/home/$USER/Documents/USB/u2f/U2F_0
#DIR_7=/home/$USER/Documents/USB/u2f/U2F_1
#DIR_8=/etc/pam.d/
#DIR_9=/etc/Yubico
#
echo
sudo mkdir -p $DIR_5
echo "DIR_7=$DIR_7"
echo "DIR_5=$DIR_5"
echo
echo "rsync -av $DIR_7/. $DIR_5/."
echo
sudo rsync -av $DIR_7/. $DIR_5/.
STATUS_LAST_CMD $?; STATUS="$?"; if [ "$STATUS" != "0" ]; then ERROR="1"; fi
echo "chmod -Rf 644 $DIR_5"
sudo chmod -Rf 644 $DIR_5
echo "sudo chown -Rf root $DIR_5"
sudo chown -Rf root $DIR_5
echo "sudo chgrp -Rf root $DIR_5"
sudo chgrp -Rf root $DIR_5
#------------------------------------------------------------------
#
# Configure pam.d using certain files of U2F_1 for: /etc/pam.d/
#
#------------------------------------------------------------------
echo "#################################################"
echo "### cp U2F_1 Files to /etc/pam.d ###"
echo "#################################################"
ls -al $DIR_8
#------------------------------------------------------------------
echo
echo "sudo cp -f $DIR_5/common-auth $DIR_8/."
sudo cp -f "$DIR_5/common-auth" $DIR_8/.
STATUS_LAST_CMD $?; STATUS="$?"; if [ "$STATUS" != "0" ]; then ERROR="1"; fi
echo
#------------------------------------------------------------------
#------------------------------------------------------------------
echo
echo "sudo cp -f $DIR_5/su $DIR_8/."
sudo cp -f "$DIR_5/su" $DIR_8/.
STATUS_LAST_CMD $?; STATUS="$?"; if [ "$STATUS" != "0" ]; then ERROR="1"; fi
echo
#------------------------------------------------------------------
#------------------------------------------------------------------
echo
echo "sudo cp -f $DIR_5/su-l $DIR_8/."
sudo cp -f "$DIR_5/su-l" $DIR_8/.
STATUS_LAST_CMD $?; STATUS="$?"; if [ "$STATUS" != "0" ]; then ERROR="1"; fi
echo
#------------------------------------------------------------------
#------------------------------------------------------------------
echo
echo "sudo cp -f $DIR_5/sudo $DIR_8/."
sudo cp -f "$DIR_5/sudo" $DIR_8/.
STATUS_LAST_CMD $?; STATUS="$?"; if [ "$STATUS" != "0" ]; then ERROR="1"; fi
echo
#------------------------------------------------------------------
#------------------------------------------------------------------
echo
echo "sudo cp -f $DIR_5/sudo-i $DIR_8/."
sudo cp -f "$DIR_5/sudo-i" $DIR_8/.
STATUS_LAST_CMD $?; STATUS="$?"; if [ "$STATUS" != "0" ]; then ERROR="1"; fi
echo
#------------------------------------------------------------------
# Cleaning...
# rm /etc/pam.d/USF_0/U2F_0, rm /etc/pam.d/USF_0/U2F_1 rm /etc/pam.d/USF_1/U2F_0, rm /etc/pam.d/USF_1/U2F_0
echo "rm -rf /etc/pam.d/USF_0/U2F_0"
sudo rm -rf /etc/pam.d/USF_0/U2F_0
echo
#------------------------------------------------------------------
echo -e "${BLUE}------------------------------------------------------------------${DFAULTCOLOR}"
echo ""
if [ "$ERROR" = "0" ]; then echo -e "${BLUE}Done !${DFAULTCOLOR}"; fi
if [ "$ERROR" != "0" ]; then echo -e "${RED}ERROR ! Something went wrong...${DFAULTCOLOR}"; fi
echo ""
echo -e "${BLUE}------------------------------------------------------------------${DFAULTCOLOR}"
functions.sh
#!/bin/bash
#
###############################################
# Version 1.9 #
# Date 2024-11-05 #
# Kay Schiller #
# Description: File for functions #
# #
###############################################
#
#
DFAULTCOLOR="\e[0m"
GREEN="\e[1;92m"
RED="\e[1;91m"
BLUE="\e[1;94m"
#
#
#------- Funkctions -------------------------------------------------
#
STATUS_LAST_CMD()
{
if [ $1 = 0 ]
then
echo -e "${GREEN}Command succesfully executed${DFAULTCOLOR}"
return 0
else
echo -e "${RED}Error, command was not executed !${DFAULTCOLOR}"
return 1
fi
}