This is my guide to setting up Two-factor SSH and Virtualmin Authentication via Google-Authenticator on CentOS.

What is Google Authenticator

The Google Authenticator project includes implementations of one-time passcode generators for several mobile platforms, as well as a pluggable authentication module (PAM)The whole one-time passcode is based on the time of the server and device with the Google-Authentication program installed being in sync. The device and the server will then know what one-time code is correct for a 30 second period, and then it will change. 

The Idea behind using this with SSH and Virtualmin is to greatly increase the security level of your server. You will install the Google-Authentication PAM module on your server and a Google Authenticator application on Android, iPhone or Blackberry phone – you will then be able to use this phone/device 

A couple of warnings. Open two ssh sessions, the reasoning is that if you get this wrong you will lock yourself out of your own system remotely, use the second session to remove or make changes to sshd and pam

So here is what we will do:

1. Install NTP to synchronize the system clock (Very important or nothing will work)

2. Install and setup the Google-Authentication PAM module on your server

3. Setup PAM authentication on the SSH server to work with the Google-Authentication PAM module

4. Setup Virtualmin to work with the Google-Authentication PAM module

So lets get started:


1. Install NTP to synchronize the system clock (Very important or nothing will work)

Login via ssh as root user

Type the following command to install ntp

yum install ntp

Turn on service on boot

chkconfig ntpd on

Synchronize the system clock with server:


Start the NTP service:

/etc/init.d/ntpd start


2. Install and setup the Google-Authentication PAM module on your server

Make sure that you have the “EPEL” repo installed so that you will be able to install everything. I normally install it and then disable it, and only –enablerepo it when I need it.

On CentOS 5.x these two commands will be a bit differnent than on CentOS 6.x

If you don’t yet have Python 2.6 installed install it: – this will install in in parallel with python2.4 so as not to break yum

yum --enablerepo=epel install python26

once  installed lets install some dependencies – I have added a couple just in case I need them

yum --enablerepo=epel install gcc gcc++ pam-devel subversion python26-devel

 On Centos 6.x 

You will already have Python 2.6 running so you wont need to install that – lets just install some dependencies in case we need them.

yum --enablerepo=epel install gcc gcc++ pam-devel subversion python-devel

Now we need to download and install git

yum --enablerepo=epel install git

Make a directory for the authenticator

mkdir ./google-authenticator
cd google-authenticator/

Download the SVN for google authenticator

git clone


Change to to downloaded directory (on mine I also had to cd into libpam) 

cd google-authenticator/libpam/

Run make and make install 

make && make install

If all goes well you should now have the Google-Authentication PAM module on your server

 3. Setup PAM authentication on the SSH server to work with the Google-Authentication PAM module

Now we need to configure Pam auth for SSH

vim /etc/pam.d/sshd

change the file to this – basically adding the “auth required” line

auth required
auth include system-auth
account required
account include system-auth
password include system-auth
session optional force revoke
session include system-auth
session required

Now we need to edit the ssh daemon configuration file (you can also do this from virtualmin if you like)

vim /etc/ssh/sshd_config 


ChallengeResponseAuthentication yes

comment out

#ChallengeResponseAuthentication no

Make sure that

UsePAM yes

To make your system truly secure – you might want to disable PubkeyAuthentication

PubkeyAuthentication no


**NOW STOP and make sure that second SSH session is working because you can then edit /etc/ssh/sshd_config & /etc/pam.d/sshd if something goes wrong. Otherwise you are going to need to make these changes on the local console – you would also still be able to edit things from Virtualmin if needed

Restart the SSH daemon

service sshd restart

Then run the google authenticator on the server by running:


You should see something like|0&cht=qr&chl=otpauth://totp/user@server%3Fsecret%3DSAEP64T5VZAVWAFB
Your new secret key is: SAEP64T5VZAVWAFB
Your verification code is 376046
Your emergency scratch codes are:

Answer each of the question to best suit your needs – I said Yes to everything except the “you can increase the window from its default
size of 1:30min to about 4min. Do you want to do so? ” option

Note: The emergency scratch codes are one-time use verification codes in the event your phone is unavailable. So save these somewhere save!

In your browser, load the URL noted above; it will show a QRCode that you can scan into your phone using the Google Authenticator application for Android, iPhone or Blackberry. If you already have a Google Authenticator token being generated on your phone, you can add a new one and it will display them both.

Start the appon your phone:

In your phone select time based 

For account use ssh enabled user i.e. root
Account: root@clearosipgoeshere 

If all is working you should be able to SSH in with the username, then authentication code from your phone/device and then your password. 

** If something does not work, make sure your time on the server and phone has synced and that you have followed the all the steps. You can always change things back in the second SSH window you kept open or via Virtualmin.

Once you are are 100% sure that everything is working with the SSH you can go on to setting it up on Virtualmin.

4. Setup Virtualmin to work with the Google-Authentication PAM module

For PAM authentication to work on Virtualmin you have to make sure you have the “perl-Authen-PAM” module installed.

Install the perl-Authen-PAM module:

yum --enablerepo=epel install perl-Authen-PAM

Then make sure that you have a webmin user setup and the password option set to UNIX Authentication:

You can do that by logging in to Virtualmin

Click on the:

 Webmin >> Webmin >>Webmin Users 

Then click on the user you already set the google-authenticator up for in the SSH section – root in my case.

Change the password to >>  UNIX Authentication

This way the password and google-authenticator code will be the same for the user all around as it authenticates the user via PAM


Then edit – /etc/pam.d/webmin in the same way you edited /etc/pam.d/sshd 

vim  /etc/pam.d/webmin

Adding the “auth required” line:

auth required
auth required nullok
account required
session required

Since the login process will now prompt for more than just a username and password, you would need to enable “Full PAM conversion” mode in Webmin. This can be done by editing /etc/webmin/miniserv.conf and adding the line pam_conv=1

so edit:

vim /etc/webmin/miniserv.conf

adding to the bottom:



Now restart the webmin service:


You should now be able to login to your Virtualmin and see it asking you for a username, then the verification code and then the password.

Normally if a user Attempts to login with the pam module enabled, it will fail if a secret file for that user is not setup. There is a patch that changes this and causes google_authenticator() to return PAM_IGNORE and not ask for a code if the user has not setup a secret file or if there was an error reading the file. Here is a link to that.

I will update this when I have tried this patch and also when I have set this up on Centos6.

I hope this helps!

How To: CentOS Two-factor SSH and Virtualmin Authentication via Google-Authenticator
Tagged on:                                                                                                                             

Leave a Reply

Your email address will not be published. Required fields are marked *

45 thoughts on “How To: CentOS Two-factor SSH and Virtualmin Authentication via Google-Authenticator

  • Hi
    thanks for the tutorial ! I almost have it working properly.
    ntp, google auth install no issue;
    my Pam settings is a cut and paste of your tutorial for pam.d/sshd
    I however am unable to log in.
    I found the following error message in /var/log/secure:
    auth sshd(pam_google_authenticator)[2605] Failed to update secret file “/home/USERNAME/.google_authenticator”
    auth sshd[2603]: error: PAM: Cannot make/remove an entry for the specified session for USERNAME from Ip

    any ideas ?
    Thanks in advance

    1. Hi Nabyl,

      I get those same errors when something is wrong with my setup or even when I enter the wrong Google Auth Code so it’s hard to debug

      What I would do is
      1. Try to log in with you user name and password but use one of the Scratch codes – you can find a list of the scratch codes in the google-authenticator file. If you can log in with the scratch code then we know everything is working on your set up. It might just be a time sync thing then or perhaps you added the code incorrectly. If you can’t log in with the scratch code I would go to step two

      2. Delete the google-Auth file – su to the actual user and rerun the google-Auth command – readd the code to your phone and try again.

      3. If step 3 does not work try to remove the Pam settings and start over. Then try again

      Let me know of you still can’t get it to work and then I can have a look for you


  • I am having trouble getting this to work. I followed the guide exactly.

    I got NTPD running and sync’d it with
    I ran through all the commands you listed to get google auth installed
    I added the auth required line right under #%PAM-1.0
    I edited the sshd_config and uncommented the ChallengeResponseAuthentication yes and commented the ChallengeResponseAuthentication no
    I restarted SSHD
    I ran google-authenticator, scanned the barcode with my phone, and answered the questions the same as you.

    When I login via ssh, it asks me for the verification code. I enter the code, it then asks me for my password. I enter my password, and get Access Denied. This loop continues. I have tried a couple dozen times and am 100% sure I am typing the code and password correctly.

    In /var/log/secure I see the below:

    May 31 20:18:34 localhost sshd(pam_google_authenticator)[2610]: Invalid verification code
    May 31 20:18:38 localhost sshd[2608]: error: PAM: Cannot make/remove an entry for the specified session for admin from wireless_broadband_router
    May 31 20:18:42 localhost sshd(pam_google_authenticator)[2611]: Invalid verification code
    May 31 20:18:46 localhost sshd[2608]: error: PAM: Cannot make/remove an entry for the specified session for admin from wireless_broadband_router

    I have ensured the time is correct and synced on my phone and computer. I tried changing the selinux from enforcing to permissive to disabled, after seeing that selinux might be the issue.

    I still cannot get it to work. Centos 6.4

    Please help 🙂

    1. Hi Josh,

      did you make sure that this line is in your sshd_config

      UsePAM yes

      I have also had this happen to me once before – try going to the users home directory and removing the google-authenticator folder and then starting over.

      If you still have problems let me know – I am sure I can help out!

      1. UsePAM was already set to use in sshd_config (this was a fresh 6.4 centos install yesterday)

        i went into the /home/josh folder, removed .google_authenticator with rm .google_authenticator
        ran google-authenticator
        y to time based
        open the link, scanned into phone,
        y to update .google-auth file
        y to man in the middle attacks
        n to 4 min timeout
        y to rate limiting

        same error unfortunately

        ran through the whole thing again and chose y to 4 min timeout, same thing 🙁

        1. Josh,

          Is this a local server or online? If you want I can have a look for you.

          Let me know and ill email you my information.

          1. Hey DieSkim,

            I finally got it working, or actually, it was set up right all along, there is just some strange issue with time. With the 4 minute timeout set to no, I couldnt get it to work at all. With the 4 minute timeout set to yes, if I entered the verification code IMMEDIATELY after it changed on my phone, then it worked. If I waited 5-10 seconds, it didn’t accept it.

            I rebooted the box, synced the time and ran hwclock –systohc, and let it sit for about 24 hours. It now seems to work a bit better. If I wait too long after the code changes on my phone, it still doesn’t accept it, and I have to wait for the next change, but the window is larger than when I first got it working.

            Thanks for taking the time to help though!


            1. Josh have you made sure your phones time is Synced? For Android there are a few apps you can get – for iPhone just connect to iTunes.

          2. DieSkim,

            I have a blackberry bold 9930. I am pretty sure it’s well synced, as I have to use Google 2 Factor for alot of stuff at work. The time limitations there are MUCH more strict, and I never have a problem.

            It seems to be doing ok this morning, I haven’t had any problems getting in.


  • Hey guys I am looking for reviews on using pam two factor authentication on a virtualmin live server.

    I have come across the following problems.

    1) if PAM is used then all new users (who havent setup google-authenticator) are rejected as they do not have passcodes.Assuming you are accessing your server remotely ,it means that no new users can be created without first disabling PAM and running google-authenticator command for each new user.

    2)in virtualmin when we create virtual servers we also create new users(database,ftp ssh).I wonder if enabling PAM will prevent these new users eg database users from running properly as only passwords have been created for them and they have no passcodes as google-authenticator has not been set for them.

    Guys please share your experiences.

    DieSkim ThankYou for a great tutorial ,worked smoothly on one installation, not so well on another but i guess i must have made some mistake

    1. I tested further and can confirm that new users created with virtual servers cannot connect with ftp or ssh with pam enabled.The work around is to
      1) login as root
      2) vi /etc/ssh/sshd_config
      3)change #UsePAM no to —> UsePAM no and Usepam yes to —> #UsePAM yes (i.e disable PAM by commenting out UsePAM yes)
      4)login as newuser
      5)run command ‘google-authenticator’ without quotes and say y to everything and note down the key and emergency scratchcodes or follow the link and scan the barcode with google authenticator app.
      6)now undo previous changes in step 2) and 3)
      7) sun command ‘service sshd restart’ without quotes and you will now be able to log in as a newuser with verification codes. for ftp login use filezilla with interactive login.

      If anyone knows a better way to do this (without disabling PAM) please let me know.
      Thank You.

      1. Dave – I already commented on your previous post but here is the way I use again.

        1 – Login as user with two-factor enabled
        2 – su – (change to root user)
        3 – run command ‘google-authenticator’ answer questions and then get code and install on app on phone
        4 – log in with the new user

        To get back to security – I would suggest turning off FTP completely on your server and only using SFTP – if you are going through the trouble to set up two-factor for the ssh user – why keep the huge FTP security risk if you can just use SFTP.

        Again please let me know if there is anything else

    2. Hi Dave!

      Here are a few quick answers to your questions.

      1 – That is correct all new users without google-authenticator set up will be rejected.
      For new users you would simply need to create the user, login via a user who already has google-authenticator set up and su to the new user and run google-authenticator for the user – get the code and then you can log in. There is also a second way but I will explain more of this later.

      2. Database users will run correctly – FTP and SSH will need google authenticator.

      So getting back to step one – if you really wanted you could also set up PubkeyAuthentication for users as that would be processed before OTP. The safest would be to only set it up per user and then add the per user settings to your sshd_config

      Match User username
      PubkeyAuthentication yes

      You have to remember the reason we are adding the Two Factor level is for extra security – once we start implementing Pubkey or even turning off Two Factor for some users (this can be done with a patch) we sacrifice security as we are creating security holes.

      Let me know if there is anything else I can help with!

  • I have just been trying installing this on a Ubuntu system – just the webmin part for now, and keep coming up against this error.
    When I enter the username at the login prompt the log shows this
    #### webmin(pam_google_authenticator)[##]: Did not receive verification code from user
    #### webmin(pam_google_authenticator)[##]: Invalid verification code
    #### USER perl[##]: pam_unix(webmin:auth): authentication failure; logname= uid=0 euid=0 tty= ruser= rhost= user={WEBMINUNIXUSER}

    And then upon typing in the confirmation code I get this
    #### webmin[##]: Non-existent login as unknown from {MY_IP}

    Now this looks to me like at first it is trying to do the whole process with just the username, and then just with the code – and never even getting as far as the password
    Does anyone have any suggestions?

    1. Just been carrying out further tests and there seems to als be a more general issue that it is not possible to login in with the default pam file (Before above changes) with pam_conv turned on, in the log it just shows
      webmin[##]: Non-existent login as WEBMINUNIXUSER from IP
      and on the login form it just shows login failed every time, before even entering password – has anyone else seem simmilar issues?

      1. Hi Oli, this happens to me some times on one of my servers – all I need to do is restart the Webmin service and then I can log in!

    2. Hi!

      I think it could be something to do with you not selecting unix authentication for the user.

      Webmin >> Webmin >>Webmin Users

      Then click on the user you already set the google-authenticator up for in the SSH section – root in my case.

      Change the password to >> UNIX Authentication

      Did you do that? If you are still having problems let me know!

      1. Sorry didnt get back for a while, I lost the link to this page and have been busy moving th server to another provider.
        I have the user password set to unix auth & have tried restarting the webmin server multiple times and it still insists on attempting to og in with just the usernme & then just the password – and never even getting to the code request at the moment
        Anyone got any ideas?

      2. Out of curiousity & frustration, I tried removing the google auth part in pam and attempting pam_conv with just username & pass nd it looks like this is nothing to do with the two factor auth, just a bug in pam_conv, as it will not let me log in with just username & pass with conversations enabled. I have opened a webmi bug tracker for this and will see what comes up

  • Hi,

    I am getting some errors on make command with libpam as below,

    [root@server libpam]# make
    gcc –std=gnu99 -Wall -O2 -g -fPIC -c -fvisibility=hidden -o pam_google_authenticator.o pam_google_authenticator.c
    pam_google_authenticator.c:50:31: error: security/pam_appl.h: No such file or directory
    pam_google_authenticator.c:51:34: error: security/pam_modules.h: No such file or directory
    pam_google_authenticator.c:82: error: expected declaration specifiers or ‘…’ before ‘pam_handle_t’
    pam_google_authenticator.c: In function ‘log_message’:
    pam_google_authenticator.c:85: error: ‘pamh’ undeclared (first use in this function)
    pam_google_authenticator.c:85: error: (Each undeclared identifier is reported only once
    pam_google_authenticator.c:85: error: for each function it appears in.)
    pam_google_authenticator.c:86: warning: implicit declaration of function ‘pam_get_item’
    pam_google_authenticator.c:86: error: ‘PAM_SERVICE’ undeclared (first use in this function)
    pam_google_authenticator.c: At top level:
    pam_google_authenticator.c:113: error: expected ‘)’ before ‘*’ token
    pam_google_authenticator.c:124: error: expected ‘)’ before ‘*’ token
    pam_google_authenticator.c:136: error: expected ‘)’ before ‘*’ token
    pam_google_authenticator.c:263: error: expected ‘)’ before ‘*’ token
    pam_google_authenticator.c:325: error: expected ‘)’ before ‘*’ token
    pam_google_authenticator.c:377: error: expected ‘)’ before ‘*’ token
    pam_google_authenticator.c:414: error: expected ‘)’ before ‘*’ token
    pam_google_authenticator.c:464: error: expected ‘)’ before ‘*’ token
    pam_google_authenticator.c:513: error: expected ‘)’ before ‘*’ token
    pam_google_authenticator.c:540: error: expected ‘)’ before ‘*’ token
    pam_google_authenticator.c:626: error: expected ‘)’ before ‘*’ token
    pam_google_authenticator.c:642: error: expected ‘)’ before ‘*’ token
    pam_google_authenticator.c:763: error: expected ‘)’ before ‘*’ token
    pam_google_authenticator.c:772: error: expected ‘)’ before ‘*’ token
    pam_google_authenticator.c:806: error: expected ‘)’ before ‘*’ token
    pam_google_authenticator.c:862: error: expected ‘)’ before ‘*’ token
    pam_google_authenticator.c:896: error: expected declaration specifiers or ‘…’ before ‘pam_handle_t’
    pam_google_authenticator.c: In function ‘invalidate_timebased_code’:
    pam_google_authenticator.c:899: warning: implicit declaration of function ‘get_cfg_value’
    pam_google_authenticator.c:899: error: ‘pamh’ undeclared (first use in this function)
    pam_google_authenticator.c:899: warning: initialization makes pointer from integer without a cast
    pam_google_authenticator.c:910: warning: implicit declaration of function ‘window_size’
    pam_google_authenticator.c:973: warning: implicit declaration of function ‘set_cfg_value’
    pam_google_authenticator.c: At top level:
    pam_google_authenticator.c:1018: error: expected ‘)’ before ‘*’ token
    pam_google_authenticator.c:1148: error: expected ‘)’ before ‘*’ token
    pam_google_authenticator.c:1218: error: expected ‘)’ before ‘*’ token
    pam_google_authenticator.c:1260: error: expected ‘)’ before ‘*’ token
    pam_google_authenticator.c:1292: error: expected ‘)’ before ‘*’ token
    pam_google_authenticator.c:1327: error: expected ‘)’ before ‘*’ token
    pam_google_authenticator.c:1570: error: expected ‘)’ before ‘*’ token
    pam_google_authenticator.c:1573: error: expected ‘)’ before ‘*’ token
    pam_google_authenticator.c:1578: error: expected ‘)’ before ‘*’ token
    pam_google_authenticator.c:1581: error: expected ‘)’ before ‘*’ token
    pam_google_authenticator.c:1586: error: expected ‘)’ before ‘*’ token
    pam_google_authenticator.c:1589: error: expected ‘)’ before ‘*’ token
    make: *** [pam_google_authenticator.o] Error 1
    [root@server libpam]# pwd

    I am running with CentOS 5.9. How can I solve this problem?

    1. Hi! It looks like everything did not download when you were downloading the files from Github – have you tried to repeat the process from the beginning? Did any of the steps before this on give errors?

    1. Glad you liked it! Let me know if you have any other Virtualmin, SSH, Google Authenticator or Security questions!

  • I followed this and i can still login via ssh without being prompted at all for any token information. Am i missing something here?

    1. Hi Brian, I am glad you found my guide on setting up Two-Factor SSH authentication via Google Authentication for Virtualmin and CentOS!

      Have you made sure that
      1. You have added the “auth required” line to the top of /etc/pam.d/sshd ?
      2. You have added “ChallengeResponseAuthentication yes” and removed “ChallengeResponseAuthentication no” from /etc/ssh/sshd_config ?
      3. You have restarted SSH
      4. Have you run “google-authenticator” as the user you are trying to add Two Factor Authentication for?

      Let me know if you still need help!

  • I’m trying to get this to work on CentOS6.3 32bit and can’t quite get there.

    I’m only trying to use the SSH part of the guide.

    I have compiled the GIT repository with no issues. I have run and generated a google authenticator secret key for my root user.

    The issue comes when I connect via SSH, basically putty is telling me it will use “keyboard-interactive authentication.” but instead of asking for verification code, it’s asking for password. And anything I enter is rejected.

    I have verified /etc/pam.d/sshd and /etc/ssh/sshd_config multiple times to match what is show in the guide.

    I’ve checked to make sure there were no other “ssh” files in /etc/pam.d/ so no conflict can arise.

    At first I tought it could be some sort of default lock for root accounts. But it doesn’t seem like it.

    This is a clean install of CentOS 6.3 32bit, running on Virtualbox.


    1. Ok,

      I’ve looked at some logs, and found this:

      localhost sshd(pam_google_authenticator)[17997]: Failed to read “/root/.google_authenticator”

      After a google search, I ended up here:

      From the 2nd post by Daniel I ran the chcon line, and now it all works… seems like a long discussion over there, so I might have to read it to make sure I didn’t render my system unsafe.


      1. Hi Dominique,

        I am glad you got this working – but I am not sure that you really needed to run that command.

        Do you have setenforce set to 0 on your server or not?

        Lets me know and then we can take it from there.


    1. Jim,
      I am really glad I could help you out!
      Thanks you for the link back to my site on your blog!

  • So i have tried following this and a few other guides .. and no go .. my server still allows me to just login with only the password.. Although i did keep pubkey on ..

    Would really like to see get implemented .. any help would be great.
    CentOS 6

    feel to ask for more details

    1. Hi,

      Make sure that you have edited
      vim /etc/pam.d/sshd
      vim /etc/ssh/sshd_config
      as shown – also make sure that you do not have any other ssh files in /etc/pam.d

      Also make sure that you SU to the user you want it to run on and then run the google-authenticator command.

      Let me know if you still cant get it to work!