Tuesday, April 10, 2018

Providing Two-factor Authentication For VMware Horizon 7.3.1 With Google Authenticator And FreeRADIUS on Ubuntu 16.04

While looking for a free RADIUS solution for my VMware Horizon lab I came across this white paper, "How To Setup 2-Factor Authentication In Horizon View With Google Authenticator."  While I was able to stand up the solution detailed in this white paper, holly cow, it was a lot of work.  Between the Ubuntu administration, the version changes and name changes, it was pretty rough.   So I documented my entire setup process and put together this updated guide.   I think a typical VDI admin could stand this solution up in an hour or two by following this procedure, assuming they had all the required access rights and resources lined up.

Here's a basic outline of the steps required to integrate Google Authenticator with VMware Horizon through FreeRADIUS.  Further below are the detailed procedures.

Ubuntu Setup
     Install Ubuntu Desktop 16.04 in VM
     Update Ubuntu image and prepare for domain membership
     Join system to domain with PowerBroker Identity Services Open (PBISO)

Setup Google Authenticator
     Install Google Authenticator
     Integrate with OpenSSH (Optional)

Setup FreeRADIUS
     Install FreeRADIUS
     Integrate FreeRADIUS with Google Authenticator

Horizon Integration
     Create Client Entry On RADIUS Server
     Configure Connection Server as RADIUS client
     Configure UAG as RADIUS client

Brief Overview

Ubuntu is used to run an open source RADIUS solution, FreeRADIUS.   This instance of FreeRADIUS is integrated with a local install of Google Authenticator, then configured to act as a RADIUS server for a Horizon Connection server.   The end result is two-factor authentication for our Horizon environment for free.  Directly below is an excellent graphic that represents how Google Authenticator works.   Then below that is my own rendition of what the entire integration with VMware Horizon and UAG looks like.

2FA With Google Authenticator:


Putting It All Together:

Ubuntu Setup

Install Ubuntu Desktop 16.04 In VM

Download Ubuntu Desktop 16.04 from here: https://www.ubuntu.com/download/desktop   (Most recently, I downloaded 16.04.4-desktop-amd64.iso.) 

Upload the ISO to a shared storage location easily accessible from the VM you're creating.

With the vSphere Web Client create a new VM in a location that's appropriate. When it comes time to select a a guest OS type go with Linux, Ubuntu Linux (64-bit).

In terms of basic specs for the VM, go with 2vCPU, 2 gigs of RAM and 16 gigs of space.

Most importatnly, configure the VMs CD rom to point to the Ubuntu ISO and ensure it's configured to, "Connect At Power On."  You're going to boot from that ISO to proceed with the upgrade of Ubuntu. If everything goes properly, at power on you'll see the Ubuntu welcome menu within the newly created VM.

Proceed with the, "Install Ubuntu," option.   Walk through the wizard, selecting keyboard type and time zone. Chose to go with "Erase disk and install Ubuntu," as the installation type.

Finally, the hardest part of the setup.  Give the computer a name and setup your admin account.  Stick with the option, "Require my password to login."

At this point, the wizard complets and you've got your Ubuntu OS installed.

Updating The Image

Update the Ubuntu OS with:

sudo apt-get update
sudo apt-get dist-upgrade

Then, install vmware tools with:

sudo apt-get install open-vm-tools

Next, install NTP with:

sudo apt-get install ntp

Finally, while we're at it, we can setup OpenSSH as well. 

sudo apt-get install openssh-server

Preparing For Domain Membership 

To enable domain membership we need to confirm that networking and DNS is all sorted out.  To that end, it's a good idea to assign a static IP address to our new system and manually create a DNS record for it.  From there we can use ping and nslookup to ensure everything's straight.

You can configure a static IP address using the Edit Connections option at right hand corner of screen.  

From there you enter in the specifics of this network connection.  Here's what the system in my lab looks like:  

Click save and disable\enable the nic for the new settings to kick in.

Upon initial setup in my own lab I ran across a really strange DNS issue.  While I was able to ping domain members by their short name, for some reason DNS resolution of the FDQN was failing.  Here's an example.

I was able to get this issue resolved by editing /etc/nsswitch.conf, changing the hosts: line so that it includes only files and DNS.

After making this change the FQDN started resolving properly.

Along with testing network connectivity and DNS resolution from the Ubuntu system to all relevant systems in the domain, I would also confirm that the domain controller you're going to join can ping and resolve the Ubuntu system. 

Finally, for extra credit, I would use nslookup on Ubuntu to confirm that a lookup against the target domain yields the addresses of domain controllers.  

nslookup domain_name

Joining Ubuntu To Active Directory

With network connectivity and DNS resolution all squared away, you can proceed with joining the system to the domain.   Download the PBISO solution from https://www.beyondtrust.com/products/powerbroker-identity-services-open/     Extract the files, make the script executable and then execute it.

chmod +x pbis-open-

sudo ./pbis-open-

At this point, with power broker installed, you can proceed with adding the system to the domain using domainjoin-cli.    Here's the syntax:

sudo domainjoin-cli join [DomainName [DomainAccount]

So, to join my lab's domain, lab.local, I executed the following:

sudo domainjoin-cli join lab.local justin@lab.local 

After successfully joining system to domain, you'll see an account for it in AD:

After a reboot of your system you now have the option to login with a domain account over SSH.

It's typically suggested that you setup default configuration for domain users by running special  commands for PowerBroker.   Here's the syntax:

sudo /opt/pbis/bin/config AssumeDefaultDomain true
sudo /opt/pbis/bin/config LoginShellTemplate /bin/bash
sudo /opt/pbis/bin/config HomeDirTemplate %H/%U

sudo /opt/pbis/bin/config UserDomainPrefix SHORT_DOMAIN_NAME

Adapted for my lab domain, lab.local , the commands look like this:

Finally, you need to add the following lines to  /usr/share/lightdm/lightdm.conf.d/50-unity-greeter.conf:


After adding these entries to 50-unity-greeter.conf and rebooting you can login with your AD credentials directly through the Ubuntu login screen. 

Setting Up Google Authenticator

To setup Google Authenticator on your Ubuntu system, type:

sudo apt install libpam-google-authenticator

At this point, you can begin enrolling users who have the Google Authenticator mobile app on their phones.   They should be able to download the app from their device manufacturers app store.

On the ubuntu system login as root.   Then use su to switch to whatever user you want to enroll, and run "google-authenticator," under their context.

su domain_user_you_wish_to_enroll


You'll initially be prompted if you want tokens to be time based.   Select yes.  Afterwards, a QR code will be displayed.

Now, open up Google Authenticator and select begin, choosing the Scan barcode option,

Position the green box overlay over the QR code. 

When your scanner captures the QR code, the authenticator app flashes and you see a new entry for the user being enrolled.  (For extra credit and some healthy fun, if you have Google Authenticator setup on your phone, try scanning the QR code of the image above.  You'll have the expired key of vditest@freeradius.)

After successfully scanning from your phone, ensure you complete the wizard on your system to complete the users enrollment. I usually answer y, y, y, n.

Integrating Google Authenticator With OpenSSH

Add the entry, "auth required pam_google_authenticator.so",  to end of the file, /etc/pam.d/sshd.  So: 

sudo vi /etc/pam.d/sshd

And add:

auth required pam_google_authenticator.so

(Note: For all the editing in my environment I use vi because I'm relatively comfortable with it.  You could just as well use nano.)

After making the edits above, we can now test using ssh.   Taking that ubuntutest account we enrolled earlier, I ssh into the box and at first receive a prompt for my normal AD password.  After entering in my password, I'm prompted for my verification code.  

Looking at Google Authenticator I see the current verification code associated with the ubuntutest account.   After plugging that code in I'm successfully logged in over SSH.  

Now we know that Google Authenticator is working on the system and we can move on to standing up FreeRADIUS.

Setup FreeRADIUS

To install FreeRadius, the command is: 

sudo apt-get install freeradius

Say yes to the continue prompt and let the install complete.

Next, we have to make an edit to /etc/freeradius/radiusd.conf.  Where it says:

users = freerad
group = freerad

Replace freerad with root, so that it looks like:

Next, we need to make an edit to /etc/freeradius/users.  We need to add the line:

DEFAULT Auth-Type := PAM

Next, we need to make an edit to /etc/freeradius/sites-enabled/default to enable PAM.  Simply uncomment out the line #PAM, so that PAM is enabled.   

Finally, we need to make an edit to /etc/pam.d/radiusd.  We need to comment out all the @include lines, and then add the following two lines:

auth requisite pam_google_authenticator.so forward_pass
account required pam_unix.so use_first_pass

Finally, for easier troubleshooting, I enable additional logging by making edits to the /etc/freeradius/radiusd.conf under the log { directive.   These include

auth = yes
auth_badpass = yes
auth_goodpass = yes 

Restart the radius server for these changes to take affect.

service freeradius restart

Horizon Integration

For the integration with Horizon, we have two steps.  First, we must configure a radius client entry for the Horizon connection server on the RADIUS server.   In my environment, the Connection server is accessible from horizon.lab.local, so I've configured this section of the /etc/freeradius/clients.conf accordingly.  Heres's a screen shot:

Restart FreeRADIUS after making this change

service freeradius restart

Next, you need setup the Horizon Connection server to connect to the RADIUS server as a client.  Go to View Configuration --> Servers -->  Connection Servers --> Select-Your-Connection-Server-And-Edit --> Authentication tab. 

Enable RADIUS for 2-factor authentication and click on the box, "Manage Authenticator."

Then select the option to Add an Authenticator.  Here we're going to configure the RADIUS client settings that will be used by the Horizon Connection server to make request from the FreeRADIUS server.  The basic settings required are a label, hostname, authentication port,  accounting port and authentication type.   For this solution we need to choose PAP as the authentication type and go with 1812 for the authentication port and 1813 for the accounting port.   The shared secret is whatever has been configured in the FreeRADIUS clients.conf.  

Click next and hit okay.

With the wizard complete, ensure your new authenticator is selected, and also ensure you check option for user matching.

At this point you're good to go.  Fire up the Horizon View client.  For your passcode take your normal AD password and then append your Google Authenticator code to it.   Here's a great graphic that details the format:

Below is what the initial login to my lab environment looks like.  I enter in my password with a 6 digit authenticator code appended to it.

Then I get the traditional Horizon credential prompt.   You simply type in your normal AD password at this 2nd prompt.

And you're in.

Integrating With UAG 

Integrating UAG with with FreeRADIUS isn't much different than integrating a traditional Horizon Connection server.   First, you create an appropriate RADIUS client entry with FreeRADIUS clients.conf, just as we did earlier for the Horizon Connection server.   Then login to admin gui of UAG.  After the initial login, choose the option for Configure Manual.  

Then under general settings, expand out and select the gear icon for RADIUS.  

At this point, we're going to populate the same basic info we did on the connection server, RADIUS server name, shared secret, etc..  

After populating all the info required for Horizon to issue requests to the RADIUS server as a RADIUS client, you're up and running.   


  1. Hello,
    I would like to congratulate you on a very well written freeradius 2FA document. After spending weeks of research, i found your article. Very helpful , straight forward and just works. I support you 100% and would like to see more like this. I appreciate your time put into this. Thank you!

  2. Hello Justin,
    How would you suggest integrating domain groups so only user belonging to certain group would be allow to authenticate. Thank you !

    1. I think page 8 and 9 of the original guide is what you're looking for. https://blogs.vmware.com/consulting/files/2015/02/VMW_15Q1_TD_Horizon-View-Google-Authenticator_021715_FINAL_EMonjoin.pdf

  3. As soon as I add "DEFAULT Auth-Type := PAM" to the client file, the FreeRadius (3.0) service on my Ubuntu 18.04 server will no longer restart. It failes with "failed to start freeradius multi-protocol policy server." Any idea how to address this problem?

    1. I suspect you may not have uncommented the line with PAM on it in /etc/freeradius/sites-enabled/default. A good way to find out more info would be to run freeradius in debug mode, freeradius -X. Here's some more info: https://networkradius.com/freeradius-debugging/

    2. I am having the same issue where I am unable to get the service to restart. The error I am receiving is "Please verify that the configuration exists in /etc/freeradius/3.0/mods-enabled/pam." I am using ubuntu 18.04 and followed your guide but adding or editing this file was not mentioned. I am thinking the version of freeradius that is shipping now is newer than the one that you did with your guide. Do you have any idea what would need to be changed?

    3. Did you ever get this figured out? I am experncing issue user trying to scan bar code saying it is not valid.

    4. If you're having trouble getting FreeRADIUS to start, I would definitely try running FreeRADIUS in debugging mode:

      $ radiusd -X > debug.txt


    5. try ln -s /etc/freeradius/3.0/mods-available/pam /etc/freeradius/3.0/mods-enabled/pam

  4. I have a question: is there any option to connect if your Phone don't have Internet or can't open the Phone?
    Can we use emergency code instead? or the other way?


    1. Phat, when you initially set up a user on Google Authenticator they're provided with emergency scratch codes. I've never played with them, but I think that's what you're looking for. If you look under the section of this post, "Setting Up Google Authenticator," you can see a screenshot during the enrollment process that displays emergency scratch codes.

  5. will this work with latest ubuntu? 18.04

    1. I'm going to upgrade ubuntu in my environment within the next couple weeks and post the results.

  6. I installed the free radius but not find the radius.conf to edit its completely blank

  7. I get invalid barcode when trying to scan say the barcode 'otpauth://totp/tc\user@freeradius?secret=39849374973 is not a valid authentication token barcode

  8. Hey everyone, I did an in place upgrade of my working environment from 16.04 to 18.04 and it broke FreeRadius for me. Seems to have blown out the FreeRADIUS installation entirely, replacing a 2.x version with a 3.x version that includes different paths. I'm optimistic I can get this issue resolved with some research and elbow grease, but don't have the bandwidth right now. If someone figures out a solution I'd love the input. Otherwise, I'll put some more work into this Feb of 2019.

  9. Justin I am having the same problem. Please let me know if you find anything

  10. Hi all,
    I have a question, how to syn user active directory with freeradius and google authenticator?
    Note: I have not used local user (root);

  11. Hi Justin,
    I'm writing an update of my white paper in order to include Ubuntu 18.04, UAG and vIDM, stay tuned :)

    1. Would love to see a new white paper Eric. I wrote this post just over a year ago and it's already out of date. There seems to be plenty of appetite out there for a free 2FA solution for Horizon so I think getting an updated procedure out there is a worthy objective.

  12. Hello Justin,

    I was able to recreate this following your instructions. I setup a 2FA server for Horizon View 7.7 environment with Ubuntu 18.04 Bionic Beaver LTS, pbis-open- 64.deb.sh, Free Radius 3.0, Google Authenticator, and UAG 3.5. Your white paper gave me the confidence to try this on my own. I just had to update the commands to reflect the software difference but it does work. I also found out 18.04 is locked down pretty good. So I had to use GEDIT to edit the files instead of vi or nano. Most of my issues were locked down files or misconfigurations on the UAG. Don't forget to take the cert from the connection server and import it into the UAG.
    I look forward to reading the new white paper. I may even blog all this fun I had setting up the environment. Props to Justin and Evengooder.com
    Matt D.

    1. Thanks for the feedback Matt! If you do end up blogging about this please post a link to it here.

  13. Great article, the only issue I have is, I have 2 connection servers with a DNS entry of horizon, I've added horizon as the radius client entry for the connection server in clients.conf as described which works, however it seems to depend on which connection server I hit, if I hit one it works, if I flush my dns cache and hit the other connection server I get an access denied message. Any suggestions? or should I just have one horizon DNS entry pointing at one of the connection servers.

    1. Do you have RADIUS enabled and a proper authenticator created on BOTH of your Horizon servers?

    2. Justin Thanks for the article. I was missing a link to /etc/freeradius/3.0/mods-enabled/pam from /etc/freeradius/3.0/mods-available/, once that was created, all was well.

  14. Same here I was missing a link to /etc/freeradius/3.0/mods-enabled/pam from /etc/freeradius/3.0/mods-available/, once the symlinc was created it started properly

  15. Is possible to have multiple domains on the radius servers to authenticate from ? Thank you

  16. Hi there,

    Auth works with Horizon Client Only. HTML Access gives Authentication failure with same credentials and not timebased Authcode...

    Does HTML-Access need some more configuration?

    1. It works fine for both client and html access with the same configuration

  17. thanks, yes got it working with html access now.

  18. Another question:)

    We configured Radius and GoogleAuth in the UAG now. But if we deactivate GoogleAuth on the Connectionserver and only have it active and configured in the UAG it does not work at all. Is this how it should be? Must it be active on the Connectionserver to work? Are we missing something?

  19. EvenGooder, awesome work on this!
    I'm running Ubuntu server 18.04.4 and had followed older instructions but hadn't managed to get the freeradius service to restart "service freeradius restart".

    Using "journalctl -xe" or "freeradius -X" got the error:
    /etc/freeradius/3.0/sites-enabled/default[513]: Failed to find "pam" as a module

    Resolved by running "ln -sf /etc/freeradius/3.0/mods-available/pam /etc/freeradius/3.0/mods-enabled/pam"

    Not sure if it's just me but if I modify anything in the "Manage Authenticators" I seem to lose the shared secret so have been bashing it back in each time.

    I'm on Horizon 7.10 and have noticed that I can enter my username and the Google Auth token on the first logon screen, then on the second logon screen just my domain password. I see in your example you are doing Google Auth Token+Password.

    I know very little about all this but when tailing the radius log I didn't like seeing my domain password!
    "tail -f /var/log/freeradius/radius.log"

    Fri Apr 10 00:38:04 2020 : Auth: (59) Login OK: [gerg.one@lab.local/MyDomainPassword521440] (from client gerg-con01.lab.local port 1)

    My using my Google Auth Code I now see
    Fri Apr 10 00:44:37 2020 : Auth: (73) Login OK: [gerg.one/411139] (from client gerg-con01.lab.local port 1)

    Hope this helps others.


  20. EvenGooder, Thanks for the wonderful documentation. I just wanted to know if there's a way to enable enrollment portal for AD users to register for QR code generation?


  21. hello, I also found a portal where or I want users to automatically scan their qr code, as well as how to specify the domain groups to use the verification, if anyone knows about this, I would appreciate it

  22. Hello, I am having issues in configuring freeradius on Ubuntu 22.04. I am not able to restart it.

    Job for freeradius.service failed because the control process exited with error code.
    See "systemctl status freeradius.service" and "journalctl -xeu freeradius.service" for details.

    1. Indeed, you have to use Ubuntu 20.04 at the latest. With 22.04, Freeradius refuse to start if run as root.