Wednesday, June 24, 2009

public and private ssh keys in ldap

Marlon did some great work on setting up a ldap servercluster for our serverpark at hyves.

We want to use that ldap repository to stick all user-accounts in so we have a better control over who gets to access what.

In order to make that happen we need both the public AND the private key for ssh access in our ldap server.

Getting the public keys in ldap proved fairly trivial, head over to the openssh-lpk project on code.google

Applying the patches is easy if you use Gentoo:
USE="ldap" emerge openssh
will do the trick, your milage may vary on other distributions :-)

Getting the private keys in ldap is completely undocumented....

Here's how you do it:

Add the following to the schema provided by the openssh-lpk guys:

attributetype ( 1.3.6.1.4.1.24552.500.1.1.1.14 NAME 'sshPrivateKey'
DESC 'OPTIONAL: OpenSSH Private key'
EQUALITY octetStringMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )

objectclass ( 1.3.6.1.4.1.24552.500.1.1.2.1 NAME 'ldapPrivateKey' SUP top AUXILIARY
DESC 'OPTIONAL: OpenSSH LPK objectclass'
MAY ( sshPrivateKey $ uid )
)
Now you need to load the public and private key into the ldap server.
The public key again is easy: It's a single string with no linebreaks in it, any tool will do.
But the private key is slightly more involved, the first couple of lines from my private key are:
-----BEGIN DSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,A683EAADB7A65E5C

o+sletw5nmOZN1Hu62HSIRMaMsqWWDSqbxrRKu5zfk/rXWKDzIU9ULSQ3giGmCRx

You can't load that with ldapmodify into your ldap server because it will mistake the content of your key for ldap attributes. I tried loading it through the webinterface (phpLDAPadmin) but that didn't work either.

The solution is specified in the RFC, you need to encode the key in base64.
base64 -w0 <> yourprivatekey_base64
After that it's trivial to load it into your ldapserver, use whatever tool you like.

Here's a sample ldif file for ldapmodify:
dn: cn=your common name,ou=your department,dc=domain,dc=tld
changetype: modify
replace: sshPrivateKey
sshPrivateKey: LS0tLS1CRUdJTiBEU0EgUFJJVkFURSBLRVktLS0tLQpQcm9jLVR5cGU6IDQsRU5DUllQVEVECkRFSy1JbmZvOiBERVMtRURFMy1DQkMs
Once you have loaded the private and public keys in ldap you need a script to install them in the homedir of your server.

We settled on a combination of autodir and puppet.

Autodir will create the homedir for the user once he logs in for the first time.
We populate the skel directory with puppet, the bash_profile file below takes care of pulling in the keys into the .ssh directory.



# /etc/skel/.bash_profile

LOGNAME=$(whoami)
NUMSESSIONS=$(who |grep ${LOGNAME} |wc -l)
HOST=$(hostname)
[[ -f ~/.firstrun ]] && source ~/.firstrun

if [[ "${FIRSTRUN}" == "YES" ]]
then
# Pick up private keys from ldap
echo "This is a firsttime run"
echo "I need to import your ssh-keys from the ldap server"
echo "Please provide the ldap password when asked"
# Find DN for user
LDAP_DN=$(ldapsearch -LLL -ZZ "(uid=$LOGNAME)" dn | cut -f2 -d:)
ldapsearch -LLL -ZZ -F~/.ssh/ -T ~/.ssh/ -D"${LDAP_DN}" -W -x -tt "(uid=${LOGNAME})" sshPublicKey sshPrivateKey
echo "Decoding your private key"
base64 -w0 -d ~/.ssh/ldapsearch-sshPrivateKey-* > ~/.ssh/id_key
echo "Adding your public key"
mv ~/.ssh/ldapsearch-sshPublicKey-* ~/.ssh/id_key.pub
echo "Cleaning up temporary files"
rm ~/.ssh/ldapsearch-*
sed -i -e 's!YES!NO!g' ~/.firstrun
echo "Your ssh keys have been setup"
fi

# only check if this is the first login
if [ "${NUMSESSIONS}" = "1" ]
then
# cleaning up possible old sessions
/usr/bin/keychain --clear -q

#keychain
echo -e "\e[32;1mAdding private keys to keychain\e[m"

for i in $(find ~/.ssh |grep id |grep -v pub)
do
/usr/bin/keychain -q ${i}
done
fi

# load ssh agent via keychain
source ~/.keychain/$(hostname)-sh

[[ -f /etc/profile.d/bash-completion ]] && source /etc/profile.d/bash-completion


Keychain is a useful tool written by Daniel Robbins, the original Gentoo architect. It loads your keys into ssh-agent and exports the relevant variables into your shell environment, the net effect is that you have to type your passphrase once.
You can find his current homepage here

Maybe this is helpful for someone trying to do something similar, if you need more detail you can always try emailing me :-)

Ramon

P.S> No, the above script is not production ready, it lacks error checking and support for multiple keys.

4 comments:

digital signature certificate said...

I am facing the problem to setup public and private ssh keys in ldap.I followed the instruction you gave but it gives error i think there is a mismatch in key.How can i check that

Anonymous said...

Were you able to get this working with encrypted LDAP (ie, ssl)? If not, how do you reconcile transmitting all your server private keys without any encryption?

Anonymous said...

You should not be storing private keys in LDAP! Even with a good ACL, its like storing unencrypted passwords!!

Users should store their ssh keys on they workstations, and use key forwarding (edit .ssh/config or ssh with -A) if they need to hop between hosts and have their private key passed.

That said, the public key bits of this are very useful.

Anonymous said...

"Add the following to the schema provided by the openssh-lpk guys:"
Hello author, Thank you for your article. I want to realize same mechanisme. Tell me please the OIDs:
1.3.6.1.4.1.24552.500.1.1.1.14 NAME 'sshPrivateKey'
and
1.3.6.1.4.1.24552.500.1.1.2.1 NAME 'ldapPrivateKey'
is already defined somewere or they are simply good idea to define it in the future by openssh-lpk community?

And I have some another question about "sshPrivateKey": did you try use
1.3.6.1.4.1.1466.115.121.1.41 (PostalAddress data type that supports multi string data)
instead current
1.3.6.1.4.1.1466.115.121.1.40 (OctetString data type)
for sshPrivateKey attribute