How to setup a Debian server on gandi.net

From ao2Wiki
Jump to: navigation, search

NOTE: I wrote this guide when gandi.net didn't offer Debian Lenny (5.0) images, now they do, so for a new server you want to skip the upgrade part of this document.

Server Setup

  • Create and Login with your Gandi.net handle, after login you could even request a trial if you are considering purchasing a VPS at Gandi.

Create a server

Go to the Manage->Servers panel and Create a Server:

  • Choose the numer of shares you want (2 in my case) and Submit
  • Select Installation Mode: Gandi MI (Manual Install) with Debian4 for me.
  • Configure your server choosing hostname, username and password
  • Wait for server creation, now in the server list you can click on server hostname to see the details (show IP address, edit configuration, get a reverse DNS entry)

Let's say that for documentation purposes (see RFC3330) your server is at 192.0.2.1 and its hostname is example.com.

Fix debian4 image

This is needed to make ssh public key authentication work properly:

  • Login to your server
    user@localhost:~$ ssh username@192.0.2.1
    and put the password you choose before.
  • Check /home permissions and fix them
    username@example.com:~$ ls -l /home
    total 4
    drwxrws--- 2 username username 4096 2009-03-17 10:07 username
    username@example.com:~$ chmod 0755 /home/username
    username@example.com:~$ ls -l /home
    total 4
    drwxr-xr-x 2 username username 4096 2009-03-17 10:07 username
    
    (inspired by this)

    You could even be more conservative and use mode 0711 but we are liberal :)

Use ssh public key authentication

Now you can upload your pubkey to the server (you'll need to create it if you don't have one already):

user@localhost:~$ ssh-copy-id -i ~/.ssh/id_rsa.pub username@192.0.2.1

You could add this key also to root@example.com, you choose.

Upgrade to latest stable debian release

At the time of writing the latest debian release is Debian 5.0 (Lenny).

  • Upgrade etch packages:
    username@example.com:~$ su -
    Password:
    root@example.com:~# aptitude
    
  • Upgrade to Lenny:
    1. Setup Lenny repositories in /etc/apt/sources.list as said here:
      # Old entries commented
      #deb http://mirrors.gandi.net/debian/ etch main contrib non-free
      #deb http://security.debian.org/debian-security etch/updates main contrib non-free
      deb http://mirrors.gandi.net/gandi/debian etch main
      
      deb     http://ftp.debian.org/debian/ lenny main contrib non-free
      deb-src http://ftp.debian.org/debian/ lenny main contrib non-free
      
      deb     http://security.debian.org/debian-security lenny/updates main contrib non-free
      deb-src http://security.debian.org/debian-security lenny/updates main contrib non-free
      
    2. Prepare for upgrade as suggested here:
      root@example.com:~# aptitude update
      root@example.com:~# mv /lib/tls/i686 /lib/tls/i686.old
      root@example.com:~# aptitude install libc6 libc6-xen
      root@example.com:~# aptitude install apt dpkg aptitude
      root@example.com:~# aptitude full-upgrade
      
    3. Do some cleanup, remove obsolete and unneeded packages, purge config for autoremoved packages, run deborphan, etc.

Stop and restart the server from gandi.net control panel, you now have your shiny Lenny VPS.

NOTE: If you get this warning on boot:

***************************************************************
***************************************************************
** WARNING: Currently emulating unsupported memory accesses  **
**          in /lib/tls glibc libraries. The emulation is    **
**          slow. To ensure full performance you should      **
**          install a 'xen-friendly' (nosegneg) version of   **
**          the library, or disable tls support by executing **
**          the following as root:                           **
**          mv /lib/tls /lib/tls.disabled                    **
** Offending process: init (pid=680)                         **
***************************************************************
***************************************************************

you can edit /etc/ld.so.conf.d/libc6-xen.conf and put this line:

hwcap 0 nosegneg

instead of the one present, and then run ldconfig.

This is a workaround, and we should revert it the day Gandi updates the kernel.

Basic Setup

IMPORTANT: Add your hostname to /etc/hosts, to make gethostbyname() work, a line like the one below is enough.

192.0.2.1   example.com

Add some handy packages like ntpdate deborphan, iproute, sysv-rc-conf, psmisc, rsync and screen, copy your dot-files to the server.

Disable inetd if you don't need it: update-rc.d -f openbsd-inetd remove && kill -9 `pidof inetd`

Run dpkg-reconfigure tzdata, and set UTC as timezone.

Securing the server

Some hardening, following the Securing Debian Howto

  • Iptables setup, this is the script I used to test the rules, a very simple one inspired from madduck, it can be made more explicit but it is ok to give the idea:
    #!/bin/bash
     
    # iptables script, generated from iptables-save file
    IPT='/sbin/iptables'
     
    # $ ip addr show dev eth0
    # 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN qlen 1000
    #     link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
    #     inet 192.0.2.1/24 brd 192.0.2.255 scope global eth0
    #        valid_lft forever preferred_lft forever
     
    MY_IFACE="eth0"
    MY_IP="192.0.2.1"
     
    $IPT -t raw -F
    $IPT -t raw -X
    $IPT -t mangle -F
    $IPT -t mangle -X
    $IPT -t nat -F
    $IPT -t nat -X
    $IPT -t filter -F
    $IPT -t filter -X
     
    $IPT -P INPUT ACCEPT
    $IPT -P FORWARD ACCEPT
    $IPT -P OUTPUT ACCEPT
    $IPT -N in-new
     
    # allow all loopback traffic
    $IPT -A INPUT -i lo -j ACCEPT
    $IPT -A OUTPUT -o lo -j ACCEPT
     
    # Ingress and egress filtering
    $IPT -A INPUT -i $MY_IFACE -s $MY_IP -j DROP
    $IPT -A OUTPUT -o $MY_IFACE -s ! $MY_IP -j DROP
     
    $IPT -A INPUT -i $MY_IFACE -s 0.0.0.0/8 -j DROP
    $IPT -A INPUT -i $MY_IFACE -s 127.0.0.0/8 -j DROP
    $IPT -A INPUT -i $MY_IFACE -s 10.0.0.0/8 -j DROP
    $IPT -A INPUT -i $MY_IFACE -s 172.16.0.0/12 -j DROP
    $IPT -A INPUT -i $MY_IFACE -s 192.168.0.0/16 -j DROP
    $IPT -A INPUT -i $MY_IFACE -s 224.0.0.0/3 -j DROP
     
     
    ### INPUT chain
     
    # allow all ICMP traffic
    $IPT -A INPUT -p icmp -j ACCEPT
     
    # packets belonging to an established connection or related to one can pass
    $IPT -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
    # packets that are out-of-sequence are silently dropped
    $IPT -A INPUT -m state --state INVALID -j DROP
     
    # new connections unknown to the kernel are handled in a separate chain
    $IPT -A INPUT -m state --state NEW -j in-new
     
    # pass SYN packets for SSH
    $IPT -A in-new -p tcp -m tcp --dport 22 --syn -j ACCEPT
     
    # pass SYN packets for HTTP
    $IPT -A in-new -p tcp -m tcp --dport 80 --syn -j ACCEPT
     
    # log everything else
    $IPT -A INPUT -m limit --limit 3/min --limit-burst 10 -j LOG --log-prefix "[INPUT]: "
     
    # Add a limit criterion to the REJECT rule and start DROPping instead if the limit is
    # excedded? This could save some bandwidth.
    $IPT -A INPUT -j REJECT
     
     
    ### OUTPUT chain
     
    # allow outgoing traffic, explicitly (despite chain policy)
    $IPT -A OUTPUT -j ACCEPT
     
     
    ### FORWARD chain
     
    # disallow forwarded traffic, explicitly (despite chain policy)
    $IPT -A FORWARD -j REJECT

    after you loaded the rules from the test script you can save them with:

    iptables-save > /etc/network/iptables.rules
    

    And add the command to reload them on boot in /etc/network/interfaces:

       pre-up iptables-restore < /etc/network/iptables.rules
    

    in your interface settings, you may also want to remove /etc/network/interfaces.dhcp since you don't need it and it will overwrite /etc/network/interfaces on boot.

  • We could even use ulog to log the firewall messages: Install ulogd
    aptitude install ulogd ulogd-mysql
    

    Config mysql:

    mysqladmin -u root -p CREATE ulogd
    mysql -u root -p ulogd < /usr/share/doc/ulogd-mysql/mysql.table
    mysql -u root -p
    mysql> USE ulogd;
    mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER
           ON ulogd.ulog
           TO 'ulogd_user'@'localhost' IDENTIFIED BY 'ulogd_passwd';
    mysql> flush privileges;
    mysql> quit
    

    Edit the config file to enable and configure the mysql output plugin if needed

    /etc/ulogd.conf
    

    Change the firewall script to log to ULOG target, all the lines above with LOG become like:

    $IPT -A INPUT -m limit --limit 3/min --limit-burst 10 -j ULOG --ulog-nlgroup 1 --ulog-prefix "[INPUT]: "

    And reboot ulogd:

    invoke-rc.d ulogd restart
    
  • Install debsecan
  • try also the harden package, it will install many tools for checking and improving the security of your system, but you should really sneak a glance at the Securing Debian Howto anyway.

Add a disk

Go to the Manage->Servers panel and Add a disk to your server:

  • Click on the Disk management tab
  • If you want to purchase more space click on Add quota
  • Click on Create a Disk
  • Chose a logical name (e.g. disk01), a size and a filesystem (the filesystem doesn't matter, we will repartition the disk anyway)
  • Wait that disk creation completes and attach the disk to a server
  • Disable the automounter: edit udev/rules.d/86-gandi.rules and comment the line.

Reformat the disk space

We will use LVM, following this guide so we can extend the layout in future if needed, our "physical" disk is /dev/xvdb.

aptitude install lvm2

create the partition table:

fdisk /dev/xvdb
n
p
1
t
8e

create physiscal volume:

pvcreate /dev/xvdb1

create a volume group called data:

vgcreate data /dev/xvdb1

display the volume group info:

vgdisplay 
  --- Volume group ---
  VG Name               data
  System ID             
  Format                lvm2
  Metadata Areas        1
  Metadata Sequence No  1
  VG Access             read/write
  VG Status             resizable
  MAX LV                0
  Cur LV                0
  Open LV               0
  Max PV                0
  Cur PV                1
  Act PV                1
  VG Size               10.00 GB
  PE Size               4.00 MB
  Total PE              2559
  Alloc PE / Size       0 / 0   
  Free  PE / Size       2559 / 10.00 GB
  VG UUID               3PwIMo-qXMx-l1SB-bXFX-8DWj-Ronu-N7BrIz

For this very first time we take all the space for our logical volume, so we use the size in extents, we will reduce or extend its size only when needed:

lvcreate --name home --extents 100%VG data

View our logical volume:

lvdisplay 
  --- Logical volume ---
  LV Name                /dev/data/home
  VG Name                data
  LV UUID                rgrcbQ-2eKj-w5tn-rxyA-RbDt-sGi1-egBHik
  LV Write Access        read/write
  LV Status              available
  # open                 0
  LV Size                10.00 GB
  Current LE             2559
  Segments               1
  Allocation             inherit
  Read ahead sectors     auto
  - currently set to     256
  Block device           254:0

Create the filesystem:

mkfs.ext3 /dev/data/home

copy our old home content to the new partition:

mkdir /tmp/home
mount /dev/data/home /tmp/home
cp -a /home/* /tmp/home/
umount /tmp/home

add our new home to /etc/fstab:

/dev/data/home   /home          ext3    rw,noatime        0    0

and restart the server from gandi.net Web interface.

Web server (with SSL? TBD)

Change user properties if needed, for instance I put web data in /home/www-data so I do:

mkdir /home/www-data
chown www-data:www-data /home/www-data
usermod -d /home/www-data www-data
usermod -s /bin/bash www-data

If needed change your DocumentRoot in /etc/apache2/sites-enabled/000-default.

Backups

Gandi.net service conditions basically say that about data backups you are on your own, so you want to take good care and make full backups of your server by yourself.

You can use a script like this on your local machine:

#!/bin/sh
 
# Make a full backup of a remote host with rsync when both the local and
# remote root users are disabled.
 
set -e
set -x
 
# We run rsync with sudo, but the local user who have access to the remote
# server could be different from local root. So run ssh as the user specified
# below in order to make it pick the key allowed to connect to the server.
LOCAL_SSH_USER=local_user_who_can_access_the_remote_host
 
# We require a remote user that is in sudoers (maybe in admin group), so we can
# fetch every file on the server.
REMOTE_SUDOER=remote_user_in_admin_group
 
REMOTEHOST=192.0.2.1
 
# Exclude patterns, relative to the remote path given on command line
EXCLUDE="--exclude '/proc' --exclude '/sys'"
 
# Des destination dir on local host
DESTDIR=/backups/root_example.com
 
 
# Script starts here.
 
# remember, we run the inner ssh as a local user
LOCAL_SSH_CMD="sudo -u $LOCAL_SSH_USER ssh"
 
REMOTEHOST="${REMOTE_SUDOER}@${REMOTEHOST}"
 
# Use sudo on the remote side as well :)
RSYNC="rsync -e \"$LOCAL_SSH_CMD\" --rsync-path=\"sudo rsync\""
 
# We run the rsync command as root also on the local host
RSYNC="sudo $RSYNC -avz -H --progress --delete $EXCLUDE --delete-excluded"
 
[ ! -d $DESTDIR ] && mkdir -p $DESTDIR
eval $RSYNC $REMOTEHOST:/ $DESTDIR