LDAP authentication - new article
Toby Burress
kurin at delete.org
Fri Nov 17 03:31:07 UTC 2006
I was wondering if I could get someone to check/commit an article
I wrote about getting FreeBSD to authenticate against an LDAP
directory. It's complete and correct, to my knowledge, though some
of my sgml tags may or may not be appropriate.
-------------- next part --------------
<!DOCTYPE article PUBLIC "-//FreeBSD//DTD DocBook V4.1-Based Extension//EN" [
<!ENTITY % articles.ent PUBLIC "-//FreeBSD//ENTITIES DocBook FreeBSD Articles Entity Set//EN">
%articles.ent;
]>
<article>
<articleinfo>
<title>LDAP Authentication</title>
<authorgroup>
<author>
<firstname>Toby</firstname>
<surname>Burress</surname>
<affiliation>
<address><email>kurin at causa-sui.net</email></address>
</affiliation>
</author>
</authorgroup>
<pubdate>$FreeBSD$</pubdate>
<copyright>
<year>2006</year>
<holder>The FreeBSD Documentation Project</holder>
</copyright>
<legalnotice id="trademarks" role="trademarks">
&tm-attrib.freebsd;
&tm-attrib.general;
</legalnotice>
<abstract>
<para>This document is indended to be a guide to using an LDAP
server (principally an <application>OpenLDAP</application> server)
for authentication on &os;. This is useful for situations where
many servers need the same accounts, e.g. as a replacement for
<application>NIS</application>.</para>
</abstract>
</articleinfo>
<indexterm><primary>ldap</primary></indexterm>
<sect1 id="preface">
<title>Preface</title>
<para>This document is intended to give the reader enough of an
understanding of LDAP to configure an LDAP server, as well as an
understanding of <filename role="package">net/nss_ldap</filename>
and <filename role="package">security/pam_ldap</filename> so that
they can be configured to use an LDAP server.</para>
<para>When finished, the reader should be able to configure and
deploy a &os; server that will authenticate against an LDAP
directory.</para>
<para>This article is not intended to be an exhaustive account of
the security, robustness, or best practices for configuring LDAP the
other related services discussed. While the author takes care not
to do anything dumb, neither does he go out of his way to address
security issues. This article should be considered as laying the
theoretical groundwork only, and any actual implementation should be
accompanied by active thought.</para>
<para>For this article, we will have two servers, <hostid
role="fqdn">server.freebsd.org</hostid> and <hostid
role="fqdn">client.freebsd.org</hostid>. The LDAP base will be
<quote>dc=freebsd,dc=org</quote>.</para>
</sect1>
<sect1 id="ldap">
<title>Configuring LDAP</title>
<para>LDAP stands for <quote>Lightweight Directory Access
Protocol</quote> and is a subset of the X.500 DAP protocol. It is
most newly codified in RFC4510 and friends. Basically it is a
database that expects to be read from more often than it is written
to.</para>
<para>The LDAP server <ulink
url="http://www.openldap.org/">OpenLDAP</ulink> will the be used in
the examples in this document, however the principles here should be
generally applicable among many different servers.</para>
<para>There are (basically) two areas of the LDAP service which need
configuration; the first is setting up the server to receive
connections properly, and the second is adding entries to the
directory that the &os; tools know how to work with.</para>
<sect2 id="ldap-connect">
<title>Setting Up the Server for Connections</title>
<note>
<para>This section is specific to OpenLDAP. If you are using
another server, you will need to consult that server's
documentation.</para>
</note>
<para>You will probably want to use some kind of encryption in
your connections to the LDAP server; otherwise your users'
passwords will be floating over the ether in what amounts to plain
text. The tools we will be using support two very similar kinds
of encryption, SSL and TLS.</para>
<para>TLS stands for Transportation Layer Security. Services that
employ TLS tend to connect on the <emphasis>same</emphasis> ports
as those same services without TLS; thus an SMTP server which
supports TLS will listen for connections on port 25, and an LDAP
server will listen on 389.</para>
<para>SSL stands for Secure Sockets Layer, and services that
implement SSL do <emphasis>not</emphasis> listen on the same ports
as their non-SSL counterparts. Thus SMTPS listens on port 465
(not 45), HTTPS listens on 443, and LDAPS is 636.</para>
<para>The reason SSL uses a different port than TLS is because a
TLS connection begins as plain text, and switches to encrypted
traffic after the <literal>STARTTLS</literal> directive. SSL
connections are encrypted from the start. Other than that there
is no difference between the two.</para>
<note>
<para>We will be configuring OpenLDAP to use SSL; this is
indicated solely by the use of the <literal>-h
"ldaps://"</literal> directive when starting the server.
Omitted, OpenLDAP would listen for TLS connections on
389.</para>
</note>
<para>Once OpenLDAP is installed, the following configuration
parameters in <filename>slapd.conf</filename> will enable SSL:</para>
<programlisting>security ssf=128
TLSCertificateFile /path/to/your/cert.crt
TLSCertificateKeyFile /path/to/your/cert.key
TLSCACertificateFile /path/to/your/cacert.crt</programlisting>
<para>Here, <literal>ssf=128</literal> tells OpenLDAP to require
128-bit encryption for all connections (search + update). This
parameter can be configured more exactly to your taste.</para>
<para>The <filename>cert.crt</filename>,
<filename>cert.key</filename>, and <filename>cacert.crt</filename>
are necessary for clients to authenticate <emphasis>you</emphasis>
as the valid LDAP server. If you simply want a server that runs,
you can create a self-signed certificate with OpenSSL:</para>
<screen>&prompt.user; <userinput>openssl genrsa -out cert.key 1024</userinput>
Generating RSA private key, 1024 bit long modulus
....................++++++
...++++++
e is 65537 (0x10001)
&prompt.user; <userinput>openssl req -new -key cert.key -out cert.csr</userinput></screen>
<para>At this point you should be prompted for some values. You
can enter whatever you like, but it is important that the
<quote>Common Name</quote> value be the fully qualified domain
name of the server OpenLDAP will be running on, in our case
<quote>server.freebsd.org</quote>. Otherwise your clients will
refuse to connect.</para>
<para>Finally, the certificate signing request needs to be
signed:</para>
<screen>&prompt.user; <userinput>openssl x509 -req -in cert.csr -days 365 -signkey cert.key -out cert.crt</userinput>
Signature ok
subject=/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd
Getting Private key</screen>
<para>This will create a self-signed certificate that can be used
for the directives in <filename>slapd.conf</filename>, with
<filename>cert.crt</filename> and <filename>cacert.crt</filename>
being the same file. If you are going to use many OpenLDAP
servers (for replication via <literal>slurpd</literal>) you will
want to see <xref linkend="ssl-ca"> to generate a CA key, and use
it to sign independent certificates.</para>
<para>Once this is done, put the following values in
<filename>/etc/rc.conf</filename>:</para>
<programlisting>slapd_flags="-h 'ldaps://'"
slapd_enable="YES"</programlisting>
<para>and run <userinput>/usr/local/etc/rc.d/slapd
start</userinput>. This should start OpenLDAP. Confirm that it
is listening on 636 with</para>
<screen>&prompt.user; <userinput>sockstat -4 -p 636</userinput>
ldap slapd 3261 7 tcp4 *:636 *:*</screen>
<para>Finally, the client machines need to be configured to talk
to the LDAP server. The client machines will always have OpenLDAP
libraries, since that is all <filename
role="package">security/pam_ldap</filename> and <filename
role="package">net/nss_ldap</filename> support, at least for the
moment.</para>
<para>The configuration file for the OpenLDAP libraries is
<filename>/usr/local/etc/openldap/ldap.conf</filename>. Edit this
file to contain the following values:</para>
<programlisting>base dc=freebsd,dc=org
uri ldaps://server.freebsd.org/
ssl on
tls_cacert /path/to/your/cacert.crt</programlisting>
<note>
<para>It is important that your clients have access to
<filename>cacert.crt</filename>, otherwise they will not be able
to connect.</para>
</note>
<para>At this point you should be able to run
<userinput>ldapsearch</userinput> on the client machine. If you
encounter an error, then something is configured wrong; most
likely it is your certificates. Use <userinput>openssl
s_client</userinput> and <userinput>openssl s_server</userinput>
to ensure you have them configured and signed properly.</para>
</sect2>
<sect2 id="ldap-database">
<title>Entries in the Database</title>
<para>Authentication against an LDAP directory is generally
accomplished by trying to bind to the directory as the user in
question. This is accomplished by establishing a simple bind on
the directory with the <literal>dn</literal> of the username and
the password in that user's <literal>userPassword</literal>
attribute.</para>
<para>Thus the first thing we have to do is figure out is where in
the directory our users will live.</para>
<para>The base entry for our database is
<literal>dc=freebsd,dc=org</literal>. The default location for
users that most clients seem to expect is
<literal>ou=people,<replaceable>base</replaceable></literal>, so
that is what will be used here, however keep in mind that this is
configurable (actually, it doesn't truly matter at all). So the
ldif entry for the <literal>people</literal> organizational unit
will look like:</para>
<programlisting>dn: ou=people,dc=freebsd,dc=org
objectClass: top
objectClass: organizationalUnit
ou: people</programlisting>
<para>All users will be created as subentries of this
organizational unit.</para>
<para>Some thought might be given to the object class your users
will belong to. Most tools by default will use
<literal>people</literal>, which is fine if you simply want to
provide entries to authenticate against. However, if you are
going to store user information in the LDAP database as well, you
will probably want to use <literal>inetOrgPerson</literal>. In
either case, the relevant schemas need to be loaded in
<filename>slapd.conf</filename>.</para>
<para>For this example we will use the <literal>person</literal>
object class. If you are using <literal>inetOrgPerson</literal>,
the steps are basically identical, except that the
<literal>sn</literal> attribute is required.</para>
<para>To add a user <literal>testuser</literal>, the ldif would
be:</para>
<programlisting>dn: uid=tuser,ou=people,dc=freebsd,dc=org
objectClass: person
objectClass: posixAccount
objectClass: shadowAccount
objectClass: top
uidNumber: 10000
gidNumber: 10000
homeDirectory: /home/tuser
loginShell: /bin/csh
uid: tuser
cn: tuser</programlisting>
<para>I start my LDAP users' UIDs at 10000; you can configure
whatever number you wish here, as long as it's less than
65536.</para>
<para>We also need group entries. They are as configurable as
user entries, but we will use the defaults below:</para>
<programlisting>dn: ou=groups,dc=freebsd,dc=org
objectClass: top
objectClass: organizationalUnit
ou: groups
dn: cn=tuser,ou=groups,dc=freebsd,dc=org
objectClass: posixGroup
objectClass: top
gidNumber: 10000
cn: tuser</programlisting>
<para>To enter these into your database, you can use
<userinput>slapadd</userinput> or <userinput>ldapadd</userinput>
on a file containing these entries. Alternatively, you can use
<filename role="package">sysutils/ldapvi</filename>.</para>
<para>An <userinput>ldapsearch</userinput> on the client machine
should now return these entries. If it does, your database is
properly configured to be used as an LDAP authentication server.</para>
</sect2>
</sect1>
<sect1 id="client">
<title>Client Configuration</title>
<para>&os; requires two services to be installed to authenticate
against an LDAP server correctly, <filename
role="package">security/pam_ldap</filename> and <filename
role="package">net/nss_ldap</filename>. Once these are installed
the server will be ready.</para>
<sect2 id="client-auth">
<title>Authentication</title>
<para><filename role="package">security/pam_ldap</filename> is
configured via <filename>/usr/local/etc/ldap.conf</filename>.
Note that this is a <emphasis>different file</emphasis> than the
OpenLDAP library functions' configuration file,
<filename>/usr/local/etc/openldap/ldap.conf</filename>, however it
takes many of the same options; in fact it is a superset of that
file. For the rest of this section, references to
<filename>ldap.conf</filename> will mean
<filename>/usr/local/etc/ldap.conf</filename>.</para>
<para>Thus, first we will want to copy all of our original
configuration parameters from
<filename>openldap/ldap.conf</filename> to the new
<filename>ldap.conf</filename>. Once this is done, we want to
tell <filename role="package">security/pam_ldap</filename> what to
look for on the directory server.</para>
<para>We are identifying our users with the <literal>uid</literal>
attribute. To configure this (though it is the default), set the
<literal>pam_ldap_attribute</literal> directive in
<filename>ldap.conf</filename>. With this set, <filename
role="package">pam_ldap</filename> will search the entire LDAP
directory under <literal>base</literal> for the value
<literal>uid=<replaceable>username</replaceable></literal>. If it
finds one and only one entry, and binds to it correctly, then it
will allow access. Otherwise it will fail.</para>
<sect3 id="client-auth-pam">
<title>PAM</title>
<para>PAM, which stands for pluggable authentication modules, is
the method by which &os; authenticates most of its sessions. To
tell &os; we wish to use an LDAP server, we will have to add the
appropriate line to the appropriate PAM file.</para>
<para>Most of the time the appropriate PAM file is
<filename>/etc/pam.d/sshd</filename>, if you want to use SSH
(remember to set the appropriate options in
<filename>/etc/ssh/sshd_config</filename>, otherwise SSH will
not use PAM).</para>
<para>To use PAM for authentication, add the line</para>
<programlisting>auth sufficient /usr/local/lib/pam_ldap.so no_warn</programlisting>
<para>Exactly where this line shows up in the file and which
options appear in the fourth column determine the exact behavior
of the authentication mechanism; see &man.pam.d.5;</para>
<para>With this configuration you should be able to authenticate
a user against an LDAP directory, however it is usually not
ideal to allow <emphasis>every</emphasis> user in the directory
into <emphasis>every</emphasis> client machine. Fortunately
there are a few ways to restrict user access.</para>
<para><filename>ldap.conf</filename> supports a
<literal>pam_groupdn</literal> directive; every account that
connects to this machine needs to be a member of the group
specified here. So, for example, if you have</para>
<programlisting>pam_groupdn cn=production,ou=accessgroups,dc=freebsd,dc=org</programlisting>
<para>in <filename>ldap.conf</filename>, then only members of
that group will be able to log in. There are a few things to
bear in mind, however.</para>
<para>Members of this group are specified in one or more
<literal>memberUid</literal> attributes, and each attribute must
have the full distinguished name of the member. So
<literal>memberUid: someuser</literal> will not work; it must be
<literal>memberUid:
uid=someuser,ou=people,dc=freebsd,dc=org</literal>.</para>
<para>Additionally, this directive is not checked in PAM during
authentication, it is checked during account management, so you
will need a second line in your PAM files under
<literal>account</literal>. This will require, in turn, cause
<emphasis>every</emphasis> user to be in the group listed. You
may want to take advantage of the
<literal>ignore_authinfo_unavail</literal> option so that you
are not locked out of every computer when the LDAP server is
unavailable.</para>
</sect3>
</sect2>
<sect2 id="client-nss">
<title>Name Service Switch</title>
<para>The <filename role="package">net/nss_ldap</filename> port
uses the same configuration file as <filename
role="package">security/pam_ldap</filename>, and should not need
any extra parameters once it is installed. Instead, what is left
is simply to edit <filename>/etc/nsswitch.conf</filename> to take
advantage of the directory. Simply replace the following
lines:</para>
<programlisting>group: compat
passwd: compat</programlisting>
<para>with</para>
<programlisting>group: files ldap
passwd: files ldap</programlisting>
<para>This will allow you to map usernames to UIDs and UIDs to
usernames.</para>
<para>Congratulations! You should now have working LDAP
authentication.</para>
</sect2>
</sect1>
<appendix id="useful">
<title>Useful Aids</title>
<para>There are a few other programs that might be useful,
particularly if you have many users and do not want to configure
everything manually.</para>
<para><filename role="package">security/pam_mkhomedir</filename> is
a PAM module that always succeeds; its purpose is to create home
directories for users which do not have them. If you have dozens of
client servers and hundreds of users, it is much easier to use this
and set up skeleton directories than to prepare every home
directory.</para>
<para><filename role="package">sysutils/cpu</filename> is a
&man.pw.8;-like utility that can be used to manage users in the LDAP
directory. You can call it directly, or wrap scripts around it. It
can handle both TLS (with the <userinput>-x</userinput> flag) and
SSL (directly).</para>
</appendix>
<appendix id="ssl-ca">
<title>OpenSSL Certificates For LDAP</title>
<para>If you are hosting two or more LDAP servers, you will probably
not want to use self-signed certificates, since each client will
have to be configured to work with each certificate. While this is
possible, it is not nearly as simple as creating your own
certificate authority, and signing your servers' certificates with
that.</para>
<para>The steps here are presented as they are with very little
attempt at explaining what is going on—further explanation
can be found in &man.openssl.1; and its friends.</para>
<para>To create a certificate authority, we simply need a
self-signed certificate and key. The steps for this again
are</para>
<screen>&prompt.user; <userinput>openssl genrsa -out root.key 1024</userinput>
&prompt.user; <userinput>openssl req -new -key root.key -out root.csr</userinput>
&prompt.user; <userinput>openssl x509 -req -days 1024 -in root.csr -signkey root.key -out root.crt</userinput></screen>
<para>These will be your root CA key and certificate. You will
probably want to encrypt the key and store it in a cool, dry place;
anyone with access to it can masquerade as one of your LDAP
servers.</para>
<para>Next, using the first two steps above create a key
<filename>ldap-server-one.key</filename> and certificate signing
request <filename>ldap-server-one.csr</filename>. Once you sign the
signing request with <filename>root.key</filename>, you will be able
to use <filename>ldap-server-one.*</filename> on your LDAP
servers.</para>
<note>
<para>Do not forget to use the fully qualified domain name for the
<quote>common name</quote> attribute when generating the
certificate signing request; otherwise clients will reject a
connection with you, and it can be very tricky to diagnose.</para>
</note>
<para>To sign the key, use <userinput>-CA</userinput> and
<userinput>-CAkey</userinput> instead of
<userinput>-signkey</userinput>:</para>
<screen>&prompt.user; <userinput>openssl x509 -req -days 1024 \
-in ldap-server-one.csr -CA root.crt -CAkey root.key \
-out ldap-server-one.crt</userinput></screen>
<para>The resulting file will be the certificate that you can use on
your LDAP servers.</para>
<para>Finally, for clients to trust all your servers, distribute
<filename>root.crt</filename> (the <emphasis>certificate</emphasis>,
not the key!) to each client, and specify it in the
<literal>TLSCACertificateFile</literal> directive in
<filename>ldap.conf</filename>.</para>
</appendix>
</article>
More information about the freebsd-doc
mailing list