Date: Thu, 16 Nov 2006 22:54:36 -0500 (EST) From: Toby Burress <kurin@delete.org> To: FreeBSD-gnats-submit@FreeBSD.org Subject: docs/105620: [article] LDAP Authentication Message-ID: <20061117035436.5A58684424@cobalt.delete.org> Resent-Message-ID: <200611170400.kAH4039r027179@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
>Number: 105620 >Category: docs >Synopsis: [article] LDAP Authentication >Confidential: no >Severity: non-critical >Priority: low >Responsible: freebsd-doc >State: open >Quarter: >Keywords: >Date-Required: >Class: update >Submitter-Id: current-users >Arrival-Date: Fri Nov 17 04:00:02 GMT 2006 >Closed-Date: >Last-Modified: >Originator: Toby Burress >Release: FreeBSD 5.4-STABLE i386 >Organization: >Environment: >Description: Documentation article for authentication against an LDAP directory. >How-To-Repeat: >Fix: I named it 'ldap-auth' but I dunno whatever's good. <!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@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; while the principles here should be generally applicable among many different servers, most of the concrete administration is OpenLDAP-specific.</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="openldap-secure"> <title>Securing LDAP Accounts</title> <para>While it is not strictly necessary, in the sense that authentication will work without it, you will probably want to secure access to some user attributes in your LDAP server. In OpenLDAP, this is accomplished through ACLs in <filename>slapd.conf</filename>.</para> <para>To begin with, the <literal>userPassword</literal> attribute should not be world-readable. By default, anyone who can connect to the LDAP server can read this attribute. To disable this, put the following in <filename>slapd.conf</filename>:</para> <programlisting>access to attrs=userPassword by self write by anonymous auth by * none access to * by self write by * read</programlisting> <para>This will disallow reading of the <literal>userPassword</literal> attribute, while still allowing users to change their own passwords.</para> <para>There is still one important security hole to close, however. By default, users can change any attribute (except for those which the LDAP schemas themselves deny changes), such as <literal>uidNumber</literal>. To close this hole, modify the above to</para> <programlisting>access to attrs=userPassword by self write by anonymous auth by * none access to attrs=homeDirectory,uidNumber,gidNumber by * read access to * by self write by * read</programlisting> <para>This will stop users from being able to masquerade as other users.</para> </appendix> <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> >Release-Note: >Audit-Trail: >Unformatted:
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20061117035436.5A58684424>