Featured Posts

The 10 Worst Things about World of Warcraft - Mists... I've been playing WoW since vanilla version starting in 2006.  Except for a six-month hiatus in late 2011, I've been a daily player.  I've seen multiple patches come...

Read more

Best Breakfast Burritos, ever! I like eating a good breakfast, usually around lunchtime once I've had my fill of coffee and am awake enough to appreciate a good breakfast. This is my recipe for my ultimate...

Read more

Testing Arrays in PHP - Back to Basics... Sometimes, when you're wallowing through your abstraction class layers, you find yourself using code for simple functions that are normally the focus of an Intro to Programming...

Read more

PHP: Comparing Object Structures I'm working on a project where I am converting an established REST API over to a rabbitMQ service.  Because, you know, dinosaur, I'm continuing to use PHP as my language...

Read more

Mountain Lion and Tunnelblick - Playing Nice Together One of the things that requires some tweaking after the installation of Mac OS X (Mountain Lion) is Tunnelblick, a free and open-source GUI for openVPN.  I use Tunnelblick...

Read more

Subscribe

Configure an External RAID-1 USB Drive for Backups – Ubuntu 12.04

Category : Technical
No Gravatar

I have a WD USB 3.0 500gb hard drive for my work laptop who’s sole purpose in life is to record back-ups of my Ubuntu 12.04 system.  I’ve never formatted the device and it’s always worked very well for me despite my cavalier treatment: tossing it around in my backpack during my commute mostly.

I use a program called back-in-time for my backups and I like it for it’s ease-of-use coupled with a healthy sense of fire-and-forget.

Recently, my back-ups started failing with generic write-fail errors so I committed to re-formatting the device.  Since I was going to nuke it, I decided to implement RAID-1 during it’s resurrection just to have an “extra” copy of my back-ups.  The partition I back-up is only 100gb — which is currently about 55% full — so splitting the device into two logical drives and configuring under RAID shouldn’t be a problem, right?

I mean, I have absolutely NO experience with RAID so how hard can this be?

After a few false starts, I honed down the process and this is what I’m sharing with you today.  Oh, and as side note — in the first paragraph, I linked to this particular drive on Amazon as a courtesy — I’m not getting and referrals or anything like that…just thought you might want to see what we’re dealing with.

Phase 1 – Installation of Tools

Ok – so, to get started, I needed to install mdadm which was pretty easy:

# sudo apt-get install mdadm

What was somewhat confusing was the need for mdadm to install a mailing agent – for the monitoring tool, but during the installation I elected to not configure the mail program and everything still went smoothly.

Next, I installed gparted and all of the available options through the Ubuntu Software Center app.

I already had back-in-time installed and I provided the link above.  Use the Ubuntu Software Center to install this app if you don’t already have it.

With all the software in-place, it’s time to configure the device.

Part 2 – Wiping and Configuring the Device

Plug your device into your system and wait for it to automount — which it will do if it still has the factory-equipped partition installed.  Once it’s installed, go to your command line and unmount the device:

# umount /dev/{your_mount_point_here}

Either sudo this command, or sudo over to root.  I chose the latter as I get tired of forgetting to sudo.

Once the device is unmounted, start-up gparted,, authenticate, and wait for gparted to stop scanning for devices.  Change your device over to your external drive which was, for me: /dev/sdb.

(Side note: The WD drive comes with it’s own software which just swell if you’re a Windows or a Mac user.  Otherwise, the software is just taking up space.  Copy these programs if you want but you can always download replacements from the WD website.)

For me, the drive was formatted into one single, large, partition.  Delete this partition and don’t look back.

Next, split your partition into two smaller partitions, equally dividing the available space between the two.  RAID-1 is mirroring – you’re creating two logical partitions in this device but you will mount it (the system will see it) as a single device.  Data is written to the first partition and RAID copies the data over to the second partition, mirroring the data to the second logical partition.

I chose the ext3 filesystem for my partition simply because:

  • it’s better than ext2
  • it’s robust
  • I don’t believe I’ll get any speed benefits from ext4 (USB, eh?)
Make a note of the names of the logical partitions (Mine was /dev/sdb1 and /dev/sdb2.) as you’re going to need to know this later.

Note too that you’re not actually doing anything at this point – you’re just building a task-list for gparted to execute when you’re finished creating tasks.

Next, flag each partition as a RAID partition by right-clicking -> Manage Flags -> Raid.

Once this is done, you’re ready to execute your task list which will partition and flag the devices.  When the task list completes successfully, you can quit gparted.

Step 3 – MDADM

Next, you need to create the software RAID volume using the mdadm tool.

This is very easy and is done with a single command:

# mdadm --create --verbose /dev/md0 --level=1 --raid-devices=2 /dev/sdb1 <a title="/dev/sdb2" href="file:///dev/sdb2">/dev/sdb2</a>

What this command does is:

  • invokes mdadm with the –create option (useful for creating RAID arrays)
  • specifies the “verbose” flag so you can get meaningful diagnostics should something head south
  • specifies the mount-point device (/dev/md0) for your RAID
  • specifies your RAID level (–level-1) (remember, 1 = mirroring)
  • specifies the number of devices in your RAID (–raid-devices=2)
  • lists the device links for the logical drives to be used in the RAID (/dev/sdb1, /dev/sdb2)
In the screen-shot below, I highlighted the raid mountpoint device, /dev/md0, because (a) I kept forgetting it was a required parameter to the command and (b) I am going to need this device name for the next command…
</div>
<div>

# mdadm --create --verbose <strong>/dev/md0</strong> --level=1 --raid-devices=2 /dev/sdb1 /dev/sdb2
mdadm: /dev/sdb1 appears to contain an ext2fs file system
size=244174848K mtime=Wed Dec 31 16:00:00 1969
mdadm: Note: this array has metadata at the start and
may not be suitable as a boot device. If you plan to
store '/boot' on this device please ensure that
your boot-loader understands md/v1.x metadata, or use
--metadata=0.90
mdadm: /dev/sdb2 appears to contain an ext2fs file system
size=244177920K mtime=Wed Dec 31 16:00:00 1969
mdadm: size set to 244173688K
Continue creating array? yes
mdadm: Defaulting to version 1.2 metadata
mdadm: array /dev/md0 started.

</div>
<div>

Press enter to execute the command and you should get the prompt back after a few seconds.  Note that the program requires no input from you.

Step 4 – Making and Mounting

This is the step that most of the online and available pages on mdadm leave out.

You still need to format your filesystem!

So, let’s do this with mkfs

# mkfs -t ext3 <strong>/dev/md0</strong>
mke2fs 1.42 (29-Nov-2011)
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
Stride=0 blocks, Stripe width=0 blocks
15261696 inodes, 61043422 blocks
3052171 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=4294967296
1863 block groups
32768 blocks per group, 32768 fragments per group
8192 inodes per group
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
4096000, 7962624, 11239424, 20480000, 23887872

Allocating group tables: done
Writing inode tables: done
Creating journal (32768 blocks): done
Writing superblocks and filesystem accounting information: done

Note that I use the RAID mount-point address of /dev/md0 to create the filesystem.

This will take a few minutes to run so go take a stretch break and play with the dog or annoy your hot receptionist or something.

Once this step completes, you’re pretty much done — all that’s left is mounting the device and using it.

# mount /dev/md0 /media/raid

&lt;do stuff like start a back-up&gt;

# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/PAMCAKES-root 84G 9.4G 70G 12% /
udev 3.9G 4.0K 3.9G 1% /dev
tmpfs 1.6G 920K 1.6G 1% /run
none 5.0M 0 5.0M 0% /run/lock
none 3.9G 49M 3.9G 2% /run/shm
/dev/sda2 242M 159M 71M 70% /boot
/dev/mapper/PAMCAKES-user 93G 48G 42G 54% /home
/dev/md0 230G 22G 196G 11% /media/raid

Note that your available filesystem space is approximate one-half the capacity of the drive.

That’s right – you paid a whopping $89 for a 500gb device out of which you can use about 230gb.

But you have two of them.  So if your first partition fails, you’ve got a back-up of your back-up thanks to the wondrous magic of RAID.  And, you learned how to set-up a RAID device, so, win.

Unless one of your kids (or grandkids) decides that your sleek, new, external device looks better in the fish tank than it does on your desk, you have an additional and available option for recovering lost data.

Keep in mind that this is a software solution — hardware RAID is still the preferred way to go when dealing with issues of redundant data storage.  But this works, too.

Step 5 – Maintenance

To stop your RAID device use this command:

# mdadm –stop /dev/md0

To see the state of your RAID, cat /proc/mdstat to your terminal:

# cat /proc/mdstat
Personalities : [raid0] [raid1]
md0 : active raid1 sdb2[1] sdb1[0]
244173688 blocks super 1.2 [2/2] [UU]
[&gt;....................] resync = 1.8% (4474560/244173688) finish=4948.7min speed=806K/sec

unused devices: &lt;none&gt;

To get details about your RAID, use mdadm:

# mdadm --detail /dev/md0
/dev/md0:
Version : 1.2
Creation Time : Tue Jul 24 14:41:55 2012
Raid Level : raid1
Array Size : 244173688 (232.86 GiB 250.03 GB)
Used Dev Size : 244173688 (232.86 GiB 250.03 GB)
Raid Devices : 2
Total Devices : 2
Persistence : Superblock is persistent

Update Time : Tue Jul 24 14:42:51 2012
State : active, resyncing
Active Devices : 2
Working Devices : 2
Failed Devices : 0
Spare Devices : 0

Resync Status : 2% complete

Name : pamcakes:0 (local to host pamcakes)
UUID : b542c27c:1d620c3e:6f07b9e8:53aee08d
Events : 1

Number Major Minor RaidDevice State
0 8 17 0 active sync /dev/sdb1
1 8 18 1 active sync /dev/sdb2

To uncouple the RAID device:

# mdadm –remove /dev/md0

 

That’s it for today…hope this helps…there’s a lot of good information already available on the web for using mdadm and software RAID – I just wanted to consolidate everything into a contiguous process.

Later, should sufficient motivation present itself, I’ll follow-up with alerts and what not…

 

Back to the 80′s…

Category : Rant, WTF
No Gravatar

Most of my first week in Mexico was spent doing battle with two cellular companies:  Boost Mobile and AT&T over cellular coverage and pricing.

As the loser of said battles my only remaining recourse is to document the events within this blog so to serve as a warning to those in similar predicaments so that they will learn from this experience and not waste time getting gobsmacked by indifference and incompetence.

Prior to moving, I enjoyed AT&T cellular service in the SF Bay area.  I very recently changed back to iPhone ownership because the iPhone would pair with my hearing-assist device where as my HTC (Verizon) would not – this, despite spending a full day at the audiologist coaxing the phone, like a groundhog emerging from it’s hole, to “see” the bluetooth device.  Eventually I abandoned my HTC phone in favor of the iPhone since the iPhone took the pairing on the first attempt.

(side note: to the credit of Verizon, when they learned of why I was switching phones, they waived the contract cancellation fee.  Well-played, Verizon – you have, through your sense of humanity, ensured an advocate in me!)

Anyway, life was good until I crossed the border into Mexico.  Within inches, AT&T graced me with a free text message informing me that cellular roam rates would now be incurred at $0.99/minute of talk-time and $19.97/megabyte of data.

Considering I had an unlimited data plan, with tethering, $20 per meg struck me as wee bit…shall we say, excessive…  Off to the interweb lumbered I, searching for call plans for my phone that would allow me to use my US phone in Mexico (albeit within a few miles of the international border) without the looming threat of immediate bankruptcy for doing so.

I was unsuccessful in locating a comparable AT&T plan that included Mexico in it’s cellular goodness without having first to pledge most, if not all, of my future earnings to this corporation in return for minimal utilization of their services.  So, back across the border to the nearest AT&T store where I met a most-helpful clerk.

Jonaton was aware immediately of my “special” needs — I don’t care for cellular minutes being hard-of-hearing but, instead, rely heavily on data use for my communication needs.  In other words, I communicate with emails and text messaging.  I can use the phone but it’s an involved process and, I assure you, I will not hear every word spoken with accuracy.

The best plan, even after he called advanced customer care, was something called the Viva-Mexico plan — where I can have 450 minutes of talk time per month (on either side of the border) but data would be offered only as a pay-as-you-go option:  text messages would cost $0.50 each and data can be consumed at the rate of a mere $5/mb.  This would also lower my basic bill by half – to about $55/month.

I commented: Boost offers me unlimited text, email, phone and data, with international support, for $55/month — how can you (AT&T) compete with this?  He just threw me a sad look and said: We can’t.

Having, literally, no other choice being a new contract holder, I accepted the new calling plan.  When I later crossed back into Mexico, I tried the cellular service and it works ok.  Texting still seemed really flaky and I don’t want to pay $0.50 per text, so I turned off all cellular service and now only use the phone, while in-country, when I can access wireless.  Basically, I am paying AT&T $55/month to not penalize me for the cost of the phone (new plan) or contract cancellation, said total being close to $1,000.

Phoneless, I next went to Boost Mobile because (a) everyone in Mexico uses the radio over the phone, and (b) the phones work in Mexico this close to the border over voice and text as well.  Finding a radio-phone, however, turned out to be an epic quest as all stores in the US have stopped stocking the phones in preparation for the removal of the IDEN towers which provide radio communications, making the (what I like to call the “beep-beep”) part of the service go the way of the dodo.

Boost is actively tearing-down their IDEN towers – radio, a far superior communication service in terms of speed and clarity imo, for some reason is going away in the US at the end of this year.

I visited a total of five Boost stores without finding a single radio-equipped phone.  I finally decided on the smart phone option but the last store was out of stock of the particular model I wanted, (weird — phone stores with no phones) so we headed back to the first store we stopped at.  Where at I learned that the store manager had contacted her manager who hand-delivered his last two radio-phones to the store.  Of which one had already sold.  Awesome!

I snatched the other one up (prematurely) declaring victory over the phone consortium’s efforts to thwart my communication needs!  Huzzah!

We activated the phone and I returned to my new home…where I learned that the phone would not work on the cellular or text network.  Dialing 611 — Boost’s customer service number which they promise on their website will never be restricted was, on my phone, restricted.  The split-second I pressed the “ok” button to send a call, a screen pop-up declared “Service Restricted” on my call.

Two people, sitting next to me on my couch, both with Boost service, both with the exact same model of phone, were able to make cellular calls (to the US and Mexico) and send text messages.

At least my radio worked.

The next morning I searched their web site for solutions and, finding none, called customer service.

Remembering how difficult it is for me to use a phone, stumbling through Boost Mobile’s IVR (interactive voice response) system was an absolute nightmare of chaos and misdirection.  I challenge you to get to a live person within five minutes of making the connection.  Not hold-time, mind you, but simply by navigating through their IVR options.  It took several tries – hanging-up and recalling – before I learned which options to not press.

As a former support manager, I’ve designed IVR systems.  Companies use them when they either (A) want to quickly route customers to the right person to talk with or, (B) do not want to talk to their customers.  Boost was clearly in category B – you have to have the persistence and patience of a diplomat to get to a real person.

I finally reached a live person and was further aggravated by having to repeat all of the information I plugged into the IVR back to the CSR.  This, to me, screams of incompetence and ambivalence towards the customer.  If you’re not going to use/save the information I provide, then don’t waste my time asking me for it.  (IVR Design Note:  A good way to reduce turn-over in your customer service organization is to not have your customers so spun-up and angry that they free-rage on your CSRs when they finally reach them.)

The CSR was clearly ESL (English as a second language) and I spent several minutes explaining the situation to her.   She attempted to “fix” my phone by having me turn the phone off and on several times, and performing master resets from the advanced settings menu.  Restricted service prevailed.  We quickly exhausted her catalog of diagnostic options.

I was escalated to level 2 — which was another ESL person, who immediately asked me for all of my information starting with everything I plugged into the IR and why I was calling.  So, in addition to the IVR not recording anything, the CSRs apparently do not record anything about why you’re calling them so that people in other departments can access (and learn from) the information.  Or they silo the info and the tech folks simply cannot see what the non-tech folks write.

Then the tech informed me (as did the CSR) that the IDEN service was going way at the end of the year leaving me with the impression of: since my phone has radio and this service is expiring, they don’t feel as if they are obligated to help me with my issue.

First thing level-2 wants me to do is reset the phone: turn it off/on, master reset, remove the battery, etc.  I wondered if he thought that if he asked me to do this, if it would differently from when the CSR asked me to do this.

Still restricted service.

Side note – on my phone display, I show full bars, that line-1 is ready, and I am connected to the Boost network.  There’s no reason why the phone should not work.

So eventually, the tech gets frustrated and takes the cop-out response of: well, since you’re in Mexico, our $5 international plan only means that calls are guaranteed to work from the US to Mexico and not from Mexico to anywhere.  Doesn’t care that services work on other phones in my proximity and has no interest of pursuing the issue further.

And that’s how we ended the call.

So, my next thought was that maybe the counter-person at Boost (back in the US) forgot to register my cell with the network.  I gave my phone to one of my friends with an identically-working phone because she was heading over the border the next day and agreed to stop by the Boost store and ask them to look at it.  However, when she got to the border with the phone, she called her husband from my phone and since cellular service seemed to working, she decided to not go to the store and instead returned with the phone.

Which was still service restricted.

So, I called Boost back, and within about 30 minutes finally made it back to a level-2 tech who was quite puzzled as to why my phone wouldn’t work.  We tried several variations of the reset, but nothing worked.  I opined to him that because the “Service Restricted” message was popping up so quickly when I pressed the send key, that the problem was in the phone’s ROM and service restriction was software and not tower based.

He agreed that this was a possibility especially considering that my phone, chronologically, was a year or two older than my friends’ phones that were working.

I asked him to call me, to see what he heard on his end and that was the end of that call as I was dropped.  Apparently too much to handle, the tech either intentionally dropped my call or his phone system was at a level so advanced he cannot work it properly.  In either case, I was pretty sure I wasn’t going to get resolution from him.

Contacting Boost CSR a final time about my brick, that I’ve yet to use successfully, I asked about returns and refunds and was informed, in no uncertain terms, that I have pre-paid for my service.  They have no refund program.  Is there anything else we can “help” you with today?

So I paid about $110 for a brick — at least I can use the two-way for the time being.  $3/day for 30 days (more or less) with more features I can’t use than I can.  Sweet deal, yo.

Yesterday, I went to the Nextel store in Rosarito to inquire about IDEN and cellular service.  tl;dr – I can get a minimal calling plan (120 minutes) with unlimited radio for about $40/month.  I only get 20 text messages per month but additional texting is about $0.06 per message.  (It’s nice when a phone company doesn’t subject you to violent sex acts for a service, isn’t it?)  All services on the phone work into California up to about Bakersfield at which point they become 1-way only.  Not sure how I feel about that but, hey, at least it works and I would have full services.

So, I’m going to chuck my Boost phone into the ocean once my month is up and buy the Mexico Nextel phone.  I use my Google-Voice number, which is paired to my Skype number, for all other phone services and eventually I’ll add a Vonage number to my house that has a US number (to which I’ll forward my Google phone to) and I’ll be set.

Side note – I made the mistake of enrolling in auto-pay on the Boost mobile website assuming I would have a working phone similar to what my friends enjoy. While it was super easy to enroll, it’s impossible to un-enroll from autopay on the website.  So another dreaded call to Boost is looming.  If you ever have to call Boost mobile, I recommend this information to help you get to a live person as quickly as possible.  I want something in writing from them canceling my autopay so that when they autobill me next month, I can force a refund.  (Look!  A windmill!  Chaaaarge!)

My AT&T phone I can use as a data terminal over wifi (facetime calls with the office and such) but I won’t turn it on unless I’m back over the border in the US.  Once my contract is at a point where I can quit for $200, I’m out.

I have my Mexican Nextel for calling when I’m not at home.  If you’re in the US and you want to call me, you’re going to have to deal with your cellular provider and pay the extra fees that they’ll extort from you.  Good luck with that.

Finally, I’m really hoping that Richard Branson does something spectacular with his Virgin line of phones.  It would be awesome to see a cellular company erase international borders and just have a phone that works, regardless of where you are, for a consistent fee.

Other than shoveling obscene amounts of profits into the never-satiated maws of the phone conglomerates, I just don’t understand why this should be so difficult.  I honestly look forward to untethering myself — like a heroin junkie coming clean — from the cellular leash.  Freedom, I crave thy sweet sting!

I’m taking the path of least resistance.  If the phone companies don’t want to offer something reasonable in return for my hard-earned dollars, then I have absolutely no problem converting those dollars to pesos and spending them here.

Just saying…

Using a VPN for Everything…

Category : Technical
No Gravatar

I recently moved to Mexico from California because my job was approved for 100% telecommuting and, since I can do that from anywhere, why not Mexico?

Unfortunately, for me, I chose to live in a town that’s a bit out of the way — between Rosarito and Ensenada, Puerto Nuevo sports DSL as the only means of internet access.  Buh-bye cable modems.

I’ve been toying with the idea of using a VPN as a gateway to my ISP simply because of the legislation that’s cropping up recently that enables our respective (not respectable) governments to spy on the internet traffic of it’s citizens without due process.  And, I believe, this due process is a long time coming because this is (to the government) a new frontier who’s sanctity has yet to be defined in the higher courts of law.  So, boys and girls, until that happens, Big Brother can pretty much do as they want.

And, they do.

Now, the prevailing argument that causes most of us to bleat our way through endless queues at airports waiting patiently for our turns to be molested is this: if you’re not doing anything wrong, then why hide what you’re doing?

Note that you’ll normally hear the same thing from some street cop when he pulls you over and asks to search your car without a warrant; if you have nothing to hide, then why can’t I search your car?   Well, it’s a little thing called the Fourth Amendment to the Constitution (of the US)…

So the passive-aggressive kicks-up in me and I consider installing a VPN so that no one can track what I do when I am online.  Never you mind that what I do is so damn boring and mind-numblingly dull (for a living, mind you) that there’s only a small fraction of the existing population that would even understand the particulars, it’s just the principal of the thing.

Same reason why I use DuckDuckGo for my search engine instead of Google.  Why?  Because DDG protects me by not linking my searches to my identity.  Google is infamous for rolling over, exposing your soft underbelly to anyone waiving a law-enforcement letter-head demanding your emails and search and browser history from the beginning of time.  That same patriotic spirit that caused my forefathers to flip King George the finger is what still burns brightly in me today:  Because I can.

So, quick side note — what’s a vpn and why do I need one?

A VPN (virtual private network) is just that – it’s a private network that exists within your existing network.

When you connect to the internet, through your ISP, you’re establishing a network between your home machine(s) (also a network) and the internet (THE network).  All your requests are routed through your ISP and out to the ‘net where their response is then filtered back to you.  An ISP, then, has the ability to know exactly what you request, when you request it, and how many requests you make.

An ISP can also filter and monitor your requests and can deny you access to certain internet-based resources based on the type, amount, or time of the activity.

For example, some ISPs throttle (reduce) your available bandwidth (the diameter of your data flow) if you exceed a set-amount of data downloaded within a period of time.  You have “unlimited” bandwidth but the reality is, once you hit some arbitrary limit determined by the ISP, things get a lot slower for you.  Unfortunately, a VPN cannot help with this.  Data is data.

Some ISPs record and/or block your ability to transact certain types of data.  The most notorious example of this would be P2P or bit-torrent packets.  While a mainstay of the gray (or darker) areas of software licensing, there’s a legitimate use for bit-torrent packets (linux distros for one!) that should never be prevented from reaching your computer.  However, like most totalitarian regimes, your ISP may have an “all or nothing” policy with regards to filtering by packet types.

Another example of filtering is by content.  China is infamous for it’s firewall of profound social cluelessness, filtering all (what the ruling regime considers to be) subversive sites from being viewed willy-nilly by it’s population.

So, as you can see, ISPs wield a tremendous amount of power.  They can meter, view, and deny data packets based on their rules and how heavily influenced they are by corporations or governments.

VPN kind of takes you around all that by creating a private tunnel to what essentially is another ISP (network) allowing you to use the internet as dog himself intended: safely, securely, and without limits.

Allow me to explain…

When you connect to the internet, you establish a network (as I mentioned earlier) between your home machine (network) and your ISP (internet).   All subsequent requests are routed through your ISP and are subject to various levels of scrutiny and transcription.   Fine.

But, creating a VPN within your network basically does this:

  • establish a secure (encrypted) connection with your VPN provider
    • all data packets you send are directed to your VPN
    • all data packets you send to your VPN are encrypted
      • your ISP cannot decrypt the data in these packets; your ISP only knows that there are packets
  • all routing (DNS) requests are sent to your VPN provider
    • address lookups happen on your VPN, not your ISP
      • your ISP does not know where you are going
  • all data requests are sent to your VPN provider, not your ISP
    • request fulfillments happen on your VPN, not your ISP
      • your ISP does not know what you are requesting or receiving

An analog analogy for his is a spy movie where someone gets one of those electronic voice scramblers — the person making the phone call (you) attaches the scrambler (encryption) device to their phone, inputs some unique code, and calls (ISP connect) their contact (VPN) who attaches their scrambler and enters the same code so they can de-scramble your voice while scrambling their own.  The phone company knows that a call is being made,  and how long the call lasts, and maybe even how much information is being transmitted, but they can’t de-scramble or understand the conversation or know who’s talking on the other end.

See?  This what a VPN does.  But it’s only one thing a VPN does.

When I moved to Mexico, for example and as I stated about a thousand words ago, I experienced an unintended consequence of exchanging an implicit .us domain for a .mx domain.  US-based web-sites detect that I am not of the US and re-direct my requests, they automatically translate my web pages into Espanyol (which I don’t speak), or they convert dollars into Mexican pesos.

By using a VPN, I can imply that I am physically in the US, and avoid all that unpleasantness.  Plus I still get to “stick it to the man” (or hombre).

So, finally getting to the point, yesterday, I signed-up for a seven-day trial offer with a VPN company called BolehVPN.  They use tunnelblick as their delivery vehicle for connecting to their VPN.  This was OK with me because I already have tunnelblick installed and use it for work.  All I needed to do was sign-up for a trail account, create my account, give them about $4 US for the 7-day trial, and download and install the configuration files for tunnelblick.

All this took about five minutes.

BolehVPN offers the following configurations:

  • Proxied – mainly used for P2P connections, servers are hosted in Europe
  • Fully-Routed – Secure P2P, anonymous surfing, and data security, servers in Europe and Canada
  • Surfing/Streaming – TCP and UDP for anonymous surfing and streaming content, servers are in the US and Hong Kong
  • TCP443 – http protocol over tls/ssl used for bypassing firewalls

What really sold me on this particular vendor, however, were these features:

  • no logging/monitoring of your traffic — so even if they were hit with a disclosure order, they have nothing which ties traffic to you
  • compatibility with portable devices — I had my iPad up on the VPN within seconds over my wireless
  • freedom to chose which VPN service best meets your needs at that time
  • Download speed reduction minimal – about 5%

Cons (so far):

  • some of their website configuration is out of date
  • forum data is dated
    • this could be a good thing — service is so easy to use, no one posts questions!
  • Expensive – BolehVPN is almost 2x the cost of comparable VPN services

Will I sign with them for a year?

Probably not.  Based on cost alone, all things being equal, there’s a lot of competition in the VPN services.  As such, if I can find a competing service that has the same performance as BolehVPN, then I’ll probably sign with the other service and save myself about $40/year.

That’s about it…hope this was helped you understand why a VPN is important and if you decide you need one, what your next steps are.

Apple Mail Encryption with GPGMail and OpenPGP

Category : Technical, Uncategorized
No Gravatar

I’ve dabbled with encryption several times over the past few decades, never really getting serious about it.  It started when, in college, I would see that the faculty in the CS department had these weird signature blocks appended to the their USEnet posts containing something called a public key.

This is like setting a can of lighter fluid and box of blue-tip matches in front of a 10 year-old boy — irresistible.

What I found in the later years is that using encryption for email is a lot like being one of the early adopters of the telephone.  Now that I have one, who am I going to call?  My family certainly doesn’t use encryption in their email…those that have discovered email anyway.

Co-workers aren’t likely to invest the time and effort into encryption simply because we’re all too busy with work to be playing spy-games with our de-coder rings.

And, Dorothy, we’re not in Academia anymore where, I imagine, it’s really in use.  Closeted anarchists posting semi-heretical Berkeley-esqe rants against the Proletariat and all that.

And then there’s the whole nouveau post-9/11 trend of “Guilty until Proven Innocent” thing happening.  I imagine some fedora-capped DHS agent squinting at me in a menacing fashion while I try to reason a plausible excuse for being so brazen as to need encryption for my emails in the first place…

So, at this point, let’s assume that, like me, you’re willing to whack the hornet’s nest with a stick and use encryption for your emails and that you actually have someone on the other end willing to dust-off the de-coder ring and play with you.  We’ll also assume that you know what PGP, GPG and OpenPGP actually are, and that you know how basic public-key encryption works.  (If not, leave comments to this article and I will do a future article explaining same.)

Standard Disclaimer: I am providing this tutorial as a hands-on, learn-with-me type of tutorial.  I am not an expert, nor do I pretend to claim anything other than neophyte status when it comes to encryption.  I do not advocate, support, or intend for you to use this, or any, technology as a means to intentionally bending, fracturing, or breaking laws in one or more jurisdictions.  MY only intent is to share what I’ve recently learned with you and to have some fun.

OK – that crap out of the way, let’s get started.  First, as the title implies, this set-up is for Apple Mail under OS X Lion.  The release of the OS I’m currently working on is 10.7.4.

Download and install the GPGTools utility  (Version 2012.03.18 as of this writing.)

Although this article is for Apple mail, the GPGTools utility includes support for Enigmail in Thunderbird 7.  When you launch the installation utility, you’ll be presented with a list of packages to install.  I installed all packages.

Once the install has been completed, you’ll see a little dialog box appear on your desk top telling you the installation was successful, and would you like to read the Quickstart Tutorial?  This would be a good thing to do because I am not going to walk you thought the next steps in any great detail.  This reference, however, does.  With pictures.  So… go there and follow the installation step to:

Generate a key

You will generate a public and a private key.  Anyone with whom you wish to exchange encrypted email with must also have done the same.  They’re called public and private keys for a reason.  One you share with the public and one you do not.  Key, using the nomenclature, are stored on what’s called a keyring.  There are public and private key-rings.  GPGTools refers to key-rings as  keychains - these are one and the same things.

Please note that for whichever email account you’re going to use to generate a key-pair for, that account must already exist in Apple mail.  The email address is case-sensitive so make sure you type it in exactly as it is stored in Apple Mail — otherwise, your encryption will not work.

Over the years, I have created several key-pairs for various email addresses I have had.  What’s critically important to remember is this:  write down your pass-phrase.  Also, click on the Advanced options tab, and set an expiration date (a couple of years is fine and 4-years is the current default – point being: set an expiration date) for your keys.  That way if, after a few years, you return to a previous email account address, and you’ve certainly forgotten your passphrase from lack of use, then you’ll still be able to generate a new key pair if the old one has expired.  To remove a key pair, most public key rings require you to enter your passphrase.  This is known as a conundrum.

Once you’ve created your passphrase and uploaded your key, and you can see your new key in your keychain, open Apple Mail.  Send an email to

the email address you’ve just created (I know…) and you should see two buttons appear in the lower-right corner of the header bar as shown in the image on the right.

The two buttons, as shown above, allow you to either sign or encrypt your mail message.

Signing your email is flagging the email to the recipient assuring them that it was actually you who sent the mail.  In order to sign an email, OpenPGP has to have access to your private key. (You did keep your private key private, right?)  Since you’re the only one, presumably, with access to your private key, then signing the mail guarantees to the recipient that the mail did come from you.

The recipient does not need to have your public key, nor do you need to have the recipient’s public key, to sign an email.  Think of this as the “certified mail” from the US Post Office equivalent for email.

If you have a recipient’s public key, then you may send them an encrypted email.  The recipient will need to have your public key in order to decrypt and read the email — this is why we store public keys on public key rings.

-----BEGIN PGP SIGNATURE-----
 
Version: GnuPG/MacGPG2 v2.0.18 (Darwin)
 
iQEcBAEBAgAGBQJPtnyiAAoJEC4S4zGLhwvBFNMH/1Yoh59etAcYZpAhZ+htpd81
QzZWDxOR2PeXtPkY3GWl4vdW7GABJ9ysl8vpdErsDtXs6LEVZXag5mV6CGTDNXmm
pdozUJCgNwbHTgoIUdjinmAXLR+4pYSfALTB1S2qpxzMpykBkR7SMuPm3+0LC77/
dwnsSVx5CNtJd8cPoPjwXJ6zaStJCNK+H17MItS5kpw3MqMU35qZdNCDV6ehhA8j
FmTyFoh1TeTmuBrNECWz9z3KniG6SWVl3K21LmS8PQExeHq8qcHGBz5yK2YhoW/w
bn4PIyHaUiXKQTNhYBSd1DrCPUWJKDJ+VCKQ0L97aUPeVPQBI14jsFOgc1dwUjs=
=xfmB
-----END PGP SIGNATURE-----

That’s pretty much it — once you send a signed an encrypted mail, you can rest assured that (hopefully) your emails are safe from casually-prying eyes as they’re no longer being sent in clear-text across the ether sphere.

Here’s the raw-text (what’s sent out over the ether) of an encrypted email message:

 

Return-Path: <mshallop@gmail.com>
 
Received: from [192.168.0.2] (c-50-136-203-107.hsd1.ca.comcast.net. [50.136.203.107])
by mx.google.com with ESMTPS id qu6sm6794406pbc.36.2012.05.18.09.54.57
(version=TLSv1/SSLv3 cipher=OTHER);
Fri, 18 May 2012 09:54:58 -0700 (PDT)
Content-Type: multipart/encrypted; boundary="Apple-Mail=_4C5344B9-76FE-43EF-8620-073841EBF944"; protocol="application/pgp-encrypted";
Subject: test both
Mime-Version: 1.0 (Apple Message framework v1278)
X-Pgp-Agent: GPGMail 201 (a30)
From: Micheal Shallop <mshallop@gmail.com>
Date: Fri, 18 May 2012 09:54:56 -0700
Content-Transfer-Encoding: 7bit
Message-Id: <407C94BA-32A1-4930-B9F6-BBFE7900D213@gmail.com>
Content-Description: OpenPGP encrypted message
To: Micheal Shallop <mshallop@gmail.com>
X-Mailer: Apple Mail (2.1278)
 
This is an OpenPGP/MIME encrypted message (RFC 2440 and 3156)
--Apple-Mail=_4C5344B9-76FE-43EF-8620-073841EBF944
Content-Transfer-Encoding: 7bit
Content-Type: application/pgp-encrypted
Content-Description: PGP/MIME Versions Identification
 
Version: 1
 
--Apple-Mail=_4C5344B9-76FE-43EF-8620-073841EBF944
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename=encrypted.asc
Content-Type: application/octet-stream;
name=encrypted.asc
Content-Description: OpenPGP encrypted message
 
-----BEGIN PGP MESSAGE-----
Version: GnuPG/MacGPG2 v2.0.18 (Darwin)
 
hQEMAy4S4zGLhwvBAQf/YX1vGFhG0CLd7UU79fjHd4/nIHH9DVVsi8oqsmIwBNpl
zXvDZf3+uw3B7Shk3bls1fHcUdU8LZprY+HbQVZlh7IiGZ28K67rNIHKUtwuoX2I
DpZcLdRPGn1iGi7TNRs/3Fn3fjCCT15uVAbZZRvR1G3XqUc+//3TstCMTaNtg5Rl
KZbKnnrOaIt/OAou7BFCLQgGKAAIa3m/gFHEQxxLVaAQ3JISeX4/UZ7YlXAj5SYp
juVRKfekmjpoFoM4erf0Jjaw63lSjAZWXJi1m6IY8uSkzQZRUwANMYE577mBiZhw
d1kIBJfBlxWjUOK6FYV3bDZFjc2Fn2WM4+tEloOrcNLpASNVPOh332341GjxwVAg
USYLkh1Co7yqaQ1c830CD58XxmYsR/x0B8etL1bYQtZDJYITMa649tYCYtrAvHSw
GsgwDD67yaICruIJqwPAtz5+fkoF9xnlltz0UTUSsmzrlGkdbkHSnVrvOdgbvJEz
60UM1p7idKEcR5SCkNuLD9hYJaD/C7qRhyxYfjyjPlwtSSb9aSY+TBa0t/lRmPtU
q5N/EHRdm3CmTVqE3eT1IOoRsFibYfRJNJnkqgmVZdoHm/QhTOmuyK1SeBbB3P7F
Lnw7hS6aB7RMISg8rWiPGap+QVO8lzMjIXhd5BDu1Kxkk5dr7FLOC37aSC7X52VT
QcwvYlYPSV5s+ivFk/uKtB8L3k9rHsYHHkP13lli2a9ELy+KTv0SFp0Q8ClaC9Xm
RoWXKQxrTV2wxub4/V0UfSSUfct0mHfTFDltpDPAfaEL/ARgvIUI2SD6WXvfbums
yG2fS4Rr9NHr9bgkZJqt+anNGQOmly7654ecckD+Nj2PtmXnowBOrub91VvXSfKB
TKHZ7xmX9GOnk6qsFZppiXIxDXR9zYeLb5Ks0hCF3XDhXu8DAkS4vbmPm7BIn6fu
N1niCzihe187mJ3bKAj5rLSHpbBrMt/XcbaL+eNz7xIEtpWQjk+8qFUaxl5NxIX1
KtCwLhxUeZUCKHIv/cGOJKPANfdhN2SYHasFJvJ1Jts4us4JJg4rBjH50hiYaQ3W
OhGSQP7kvcKqMzHBhSXBKPpnwBtJ+tkXy5IpeMHUrREpGt39EOO4oiiyRYSrrm2T
GswOJwvv4Z32hreR2eAgfPAXZW3R6MtGA07xN3mqPtV1IO+izegbflVpQEi8Zlq3
KfT/ljoLiDTKdIsrlfIgtgec+G5SH6znO9Kv1IidYg9wtdL6G2bsTP9pCV24bCem
srTw3NDKfTK56Yu1ESQpf0WYMTzpGjzDhyHW86F5ej+jJV2rd4kicxvy0HJXOGbI
iHPvKVVoA7bMvvfVARoMIlqiQ5gzm41+
=z4VQ
-----END PGP MESSAGE-----
 
--Apple-Mail=_4C5344B9-76FE-43EF-8620-073841EBF944--

 

Reference Pages and Additional Reading:

GPGTools First Steps
Secure Email in Thunderbird and Apple Mail with GPG

Renaming mongodb Columns

Category : Technical
No Gravatar

Today I was putzing around in the geo-spatial collection when I noticed that I had an unhappy over one of the column names within the collection.

In the mySQL world, changing a column name is pretty straight-forward courtesy of the alter table command.

Mongo…not so much…

<BEGIN_UNRELATED_SIDE_RANT>

The Mongo documentation is normally the first place most of us go when we’re looking for help in using our favorite noSQL database.

Why?

Well…because that’s usually where Google directs us to go and also because there just isn’t a whole lot of documentation out there on the subject to begin with.

The mongo (10gen) documentation is pretty good.  It’s not, however, excellent.  And I can articulate the reason why.

It’s pretty easy to identify documentation written by engineers as opposed to documentation written by everyone else (on the planet).  And not because of technical content or the (ab)use of really big and impressive-sounding jargon.

No – it’s because most engineering-authored documents are written using a solution-based voice instead of a problem-based voice.

Think about it:  when I have to go to the man-page for help, it’s because I have a problem.  If I had a solution, I would be writing a blog post.    But since I have a problem, I need the man-pages, online docs, whatever, to help me figure-out a solution.

Engineering documents are written from a solution perspective:  the document assumes you possess some bit of arcane lore (which is probably just exactly that little bit of lore that you’re missing which has caused your trip to the documentation vault) and everything that is explained within the document all hinges on this piece of knowledge which the author, albeit with the finest of intentions, assumes is already firmly in your mental possession.

And that’s why I usually don’t like 10gen’s documentation.  But, like I said earlier, it’s the only game in (Google)town.

<END_UNRELATED_SIDE_RANT>

In mongo, to change the name of a column within a collection, you first have to be on a release of mongodb 1.7.2 or later.  Since most of us bleeding-edge, early-adopter types are all 2.x versioned, this shouldn’t be an issue.

This page from 10Gen is the update page and, within, talked about the $rename modifier to the update command.  What the section doesn’t say, because it’s assuming you’re wanting to update records and not schema, is how to apply a change to all of the records in your collection.

In my case, I have a column-name which I fat-fingered the name right out it’s camel-case:  CountryID instead of countryID.  (And, yes, OCD-peeps, I know that it’s not strictly camelCase, thank-you!)  I want to spin through all 3.7 million rows in my collection and rename this column…

> db.geodata_geo.update( {} , { $rename : { 'CountryID' : 'countryID' }}, true, true );

So what we have here is the update command to the collection (geodata_geo) and four parameters:

  1. {} — the empty set (this is what’s missing from the 10gen doc) implying to do whatever to each record in the collection
  2. $rename — the modifier to the update command which, in this case: replace ‘CountryID’ with ‘countryID’
  3. false — indicates to allow upserts if the record does not exist
  4. true — multi option:  means to apply command to all records since, by default, the update() quits after updating the first record

And I run this command and mongo goes off (whirr…whirr … I have two-node replication…) and renames the column in my collection!

What it didn’t do was update my index. 

So, after my column-renaming completed, I needed to drop the index(es) that had ‘CountryID’ as members and re-index the collection to reflect the new column name.

Executing getIndexes() confirmed that my mongo world was back in it’s correct orbit and life, once again, was good.

Why is my mongo query so slow?

Category : Technical
No Gravatar

Why’s my mongodb query so slow?

I got my geospatial collection set-up — I am running some really great queries making sure that the locations I am pulling aren’t in any sort of cache, and I am just blown-away by how fast data is being returned.

The problem is:  when I query the collection to pull up the requisite lon/lat data by name:  city & state, or city & country, the query seems to take seconds to complete!

I set-up the table correctly…I indexed the crap out of all my columns…a week or two ago, I was at the mongoSV 2011 in Santa Clara and learned some really cool stuff about queries, indexing, and performance management, so let’s dig-out the notes and see where I went wrong.  Because I strongly doubt that the problem is in mongo but, rather as we used to say in technical support: this is a PBCK issue…

The first thing I want to do is run an explain against my query so I can see mongo’s query plan for my query.  This should provide me with a starting point for trying to figure out what went wrong.

> db.geodata_geo.find({ cityName : “Anniston”, stateName : “Alabama” }).explain();

By adding the trailing function: .explain(), I’m requesting that mongoDB return the query-plan to me instead of executing the query.  I hit enter to launch the explain() and get back the following output:

> db.geodata_geo.find({ cityName : “Anniston”, stateName : “Alabama”}).explain();
{
“cursor” : “BasicCursor“,
“nscanned” : 3691723,
“nscannedObjects” : 3691723,
“n” : 1,
“millis” : 2269,
“nYields” : 0,
“nChunkSkips” : 0,
“isMultiKey” : false,
indexOnly” : false,
“indexBounds” : {

}
}

The important information, I bold-faced in the query output (above).   What this output is telling me is that I’ve using a “BasicCursor” for my search cursor — which is indicates that, yes, I am doing a table-scan on the collection.  So, already I know my query is not optimal.  But, wait!  More good news…

The value for nscanned and nscannedObjects is the same: 3,691,723 — which coincidently is the same as the cardinality of the collection.  This number is the number of documents scanned to satisfy the query which, given it’s value, confirms that I am doing a full table scan.

millis tells me the number of milliseconds that the query would take:  2.269 seconds:  way too slow for my back-end methods() serving a REST API — unacceptable.

And then we get to the tell:  IndexOnly tells me that if the query could have been resolved by an (existing) covering index.  Seeing the value false here tells me that the collection has no index on the columns I am scanning against.

What?!?  I know I indexed this collection…

So, I run db.geodata_geo.getIndexes() to dump my indexes and … I … don’t see my name columns indexed.  Oh, I remembered to index the the ID and Code columns…but somehow, indexing the Name columns completely slipped past my lower brain-pan.

I add these indexes to my collection:

> db.geodata_geo.ensureIndex({ cityName : 1 });
> db.geodata_geo.ensureIndex({ stateName : 1 });

And then I rerun the query plan and see the following output:

> db.geodata_geo.find({ cityName : “Anniston”, stateName : “Alabama”}).explain();
{
“cursor” : “BtreeCursor cityName_1″,
“nscanned” : 2,
“nscannedObjects” : 2,
“n” : 1,
“millis” : 101,
“nYields” : 0,
“nChunkSkips” : 0,
“isMultiKey” : false,
“indexOnly” : false,
“indexBounds” : {
“cityName” : [
[
"Anniston",
"Anniston"
]
]
}
}

Instead of BasicCursor, I see BtreeCursor which gives me a happy.  I also see that the nscanned and nscannedObjects values are now more realistic…seriously:  2 is a LOT better than 3.6 million something, right?  Another happy for me!

I score the third happy when I see that the millis has dropped down to 101:  0.101 seconds to execute this search/query!  Not jaw-dropping, I agree — but acceptable considering that everything is running off my laptop…I know production times will be much, much lower.

 

In the end, I learned that a simple tool like .explain() can tell me where my attention is needed when it comes to optimization and fixing even simple, seemingly innocent queries.  Knowing what you’re looking at, and what you’re looking for, is pretty much thick-end of the baseball bat when it comes to crushing one out of the park.

I hope this helps!

 

Reference Link:  Explain

Searching MongoDB Sub-Documents…

Category : Technical
No Gravatar

I’ve recently finished a mongo collection that stores all auditing data from my application — specifically, it records every database transaction, conducted in either mySQL or mongo, assigning an event-identifier to the event, and storing the data under an event ID within a single sessionManger object.

Sounds good?

Well, I like it.   This design eliminated the need to maintain meta-data in my data tables since I can pull transaction history for any record that I’ve accessed.

The problem is that, being new to mongodb, accessing what I’ve put into mongodb isn’t (yet) as intuitive as, say, my mySQL skills are.

Sub-documents within a mongo document are analogous to the results of a mySQL join.  One of the key motivators in storing this information in mongodb to begin with was that I could de-normalize the data by storing the sub-document with it’s parent instead of having to incur the expense of a search-join-fetch later.

Traditionally, any data objects defined as a one-to-many type of a relationship (1:m) were stored in multiple mySQL tables and were accessed via some sort of join mechanism.

Mongodb breaks that traditional mold by allowing you to store a sub-document (the “m” part of the 1:m relationship) within the same document in which you’re currently working.

Using my sessionManger document, I have a document that looks something like this:

{
_id : somevalue,
foo : bar,
event : {},
argle : bargle,
}

My desire is to, for every database event that is recorded, enter information about that event within the sub-document that I’ve wittily named “event”.

In my PHP code, I’ve written a sequence manager for mongo that maintains a document containing sequence values for various tables.  Think of this as the functional version of mySQL’s auto-increment feature.  I decided, then, for the sessionManager events, I would use this key sequence to obtain unique values and use those as my sub-document index.  I’d then store whatever data I needed to store using the sequence value as a sub-document key, or index:

{
_id : somevalue,
foo: bar,
event : {
n : {
created : dateval,
table : tableName,
schema : dbSchema,
query : lastQuery
}
}
argle : bargle
}

So, when I need to add another event, I just create a new sub-document under the event key, then add the data I need to store under the sub-document index key.

Worked like a champ!

And then I asked myself:  ”So, Brainiac, how would you go about extracting event -n- from your collection?”

I went through a lot of failed query attempts, bugged a lot of people, googled and saw stuff  that led me down many plush ratholes until I finally, through some serious trial-and-error, got the answer…

> db.mytable.find( { foo : bar }, { ‘event.n’ : 1 } );

where n = the number of the event I want to find.

If I want to get all of the events for a particular document (sessionManger object), then I would write something like:

> db.mytable.find( {foo : bar}, { event : 1});

If I wanted to return all of the events for all of the objects, then I would write this:

> db.mytable.find( {}, {event : 1});

What I’ve not been able to figure out, so far, is how I can use $slice to grab a range of events within a document.  Everything I try returns the full sub-set of documents back to me.  The doc tells me that $slice is used to return a subrange of array elements, which is what I thought “event.n” was but, apparently, it’s not.  (I think it’s an object (sub-document) which is why $slice fails for me.)

It’s not a big deal because, programmatically, I can grap the entire sub-document from it’s parent and parse in-memory to get the desired record.  And, if I know what the value for -n- is, then I can fetch just that one sub-document.  So, I’m ok for now.  However, please feel free to enlighten me with your expertise and experience should you see where I am failing here, ok?

 

Web Services with PHP & nuSoap – Part 1.1

Category : Technical
No Gravatar

Introduction

[EDIT] – This is a re-hash of a document I wrote a couple years ago.  There’s been changes to the nuSOAP library and I wanted to document the updates relative to the tutorial series.  Also, I need to fix a lot of the broken-links and code listings since I’ve changes hosting providers since this article was first written.

This article is specific to nuSOAP release 0.9.5 on 2011-01-13.  This tutorial was updated on September 22, 2011.

Once upon a time, I was tasked with developing a web-services by my boss as the integration point between our production application and Sales Force.  At this point, although I’d heard of web services … kind of … didn’t Amazon use web services for something?  Still, I’d never coded a web-services based application before.

At this point, I have to assume you are unfamiliar with the concept of web services and why you may have to create and provide a web-services offering to your client base.  Web services allow a remote client to access functionality (as defined by you, the programmer) via the standard HTTP interface (normally, port:80) to your application and database services.

Back in the day, networking services (semaphores, pipes, streams, message queues, and other forms of IPC) were custom-written and assigned/slaved to unique networking ports for accessing specific service daemons.  Of course, the internet was a kinder, gentler place back then… and a given server may have had dozens, or even hundreds, of non-standard ports open, listening, waiting, for networking service requests.  Or hacking attempts.

Today, web-services is a replacement to dedicated networking apps – all handled by your web server, and all serviced over the same network port: port 80.  Since port 80 is a standard port and, usually, already open on a web-server, additional security risks by opening new non-standard ports for networking services are averted.  Your web-server, such as Apache, now has the responsibility of processing the request and delivering the results to the client.

The web-services component piece is a collection of functions that you’ve written that provide remote clients access to your system.  These functions are accessible only via the web services framework and while they may be duplicated from a dedicated and traditional web-based application, the web-services framework is designed as a stand-alone piece of software.  Think of the web-services piece as your application’s data-processing layer minus the presentation layer.  The “M” and “C” of the “MVC” model.

Initially, when I was tasked with a similar set of objectives, I initially tried xml-rpc.  This led me down a rabbit-hole that spanned nearly a week of my time with the end-result being abandonment.  I hit road-blocks with xml-rpc over server authentication and passing complex objects.  Exacerbating the issue overall is that xml-rpc seems to be dated technology – I had a hard time locating resources that were recent.

Then I stumbled across nuSOAP – it’s free via sourceForge, stable and, using a quote from sourceForge:

“NuSOAP is a rewrite of SOAPx4, provided by NuSphere and Dietrich Ayala. It is a set of PHP classes – no PHP extensions required – that allow developers to create and consume web services based on SOAP 1.1, WSDL 1.1 and HTTP 1.0/1.1.”

nuSOAP seemed to have more of everything available: tutorials, examples, articles, blog posts.  When I started my implementation with nuSOAP, the first thing I received help with was server-level authentication.  I was able to immediately get my remote requests validated by the web-server and handed off to the web-services module!

The major selling point, for me, on nuSOAP is that nuSOAP is self-documenting.  As part of the API functionality, nuSOAP generates HTML pages that documents the exposed services via the WSDL  and also provides you with a complete XSLT definition file!

First off, download and install the nuSOAP libraries – I provided a link to the sourceForge site a couple paragraphs ago – and unpack the tarball.  You’ll end-up with a directory (mine is called: ./nuSOAP) and, within that directory, is the one file you include: nusoap.php.

There are two pieces to this tutorial — a server side piece and a client-side piece.  While you can execute both pieces off the same environment (machine), normally you’d use the client remotely to access the API server-side code.

What’s Not Covered:

Apache.  Apache configuration for your vhost.  Apache .htaccess.  This article assumes that you’ve a working server and that you’re able to install and access the server files via Apache.  Even if your Apache server is a localhost configuration, access the server-side files via localhost client, traversing the TCP stack locally, is still a valid method for testing your web-services server application.

 

Time to push up our sleeves and start working on the server code…

The Web-Services Server

Today, we’re going to write a ping server — where the server has an exposed service (method) named “ping” which takes a single argument (a string) and returns an array back to the calling client.  The return array contains two associative members: a boolean (which should always be true – otherwise there are other issues…) and a string which is the modification of the original string in order to prove that, yes, we went there and we came back.

Because my project, and I’m doing this project for my new company, is going to represent significant effort, size and complexity, I’ve broken out the components of nuSOAP request into exterior files because, later, these will become control file which will, in turn, load files that have been organized into a hierarchy friendly to the application’s data model.

So, if this file, which I’ve named index.php, seems small, remember that you’re not viewing the dependent files (yet).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<pre><?php
/**
 *
 */


// setup...
require('./nuSOAP/nusoap.php');
// set namespace and initiate soap_server instance
$namespace = "http://myapi/index.php";
$server = new soap_server('');
// name the api
$server->configureWSDL("myapi");
// assign namespace
$server->wsdl->schemaTargetNamespace = $namespace;
// register services
require('myServiceRegistrations.php');
// load WSDL...
include('mywsdl.php');
// load services code modules...
require('myServicesModules.php');
// create HHTP listener:
$request = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : '';
$server->service($request);
exit();
?></pre>

So far, so good – let’s take a look at what we’ve just done:

  • we’ve included the nu_soap library…
  • declared our namespace (which is the URL of the api server)
  • instantiated a new soap_server instance and assigned it to the variable $server
  • initialized the WSDL
  • assigned the namespace variable to the WSDL
  • load and register our exposed services
  • load the WSDL
  • load the service code
  • create the HTTP listener
  • invoke the requested service
  • exit

This (index.php) file is the server-side file that will be invoked for ALL future API calls to the service.  It invokes three control files which, in turn loads the services (WSDL definitions), the WSDL variable definitions (think of these as inputs and outputs to your exposed services), and the actual code for all of the services, and their supporting functions, that you’re going to expose via your API.

Side Note:  This is the file you’ll reference in Apache when you’re (preferably) creating a new Virtual Host for the API.  HTTP requests that resolve to your server will be serviced by Apache which will, in turn, serve the results of this program back to the client.

Next, we’re going to define the myServiceRegistrations.php file that is required by index.php.  This file contains the WSDL for each and every exposed service that the API serves.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<pre><?php
/**
 *
 *
 */


$server->register('ping', array('testString' => 'xsd:string'),
                          array('return' => 'tns:aryReturn'),
                  $namespace, false, 'rpc', 'encoded',
'<p><strong>Summary</strong>: returns response to a ping request from the client.  Response
includes testString submitted.  Used to test Server response/connectivity.
</p>
<p>
<strong>Input</strong>: $testString (type: string) and random collection suffices.
</p>
<p>
<strong>Response</strong>: Service returns an array named $aryReturn with two associative
members: &quot;status&quot; and &quot;data&quot;.<br />$aryReturn[&quot;status&quot;] should
<i>always</i> return true.  (A time-out is an implicit false.)<br />If no value was passed
to the service via $testString, then the value of $aryReturn[&quot;data&quot;] will be
empty.<br />Otherwise it will contain the string: &quot;Rcvd: {yourString} (count)&quot; to
show that the message was received and returned. (count) is a character count of the
passed string, also validating that the passed data was received and processed.
</p>
'
);</pre>

This PHP code registers a function called “ping” with the nuSOAP $server instance.  The second parameter is the input to function.  Note that all input (and output) parameters have to be declared as an array even if there’s only a single value being passed.  Also notice that you have to type-cast the variable being passed using XML datatypes.  For your data definitions, you use one of the 44 built-in datatypes defined in this document: http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/.

(For more information on XSD object and XML schema, please visit: http://ws.apache.org/axis/cpp/arch/XSD_Objects.html.)

The third argument to the method “register” is the data type returned.  This is a complex variable called “aryReturn” — don’t worry about right now, we’re going to define this complex-type variable in another configuration file.

The next four arguments are:

  • our $namespace variable we set in index.php
  • boolean false
  • ‘rpc’ for the call type
  • ‘encoded’
Use these values literally.
The last variable is a huge block of HTML.  This block of HTML can be as large, or as small, as you need it to be.  It’s the basis for the WSDL documentation that nuSOAP generates for your client-side developers.
When developers hit the server URL, they’ll be presented with your API documentation that nuSOAP generates from the WSDL file(s).  As your API grows, exposed methods will be listed in the blue-ish box on the left side of the screen.  Clicking on any of the methods will expose the details (requirements) about that method thus:

Nice, huh?

The second file (that we’ve included in our source: (mywsdl.php)) is the WSDL file that defines our data structures that are used as either inputs, outputs, or both, to the exposed services.  Another thing I like about SOAP and nuSOAP is that it introduces a layer of strong-typing to the PHP stack.  You can save a bit of debugging time by requiring that you call a method with EXACTLY this and return EXACTLY that.  Anything else tends to generate a system-level error:

1
2
3
4
<pre>[Thu Sep 22 14:36:59 2011] [error] [client ::1] PHP Fatal error:  Call to a member function addComplexType() on a non-object in /htdocs/LL2/trunk/services/mywsdl.php on line 9
[Thu Sep 22 14:36:59 2011] [error] [client ::1] PHP Stack trace:
[Thu Sep 22 14:36:59 2011] [error] [client ::1] PHP   1. {main}() /htdocs/LL2/trunk/services/ll2wsdl.php:0</pre>
<pre>

This error message, from the apache error log, is somewhat obfuscated in it's meaning.   I attempted to return only the string, by itself, instead of returning the array (of two elements) that I had told nuSOAP I would return for this service.  This error was generated because the types (between the code and the WSDL) of the return variable (structure) did not exactly match.

If you've only ever coded in a loosely-typed language, like PHP, than this part of SOAP is going to be a bit of a ... transition ... for you.  When we say that something, be it a variable, function, or exposed service, is strongly typed, we're declaring the type of that object and, if the type of the object during run-time does not match, then SOAP will force PHP to throw a fatal as shown in the error log snippet above.

Keep this in-mind as you develop exposed services that are increasingly complex.  Since the error messages tend to point you at your code, at the point of failure, it's easy to forget that that the requirements of the underpinnings (in this case, the WSDL), are the root cause of your PHP fatals.

That being said, let's take a look at the WSDL for our ping service:

1
2
3
4
5
6
7
8
9
10
11
<pre><?php
/**
 * WDSL control structures
 *
 * initially, while in dev, this will be one large file but, later, as the product
 * matures, the plan will be to break out the WSDL files into associative files based
 * on the object class being defined.
 */

$server->wsdl->addComplexType('aryReturn', 'complexType', 'struct', 'all', '',
            array('status' => array('name' => 'status', 'type' => 'xsd:boolean'),
                  'data'   => array('name' => 'data',   'type' => 'xsd:string')));</pre>

We're invoking the nuSOAP method addComplexType to define a structure to the WSDL in our Table Name Space (tns).   To do this, we first define the name of the structure that we're going to use: aryReturn and then we define the composition of that structure.

The declaration for this looks a lot like a standard PHP declaration for an array with the exception of the XSD (XML Schema Definition) appended at the end of each element's declaration.  (See the links I embedded above for explanations and examples of valid XSD.)

XSD provides part of the strongly-typed concept for our structure elements.  We're telling nuSOAP to expect a variable structure containing these named elements of this type.

What we have, then, is an associative array with two elements: 'status' and 'data'.  $aryReturn['status'] and $aryReturn['data'] and they're of type BOOL and STRING respectively.

Note, finally, that this variable structure isn't confined to single-use.  Once we've declared it within our tns, it's available to any exposed service where it's needed.  This is the model for my common error structure -- the boolean indicates success or fail on some service operation and the data component contains the relative diagnostic information.

The third and final file we're including into the server source is the code for the exposed service.  This is where you write the function handlers for your services.  Since you've already defined the input parameters, and the return types for the ping service, there's very little left to do.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<pre><?php
/**
 * ping
 *
 * service that confirms server availability
 *
 * input: any string
 *
 * output: reformatted string:  "Rcvd: {string} (charcount{string})"
 *
 * @param string $testString
 * @return string
 */

function ping($testString = '') {
    return(array('status' => true, 'data' => 'Rcvd: ' . $testString . '(' . strlen($testString) . ')'));
}</pre>

Note the following in the code for our exposed service:

  • our exposed method is named ping because that's how we registered the service (myServiceRegistrations.php)
  • we're providing a default type-cast for the input param of string in case the service is invoked without input params.
  • we're returning BOOL true and prepending "Rcvd: " to the received string, and appending a character count to prove that the service successfully responded to the client's request.
  • the return structure exactly matches the WSDL declaration: the name of the array elements, and the element types.
If you've correctly installed and referenced (within your PHP) the nuSOAP libraries, then you should be able to load the url of the new server source file into your web browser to see the nuSOAP-generated documentation for your new web services.  Click on the WSDL service function: ping to see a detailed description of the function.

If you're using IE, then clicking on the WSDL link will return the XML.  If you're using Firefox, Chrome or other browsers, clicking on WSDL will display the generated XML for your service.

Now that the server is working on it's own, it remains fairly useless until we can get a client to connect to it invoke it's methods.  Let's work on the client next...

The Web-Services Client

The web services client application will also be written in PHP.

The web client is an application that connects to remote server using the http port 80.  To do so, you'll need the client to be aware of certain bits of information that may, or may not, be required to access the remote server.

In our client, we're not going to require remote authentication -- but I'll take a quick aside andexplain how you would include this, client-side, if your server required .htaccess authentication.

nuSOAP has a client method called setCredentials which allows you to specify your .htaccess username and password and the authentication schema.  It's a single line of code which is normally used to require not only clients to login to access your API, but, once identified, you can limit the set of exposed methods available to individual clients or groups.

For example, if you have a product you've developed in-house, then you'd want full-access for your front-end web/applications servers.  Your PM later decides to open a subset of the API to the general public and a subscription-based set of exposed methods in order to monetize your product.  Finally, the PM also wants to "white-box" the product so that other companies can use it but with their branding and access to isolated or discrete data sets.

What you'd end-up with is several levels of client access to your web services.  Implementation of limiting exposed services would be handled server-side but it would be based on your client's authentication and possibly the subject of a future tutorial...

So, to the client-side code: (name this file: apiTestClient.php)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<pre><?php
// Pull in the NuSOAP code
require_once('./nuSOAP/nusoap.php');
$proxyhost = isset($_POST['proxyhost']) ? $_POST['proxyhost'] : '';
$proxyport = isset($_POST['proxyport']) ? $_POST['proxyport'] : '';
$proxyusername = isset($_POST['proxyusername']) ? $_POST['proxyusername'] : '';
$proxypassword = isset($_POST['proxypassword']) ? $_POST['proxypassword'] : '';
$useCURL = isset($_POST['usecurl']) ? $_POST['usecurl'] : '0';
$client = new nusoap_client('http://{YOURSERVERURLHERE}/index.php', false, $proxyhost, $proxyport, $proxyusername, $proxypassword);
$err = $client->getError();
if ($err) {
    echo '<h2>Constructor error</h2><pre>' . $err . '</pre>';
}
$client->setUseCurl($useCURL);
$client->useHTTPPersistentConnection();
// Call the SOAP method
$result = $client->call('ping', array('testString' => 'Argle'), 'http://localhost');
// Display the result
if (!$result) {
    echo "service returned false";
} else {
    print_r($result);
}
unset($client);</pre>
<pre>?></pre>

The first thing we do in our client-side code is to include the nuSOAP libraries.

The next five lines of code read from the local POST environment, testing if you've established a proxy for your web-services server and, if so, populating the proxy variables.

The next line instantiates a nuSOAP client and associates it with our remote server.  Change "YOURSERVERURLHERE" to the name of your apache web server URL where you have the server side code installed.  (e.g.: localhost, myserver.com, etc.)

Note the name of the function call: newsoap_client()...as opposed to using the function soapclient().  This function name is legacy-compatible with PHP 5.0's instantiation call:  new soapclient() - the PHP SOAP extension uses the same instantiation function name as the nuSOAP library.  If you have both installed, (PHP 5.0 SOAP extension, and the nuSOAP libraries), executing the client will return errors as you've overloaded the soapclient() function.  (You're calling the PHP SOAP function with the nuSOAP function parameters.)  Rename the soapclient() function to the back-compatible function: newsoap_client().

The next two lines tell the nuSOAP client to useCurl, when possible and if previously saved and, if possible, to use HTTP persistent connections.

Next, we're going to consume the web-services from the server by making a call to the nuSOAP method: call().  The arguments to this method are:

  1. the name of the service being consumed (ping)
  2. the input string (note:  all inputs must be passed as arrays!)
  3. the namespace URI (optional:  WSDL can override)

Store the results of the web-services to the aptly-named variable $results and evaluate it upon return.  If the client call was not successful, then display an error message.

If the client call was successful, then display the contents of the returned array.

Note that you can run this client code from either a browser using the "file://" option or, if the client source code is accessible to apache, then you can display using a browser.

When I run this client-side software in the browser, I get displayed back:

Array ( [status] => 1 [data] => Rcvd: Argle(5) )

So, how do we know that the client went out and successfully returned from the server with the data?  Simple - in the client source, you will not find the literal "Rcvd:" anywhere in the source.  This data was supplied by the server-side function and returned back to the client.  It's a simple example, but it provides proof-of-concept that we're successfully able to connect to a remote web-server and return data created on the remote server back to the client program.

Let's wrap this up...

Summary:

This tutorial (hopefully) explained what web-services are, and provided you with a practical example of a consumable service: ping().  Such a service would normally be invoked as a means of testing server availability.

We created a web-services server file using the nuSOAP library by defining a complex-structure (an associative array) and registering a method with the nuSOAP server.  The method takes an input parameter which, although it's only a single input parameter, must be built and passed as an array construct to the server method.  Next, we showed that by loading the server source into a browser, we learned the nuSOAP provides built-in documentation for your web-services structures and methods.  Which is a very nice-to-have when you're creating your developer documentation!  Next we created the web-services client source code file which connected to our remote server and invoked the server's method: hellow().  If all worked correctly, you displayed the string returned from the remote server within your browser window.

In the next installment, I'll cover the web-services server-side of this business and we'll see how to create the various methods for accessing a mySQL database, complex structures as input and output parameters to those methods, and general debugging techniques.

Thank-you for your patience - I hope this article helped you.

September 26, 2011

X11Forwarding from CentOS 6 Linux to Mac OS X Lion via SSH

Category : Technical
No Gravatar

In my previous post, I wrote about getting gpass (a password manager for the gnome desktop) compiled from source and running on our CentOS 6 platform.  The screenie I took of the welcome screen was a mac-i-fied version.

I had configured my Linux machine to support X11 port-forwarding over a secure shell.  It was surprisingly quick and easy to set-up and execute.

I wanted to remote-display the gpass window to my Mac OS X Lion desktop because I needed to transfer passwords from my 1Password application (running on Lion) to my gpass (Linux) program.  Some of the passwords are pretty gnarly so the only way I can guarantee transferring data without making typos was to set-up a copy-paste-friendly environment.

One quick caveat. I’ve noticed that, when I terminate an X11 program from my Lion shell, I can no longer use that shell to initialize another X11 applet.  I need to exit and re-start the terminal.  If you know of the work-around for this, please leave a comment/reply to this post.

For all the following commands, it is assumed you have sudo privileges on your Linux system.

The first step I took was to edit the /etc/ssh/ssh_config file.  At the end of the file, past the comments, there is a section labeled:

Host *

ForwardX11Trusted yes
X11 Forwarding yes

Make sure that you have those two lines, uncommented and present, in your configuration.

Next, (re)start your sshd server:

# /etc/init.d/sshd restart

Stopping sshd:                                         [ FAILED ]
Generating SSH1 RSA host key:         [      OK      ]
Generating SSH2 RSA host key:         [      OK      ]
Generating SSH2 DSA host key:         [      OK      ]
Starting sshd:                                           [      OK      ]

 

In case you’re curious, the FAILED message in the first line of output was generated because I didn’t already have sshd running on my system.

My machines run on a 192.168 subnet behind two firewalls – the firewall on my DSL modem, and the firewall on my multi-port router.  Normally, I’m not too concerned about the security of my individual machines.  (e.g.: I’m not running a software firewall on my Mac or my Linux server.)  My subnet is DHCP-served by my router and the router is on it’s own subnet DHCP-served by the dsl router/modem.

I need to obtain the current IP address of my linux server which I do so my running the ipconfig command.

Next, I switch over to my Mac and open a terminal — within the terminal, I enter:

iMac:~ mike$ ssh -X 192.168.0.6
The authenticity of host '192.168.0.6 (192.168.0.6)' can't be established.
RSA key fingerprint is f9:04:2d:0e:70:3d:a7:8f:92:c0:02:69:8c:f2:e6:51.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.0.6' (RSA) to the list of known hosts.
mike@192.168.0.6's password:
whassup?
/usr/bin/xauth: creating new authority file /home/mike/.Xauthority
[mike@codeMonkey ~]$

At the command prompt, I now only have to enter whatever X11 command and that program will be displayed on my Mac Desktop.  I can even open and start an entire desktop session.  I could – but I won’t — my Linux server only has 2gB of Ram…

Instead, I’ll open a gnome-terminal.  So, at the prompt, I simply type: gnome-terminal and I get the gnome-terminal to appear on my desktop:

That’s pretty much all there is to it, as far as I could tell.  Eazy-peezy.

One last note — once you have a terminal running on your Lion desktop, then any X11 commands, such as gpass, you enter will all be displayed on your Lion desktop.  This circumvents the one-terminal-one-applet restriction I mentioned at the top of this article.

That’s pretty much it for this article — hope this helps!

Installing gpass on CentOS 6 Linux

Category : Technical
No Gravatar


Over the last year I have become utterly dependent on a product called 1Password by Agile Bits software.  For those of you that are unfamiliar with this software, 1Password is a multi-platform program that manages all your passwords, in additional to other sensitive information, in an easy-to-use interface.

Originally written for the Mac, the software is now offered on iPad, iPod, ‘droid, and Windows machines.  I have it installed on all available platforms.  While initially bemoaning the cost of the product – it’s not cheap – I’ve come to depend on it for all of password storage, my software license management, and even the credit-card information for the card I use for online purchases and subscriptions.

Quick aside and then I’ll cease the fanboi gushing: my favorite feature of the program is the password generator.  I can custom-tailor a password to be as obnoxiously long, and obfuscated, as I need and I don’t ever, ever, have to type it in when challenged.  Passwords are simply copy-pasted from the 1Password program, or you can use the embedded 1-click feature functionality of the support extensions available for all browsers.

My only complaint with 1Password is the lack of Linux support.  Since I’m using Linux as my LAMP development platform while at-home, I need a comparable password manager. I know I won’t have all of the slick features of 1Password, but at least I’ll be able to copy-paste long, obfuscated, passwords from the password manager into my Linux desktop applications.

So, let’s get started!

There’s some good tutorials already available on the ‘net about doing just this – however, none I found were exactly right and, following those tutorials, I did run into several side issues.  I’ll cover all those issues here so that your installation will be seamless.

Operating System: CentOS 6 Linux
Desktop GUI: Gnome
gPass version: 0.5.1
EPEL repository: 6.5

Download the gpass source into your “Downloads” directory and unpack the tarball:

wget http://projects.netlab.jp/gpass/release/gpass-0.5.1.tar.gz

tar xvzf gpass-0.5.1.tar.gz

cd gpass-0.5.1

I based my initial install of gpass from the UnixCraft blog post here.  (In the tutorial, they omitted the arguments to the tar command to un-tar the tarball that creates the gpass source directory.)

In step 1, the blog asks you to do a group install of the development tools and, secondly, install the gnome-ui, mhash, and mcrypt development libraries.  The second step failed for me following the successful install of the gnome-ui as my stock yum configuration was unable to locate either the mhash or the mcrypt packages.

After googling the issue, I determined that I needed to at the EPEL repository to my yum configuration.  It’s common to have several repositories in your yum catalog.  You’ll add additional repositories by establishing configuration files in /etc/yum.repos.d/.

Setting up the EPEL repository is pretty easy as they’ve created an rpm just for this purpose.  Make sure you have sudo privileges on your account and enter the following commands: (I’m currently in the “Downloads” directory in my $HOME.)

wget http://download.fedora.redhat.com/pub/epel/6/x86_64/epel-release-6-5.noarch.rpm
...
rpm -Uvh epel-release-6-5.noarch.rpm

Side note: I’m aware when I’m reading how-to’s on other sites that reference software versions that said versions may not always be the current, and most stable, release available today.  I always check the repository, using a browser, before downloading to ensure I’m obtaining the latest version.

Once the rpm is installed, you’ll need to edit the repository file.  Again, using sudo, edit the /etc/yum.repos.d/epel.repo file and in the EPEL repository section, add the line: priority=3 at the end of the section.

I’m now ready to install the mhash and mcrypt packages, obtaining them from the Redhat EPEL repository.  Again, assuming sudo privileges:

# yum install libmcrypt-devel
# yum install mhash-devel

From this point, you need merely to follow the instructions in the UnixCraft blog I linked-to above, but here are the steps to finish the installation.  Again, assuming you’ve changed-directory to the gpass source:

./configure

./make

./make install

At this point, as long as you’ve not seen any error messages in your output, your gpass program is ready to use.  Test by typing gpass at the command line — you should see the gpass window pop-up on your desktop:

In the screen-shot to the right, those of you that are past your second cup of coffee may have noticed that my gpass window looks suspiciously like a Mac OS X version.

I am running the gpass application on my Linux server, but I am serving the display to Mac OS X Lion desktop.  I set-up the configuration to do this for two reasons.

  1. to capture and display screenies
  2. to copy paste data from my native Mac 1Password application into my Linux gpass application.  I do NOT want to retype some of those passwords…
That’s pretty much it.  I leave the exploration and use of gpass up to you.  I’ll do a follow-up tutorial quick-post on how-to set-up XForwarding on Linux to your remote desktop (Mac) via secure shell.
Thanks for reading – hope this helps!

 

Page optimized by WP Minify WordPress Plugin

Our weather forecast is from Wordpress Weather