Deploying net authentication with Linux and Directory administrator
"Manuel Amador (Rudd-O)" <[email protected]> Wed, 31 Mar 2004 20:27:45 -0500
| Newsgroups | gmane.network.directoryadmin |
|---|---|
| Message-ID | <[email protected]> |
Hi fellow diradmin'ers, This is a small guide I wrote last night. Please test it, rip it apart, modify it and send me the modified files, so I can incorporate the shared knowledge into the document and have it published on one of the high-profile Linux sites. There are comments of mine that may be useful as well. I hope we can discuss the comments in this list, as much as possible. The document is attached. You'll find the style sheet attached as well and a java script file which autogenerates a table of contents. Feel free to use them in any way you want. But please do save them in the same dir as the html document. luck, -- Manuel Amador (Rudd-O) GPG key ID: 0xC1033CAD at keyserver.net
(unnamed)
(text/html, 25.3 KB)
<html>
<head>
<title>Deploying network authentication with Linux and Directory administrator</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style type="text/css">
<!--
.comment { color: green }
-->
</style>
<link href="manual.css" rel="stylesheet" type="text/css">
<script language="JavaScript" type="text/JavaScript" src="toc.js"></script>
</head>
<body>
<h1 id="title">Deploying network authentication with Linux and Directory administrator</h1>
<div id="toc"><h2>Table of contents</h2></div>
<p><author>Manuel Amador (Rudd-O)</author> <a href="mailto:[email protected]"><[email protected]></a></p>
<p class="comment">Some things are missing here. Schema check? Incompatible RFC2301bis? passwd-based password changing? SSL? Stuff! Please edit this file and submit the edited file back to the list or directly to me. I'll diff in changes and write your contribs down. Make sure you include yourself at the bottom in a credits fashion.</p>
<p>Hello there, fellow Linux fans! This time, we'll learn how to set
up a centralized authentication system for your corporate or home network, using Linux, OpenLDAP
and Directory administrator.</p>
<p>This is the Linux equivalent of using Windows 2000 as a domain
controller with Active Directory, only we'll be using secure open-source software with
no strings attached or license fees.</p>
<h2>Introduction</h2>
<p>No article will ever be complete with a simple introduction. Let's see what's a centralized authentication system. Let's also see what good it is for. Finally, we'll talk about its drawbacks.</p>
<h3>How does this work?</h3>
<p>The way it works is quite simple: a user attempts to log in on a computer,
and the computer contacts a directory server, passing the user's credentials
to it. The directory server responds telling the workstation if the user indeed
does have permission to log in, and the credentials are correct. Then the workstation
lets him log in, or not.</p>
<p>The directory server also keeps a user-to-UID mapping, which the workstations use to determine file ownership. The same applies to groups.</p>
<p>You could also do this by maintaining a single master copy of your <var>/etc/passwd</var>,
<var>/etc/group</var> and <var>/etc/shadow</var>, and copying it every once in a while to each computer
on your network, but imagine ten (or two thousand) workstations and you begin to understand
the benefits of using centralized authentication. You could also use NIS, and wait patiently until you see your network hacked.</p>
<h3>Well, what good is this stuff for?</h3>
<p>Simple:</p>
<ul><li>Ease of use: Your network users will have one single user name and password across all network computers</li>
<li>Security: Your passwords will only be stored in a single place, while server-to-workstation communications take place via SSL</li>
<li>Shared file systems: You can use this technique instead of <acronym title="Network Information System">NIS</acronym>, which is insecure, to provide a coherent user and group map, and enable <acronym title="Network File System">NFS</acronym> on your network computers</li>
<li>Centralized information system: SAMBA servers can use the LDAP directory to store passwords as well</li>
<li>More functionality: the directory server can serve as a shared address book for your company</li>
</ul>
<h3>What are the drawbacks?</h3>
<p>Well, for one, computers configured to use an LDAP server will not allow anyone to log in without a working network connection, because the directory server will not be available. In fact, if the network connection ever goes down (or if the directory server goes down), most user programs will halt until the network comes back up. More advanced techniques will let you set up redundant directory servers (a good thing), a fact that still won't help your laptop or dial-up users.</p>
<p>Another thing you might want to keep in mind: the <code>passwd</code> command will no longer work after following this instructions: you'll need to use Directory administrator to change passwords. Mind you, <code>passwd</code> <em>can be made to work</em>, but it needs PAM configuration and some extra work which Directory administrator is not prepared for. We will not explore PAM here, because this article is mainly intended to teach you, hands-on, how to deploy LDAP. Going further will require your investigation.</p>
<h2>What we need to do</h2>
<p>We have a very short checklist of things to do, which could take us 30 minutes
or less (add more if you have a lot of workstations to set up). We'll step into
the following items:</p>
<ul>
<li>Installing the required software on the server</li>
<li>Configuring the server software</li>
<li>Migrating existing users on the server</li>
<li>Tightening up security</li>
<li>Configuring the workstations</li>
</ul>
<p>Most of these tasks will require you to have administrator privileges at the server console (in other
words, being root). I'll let you know when it's not the case.</p>
<p>Choose a computer that will function as a server. Keep in mind that if this
computer fails, no one will be able to log in to the workstations (albeit system
services on them should not stop working).</p>
<h2>Installing the server software</h2>
<p>At this point, I'll make a small advocacy effort: do use your distribution's packages if you can. You can try compiling from source, but it'll take much longer.<p>
<p>For this step, we'll need a couple of software packages freely available on
the Internet:</p>
<ul>
<li><a href="http://www.openldap.org/">OpenLDAP</a></li>
<li><a href="http://www.padl.com/">PADL.com's MigrationTools</a></li>
<li><a href="htp://diradmin.open-it.org/">Directory administrator</a></li>
</ul>
<h3>OpenLDAP: the LDAP server</h3>
<p>OpenLDAP will serve as the user and group database for our network. The MigrationTools
will help us put our current users and groups into the database, and Directory
administrator will let us manage users, groups and address books after we've got everything
else ready.</p>
<p>Install the OpenLDAP packages on the server. Either use your distribution's provided packages or refer to the OpenLDAP administrator's guide. Heck, keep the administrator's guide handy at all times! I seriously recommend that you read it before attempting this (or after, but, well, that's up to you).</p>
<h3>PADL.com's MigrationTools</h3>
<p>Now
<a href="http://freshmeat.net/redir/migrationtools/6414/url_homepage/tools.html">download
the latest version of the MigrationTools</a>. Unpack the package on your home
directory of your server (/root), you'll be using it shortly.</p>
<h3>Ye olde Directory administrator</h3>
<p>Now <a href="http://diradmin.open-it.org/files.php">download the latest version
of Directory administrator</a>. Install it on your server: if you don't have the required
libraries (OpenLDAP libraries or GNOME libraries) it will prompt you when you're
attempting to install it (so long as you use binary packages).</p>
<p>In case you're wondering: Directory administrator has the ability to manage both UNIX and SAMBA users and passwords, without all the fuss of using separate tools. Changing a password really does change both the SAMBA and the UNIX passwords. We're not dealing with SAMBA-LDAP server configuration so I'll drop the subject for now.</p>
<p>Now we're ready to begin configuring the software</p>
<h2>Configuring the server software</h2>
<p class="comment">We diradminers need to write a wizard-based GUI tool to automatically configure OpenLDAP. This will boost DA audience tenfold. I do not know if it would be good to have the tool directly integrated into DA and have DA check whether he has write access to slapd.conf, or if we need a standalone tool, or a different EXE in the same RPM package. I think the tool should be written in python and a menu item in DA's Tools menu should invoke it. Python would make a GREAT devel environ for such a tool. Given the updated GNOME python bindings, it would also make a widely distributed platform.
The config tool would 1) configure the appropriate config directives DA needs 2) install object class files 3) configure access control *AND* let the user specify ACL rules in an ACL editor. Say the user reopens the LDAP config tool, and the LDAP server is already correctly configured for DA, it should only prompt the ACL editor, where an evolution-style rule editor should appear. Period. Another alternative is to make DA able to edit ACIs inside the directory server, and config slapd.conf to use ACIs.
</p><p class="comment">Whatever approach is taken, whatever the configger does, it should auto create the empty DIT tree for DA to use. Therefore, DA should look for empty DITs upon connecting and offer to run this tool if UNIX privileges allow (root user).</p>
<p class="comment">The DIT tree our configger creates should at the minimum allow for a separate location of machine trust accounts. That means DA should be able to create machine trust accoutns, which are NOT unix accounts at all. But they can be SAMBA accounts alright.</p><p class="comment">Idea: should the configger accept client certificates instead of passwords for the machine accounts? If this were the case, our migration assistant (see below) should generate a client certificate and upload it to the LDAP server.</p>
<p>From this point on, I will assume you installed all software using standard distribution packages (except, perhaps, for the MigrationTools). This means all file paths I mention will comply with the <acronym title="Filesystem Hierarchy Standard">FHS</acronym>. Source compilation fans beware.</p>
<h3>Configuring OpenLDAP</h3>
<p>At this point, you have OpenLDAP installed, but it's neither enabled nor set up to work.
We're going to tell it to prepare a user and group database and to keep it online.
First, with a text editor, open <var>/etc/openldap/slapd.conf</var>. This is the master
configuration file for the OpenLDAP server; in this file, comment lines begin
with a hash mark (#), and each line carries a setting.</p>
<ol>
<li>At the top of the file, add the following lines:
<pre>allow bind_v2
</pre>
This allows Directory administrator to hook up with OpenLDAP.
<p class="comment">Let's see who comes up with a patch to make this stupid config line unneeded forever and ever.</p>
</li>
<li>Look for the settings TLSCertificateFile and TLSCertificateKeyFile and uncomment
both of them (remove the hash mark and the spaces before the settings).</li>
<li>Look for a section that says <samp>ldbm database definitions</samp>; below that section lie the
settings for the user database.
<li>Choose an organization name, for example, Acme
Systems, and change the following user database settings to these values:
<pre><b>suffix "o=Acme Systems"
rootdn "cn=Manager,o=Acme Systems"</b>
<b>rootpw secret</b></pre>
(the o= means Acme Systems is the organization, and also it's the topmost item
of your database).
<p class="comment">Our openldap config tool should generate the certificate at some step of the wizard, and config it in the slapd.conf</p>
</li><li> Now open /etc/openldap/ldap.conf. Near the end of the file,
you'll see something like this:
<pre>HOST 127.0.0.1
BASE dc=example,dc=com </pre>
Change it to:
<pre>HOST 127.0.0.1
<b>BASE o=Acme Systems</b></pre><p class="comment">This is not a task for our openldap configger, bt for the Migration Assistant.</p></li>
<li>Change to the <var>/usr/share/ssl/certs</var> directory, and run the following commands to create the SSL certificate:<p class="comment">See above comment about certs.</p>
<pre>rm -f slapd.pem
make slapd.pem
chown root.ldap slapd.pem
chmod 640 slapd.pem</pre>
The last commands will ask you a couple of questions. Make sure you answer
correctly to all of them. On the host name question, type the correct host name of the server, and by that, I mean the host name all of your workstations will use to refer to this server. IP addresses or <samp>localhost</samp> will definitely <em>not work</em>.</li>
<li>Finally, let's set up OpenLDAP to start as a service. Run:
<pre>/sbin/chkconfig --level 235 ldap on
/sbin/service ldap start</pre>
<p class="comment">Evidently our configger should perform this task</p>You should see OpenLDAP starting up with an OK message. The user database is
ready, and everyone can read everything from the directory, but only the Manager
(cn=Manager,o=Acme Systems) can modify its contents. We'll tighten up security
after we've migrated our users and groups into the directory.</li>
</ol>
<h2>Migrating users and groups</h2>
<p>Now that we have the user database ready, it's time to migrate the users into
the database.</p>
<p class="comment">We need another tool to do this, only I am confident this tool needs for sure to reuse code from DA. A Migration Assistant. That tool should open a wizard that automatically performs each of these steps below. It should also generate a client certificate and save it on thw workstation, create a machine account for the workstation on the server, and upload the certificate to its DIT entry. The migration assistant would prompt for a DN and password with write acess to the DIT.</p>
<ol><li>Change to the directory you unzipped the MigrationTools into.<br>When you
unpackaged it, a directory was created. Go into that directory.
</li><li> Open the file
<var>migrate_common.ph</var>; this is the configuration file for the MigrationTools.
</li><li> Search
for the setting <samp>DEFAULT_MAIL_DOMAIN</samp> and set it to your organization's e-mail
domain, for example:
<pre>$DEFAULT_MAIL_DOMAIN = "<b>acmesystems.com</b>"</pre></li><li>Now find <samp>DEFAULT_BASE</samp> and change it to:
<pre>$DEFAULT_BASE = "<b>o=Acme Systems</b>"</pre>
</li><li>Now find <samp>EXTENDED_SCHEMA</samp> and change it to:
<pre>$EXTENDED_SCHEMA = <b>1</b></pre>
</li><li>Now find <samp>DEFAULT_MAIL_HOST</samp> and change it to:
<pre>$DEFAULT_MAIL_HOST = <b>""</b></pre><p class="comment">DAs migration assistant would ask for the same information, plus 'Create samba accounts?' crap, in the first migration page...</p>
</li><li>That's it. Save the file, we're about to create the topmost entry in our directory.
</li><li>Create a new file and place the following lines into it:
<pre>dn: o=Acme Systems
objectClass: top
objectClass: OpenLDAPRootDSE
objectClass: organization
o: Acme Systems</pre>
This file contains the data for the first entry in your directory.
</li><li> Save this file
as <var>entries.ldif</var> and run the following commands:
<pre>echo >> entries.ldif
./migrate_base.pl >> entries.ldif
./migrate_passwd.pl /etc/passwd >> entries.ldif
./migrate_group.pl /etc/group >> entries.ldif
ldapadd -D "cn=Manager, o=Acme Systems" -w secret -x < entries.ldif
</pre>
These commands create the topmost entry in your directory and migrate all user
and group data into it. You will be prompted for some information: accept the
defaults with the <kbd>ENTER</kbd> key, except for the password and server, where you should
type <var>secret</var> and <var>localhost</var>, respectively. After that, you should
see a lot of messages scrolling, but no error messages.</li><p class="comment">...then create all of the accounts, first groups, then users, then adding users to their secondary groups...</p></ol>
<p>Okay, it's time to test it.</p>
<h3>Testing the migration</h3>
<p>Run the following command:</p>
<pre>ldapsearch "*" -x
</pre>
<p>A lot of information should scroll past your screen, containing the user and
group records. If the command spouted an error message instead, note the error message and read the OpenLDAP
manual pages for more information.</p>
<p>Now, let's see the users and groups on your directory visually.</p>
<ol><li>
Fire up Directory administrator (it should be located under the System configuration
group in your Programs menu). Directory administrator will welcome you with
a first-time run wizard.</li>
<li>Let the wizard know that you want to connect to server
<var>localhost</var>, using <var>cn=Manager, o=Acme Systems</var> as your username,
<var>secret</var> as your password, and <var>o=Acme Systems</var> as the search base. </li><li>
Once the wizard is finished, press the Connect button. You should see
all of your migrated users and groups at once.</li></ol>
<h2>Tightening up security</h2>
<p>Up to here, we've been using the user <var>cn=Manager, o=Acme Systems</var>, and
<var>secret</var> as the password. Now, this is not very secure. We need to define
a user that will have administrative access to the entire directory, as well
as computer accounts for each workstation.</p>
<ol>
<li>Pick one of the migrated groups as your administrator group, and note its DN.<br>You can quickly check the DN by double-clicking the corresponding group icon on Directory administrator and writing the DN field down.
</li><li>Open <var>/etc/openldap/slapd.conf</var> and scroll down to the user database section.</li>
<li>Add the following security directives below the database section:
<pre>access to dn.subtree="o=Acme Systems"
by group "<full DN of the group you took note of>" write
access to attrs=userPassword
by * auth
access to attrs=*
by users read
by * read
defaultaccess none
</pre>
Thus, all group members of the group you chose will have full administrative privileges, while the rest can only read the database, except for the passwords.
</li><li>While you're still connected with Directory administrator as <var>cn=Manager, o=Acme Systems</var>, open the (now fully administrative) group, and add yourself to the member list.</li>
<li>Disconnect the Directory administrator with the Connect button, and restart the OpenLDAP server with the command:
<pre>/sbin/service ldap restart
</pre>
</li><li>In Directory administrator, go to the Preferences menu. Edit your connection profile, changing the User name from <var>cn=Manager, o=Acme Systems</var> to just your UNIX user name (e.g. <samp>peterv</samp>), and the Password to your UNIX password</li>
<li>Reconnect using the Connect button, and try changing some users. If you can change users without errors, your UNIX user is now an LDAP database administrator. Create another profile with a test username and password, and test that you <em>cannot change</em> anything in the database.</li>
<li>Reopen the <var>slapd.conf</var> file, and comment out or remove both the <samp>rootdn</samp> and <samp>rootpw</samp> configuration directives you created before. After doing this, restart OpenLDAP again, and verify you can no longer connect using the <var>cn=Manager, o=Acme Systems</var> DN and <var>secret</var> password.</li>
</ol>
<p>That's it. Your server is now configured and ready to go.</p>
<h2>Configuring the workstations</h2>
<p>Fortunately for you, the only step left to do is configuration in the workstations. While we're on the topic: there's one single hurdle you must overcome: the workstations must either not have any regular users on them, or these users must be migrated to the LDAP server.</p>
<h3>Workstations with existing users</h3>
<p>If any of the workstations already have users, you will need to watch out for conflicting user and group ID numbers. The following process can be used to manually migrate these users and groups into the LDAP server:
</p>
<p class="comment">...then executing these commands on the workstation that is being migrated. That should give excellent results</p><ul>
<li>For each workstation group you wish to migrate, use Directory administrator to create a new LDAP group if it doesn't already exist. Write the LDAP group's GID number down. Run the following command on the workstation:
<pre>cd / && find -print0 -group <group name> | xargs -0 chgrp <LDAP group's GID>
</pre>This should provide a quick migration route, by changing each file owned by that group to the new group ID (which doesn't exist on the workstation yet, but that doesn't matter).</li>
<li>For each workstation user you wish to migrate:<ul><li>Create a new LDAP user if it doesn't already exist, selecting the same primary group as on the workstation (this info can be gleaned using the <code>id -a <user name></code> command).</li><li>Add the user to each secondary group that he belongs.</li><li>Write the LDAP user's UID number down. </li><li>Run the following command on the workstation:
<pre>cd / && find -print0 -user <user name> | xargs -0 chown <LDAP user's UID>
</pre>which will assign each file owned by the old user to the LDAP user.</li>
</ul></ul>
<h3>Configuring the workstation for LDAP access</h3>
<ul><li>Open /etc/openldap/ldap.conf. Near the end of the file,
you'll see something like this:
<pre>HOST 127.0.0.1
BASE dc=example,dc=com </pre>
Change it to:
<pre>HOST <your LDAP server host>
<b>BASE o=Acme Systems</b></pre></li></ul><p>Everything is ready for the switchover. Keep in mind that faulty network access or a misstep in one of the commands above can make your workstation bootable only in maintenance mode.</p><p>Red Hat, Mandrake and Fedora users can use the <code>authconfig</code> command. Simply run the command, and in the dialog that appears, select <samp>LDAP authentication</samp>, specifying the LDAP server's host name, the base DN (which should be <var>o=Acme Systems</var>) and checking the SSL check box. You do not need to specify an LDAP user name or password: depending upon which user logs in, the system reuses the user and password for access to the LDAP server.</p>
<h2>Conclusions and ideas</h2>
<p>Of course, more restrictive configurations are possible. You can imagine an scenario where only authenticated users would have read access to the database. This is easily achievable by deploying a different OpenLDAP access control policy:</p>
<pre>access to dn.subtree="o=Acme Systems"
by group "<full DN of the group you took note of>" write
access to attrs=userPassword
by * auth
access to attrs=*
by users read
defaultaccess none
</pre>
<p>...then creating one account per machine, and using that account's DN and password in the respective workstation's configuration. See the <a href="man:slapd.access">slapd.access manual page on your system</a>.<p>
<p>Other possible scenarios would include restricting information available to users, based on a much more restrictive access control directive set, and creating machine trust accounts that let each machine's <code>passwd</code> command write to the LDAP database.</p>
<p>You can also integrate this with Kerberos, and make Kerberos the password database. This would provide unified single sign-on authentication, and reduce the times your users need to type passwords.</p>
<h3>Conclusion</h3>
<p>This has been a fruitful experience for me. Writing this down has forced me to develop a standard, documented procedure for doing this technique. I hope this article helps you in deploying more advanced services using Linux as a platform.</p>
<p>This also got me thinking: what if LDAP server authentication was the <em>default</em> scenario on each Linux computer? Mac OS X does exactly that: one locally-running, locally-listening LDAP server on each computer. Linux distributors should start phasing <var>passwd</var>-based authentication out, and deploying this as well. Sure, it would require an LDAP server by default. Corporate networks have to install them anyhow, while home users wouldn't be impacted. Mac OS X users aren't complaining.</p>
<p>Linux could stand to gain much more than just modern user authentication. The LDAP database could serve as the de-facto read-only computer address book. It could also store much more information, very much like Active Directory. SAMBA server deployments would be much, much easier, because SAMBA could be preconfigured to use the LDAP directory, saving a <em>lot</em> of time. If there's a single reason to deploy LDAP directories in Linux, SAMBA would be it.</p>
<p>Trust me, it can be done. All the required software is mature enough. Please contact your favorite distributor and request it. If people do, it won't take long.</p>
<p class="comment">In summary: a DIT setup tool which configures openldap and creates an empty DIT, which out to be thought out for more than just unix accounts (think samba, think machine and application service accounts, think apache), and lets the user edit Access controls. Also, a migration tool that auto migrates workstation's user accounts and reassigns file ownerships. These things are much more sorely needed than, e.g. tree views in DA, or template users. These are the things that would launch DA into a consulting market, making all of you money =). Think PADL.com.</p>
<h2>About the author</h2>
<blockquote>Manuel Amador (also known as Rudd-O) is an overworked individual which created and now partakes in the Directory administrator project. He's 24 years old, and has seven years of Linux and OSS-related expertise. He's also scrambling to find time between the completion of his Engineering degree and <a href="http://www.amautacorp.com/">Amauta</a>, the software and service company. Contact him for professional services in the OSS field.</blockquote>
<h2>References</h2>
<ul>
<li>OpenLDAP administrator's guide</li>
<li>OpenLDAP man pages</li>
<li>RFC 2307: An Approach for Using LDAP as a Network Information Service</li>
</ul>
<script language="JavaScript" type="text/javascript">
<!--
document.getElementById('toc').appendChild(generateTOC(3));
-->
</script>
</body>
</html>
toc.js
(text/x-javascript, 3.8 KB)
// Generate Table of contents from HTML document, and insert it there // Manuel Amador (Rudd-O) <[email protected]> // This program is under the GNU GPL, version 2 var trackTime; function profile() { trackTime = new Date(); } function unprofile() { alert("Profiling ended. Time: " + (new Date() - trackTime)); trackTime = null; } function inspect(element) { var props = ''; for (var n in element) props = props + n + '; '; alert (props);//+ ': ' + element[n] //alert (element['compareDocumentPosition']); } function getAsText(element) { var text = ''; for (var i = element.firstChild; i; i = i.nextSibling) { if (i.nodeType == 3) { if (i.nodeValue.replace(' ','') != "") { text = text + i.nodeValue; } } else if (i.childNodes) { text = text + getAsText(i); } } return text; } var headingLevel = new Array(); headingLevel['H1'] = 1; headingLevel['H2'] = 2; headingLevel['H3'] = 3; headingLevel['H4'] = 4; headingLevel['H5'] = 5; headingLevel['H6'] = 6; function findHeadings(node,maxDepth,headingsFound) { if (!headingsFound) headingsFound = new Array(); for (var i = node.firstChild; i; i = i.nextSibling) { if (i.nodeType == 1) { findHeadings(i,maxDepth,headingsFound); } } if (headingLevel[node.nodeName] <= maxDepth) { headingsFound.push(node); } return headingsFound; } function generateTOC(maxDepth) { if (maxDepth < 1 || maxDepth > 6) return null; var toc = document.createElement('div'); var currentNestingLevel = 0; var currentHeadingLevel = 0; var headingsFound = findHeadings(document.getElementsByTagName('body')[0],maxDepth); for (i = 0 ; i < headingsFound.length; i++) { currentHeadingLevel = headingLevel[headingsFound[i].nodeName]; while (currentNestingLevel != currentHeadingLevel) { if (currentHeadingLevel > currentNestingLevel) { var newList = document.createElement('ol'); var newListElement = document.createElement('li'); newList.appendChild(newListElement); toc.appendChild(newList); toc = newListElement; currentNestingLevel++; } else if (currentHeadingLevel < currentNestingLevel) { toc = toc.parentNode.parentNode; currentNestingLevel--; } } if (toc.childNodes.length > 0) { newListElement = document.createElement('li'); toc.parentNode.appendChild(newListElement); toc = newListElement; } var hyperlink = document.createElement('a'); for (j = headingsFound[i].firstChild; j; j = j.nextSibling) { hyperlink.appendChild(j.cloneNode(headingsFound[i])); } if (!headingsFound[i].id) { re = /\s/g; headingsFound[i].id = 'toc-' + getAsText(headingsFound[i]).toLowerCase().replace(re,'-'); } hyperlink.href = '#' + headingsFound[i].id; toc.appendChild(hyperlink); } while (toc.parentNode) toc = toc.parentNode; return toc; } // to insert an autogenerated Table Of Contents in your HTML document // you can use the function generateTOC(maximumHeadingLevel) // the following is an example of how to use this code // (this will insert the TOC at the end of the document body) // <script language="JavaScript" type="text/javascript"> // <!-- // document.getElementByTagName('body')[0].appendChild(generateTOC(3)); // --> // </script> // ignore what follows down here /* function insertTOC(maxDepth,inNode,beforeNode,afterNode) { if (maxDepth < 1 || maxDepth > 9) return false; if (!inNode && !beforeNode && !afterNode) return; false var tocentries = generateTOC(maxDepth); if (!tocentries) return false; tocentries.id = 'toc-entries'; if (inNode) { inNode.appendChild(tocentries); } else if (beforeNode) { beforeNode.parentNode.insertBefore(tocentries,beforeNode); } else if (afterNode) { if (afterNode.nextSibling) afterNode.parentNode.insertBefore(tocentries,afterNode.nextSibling); else afterNode.parentNode.appendChild(tocentries); } return true; } */
manual.css
(text/css, 1.6 KB)
body {
font-family: Verdana, Arial, Helvetica, sans-serif;
margin: 0px; padding: 0px; background: white; color: black;
}
p, h1, h2, h3, div, pre {
margin-left: 2mm;
margin-right: 2mm;
}
h2 {
background: url(arrow-decoration.gif);
background-position: top left;
background-repeat: no-repeat;
padding-left: 4mm;
}
div.instructions {
background-color: #FFFF99;
margin: 4mm;
padding: 2mm; padding-left: 48px;
background-image: url(book.gif);
background-repeat: no-repeat;
background-position: 2mm left;
}
div.instructions li {
list-style-type: square;
list-style-image: none;
}
div.instructions p { margin: 0px; padding: 0px; font-style: italic;}
div.instructions p,ul, div.instructions ol { padding-top: 1mm; padding-bottom: 1mm;}
div.instructions ul,div.instructions ol { margin: 0px; padding-left: 5mm;}
.authorcomments {
color: #CC0000;
}
body { font-size: 75% }
ins{ color: green; text-decoration: none; border-bottom: 1px dashed green; }
del{color: gray}
#tocdisabled {
padding: 3mm;
border: 1px solid black;
}
a {
color: green;
}
#toc a {
color: brown;
}
#toc a *{
color: brown;
}
#toc del {
display: none;
}
#toc ol {
list-style-type: decimal;
}
#toc ol ol{
list-style-type: lower-alpha;
}
#toc ol ol ol{
list-style-type: lower-roman;
}
h1 {
font-size: 140%;
disabledborder-top: 1px solid black;
margin-top: 5mm;
}
h2 {
font-size: 120%;
}
h3 {
font-size: 100%;
}
h1, h2, h3 {
margin-bottom: 1mm;
}
p { margin-top: 1mm; margin-bottom: 1mm}
#title {
font-size: 200%; text-align: center;
font-weight: bold;
color: white;
background: #441100;
padding: 3mm; margin: 0px; margin-bottom: 5mm;
}
signature.asc
(application/pgp-signature, 189 B)
-----BEGIN PGP SIGNATURE----- Version: GnuPG v1.2.3 (GNU/Linux) iD8DBQBAa3ARWyznNMEDPK0RApwkAJ4qSJB95hMjsVCBGC30LFCDU4e++ACfT6OR ZZ/2uV0yHU9bS46QiT2WlNY= =GQbD -----END PGP SIGNATURE-----