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