74

In short:

Would like a way to do SSH key authentication via LDAP.

Problem:

We use LDAP (slapd) for directory services and we've recently moved to using our own AMI for building instances. The reason the AMI bit is important is that, ideally, we would like to be able to login with SSH via key authentication as soon as the instance is running and not have to wait for our somewhat slow configuration management tool to kickoff a script to add the correct keys to the instance.

The ideal scenario is that, when adding a user to LDAP we add their key as well and they'd be immediately be able to login.

Key authentication is a must because password-based login is both less secure and bothersome.

I've read this question which suggests there's a patch for OpenSSH called OpenSSH-lpk to do this but this is no longer needed with OpenSSH server >= 6.2

Added a sshd_config(5) option AuthorizedKeysCommand to support fetching authorized_keys from a command in addition to (or instead of) from the filesystem. The command is run under an account specified by an AuthorizedKeysCommandUser sshd_config(5) option

How can I configure OpenSSH and LDAP to implement this?

c4urself
  • 5,530
  • 3
  • 28
  • 39

4 Answers4

93

Update LDAP to include the OpenSSH-LPK schema

We first need to update LDAP with a schema to add the sshPublicKey attribute for users:

dn: cn=openssh-lpk,cn=schema,cn=config
objectClass: olcSchemaConfig
cn: openssh-lpk
olcAttributeTypes: ( 1.3.6.1.4.1.24552.500.1.1.1.13 NAME 'sshPublicKey'
    DESC 'MANDATORY: OpenSSH Public key'
    EQUALITY octetStringMatch
    SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
olcObjectClasses: ( 1.3.6.1.4.1.24552.500.1.1.2.0 NAME 'ldapPublicKey' SUP top AUXILIARY
    DESC 'MANDATORY: OpenSSH LPK objectclass'
    MAY ( sshPublicKey $ uid )
    )

Create a script that queries LDAP for a user's public key:

The script should output the public keys for that user, example:

ldapsearch '(&(objectClass=posixAccount)(uid='"$1"'))' 'sshPublicKey' | sed -n '/^ /{H;d};/sshPublicKey:/x;$g;s/\n *//g;s/sshPublicKey: //gp'

Update sshd_config to point to the script from the previous step

  • AuthorizedKeysCommand /path/to/script
  • AuthorizedKeysCommandUser nobody

Bonus: Update sshd_config to allow password authentication from internal RFC1918 networks as seen in this question:

Only allow password authentication to SSH server from internal network

Useful links:

EDIT: Added user nobody as suggested TRS-80

c4urself
  • 5,530
  • 3
  • 28
  • 39
  • 7
    This is fantastic, although I would suggest `AuthorizedKeysCommandUser nobody` instead of root. – TRS-80 Mar 27 '15 at 12:14
  • There must be something different about either my ldapsearch or sed because piping the output to the sed black magic command you have there gives me no output, even though my plain ldapsearch command is returning data. I'm going to have to write a script to clean the output instead of using sed. – Chris L Jun 23 '15 at 20:34
  • 1
    Disregard my previous comment. My problem was caused by having a trailing newline in the sshPublicKey property, which in turn causes ldapsearch to base64 encode the whole thing. I simplified the sed command tho: `ldapsearch -u -LLL -o ldif-wrap=no '(&(objectClass=posixAccount)(uid='"$1"'))' 'sshPublicKey' | sed -n 's/^[ \t]*sshPublicKey:[ \t]*\(.*\)/\1/p'` – Chris L Jun 23 '15 at 20:45
  • 1
    @Chris indeed less black magic, but sed is still a write-once, 1-way hashing function ;) – Froyke Aug 24 '15 at 23:19
  • Does anyone know how to get this to work with RHDS 389 server? The 'subschema' objectClass appears similar to 'olcSchemaConfig' parameter-wise, but my 389 server seems to reject it based on a lack of support for a cn. – Pretzel Jun 15 '17 at 17:47
  • Think I found it: http://etcfstab.com/oraclelinux/sshkey_in_389ds.html – Pretzel Jun 15 '17 at 17:49
  • 1
    On my version of OpenSSH (5.3p1-122.el6) there is `AuthorizedKeysCommandRunAs` and not `AuthorizedKeysCommandUser` – mveroone Jun 16 '17 at 15:25
  • 1
    You could replace that sed with `awk '/^sshPublicKey/{$1=""; p=1} /^$/{p=0} {printf p?$0:""}'` which is much cleaner IMO. – miken32 May 01 '18 at 21:25
  • I needed to have `AuthorizedKeysCommand /script %u` to pass the user to it with openssh 1:7.2p2-4ubuntu2.4. This option also doesn't exist before openssh 6.2 – jdf Jun 07 '19 at 19:43
  • On a redhat family you will also need to set the `authlogin_nsswitch_use_ldap` selinux knob. – drookie Oct 01 '20 at 11:16
9

This is not a full answer, just an addition to c4urself's answer. I would have added this as a comment, but I don't have sufficient reputation to comment, so please don't downvote!

This is the script I'm using for the AuthorizedKeysCommand (based on c4urself's version). It works regardless of whether the value is returned in base64 encoding or not. This can be especially useful if you want to store multiple authorized keys in LDAP -- simply seperate the keys with newline characters, similar to the authorized_keys file.

#!/bin/bash
set -eou pipefail
IFS=$'\n\t'

result=$(ldapsearch '(&(objectClass=posixAccount)(uid='"$1"'))' 'sshPublicKey')
attrLine=$(echo "$result" | sed -n '/^ /{H;d};/sshPublicKey:/x;$g;s/\n *//g;/sshPublicKey:/p')

if [[ "$attrLine" == sshPublicKey::* ]]; then
  echo "$attrLine" | sed 's/sshPublicKey:: //' | base64 -d
elif [[ "$attrLine" == sshPublicKey:* ]]; then
  echo "$attrLine" | sed 's/sshPublicKey: //'
else
  exit 1
fi
mbrgm
  • 91
  • 1
  • 1
  • This isn't necessary. `objectClass: ldapPublicKey` supports storing multiple values in `sshPublicKey` - *without* the need for newline character (which triggers base64 encoding). So if you have more than one ssh-key, store each ssh-key in it's own `sshPublicKey` attribute-value, then the [accepted answer](https://serverfault.com/a/653793/357772) script will produce the correct multi-line output. – colm.anseo Sep 11 '20 at 21:43
5

For anyone getting the error when running the ldapsearch:

sed: 1: "/^ /{H;d};": extra characters at the end of d command

as I was (on FreeBSD), the fix is to change the first sed command to:

/^ /{H;d;};

(adding a semicolon after the 'd').

Pierre.Vriens
  • 1,159
  • 34
  • 15
  • 19
Scott
  • 53
  • 1
  • 4
4

Just wanted to share my "method", my client side is Debian/Ubuntu Specific, but my Server side is basically the same as above, but with a little more "HowTo:"

Server :

Enable Public Key Attribute :

Credit :

https://blog.shichao.io/2015/04/17/setup_openldap_server_with_openssh_lpk_on_ubuntu.html

cat << EOL >~/openssh-lpk.ldif
dn: cn=openssh-lpk,cn=schema,cn=config
objectClass: olcSchemaConfig
cn: openssh-lpk
olcAttributeTypes: ( 1.3.6.1.4.1.24552.500.1.1.1.13 NAME 'sshPublicKey'
  DESC 'MANDATORY: OpenSSH Public key'
  EQUALITY octetStringMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
olcObjectClasses: ( 1.3.6.1.4.1.24552.500.1.1.2.0 NAME 'ldapPublicKey' SUP top AUXILIARY
  DESC 'MANDATORY: OpenSSH LPK objectclass'
  MAY ( sshPublicKey $ uid )
  )
EOL

Now use this to add ldif :

ldapadd -Y EXTERNAL -H ldapi:/// -f ~/openssh-lpk.ldif

Adding a user with SSH public key in phpLDAPadmin

First, create a user with the “Generic: User Account” template. Then, go to the “objectClass” attribute section, click “add value”, and choose the “ldapPublicKey” attribute. After you submit, go back to the user edit page, click “Add new attribute” on the top part, and choose “sshPublicKey”, paste the public key into the text area, and finally click “Update Object”."

sshPublicKey Attribute not showing - OpenLDAP PHPLDAP SSH Key Auth

Ubuntu Client :

apt-get -y install python-pip python-ldap
pip install ssh-ldap-pubkey
sh -c 'echo "AuthorizedKeysCommand /usr/local/bin/ssh-ldap-pubkey-wrapper\nAuthorizedKeysCommandUser nobody" >> /etc/ssh/sshd_config' && service ssh restart

Create Test Keys :

ssh-keygen -t rsa
FreeSoftwareServers
  • 515
  • 1
  • 8
  • 26