Thursday, December 22, 2011

How to Convert FLAC+CUE to MP3

Converting flac to mp3 package pre-requirements are the same as published in previous post (How to Convert APE+CUE to MP3). So I will add what is necessary. You will need flac package.
apt-get -y install flac
Here is a script that does the rest (file flac-mp3.sh):
#!/bin/sh

# Convert FLAC to MP3 VBR
flac -cd CDImage.flac | lame -h - -v \
    --preset cd CDImage.mp3

# Split file
mp3splt -a -d mp3 -c CDImage.flac.cue -o \
    @a/@b/@n-@a-@t CDImage.mp3
rm CDImage.mp3
Drop that file into a directory that has two files input CDImage.flac and CDImage.flac.cue. Run the script and in few minutes you will get a mp3 directory with your mp3 tracks.

Wednesday, December 21, 2011

How to Convert APE+CUE to MP3

There is no a single tool to convert a APE file to a number of mp3 tracks. We are going to convert ape file to wav first, than wav to mp3 (a single file) and finally cut a single mp3 file into several (per CUE file). We need several packages. Two of them (libmac2 monkey-audio) are from debian-multimedia, choose mirror from the following list. You need to install debian-multimedia-keyring package. In my case I have obtained it from here:
http://mirror.yandex.ru/debian-multimedia/pool/main/d/deb-multimedia-keyring/
Download debian-multimedia-keyring_2010.12.26_all.deb file (in your case the file can be newer) and install:
dpkg -i debian-multimedia-keyring_2010.12.26_all.deb
Once above is done we need to add debian-multimedia repository location to apt source list and update it:
echo "deb http://mirror.yandex.ru/debian-multimedia/ testing main non-free" \
    >> /etc/apt/sources.list
apt-get update
Install required packages:
apt-get -y install libmac2 monkeys-audio shntool \
    lame mp3splt
Here is a script that does the rest (file ape-mp3.sh):
#!/bin/sh

# Convert APE to WAV
shnconv -o wav CDImage.ape
# Convert WAV to MP3 VBR
lame -h -v --preset cd CDImage.wav CDImage.mp3
rm CDImage.wav
# Split file
mp3splt -a -d mp3 -c CDImage.ape.cue -o \
    @a/@b/@n-@a-@t CDImage.mp3
rm CDImage.mp3
Drop that file into a directory that has two files input CDImage.ape and CDImage.ape.cue. Run the script and in few minutes you will get a mp3 directory with your tracks.

Troubleshooting: mp3splt does not set ID3 tags

As of this writing Debian testing comes with mp3splt version 2.2.5-1. The problem has been fixed since version 2.3. So in order to install latest version you need to add the following to /etc/apt/sources.list:
echo "deb http://mp3splt.sourceforge.net/repository wheezy main" \
    >> /etc/apt/sources.list
apt-get update
Note that you need remove previously installed packages related to mp3splt:
apt-get remove libmp3splt-mp3 libmp3splt-ogg \
    libmp3splt0 mp3splt
Install latest:
apt-get install libmp3splt0-mp3 libmp3splt0-ogg \
    libmp3splt0 mp3splt

Wednesday, November 30, 2011

How to share network connection with iptables

While working in isolated environment you might need to share your machine internet connection with other computers or virtual machines (e.g. host only network in VirtualBox). Ensure you have iptables installed.
apt-get install iptables
There are two thing we need to do: let kernel know that it is permitted to forward network traffic.
echo "sysctl net.ipv4.ip_forward=1" >> \
    /etc/sysctl.d/ip_forward.conf
and apply masquerading for the interface that we what to share (eth0), add the following line to /etc/rc.local:
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
You have to restart your computer so the changes take place during the next system boot.

Monday, November 28, 2011

How to rewrite all to https in nginx

Here we are going redirect all http traffic to https with nginx. I suppose you already have nginx installed, if not have a look here. We will store SSL certificates in /etc/nginx/ssl directory.
cd /etc/nginx
mkdir ssl
openssl req -new -x509 -sha256 -days 9999 -nodes \
    -out ssl/cert.pem -keyout ssl/cert.key
chown -R www-data:www-data ssl
chmod -R 700 ssl
Here is nginx configuration:
upstream backend {
    server 127.0.0.1:8080;
}

server {
    listen  *:80;
    return 301 https://$host$request_uri;
    #if ( $scheme = "http" ) {
    #    rewrite  ^/(.*)$  https://$host/$1 permanent;
    #}
}

server {
    listen  *:443;

    ssl on;
    ssl_protocols TLSv1;
    ssl_certificate /etc/nginx/ssl/cert.pem;
    ssl_certificate_key /etc/nginx/ssl/cert.key;

    location / {
        proxy_pass http://backend;
    }
}
You have to reload nginx so the changes take place.

Saturday, November 26, 2011

Building Python EGGs in Batch

While working with different versions of python you deal with libraries that comes with source code and doesn't provide a compiled python egg file.

Method1: Offline

Here is a script that develops eggs per python version you have installed for all packages found in lib directory (you must create that directory at the same place where the script below and drop downloaded source distribution archives in).
#!/bin/sh

rm -rf eggs
mkdir eggs
for p in /usr/local/bin/python?.?
do
    echo -n $p
    rm -rf env/
    virtualenv --no-site-packages --python=$p -q env > /dev/null 2>/dev/null
    echo -n .
    env/bin/easy_install -i lib -U -O2 -z distribute > /dev/null 2>/dev/null
    for f in lib/*  # packages to build
    do
        echo -n .
        env/bin/easy_install -i lib -O2 -z $f > /dev/null 2>/dev/null
    done
    cp env/lib/python*.*/site-packages/*.egg eggs/ 2>/dev/null
    echo done
done
rm -rf env/
rm -f eggs/setuptools* eggs/distribute*
Run script with:
./sh make_eggs.sh
Python eggs are now in eggs directory.

Method2: Online

In this case the script relies on internet connection and downloads sources right from pypi.
#!/bin/sh

rm -rf eggs
mkdir eggs
for p in /usr/local/bin/python?.?
do
    echo -n $p
    rm -rf env/
    virtualenv --no-site-packages --python=$p -q env > /dev/null 2>/dev/null
    echo -n .
    env/bin/easy_install -U -O2 -z distribute > /dev/null 2>/dev/null
    for f in $@
    do
        echo -n .
        env/bin/easy_install -O2 -z $f > /dev/null 2>/dev/null
    done
    cp env/lib/python*.*/site-packages/*.egg eggs/ 2>/dev/null
    echo done
done
rm -rf env/
rm -f eggs/setuptools* eggs/distribute*
Run script by passing packages you want to build:
./sh make_eggs.sh pycrypto lxml
Python eggs are now in eggs directory.

Tuesday, November 22, 2011

Using Buildbot with Multiple Projects

The BuildBot is a system to automate the compile/test cycle required to validate code changes. Here we are going setup buildbot master and slaves with the following requirements:
  1. We have serveral projects: project1, project2, project3
  2. These projects live under Mercurial (e.g. hosted at bitbucket.org) with base url https://scm.dev.local/hg/ followed by project name (e.g. https://scm.dev.local/hg/project1)
  3. We should be able run at least 2 builds in parallel (this is where we can use buildbot slaves: linux-slave1 and linux-slave2)
  4. The build should trigger automatically if the changes detected every 30 minutes during working day.
  5. The builder should execute make all that ultimatelly does everything we need to verify integrity.
  6. The builder should be checked agains certain python versions: 2.4, 2.5, 2.6, 2.7 and 3.2

Monday, September 12, 2011

How to chroot SFTP (Secure File Transfer)

SFTP (SSH File Transfer Protocol) is a network protocol that provides file transfer functionality over reliable data stream. It has nothing related with old ftp protocol however it treated as a secure replacement. Here we are going to achieve the following:
  1. Service root directory: /srv/sftp
  2. Each user must have isolated sftp location, e.g. /srv/sftp/user1
  3. User top level directory include directories: files, archive
  4. User session is chrooted
  5. User is limited to sftp only, no shell, no ssh access
If you have ssh installed, you have sftp. Just in case:
apt-get -y install ssh

SSH Configuration for SFTP

You need to ensure the sftp subsystem is enabled in ssh. We are going to use internal-sftp implementation, that is in-process ssh subsystem (file /etc/ssh/sshd_config):
#Subsystem sftp /usr/lib/openssh/sftp-server
Subsystem sftp internal-sftp
Let use sftp group to identify users for sftp. Here is a matching rule for ssh, add it at the end of /etc/ssh/sshd_config file:
Match group sftp
    ChrootDirectory /srv/sftp/%u
    X11Forwarding no
    AllowTcpForwarding no
    MaxAuthTries 2
    ForceCommand internal-sftp
Restart ssh so the changes take place:
/etc/init.d/ssh restart

Users

Let create a security group for our sftp users:
groupadd sftp
Here is a script that does the rest (file sftp-add.sh).
#!/bin/bash

sftproot=/srv/sftp

genpasswd() {
    local l=$1
    [ "$l" == "" ] && l=20
    tr -dc A-Za-z0-9_ < /dev/urandom \
        | head -c ${l} | xargs
}

if [ -z $1 ]; then 
    echo "Usage $0 username"
    exit 1
fi

# 1. User is created with home directory set to /, 
# this is the directory sftp change once chroot.
# 2. User added to group sftp.
# 3. Do not create home directory.
# 4. User has no shell, ssh login impossible.
useradd -d / -G sftp -M -s /bin/false $1

echo "Auto generated password:"
genpasswd
passwd $1

mkdir -p $sftproot/$1/{files,archive}
# Chroot directory must be owned by root
chown root:$1 $sftproot/$1 
# User has read-only access
chmod -R 750 $sftproot/$1
# User owns everything below chroot directory
chown $1:$1 $sftproot/$1/*
Just invoke it this way:
./sftp-add.sh user1
Now you should be able use sftp.

Monday, September 5, 2011

How to Compile Python from Source

Here we are going compile python from source. I assume you have a clean installation of Debian testing. Here are few packages required for compilation.
apt-get -y install build-essential zlib1g-dev libbz2-dev \
    libncurses5-dev libreadline-gplv2-dev libsqlite3-dev \
    libssl-dev libgdbm-dev
Once above installation is complete, download python source code from here: http://www.python.org/ftp/python/. Suppose you choose to download python 2.5.2.
cd /usr/local/src
wget http://www.python.org/ftp/python/2.5.2/Python-2.5.2.tar.bz2
tar xjf Python-2.5.2.tar.bz2
cd Python-2.5.2
Since most of libraries in Debian moved from /usr/lib to /usr/lib/i386-linux-gnu we need create symbolic links in old location so the build scripts can find them all. This is far easier than specify a valid library location for each case. Here are links:
ln -s /usr/lib/i386-linux-gnu/libssl.so \
    /usr/lib/libssl.so
ln -s /usr/lib/i386-linux-gnu/libcrypt.so \
    /usr/lib/libcrypt.so
ln -s /usr/lib/i386-linux-gnu/libcrypto.so  \
    /usr/lib/libcrypto.so
ln -s /usr/lib/i386-linux-gnu/libbz2.so  \
    /usr/lib/libbz2.so
ln -s /usr/lib/i386-linux-gnu/libgdbm.so  \
    /usr/lib/libgdbm.so
ln -s /usr/lib/i386-linux-gnu/libcurses.so  \
    /usr/lib/libcurses.so
ln -s /usr/lib/i386-linux-gnu/libz.so  \
    /usr/lib/libz.so
ln -s /usr/lib/i386-linux-gnu/libsqlite3.so  \
    /usr/lib/libsqlite3.so

Compilation

Before we start compile we need to configure it first. You can run it with all defaults (this will install python to /usr/local/).
./configure
Or you can specify some other location:
./configure --prefix=/usr/local
The configuration process take few seconds. Next issue make command to actually compile it (this may take few minutes). The -s option prints warning only and -j 2 utilizes 2 CPU cores during the compilation).
make -s -j 2
You can optionally test it before installing with:
make test
or run specific tests of your interest:
./python Lib/test/test_hashlib.py

Install

make install
Python executable should be located at /usr/local/bin/python2.5.

Extra Packages

While python is perfectly working at this moment you might need install some extra packages (e.g. virtualenv) with easy_install.
wget -O - -q http://python-distribute.org/distribute_setup.py | python2.5
easy_install-2.5 virtualenv
This way you can install as many python versions as you like.

Troubleshooting

While working with some third party package (e.g. django) you got the following error:
ImportError: ...undefined symbol: PyUnicodeUCS2_Replace
There reason is described here. You have to re-configure the python:
./configure --enable-unicode=ucs4
and build/install it again.

Tuesday, August 23, 2011

How to install XMPP (Jabber) IM server in Debian

XMPP is an open-standard communications protocol for message-oriented middleware based on XML (originally named Jabber). Prerequisites:
  • XMPP domain: dev.local
  • ejabberd server name: im1.dev.local
  • Administrative account: admin@dev.local
In order to install the ejabberd IM server in Debian:
apt-get -y install ejabberd
Add administrative account (user, host, password):
ejabberdctl register admin dev.local P@ssw0rd

Configuration

You will need a certificate file for your domain. While obtaining self signed certificate, please ensure:
  1. Common Name is the XMPP domain name, e.g. dev.local.
  2. Resulting pem file has both key and certificate
Take a look how to can create a self signed certificate here and add it to trusted certificates here. All configuration is stored in /etc/ejabberd/ejabberd.cfg file.
%% Admin user
{acl, admin, {user, "", "dev.local"}}.

%% Hostname (The list of domains we are going to serve)
{hosts, ["dev.local"]}.

...

%% domain_certfile: Specify a different certificate for 
%% each served hostname    .
{domain_certfile, "dev.local", "/etc/ejabberd/dev.local.pem"}.

...

%% To enable in-band registration, replace 'deny' with 
%% 'allow'. This let users create IM account from theirs
%% client applications.
{access, register, [{allow, all}]}.

XMPP DNS Discovery

You need to add the following records to your dns server:
$ORIGIN _tcp.dev.local.
$TTL 900    ; 15 minutes
_jabber         SRV 5 0 5269 im1.dev.local.
_xmpp-client    SRV 5 0 5222 im1.dev.local.
_xmpp-server    SRV 5 0 5269 im1.dev.local.
If you are editing zone of your dynamic dns server consider have a look here.

Tuesday, August 16, 2011

How to Install django on Debian using nginx uwsgi

Here we are going install a basic django application using uwsgi application server and nginx as load balancer. Requirements:
  • django code location: /usr/local/lib
  • django project: hello
Let install few packages we need:
apt-get -y install nginx uwsgi \
    uwsgi-plugin-python python-django
Create a simple django application:
cd /usr/local/lib
django-admin startproject hello

uWSGI

Create uWSGI configuration (file /etc/uwsgi/apps-available/hello.yaml):
uwsgi:
    uid: www-data
    gid: www-data
    socket: /tmp/uwsgi-hello.sock
    plugins: http, python
    module: django.core.handlers.wsgi:WSGIHandler()
    pythonpath: /usr/local/lib
    chdir: /usr/local/lib/hello
    env: DJANGO_SETTINGS_MODULE=hello.settings 
Enable this configuration and restart uwsgi server:
ln -s /etc/uwsgi/apps-available/hello.yaml \
    /etc/uwsgi/apps-enabled/hello.yaml
/etc/init.d/uwsgi restart

nginx

Create nginx site configuration (file /etc/nginx/sites-available/hello):
upstream backend {
    server unix:///tmp/uwsgi-hello.sock;
}

server {
    location / { 
        uwsgi_pass  backend;
        include     uwsgi_params;
    }   

    location /static/admin/ {
        alias /usr/lib/pymodules/python2.6/django/contrib/admin/media/;
        access_log off;
        expires 7d;
    }

    location  /static/ {
        alias  /usr/local/lib/hello/static/;
        access_log off;
        expires 7d;
    }
}
Remove default site, enable this configuration and restart nginx server:
rm /etc/nginx/sites-enabled/default
ln -s /etc/nginx/sites-available/hello \
    /etc/nginx/sites-enabled/hello
/etc/init.d/nginx restart

nginx - uwsgi_cache

Web sites that serve almost static content (updated let say every 15 mins) can benefit by utilizing content caching:
   ...
uwsgi_cache_path   /var/cache/hello levels=1:2
                   keys_zone=NAME:15m
                   inactive=5m;
server {
   location / {
       uwsgi_pass  backend;
       include     uwsgi_params;
       uwsgi_cache   NAME;
       uwsgi_cache_valid   200 302 15m;
       uwsgi_cache_key $request_uri;
       expires 15m;
   }
   ...
}
If you experience performance issue with content rendering, overhead of framework internal time, have a willing to fine control content caching consider take a look at the following post and this one.

Simple Load Balancing with nginx on Debian

Suppose you need distribute http workload across multiple computers/processes. Here we will be using nginx to implement load balancing (using round-robin scheduling). Let start by installing nginx package:
apt-get -y install nginx
Place the following into /etc/nginx/sites-available/my-site:
upstream backend {
    // every 7 requests: 5 requests to s1 server 
    // and one request to the second and the third
    server s1 weight=5;
    // after 3 unsuccessful attempts within 
    // the 30 seconds s2 is considered 
    // inoperative
    server unix:/tmp/s2 max_fails=3 fail_timeout=30s;
    server 127.0.0.1:8080;
}   
 
server {
    location / {
       proxy_pass http://backend;
    }   
}  
Now disable default site configuration, enable a new one and restart nginx:
rm /etc/nginx/sites-enabled/default
ln -s /etc/nginx/sites-available/my-site \
    /etc/nginx/sites-enabled/my-site
/etc/init.d/nginx restart
Read more about nginx upstream module here.

Monday, August 15, 2011

How to Serve Django Static Files by Apache

Serving static files out from python is not good idea since there is more effective way to do that. Why not let apache do this? Suppose you followed one of two previous posts that let you configure apache to server django application using mod_python or mod_wsgi. The idea is simple, we define an alias for the media directory of the django application and set it handler to none. Here is a partial example of apache site configuration file:
    ...

    Alias "/static/admin/" "/usr/lib/pymodules/python2.7/django/contrib/admin/media/"
    <Location "/static/admin/">
        SetHandler None
    </Location>

    Alias "/static/" "/usr/local/lib/hello/static/"
    <Location "/static/">
        SetHandler None
    </Location>
And finally let turn on static content expiration.
    ...

    <IfModule mod_expires.c>
        <FilesMatch "\.(jpg|gif|png|css|js)$">
            ExpiresActive on
            ExpiresDefault "access plus 7 days"
        </FilesMatch>
    </IfModule>
Enable expires module and restart apache2.
a2enmod expires
/etc/init.d/apache2 restart

How to Install Django on Debian using Apache mod_wsgi

Here we are going install a basic django application using apache2 mod_wsgi module. Requirements:
  • server dns: web01.dev.local
  • django code location: /usr/local/lib
  • django project: hello
Let install few packages we need:
apt-get -y install apache2 libapache2-mod-wsgi \
    python-django 
Once you ensure apache is up and running. Let create a simple django application:
cd /usr/local/lib
django-admin startproject hello
Create a /usr/local/lib/django-hello.wsgi file:
import sys
import os
import os.path

sys.path.append(os.path.dirname(__file__))
os.environ['DJANGO_SETTINGS_MODULE'] = 'hello.settings'

from django.core.handlers.wsgi import WSGIHandler
application = WSGIHandler()
Add apache site configuration that will serve the django wsgi site. Add the following to /etc/apache2/sites-available/django-hello:
<VirtualHost *:80>
        ServerName web01.dev.local
        DocumentRoot /var/www/

        ErrorLog ${APACHE_LOG_DIR}/error.log
        # Possible values include: debug, info, notice, 
        # warn, error, crit, alert, emerg.
        LogLevel warn
        CustomLog ${APACHE_LOG_DIR}/access.log combined
    
        WSGIScriptAlias / /usr/local/lib/django-hello.wsgi
        WSGIProcessGroup hello-site
        WSGIDaemonProcess hello-site processes=2 threads=16 maximum-requests=1000 display-name=apache-hello-wsgi
</VirtualHost>
Now we are going disable default apache site and enable django-hello site:
a2dissite default
a2ensite django-hello
/etc/init.d/apache2 restart
Once you navigate to http://web01.dev.local/ you should be able to see the default django welcome screen.

If you experience performance issue with content rendering, overhead of framework internal time, have a willing to fine control content caching consider take a look at the following post.

How to Install Django on Debian using Apache mod_python

Here we are going install a basic django application using apache2 mod_python module. Requirements:
  1. server dns: web01.dev.local
  2. django code location: /usr/local/lib
  3. django project: hello
Let install few packages we need:
apt-get -y install apache2 libapache2-mod-python \
    python-django 
Once you ensure apache is up and running. Let create a simple django application:
cd /usr/local/lib
django-admin startproject hello
Add apache site configuration that will serve the django site. Add the following to /etc/apache2/sites-available/django-hello:
<VirtualHost *:80>
        ServerName web01.dev.local
        DocumentRoot /var/www/

        ErrorLog ${APACHE_LOG_DIR}/error.log
        # Possible values include: debug, info, notice, 
        # warn, error, crit, alert, emerg.
        LogLevel warn
        CustomLog ${APACHE_LOG_DIR}/access.log combined
    
        SetHandler python-program
        PythonHandler django.core.handlers.modpython
        SetEnv DJANGO_SETTINGS_MODULE hello.settings
        PythonDebug On
        PythonPath "['/usr/local/lib'] + sys.path"
</VirtualHost>
Now we are going disable default apache site and enable django-hello site:
a2dissite default
a2ensite django-hello
/etc/init.d/apache2 restart
Once you navigate to http://web01.dev.local/ you should be able to see the default django welcome screen.

Troubleshooting

If you unable navigate to the apache this could be related to ipv6 support (that is turned on by default). You can verify that issuing the following command:
netstat -tunlp | grep :80
If the apache process is not listening on ipv4 address (e.g. 0.0.0.0:80) just follow the following post to disable ipv6 in apache.

Saturday, July 16, 2011

How to Install Bugzilla in Debian

Here we are going install bugzilla 3.6.3 in Debian testing (all in one host). By the time of this writing bugzilla package was not available in testing release and one from stable failed to run in testing release. The option is to use it from sid release.

Install

  1. Since we are going install everything on one box we need two essential components: a web server and database server, apache and mysql serve this purpose quite well (if you have mysql already somewhere in the network you need mysql-client package instead).
    apt-get -y install apache2 mysql-server
    
  2. Add sid repository to your apt sources list and update package list:
    echo "deb http://ftp.debian.org/debian sid main" \
        >> /etc/apt/sources.list
    apt-get update
    # You should be able to find bugzilla3 package now
    apt-cache search bugzilla3
    
  3. Install bugzilla:
    apt-get install bugzilla3
    
    Configure database for bugzilla3 with 
    dbconfig-common? Yes
    
  4. Comment out sid repository in apt sources file
  5. You can now navigate to your bugzilla home page by simply entering:
    http://bugzilla-server-name/bugzilla3
    

Serving from Web Server Root

Default installation of bugzilla install it into bugzilla3 virtual directory. You can change it to be server from root directory:
  1. Set urlbase in /etc/bugzilla3/params:
    'urlbase' => ''
    
    or even better (this url is the common initial leading part of all Bugzilla urls):
    'urlbase' => 'http://bugzilla-server-name/'
    
  2. Disable default site in apache:
    a2dissite default
    
  3. Ensure the following in /etc/apache2/conf.d/bugzilla3.conf:
    <VirtualHost *:80>
        #Alias /bugzilla3 /usr/share/bugzilla3/web
        DocumentRoot /usr/share/bugzilla3/web
        SetEnv X_BUGZILLA_WEBPATH "/" 
    
        ...
    
    </VirtualHost>
    
  4. Let apache know about the changes we made:
    /etc/init.d/apache2 reload
    

Database backup/restore

During installation of bugzilla it creates a new database with name bugzilla3.
  1. Here is a small script to backup database:
    # Backup bugzilla database
    /etc/init.d/apache2 stop
    mysqldump -p bugzilla3 > backup.sql
    /etc/init.d/apache2 start
    
  2. ... and restore.
    # Restore bugzilla database
    /etc/init.d/apache2 stop
    mysql -p bugzilla3 < backup.sql
    /etc/init.d/apache2 start
    
Read more about bugzilla here.

Monday, July 11, 2011

How to shrink qcow2 file

While working with kvm/qemu virtual environment you might encounter need to shrink image file after a removal of unnecessary files, etc. You will be surprised that the space you freed in guest virtual machine is not actually released in host file. It's size remain the same. Here you will know how to shrink it to minimum.

Windows Guest

The idea here is simple, there are few things you have to do:
  1. Delete all unnecessary files, empty recycle bin
  2. Defragment drive (you might need to do this several times, until you see it "compacted" well)
  3. Use sdelete to zero free disk space. Please note that this operation will cause that all drive free space will be filled by zero, so the virtual machine image will grow to the maximum size.
    sdelete -c c:
    

Linux/FreeBSD Guest

dd if=/dev/zero of=./zero bs=1M
sync
rm -f ./zero
Note, the bs parameter is important, since it greatly reduce time necessary to complete this task.

Host

Convert image to the same format that is currently is (e.g. qcow2 => qcow2)... during this procedure it will release unused space.
qemu-img convert -O qcow2 w2k3.qcow2 \
 w2k3-shrinked.qcow2
The process is time consuming and each phase greatly depends on physical disk IO performance and available free space.

Sunday, July 3, 2011

Debian Samba Server

Samba is the protocol by which a lot of PC-related machines share files and printers.
apt-get install samba

Temporary File Space

We will create a simple network share tmp. All users in WORKGROUP will have read-write access. Let create a directory tree (under /srv/smb/) we are going to serve.
mkdir -p /srv/smb/tmp
Place the following in /etc/samba/smb.conf (backup the original file first):
[global]
workgroup = WORKGROUP
server string = Public File Server

# When clients connect to a share level security server, 
# they need not log onto the server with a valid 
# username and password before attempting to connect to
# a shared resource. Instead, the clients send 
# authentication information (passwords) on a per-share
# basis, at the time they attempt to connect to that 
# share.
security = share

# This parameter determines whether or not smbclient(8) 
# and other samba client tools will attempt to 
# authenticate itself to servers using the weaker 
# LANMAN password hash.
client lanman auth = no

# This parameter determines whether or not smbd(8) will 
# attempt to authenticate users or permit password 
# changes using the LANMAN password hash.
lanman auth = no

[tmp]
# If this parameter is yes for a service, then no 
# password is required to connect to the service.
public = yes
comment = Temporary File Space (cleaned up at midnight)
path = /srv/smb/tmp
read only = no
hide files = lost+found
Now from Windows machine you can access this share as \\samba-server-name\tmp. Here is a cron job to make daily clean ups (file /etc/cron.d/clean-samba-tmp):
#
# Regular cron jobs for removing everything 
# in samba tmp share
#
SHELL=/bin/sh

# Run daily at 1:35 AM
# m h dom mon dow user  command
35 1 * * * root /bin/rm -rf /srv/smb/tmp/*
See more configuration options here.

Thursday, June 23, 2011

Performance Monitoring in Linux

There are few useful tools that can help find out a bottleneck of your Linux box performance.

What to monitor first?

The system load is a measure of the amount of work that a computer system performs. You can use this command to read system load:
uptime
Here is a sample output:
... load average: 1.07, 1.63, 2.81
The three values of load average refer to the past 1, 5, and 15 minutes of system operation. These numbers should be read this way: the number represents how well a single CPU can handle load, thus if the number is 1 or less - it is pretty comfortable (the 4-CPU system works well at load number 4 or less); 1.5 - means at least 50% of load is not handled on time, it is queued for processing and is a subject for attention.

System Monitoring

Real time monitoring can be observed with top and htop commands. Command htop gives you more convenient way of what top does. Particularly it is handy to add two more columns (via 'F2' Setup) related to IO read and IO write.
htop
Processors related statistics with mpstat:
watch -n 1 mpstat

Disk Monitoring

IO can be a one of possible bottleneck of system performance degradation. The tool iotop tracks disk I/O by process, and prints a summary report that is refreshed every second.
iotop
Statistic for IO devices and partitions can be monitored with iostat:
watch -n 1 iostat

Who is waiting and blocked?

It is useful to know how the system load goes across processes, however most interest is related to processes that keep waiting for the operation to complete, thus cause delays. Here is a simple command to get this kind of report every second:
watch -n 1 "(ps aux | awk '\$8 ~ /D/  { print \$0 }')"

Network Monitoring

Intensive network related operation can cause the high load as well. Here is a tool that let you have a better idea of your network traffic utilization - iftop:
iftop

Saturday, June 4, 2011

Troubleshooting: Could not update .ICEauthority file

Here is the error message that might encounter duing gnome desktop startup.
Could not update .ICEauthority file 
/var/lib/gdm3/.ICEauthority
I have resolved this problem by simply removing that file and let gdm3 daemon re-create it. The commands below must be executed with root privileges.
/etc/init.d/gdm3 stop
rm /var/lib/gdm3/.ICEauthority*
/etc/init.d/gdm3 start
Note, restarting gdm not helps all the time, so try reboot computer instead.

Wednesday, June 1, 2011

How to Backup and Restore OpenLDAP Database

Instead of backup / restore of actual ldap database (hdb, etc) we will export/import ldap directory tree into ldif format that ultimately let us do the same, however without any particular database implementation specifics.

Backup

The backup will be stored in backup.ldif text file.
#!/bin/sh

slapcat -v -l ldap.diff

Restore

The restore will go through replacing current database from a one we have in ldif backup.
#!/bin/sh

# Stop slapd daemon
/etc/init.d/slapd stop

# Remove current database
rm -rf /var/lib/ldap/*

# Import directory tree from backup
slapadd -l backup.ldif

# Fix permissions
chown -R openldap:openldap /var/lib/ldap/*

# Start slapd daemon
/etc/init.d/slapd start

Saturday, May 7, 2011

Debian Link Aggregation (Network Bonding)

Here we are going setup link aggregation for Debian. Your system must have at least 2 network interfaces (but not limited to).

Bonding

  1. Install ifenslave-2.6 package:
    apt-get install ifenslave-2.6
    
  2. Load bonding kernel module:
    modprobe bonding
    
  3. Note that you shouldn't have any configuration for eth0 and eth1, they are attached as slaves for bond0 interface. Network configuration (file /etc/network/interfaces):
    auto bond0
    iface bond0 inet static
         address 192.168.10.14
         netmask 255.255.255.0
         network 192.168.10.0
         broadcast 192.168.10.255
         gateway 192.168.10.1
    
         slaves eth0 eth1
    
         # Transmit packets in sequential order 
         # from the first available slave through 
         # the last. This mode provides load 
         # balancing and fault tolerance.
         bond-mode balance-rr
    
         # Only one slave in the bond is active. 
         # A different slave becomes active if, 
         # and only if, the active slave fails. 
         #This mode provides fault tolerance.
         #bond-mode active-backup
    
         bond-miimon 100
         bond-downdelay 200
         bond-updelay 200
    
  4. Once above is complete you need to reboot your server so the network changes take place (that should be faster than bringing up/down network interfaces and restarting networking service).

Bridge

You can use link aggregation with network bridge, for example if you are using lxc / kvm virtualization. Here is network configuration (file /etc/network/interfaces):
auto bond0
iface bond0 inet manual
     slaves eth0 eth1
     bond-mode balance-rr
     bond-miimon 100
     bond-downdelay 200
     bond-updelay 200

auto br0
iface br0 inet static
     address 192.168.10.14
     netmask 255.255.255.0
     network 192.168.10.0
     broadcast 192.168.10.255
     gateway 192.168.10.1
     bridge_ports bond0
     bridge_fd 0
     bridge_maxwait 0
     bridge_stp off

Network Switch

If your switch supports port trunking you should add the switch ports used by eth0 and eth1 to a single trunk.

Sunday, May 1, 2011

How to change default OS in grub2

Change the following line in file /etc/default/grub (the number corresponds to the grub2 boot menu item starting from zero):
GRUB_DEFAULT=0
Once you made your changes issue the following command:
update-grub
Once you reboot the default selected item will be changed per your changes above.

Saturday, April 16, 2011

How to Shutdown Windows Guest Gracefully in KVM

KVM uses ACPI to send shutdown event to the guest virtual machine. But it can't do that in case your windows settings prohibit shutdown when there is no user logged in, you have to change this settings. Here is how:
  1. Ensure the ACPI is enabled in your virtual machine settings.
  2. Login to your Windows guest and launch Group Policy Object Editor (gpedit.msc).
  3. Locate the following key and change the settings to enabled.
    Computer Configuration\Windows Settings\
    Security Settings\Local Policies\Security Options\
    Shutdown: Allow system to be shut down 
    without having to log on
    
  4. If you want to be able shutdown guest even there is a logged in user add the following to file ShutdownWarningDialogTimeout.reg and enter it into windows registry.
    Windows Registry Editor Version 5.00
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows]
    "ShutdownWarningDialogTimeout"=dword:00000001
    
Finally here is a simple command using virsh to shutdown guest:
virsh -c qemu:///system shutdown w2k3
Once the above settings is enabled you should be able gracefully shutdown your windows guests using virtual machine ACPI power management.

Thursday, April 14, 2011

Troubleshooting: Windows Guest on KVM

While migrating windows virtual machine from virtualbox to kvm you might experience an issue related to significant virtual hardware changes, in other words you get BSoD (bluescreen). Here are few findings that can help you:
  1. Windows 2003, BSoD. STOP: 0x0000007B. Here is a description. You need apply this before you convert image to kvm.
  2. Error message that computer does not have a parallel port: "The Parallel port driver service failed to start". Read more here.
  3. If your Windows host was configured in KVM configuration first choose IDE drive as disk bus, add SCSI controller. When guest starts let it scan for hardware changes, that might take few minutes, when it finds SCSI controller shutdown guest and change disk bus to use SCSI controller, you can remove IDE controller.
  4. Uninstall virtualbox guest tools, so windows doesn't complain (see system event log).
The above "fixes" should let you start your windows guest in kvm.

How to Convert VirtualBox VDI image to KVM

The idea is very simple. We convert virtualbox vdi image to raw format and than from raw format to kvm specific. I have chosen qcow2 format based on this.
  1. Convert img.vdi to raw format. Please note, that raw format means if your vdi image is configured for maximum size of 10 Gb than the resulting raw file will be 10 Gb. Make sure you have enough disk space.
    VBoxManage clonehd --format RAW img.vdi img.raw
    
  2. And now raw image to qcow2 format. Here the resulting file (img.qcow2) will be the same size as the original one (img.vdi).
    qemu-img convert -f raw -O qcow2 img.raw img.qcow2
    
  3. Delete raw file since you will not need it anymore.
You can attach resulting img.qcow2 file to the kvm virtual machine now.

Debian KVM

Kernel-based Virtual Machine (KVM) is a virtual machine implementation using the operating system's kernel (read more here). Here are few steps to install kvm in debian:

Server

  1. Setup SSH. Read more here.
  2. Setup bridge-utils package...
    apt-get install bridge-utils
    
    ... and configure network interface (restart computer so network changes take place):
    auto eth0
    iface eth0 inet manual
    
    auto br0
    iface br0 inet static
         address 192.168.10.11
         netmask 255.255.255.0
         network 192.168.10.0
         broadcast 192.168.10.255
         gateway 192.168.10.1
         bridge_ports eth0
         bridge_stp off
         # 1.
         bridge_fd 0
         bridge_maxwait 0
         # 2.
         #bridge_fd 9
         #bridge_hello 2
         #bridge_maxage 12
    
  3. Install qemu-kvm and libvirt-bin packages:
    apt-get -y install qemu-kvm libvirt-bin
    
  4. Add a user that will be managing kvm to group libvirt (e.g. user1):
    adduser user1 libvirt
    

Client

  1. Setup Password-less ssh login to kvm server. Read more here.
  2. Install virt-manager package:
    apt-get -y install virt-manager
    
  3. If your client is not going to host kvm virtual machines you can disable the following daemons:
    update-rc.d ebtables disable
    update-rc.d libvirt-bin disable
    update-rc.d libvirt-guests disable
    update-rc.d lvm2 disable
    
  4. Open Virtual Machine Manager from Applications > System Tools.
  5. In File menu select Add Connection. In dialog that appears ensure method ssh and user that you added on server to group libvirt).

Performance Tuning

  1. The KVM host can take benefit of KSM by finding and sharing memory blocks between vitual machines (add the following to /etc/rc.local).
    echo 100 > /sys/kernel/mm/ksm/sleep_millisecs
    echo 1 > /sys/kernel/mm/ksm/run
    
    You can take a look at pages sharing / shared:
    cat /sys/kernel/mm/ksm/pages_sharing
    cat /sys/kernel/mm/ksm/pages_shared
    
    Another useful thing is to use vhost-net kernel module to boost virtual machine network performance (ensure guest vm uses virtio network device).
    echo vhost-net >> /etc/modules
    
  2. The KVM linux guest IO performance can be improved by:
    • using virtio as disk bus
    • setting virtual disk performance options to: cache mode - none, IO mode - native
    • using noop IO scheduler for each guest (file /etc/default/grub):
    GRUB_CMDLINE_LINUX_DEFAULT="quiet elevator=noop"
    
    Update grub by issuing update-grub command.

Monday, April 4, 2011

How to chroot ntp in Debian

Here are few simple steps to chroot ntp in debian. Add the following to file /usr/local/sbin/chroot-ntp and execute (alternatively you can download script from bitbucket site here):
#!/bin/bash

/etc/init.d/ntp stop

rootfs=/var/chroot/ntp
mkdir -p $rootfs/{etc,var/lib/ntp,var/log}

mv /etc/ntp.conf $rootfs/etc
ln -s $rootfs/etc/ntp.conf /etc/ntp.conf

if [ -e /var/lib/ntp/ntp.drift ]; then
    mv /var/lib/ntp/ntp.drift $rootfs/var/lib/ntp
fi
ln -s $rootfs/var/lib/ntp/ntp.drift \
    /var/lib/ntp/ntp.drift
chown -R ntp:ntp $rootfs/var/lib/ntp

mv /var/log/ntpstats $rootfs/var/log
ln -s $rootfs/var/log/ntpstats /var/log/ntpstats
chown -R ntp:ntp $rootfs/var/log/ntpstats

sed -e "s,'-g','-4 -i /var/chroot/ntp -g'," \
    /etc/default/ntp > /tmp/x && \
    mv /tmp/x /etc/default/ntp

sed -e "s,restrict -6,#restrict -6," \
    -e "s,restrict ::1,#restrict ::1," \
    /etc/ntp.conf > /tmp/x && \
    mv /tmp/x /etc/ntp.conf

/etc/init.d/ntp start
Verify that ntp uses the chroot (file /etc/default/ntp):
NTPD_OPTS='-4 -i /var/chroot/ntp -g'

Debian NTP Server

Let start by installing few packages:
apt-get -y install ntp ntpdate
The options passed to ntp daemon are set in /etc/default/ntp file. We are interested to turn off ipv6 for now:
NTPD_OPTS='-4 -g'
As well we are going restrict ntp daemon for use ipv4 only (file /etc/ntp.conf):
# By default, exchange time with everybody, but don't 
# allow configuration.
restrict -4 default kod notrap nomodify nopeer noquery
#restrict -6 default kod notrap nomodify nopeer noquery

# Local users may interrogate the ntp server more 
# closely.
restrict 127.0.0.1
#restrict ::1
Restart ntp daemon so the changes take place:
/etc/init.d/ntp restart
Look which servers it uses for synchronization:
ntpq -4p

Client

Install ntpdate package:
apt-get -y install ntpdate
You can sync the client with you ntp server by issuing the following command (I assume your local htp server resolves to ntp.dev.local):
ntpdate -p 2 ntp.dev.local
Consider have a look at the following post (you just need to substitute the ntp server name with yours).

Tuesday, March 15, 2011

Debian Diskless Setup and Configuration

Here we are going setup a server that serves over network to pxe clients a diskless debian. We will be using the following:
  • DHCP server: dh1 (see how to install here)
  • TFTP server: tftp1 (IP: 192.168.10.35)
  • NFS server: nfs1 (IP: 192.168.10.30)
  • PXE image location on NFS server: /srv/diskless/c1

Saturday, March 5, 2011

How to properly "halt" virtual machine in LXC

While running a number of virtual machines in LXC you might need gracefully shutdown each virtual machine while host reboot. Here is a script (file /usr/local/sbin/lxc-shutdown):
#!/bin/sh

name=$1
timeout=15

if lxc-info -n $name | grep -qs "STOPPED"
then
    echo $name not running...
    exit 0
fi                                           
                                                                               
ssh $name halt &                                                               
#if [ -e /usr/bin/lxc-halt ]; then                                             
#    /usr/bin/lxc-halt -n $name                                                
#else                                                                          
#    ssh $name halt &                                                          
#fi

while [ $timeout -gt 0 ]
do
    timeout=$(($timeout-1));sleep 1
    if lxc-info -n $name | grep -qs "STOPPED"
    then
        exit 0
    fi
done

lxc-stop -n $name
lxc-wait -n $name -s 'STOPPED'
This approach requires root to have password-less ssh login (see more here). So now that you have a script that let you halt gracefully virtual machine, let make few changes to /etc/init.d/lxc (somewhere around line 56):
# ...
    stop)
    log_daemon_msg "Stopping $DESC"
    #action_all "lxc-stop -n"
    # Uncomment below if you need to halt containers 
    # in reverse order
    CONTAINERS=`echo $CONTAINERS | tac -s ' '`
    action_all "lxc-halt"
    ;;
# ...
Use the following two commands to override lxc-shutdown for lxc v0.8+
                                           
update-alternatives --install /usr/bin/lxc-shutdown \                        
   lxc-shutdown /usr/local/sbin/lxc-shutdown 1                                
update-alternatives --set lxc-shutdown \                                     
   /usr/local/sbin/lxc-shutdown

Friday, March 4, 2011

Exim4 SSL/TLS Configuration

Here we are going configure exim4 to use SSL/TLS for incoming connections:
  1. First of all let create an exim4 certificate request (see here how to create a certificate authority):
    openssl req -newkey rsa:2048 -keyout exim.key -out exim.csr -days 3650 -nodes
    
  2. Now let sign it with our certificate authority:
    openssl ca -out exim.crt -infiles exim.csr
    
  3. Here we get two important files: exim.key (that is private key) and exim.crt (x509 certificate file). Let copy them to /etc/exim4
  4. Secure certificates:
    chown root:Debian-exim exim.key exim.crt
    chmod g=r,o= exim.key exim.crt
    
  5. Enable exim4 daemon listening options for ports 25 and 465 (file /etc/default/exim4):
    SMTPLISTENEROPTIONS='-oX 465:25 -oP /var/run/exim4/exim.pid'
    
  6. Turn on SSL/TLS option (new file /etc/exim4/conf.d/main/00_exim4-localmacros):
    MAIN_TLS_ENABLE = true
    
  7. Restart exim4 and have a look at log file when you send a test message.
    /etc/init.d/exim4 restart
    echo test | mail -s "ssl/tls test" root@dev.local
    
    Here is what you will see in log file (/var/log/exim4/mainlog):
    ... P=esmtps X=TLS1.0:RSA_AES_256_CBC_SHA1:32 ...
    
    If for some reason you can not see esmtps message in log file it most likely it doesn't use SSL/TLS for local delivery, try from remote machine.

Alternative Certificate Location

You can specify any location for ssl/tls certificate (file /etc/exim4/conf.d/main/00_exim4-localmacros):
MAIN_TLS_CERTIFICATE=/etc/ssl/certs/mail.dev.local-cert.pem
MAIN_TLS_PRIVATEKEY=/etc/ssl/private/mail.dev.local-key.pem
This is useful when you host both SMTP and IMAP services on the same host. Note, group Debian-exim must have read access to both files.

Wednesday, March 2, 2011

How to generate consistent "random" numbers

Here is an article that explain a way to get consistent "random" number. The problem appears in virtual environments, like lxc, where hostid is the same. The sample below solves this problem by generating "random" number from a network adapter (eth0) MAC address. So here it is (file func/random_sleep)
#!/bin/bash

# 
# random_sleep shift_max rnd_max
#
# shift_max - a number to generate consistent "random"
# number.
# rnd_max - a random number to add
random_sleep()
{
    shift_max=600
    if [ ! -z $1 ]; then shift_max=$1; fi

    rnd_max=60
    if [ ! -z $2 ]; then rnd_max=$2; fi

    # shift is a number betweem 0 and 600
    shift=`/sbin/ifconfig eth0 | /bin/grep HWaddr | \
        /bin/sed -e 's/.*HWaddr //' | /usr/bin/tr -d ':'`
    shift=$((0x$shift%$shift_max))

    # Sleep a random amount of time + shift
    sleep_time=$(($RANDOM % $rnd_max + $shift))

    echo "Sleeping for $sleep_time seconds..."
    /bin/sleep $sleep_time
}
Here is how you can use it:
#!/bin/bash
. /usr/local/sbin/func/random_sleep
# or
# . `dirname $_`/func/random_sleep

random_sleep 300 60

Friday, February 18, 2011

Apache Kerberos Authentication over SSL for SVN

Suppose you already have a web site working over SSL (see here) and you would like add security on top of that, namely use Kerberos for authentication. I assume you saw the following:
  • Serving Multiple SVN Repositories with Apache (see here)

Thursday, February 17, 2011

How to add CA certificate to Common CA certificates

Debian package ca-certificates installs a number of common CA certificates, well known. Your certificate authority is not there, so you will get a warning messages every time it used by browser, mail client, IM, etc. Here are few simple steps to install your own CA certificate.
apt-get install ca-certificates
Copy CA certificate and reconfigure ca-certificates package:
cp cacert.pem /usr/share/ca-certificates
dpkg-reconfigure ca-certificates
You will be asked "Trust new certificates from certificate authorities?", choose Ask than from the list of activated certificates mark yours. This will rebuild certificates database with your CA certificate.

Apache Basic Authentication over SSL with PAM Kerberos/LDAP

Suppose you already have a web site serving multiple subversion repositories over SSL (see here) and you would like add security on top of that, namely use Kerberos for authentication and LDAP for authorization. Before we proceed please ensure your machine is capable to authenticate against Kerberos/LDAP (see here). I will assume you saw the following:
  • Serving Multiple SVN Repositories with Apache (see here)
  • Debian OpenLDAP client with Kerberos (see here)

Wednesday, February 16, 2011

Serving Multiple SVN Repositories with Apache

Here are our requirements:
  • SVN web server FQDN: scm1 ; scm1.dev.local
  • SVN is served via SSL only
  • Repositories access url: https://scm1/svn/project1, https://scm1.dev.local/svn/project2
  • Access: public
  • Policies: /var/lib/svn/conf/policies
  • Root: /var/lib/svn/repos
Before we proceed please see:
  • Apache with SSL (see here)
  • Revision control with subversion (see here). You can skip settings related to security permissions, etc since the authentication/authorization will be managed by apache.

Apache with SSL

Here we are going setup a web site with SSL support, so content can be securely served via https.
  • Web server FQDN: web1 ; web1.dev.local
  • Content served via: HTTP and HTTPS
  • Content location: /var/www/

Tuesday, February 15, 2011

Dovecot with Kerberos Authentication

Dovecot authentication/authorization consists of two important parts: passdb and userdb; passdb is used to confirm user credentials are valid for access, while userdb determines how authenticated user is mapped to uid/gid (this is necessary since mail box is on file system).

In this post we take a look at Dovecot configuration when Kerberos is used for passdb role. We also take a look at few possibilities for userdb implementation.

Before we proceed with setup (let assume our client machine name is mail1.dev.local) you need to setup the following:
  • Kerberos Client (look here).
Once the basic installation of the above is complete, here we go: Add imap service principal:
kadmin -p admin -q "addprinc -randkey imap/mail1.dev.local"

kadmin -p admin -q "ktadd imap/mail1.dev.local"

Dovecot V1.x Configuration

  1. Configure dovecot to use gssapi for authentication (file /etc/dovecot/dovecot.conf):
    auth default {
      #mechanisms = plain
      mechanisms = gssapi
    }
    
  2. If you want permit users to authenticate to dovecot using password (vs using transparent kerberos authentication via gssapi) than plain authentication mechanism must remain.
  3. Restart dovecot:
    /etc/init.d/dovecot restart
    

Dovecot V2.0 Configuration

  1. Install dovecot gssapi package:
    apt-get install dovecot-gssapi
    
  2. Group dovecot need to have read permission on kerberos keytab file (/etc/krb5.keytab).
    chgrp dovecot /etc/krb5.keytab
    chmod g+r /etc/krb5.keytab
    
  3. Ensure the following settings in authentication configuration (file /etc/dovecot/conf.d/10-auth.conf):
    # FQDN for the mail server
    auth_gssapi_hostname = mail1.dev.local
    
    # Locaction of keytab file
    auth_krb5_keytab = /etc/krb5.keytab
    
    auth_mechanisms = gssapi
    
  4. Restart dovecot:
    /etc/init.d/dovecot restart
    

Virtual Hosting

While all users are authenticated against Kerberos, we can map mailbox access to a single local user/group, e.g. vmail. This scenario is implemented by dovecot userdb static configuration option.
# 1. User is created with home directory set 
# to /var/mail.
# 2. User added to group vmail.
# 3. Do not gcreate a home directory.
# 4. User has no shell, ssh login impossible.
groupadd vmail
useradd -d /var/mail -G vmail -M -s /bin/false vmail
Changes to dovecot configuration below:
auth default {
  mechanisms = gssapi
  userdb static {
    args = uid=vmail gid=vmail home=/var/mail/%u
  }
}
When you create a new mailbox vmail user must be an owner. Let create a mailbox for user1:
mkdir /var/mail/user1
chown vmail /var/mail/user1
On successful user1 authentication dovecote will populate all necessary files for mailbox.

Open LDAP

You can use Kerberos authentication together with LDAP authorization. In this case LDAP database will serve userdb purpose. You have to setup OpenLDAP client with Kerberos (see here). Ensure the following settings in dovecot configuration:
auth default {
  mechanisms = plain gssapi
  passdb pam {
  }
  userdb passwd {
  }
}
This approach uses PAM. When you create a mailbox for user ensure user account (uid defined LDAP) is an owner for mailbox.

How to add CA certificate to NSS Certificate DB

If you have created a Certificate Authority (see here), you probably want get rid of warnings the consumers shows to your users, e.g. email clients while accessing the mailbox. Here are few simple steps to add your local Certificate Authority to to NSS Certificate DB:
  1. Copy CA certificate to known certificates:
    cp cacert.pem /etc/ssl/certs
    chmod go+r /etc/ssl/certs/cacert.pem
    
  2. Let install a tools to manage NSS Certificate DB:
    apt-get install libnss3-tools
    
  3. The default location of NSSDB is in $HOME/.pki/nssdb. If you do not have one yet issue the following command to create (see more baout certutil here):
    mkdir -p .pki/nssdb ; certutil -N -d sql:.pki/nssdb
    
  4. Add CA certificate:
    certutil -d sql:.pki/nssdb -A -t "CT,c,c" -n DEV.LOCAL \
    -i /etc/ssl/certs/cacert.pem
    

Evolution email client

Nothing specific need to be done. It uses .pki/nssdb by default

Firefox/Iceweasel web browser

The idea here is to point existing nssdb files to one in .pki/nssdb:
cd .mozilla/firefox/your-profile/
rm cert9.db key4.db
ln -s ~/.pki/nssdb/key4.db .
ln -s ~/.pki/nssdb/cert9.db .

Thunderbird email client

Things you need to do are exactly the same as for firefox, with the only exception to change default directory to .thunderbird/your-profile instead.

Final Note

At this point you should be fine to see SSL content (web, mail, etc) without a security warning since your CA is trusted. Consider copy nss db to /etc/skel, so the new users will get it working automatically:
cp -r .pki /etc/skel
The first time a new user logging, the nssdb will be copied from skel directory and as result the user will get valid CA certificate. Read more here.

Dovecot IMAP Server

The Internet Message Access Protocol (IMAP) is one of the two most prevalent Internet standard protocols for e-mail retrieval. Dovecot is an open source IMAP.
  • IMAP host FQDN: mail1.dev.local, ip: 192.168.10.11, DNS alias: mail.dev.local
  • Mailbox type: Maildir
  • Mail location: /var/mail/<user>
  • Communication: Only secure, TLS/SSL

Basic Installation

Here are few simple steps to configure: Let install dovecot:
apt-get -y install dovecot-imapd

Dovecot V1.x Configuration

Ensure imaps in the following configuration (file /etc/dovecot/dovecot.conf):
protocols = imap imaps
mail_location = maildir:/var/mail/%u

Dovecot V2.0 Configuration

Setup mail location (file /etc/dovecot/conf.d/10-mail.conf)
mail_location = maildir:/var/mail/%u

SSL

  1. Create SSL certificate (see here). While answering questions make sure the following (this is the name the clients will access your IMAP server):
    Common Name (eg, YOUR name) []:mail.dev.local
    
    There are two important files we created here: newreq.pem and newcert.pem. Rename those files:
    mv newreq.pem mail-key.pem
    mv newcert.pem mail-cert.pem
    
  2. Copy these files:
    cp mail-cert.pem /etc/ssl/certs
    cp mail-key.pem /etc/ssl/private
    
  3. Dovecot V1.x Configuration

    Let dovecot know about our certificates (file /etc/dovecot/dovecot.conf):
    ssl_cert_file = /etc/ssl/certs/mail-cert.pem
    ssl_key_file = /etc/ssl/private/mail-key.pem
    

    Dovecot V2.0 Configuration

    Let dovecot know about our certificates (file /etc/dovecot/conf.d/10-ssl.conf):
    ssl_cert = </etc/ssl/certs/mail-cert.pem
    ssl_key = </etc/ssl/private/mail-key.pem
    
  4. Restart dovecot:
    /etc/init.d/dovecot restart
    
You should be able access the mail via IMAP TLS/SSL now. See here how to get rid of warning message about the SSL certificate signature by mail clients, e.g. Evolution, etc.

Troubleshooting: namespace missing

While upgrading to dovecot v2.1.7 I noticed the following error:
mail1 dovecot: imap(xxx): Error: user xxx: Initialization failed: 
namespace configuration error: inbox=yes namespace missing
mail1 dovecot: imap(xxx): Error: Invalid user settings. Refer to 
server log for more information.
You need define inbox namespace and explicitly set the `inbox` attribute (file /etc/dovecot/conf.d/10-mail.conf)
namespace inbox {
    inbox = yes
}
Restart dovecot and that fix it.

Configure exim4 internet site; mail is sent and received directly using SMTP

This option of exim4 let you configure SMTP server for your domain.
  • SMTP host FQDN: mail1.dev.local, ip: 192.168.10.11
  • Domain: dev.local, serves emails like user1@dev.local
  • Delivery mode: Maildir
  • Mail location: /var/mail/<user>
Here are few simple steps to configure:

Monday, February 7, 2011

Email Available Updates for Debian

Before you proceed below please check:
  • Simple apt daily update script, see here.
  • Configure exim4 to send messages by smarthost, no local mail, see here.
We are going enhance daily update script by ability to send a list of new updates to mail.
#!/bin/sh

mailto=debian-updates@dev.local

# Download only; package files are only retrieved, not 
# unpacked or installed.
apt-get -dqq update
apt-get -dyqq upgrade

has_upgrades=$(apt-get -s upgrade | grep ^Inst)
if [ "$has_upgrades" ] ; then
    echo "$has_upgrades" | mail -s \
    "Updates for $(hostname) on $(date +%Y-%m-%d)" \
    $mailto
fi
Alternatively consider using apticron, read more here.

Configure exim4 to send messages by smarthost, no local mail

This option of exim4 configuration is suitable for a client system which is not responsible for a local e-mail domain. All locally generated e-mail is sent to the smarthost.
  • Smarthost FQDN: mail.dev.local
  • Client: deby01.dev.local
Here are few simple steps to configure:
  1. The easiest way is to reconfigure exim4-config package:
    dpkg-reconfigure exim4-config
    
  2. General type of mail configuration:
       mail sent by smarthost; no local mail
    System mail name:
       deby01.dev.local
    IP-addresses to listen on for incoming SMTP connections:
       127.0.0.1
    Other destinations for which mail is accepted:
       deby01.dev.local
    Visible domain name for local users:
       deby01.dev.local
    IP address or host name of the outgoing smarthost:
       mail.dev.local
    Keep number of DNS-queries minimal (Dial-on-Demand)?
       No
    Split configuration into small files?
       No
    
Let verify it is working:
echo "test message" | mail -s "test" user1@dev.local
... exim4 log (file /var/log/exim4/mainlog):
1PmoNq-0001or-55 <= root@deby01.dev.local H=deby01.dev.local (localhost) [192.168.XX.XXX] P=esmtps X=TLS1.0:RSA_AES_256_CBC_SHA1:32 S=715 id=E1PmoNq-0001f6-08@localhost
1PmoNq-0001or-55 => user1  R=local_user T=maildir_home
1PmoNq-0001or-55 Completed
At this point user1 should be able to receive your test message.

Saturday, February 5, 2011

How to setup LDAP DNS Discovery

LDAP DNS discovery can simplify the client hosts setup. The following need to be added to zone file.
$ORIGIN dev.local.
_ldap._tcp  IN SRV 10 0 389 ldapk1
_ldap._tcp  IN SRV 20 0 389 ldapk2
The client configuration can now look like this (file /etc/ldap/ldap.conf):
# BASE    dc=dev,dc=local
# URI     ldap://ldapk1/
Let test it:
host -t SRV _ldap._tcp

Troubleshooting: dbus-daemon nss_ldap failed to bind to LDAP server

While installing Debian OpenLDAP client with Kerberos (see here) on Gnome desktop you might experience the following errors in auth.log.
dbus-daemon: GSSAPI Error: Unspecified GSS failure.  Minor code may provide more information (Credentials cache file '/tmp/krb5cc_101' not found)
dbus-daemon: nss_ldap: failed to bind to LDAP server ldap://ldapk1.dev.local/: Local error
dbus-daemon: nss_ldap: could not search LDAP server - Server is unavailable
At the same time you will might see a number of errors reported by slapd:
slapd: conn=2806 op=0 UNBIND
slapd: conn=2806 fd=27 closed
slapd: conn=2807 fd=27 ACCEPT from IP=192.168.XX.XXX:XXXXX (IP=0.0.0.0:XXX)
The problem is related to parallel boot of your system. By default dbus and nscd are started in parallel, the problem appears when dbus is launched before nscd daemon. In order to fix that you need to change boot sequence. Make sure you have the following in /etc/init.d/dbus (notice line Should-Start):
# Provides:          dbus
# Should-Start:      nscd
# Required-Start:    $remote_fs $syslog
Once above is done simple re-enable dbus service so it updates everything necessary:
rcconf --off dbus ; rcconf --on dbus
Notice changes in /etc/rc2.d:
# ls /etc/rc2.d/
S17nscd ... S18dbus
You need restart your computer (or at least restart dbus daemon) in order changes take place.

Debian OpenLDAP client with Kerberos

Before we proceed with client setup (let assume our client machine name is deby01.dev.local) you need to setup the following:
  • Kerberos Client (look here).
Once the basic installation of the above is complete, here we go:
  1. We need install few packages:
    apt-get -y install ldap-utils libpam-ldap \
    libsasl2-modules-gssapi-mit nscd libnss-ldap kstart
    
    During installation you will be prompted for few questions:
    • libnss-ldap
      LDAP server URI: ldap://ldapk1.dev.local/
      Distinguished name of the search base: dc=dev,dc=local
      LDAP version to use: 3
      cn=admin,ou=people,dc=dev,dc=local
      LDAP account for root: cn=admin,ou=people,dc=dev,dc=local
      LDAP root account password: <just hit enter>
      
    • libpam-ldap
      Allow LDAP admin account to behave like local root? No
      Does the LDAP database require login? No
      
  2. Reconfigure libpam-runtime and disable LDAP Authentication:
    dpkg-reconfigure libpam-runtime
    
  3. Configure kstart, add the following to /etc/inittab (It will check every 10 minutes of the Kerberos ticket needs to be renewed and set the ticket lifetime to 24 hours:
    KS:2345:respawn:/usr/bin/k5start -U -f /etc/krb5.keytab -K 10 -l 24h
    
    Force init to reload configuration:
    kill -HUP 1
    
    Ensure /tmp/krb5cc_0 file is created:
    ls -lh /tmp/krb5cc_0
    
  4. Kerberise libnss-ldap (file /etc/libnss-ldap.conf), ensure the following:
    base dc=dev,dc=local
    uri ldap://ldapk1.dev.local/
    ldap_version 3
    rootbinddn cn=admin,ou=people,dc=dev,dc=local
    
    # Use SASL and GSSAPI and where to find the 
    # Kerberos ticket cache.
    use_sasl        on
    sasl_mech       gssapi
    krb5_ccname FILE:/tmp/krb5cc_0
    
  5. Set defaults for LDAP clients (file /etc/ldap/ldap.conf). Note client configuration changes if ldap is configured via SSL (see here).
    BASE    dc=dev,dc=local
    URI     ldap://ldapk1.dev.local/
    SASL_MECH GSSAPI
    
  6. Add LDAP support for login process by nscd (file /etc/nsswitch.conf):
    passwd:         compat ldap
    group:          compat ldap
    shadow:         compat ldap
    
  7. Restart Name Service Cache daemon:
    /etc/init.d/nscd restart
    
  8. Configure PAM to automatically create a user home directory (file /etc/pam.d/common-session):
    session  required  pam_mkhomedir.so
    
You should be ready to login with a user created in LDAP and password set in Kerberos.

Troubleshooting

  • You might experience the following error while initializing kerberos ticket in Debian Gnome desktop:
    Cannot resolve network address for KDC in realm DEV.LOCAL
    
    This somehow conflicts with avahi-daemon, you will need disable it:
    rcconf --off avahi-daemon
    
  • If you are using Debian Gnome desktop, have a look at Troubleshooting: dbus-daemon nss_ldap failed to bind to LDAP server, that you can find here.

Debian OpenLDAP with Kerberos Authentication

Before we proceed with ldap kerberization (let assume our server name is ldapk1.dev.local) you need to setup the following:
  • OpenLDAP Server (look here).
  • Kerberos Client (look here).
Once the basic installation of the above is complete, here we go:

Remove Authentication from LDAP

  1. Since we are going to authenticate users with Kerberos we need to prohibit users access to password stored in ldap. Add the following to file access-passwd.ldif:
    dn: olcDatabase={1}hdb,cn=config
    changetype: modify
    #
    # Delete default user access to password
    delete: olcAccess
    olcAccess: {0}to attrs=userPassword,shadowLastChange
      by self write
      by anonymous auth
      by dn="cn=admin,dc=dev,dc=local" write
      by * none
    -
    # Prohibit access to password
    add: olcAccess
    olcAccess: {0}to attrs=userPassword,shadowLastChange
      by * none
    -
    # Only authenticated users have read access 
    # Anonymous users have no access. 
    add: olcAccess
    olcAccess: {1}to *
      by users read
      by * none
    
    and apply changes:
    ldapmodify -QY EXTERNAL -H ldapi:/// -f access-passwd.ldif
    
  2. Delete admin account:
    ldapdelete -cxWD cn=admin,dc=dev,dc=local cn=admin,dc=dev,dc=local
    
    and it access rights in the directory (file access-noadmin.ldif):
    dn: olcDatabase={1}hdb,cn=config
    changetype: modify
    #
    # Revoke admin write rights to the directory
    delete: olcAccess
    olcAccess: {3}to *
      by self write
      by dn="cn=admin,dc=dev,dc=local" write
      by * read
    -
    # Move admin account to people unit
    replace: olcRootDN
    olcRootDN: uid=admin,ou=people,dc=dev,dc=local
    -
    # Remove admin password
    delete: olcRootPW
    
    and apply changes:
    ldapmodify -QY EXTERNAL -H ldapi:/// -f access-noadmin.ldif
    

Setup GSSAPI mapping between OpenLDAP and Kerberos

  1. Install SASL module for Kerberos:
    apt-get install libsasl2-modules-gssapi-mit
    
  2. Add ldap principal:
    kadmin -p admin -q "addprinc -randkey ldap/ldapk1.dev.local"
    
    kadmin -p admin -q "ktadd ldap/ldapk1.dev.local"
    
  3. Allow openldap group (slapd service is running under openldap account) access kerberos information:
    chgrp openldap /etc/krb5.keytab
    chmod g+r,o= /etc/krb5.keytab
    ls -lh /etc/krb5.keytab 
    
  4. Specify authentication mode (file /etc/ldap/ldap.conf):
    SASL_MECH GSSAPI
    
  5. Setup SASL mapping between Kerberos and LDAP accounts (file auth-kerberos.ldif):
    dn: cn=config
    changetype: modify
    #
    # Regular expression that match a simple user name
    # provided by SASL and map it to ldap entry
    add: olcAuthzRegexp
    olcAuthzRegexp: uid=([^,]+),cn=dev.local,cn=gssapi,cn=auth
      uid=$1,ou=people,dc=dev,dc=local
    -
    # Specify SASL Kerberos realm
    add: olcSaslRealm
    olcSaslRealm: DEV.LOCAL
    
    and apply changes:
    ldapmodify -QY EXTERNAL -H ldapi:/// -f auth-kerberos.ldif
    
  6. Restart OpenLDAP service:
    /etc/init.d/slapd restart
    

Verify settings

Verify above changes by querying config:
ldapsearch -LLLQY EXTERNAL -H ldapi:/// -b \
cn=config "(|(cn=config)(olcDatabase={1}hdb))"
Here it is:
dn: cn=config
objectClass: olcGlobal
cn: config
olcArgsFile: /var/run/slapd/slapd.args
olcLogLevel: stats
olcPidFile: /var/run/slapd/slapd.pid
olcToolThreads: 1
olcAuthzRegexp: {0}uid=([^,]+),cn=dev.local,cn=gssapi,cn=auth uid=$1,ou=people
 ,dc=dev,dc=local
olcSaslRealm: DEV.LOCAL

dn: olcDatabase={1}hdb,cn=config
objectClass: olcDatabaseConfig
objectClass: olcHdbConfig
olcDatabase: {1}hdb
olcDbDirectory: /var/lib/ldap
olcSuffix: dc=dev,dc=local
olcAccess: {0}to attrs=userPassword,shadowLastChange by * none
olcAccess: {1}to dn.base="" by * read
olcLastMod: TRUE
olcDbCheckpoint: 512 30
olcDbConfig: {0}set_cachesize 0 2097152 0
olcDbConfig: {1}set_lk_max_objects 1500
olcDbConfig: {2}set_lk_max_locks 1500
olcDbConfig: {3}set_lk_max_lockers 1500
olcDbIndex: objectClass eq
olcDbIndex: uid eq
olcDbIndex: cn eq
olcDbIndex: ou eq
olcDbIndex: dc eq
olcRootDN: uid=admin,ou=people,dc=dev,dc=local

Kerberos Authentication Test

  1. Let ensure anonymous has no access
    ldapk1:~/ldap# ldapsearch -xLLL
    No such object (32)
    
  2. Authenticate to Kerberos:
    kinit -p admin
    
  3. Let make the search as authenticated user (you should be able to see organization units people and groups):
    ldapsearch -LLL
    

Troubleshooting

  1. Cannot create replay cache: No such file or directory
    ldap1:~# ldapsearch -LLL
    SASL/GSSAPI authentication started
    ldap_sasl_interactive_bind_s: Other (e.g., implementation specific) 
    error (80) additional info: SASL(-1): generic failure: GSSAPI Error: 
    Unspecified GSS failure.  Minor code may provide more information 
    (Cannot create replay cache: No such file or directory)
    
    The only way recover from this error:
    • Restart slapd daemon
    • Consider add cron job on reboot that restarts slapd (file /etc/cron.d/slapd)
      @reboot root /etc/init.d/slapd restart
      
While authentication provided by Kerberos is secure now, consider protect communication with OpenLDAP by SSL/TLS encryption (read how here).

Tuesday, January 25, 2011

How to create .img file

An IMG file contains a raw dump of the content of a disk. We will use losetup to associate loop device with a file and mount it to work with.
  1. Pre-allocate img file (stuff.img) of the size you need (10M):
    dd if=/dev/zero of=/tmp/stuff.img bs=1M count=10
    
  2. Setup a loop device:
    losetup /dev/loop0 /tmp/stuff.img
    
  3. Create ext3 file system on loop device:
    mkfs.ext3 /dev/loop0
    
  4. Mount it:
    mount /dev/loop0 /mnt
    
Once you are done:
  1. Unmount file system:
    umount /mnt
    
  2. Delete loop device:
    losetup -d /dev/loop0
    

Managing LXC container

Just like a virtual machine you can start/stop it:
  1. Start lxc container vm0:
    lxc-start -n vm0 -d
    
  2. Login into console:
    lxc1:~# lxc-console -n vm0
    
    Type <Ctrl+a q> to exit the console
    
    Debian GNU/Linux 6.0 vm0 tty1
    
    vm0 login:
    
  3. Since ssh server is already installed, you should be fine to login (assuing you have dhcp server running in the network and dynamic dns is configured accordingly):
    ssh vm0
    
  4. Shutdown lxc container vm0:
    ssh vm0 halt && lxc-wait -n vm0 -s STOPPED
    
  5. Stop lxc container vm0 (this simply kills all processes related to container):
    lxc-stop -n vm0
    

How to create a LXC container in Debian

Here we are going create an lxc debian container for it's squeeze/testing release (see also Setup LXC container):

Choose Packages

We will setup debian base minimal configuration, however you can customize which packages you would like to have installed (file /etc/lxc/packages):
# Extra packages
packages=\
ifupdown,\
locales,\
libui-dialog-perl,\
dialog,\
isc-dhcp-client,\
netbase,\
net-tools,\
iproute,\
openssh-server,\
vim,\
apt-utils

LXC Prepare Script

The script below does the following:
  1. Downloads installation packages and stores them in a single file located at /var/cache/lxc. The file name format is debian-$release.tar. If the file exists it doesn't create it again, thus saves your bandwidth.
    lxc1:~# ls -lh /var/cache/lxc/
    total 66M
    -rw-r--r-- 1 root root 66M debian-testing.tar
    
  2. The installation goes to /var/lib/lxc. A subdirectory is created for each container.
  3. The installed container comes with configured:
    • SELinux is disabled.
    • User ssh public key is added to container authorized keys (this allows password less ssh login).
    • Initab is setup with 2 virtual consoles only (tty1, tty2).
    • Network interface is configured for DHCP.
    • Host name is set to container name.
    • DHCP client is set to publish client name in DNS.
    • The hosts file resolves container name to 127.0.0.1.
    • Disable IPv6 in the kernel.
    • Disable IPv6 for sshd.
    • The default local is set to en_US.UTF-8 UTF-8.
    • The following services removed: umountfs, hwclock.sh, hwclockfirst.sh.
    • The system is updated/upgrade/cleaned.
    • Password for user root is set to root.
  4. LXC container network adapter settings are set to random MAC address (starting from 00:1E).
  5. The LXC container is registered with in /etc/lxc in order to be able use it with lxc service.
Copy the following into file /usr/local/sbin/lxc-prepare:
#!/bin/bash

hostname=vm0
release=testing
arch=i386

cache=/var/cache/lxc
lib=/var/lib/lxc

. /etc/lxc/packages

check_args() 
{
    if [ ! -z $1 ]; then hostname=$1; fi
    if [ ! -z $2 ]; then release=$2; fi
}

download() 
{
    if [ ! -e "$cache/debian-$release-$arch.tar" ]; then
        debootstrap --verbose --arch=$arch \
          --make-tarball="$cache/debian-$release-$arch.tar" \
          --include $packages --variant=minbase \
          $release "$cache/$release-$arch"
        if [ $? -ne 0 ]; then
           exit 1
        fi
    else
        echo "There is '$cache/debian-$release-$arch.tar'... skipping download."
    fi
}

install() 
{
    if [ ! -d "$lib/$hostname/rootfs" ]; then
        debootstrap --verbose --arch=$arch \
          --unpack-tarball="$cache/debian-$release-$arch.tar" \
          --include $packages --variant=minbase \
          $release "$lib/$hostname/rootfs"
        if [ $? -ne 0 ]; then
           exit 1
        fi
    else
        echo "There is '$lib/$hostname'... skipping install."
    fi
}

configure()
{
    rootfs=$lib/$hostname/rootfs

    mkdir -p $rootfs/selinux
    echo 0 > $rootfs/selinux/enforce

    if [ -f "$HOME/.ssh/id_rsa.pub" ]; then
        mkdir -p $rootfs/root/.ssh
        cp ~/.ssh/id_rsa.pub "$rootfs/root/.ssh/authorized_keys"
    fi

    cat <<EOF > $rootfs/etc/inittab
id:3:initdefault:
si::sysinit:/etc/init.d/rcS
l0:0:wait:/etc/init.d/rc 0
l1:1:wait:/etc/init.d/rc 1
l2:2:wait:/etc/init.d/rc 2
l3:3:wait:/etc/init.d/rc 3
l4:4:wait:/etc/init.d/rc 4
l5:5:wait:/etc/init.d/rc 5
l6:6:wait:/etc/init.d/rc 6
# Normally not reached, but fallthrough in case of emergency.
z6:6:respawn:/sbin/sulogin
1:2345:respawn:/sbin/getty 38400 console
c1:12345:respawn:/sbin/getty 38400 tty1 linux
c2:12345:respawn:/sbin/getty 38400 tty2 linux
EOF

    cat <<EOF > $rootfs/etc/apt/sources.list
 
deb http://ftp.debian.org/debian/ $release main contrib non-free
deb-src http://ftp.debian.org/debian/ $release main contrib non-free

deb http://security.debian.org/ $release/updates main
deb-src http://security.debian.org/ $release/updates main
EOF

    cat <<EOF > $rootfs/etc/network/interfaces
auto lo
iface lo inet loopback

auto eth0
iface eth0 inet dhcp
EOF

    domain=`hostname -d`
    if [ ! -z $domain ]; then
        domain=.$domain
    fi
    cat <<EOF > $rootfs/etc/hostname
$hostname
EOF

    cat <<EOF >> $rootfs/etc/dhcp/dhclient.conf
send host-name "$hostname";
EOF

    cat <<EOF > $rootfs/etc/hosts
127.0.0.1   localhost
EOF

    cat <<EOF > $rootfs/etc/locale.gen
en_US.UTF-8 UTF-8
EOF

    echo net.ipv6.conf.all.disable_ipv6=1 >\
$rootfs/etc/sysctl.d/disableipv6.conf

    echo "AddressFamily inet" >>\
$rootfs/etc/ssh/sshd_config

    cp /etc/timezone $rootfs/etc/timezone
    cp /etc/localtime $rootfs/etc/localtime
    area=$(debconf-show tzdata | grep Areas | cut -d ' ' -f 3)
    zone=$(debconf-show tzdata | grep Zones/${area} | cut -d ' ' -f 3)

    cat <<EOF | chroot $rootfs
mknod -m 666 /dev/tty1 c 4 1
mknod -m 666 /dev/tty2 c 4 2
locale-gen en_US.UTF-8
update-locale LANG=en_US.UTF-8
echo "tzdata tzdata/Areas select $area" | debconf-set-selections
echo "tzdata tzdata/Zones/$area select $zone" | debconf-set-selections
dpkg-reconfigure -u tzdata
/usr/sbin/update-rc.d -f umountfs remove
/usr/sbin/update-rc.d -f hwclock.sh remove
/usr/sbin/update-rc.d -f hwclockfirst.sh remove
apt-get update
apt-get -y upgrade
apt-get autoremove
apt-get clean
apt-get autoclean
echo "root:root" | chpasswd
EOF

    cat <<EOF > $lib/$hostname/config
lxc.utsname = $hostname
lxc.tty = 4
lxc.pts = 1024
lxc.rootfs = $rootfs
lxc.cgroup.devices.deny = a
# /dev/null and zero
lxc.cgroup.devices.allow = c 1:3 rwm
lxc.cgroup.devices.allow = c 1:5 rwm
# consoles
lxc.cgroup.devices.allow = c 5:1 rwm
lxc.cgroup.devices.allow = c 5:0 rwm
lxc.cgroup.devices.allow = c 4:0 rwm
lxc.cgroup.devices.allow = c 4:1 rwm
# /dev/{,u}random
lxc.cgroup.devices.allow = c 1:9 rwm
lxc.cgroup.devices.allow = c 1:8 rwm
lxc.cgroup.devices.allow = c 136:* rwm
lxc.cgroup.devices.allow = c 5:2 rwm
# rtc
lxc.cgroup.devices.allow = c 254:0 rwm

# mounts point
lxc.mount.entry=proc $rootfs/proc proc nodev,noexec,nosuid 0 0
lxc.mount.entry=devpts $rootfs/dev/pts devpts defaults 0 0
lxc.mount.entry=sysfs $rootfs/sys sysfs defaults  0 0

# network
lxc.network.type = veth
lxc.network.flags = up
lxc.network.link = br0
lxc.network.hwaddr = 00:1E:$(hex):$(hex):$(hex):$(hex)
EOF
}

hex() 
{
    echo "`tr -dc A-F0-9 < /dev/urandom | head -c 2 | xargs`"
}

register()
{
    if [ -h /etc/lxc/$hostname.conf ]; then
        rm /etc/lxc/$hostname.conf
    fi

    ln -s $lib/$hostname/config /etc/lxc/$hostname.conf
}

check_args $1 $2
download
install
configure
register
Make the file executable:
chmod +x /usr/local/sbin/lxc-prepare

Create LXC Linux Container

We are going create a linux container vm0 based on debian testing.
lxc1:~# lxc-prepare vm0 testing
...
lxc1:~# du -hs /var/lib/lxc/vm0
235M /var/lib/lxc/vm0/

Auto start LXC Linux Container

Ensure the following in /etc/default/lxc, this one autostart vm0 and vm1:
# Comment out to run the lxc init script
RUN=yes

# Directory containing the container configurations
CONF_DIR=/etc/lxc

# Start /etc/lxc/example.conf, /etc/lxc/autostart.conf, etc.
#CONTAINERS="example autostart container"
CONTAINERS="vm0 vm1"
Since version 0.7.5 a way lxc autostart has been changed. Instead of listing in CONTAINERS variable make a symbolic link to configuration at /etc/lxc/auto directory.
ln -s /etc/lxc/vm0.conf /etc/lxc/auto/vm0.conf
Read more here.

Monday, January 24, 2011

Debian Squeeze/Testing LXC container patch

Prepare Debian Squeeze/Testing container

  1. Copy the following into file lxc-debian-testing. This is a modified version of /usr/lib/lxc/templates/lxc-debian to get it working with squeeze/testing release of dedian:
    #!/bin/bash
    
    configure_debian()
    {
        rootfs=$1
        hostname=$2
    
        # configure the inittab
        cat <<EOF > $rootfs/etc/inittab
    id:3:initdefault:
    si::sysinit:/etc/init.d/rcS
    l0:0:wait:/etc/init.d/rc 0
    l1:1:wait:/etc/init.d/rc 1
    l2:2:wait:/etc/init.d/rc 2
    l3:3:wait:/etc/init.d/rc 3
    l4:4:wait:/etc/init.d/rc 4
    l5:5:wait:/etc/init.d/rc 5
    l6:6:wait:/etc/init.d/rc 6
    # Normally not reached, but fallthrough in case of emergency.
    z6:6:respawn:/sbin/sulogin
    1:2345:respawn:/sbin/getty 38400 console
    c1:12345:respawn:/sbin/getty 38400 tty1 linux
    c2:12345:respawn:/sbin/getty 38400 tty2 linux
    EOF
     
        # create tty devices
        chroot $rootfs mknod -m 666 /dev/tty1 c 4 1
        chroot $rootfs mknod -m 666 /dev/tty2 c 4 2
    
        # disable selinux in debian
        mkdir -p $rootfs/selinux
        echo 0 > $rootfs/selinux/enforce
    
        # configure the network using the dhcp
        cat <<EOF > $rootfs/etc/network/interfaces
    auto lo
    iface lo inet loopback
    
    auto eth0
    iface eth0 inet dhcp
    EOF
    
        # set the hostname
        cat <<EOF > $rootfs/etc/hostname
    $hostname
    EOF
    
        # set dhcp hostname
        cat <<EOF >> $rootfs/etc/dhcp/dhclient.conf
    send host-name "$hostname";
    EOF
    
        # set default locale
        cat <<EOF > $rootfs/etc/locale.gen
    en_US.UTF-8 UTF-8
    EOF
        
        # reconfigure some services
        if [ -z "$LANG" ]; then
        chroot $rootfs locale-gen en_US.UTF-8
        chroot $rootfs update-locale LANG=en_US.UTF-8
        else
        chroot $rootfs locale-gen $LANG
        chroot $rootfs update-locale LANG=$LANG
        fi
    
        # remove pointless services in a container
        chroot $rootfs /usr/sbin/update-rc.d -f umountfs remove
        chroot $rootfs /usr/sbin/update-rc.d -f hwclock.sh remove
        chroot $rootfs /usr/sbin/update-rc.d -f hwclockfirst.sh remove
    
        echo "root:root" | chroot $rootfs chpasswd
        echo "Root password is 'root', please change !"
    
        return 0
    }
    
    download_debian()
    {
        packages=\
    ifupdown,\
    locales,\
    libui-dialog-perl,\
    dialog,\
    isc-dhcp-client,\
    netbase,\
    net-tools,\
    iproute,\
    vim,\
    openssh-server
    
        cache=$1
        arch=$2
    
        # check the mini debian was not already downloaded
        mkdir -p "$cache/partial-$arch"
        if [ $? -ne 0 ]; then
            echo "Failed to create '$cache/partial-$arch' directory"
         return 1
        fi
    
        # download a mini debian into a cache
        echo "Downloading debian minimal ..."
        debootstrap --verbose --variant=minbase --arch=$arch \
        --include $packages \
        testing $cache/partial-$arch http://ftp.us.debian.org/debian
        if [ $? -ne 0 ]; then
        echo "Failed to download the rootfs, aborting."
        return 1
        fi
    
        mv "$1/partial-$arch" "$1/rootfs-$arch"
        echo "Download complete."
    
        return 0
    }
    
    copy_debian()
    {
        cache=$1
        arch=$2
        rootfs=$3
    
        # make a local copy of the minidebian
        echo -n "Copying rootfs to $rootfs..."
        cp -a $cache/rootfs-$arch $rootfs || return 1
        return 0
    }
    
    install_debian()
    {
        cache="/var/cache/lxc/debian-testing"
        rootfs=$1
        mkdir -p /var/lock/subsys/
        (
            flock -n -x 200
            if [ $? -ne 0 ]; then
                echo "Cache repository is busy."
                return 1
            fi
    
            arch=$(arch)
            if [ "$arch" == "x86_64" ]; then
                arch=amd64
            fi
    
            if [ "$arch" == "i686" ]; then
                arch=i386
            fi
    
            echo "Checking cache download in $cache/rootfs-$arch ... "
            if [ ! -e "$cache/rootfs-$arch" ]; then
                download_debian $cache $arch
                if [ $? -ne 0 ]; then
                    echo "Failed to download 'debian base'"
                    return 1
                fi
            fi
    
            copy_debian $cache $arch $rootfs
            if [ $? -ne 0 ]; then
                echo "Failed to copy rootfs"
                return 1
            fi
    
            return 0
    
        ) 200>/var/lock/subsys/lxc
    
        return $?
    }
    
    copy_configuration()
    {
        path=$1
        rootfs=$2
        name=$3
    
        cat <<EOF >> $path/config
    lxc.tty = 4
    lxc.pts = 1024
    lxc.rootfs = $rootfs
    lxc.cgroup.devices.deny = a
    # /dev/null and zero
    lxc.cgroup.devices.allow = c 1:3 rwm
    lxc.cgroup.devices.allow = c 1:5 rwm
    # consoles
    lxc.cgroup.devices.allow = c 5:1 rwm
    lxc.cgroup.devices.allow = c 5:0 rwm
    lxc.cgroup.devices.allow = c 4:0 rwm
    lxc.cgroup.devices.allow = c 4:1 rwm
    # /dev/{,u}random
    lxc.cgroup.devices.allow = c 1:9 rwm
    lxc.cgroup.devices.allow = c 1:8 rwm
    lxc.cgroup.devices.allow = c 136:* rwm
    lxc.cgroup.devices.allow = c 5:2 rwm
    # rtc
    lxc.cgroup.devices.allow = c 254:0 rwm
    
    # mounts point
    lxc.mount.entry=proc $rootfs/proc proc nodev,noexec,nosuid 0 0
    lxc.mount.entry=devpts $rootfs/dev/pts devpts defaults 0 0
    lxc.mount.entry=sysfs $rootfs/sys sysfs defaults  0 0
    
    lxc.utsname = $name
    lxc.network.type = veth
    lxc.network.flags = up
    lxc.network.link = br0
    # It is fine to be commented out
    #lxc.network.ipv4 = 192.168.10.21/24
    # Change this
    # lxc.network.hwaddr = 00:11:22:33:44:00
    EOF
    
        if [ $? -ne 0 ]; then
            echo "Failed to add configuration"
            return 1
        fi
    
        return 0
    }
    
    clean()
    {
        cache="/var/cache/lxc/debian-testing"
    
        if [ ! -e $cache ]; then
            exit 0
        fi
    
        # lock, so we won't purge while someone is creating a repository
        (
            flock -n -x 200
            if [ $? != 0 ]; then
                echo "Cache repository is busy."
                exit 1
            fi
    
            echo -n "Purging the download cache..."
            rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1
            exit 0
    
        ) 200>/var/lock/subsys/lxc
    }
    
    usage()
    {
        cat <<EOF
    $1 -h|--help -p|--path=<path> --clean
    EOF
        return 0
    }
    
    options=$(getopt -o hp:n:c -l help,path:,name:,clean -- "$@")
    if [ $? -ne 0 ]; then
        usage $(basename $0)
        exit 1
    fi
    eval set -- "$options"
    
    while true
    do
        case "$1" in
            -h|--help)      usage $0 && exit 0;;
            -p|--path)      path=$2; shift 2;;
            -n|--name)      name=$2; shift 2;;
            -c|--clean)     clean=$2; shift 2;;
            --)             shift 1; break ;;
            *)              break ;;
        esac
    done
    
    if [ ! -z "$clean" -a -z "$path" ]; then
        clean || exit 1
        exit 0
    fi
    
    type debootstrap
    if [ $? -ne 0 ]; then
        echo "'debootstrap' command is missing"
        exit 1
    fi
    
    if [ -z "$path" ]; then
        echo "'path' parameter is required"
        exit 1
    fi
    
    if [ "$(id -u)" != "0" ]; then
        echo "This script should be run as 'root'"
        exit 1
    fi
    
    rootfs=$path/rootfs
    
    install_debian $rootfs
    if [ $? -ne 0 ]; then
        echo "failed to install debian"
        exit 1
    fi
    
    configure_debian $rootfs $name
    if [ $? -ne 0 ]; then
        echo "failed to configure debian for a container"
        exit 1
    fi
    
    copy_configuration $path $rootfs $name
    if [ $? -ne 0 ]; then
        echo "failed write configuration file"
        exit 1
    fi
    
    if [ ! -z $clean ]; then
        clean || exit 1
        exit 0
    fi
    
  2. Make the file executable:
    chmod +x lxc-debian-testing
    

Create LXC Linux Container

We are going create a linux container based on debian testing.
  1. Let give the machine name vm0.
    lxc1:~# mkdir -p /var/lib/lxc/vm0
    lxc1:~# ./lxc-debian-testing -p /var/lib/lxc/vm0 -n vm0
    debootstrap is /usr/sbin/debootstrap
    Checking cache download in /var/cache/lxc/debian-testing/rootfs-i386 ... 
    Downloading debian minimal ...
    ...
    Download complete.
    Copying rootfs to /var/lib/lxc/vm0/rootfs...Generating locales (this might take a while)...
      en_US.UTF-8... done
    Generation complete.
    ...
    Root password is 'root', please change !
    
  2. Set network adapter mac address (must be unique):
    lxc1:~# cd /var/lib/lxc/vm0
    lxc1:/var/lib/lxc/vm0# echo "lxc.network.hwaddr = 00:AF:22:33:44:00" >> config
    
  3. Make some clean up and backup:
    
    lxc1:/var/lib/lxc/vm0# chroot rootfs/
    root@lxc1:/# apt-get update && apt-get -y upgrade && apt-get clean && apt-get autoclean
    ...
    lxc1:/# exit
    lxc1:/var/lib/lxc/vm0# du -hs rootfs
    235M vm0
    lxc1:/var/lib/lxc/vm0# tar czf ../vm0.tgz config rootfs/
    lxc1:/var/lib/lxc/vm0# cd .. && ls -lh | grep tgz
    -rw-r--r-- 1 root root  80M vm0.tgz
    

How to setup LXC containers in Debian

LXC Linux Containers provides the resource management through the control groups aka process containers and resource isolation through the namespaces.

Installation

  1. The basic installation of lxc containers requires three package:
    apt-get install bridge-utils debootstrap lxc
    
  2. We need to mount the virtual control group file system on /cgroup:
    mkdir /cgroup
    echo "cgroup /cgroup cgroup defaults 0 0" >> /etc/fstab
    mount -a
    

Network Bridge

  1. Add network bridge to primary network interface (file /etc/network/interfaces):
    # The primary network interface
    #allow-hotplug eth0
    #iface eth0 inet dhcp
    auto br0
    iface br0 inet dhcp
          bridge_ports eth0
          bridge_fd 0
          bridge_maxwait 0
          bridge_stp off
    
  2. Restart networking:
    /etc/init.d/networking restart
    
What is amazing about lxc is how it is savy to resources. A newly started container takes not much but just extra 1M of memory.