One of our current projects involves using VMWare Infrastructure (specifically ESXi Server and VirtualCenter) to create a large number of VMs for users to log into and test out our educational environment without the hassle of traveling to our office just to see what we're talking about.
We put the entire infrastructure behind an SSL-VPN which made it simple to sync with our Windows Active Directory backend, so far so good. VMWare's VirtualCenter also makes it very simple to generate URLs to interface with a virtual machine through a web browser, but VMWare doesn't provide a method of interfacing with a VM from within the web browser, which means that after a user has logged in through the SSL-VPN they have to type in their (same) credentials again to gain access to the console of the virtual machine.
While this isn't really a problem, per se, it is very annoying. It's even more annoying when you're doing testing and have to keep typing the same username and password (in pairs) every time you change something and want to log in again. VMWare's recommendation is to use an SSO Broker service, such as Leostream, which will transparently handle all the Single Sign-On details for you. While an SSO Broker is definitely an option, it seems a bit of overkill for what I'm trying to do, and also adds cost to project which I'm trying to avoid.
So I decided to make my own Single Sign-On method using the VMWare Perl SDK, PHP and jQuery.
Background
I'll explain a little about the environment I was working in to make it easy to know if this kind of thing will work with your own VMWare project.
The entire VMWare environment is behind an SSL-VPN, which means that when users browse to the page they are presented with a username/password prompt and they must log in. Once the user is authenticated I capture and store their username and password in the PHP SESSION. I'm sure there are more secure ways to do this, but this was the most direct path to a solution so it's the way I chose.
After logging in users are transferred to a portal page. I'm actually using 2 pages for the users:
- a page that lists the VMs currently assigned to them (using the VMWare Perl SDK)
- a page with various pieces of educational information, that has the virtual machine embedded in an <iframe>
The users are first sent to a portal page, which generates the VirtualCenter URLs needed to access the console through a web browser. Once the user clicks on one of the virtual machines they're sent to the next page which has the console URL embedded in an iframe called embeddedVM.
Client-Side Code
Here's the main part of the necessary code:
Most jQuery users are probably used to using the $(document).ready() line to start out
your scripts. Since the embedded virtual machine is actually a Java applet, we have to wait until the
entire window is finished loading to know that all the elements are in place. Attempting to run the next
lines before everything is loaded doesn't work.
- embeddedVM is the id of my iframe, the quotes and # are necessary parts of the jQuery selector
- contents() returns the HTML inside embeddedVM
- find() allows us to search through the returned HTML for certain tags
- input[name^='userName'] is a regular expression, search for any input elements that contain the phrase "userName"
- .val() allows us to set the value of the selected field. In this case I use PHP to output the $username and $password variables extracted earlier
- .click() is used in the final statement to actually click the "submit" button to log in to the VM
The above code will cause the username and password entered initially to be filled in to the VMWare form and submitted, automatically logging the user in. Because it's all done dynamically, and client-side, users will notice a quick flash where their username will appear on the screen before the page reloads. This is fairly minor, but I didn't want our users to be confused if they started to fill in the form and then it automatically submits (especially if they're on a slower connection) with no feedback as to what's happening.
The final step I took is using a jQuery plugin called BlockUI to block input to the iframe for a set amount of time to give the user some idea of what's happening. BlockUI will create an overlay, with an optional message, over a page, or certain areas of a page. My embeddedVM is inside a div called vmContainer.
My final code looks like this:
This code blocks the vmContainer div with a message reading "Please wait..." as soon as all the page elements are in place. Once the window finishes loading, our script inputs the username and password, submits the form, then waits 5 seconds and removes the block. 5 seconds is an arbitrary number but, in testing with our setup, 5 seconds was more than enough time to allow the applet to appear and start loading, so the users had a good idea that something was happening.
I am not a VMWare or jQuery expert, so if you've done this in a simpler way (or know of a more efficient way to write that code), please feel free to leave a comment.
Great article.
The funny thing about it is that when we started out with our first real deployment of a Connection Broker (for the USPTO) we did not even call it a Connection Broker, and we did not think it was really a product - just a feature.
So, yes, you are on the right path. What I would do next IMHO is dynamically create a webpage with the Microsoft ActiveX RDP client.
That way you can log into the SSL VPN ( we support Cisco, Juniper, etc but you can use an open source one), and then get automatically signed into a RDP session in your web browser connected directed to the VM.
After that it is just coding ;-)
Good luck
Cheers
David
Posted by: David Crosbie (CTO Leostream) | December 05, 2008 at 05:12 PM
@David:
Thanks for the comment. We were talking to a VMWare engineer about how to manage single sign-ons for our environment and they mentioned your company right away. Seems like you offer a lot of great services (beyond just what I've replicated with some quick Javascript).
And definitely thanks for the pointer on where to go next, that actually helps me with the next direction for my code.
- dmo
Posted by: dmo | December 06, 2008 at 07:53 PM