some guy on the Internet

the network engineer fears the backhoe, and the systems engineer fears the network engineer

Bundler 1.10.x and Warbler 1.4.7 Are NOT Friends

| Comments

On the off chance that you are deploying Ruby apps as Java Servlets using JRuby/Warbler, beware of recent releases of Bundler. Bundler 1.10.0 came out this past Sunday (May 28, it looks like), and it doesn’t play nicely with Warbler.

The failure happens when you first use Bundler >= 1.10 to build a dependency tree prior to running warble, whereupon warble blows up with a distinctive error message like ArgumentError: dependency name must be a String, was #<Bundler::StubSpecification:0x1e69dff6.

I’m a bit perplexed by @indirect’s comment on the issue linked above that “Bundler’s internal Ruby APIs are not stable and may change”; sure, but this seems to me like the kind of change that merits a major version bump. If a method on an object used to return a String, and now it returns a Bundler::StubSpecification, that’s not an insignificant change! At least perhaps include a to_s method that spits out the string that would previously have been returned?

I dunno, maybe there’s a policy doc somewhere on the Bundler site which defines what the internal Ruby APIs are that one should assume will change at any moment; I searched a bit and couldn’t find one, but I apologize in advance if the failure is in my searching.

A Ruby Client for the SurveyMonkey REST API

| Comments

So, if you’ve had an email account for more than a few minutes, you’ve probably at some point been prompted to fill out a survey hosted by SurveyMonkey. They’re a ubiquitous online survey service.

As it so happens, they offer a RESTful API enabling customers to access their survey data programmatically. Their API is hosted by Mashery, which is some sort of API service provider; I hadn’t realized that this was a thing, but it makes perfect sense now that I think about it; you get a consistent API style, you get someone else managing your public-facing API endpoints, seems like a fine idea.

We use SurveyMonkey at work to implement our user surveys, and so of course we will want to pull statistics about these surveys into our analytics system. To this end, I have written and published a Ruby client library for the SurveyMonkey API. World, meet surveymonkey (GitHub)!

I hope this will be of some use to anyone besides me; I couldn’t find another Ruby API client, so I figured that writing my own would be a decent programming exercise.

Pull requests are gratefully accepted!

P.S. Yes, I realize that to be a real Ruby Gem, the library should really be called “cattywompus” or “moscow_mule” or “velvet_underground” or some such; I promise my next one will have a sufficiently preposterous name :)

Moments of Transition

| Comments

I had a post I had been meaning to write this evening. I’d been thinking about it since I woke up; essentially, last night an acquaintance posted something on a mailing list (in the course of a discussion of a code of conduct) that really brought home to me that there’s a certain population in my community with whom I simply do not share common values and priorities. Sure, this is not unheard-of, and is not really news to me, but it’s still a bit saddening and alienating to realize it so viscerally, and so I was going to write about that feeling, and then get up on my soapbox and declaim for a while.

Instead, though, I spent the evening catching up with a bunch of my old colleagues from Harvard. It sounds like their situations have improved in the year since I left, and I could not be happier to hear this; there was still plenty of sysadmin ranting, but compared to our last meetup, it seems like there was a lot more “check out this awesome thing” instead of “you won’t believe what happened today”.

Then I walked home through the chill of the evening, parting ways with one friend after another as we went, until I was walking with my buddy Tim, talking about the trajectories of our lives. He’s on the cusp of several big changes right now, and mentioned that he really appreciated the opportunity to make a conscious choice about what kind of life he wanted to lead, and what sort of a person he wanted to be. That resonated with me; I feel like I’ve had a lot of my life just sort of happen to me, and that a lot of the influence that I’ve exerted over the course of my life has been indirect and diffuse. I’m not particularly unhappy about this, but I do want to be more cognizant of times when I am at an inflection point, or a distinct moment of transition, rather than just moving gradually from one state to another.


Speaking of moments of transition, I recently got a new pair of glasses. This is actually a really big deal for me; I dearly love my previous pair, which are delicate, rimless constructions of articulated metal and glass, nearly insubstantial. For a number of years I’ve worn them and frequently forgotten that I have them on my face. I’ve stuck with them through a few prescription changes, lens upgrades, and so forth.

However, my optician has been having more and more difficulty working on them every time I’ve brought them in to be adjusted. They are many years discontinued at this point, and no spare parts are to be had (and they have a sufficiently bizarre and impractical design that stealing components from other models is not really an option). Plus, they are finally starting to show their age; they need adjustment ever more frequently, and for the past year they haven’t really wanted to stay on my face; it’s only a matter of time and probability before they go flying off at some inopportune moment and meet with an irredeemable misfortune of some sort.

My new glasses are more substantial; the first day I wore them to work, one of my teammates asked “Have I seen you with glasses before?” (I’ve worn these glasses during the entirety of my tenure at RunKeeper). I got them at the beginning of the week, and took them to be fitted yesterday. I’m still getting used to them, but we’re off to a good start: no more double vision from misaligned lenses! no more constantly pushing them up my nose! no more trying to tighten tiny bolts using only my fingernails!

Doesn’t mean I won’t miss the previous pair, though. Ave atque vale, old friends.

"kio yamato glasses"

I Have Become a Stereotype

| Comments

So, a regional Comcast outage (and associated interruption of my home broadband connection) has finally completed my transformation into full-blown DevOps Hipster.

I have packed my sticker-festooned MacBook and my vintage studio headphones into my messenger bag and strolled off to the local coffee shop (bearing in hand a burnt-out CFL bulb, to be recycled at the hardware store en route), there to finish out the workday stroking my goatee while I contemplate the AWS console and try to write some Java. :)

GitHub Third-Party Application Access

| Comments

My buddy Phil recently encountered an interesting example of unintended consequences by starting down the (initially nonthreatening) path of trying to integrate a third-party site with a GitHub organization. It turns out the default access control configuration of a GitHub organization allows any member of the organization to grant third-party apps access to the data in that organization.

Go back and read that again.

Then read Phil’s writeup for some more details. This gives me plenty to think about.

Serving Index Pages From a Non-root Location via CloudFront

| Comments

As a change of pace from my last post, I now present you with a wonky AWS issue, created mostly by departing from Amazon’s standard pattern of S3 static site hosting; hopefully this will be useful to someone else in a similar position.

So, as you may be aware, you can use AWS S3 to host a static website. Furthermore, once you’ve done so it’s not difficult to use AWS CloudFront (the content distribution network service) to serve out your site; the CloudFront workflow is clearly set up for you to put CloudFront in front of a S3-hosted website (such as this blog!).

As long as your site is laid out something like the following, you’ll be in good shape:

/
/index.html
/images/
/images/foo.png
/images/bar.png
...

However, you can run into odd behavior if your site contains paths like this:

...
/some_subdirectory/index.html
...

and you want to be able to browse to http://your-site.tld/some_subdirectory/ and have the server automatically serve out the index page. Since S3 doesn’t really have a notion of a directory hierarchy, but rather has a flat structure of keys and values with lots of cleverness that enables it to simulate a hierarchical structure, your request to CloudFront gets converted into “hey S3, give me the object whose key is some_subdirectory/”, to which S3 correctly replies “there is no such object”.

When you enable S3’s static website hosting mode, however, some additional transformations are performed on inbound requests; these transformations include the ability to translate requests for a “directory” to requests for the default index page inside that “directory” (which is what you want), and this is the key to the solution. In brief: when setting up your CloudFront distribution, don’t set the origin to the name of the S3 bucket; instead, set the origin to the static website endpoint that corresponds to that S3 bucket.

Huh? Ok, here’s an example:

Losing Weight

| Comments

[Content warning: I’m about to talk about my personal weight loss. I’ll do my best not to repeat the same fatphobic crap you’ve seen before, but at the same time, be warned that I am dissatisfied with my own weight and have been working on changing both the weight and the dissatisfaction.]

I’ve always been on the heavy side.

I learned the word “percentile” from the pediatric growth charts in my childhood doctor’s office, in the context of discovering that I was always above the 75th percentile in weight for my age (though not always in height). The first time I ever remember being ashamed of my own body was in grade school, sitting on the flat plastic seat of a swingset and noticing how my thighs squashed against each other; from then on I disliked wearing the navy blue uniform shorts my school required in warm weather, even though the navy blue slacks were hot and heavy in the spring and early summer in the muggy Ohio River valley. I remember not being able to run as fast as other kids, or as long, but at the same time I wasn’t prodigiously strong like some of the other oversized kids in my class.

As I got older, I got into better shape (thanks first to martial arts, and then to cross-country running; I wasn’t particularly spectacular at either, but I was good enough). Cross-country in particular was the refuge of my overachieving circle of friends during our high school years, when we decided that we should probably do some sort of sport, but needed to find one without an overly onerous time commitment. My undergraduate years, however, were not great for me as far as my weight was concerned. I stopped exercising, and between the dining hall and the late-night house of pizza across the street, my freshman fifteen (ha!) was more like a freshman eighty-three.

Eventually I found myself a comically stereotypical systems administrator (beard, ponytail, cargo pants, paunch encased in black t-shirt). The ponytail was the first to go, and the glasses came to replace it, but the paunch stayed throughout the years.

So what?

SSL Root Certificate Hashes

| Comments

Speaking of things about SSL that I am tired of forgetting:

Programs that use OpenSSL libraries (including the OpenSSL command-line tools) can sometimes need handholding in order to find their certificate authority root certificates. For example, here’s me trying to verify that a newly deployed certificate is valid:

1
2
3
4
5
$ openssl s_client -connect ${HOSTNAME}:443 </dev/null
CONNECTED(00000003)
depth=1 /C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance CA-3
verify error:num=20:unable to get local issuer certificate
verify return:0

What you say? I tested this cert on my workstation before deployment, and I don’t see any certificate errors in Chrome or Firefox. Hm… oh, facepalm, I forgot to tell it where to look for CA roots.

1
2
3
4
5
$ openssl s_client -CApath /etc/ssl/certs -connect ${HOSTNAME}:443 </dev/null
CONNECTED(00000003)
depth=1 /C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance CA-3
verify error:num=20:unable to get local issuer certificate
verify return:0

Um. The CA root is totally in that directory, I checked. Paul Heinlein’s OpenSSL Command-Line HOWTO to the rescue!

In brief: it’s not sufficient to simply drop the CA root into your certs directory, you also have to create a symlink based on the hash value of the root certificate. Paul provides a helper script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/bin/sh
#
# usage: certlink.sh filename [filename ...]

for CERTFILE in $*; do
  # make sure file exists and is a valid cert
  test -f "$CERTFILE" || continue
  HASH=$(openssl x509 -noout -hash -in "$CERTFILE")
  test -n "$HASH" || continue

  # use lowest available iterator for symlink
  for ITER in 0 1 2 3 4 5 6 7 8 9; do
    test -f "${HASH}.${ITER}" && continue
    ln -s "$CERTFILE" "${HASH}.${ITER}"
    test -L "${HASH}.${ITER}" && break
  done
done

You need to run this on the host where your client is, not the host that is serving out your SSL-enabled content! And now peace and tranquility are restored:

1
2
3
4
5
6
7
8
9
10
$ certlink.sh <CA ROOT CERTIFICATE>.crt
$ openssl s_client -CApath /etc/ssl/certs -connect ${HOSTNAME}:443 </dev/null
depth=2 /C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance EV Root CA
verify return:1
depth=1 /C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance CA-3
verify return:1
depth=0 /C=US/<SOME OTHER CN>
verify return:1
DONE
CONNECTED(00000003)