ac8327d8ca
log in because of a missing shell. When shells like bash are on different paths (/bin/bash vs. /usr/local/bin/bash), entries need to be created in /etc/shells and proper symlinks set to make this work. Reviewed by: wblock Approved by: wblock Committed at: Essen FreeBSD Hackathon Differential Revision: https://reviews.freebsd.org/D3194
972 lines
36 KiB
XML
972 lines
36 KiB
XML
<?xml version="1.0" encoding="iso-8859-1"?>
|
|
<!DOCTYPE article PUBLIC "-//FreeBSD//DTD DocBook XML V5.0-Based Extension//EN"
|
|
"http://www.FreeBSD.org/XML/share/xml/freebsd50.dtd">
|
|
<article xmlns="http://docbook.org/ns/docbook"
|
|
xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0"
|
|
xml:lang="en">
|
|
<info>
|
|
<title>LDAP Authentication</title>
|
|
|
|
<authorgroup>
|
|
<author>
|
|
<personname>
|
|
<firstname>Toby</firstname>
|
|
<surname>Burress</surname>
|
|
</personname>
|
|
<affiliation>
|
|
<address>
|
|
<email>kurin@causa-sui.net</email>
|
|
</address>
|
|
</affiliation>
|
|
</author>
|
|
</authorgroup>
|
|
|
|
<copyright>
|
|
<year>2007</year>
|
|
<year>2008</year>
|
|
<holder>The FreeBSD Documentation Project</holder>
|
|
</copyright>
|
|
|
|
<legalnotice xml:id="trademarks" role="trademarks">
|
|
&tm-attrib.freebsd;
|
|
&tm-attrib.general;
|
|
</legalnotice>
|
|
|
|
<pubdate>$FreeBSD$</pubdate>
|
|
|
|
<releaseinfo>$FreeBSD$</releaseinfo>
|
|
|
|
<abstract>
|
|
<para>This document is intended as a guide for the configuration
|
|
of an LDAP server (principally an
|
|
<application>OpenLDAP</application> server) for authentication
|
|
on &os;. This is useful for situations where many servers
|
|
need the same user accounts, for example as a replacement for
|
|
<application>NIS</application>.</para>
|
|
</abstract>
|
|
</info>
|
|
|
|
<sect1 xml: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. This
|
|
document will attempt to provide an explanation of
|
|
<package>net/nss_ldap</package> and
|
|
<package>security/pam_ldap</package> for use with client
|
|
machines services for use with the LDAP server.</para>
|
|
|
|
<para>When finished, the reader should be able to configure and
|
|
deploy a &os; server that can host an LDAP directory, and to
|
|
configure and deploy a &os; server which can authenticate
|
|
against an LDAP directory.</para>
|
|
|
|
<para>This article is not intended to be an exhaustive account of
|
|
the security, robustness, or best practice considerations for
|
|
configuring LDAP or the other services discussed herein. While
|
|
the author takes care to do everything correctly, they do not
|
|
address security issues beyond a general scope. This article
|
|
should be considered to lay the theoretical groundwork only, and
|
|
any actual implementation should be accompanied by careful
|
|
requirement analysis.</para>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="ldap">
|
|
<title>Configuring LDAP</title>
|
|
|
|
<para>LDAP stands for <quote>Lightweight Directory Access
|
|
Protocol</quote> and is a subset of the X.500 Directory Access
|
|
Protocol. Its most recent specifications are in <link
|
|
xlink:href="http://www.ietf.org/rfc/rfc4510.txt">RFC4510</link>
|
|
and friends. Essentially it is a database that expects to be
|
|
read from more often than it is written to.</para>
|
|
|
|
<para>The LDAP server <link
|
|
xlink:href="http://www.openldap.org/">OpenLDAP</link> will be
|
|
used in the examples in this document; while the principles here
|
|
should be generally applicable to many different servers, most
|
|
of the concrete administration is
|
|
<application>OpenLDAP</application>-specific. There are several
|
|
server versions in ports, for example
|
|
<package>net/openldap24-server</package>. Client servers will
|
|
need the corresponding <package>net/openldap24-client</package>
|
|
libraries.</para>
|
|
|
|
<para>There are (basically) two areas of the LDAP service which
|
|
need configuration. The first is setting up a server to receive
|
|
connections properly, and the second is adding entries to the
|
|
server's directory so that &os; tools know how to interact with
|
|
it.</para>
|
|
|
|
<sect2 xml:id="ldap-connect">
|
|
<title>Setting Up the Server for Connections</title>
|
|
|
|
<note>
|
|
<para>This section is specific to
|
|
<application>OpenLDAP</application>. If you are using
|
|
another server, you will need to consult that server's
|
|
documentation.</para>
|
|
</note>
|
|
|
|
<sect3 xml:id="ldap-connect-install">
|
|
<title>Installing <application>OpenLDAP</application></title>
|
|
|
|
<para>First, install
|
|
<application>OpenLDAP</application>:</para>
|
|
|
|
<example xml:id="oldap-install">
|
|
<title>Installing
|
|
<application>OpenLDAP</application></title>
|
|
|
|
<screen>&prompt.root; <userinput>cd /usr/ports/net/openldap24-server</userinput>
|
|
&prompt.root; make install clean</screen>
|
|
</example>
|
|
|
|
<para>This installs the <command>slapd</command> and
|
|
<command>slurpd</command> binaries, along with the required
|
|
<application>OpenLDAP</application> libraries.</para>
|
|
</sect3>
|
|
|
|
<sect3 xml:id="ldap-connect-config">
|
|
<title>Configuring <application>OpenLDAP</application></title>
|
|
|
|
<para>Next we must configure
|
|
<application>OpenLDAP</application>.</para>
|
|
|
|
<para>You will want to require encryption in your connections
|
|
to the LDAP server; otherwise your users' passwords will be
|
|
transferred in plain text, which is considered insecure.
|
|
The tools we will be using support two very similar kinds of
|
|
encryption, SSL and TLS.</para>
|
|
|
|
<para>TLS stands for <quote>Transportation Layer
|
|
Security</quote>. Services that employ TLS tend to
|
|
connect on the <emphasis>same</emphasis> ports as the 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 <quote>Secure Sockets Layer</quote>, 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 25), HTTPS listens on
|
|
443, and LDAPS on 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
|
|
beginning. Other than that there are no substantial
|
|
differences between the two.</para>
|
|
|
|
<note>
|
|
<para>We will adjust <application>OpenLDAP</application> to
|
|
use TLS, as SSL is considered deprecated.</para>
|
|
</note>
|
|
|
|
<para>Once <application>OpenLDAP</application> is installed
|
|
via ports, the following configuration parameters in
|
|
<filename>/usr/local/etc/openldap/slapd.conf</filename> will
|
|
enable TLS:</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
|
|
<application>OpenLDAP</application> to require 128-bit
|
|
encryption for all connections, both search and update.
|
|
This parameter may be configured based on the security needs
|
|
of your site, but rarely you need to weaken it, as most LDAP
|
|
client libraries support strong encryption.</para>
|
|
|
|
<para>The <filename>cert.crt</filename>,
|
|
<filename>cert.key</filename>, and
|
|
<filename>cacert.crt</filename> files 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>
|
|
|
|
<example xml:id="genrsa">
|
|
<title>Generating an RSA Key</title>
|
|
|
|
<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>
|
|
</example>
|
|
|
|
<para>At this point you should be prompted for some values.
|
|
You may enter whatever values you like; however, it is
|
|
important the <quote>Common Name</quote> value be the fully
|
|
qualified domain name of the
|
|
<application>OpenLDAP</application> server. In our case,
|
|
and the examples here, the server is
|
|
<replaceable>server.example.org</replaceable>. Incorrectly
|
|
setting this value will cause clients to fail when making
|
|
connections. This can the cause of great frustration, so
|
|
ensure that you follow these steps closely.</para>
|
|
|
|
<para>Finally, the certificate signing request needs to be
|
|
signed:</para>
|
|
|
|
<example xml:id="self-sign">
|
|
<title>Self-signing the Certificate</title>
|
|
|
|
<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>
|
|
</example>
|
|
|
|
<para>This will create a self-signed certificate that can be
|
|
used for the directives in <filename>slapd.conf</filename>,
|
|
where <filename>cert.crt</filename> and
|
|
<filename>cacert.crt</filename> are the same file. If you
|
|
are going to use many <application>OpenLDAP</application>
|
|
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 individual server
|
|
certificates.</para>
|
|
|
|
<para>Once this is done, put the following in
|
|
<filename>/etc/rc.conf</filename>:</para>
|
|
|
|
<programlisting>slapd_enable="YES"</programlisting>
|
|
|
|
<para>Then run <userinput>/usr/local/etc/rc.d/slapd
|
|
start</userinput>. This should start
|
|
<application>OpenLDAP</application>. Confirm that it is
|
|
listening on 389 with</para>
|
|
|
|
<screen>&prompt.user; <userinput>sockstat -4 -p 389</userinput>
|
|
ldap slapd 3261 7 tcp4 *:389 *:*</screen>
|
|
</sect3>
|
|
|
|
<sect3 xml:id="ldap-connect-client">
|
|
<title>Configuring the Client</title>
|
|
|
|
<para>Install the <package>net/openldap24-client</package>
|
|
port for the <application>OpenLDAP</application> libraries.
|
|
The client machines will always have
|
|
<application>OpenLDAP</application> libraries since that is
|
|
all <package>security/pam_ldap</package> and
|
|
<package>net/nss_ldap</package> support, at least for the
|
|
moment.</para>
|
|
|
|
<para>The configuration file for the
|
|
<application>OpenLDAP</application> libraries is
|
|
<filename>/usr/local/etc/openldap/ldap.conf</filename>.
|
|
Edit this file to contain the following values:</para>
|
|
|
|
<programlisting>base dc=example,dc=org
|
|
uri ldap://server.example.org/
|
|
ssl start_tls
|
|
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>
|
|
|
|
<note>
|
|
<para>There are two files called
|
|
<filename>ldap.conf</filename>. The first is this file,
|
|
which is for the <application>OpenLDAP</application>
|
|
libraries and defines how to talk to the server. The
|
|
second is <filename>/usr/local/etc/ldap.conf</filename>,
|
|
and is for <application>pam_ldap</application>.</para>
|
|
</note>
|
|
|
|
<para>At this point you should be able to run
|
|
<userinput>ldapsearch -Z</userinput> on the client machine;
|
|
<option>-Z</option> means <quote>use TLS</quote>. If you
|
|
encounter an error, then something is configured wrong; most
|
|
likely it is your certificates. Use &man.openssl.1;'s
|
|
<command>s_client</command> and <command>s_server</command>
|
|
to ensure you have them configured and signed
|
|
properly.</para>
|
|
</sect3>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="ldap-database">
|
|
<title>Entries in the Database</title>
|
|
|
|
<para>Authentication against an LDAP directory is generally
|
|
accomplished by attempting to bind to the directory as the
|
|
connecting user. This is done by establishing a
|
|
<quote>simple</quote> bind on the directory with the user name
|
|
supplied. If there is an entry with the
|
|
<literal>uid</literal> equal to the user name and that entry's
|
|
<literal>userPassword</literal> attribute matches the password
|
|
supplied, then the bind is successful.</para>
|
|
|
|
<para>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=example,dc=org</literal>. The default location
|
|
for users that most clients seem to expect is something like
|
|
<literal>ou=people,<replaceable>base</replaceable></literal>,
|
|
so that is what will be used here. However keep in mind that
|
|
this is configurable.</para>
|
|
|
|
<para>So the ldif entry for the <literal>people</literal>
|
|
organizational unit will look like:</para>
|
|
|
|
<programlisting>dn: ou=people,dc=example,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 against which to authenticate. However, if
|
|
you are going to store user information in the LDAP database
|
|
as well, you will probably want to use
|
|
<literal>inetOrgPerson</literal>, which has many useful
|
|
attributes. 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=example,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 to avoid collisions
|
|
with system accounts; you can configure whatever number you
|
|
wish here, as long as it is 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=example,dc=org
|
|
objectClass: top
|
|
objectClass: organizationalUnit
|
|
ou: groups
|
|
|
|
dn: cn=tuser,ou=groups,dc=example,dc=org
|
|
objectClass: posixGroup
|
|
objectClass: top
|
|
gidNumber: 10000
|
|
cn: tuser</programlisting>
|
|
|
|
<para>To enter these into your database, you can use
|
|
<command>slapadd</command> or <command>ldapadd</command> on a
|
|
file containing these entries. Alternatively, you can use
|
|
<package>sysutils/ldapvi</package>.</para>
|
|
|
|
<para>The <command>ldapsearch</command> utility 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 xml:id="client">
|
|
<title>Client Configuration</title>
|
|
|
|
<para>The client should already have
|
|
<application>OpenLDAP</application> libraries from <xref
|
|
linkend="ldap-connect-client"/>, but if you are installing
|
|
several client machines you will need to install
|
|
<package>net/openldap24-client</package> on each of them.</para>
|
|
|
|
<para>&os; requires two ports to be installed to authenticate
|
|
against an LDAP server, <package>security/pam_ldap</package> and
|
|
<package>net/nss_ldap</package>.</para>
|
|
|
|
<sect2 xml:id="client-auth">
|
|
<title>Authentication</title>
|
|
|
|
<para><package>security/pam_ldap</package> is configured via
|
|
<filename>/usr/local/etc/ldap.conf</filename>.</para>
|
|
|
|
<note>
|
|
<para>This is a <emphasis>different file</emphasis> than the
|
|
<application>OpenLDAP</application> 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>
|
|
</note>
|
|
|
|
<para>Thus, 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 <package>security/pam_ldap</package> 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_login_attribute</literal> directive in
|
|
<filename>ldap.conf</filename>:</para>
|
|
|
|
<example xml:id="set-pam-login-attr">
|
|
<title>Setting <literal>pam_login_attribute</literal></title>
|
|
|
|
<programlisting>pam_login_attribute uid</programlisting>
|
|
</example>
|
|
|
|
<para>With this set, <package>security/pam_ldap</package> 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, it will attempt to bind as
|
|
that user with the password it was given. If it binds
|
|
correctly, then it will allow access. Otherwise it will
|
|
fail.</para>
|
|
|
|
<para>Users whose shell is not in
|
|
<filename>/etc/shells</filename> will not be able to log in.
|
|
This is particularly important when
|
|
<application>Bash</application> is set as the user shell on
|
|
the LDAP server. <application>Bash</application> is not
|
|
included with a default installation of &os;. When installed
|
|
from a package or port, it is located at
|
|
<filename>/usr/local/bin/bash</filename>. Verify that the
|
|
path to the shell on the server is set correctly:</para>
|
|
|
|
<screen>&prompt.user; <userinput>getent passwd <replaceable>username</replaceable></userinput></screen>
|
|
|
|
<para>There are two choices when the output shows
|
|
<literal>/bin/bash</literal> in the last column. The first is
|
|
to change the user's entry on the LDAP server to
|
|
<filename>/usr/local/bin/bash</filename>. The second option
|
|
is to create a symlink on the LDAP client computer so
|
|
<application>Bash</application> is found at the correct
|
|
location:</para>
|
|
|
|
<screen>&prompt.root; <userinput>ln -s /usr/local/bin/bash /bin/bash</userinput></screen>
|
|
|
|
<para>Make sure that <filename>/etc/shells</filename> contains
|
|
entries for both <literal>/usr/local/bin/bash</literal> and
|
|
<literal>/bin/bash</literal>. The user will then be able to
|
|
log in to the system with <application>Bash</application> as
|
|
their shell.</para>
|
|
|
|
<sect3 xml:id="client-auth-pam">
|
|
<title>PAM</title>
|
|
|
|
<para>PAM, which stands for <quote>Pluggable Authentication
|
|
Modules</quote>, 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 a 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
|
|
<application>SSH</application> (remember to set the relevant
|
|
options in <filename>/etc/ssh/sshd_config</filename>,
|
|
otherwise <application>SSH</application> 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.
|
|
<application>PAM</application> will perform a bind with your
|
|
credentials, and if successful will tell
|
|
<application>SSH</application> to allow access.</para>
|
|
|
|
<para>However it is not a good idea to allow
|
|
<emphasis>every</emphasis> user in the directory into
|
|
<emphasis>every</emphasis> client machine. With the current
|
|
configuration, all that a user needs to log into a machine
|
|
is an LDAP entry. 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. For example, if you have</para>
|
|
|
|
<programlisting>pam_groupdn cn=servername,ou=accessgroups,dc=example,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:</para>
|
|
|
|
<programlisting>memberUid: uid=someuser,ou=people,dc=example,dc=org</programlisting>
|
|
|
|
<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, <emphasis>every</emphasis> user to be listed in the
|
|
group, which is not necessarily what we want. To avoid
|
|
blocking users that are not in LDAP, you should enable the
|
|
<literal>ignore_unknown_user</literal> attribute. Finally,
|
|
you should set the
|
|
<literal>ignore_authinfo_unavail</literal> option so that
|
|
you are not locked out of every computer when the LDAP
|
|
server is unavailable.</para>
|
|
|
|
<para>Your <filename>pam.d/sshd</filename> might then end up
|
|
looking like this:</para>
|
|
|
|
<example xml:id="pam">
|
|
<title>Sample <filename>pam.d/sshd</filename></title>
|
|
|
|
<programlisting>auth required pam_nologin.so no_warn
|
|
auth sufficient pam_opie.so no_warn no_fake_prompts
|
|
auth requisite pam_opieaccess.so no_warn allow_local
|
|
auth sufficient /usr/local/lib/pam_ldap.so no_warn
|
|
auth required pam_unix.so no_warn try_first_pass
|
|
|
|
account required pam_login_access.so
|
|
account required /usr/local/lib/pam_ldap.so no_warn ignore_authinfo_unavail ignore_unknown_user</programlisting>
|
|
</example>
|
|
|
|
<note>
|
|
<para>Since we are adding these lines specifically to
|
|
<filename>pam.d/sshd</filename>, this will only have an
|
|
effect on <application>SSH</application> sessions. LDAP
|
|
users will be unable to log in at the console. To change
|
|
this behavior, examine the other files in
|
|
<filename>/etc/pam.d</filename> and modify them
|
|
accordingly.</para>
|
|
</note>
|
|
</sect3>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="client-nss">
|
|
<title>Name Service Switch</title>
|
|
|
|
<para><application>NSS</application> is the service that maps
|
|
attributes to names. So, for example, if a file is owned by
|
|
user <literal>1001</literal>, an application will query
|
|
<application>NSS</application> for the name of
|
|
<literal>1001</literal>, and it might get
|
|
<literal>bob</literal> or <literal>ted</literal> or whatever
|
|
the user's name is.</para>
|
|
|
|
<para>Now that our user information is kept in LDAP, we need to
|
|
tell <application>NSS</application> to look there when
|
|
queried.</para>
|
|
|
|
<para>The <package>net/nss_ldap</package> port does this. It
|
|
uses the same configuration file as
|
|
<package>security/pam_ldap</package>, 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>
|
|
|
|
<sect2 xml:id="caveats">
|
|
<title>Caveats</title>
|
|
|
|
<para>Unfortunately, as of the time this was written &os; did
|
|
not support changing user passwords with &man.passwd.1;.
|
|
Because of this, most administrators are left to implement a
|
|
solution themselves. I provide some examples here. Note that
|
|
if you write your own password change script, there are some
|
|
security issues you should be made aware of; see <xref
|
|
linkend="security-passwd"/></para>
|
|
|
|
<example xml:id="chpw-shell">
|
|
<title>Shell Script for Changing Passwords</title>
|
|
|
|
<programlisting><![CDATA[#!/bin/sh
|
|
|
|
stty -echo
|
|
read -p "Old Password: " oldp; echo
|
|
read -p "New Password: " np1; echo
|
|
read -p "Retype New Password: " np2; echo
|
|
stty echo
|
|
|
|
if [ "$np1" != "$np2" ]; then
|
|
echo "Passwords do not match."
|
|
exit 1
|
|
fi
|
|
|
|
ldappasswd -D uid="$USER",ou=people,dc=example,dc=org \
|
|
-w "$oldp" \
|
|
-a "$oldp" \
|
|
-s "$np1"]]></programlisting>
|
|
</example>
|
|
|
|
<caution>
|
|
<para>This script does hardly any error checking, but more
|
|
important it is very cavalier about how it stores your
|
|
passwords. If you do anything like this, at least adjust
|
|
the <literal>security.bsd.see_other_uids</literal> sysctl
|
|
value:</para>
|
|
|
|
<screen>&prompt.root; <userinput>sysctl security.bsd.see_other_uids=0</userinput></screen>
|
|
</caution>
|
|
|
|
<para>A more flexible (and probably more secure) approach can be
|
|
used by writing a custom program, or even a web interface.
|
|
The following is part of a <application>Ruby</application>
|
|
library that can change LDAP passwords. It sees use both on
|
|
the command line, and on the web.</para>
|
|
|
|
<example xml:id="chpw-ruby">
|
|
<title>Ruby Script for Changing Passwords</title>
|
|
|
|
<programlisting><![CDATA[require 'ldap'
|
|
require 'base64'
|
|
require 'digest'
|
|
require 'password' # ruby-password
|
|
|
|
ldap_server = "ldap.example.org"
|
|
luser = "uid=#{ENV['USER']},ou=people,dc=example,dc=org"
|
|
|
|
# get the new password, check it, and create a salted hash from it
|
|
def get_password
|
|
pwd1 = Password.get("New Password: ")
|
|
pwd2 = Password.get("Retype New Password: ")
|
|
|
|
raise if pwd1 != pwd2
|
|
pwd1.check # check password strength
|
|
|
|
salt = rand.to_s.gsub(/0\./, '')
|
|
pass = pwd1.to_s
|
|
hash = "{SSHA}"+Base64.encode64(Digest::SHA1.digest("#{pass}#{salt}")+salt).chomp!
|
|
return hash
|
|
end
|
|
|
|
oldp = Password.get("Old Password: ")
|
|
newp = get_password
|
|
|
|
# We'll just replace it. That we can bind proves that we either know
|
|
# the old password or are an admin.
|
|
|
|
replace = LDAP::Mod.new(LDAP::LDAP_MOD_REPLACE | LDAP::LDAP_MOD_BVALUES,
|
|
"userPassword",
|
|
[newp])
|
|
|
|
conn = LDAP::SSLConn.new(ldap_server, 389, true)
|
|
conn.set_option(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3)
|
|
conn.bind(luser, oldp)
|
|
conn.modify(luser, [replace])]]></programlisting>
|
|
</example>
|
|
|
|
<para>Although not guaranteed to be free of security holes (the
|
|
password is kept in memory, for example) this is cleaner and
|
|
more flexible than a simple <command>sh</command>
|
|
script.</para>
|
|
</sect2>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="secure">
|
|
<title>Security Considerations</title>
|
|
|
|
<para>Now that your machines (and possibly other services) are
|
|
authenticating against your LDAP server, this server needs to be
|
|
protected at least as well as
|
|
<filename>/etc/master.passwd</filename> would be on a regular
|
|
server, and possibly even more so since a broken or cracked LDAP
|
|
server would break every client service.</para>
|
|
|
|
<para>Remember, this section is not exhaustive. You should
|
|
continually review your configuration and procedures for
|
|
improvements.</para>
|
|
|
|
<sect2 xml:id="secure-readonly">
|
|
<title>Setting Attributes Read-only</title>
|
|
|
|
<para>Several attributes in LDAP should be read-only. If left
|
|
writable by the user, for example, a user could change his
|
|
<literal>uidNumber</literal> attribute to <literal>0</literal>
|
|
and get <systemitem class="username">root</systemitem>
|
|
access!</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>
|
|
|
|
<example xml:id="hide-userpass">
|
|
<title>Hide Passwords</title>
|
|
|
|
<programlisting>access to dn.subtree="ou=people,dc=example,dc=org"
|
|
attrs=userPassword
|
|
by self write
|
|
by anonymous auth
|
|
by * none
|
|
|
|
access to *
|
|
by self write
|
|
by * read</programlisting>
|
|
</example>
|
|
|
|
<para>This will disallow reading of the
|
|
<literal>userPassword</literal> attribute, while still
|
|
allowing users to change their own passwords.</para>
|
|
|
|
<para>Additionally, you'll want to keep users from changing some
|
|
of their own attributes. 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>
|
|
|
|
<example xml:id="attrib-readonly">
|
|
<title>Read-only Attributes</title>
|
|
|
|
<programlisting>access to dn.subtree="ou=people,dc=example,dc=org"
|
|
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>
|
|
</example>
|
|
|
|
<para>This will stop users from being able to masquerade as
|
|
other users.</para>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="secure-root">
|
|
<title><systemitem class="username">root</systemitem> Account
|
|
Definition</title>
|
|
|
|
<para>Often the <systemitem class="username">root</systemitem>
|
|
or manager account for the LDAP service will be defined in the
|
|
configuration file. <application>OpenLDAP</application>
|
|
supports this, for example, and it works, but it can lead to
|
|
trouble if <filename>slapd.conf</filename> is compromised. It
|
|
may be better to use this only to bootstrap yourself into
|
|
LDAP, and then define a <systemitem
|
|
class="username">root</systemitem> account there.</para>
|
|
|
|
<para>Even better is to define accounts that have limited
|
|
permissions, and omit a <systemitem
|
|
class="username">root</systemitem> account entirely. For
|
|
example, users that can add or remove user accounts are added
|
|
to one group, but they cannot themselves change the membership
|
|
of this group. Such a security policy would help mitigate the
|
|
effects of a leaked password.</para>
|
|
|
|
<sect3 xml:id="manager-acct">
|
|
<title>Creating a Management Group</title>
|
|
|
|
<para>Say you want your IT department to be able to change
|
|
home directories for users, but you do not want all of them
|
|
to be able to add or remove users. The way to do this is to
|
|
add a group for these admins:</para>
|
|
|
|
<example xml:id="manager-acct-dn">
|
|
<title>Creating a Management Group</title>
|
|
|
|
<programlisting>dn: cn=homemanagement,dc=example,dc=org
|
|
objectClass: top
|
|
objectClass: posixGroup
|
|
cn: homemanagement
|
|
gidNumber: 121 # required for posixGroup
|
|
memberUid: uid=tuser,ou=people,dc=example,dc=org
|
|
memberUid: uid=user2,ou=people,dc=example,dc=org</programlisting>
|
|
</example>
|
|
|
|
<para>And then change the permissions attributes in
|
|
<filename>slapd.conf</filename>:</para>
|
|
|
|
<example xml:id="management-acct-acl">
|
|
<title>ACLs for a Home Directory Management Group</title>
|
|
|
|
<programlisting>access to dn.subtree="ou=people,dc=example,dc=org"
|
|
attr=homeDirectory
|
|
by dn="cn=homemanagement,dc=example,dc=org"
|
|
dnattr=memberUid write</programlisting>
|
|
</example>
|
|
|
|
<para>Now <systemitem class="username">tuser</systemitem> and
|
|
<systemitem class="username">user2</systemitem> can change
|
|
other users' home directories.</para>
|
|
|
|
<para>In this example we have given a subset of administrative
|
|
power to certain users without giving them power in other
|
|
domains. The idea is that soon no single user account has
|
|
the power of a <systemitem
|
|
class="username">root</systemitem> account, but every
|
|
power root had is had by at least one user. The <systemitem
|
|
class="username">root</systemitem> account then becomes
|
|
unnecessary and can be removed.</para>
|
|
</sect3>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="security-passwd">
|
|
<title>Password Storage</title>
|
|
|
|
<para>By default <application>OpenLDAP</application> will store
|
|
the value of the <literal>userPassword</literal> attribute as
|
|
it stores any other data: in the clear. Most of the time it
|
|
is base 64 encoded, which provides enough protection to keep
|
|
an honest administrator from knowing your password, but little
|
|
else.</para>
|
|
|
|
<para>It is a good idea, then, to store passwords in a more
|
|
secure format, such as SSHA (salted SHA). This is done by
|
|
whatever program you use to change users' passwords.</para>
|
|
</sect2>
|
|
</sect1>
|
|
|
|
<appendix xml: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><package>security/pam_mkhomedir</package> 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><package>sysutils/cpu</package> 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 <option>-x</option> flag) and SSL
|
|
(directly).</para>
|
|
|
|
<para><package>sysutils/ldapvi</package> is a great utility for
|
|
editing LDAP values in an LDIF-like syntax. The directory (or
|
|
subsection of the directory) is presented in the editor chosen
|
|
by the <envar>EDITOR</envar> environment variable. This makes
|
|
it easy to enable large-scale changes in the directory without
|
|
having to write a custom tool.</para>
|
|
|
|
<para><package>security/openssh-portable</package> has the ability
|
|
to contact an LDAP server to verify
|
|
<application>SSH</application> keys. This is extremely nice if
|
|
you have many servers and do not want to copy your public keys
|
|
across all of them.</para>
|
|
</appendix>
|
|
|
|
<appendix xml:id="ssl-ca">
|
|
<title><application>OpenSSL</application> 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>
|
|
|
|
<example xml:id="make-cert">
|
|
<title>Creating a Certificate</title>
|
|
|
|
<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>
|
|
</example>
|
|
|
|
<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 <option>-CA</option> and
|
|
<option>-CAkey</option> instead of
|
|
<option>-signkey</option>:</para>
|
|
|
|
<example xml:id="ca-sign">
|
|
<title>Signing as a Certificate Authority</title>
|
|
|
|
<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>
|
|
</example>
|
|
|
|
<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>
|