\documentclass{ictlab}

\RCS $Revision: 1.6 $

\usepackage{verbatim,key,alltt,amstext,answer2}
\usepackage[hang,bf,nooneline]{caption2}
\usepackage[pdfpagemode=None,pdfauthor={Nick Urbanik}]{hyperref}

\newcommand*{\labTitle}{LDAP Schemas}

\renewcommand*{\floatpagefraction}{0.75} % default is .5, to increase
% density.
\renewcommand*{\bottomfraction}{0.6} % default is 0.3
\renewcommand*{\topfraction}{0.85} % default is 0.7
\renewcommand*{\textfraction}{0.1} % default is 0.2


\begin{document}

\section{Background}

The \LDAP protocol is a standard for network directories.  Some
proprietary directory servers have been based on \LDAP, for example,
Novell Directory Services (\NDS) or Microsoft Active Directory
(M?\acro{AD}).  Open\LDAP is a standard \LDAP server project begun
many years ago at the University of Michegan\e.  The \URL is
\url{http://www.openldap.org/}.

The \LDAP protocol provides a \emph{standard method for query} over
the network (unlike \acro{SQL}, which has many proprietary
extensions), and is designed for \emph{rapid read access} over the
network.  It also \emph{supports encryption} using \TLS with the start
\TLS protocol.  It supports security functions that allow it to be
used for authentication and for storing system information.  An \LDAP
directory is not designed for storing large ``chunks'' of data, and
has a slower write operation than many relational databases.  It also
does not support transactional integrity in the same way as a
relational database.

\LDAP directories can be used for storing computer account
information, for email information, for corporate address books, for
network management information, and for many other applications.  


\section{Overview}
\label{sec:overview}

Here you will create an \LDAP directory that provides network
authentication.  You will create some accounts on your computer, then
migrate them to your \LDAP directory.  You will then configure your
machine to use these network accounts.

\section{Procedure}
\label{sec:procedure}


\subsection{Setting up your directory server}
\label{sec:setting-up-server}


\begin{enumerate}
\item Ensure that the \texttt{openldap-servers} package is installed
  on your computer:
\begin{verbatim}
$ rpm -qa | grep openldap-
openldap-clients-2.0.27-2.8.0
openldap-servers-2.0.27-2.8.0
openldap-2.0.27-2.8.0
openldap-devel-2.0.27-2.8.0
\end{verbatim}%$
  If not, then install the updates (this will replace any that are
  missing):
\begin{alltt}
$ \textbf{sudo rpm -Uhv /home/nfs/rh-8.0-updated/RedHat/RPMS/openldap-*}
\end{alltt}%$
\end{enumerate}


\subsection{Configuring the \LDAP server}
\label{sec:configuration}


\begin{enumerate}
\item Edit the configuration file for your \LDAP server and create a
  proper secure hashed password for the manager.  The password you
  choose should be different from your other passwords.  It will be
  used to administer your directory.
\begin{alltt}
$ \textbf{xhost +localhost}
$ \textbf{sudo -v}
$ \textbf{sudo emacs /etc/openldap/slapd.conf &}
$ \textbf{slappasswd}
New password:
Re-enter new password:
\{SSHA\}PTPNHoKcjUjKAKHb/yyCV/C6N4rBK31v
\end{alltt}
Now copy and paste this password into
\texttt{/etc/openldap/slapd.conf} so that exactly one uncommented line
begins with \texttt{rootpw} and looks like this (but copy and paste
\emph{your} password, not this silly example):
\begin{verbatim}
rootpw         {SSHA}PTPNHoKcjUjKAKHb/yyCV/C6N4rBK31v
\end{verbatim}

\item Select a domain name for your system.  Here I use the domain
  \texttt{abc.com}.  If you stick with \texttt{my-domain.com}, you can
  skip the next step.
  
\item Specify the \emph{base} of your directory.  Change all cases of
  ``\texttt{dc=my-domain,dc=com}'' to match your domain.  For example
  if your domain is \texttt{abc.com}, then change all cases of
  ``\texttt{dc=my-domain,dc=com}'' to ``\texttt{dc=abc,dc=com}''.
  Another example: the domain \texttt{tyict.vtc.edu.hk} would give you
  a distinguished name for your base of
  ``\texttt{dc=tyict,dc=vtc,dc=edu,dc=hk}''.
  
\item Add the following to the section on access control to restrict
  access to the password fields:
\begin{verbatim}
access to attr=userPassword
        by self write
        by anonymous auth
        by * none

access to attr=loginShell
        by self write
        by * read

access to *
        by * read
\end{verbatim}
  \begin{explanation}
    This provides access control to three sets of attributes: the
    attribute \texttt{userPassword}, the attribute
    \texttt{loginShell}, and all other attributes.

    The rules to access the \texttt{userPassword} attribute are:
    \begin{itemize}
    \item If you are logged in, you can read and write your own
      password (in other words, you can change your own password)
    \item If you are connected anonymously to the directory server,
      you are allowed to authenticate, but you may not read or write
      any passwords.
    \item No other access to the \texttt{userPassword} attribute is
      permitted.
    \end{itemize}

    The rules to access the attribute \texttt{loginShell} are:
    \begin{itemize}
    \item If you are logged in, you can read and write the attribute
      \texttt{loginShell} --- in other words, you can change your own
      shell.
    \item Other users have read access to all \texttt{loginShell}
      attributes.
    \end{itemize}
    
    The last rule controls access to all other attributes that are not
    covered earlier; it means that all other attributes can be read by
    anyone.
  \end{explanation}

\item Replace the line in \texttt{/etc/openldap/slapd.conf} that loads
  the \texttt{rfc822-MailMember.schema} and load the
  \texttt{misc.schema} instead.  The change to your
  \texttt{slapd.conf} will look like this:
\begin{verbatim}
# include       /etc/openldap/schema/redhat/rfc822-MailMember.schema
include         /etc/openldap/schema/misc.schema
\end{verbatim}

\item Make sure you save \texttt{slapd.conf} before you move to the
  following steps.

\item The configuration file \emph{must} be owned by the user
  \texttt{ldap}:
\begin{alltt}
$ \textbf{sudo chown ldap.ldap /etc/openldap/slapd.conf}
\end{alltt}%$

\item Start the ldap service, and ensure it starts when you boot your
  computer:
\begin{alltt}
$ \textbf{sudo service ldap start}
$ \textbf{sudo chkconfig --level 345 ldap on}
\end{alltt}
\item Make sure it's running:
\begin{alltt}
$ \textbf{sudo service ldap status}
slapd (pid 19150 19077 19076) is running...
\end{alltt}%$
\end{enumerate}


\subsection{Set up the Migration Tools}
\label{sec:setup-migration-tools}

\begin{enumerate}
\item The following patch to the migration tools restricts what is put
  into the directory to non-system users and groups.  It is available
  from
  \url{http://nicku.org/snm/lab/ldap-schemas/migration-tools-rh-nick.patch}.
  Apply it to the migration tools that come with OpenLDAP like this:
\begin{alltt}
$ \textbf{cd /usr/share/openldap/migration}
$ \textbf{sudo patch -b < \(\sim\)/migration-tools-rh-nick.patch}
\end{alltt}
{\small%
\begin{verbatim}
--- migrate_common.ph.orig      2002-12-14 04:42:40.000000000 +0800
+++ migrate_common.ph   2003-04-22 14:05:06.000000000 +0800
@@ -67,6 +67,13 @@
        $NAMINGCONTEXT{'services'}          = "ou=Services";
 }
 
+# Nick: reduce directory to what we need:
+my @wanted_namingcontexts = ( 'passwd', 'group' );
+for my $context ( keys %NAMINGCONTEXT ) {
+    delete $NAMINGCONTEXT{$context}
+        unless grep { /$context/ } @wanted_namingcontexts;
+}
+
 # Default DNS domain
 $DEFAULT_MAIL_DOMAIN = "padl.com";
 
--- migrate_group.pl.orig       2002-12-14 04:42:40.000000000 +0800
+++ migrate_group.pl    2003-04-22 14:05:06.000000000 +0800
@@ -41,6 +41,10 @@
 $PROGRAM = "migrate_group.pl";
 $NAMINGCONTEXT = &getsuffix($PROGRAM);
 
+# Nick Urbanik <nicku@vtc.edu.hk>
+# It's a lousy idea to create groups for system groups, which depend
+# on the locally installed software.
+my $min_gidNumber = 100;
 &parse_args();
 &open_files();
 
@@ -52,6 +56,7 @@
 
        local($group, $pwd, $gid, $users) = split(/:/);
        
+        next unless $gid >= $min_gidNumber;
        if ($use_stdout) {
                &dump_group(STDOUT, $group, $pwd, $gid, $users);
        } else {
--- migrate_passwd.pl.orig      2002-12-14 04:42:40.000000000 +0800
+++ migrate_passwd.pl   2003-04-26 16:27:29.000000000 +0800
@@ -42,6 +42,10 @@
 $PROGRAM = "migrate_passwd.pl";
 $NAMINGCONTEXT = &getsuffix($PROGRAM);
 
+# Nick Urbanik <nicku@vtc.edu.hk>
+# It's a lousy idea to create accounts for system user accounts, which depend
+# on the locally installed software.
+my $min_uidNumber = 500;
 &parse_args();
 &read_shadow_file();
 &open_files();
@@ -73,6 +77,7 @@
     s/å/ae/g;
 
     local($user, $pwd, $uid, $gid, $gecos, $homedir, $shell) = split(/:/);
+    next unless $uid >= $min_uidNumber;
     
     if ($use_stdout) {
        &dump_user(STDOUT, $user, $pwd, $uid, $gid, $gecos, $homedir, $shell);
@@ -122,7 +127,7 @@
                if ($DEFAULT_MAIL_HOST) {
                        print $HANDLE "mailRoutingAddress: $user\@$DEFAULT_MAIL_HOST\n";
                        print $HANDLE "mailHost: $DEFAULT_MAIL_HOST\n";
-                       print $HANDLE "objectClass: mailRecipient\n";
+                       print $HANDLE "objectClass: inetLocalMailRecipient\n";
                }
                print $HANDLE "objectClass: person\n";
                print $HANDLE "objectClass: organizationalPerson\n";
\end{verbatim}%$
}
\item Create a little shell file to set up your environment that looks
  something like this (available from\\
  \url{http://nicku.org/snm/lab/ldap-schemas/migrate-environment.sh}).
  It \emph{must} match the base and domain name you selected earlier.
\begin{verbatim}
# Should source into current environment
 
LDAP_BASEDN="dc=abc,dc=com"
# Kerberos realm:
LDAP_DEFAULT_MAIL_DOMAIN="abc.com"
LDAP_DEFAULT_MAIL_HOST="abc.com"
LDAP_BINDDN=
LDAP_EXTENDED_SCHEMA=1
 
export LDAP_BASEDN LDAP_DEFAULT_MAIL_DOMAIN LDAP_DEFAULT_MAIL_HOST \
       LDAP_BINDD LDAP_EXTENDED_SCHEMA
\end{verbatim}

\item \emph{Source} this file into the current shell process:
\begin{alltt}
$ \textbf{. \(\sim\)/migrate-environment.sh}    
\end{alltt}%$
Note that you need to do this before running any of the migration
tools.  Check the environment is set correctly with the command:
\begin{alltt}
$ \textbf{set | grep LDAP}
\end{alltt}%$
\end{enumerate}


\subsection{Generating the Base Entries for your Directory}
\label{sec:generating-the-base-entries}

The directory will have a tree structure.  Before you can add user
accounts to it, you need to create the top-level entries for your
directory.  We create them here.
\begin{enumerate}
\item Now change to the migrate directory and generate some \LDIF:
\begin{alltt}
$ \textbf{cd /usr/share/openldap/migration}
$ \textbf{./migrate_base.pl | tee \(\sim\)/base.ldif}
\end{alltt}

\item Carefully examine the resulting \LDIF.

\item Add your \LDIF to the the \LDAP database:
\begin{alltt}
$ \textbf{ldapadd -x -h localhost -D "cn=Manager,dc=abc,dc=com" -W -f \(\sim\)/base.ldif}
\end{alltt}%$
\item When prompted, enter the directory administrator password you
  created earlier.

\item If you see any error messages, correct the errors in either the
  \texttt{slapd.conf} file or your \LDIF input.
  
\item Verify that your directory has the base entries:
\begin{alltt}
$ \textbf{ldapsearch -x -h 'localhost' -b 'dc=abc,dc=com'}
\end{alltt}%$
\end{enumerate}


\subsection{Migrating User Accounts and Groups }
\label{sec:migrating-accounts}

\begin{enumerate}
\item Now migrate the accounts from your password and group files:
\begin{alltt}
$ \textbf{sudo ./migrate_passwd.pl /etc/passwd | tee \(\sim\)/accounts.ldif}
$ \textbf{./migrate_group.pl /etc/group | tee -a \(\sim\)/accounts.ldif}
\end{alltt}

\item Carefully examine the resulting \LDIF.

\item Add your \LDIF to the the \LDAP server:
\begin{alltt}
$ \textbf{ldapadd -x -h localhost -D "cn=Manager,dc=abc,dc=com" -W -f \(\sim\)/accounts.ldif}
\end{alltt}%$

\item If you see any error messages, correct the errors in either the
  \texttt{slapd.conf} file or your \LDIF input.
  
\item Verify that your directory has entries for all the users and
  groups in your \LDIF file:
\begin{alltt}
$ \textbf{ldapsearch -x -h 'localhost' -b 'dc=abc,dc=com'}
\end{alltt}%$
You should see all the data from your \LDIF file (except for the
passwords) displayed on the output


%% \item Download the \LDIF file from this location:
%%   \url{http://nicku.org/snm/lab/ldap-schemas/base.ldif}
%%   and examine its contents.

%% \item Add this \LDIF to your directory:
%% \begin{verbatim}
%% ldapadd -x -h localhost -D "cn=Manager,dc=my-domain,dc=com" -W -f base.ldif
%% \end{verbatim}
%% and type the password \texttt{secret} when prompted.

%% \item Now search your directory to see the content:
%% \begin{verbatim}
%% ldapsearch -x -h localhost -b 'dc=my-domain,dc=com'
%% \end{verbatim}
%% If that shows a successful search, your directory server is now
%% functioning.  We can now add more entries to it and experiment with
%% schemas.
\end{enumerate}


\subsection{Suppose I Make a Big Mistake?}
\label{sec:how-delete-directory}

Suppose you find that you forgot to set the \texttt{LDAP\_\ldots}
environment variables by doing \texttt{source
  \(\sim\)/migrate-environment}, and the base is wrong?  Or suppose
you reversed the patch to the migration tools, and now your directory
is filled with system accounts?  How to undo all this?  You could
delete the entries online with the \texttt{ldapdelete} tool, but it will be
simpler to just delete the directory and recreate it.  Here is how you
could do that:
\begin{alltt}
$ \textbf{sudo service ldap stop}
$ \textbf{sudo rm -rf /var/lib/ldap}
$ \textbf{sudo mkdir /var/lib/ldap}
$ \textbf{sudo chmod 700 /var/lib/ldap}
$ \textbf{sudo chown ldap.ldap /var/lib/ldap}
\end{alltt}%$
Then just run the migration tools again.  You may also find the
\texttt{-c} option to \texttt{slapadd} useful.


\subsection{Adding Entries Offline with slapadd}
\label{sec:using-slapadd}

For a large number of entries (more than 2,000), \texttt{ldapadd} can
be a bit slow.  You may wish to use the offline tool,
\texttt{slapadd}.  It is quite easy to use, although the error
messages are sometimes a little harder to understand than when using
\texttt{ldapadd}.  \texttt{ldapadd} also performs more thorough schema
checking, since the checking is done by the directory server itself.
Here we add the accounts offline, using \texttt{slapadd}:
\begin{enumerate}
\item Stop the directory server.
\begin{alltt}
$ \textbf{sudo service ldap stop}
\end{alltt}%$

\item Add your \LDIF to the the \LDAP database:
\begin{alltt}
\textbf{sudo -u ldap slapadd -v -l \(\sim\)/accounts.ldif}
\end{alltt}

\item If you see any error messages, correct the errors in either the
  \texttt{slapd.conf} file or your \LDIF input.
  
\item Restart the server:
\begin{alltt}
$ \textbf{service ldap start}
\end{alltt}%$
\end{enumerate}

%% \subsection{Migrating Online With \texttt{ldapadd}}
%% \label{sec:ldapadd}

%% The error messages from \texttt{slapadd} are sometimes not very
%% helpful.  It is sometimes better to have the \LDAP server running, and
%% then add the directory entries online with the tool \texttt{ldapadd}.
%% Here is how to use it to add the entries in
%% $\sim$\texttt{/accounts.ldif}:
%% \begin{alltt}
%% $ \textbf{ldapadd -x -h localhost -D "cn=Manager,dc=abc,dc=com" -W -f \(\sim\)/accounts.ldif}
%% \end{alltt}%$
%% Then when prompted, type the password you added to for the directory
%% administrator.  In fact, this is better than using \texttt{slapadd},
%% since the directory can do proper schema checking.

\section{Adding support for Automounting Home Directories}
\label{sec:autofs}

\begin{enumerate}
\item Select a machine to serve home directories via \NFS (and
  \texttt{samba})

\item Create the required home directories for all users who will have
  their home directories on the network

\item Create two files to migrate into the directory:
  \texttt{auto.master} and \texttt{auto.home}
\begin{alltt}
$ \textbf{cat /etc/auto.master}
# Sample auto.master file
# Format of this file:
# mountpoint map options
# For details of the format look at autofs(8).
# /misc /etc/auto.misc  --timeout=60
/.auto /etc/auto.auto --timeout=60
/home ldap:nicksbox.tyict.vtc.edu.hk:/etc/auto.home --timeout=60
$ \textbf{cat /etc/auto.home}
nicku -rw,hard,intr nicksbox.tyict.vtc.edu.hk:/home/nicku
pam -rw,hard,intr nicksbox.tyict.vtc.edu.hk:/home/pam
linus -rw,hard,intr nicksbox.tyict.vtc.edu.hk:/home/linus
nfs -ro,soft,intr nicksbox.tyict.vtc.edu.hk:/var/ftp/pub
mp3 -ro,soft,intr nicksbox.tyict.vtc.edu.hk:/mp3
\end{alltt}
Note that it is better to have only one space between each field,
otherwise the data will be a little bit harder to read in your
directory, as it would be \acro{MIME} encoded.
%% $ ldapsearch -x -b 'ou=auto.master,dc=tyict,dc=vtc,dc=edu,dc=hk'
%% version: 2
 
%% #
%% # filter: (objectclass=*)
%% # requesting: ALL
%% #
 
%% # auto.master, tyict, vtc, edu, hk
%% dn: ou=auto.master,dc=tyict,dc=vtc,dc=edu,dc=hk
%% ou: auto.master
%% objectClass: top
%% objectClass: automountMap
 
%% # /home, auto.master, tyict, vtc, edu, hk
%% dn: cn=/home,ou=auto.master,dc=tyict,dc=vtc,dc=edu,dc=hk
%% objectClass: automount
%% cn: /home
%% automountInformation: ldap:nicku.org:ou=auto.home,dc=tyict,dc=vt
%%  c,dc=edu,dc=hk --timeout 60
 
%% # /usr/remote, auto.master, tyict, vtc, edu, hk
%% dn: cn=/usr/remote,ou=auto.master,dc=tyict,dc=vtc,dc=edu,dc=hk
%% objectClass: automount
%% automountInformation: ldap:nicku.org:ou=auto.practical,dc=tyict,
%%  dc=vtc,dc=edu,dc=hk --timeout 60
%% cn: /usr/remote

% Can use a wildcard autofs entry.  Need check this.  Read the autofs
% source code, mailing lists.  Implement and check.
% Apr 23 16:06:15 ictlab slapd[16910]: conn=25266 op=2 SRCH base="ou=auto.home,dc=tyict,dc=vtc,dc=edu,dc=hk" scope=2 filter="(&(objectClass=automount)(cn=/))"

\item Migrate them into another \LDIF file:
\begin{alltt}
$ \textbf{./migrate_automount.pl /etc/auto.master | tee \(\sim\)/automount.ldif}
$ \textbf{./migrate_automount.pl /etc/auto.home | tee -a \(\sim\)/automount.ldif}
\end{alltt}

\item Export the \texttt{/home} directory to the machines that are
  authorised to access them by adding the appropriate line to the
  \texttt{/etc/exports} file:
\begin{verbatim}
/var/ftp/pub  192.168.0.0/24(ro)
/mp3          192.168.0.0/24(ro)
/home         192.168.0.0/24(rw)
\end{verbatim}
  
\item Use \texttt{ldapadd} to add the entries in your new \LDIF file,
  and check that you can read the entries with \texttt{ldapsearch -x},
  then verify that clients can get their home directory.
\end{enumerate}

\begin{thebibliography}{HSG99}
\bibitem[HSG99]{bib:HSG99} Timothy Howes and Mark C. Smith and Gordon
  S. Good, \emph{Understanding and Deploying LDAP Directory Services},
  Macmillan, 1999, ISBN: 1-57870-070-1 (Tsing Yi Library call number:
  TK 5105.595.H69 1999 )
\bibitem[Car03]{bib:Car03} Gerald Carter, \emph{LDAP System
    Administration}, O'Reilly, March 2003, ISBN: 1-56592-491-6
\bibitem[Don03]{bib:Don03} Clayton Donley, \emph{LDAP Programming,
    Management and Integration}, Manning, 2003, ISBN: 1-930110-40-5
\bibitem[HSG03]{bib:HSG03} Tim Howes, Timothy A. Howes, Mark C. Smith,
  Gordon S. Good, \emph{Understanding and Deploying LDAP Directory
    Services (2nd Edition)} ISBN: 0672323168, Addison Wesley
  Professional, May 2, 2003
\end{thebibliography}
\end{document}
