Authorized_Keys in Active Directory

November 21, 2015 at 6:21 pm

Now that we are implementing more Linux systems, I’m noticing some of the pain points of keeping certain things in sync. A big annoyance, for example, is keeping our infrastructure and users’ SSH keys in sync across all of our machines. There are several methods currently available, but I had issues with each. I’ve listed the two main methods below.

Via Configuration Management

A very DevOpsy way of tackling the problem would be to us a configuration management system like Chef to keep the files updated. In fact, there are several examples of this solution out there already. However, this seems a bit counter-intuitive to me. Why keep user account and related information in a config management system instead of a directory service? This is probably my Windows World bias, but there are others that agree.

Via Scripts/Dedicated Systems

From simple shell scripts, to complex systems, there are many ways to keep this data in sync. The simplest would appear to to be setting up NFS and pointing all users’ home directories there… But then you have to keep those NFS servers in sync and backed up across multiple sites, which can be problematic at scale.

Our Solution – AD/LDAP storage of SSH keys

To be up front, this was not my idea. There are many other folks who have implemented similar solutions. We are using this method specifically because we already have a robust AD infrastructure with all of our Linux authentication going through AD already (a post on this is soon to come). It probably doesn’t make sense for a group that already has a solid solution in, say, chef or puppet. For us, it did, and this is how we built it.

First, we had to extend the Active Directory schema. This is not something for the faint of heart, but is also not something to be afraid of. I followed the procedure listed here (after backing things up) and had everything ready to go in about 15 minutes. A note on the procedure: you do not need to use ADSIEdit to manage the custom attirbute afterwards. Just open AD Users and Computers and switch to the advanced view mode. Each item will then have an “attributes” tab in its properties page.

Once the schema was extended, the fun began. OpenSSH supports a config variable called “AuthorizedKeysCommand”. This allows us to call an arbitrary script to pull the users authorized_keys file. This serverfault post got me going on creating a custom command, but the output of SED wasn’t clean enough. I whipped up the following script in perl to get everything working nicely. It binds to AD using a username and password and then pulls all sshPublicKey values from the specified user account.

#!/usr/bin/perl
# Gets authorized keys from LDAP. Cleaner and supports any number of ssh keys, within reason. 
# Requires Net::LDAP.
use Net::LDAP;

$BINDDN="cn=service account,dc=example,dc=com";
$BINDPW="Password";
$SEARCHBASE="dc=example,dc=com";
$SERVER="domain or ip";
$SearchFor="samaccountname=$ARGV[0]";

$ldap = Net::LDAP->new( $SERVER ) or die "$@";
$msg = $ldap->bind( $BINDDN, password=> $BINDPW);

$result = $ldap->search( base => $SEARCHBASE,
                         filter => $SearchFor,
                        );

while (my $entry = $result->shift_entry) {
    foreach ($entry->get_value('sshPublicKey')){
        print $_ , "\n"
        } ;
}

$ldap->unbind;

Once the script is created, it can be called by adding “AuthorizedKeysCommand /path/to/script” to the sshd_config file. I also had to set the script to run as root by using the “AuthorizedKeysCommandUser root” command.

Next Steps

I want to improve this script in a few ways long-term…

  1. Since all of our Linux systems are part of our domain, there should be a way to have them bind to LDAP by using the machine’s Kerberos ticket. I don’t like using a username and password, but didn’t have the time to get the Kerberos bind working reliably.
  2. On the security front, this should be a TLS bind. No reason to have the data going over the wire cleartext.
  3. The script should not have to run as root…
  4. Cache the authorized_keys file on a per-user basis. We have a very robust AD infrastructure, but there is always a concern that it could become unavailable. The system’s resiliency would be greatly increased if it could cache the authorized_keys locally on a per-user basis, where sshd would normally look for it.
  5. Error Handling and Logging. It’s not fun, but it’s important. I wanted to get this solution out quickly, but it should be able to log to standard sources and handle some edge cases.
  6. Since the above is a lot of work, perhaps I can just improve a project like ssh-ldap-pubkey to support Kerberos.

 

External Links

I found the following links quite helpful in generating this solution.

Flexible Email Alerts for Logstash

November 13, 2015 at 5:02 pm

Logstash LogoMy company currently does a lot of it’s debug logging via email.  This means that every time an unhandled exception occurs in production, qa, uat, or integration, we get an email. Thank goodness for custom email rules and single instance storage in Exchange. Oh wait.

I have been a proponent of Logstash and the ELK stack for quite a while now. It is a wonderfully flexible framework for centralizing, enriching, and viewing log data. This past week, I built a proof of concept for management and they loved it. However, many folks wanted to know how we could send out emails from the logging system. I pointed them at the Logstash email output plugin, but they weren’t convinced. They wanted to see some flexible routing capabilities that could be leveraged in any config file, for any log type. Thankfully, this was pretty easy to accomplish.

Below I present a simple tag and filed based config for email notifications.

# This config is designed to flexibly send out email notifications 
# It *requires* certain fields to work 
# Create a tag "SendEmailAlert" 
# Required field emailAlert_to - the email address to send to 
# Required field emailAlert_subject - The subject of the email
# Required field emailAlert_body - The body, defaults to %message 
# 

output { 
  if "SendEmailAlert" in [tags] { 
    email { 
      address => "smtp.XXXXX.org" 
      username => "XXXXX" 
      password => "XXXXX" 
      via => "smtp" 
      from => "logstash.alert@XXXXXX.com" 
      to => "%{emailAlert_to}" 
      subject => "%{emailAlert_subject}" 
      body => "%{emailAlert_body}" 
      } 
   } 
} 

As the comments indicate, all you need to do is tag a message with “SendEmailAlert” and add the appropriate fields and voila: flexible email notifications. In order to use it, a simple mutate is all that is needed.

mutate {
    add_tag => ["SendEmailAlert"]
    add_field => { 
       "emailAlert_to" => "user@XXXXX.com"
       "emailAlert_subject" => "Test Alert"
       "emailAlert_body" => "%{message}"
    }
}

We could easily extend it further, but this has been fine for our POC thus far. We have also implemented similar notifications for Hipchat and PagerDuty.