Saturday, May 28, 2011

Cisco AnyConnect VPN Client on 64-Bit LinuxMint 11

I've posted before about getting Cisco AnyConnect running on Ubuntu 9.10 and Ubuntu 10.04, but I've since started using LinuxMint as my daily driver and did a clean install of MInt 11 today. Mint is based on Ubuntu so on Mint 10 the previous strategy to get AnyConnect running worked fine, but I had to take a different approach after installing Mint 11. (I suspect it'll be the same issue on Ubuntu 11.04 but I haven't tried it.)

In doing a bit of research I came across this link that explains quite correctly that you don't need to actually download and extract Firefox to get this all working, which is what I had been doing previously. The Cisco client (for some stupid reason) expects certain things to be in a /usr/local/firefox directory but you can simply create that directory, download some other files, and then create the appropriate symlinks in /usr/local/firefox to make AnyConnect happy.

I also ran into some inexplicable weirdness related to a certificate file in my ~/.mozilla/firefox profile directory but I'll cover that as I outline the steps I took to get AnyConnect working.

Summary of Steps

Follow these and if you're lucky it'll work; if it doesn't read the information that follows for more details and troubleshooting ideas.



  1. Follow the steps in this blog post, which are as follows:

    1. sudo apt-get install ia32-libs lib32nss-mdns

    2. sudo mkdir /usr/local/firefox

    3. sudo ln -s /usr/lib32/libnss3.so /usr/local/firefox

    4. sudo ln -s /usr/lib32/libplc4.so /usr/local/firefox

    5. sudo ln -s /usr/lib32/libnspr4.so /usr/local/firefox

    6. sudo ln -s /usr/lib32/libsmime3.so /usr/local/firefox

    7. sudo ln -s /usr/lib32/nss/libsoftokn3.so /usr/local/firefox


  2. Download the AnyConnect installer from somewhere. The usual method of browsing to your VPN server and logging in may not work, so see below for details.

  3. Run the installer from the directory to which it was downloaded (sudo ./vpnsetup.sh). The daemon may fail to start at this point but don't worry if it doesn't.

  4. If the daemon failed to start, start the VPN daemon: sudo /etc/init.d/vpnagentd_init start

    1. You shouldn't get an error regarding /opt/cisco/vpn/bin/vpnagentd not being found at this point if you followed the above steps accurately. If you do, read on to see if any ideas come out of any of the subsequent discussion.


  5. Start the AnyConnect client. It should be in your Internet programs menu.

    1. If you get a "server certificate problem" error, stop Firefox and delete ~/.mozilla/firefox/YOUR_PROFILE.default/cert8.db where YOUR_PROFILE is whatever random string Firefox assigned your default profile (you should only have one directory with .default at the end of it in ~/.mozilla/firefox). In my case this problem didn't rear its head until after I rebooted, so you might want to reboot at the end of all of this to make sure everything's working.



If you're still getting errors read on for more info ...

Downloading AnyConnect

I ran into problems right out of the gate on Mint 11. On Mint 10 as well as previous versions of Ubuntu I could at least hit my VPN server in a browser, try to fire up the Java applet, and when that fails it prompts you to download, but this time around the "launching Java applet" screen on the VPN server just hung. I verified that Java is enabled in Firefox and tested with other applets so I'm not sure what the issue is there, particularly since this did work on my 32-bit machine with Mint 11.

So word of caution: you need to get the installer elsewhere, or at least I did. There may be a solution to this I haven't yet come up with so if you know what's up here, please be sure and comment.

Luckily I had the installer backed up from when I copied my home directory to an external hard drive prior to installing Mint 11, so I ran the installer from my home directory.


sudo ./vpnsetup.sh

This at least got the daemon installed for me, but it failed to start after installation (usually it starts fine after it's installed), throwing an error about /opt/cisco/vpn/bin/vpnagentd file not being found. The file's definitely there so I'm not sure what its problem is, but this gets resolved in the subsequent steps so you can ignore that error for now.

Install Necessary Libraries and Create Symlinks

See the above steps for details (all the steps under #1 above). In my case this resolved the file not found error the daemon was throwing when I tried to install AnyConnect prior to creating those symlinks. If you do that step first everything should work.

Launch the VPN Daemon


sudo /etc/init.d/vpnagentd_init start

If that throws errors doublecheck all the symlinks you created above. Note that in previous versions one of the things you were supposed to install and symlink to was sqlite3.so but that does not seem to be necessary.

Launch the AnyConnect Client

You should now be able to launch AnyConnect from your Internet programs menu. If you get a "server certificate problem" error, for me this seemed to be related to a certificate file in my Firefox profile.

How I came across this was after I rebooted and started Firefox on my 32-bit machine, since my home page is my Google Mail login, Firefox immediately threw a "Could not initialize the browser's security component" error. I found information on that error on Mozilla's site, so on GNU/Linux this means stopping Firefox and deleting the cert8.db file that's in your profile (~/.mozilla/firefox/YOUR_PROFILE.default).

On my 64-bit machine the behavior was slightly different. Everything seemed to work with AnyConnect until I rebooted, at which point it threw the server certificate error. I then launched Firefox and it popped up a completely blank alert window, but when I closed that window and Firefox finished loading, I noticed I couldn't browse to any sites. No matter what I put in the location box the top of the Firefox UI was completely unresponsive.

Since I'd happened to have the security component issue on my 32-bit machine, I figured even though on the 64-bit machine it wasn't actually showing me the error, that might be the problem. Sure enough when I deleted the cert8.db file Firefox then began to work, as did the AnyConnect client. I rebooted to make sure it wasn't a fluke and thus far everything is working.

Remaining Issues

At this point the only remaining issue is that for some reason when I connect to the VPN, AnyConnect doesn't minimize itself into that little "stacked blue balls" icon thingee over near the clock. It just minimizes itself and shows up in your task bar like any other program. Minor annoyance but it does behave correctly on my 32-bit machine so I'm not sure what's going on there.

Hope that helps some others who are trying to get this running!

Sunday, May 22, 2011

String Matching in CouchDB Views

We're in the process of porting an application that has been running on SQL Server over to the fabulous and amazing CouchDB. We were originally under the impression that everyone accessing data from this application in their own code was doing so through our web service, which would have made our job pretty simple since we could swap the guts of the web service methods out and return the same data types to the caller, but upon further investigation we discovered that people had written their own custom queries directly against the database.

This alone isn't a big deal but in some cases people were running queries that included LIKE clauses, and since we opted not to install CouchDB-Lucene given both time constraints as well as the fact that the LIKE queries against SQL Server were pretty limited in scope and number, I thought I'd share what we came up with to do string matching in views in CouchDB.

This is by no means to suggest you should not use CouchDB-Lucene if you want true full-text searching against data in CouchDB, but in our case this was an acceptable compromise.

Matching Fields That Start With a String in Couch

SQL Equivalent: "WHERE field LIKE 'foo%'"

Let's assume I have a database called test and in that database I have documents that have fields of firstName and lastName. I want to write a view that will let me do wildcard matches against first names that begin with a string.

This turns out to be pretty simple given how keys work in CouchDB map functions. Since a view emits a key and a value and we can use start and end keys in our calls to CouchDB, we simply provide the string against which we want to match as our start key and some end key that will ensure we don't get back more than what we're wanting.

For example, let's say I want to match all documents in my database that start with 'Mat' so I can retrieve all people with a first name of Matt, or Matthew, or Mathew, or Mat, or Mathias ... you get the idea.

First I write a view that in its map function emits firstName as the key:

function (doc) {
  if (doc.firstName && doc.lastName) {
    emit(doc.firstName, doc);
  }
}

Assume that my design document is 'people' and that's the map function for a view called 'byFirstName.' To call that view and get back only people with a first name staring with 'Mat' I use the following URL:

http://couch/test/_design/people/_view/byFirstName?startkey="Mat"&endkey="MatZ"

In case that wraps poorly in the blog post display, here's just the start and end keys:

startkey="Mat"
endkey="MatZ"

That tells CouchDB to start its output for that view with anything that starts with Mat and end once it hits anything that starts with MatZ.

Matching Specific Strings Contained in Fields

SQL Equivalent: "WHERE field LIKE '%KnownString%'"

We had some use cases where users had canned queries (i.e. users can't enter random search terms) that were looking for a specific term contained anywhere within a specific field. I say specific term here and in the example I use "KnownString" because if you know the string ahead of time this is a simple problem to solve, whereas ad hoc terms are more problematic, but I'll address that below.

Remember that within CouchDB views you have full access to JavaScript, so solving this use case is simply a matter of using a regex to match against the known term.

Let's say I want to pull all documents that have a bio field containing the term 'CouchDB':

function(doc) {
  if (doc.bio && doc.bio.toUpperCase().match(/\bCOUCHDB\b/)) {
    emit(doc._id, doc);
  }
}

Again, since I know the term ahead of time I can do a regex match against it quite easily in my view.

Matching Ad Hoc Strings Contained in Fields

SQL Equivalent: "WHERE field LIKE '%adHocSearchTerm%'"

Where things get tricky in CouchDB without using something like CouchDB-Lucene is matching ad hoc strings. "Tricky" is actually putting it mildly, because the real story is you can't do this in CouchDB. So in use cases where people had code that had a search box into which users could type anything, we had to come up with another solution.

What I've found as I've been using CouchDB more and more is that it can shift things that you used to do in the database layer up into the application layer, and vice-versa. So in this case it was simply a matter of coming up with a view that pulled back a subset of documents into the application code, and then doing the matching there.

One caveat here is that since our database contains thousands of documents, it wasn't really feasible to pull back all the documents in the database and then perform matching in the application layer. Since these documents all have a date associated with them, what we wound up doing is using date range as start and end keys as a way of reducing the number of documents we have to match against in the application. This wasn't a huge burden on users and certainly will improve performance.

We wound up limiting documents returned by year (i.e. the users have to choose a year in which to search), which is enough of a range to not make things too annoying for users, but is also a small enough set of documents not to kill performance on the application side.

To call the view that uses date as its key, the URL params look like this to pull back all documents for 2011 in descending date order:

?startkey="2012/01/01"&endkey="2011/01/01"&descending=true

Remember that when you order descending you essentially flip the start and end keys around, hence why 2012/01/01 is used as the start key.

Once I have the documents back, I then deserialize the JSON into something usable by CFML and then loop over the documents to do my further refinement by search term.

Leaving out the subset controlled by date I described above, assuming I wanted to find all people with a bio field that contained the search term entered by a user on a form, the code winds up looking something like this:

<cfhttp url="http://server/test/_design/people/_view/hasBio"
        method="get"
        result="peopleJSON" />

<cfset peopleReturned =
        DeserializeJSON(peopleJSON.FileContent).rows />

<cfset matchingPeople = ArrayNew(1) />

<cfloop array="#peopleReturned#" index="person">
  <cfif FindNoCase(form.searchTerm, person.value.bio) neq 0>
    <cfset ArrayAppend(matchingPeople, person) />
  </cfif>
</cfloop>

What we wind up with there is the matchingPeople array will contain only the people who had the search term included in their bio field.

The big caveat here again is that if you have a huge number of documents you can get into trouble on the application side, so make sure and limit what you get back from CouchDB since you'll wind up looping over all of those documents to do your search term matching.

Hope that helps others do some quick and dirty LIKE type queries in CouchDB. If there's a better way to do any of these I'm all ears!

Thursday, May 12, 2011

Very Simple Fix to Enable USB Device Support in VirtualBox on GNU/Linux

I finally got motivated enough last night to look into why I could see my USB devices in my Windows 7 VM but they were all grayed out, and came across the very simple solution:
http://news.softpedia.com/news/How-to-Fix-VirtualBox-USB-Support-111715.shtml

Short answer is add your user to the vboxusers group, log out and log back in, and you can access all your USB drvices in your VM.

Monday, May 9, 2011

cf.Objective() NoSQL BOF

Heads up that on Friday night of cf.Objective() I'll be facilitating a BOF on using NoSQL databases with CFML, so if you're interested in things like CouchDB (my favorite thing on the planet as of late), MongoDB, or any of the numerous others please come to the BOF!

All skill levels are welcome so come to learn, come to share what you've done, or come to mock crazy people like myself who think the relational model is the biggest hoax ever perpetrated on the technology world and that we should have been using document-based datastores all along. Yes, that statement is meant to incite you to come to the BOF if you think I'm wrong, but I do believe it to a certain extent. ;-)

When I say I'll be facilitating a BOF I mean just that--BOFs are meant to be highly participatory, free-form discussion forums, so while I'm happy to show off what I know about CouchDB, I'd personally love to learn more about some of the other NoSQL databases from people using those, and would love to have some heated discussions about NoSQL in general.

See you Friday night at 8 pm!

Prerequisites For My cf.Objective() Presentation on Tomcat

Quick note to anyone planning to attend my "Running Multiple CFML Engines on Apache Tomcat" talk at cf.Objective() -- even though this is only a one-hour session, with just a bit of prep work you can easily turn this into a hands-on session since I only have a few slides and it will be mostly demo. You don't have to follow along to get a ton of great info from this session, but if you want to follow along please grab the following ahead of time:



Some additional notes:



  • You do NOT need to install Tomcat ahead of time

  • You SHOULD install Apache ahead of time

  • If you want to use Adobe CF as one of your engines, you'll want to run the installer ahead of time and for the installation type choose "generate a WAR file" and have that available on your laptop. Note that even if you have Adobe CF installed on your machine already, you can run the installer again and generate a WAR file without affecting your existing installation.

  • For Open BlueDragon and Railo, grab the WAR files and have those handy

  • Your operating system doesn't matter--all the Tomcat stuff is pure Java, so whether you're on GNU/Linux, Windows, or Mac it's all good.


If you have questions/concerns ahead of time please comment here or email me. See you at cf.Objective()!