grima repository

On getting my own server I had two purposes, firstly to get access to some online services (self-production) and to learn many aspects of server administration and configuration (Mail and LDAP by example are not so trivial).

Since i've learned a lot of this experience (I maintain a server for 2 years now), and sometimes in pain (LDAP I'll curse you to twenty-six generations), I thinked it would be usefull to publish a summary of such a configuration. Fristly to help me in my next migration and perhaps to help anyone who find this article.

The hardware

I had the chance to get an SUN Ultra 5 wich is a 64bit workstation. Emmbeded with a 360Mhz UltraSPARC-IIi and 320 meg of ram. Inside there was a 8.3Go IDE HDD, one floppy and one IDE CD-ROM.

Since it has only one Ethernet card and i'll need twice, i just plugged a PCI Ethernet card with a RealTek chipset.

Next, I unplugged the floppy disk, since nobody still actually use it (or so rarely that i could put it back for the occasion). The floppy was mounted onto a rack that perfectly fit two IDE hard drive, so here they goes. The first one, a 80go for the data and the second one 4Go for daily backup

Choosing the distro

My first choice was a Debian GNU/Linux 3.1 for SPARC. But i wasn't able to boot on the box with it, so i used a Ubuntu 6.06 LTS Server for Sparc wich is pretty the same. Note that I had to boot on rescue mode since normal mode wasn't working, after that everything goes well.

Note that to boot on cd-rom, i had to press Stop-A and then type boot cdrom. Then the box restarted and booted on the cd.

After installation, i modified /etc/fstab to use my HDs.

# home partition
/dev/hdc2   /home     ext3  defaults 0 0
# backup
/dev/hdd2  /mnt/hd    ext3  defaults 0 0

Configuring Network

On debian|ubuntu to configure your network you have to modify the /etc/network/interfaces file. Of course on another distribution you'll have to modify something else (example: on Slackware the file would be /etc/rc.d/rc.inet1). Here's mine configuration :

# The loopback network interface
auto lo eth0 eth1
iface lo inet loopback

iface eth1 inet static
        address 172.19.3.3
        network 172.19.3.0
        broadcast 172.19.3.255
        netmask 255.255.255.0
        gateway 172.19.3.1
        post-up /etc/init.d/firewall

iface eth0 inet static
        address 10.41.6.201
        network 10.41.6.0
        netmask 255.255.255.0

Of course this is a fairly personal configuration, it probably won't fit your needs. First interface (eth0) is connected to the LAN where the ADSL modem-router is connected. The second interface (eth1) is used to plug my laptop in direct connection (yeah i know, hub are cheap, but looking at my house this is the best solution). Of course i want my laptop be NATed. So I configured a firewall script, for NAT and for security (did i mention i'm a little paranoid ?), this is the meaning of the post-up /etc/init.d/firewall directive. It call the following script just after the last interface gets up :

$ cat /etc/init.d/firewall
#!/bin/sh
echo "Load rc.firewall rules set ..."

EXTIF="eth1"
INTIF="eth0"
echo "  External Interface:  $EXTIF"
echo "  Internal Interface:  $INTIF"
INTNET="10.41.6.0/24"

# Load the NAT module (this pulls in all the others).
modprobe iptable_nat

# clear everything
iptables --flush

# Set up the Ports for the main servers
# i don't use FTP anymore, SFTP or WebDAV is enough
#iptables -A INPUT -p tcp --dport 20 -j ACCEPT ## FTP - Data Transfer
#iptables -A INPUT -p udp --dport 20 -j ACCEPT ## FTP - Data Transfer
#iptables -A INPUT -p tcp --dport 21 -j ACCEPT ## FTP - Connection
iptables -A INPUT -p tcp --dport 2222 -j ACCEPT ## SSH
iptables -A INPUT -p tcp --dport 80 -j ACCEPT ## HTTP
iptables -A INPUT -p tcp --dport 636 -j ACCEPT ## LDAPS
iptables -A INPUT -p tcp --dport 993 -j ACCEPT ## IMAPS
#iptables -A INPUT -p tcp --dport 4662 -j ACCEPT ## eDonkey2000
#iptables -A INPUT -p udp --dport 4666 -j ACCEPT ## eDonkey2000
#iptables -A INPUT -p tcp --dport 6882 -j ACCEPT ## Bittorent
#iptables -A INPUT -p tcp --dport 6881 -j ACCEPT ## BT Tracker
iptables -A INPUT -p tcp --dport 4080 -s $INTNET -j ACCEPT ## Mldonkey control

# Xorg from laptop
iptables -A INPUT -s 10.41.6.206 -p tcp --dport 6000 -j ACCEPT


# In the NAT table (-t nat), Append a rule (-A) after routing
# (POSTROUTING) for all packets going out EXTIF which says to
# MASQUERADE the connection (-j MASQUERADE).
iptables -t nat -A POSTROUTING -o $EXTIF -j MASQUERADE
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -t nat -A POSTROUTING -s $INTNET -j MASQUERADE

# Turn on IP forwarding
echo 1 > /proc/sys/net/ipv4/ip_forward

# Drop everything else except from localhost
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -j DROP

Installing Packages

Next i just installed theses packages from apt-get to provide differents services:

And i had to retreive those packages from various SPARC repositery since Ubuntu does not support them :

And last but not least, i compiled myself dirsync since no SPARC version is aviable.

Configuring Services

Dynamic DNS

I use two dynamic DNS solution:

That's why i use updatedd. It support a large variety of dynamic dns solutions and is coded in pure C. To configure it, I just modified the file /etc/updatedd-wrapper.conf like this :

dyndns(active) {
        login           = dyndns_1;
        hostnames       = "grima.homelinux.net";
        ip-addr         = ipv4:`/usr/share/updatedd/ipserv.pl dyndns`;
        use-syslog      = yes;
}

ovh(active) {
        login           = ovh_1;
        hostnames       = "grima.be";
        ip-addr         = ipv4:`/usr/share/updatedd/ipserv.pl dyndns`;
        use-syslog      = yes;
}

Of course the login fields must be modified accordingly. Since my server is behind a ADSL-Modem/Router, I just added this line in the crontab :

00 * * * * updatedd-wrapper

It call the script each hours to update (if needed) the DNS. If your box is intended to directly make the PPP connection, just had the script in ip-up of /etc/ppp/.

Generating SSL Certificate

You'll perhaps need to generate self-signed SSL certificates for your server. I needed three, one for IMAPS, one for LDAPS and the last for HTTPS (of course you could use the same certificate for the three services).

I invite you to read the officiel OpenSSL documentation wich explain how to generate such self-signed certificate. But if you're in a hurry, I think this link will help you : http://www.tc.umn.edu/~brams006/selfsign.html.

Automatised Backup

Easy task with a tools like dirsync. Just mount the HDD dedicated to backup somewhere (i use /mnt/hd). You'll just have to configure your crontab :

 
30 0 * * * dirsync /var/lib/mysql/ /mnt/hd/mysql/ >/dev/null 2>&1
35 0 * * * dirsync /home/www/ /mnt/hd/www/ >/dev/null 2>&1
50 0 * * * dirsync /etc/ /mnt/hd/etc/ >/dev/null 2>&1

Of course you could save nearly anything that you want, just add directories (like /home, /var/lib/ldap/, etc.).

Remote Login : OpenSSH

Default installation is ready to use, I just tweaked the /etc/ssh/sshd_config a little bit :

Webserver: Apache 2 / MySQL / PHP5

Debian/Ubuntu has a very pretty packaging for Apache 2. It's a real piece of cake to configure it. First i added /etc/apache2/conf.d/admin with theses directives :

ServerAdmin thegrima@altern.org
AddDefaultCharset UTF-8
DocumentRoot "/home/www/"
ServerName grima.be

Then we must configure the virtualhost in /etc/apache2/sites-available (next just link this file to sites-enabled) :

NameVirtualHost *

<VirtualHost *>
        ServerAdmin thegrima@altern.org

        DocumentRoot /home/www
        ServerName grima.homelinux.net
        ServerAlias grima.be

        ErrorLog /var/log/apache2/error.log

        # Possible values include: debug, info, notice, warn, error, crit,
        # alert, emerg.
        LogLevel warn

        CustomLog /var/log/apache2/access.log combined
        ServerSignature Email
</VirtualHost>

Since I use this server only for me and not to host other people, I didn't tweaked much PHP and MySQL configuration. I just modified (in php.ini) short_open_tag to Off since it cause problems with XML and extension=mysql.so to load MySQL extension (disabled by default with PHP5).

For MySQL i just configured everything with phpMyAdmin.

Mails : Procmail / Fetchmail / ssmtp / BogoFilter

Here comes the big thing. Dealing with mail was the primary objective of my server, I needed to access them from many computer (mine, my laptop, dad, school, friends, etc.) always securely (I don't want my password goes clear on untrusted network).

So here's the configuration I use : Since every belgian ISP are filtering port 25, i cannot host myself an SMTP server. For historical reasons, my primary mails are on altern.org, a free pop3 hoster. So i had to get my mails on the server first, using fetchmail.

$ cat .fetchmailrc
poll altern.org with proto POP3
user XXX1 there with password YYY1 is grima here and wants mda "/usr/bin/procmail"
user XXX2 there with password YYY2 is grima here and wants mda "/usr/bin/procmail"

As you see, fetchmail connect to the remote server, get the mails and deliver them on local to procmail (grima is, of course, my local user). So here's the procmail configuration :

$ cat .procmailrc
DROPPRIVS=yes
LINEBUF=4096
VERBOSE=off
MAILDIR=$HOME/Mail/
FORMAIL=/usr/bin/formail
SENDMAIL=/usr/sbin/ssmtp
PMDIR=$HOME/.procmail
#LOGFILE=$PMDIR/log
#LOG="
#"

INCLUDERC=$PMDIR/rc.maillists
INCLUDERC=$PMDIR/mountyhall.maillists
INCLUDERC=$PMDIR/hex05.maillists
INCLUDERC=$PMDIR/akt.maillists
INCLUDERC=$PMDIR/unif.maillists
INCLUDERC=$PMDIR/spam.rc
:0:
*
$MAILDIR

Report to the procmail man page to get details of this configuration but basically, it include different filters for mailing-list and spam and the deliver to my local mailbox wich is ~/Mail. You may alos ask why i use ssmtp and not classical sendmail (or postfix). Since my server was not intended to directly receive mail, I just needed a little tiny program to send mail to other SMTP: Ssmtp has been conceived just that goal. And since sendmail has a long tradition of remote-root exploit, i prefer not using it.

Here is two included files for your learning :

$ cat .procmail/rc.maillists
:0:
* ^TOniouz@reseaucitoyen.be
$HOME/Mail/.INBOX.RC/

$ cat .procmail/spam.rc
## Silently drop asiatic language mails
UNREADABLE='[^?"]*big5|iso-2022-jp|ISO-2022-KR|euc-kr|gb2312|ks_c_5601-1987'
:0:
* 1^0 $ ^Subject:.*=\?($UNREADABLE)
* 1^0 $ ^Content-Type:.*charset="?($UNREADABLE)
$HOME/Mail/.Junk/

:0:
* ^Content-Type:.*multipart
* B ?? $ ^Content-Type:.*^?.*charset="?($UNREADABLE)
$HOME/Mail/.Junk/

:0:
* ^X-Spam-Status: Yes
$HOME/Mail/.Junk/

:0fw
| bogofilter -u -e -p

:0:
* ^X-Bogosity: Yes, tests=bogofilter
$HOME/Mail/.Junk/

:0:
* ^X-Bogosity:.Spam
$HOME/Mail/.Junk/

For details about this configuration, report to the bogofilter documentation. Also, don't forget that bogofilter must be trained before deployed like that. You must have a corpus of spam and non-spam mail to train it. Against, report to the bogofilter configuration if you're interested (otherwise just don't include it).

So the configuration to get our mails is nearly complete. I just added in crontab (crontab -e) this line to get my mails every ten minutes :

0,10,20,30,40,50 * * * * fetchmail >/dev/null 2>&1

Mail Server: Dovecot

After all this configuration, just to get your mails, you may think building a IMAP server will be an hard task. It is not ! I had just four directives in the default configuration file (/etc/dovecot/dovecot.conf) modified and all was running:

protocols = imaps
default_mail_env = maildir:~/Mail
ssl_cert_file = /etc/ssl/certs/imapd.pem
ssl_key_file = /etc/ssl/private/imapd.pem

First one is because i want ONLY IMAPS and not traditional unencrypted IMAP. Second one precise the place where to get mails for users. The two last are the SSL certificate to use. And that's all ! Restart dovecot (sudo /etc/init.d/dovecot restart) and you've got a working IMAPS server, bravo !

Directory Server: OpenLDAP

Deploying an LDAP server may not be as hard as it looks, but if you don't understand how works LDAP hierarchy you'll crunch your mind. So I invite to read this article on Wikipedia. Ok, ready ?

So the first step will be to write down a directory structure. I primary deployed LDAP to have a centralised address book compatible with most of the mail reader. I won't use it to authenticate user against a whole system but I need user authetification to configure access right to the address book. So here is the deal :

dc=grima, dc=be
|->cn=Manager
|->ou=People
|->ou=users

Manager is intended to be the super user of the LDAP (it could be putted in ou=users, but it's more easy to retain his full DN like that). People is the organisational unit to store the address book and users is the ou to store user credentials. Now we have decided a basic hierarchy, let's configure sldap :

$ cat /etc/ldap/slapd.conf

# Schema and objectClass definitions
include         /etc/ldap/schema/core.schema
include         /etc/ldap/schema/cosine.schema
include         /etc/ldap/schema/nis.schema
include         /etc/ldap/schema/inetorgperson.schema

# Schema check allows for forcing entries to
# match schemas for their objectClasses's
schemacheck     on

# Where the pid file is put. The init.d script
# will not stop the server if you change this.
pidfile         /var/run/slapd/slapd.pid

# List of arguments that were passed to the server
argsfile        /var/run/slapd.args

# Read slapd.conf(5) for possible values
loglevel        0

# Where the dynamically loaded modules are stored
modulepath      /usr/lib/ldap
moduleload      back_bdb

# SSL:
TLSCertificateFile      /etc/ssl/certs/ldap.pem
TLSCertificateKeyFile   /etc/ssl/private/ldap.pem

# Specific Backend Directives for bdb:
backend         bdb
checkpoint 512 30


# Specific Directives for database #1, of type bdb:
database        bdb

# The base of your directory in database #1
suffix          "dc=grima,dc=be"
rootdn          "cn=Manager,dc=grima,dc=be"
# Cleartext passwords, especially for the rootdn, should
# be avoid.  See slappasswd(8) and slapd.conf(5) for details.
# Use of strong authentication encouraged.
rootpw         secret

# Where the database file are physically stored for database #1
directory       "/var/lib/ldap"

# Indexing options for database #1
index           objectClass eq

# Save the time that the entry gets modified, for database #1
lastmod         on

#############
# ACCESS CONFIG
#############

# everyone could auth and chage his own password
# but no-one could read hash
access to attrs=userPassword
        by dn="cn=Manager,dc=grima,dc=be" write
        by anonymous auth
        by self write
        by * none

# allow users to write some stuff
access to attrs=loginShell,shadowLastChange
        by dn="cn=Manager,dc=grima,dc=be" write
        by self write
        by * read

# Address Book access
# user could read/write
access to dn.subtree="ou=People,dc=grima,dc=be"
       by users write
       by anonymous none

# Ensure read access to the base for things like
# supportedSASLMechanisms.
access to dn.base="dc=grima,dc=be" by * read

# The admin dn has full write access and users could read eveything
access to *
        by dn="cn=Manager,dc=grima,dc=be" write
        by users read
        by * none

As you could see, I activated SSL, included Schema, configured Manager Password, root DN and access rights. Next step is to configure on wich interface the deamon should be aviable. This is done at deamon startup and could be configured on Ubuntu/Debian on the file /etc/default/slapd. I just modified one line :

SLAPD_SERVICES="ldap://127.0.0.1/ ldaps:///"

This tell to slapd to be aviable with SSL to each interface and with clear-text connection only for localhost. I use this to prevent local software (especially PHP one) to use unnecessary encrypted channel and to ensure remote connection are encrypted.

Now that slapd is configured, just launch it :sudo /etc/init.d/slapd (re)start. If everything goes well it works, elsewhere try to run slapd -d 256 to have debug printed in syslog.

Next step is to import the initial structure in the LDAP. First create a file (by example: mylldap.ldif) :

dn: dc=grima,dc=be
objectclass: dcObject
objectclass: organization
o: grima.be
dc: grima

dn: cn=Manager,dc=grima,dc=be
objectclass: organizationalRole
cn: Manager

dn: ou=People,dc=grima,dc=be
ou: People
objectClass: top
objectClass: organizationalUnit

dn: ou=users,dc=grima,dc=be
ou: users
objectClass: top
objectClass: organizationalUnit

As you can see it's the reflect of our directory structure. You can import it in your LDAP with : ldapadd -x -D "cn=Manager,dc=grima,dc=be" -W -f myldap.ldif. And here it is, you've a functionnal LDAP system.

Of course for now it is pretty empty. You'll have to configure one (or more) common user and to fill in the addressbook. But you don't have to use such a rough interface, just install a phpLDAPadmin ! It will make your life a lot easier.

To manage Address Book online, I configured ConTagged a PHP WebApp wich use LDAP as backend. It's not perfect but working pretty well.