Ruby LDAP Membership Query

01.20.2014 15:15 by kbeckman | Comments

Omniauth is easily one of the most often used Ruby gems in my toolset. Recently, I needed to use an Omniauth plugin that I haven’t used before – omniauth-ldap. Getting omniauth-ldap setup and configured was relatively straightforward; and as advertised, it provided authentication via LDAP query against the organization’s domain controller.

 

Unfortunately (and a bit unexpectedly) though, the contents of the Omniauth authentication hash didn’t contain a list of groups the user belonged to – a requirement for our authentication strategy. The applications I’m working need to translate a given user’s LDAP group membership(s) into application roles for authorization purposes. So as it turns out, after the initial LDAP call for authentication (via omniauth-ldap), I needed to make an additional query against the LDAP provider to get a list of the user’s memberships to parse and convert to application roles.

 

Here’s the short Ruby shell script I whipped up to test making an additional LDAP query for user memberships before I built a few classes and actually integrated it into the application. The @ldap_settings[:auth] hash are the account details used to connect and issue the LDAP query. The values of @ldap_settings and @base will change for your environment (I removed the actual values in this example).

 

#!/usr/bin/env ruby

require 'net/ldap'
require 'pry'

class MembershipQuery

  def initialize
    @ldap_settings = {
        :host => 'ldap-server',
        :auth => {
            :method   => :simple,
            :username => 'CN=Ldap Auth,OU=Authentication,OU=Service Accounts,DC=YOURDOMAIN',
            :password => 'password'
        }
    }

    @base = 'DC=YOURDOMAIN'
  end

  def query(username)
    result  = nil
    ldap    = Net::LDAP.new(@ldap_settings)
    filter  = "(&(objectClass=user)(sAMAccountName=#{username}))"

    if ldap.bind

      ldap.search(:base => @base, :filter => filter) do |object|
        result = object.memberof
      end
    else
      raise 'Authentication Error!'
    end

    result
  end

end

puts MembershipQuery.new().query('Ric_Flair')

A Quick Intro to OpenSSL Asymmetric Encryption

10.03.2013 05:16 by kbeckman | Comments

This post is my summary of the lightning talk for tonight’s Ruby Meetup in Nashville. I should have known that I’d need a bit more than five minutes to cover an intro to OpenSSL asymmetric encryption… Thank goodness for blogs! In the spirit of a lightning talk, I’ll keep this short an cover only the high-level points of this topic.

 

A Quick Primer…

Asymmetric cryptography is important in a number of applications and protocols that we use every day and it’s beneficial for software developers to know at least a little bit about the topic. Let’s start with a quick definition… Asymmetric cryptography is a type of cryptographic algorithm that utilizes private/public key pairs for encrypting data. In simplistic terms, data encrypted with a public key may only be decrypted with its corresponding private key and data signed with a private key can be verified with its corresponding public key.

 

One important use for asymmetric cryptography is for the generation of “digital signatures”. Digital signatures are useful for verifying the identity behind an email message or authentication token (SAML tokens anyone?). Digital signatures are also used to verify the authenticity of software packages. To generate a digital signature, an entity will generate an encrypted hash [serving as the “signature”] based on a digest algorithm (message digest) and some message content (i.e. an email message) using their private key. The message is then sent to a third party along with its digital signature. To verify authenticity of the original message, a third party verifies the encrypted signature hash by using the sender’s public key to decrypt the signature hash and compares the decrypted contents with its own hash of the original message content. If the message hashes match, the message is said to be authentic. If the hashes do not match, the message has been tampered with in some way before it was received.

 

Taking this a step further, asymmetric cryptography is also used to encrypt the contents of messages. Building on the example above, two parties can send secure messages to each other provided that they first share their public keys. Party X can secure a message for Party Y by encrypting a message using Party Y’s public key. Since the only key able to decrypt the data is Party Y’s private key, the message is unreadable to anyone other than Party Y.

 

Unfortunately, asymmetric encryption is an extremely slow method of encryption – much slower than symmetric encryption algorithms that use a single key (i.e Advanced Encryption Standard or AES). Web browsers use a combination of asymmetric and symmetric encryption to enforce the SSL/TLS protocols for secure web sites. In this scenario, the web server and browser use asymmetric encryption negotiate a secure key using the website’s SSL certificate and client machine’s security certificate. The negotiated message/key is then used as the encryption key for continued communication using faster symmetric encryption.

The Code…

This code snippet is a trivial example whereas a single Ruby shell script simulates secure communication between the Nashville Ruby Meetup Group and our favorite patriot, Edward Snowden.  The script generates an RSA private/public key pair for each party; exports the public key (to provide to the other party); encrypts a message for each party using the other’s public key; and decrypts the message using each party’s own private key. Additionally, I added message signing and verification to this snippet (although it was not part of my original lightning talk code sample).

 

#!/usr/bin/env ruby

require 'openssl'

KEY_LENGTH      = 4096  # Key size (in bits)
digest_fuction  = OpenSSL::Digest::SHA256.new # Predetermined signature digest function


# Generate RSA private/public key pairs for both parties...
keypair_snowden = OpenSSL::PKey::RSA.new(KEY_LENGTH)
keypair_meetup  = OpenSSL::PKey::RSA.new(KEY_LENGTH)

# Public key export for exchange between parties...
pubkey_snowden  = OpenSSL::PKey::RSA.new(keypair_snowden.public_key.to_der)
pubkey_meetup   = OpenSSL::PKey::RSA.new(keypair_meetup.public_key.to_der)

# Plain text messages...
message_to_snowden  = 'You are a patriot!'
message_to_meetup   = "Russia is really nice this time of year...\nUse encryption and make the NSA CPUs churn and burn!"

# Generate digital signatures using private keys...
signature_meetup  = keypair_meetup.sign(digest_fuction, message_to_snowden)
signature_snowden = keypair_snowden.sign(digest_fuction, message_to_meetup)

# Encrypt messages using the other party's public key...
encrypted_for_snowden = pubkey_snowden.public_encrypt(message_to_snowden) #from Meetup
encrypted_for_meetup  = pubkey_meetup.public_encrypt(message_to_meetup)   #from Snowden

# Decrypt messages using own private keys...
decrypted_snowden = keypair_snowden.private_decrypt(encrypted_for_snowden)
decrypted_meetup  = keypair_meetup.private_decrypt(encrypted_for_meetup)


# Signature validation and console output...
if pubkey_meetup.verify(digest_fuction, signature_meetup, decrypted_snowden)
  puts "Edward Snowden received from Ruby Meetup:"
  puts "\"#{message_to_snowden}\"\n\n"
end

if pubkey_snowden.verify(digest_fuction, signature_snowden, decrypted_meetup)
  puts "Ruby Meetup received from Edward Snowden:"
  puts "\"#{message_to_meetup}\""
end

Why is this Important?

I’m sure by this point, you’re no stranger to the countless news reports of  domestic NSA spying. The NSA is capable of seeing  just about everything on the Internet including your personal emails regardless of constitutionality or legality. There are also a large number of American organizations, who either by their own will or by NSA influence, provide the NSA with what is essentially an open, unencrypted pipeline to all of your online data. These organizations bypass encryption or provide your data before it has the chance to be encrypted. If that doesn’t scare you, it should… The only way you can ensure the security of your data and your messages is to encrypt that data personally before it’s transmitted over the wire. …and the intent of this post was to show you how that can be done. There’s no guarantee that the NSA won’t eventually be able to decrypt the encrypted messages you send. However, you can and should slow them down a bit by making their servers work a lot harder to get at your personal information. I’m an honest, law-abiding citizen who pays taxes every April 15th and will do so until the day I die. If the NSA choses to read my personal emails to my wife, invoices I send to my clients or anything else that’s either none of their business or NOT a threat to the national security of the United States, then they’re going to have to waste some CPU cycles doing so.

 

One Last Comment…

Check out GPG Tools. It’s an open-source encryption suite that plugs in to Apple Mail allowing you to sign and encrypt emails using asymmetric encryption. It allows you to create keys with a maximum of 4096 bits and according to the algorithm estimates on keylength.com, there’s a good chance it would take the NSA a while to crack your encryption.

Just Go Ahead and Write the Test

07.05.2012 23:37 by kbeckman | Comments

Recently, I’ve found myself in a couple of rework or debug situations where I’ve really wished that I had taken a few extra minutes and written a test or two to prove the code in question actually worked at one time… Both situations were outside of what I would consider the “normal” day-to-day unit testing scenarios where I’m covering typical model or controller logic. One situation involved dynamic routes created by OmniAuth and the other involved a local Git submodule for a gem I’m working on.

 

Adding the route tests was an easy fix, but it took me a couple days worth of recommitting the same submodule pointer updates in our base repo before I finally got sick enough of re-doing it. It helped to finally figure out why it kept happening too… One of the other devs on the team had re-cloned the project repo and forgot to do the submodule init/update steps. Every time I would commit, I would update the submodule commit pointer. Every time he would commit, it would roll the commit pointer back to a previous version. One RSpec test put an end to all of the commit games…

 

require 'spec_helper'

describe "Git Development Submodule" do

  it 'should have the latest commit id' do
    submodule_status = `git submodule status`
    error_msg        = 'Solution: git submodule update && git submodule init"

    # Ensuring the [git submodule status] command actually produces a result...
    # TeamCity tests are run under a build agent where source code is copied to rather than updated/fetched in a git
    # repo. This is a local dev machine test only...
    if status_msg.present?
      status_msg.should include '24a1c1299....', error_msg
    end
  end

end
  • Just shell-out using the git submodule statuscommand which returns the current submodule commit ID.
  • Notice the check for a status message before checking the commit ID. Depending on your build server configuration, you may not get a value for this when running in a CI scenario…