The objective is to set-up email server with web-mail and anti-spam – all on a £30 Raspberry Pi. On top of that some management tools plus WordPress blogging platform.


For additional few pounds you can purchase yourself a domain and start your own online blog – I use NameCheap – highly recommended – have a look at their offering.

Fundamentals:
E-mail server:
Milters:
Webserver:
Additional bits:
Update Firmware:
sudo apt-get install ca-certificates
sudo apt-get install git-core mc
sudo wget http://goo.gl/1BOfJ -O /usr/bin/rpi-update
sudo chmod +x /usr/bin/rpi-update
sudo rpi-update
sudo ldconfig
sudo rpi-update
sudo reboot
Configure IP addresses
nano /etc/network/interfaces
Modify the file so it looks like this:
auto eth0
iface eth0 inet static
address 10.0.0.10
netmask 255.255.255.0
gateway 10.0.0.1
then
/etc/init.d/networking restart
Top
Install MySQL server and client
apt-get install mysql-server mysql-client
Enter a MySQL root password when prompted.
Check that mysqld is running:
ps aux
Top
Install PHP5 and Nginx:
apt-get install php5-fpm php5-imap php5-mysql php5-mcrypt php5-intl nginx openssl ssl-cert
Make sure php5-fpm and nginx are running:
service php5-fpm start
service nginx start
Create folders to store web files:
mkdir /home/clients_ssl
mkdir /home/clients_ssl/MyDomain.net
mkdir /home/clients_ssl/MyDomain.net/logs
mkdir /home/clients_ssl/MyDomain.net/tmp
mkdir /home/clients_ssl/MyDomain.net/www
Create nginx config for this site:
nano /etc/nginx/sites-available/MyDomain.net_ssl
Replace and MyDomain.net with your info:
server {
listen 10.0.0.10:443;
server_name MyDomain.net;
ssl on;
ssl_certificate /etc/nginx/certs/MyDomain.net.combined.crt;
ssl_certificate_key /etc/nginx/certs/MyDomain.net.key;
root /home/clients_ssl/MyDomain.net/www;
index index.php index.html index.htm;
location ~ \.php$ {
fastcgi_pass unix:/etc/php5/fpm/socks/ssl_MyDomain.net.sock;
include fastcgi_params;
fastcgi_param HTTPS on;
}
location ~ /\. {
deny all;
}
access_log /home/clients_ssl/MyDomain.net/logs/access.log;
error_log /home/clients_ssl/MyDomain.net/logs/error.log;
error_page 404 /404.html;
}
Remove the default site and put your site online:
rm /etc/nginx/sites-available/default
ln -s /etc/nginx/sites-available/MyDomain.net_ssl /etc/nginx/sites-enabled/MyDomain.net_ssl
Create the certs folder.
mkdir /etc/nginx/certs
cd /etc/nginx/certs
openssl genrsa -des3 -out self-ssl.key 2048
openssl req -new -key self-ssl.key -out self-ssl.csr
You can remove passphrase from self-ssl.key for nginx server, enter:
cp -v self-ssl.{key,original}
openssl rsa -in self-ssl.original -out self-ssl.key
rm -v self-ssl.original
openssl x509 -req -days 4000 -in self-ssl.csr -signkey self-ssl.key -out self-ssl.crt
mv self-ssl.crt MyDomain.net.combined.crt
mv self-ssl.key MyDomain.net.key
Restart Nginx
service nginx restart
Create a php5-fpm config file:
nano /etc/php5/fpm/pool.d/ssl_MyDomain.net.conf
paste the following and replace MyDomain.net with your info:
[ssl_MyDomain.net]
listen = /etc/php5/fpm/socks/ssl_MyDomain.net.sock
user = u1005
group = g1005
pm = dynamic
pm.max_children = 50
pm.start_servers = 1
pm.min_spare_servers = 1
pm.max_spare_servers = 5
pm.max_requests = 0
php_admin_value[open_basedir]=/
php_admin_value[session.save_path]=/home/clients_ssl/MyDomain.net/tmp
php_admin_value[upload_tmp_dir]=/home/clients_ssl/MyDomain.net/tmp
php_admin_value[disable_functions]=dl
Create a user for this virtualhost:
groupadd -g 1005 g1005
useradd --no-create-home -g 1005 -u 1005 u1005
Create socks folder:
mkdir /etc/php5/fpm/socks
Remove the default virtualhost:
rm /etc/php5/fpm/pool.d/www.conf
Add timezone info to php ini file /etc/php5/fpm/php.ini:
date.timezone = Europe/London
Restart php5-fpm
service php5-fpm restart
Top
Install phpMyAdmin
cd /home/clients_ssl/MyDomain.net/www
wget 'http://downloads.sourceforge.net/project/phpmyadmin/phpMyAdmin/4.0.8/phpMyAdmin-4.0.8-english.tar.gz?use_mirror=netcologne'
mv phpMyAdmin-4.0.8-english.tar.gz\?use_mirror=netcologne pma.tar.gz
tar -zxvf pma.tar.gz
Hide pma or bots will try to hack into it:
mv phpMyAdmin-4.0.8-english mysql_admin
cd mysql_admin
cp config.sample.inc.php config.inc.php
Set the right owner for www and tmp folder:
cd /home/clients_ssl/MyDomain.net
chown -R 1005.1005 www tmp
Now you should be able to access pma at: https://MyDomain.net/mysql_admin/
Now open phpMyAdmin and click on ‘SQL’ on the top menubar. Paste the following SQL queries to create a database and user, replace as you see fit:
CREATE DATABASE postfix;
GRANT ALL PRIVILEGES ON postfix.* TO 'postfix_admin'@'%' IDENTIFIED BY '<password1>';
GRANT SELECT ON postfix.* TO 'postfix'@'%' IDENTIFIED BY '<password1>';
FLUSH PRIVILEGES;
Top
Install PostfixAdmin
Although you can install it from a standard Debian package I am going to download it directly instead so I can put it under my custom path immediately.
cd /home/clients_ssl/MyDomain.net/www
wget 'http://downloads.sourceforge.net/project/postfixadmin/postfixadmin/postfixadmin-2.3.6/postfixadmin-2.3.6.tar.gz?use_mirror=garr'
mv postfixadmin-2.3.6.tar.gz\?use_mirror=garr pfa.tar.gz
tar -zxvf pfa.tar.gz
mv postfixadmin-2.3.6 mail_admin
chown -R 1005.1005 mail_admin
cd mail_admin
sed -i 's/MyDomain.net/postfixadmin/g' config.inc.php
Now edit configuration file config.inc.php and change these values:
$CONF['configured'] = true;
$CONF['postfix_admin_url'] = 'https://MyDomain.net/postfixadmin';
$CONF['database_type'] = 'mysqli';
$CONF['database_host'] = 'localhost';
$CONF['database_user'] = 'postfix_admin';
$CONF['database_password'] = '<password1>';
$CONF['database_name'] = 'postfix';
$CONF['domain_path'] = 'YES';
$CONF['domain_in_mailbox'] = 'NO';
$CONF['fetchmail'] = 'NO';
Go to https://MyDomain.net/postfixadmin/setup.php
This setup script should create the necessary tables to postfix database.
At the bottom of setup.php enter your admin password and click ‘Gererate password hash’.
Edit config.inc.php and add the hash:
$CONF['setup_password'] = '';
Now enter superadmin account info. You can use this to access PostfixAdmin and configure domains, e-mail accounts, aliases etc.
Try to log in with the admin account here: https://MyDomain.net/postfixadmin
Top
Install Postfix and Sasl library
apt-get install postfix postfix-mysql libsasl2-modules libsasl2-modules-sql
When prompted, choose ‘Internet Site’.
Use your domain name as ‘System mail name’: MyDomain.net
Create virtual mail user and group:
groupadd -g 3000 vmail
useradd -d /home/vmail -m -u 3000 -g 3000 vmail
Edit /etc/postfix/main.cf and add the following lines:
mydestination = localhost
virtual_uid_maps = static:3000
virtual_gid_maps = static:3000
virtual_mailbox_base = /home/vmail
virtual_mailbox_domains = mysql:/etc/postfix/mysql_virtual_mailbox_domains.cf
virtual_mailbox_maps = mysql:/etc/postfix/mysql_virtual_mailbox_maps.cf
virtual_alias_maps = mysql:/etc/postfix/mysql_virtual_alias_maps.cf
relay_domains = mysql:/etc/postfix/mysql_relay_domains.cf
virtual_transport = lmtp:unix:private/dovecot-lmtp
smtpd_recipient_restrictions =
permit_mynetworks,
permit_sasl_authenticated,
reject_non_fqdn_hostname,
reject_non_fqdn_sender,
reject_non_fqdn_recipient,
reject_unauth_destination,
reject_unauth_pipelining,
reject_invalid_hostname
smtpd_sasl_auth_enable = yes
smtpd_sasl_security_options = noanonymous
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
milter_default_action = accept
Create the following files:
nano /etc/postfix/mysql_virtual_mailbox_domains.cf
then paste the following:
hosts = 127.0.0.1
user = postfix
password = <password1>
dbname = postfix
query = SELECT domain FROM domain WHERE domain='%s' and backupmx = 0 and active = 1
nano /etc/postfix/mysql_virtual_mailbox_maps.cf
then paste the following:
hosts = 127.0.0.1
user = postfix
password = <password1>
dbname = postfix
query = SELECT maildir FROM mailbox WHERE username='%s' AND active = 1
nano /etc/postfix/mysql_virtual_alias_maps.cf
then paste the following:
hosts = 127.0.0.1
user = postfix
password = <password1>
dbname = postfix
query = SELECT goto FROM alias WHERE address='%s' AND active = 1
nano /etc/postfix/mysql_relay_domains.cf
and paste the following:
hosts = 127.0.0.1
user = postfix
password = <password1>
dbname = postfix
query = SELECT domain FROM domain WHERE domain='%s' and backupmx = 1
nano /etc/postfix/sasl/smtpd.conf
and paste the following:
pwcheck_method: auxprop
mech_list: plain login
auxprop_plugin: sql
sql_engine: mysql
sql_hostnames: 127.0.0.1
sql_user: postfix
sql_passwd: <password1>
sql_database: postfix
sql_select: SELECT password FROM mailbox WHERE username = '%u@%r' AND active = 1
Add postfix user to sasl group:
adduser postfix sasl
Enable secure smtp ports, edit /etc/postfix/master.cf and uncomment:
submission inet n - - - - smtpd
-o syslog_name=postfix/submission
-o smtpd_tls_security_level=encrypt
-o smtpd_sasl_auth_enable=yes
-o smtpd_client_restrictions=permit_sasl_authenticated,reject
-o milter_macro_daemon_name=ORIGINATING
smtps inet n - - - - smtpd
-o syslog_name=postfix/smtps
-o smtpd_tls_wrappermode=yes
-o smtpd_sasl_auth_enable=yes
-o smtpd_client_restrictions=permit_sasl_authenticated,reject
-o milter_macro_daemon_name=ORIGINATING
Configure Gmail smart host relay:
Add this bit to /etc/postfix/main.cf
#Smart host relay stuff
relayhost = [smtp.gmail.com]:587
smtp_use_tls=yes
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/relay_passwd
smtp_sasl_security_options = noanonymous
create password map: /etc/postfix/relay_passwd
[smtp.gmail.com]:587 address@gmail.com:gmailpassword
Then issue this command:
Postmap /etc/postfix/relay_passwd
For instructions how to setup a SMTP relay with GMX.net see this post
Top
Install Dovecot
apt-get install dovecot-imapd dovecot-pop3d dovecot-mysql dovecot-lmtpd
Create file /etc/dovecot/dovecot-mysql.conf.ext:
driver = mysql
connect = host=127.0.0.1 dbname=postfix user=postfix password=<password1>
default_pass_scheme = MD5-CRYPT
user_query = SELECT '/home/vmail/%d/%n' as home, 3000 AS uid, 3000 AS gid FROM mailbox WHERE username = '%u'
password_query = SELECT password FROM mailbox WHERE username = '%u'
Edit /etc/dovecot/conf.d/10-auth.conf
disable_plaintext_auth = no
auth_mechanisms = plain login
#!include auth-system.conf.ext
!include auth-sql.conf.ext
Edit /etc/dovecot/conf.d/10-mail.conf
mail_location = maildir:/home/vmail/%d/%n:INDEX=/home/vmail/%d/%n/indexes
Edit /etc/dovecot/conf.d/10-ssl.conf
ssl = yes
Edit /etc/dovecot/conf.d/20-imap.conf
mail_max_userip_connections = 10
Edit /etc/dovecot/conf.d/auth-sql.conf.ext
passdb {
driver = sql
# Path for SQL configuration file, see example-config/dovecot-sql.conf.ext
args = /etc/dovecot/dovecot-mysql.conf.ext
}
userdb {
driver = sql
args = /etc/dovecot/dovecot-mysql.conf.ext
}
Edit /etc/dovecot/conf.d/10-master.conf
service lmtp {
unix_listener /var/spool/postfix/private/dovecot-lmtp {
mode = 0600
user = postfix
group = postfix
}
}
service auth {
# Postfix smtp-auth
unix_listener /var/spool/postfix/private/auth {
mode = 0666
}
}
Restart services:
service dovecot restart
service postfix restart
You can now add a domain with PostfixAdmin and test your e-mail server.
Any errors are found in logfiles:
/var/log/auth.log
/var/log/mail.log
/var/log/syslog
Top
Install Milters
apt-get install milter-greylist spamass-milter
spamass-milter:
Edit /etc/default/spamass-milter:
Add ‘-m’ so it won’t change the subject header.
Add ‘-r -1′ so Postfix rejects what SpamAssassin flags as spam.
Add ‘-l’ to avoid scanning e-mails sent by logged in users.
OPTIONS="-u spamass-milter -i 127.0.0.1 -m -r -1 -I"
Restart milter:
service spamass-milter restart
Add a dedicated user for SpamAssassin daemon:
adduser --shell /bin/false --home /var/lib/spamassassin --disabled-password --disabled-login --gecos "" spamd
Edit /etc/default/spamassassin:
ENABLED=1
OPTIONS="--create-prefs --max-children 5 --helper-home-dir=/var/lib/spamassassin -u spamd -g spamd"
CRON=1
Update rules and restart the daemon:
sa-update
service spamassassin restart
Tell Postfix to use new milter:
postconf -e 'smtpd_milters =unix:/spamass/spamass.sock'
postfix reload
Top
milter-greylist:
Edit /etc/milter-greylist/greylist.conf:
# For sendmail use the following two lines
#socket "/var/run/milter-greylist/milter-greylist.sock"
#user "smmsp"
# For Postfix uncomment the following two lines and comment out the
# sendmail ones above.
socket "/var/spool/postfix/milter-greylist/milter-greylist.sock" 660
user "greylist"
Edit /etc/default/milter-greylist:
ENABLED=1
SOCKET="/var/spool/postfix/milter-greylist/milter-greylist.sock"
Make a folder for the socket and restart milter:
mkdir /var/spool/postfix/milter-greylist
chmod 2755 /var/spool/postfix/milter-greylist
chown greylist:postfix /var/spool/postfix/milter-greylist
service milter-greylist restart
Tell Postfix to use the new milter:
postconf -e 'milter_connect_macros = i b j _ {daemon_name} {if_name} {client_addr}'
postconf -e 'smtpd_milters = unix:/milter-greylist/milter-greylist.sock, unix:/spamass/spamass.sock'
postfix reload
Top
Install RoundCube
wget 'http://downloads.sourceforge.net/project/roundcubemail/roundcubemail/0.9.5/roundcubemail-0.9.5.tar.gz?use_mirror=heanet'
mv roundcubemail-0.9.5.tar.gz\?use_mirror=heanet roundcubemail-0.9.5.tar.gz
tar -zxvf roundcubemail-0.9.5.tar.gz
mv roundcubemail-0.9.5 webmail
chown -R 1005.1005 webmail
mysql -r -p
then:
CREATE DATABASE roundcube;
GRANT ALL PRIVILEGES ON roundcube.* TO roundcube@localhost IDENTIFIED BY '<password9>';
FLUSH PRIVILEGES;
Add initial tables and data:
cd /home/clients_ssl/MyDomain.net/www/webmail
mysql -u roundcube -p < SQL/mysql.initial.sql
Go to https://MyDomain.net/webmail/installer/
See that your environment is ok and click Next.
On the ‘Create config’ page, you may want to change the following values:
product_name: Webmail
support_url:
database name: roundcube
database password: <password9>
default_host: localhost
smtp_server: localhost
language: en_US
Click Continue
Change the value in textarea to:
$rcmail_config['use_https'] = true;
Then copy all from textarea and paste the contents to main.inc.php and db.inc.php under /home/clients_ssl/MyDomain.net/www/webmail/config
Remove installer folder:
mv installer ../../
Open RoundCube at https://MyDomain.net/webmail/
Top
Testing
Testing delivery
If you’d like to test out your setup, you can use the following:
apt-get install swaks
swaks --to=someone@example.com --server=localhost
ls -l /home/vmail/example.com/someone/new # You should see the delivered mail there.
Testing mail retrieval with Dovecot
And now we should be able to test a connection. There’s no quick and handy tool for testing this out, so brace yourselves: we’ll have to make an IMAP session manually (it’s not very complicated, really). From your computer, or another point which should have acces to the IMAP server:
openssl s_client -connect youhost.domain:143 -starttls imap
[... you'll see a bunch of info about the SSL certificate bein printed out]
a login someone@example.com something
[... list of capabilities]
a select inbox
[... if you did at least one test in the last step, you should see 1 EXISTS in the output]
a fetch 1 rfc822.text
[... message body]
a logout
Let’s try logging in without encryption to see if we get through (we shouldn’t !). If the server responds that login was successful there’s a problem somewhere.
telnet localhost 143
. login someone@example.com something
* BAD [ALERT] Plaintext authentication not allowed without SSL/TLS, but your client did it anyway. If anyone was listening, the password was exposed.
. logout
Testing the spam filter
For testing this, we’ll use the special string from GTUBE. This string triggers a special rule that sets the score to a very high value so that SpamAssassin flags the mail as spam without a doubt.
On your computer, download a text file with the GTUBE signature line and use it as the body of a test email:
wget -O /tmp/gtube.txt https://spamassassin.apache.org/gtube/gtube.txt swaks --from some_existing@email.address --to=someone@example.com --server=your.domain --body=/tmp/gtube.txt
The email should be blocked with the message:
<** 550 5.7.1 Blocked by SpamAssassin
Testing Greylist
If you want to have a better understanding of what’s going on, you can comment out the line “quiet” in /etc/milter-greylist/greylist.conf
. This will make the rejection message specify how much time is left for the greylisting.
Send an e-mail to a user on the server in the same manner as you did above when testing delivery, although for this test you’ll need to send the e-mail from another computer (since we whitelisted 127.0.0.1):
swaks --to=someone@example.com --server=your.host
You should get rejected with a message looking like this:
<** 451 4.7.1 Greylisting in action, please come back in 00:23:33
Wait until that period is elapsed and re-try sending your mail. You should now be accepted.
Make sure you go back to the config file and comment out the “quiet” line again so that you don’t tell spammers how much time they need to wait.
Top
WordPress
nano /etc/nginx/sites-available/MyDomain.net_ssl
and edit it so it looks like the one below:
#Wordpress stuff
# Upstream to abstract backend connection(s) for php
upstream php {
server unix:/tmp/php-cgi.socket;
server 127.0.0.1:9000;
}
#End WordPress stuff
server {
listen 10.0.0.10:443;
server_name MyDomain.net;
ssl on;
ssl_certificate /etc/nginx/certs/MyDomain.net.combined.crt;
ssl_certificate_key /etc/nginx/certs/MyDomain.net.key;
root /home/clients_ssl/MyDomain.net/www;
index index.php index.html index.htm;
location ~ \.php$ {
fastcgi_pass unix:/etc/php5/fpm/socks/ssl_MyDomain.net.sock;
include fastcgi_params;
fastcgi_param HTTPS on;
#Wordpress stuff
#NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini
fastcgi_intercept_errors on;
fastcgi_pass php;
#End WordPress stuff
}
location ~ /\. {
deny all;
}
##Wordpress stuff
location = /favicon.ico {
log_not_found off;
access_log off;
}
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
location / {
# This is cool because no php is touched for static content.
# include the "?$args" part so non-default permalinks doesn't b$
try_files $uri $uri/ /index.php?$args;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
expires max;
log_not_found off;
}
#End WordPress stuff
access_log /home/clients_ssl/MyDomain.net/logs/access.log;
error_log /home/clients_ssl/MyDomain.net/logs/error.log;
error_page 404 /404.html;
}
Download and Extract WordPress
I will be setting up two blogs: one called: blog1, second: blog2
Download the latest wordpress source files then extract the downloaded archive:
cd /home/client_ssl/MyDomain.net/www/
wget http://wordpress.org/latest.tar.gz
tar -xvzf latest.tar.gz
mv wordpress blog1
tar -xvzf latest.tar.gz
mv wordpress blog1
Configure MySQL
The next step is to configure MySQL for use with WordPress. WordPress needs a database and user, and permissions set correctly for access. Start the mysql config:
mysql -u root -p
Enter the root MySQL password, then arrive at the mysql prompt
mysql>
Create a database for WordPress, create a user for the database, and set password set the permissions:
CREATE DATABASE wordpress_blog1;
CREATE DATABASE wordpress_blog2;
CREATE USER 'wordpressuser'@'localhost' IDENTIFIED BY '<password5>';
grant all privileges on wordpress_blog1.* to wordpressuser@localhost with grant option;
grant all privileges on wordpress_blog2.* to wordpressuser@localhost with grant option;
FLUSH TABLES;
exit
test the credentials:
mysql -u wordpressuser -p
Now create a new wordpress configuration based on the sample:
sudo cp /var/www/wordpress/wp-config-sample.php /var/www/wordpress_blog1/wp-config.php
And open it up for edit in an editor:
sudo nano /var/www/wordpress_blog1/wp-config.php
Repeat the step for blog2.
Change this:
/** The name of the database for WordPress */
define(‘DB_NAME’, ‘database_name_here’);
/** MySQL database username */
define(‘DB_USER’, ‘username_here’);
/** MySQL database password */
define(‘DB_PASSWORD’, ‘password_here’);
to this (substituting password_here with your own from above):
/** The name of the database for WordPress */
define(‘DB_NAME’, ‘wordpress_blog1’);
/** MySQL database username */
define(‘DB_USER’, ‘wordpressuser’);
/** MySQL database password */
define(‘DB_PASSWORD’, ‘<password5>’);
and do the same thing for the second blog.
To test the go to
https://MyDomain.net/blog1
Top
Install FTP
apt-get install proftpd
mkdir /etc/proftpd
cd /etc/proftpd
openssl req -new -x509 -days 4000 -nodes -out ftpd-rsa.pem -keyout ftpd-rsa-key.pem
nano proftpd.conf
and add
<IfModule mod_tls.c>
TLSEngine on
TLSLog /var/log/proftpd-tls.log
TLSProtocol TLSv1
# Are clients required to use FTP over TLS when talking to this server?
TLSRequired off
TLSRSACertificateFile /etc/proftpd/ftpd-rsa.pem
TLSRSACertificateKeyFile /etc/proftpd/ftpd-rsa-key.pem
# Authenticate clients that want to use FTP over TLS?
TLSVerifyClient off
</IfModule>
Then
Service proftpd restart
Top
Install Webmin and dependencies
sudo apt-get install libapt-pkg-perl libnet-ssleay-perl libauthen-pam-perl libio-pty-perl apt-show-versions
wget http://prdownloads.sourceforge.net/webadmin/webmin_1.590_all.deb
sudo dpkg --install webmin_1.590_all.deb
Once webmin is install you should be able to access it via: https://MyDomain.net:10000 login with your linux account and if necessary update to the latest available version from the main screen.
Top
Password protect Nginx folder
Now I create admin folder where I will host my website with links to admin consoled. But first let’s set it up:
mkdir /home/clients_ssl/MyDomain.net/www/admin/
Cd /etc/nginx/sites-available
nano MyDomain.net_ssl
and add:
location ~ /admin {
root /home/clients_ssl/MyDomain.net/www;
auth_basic "Restricted";
auth_basic_user_file /path/to/your/htpass/file/htpasswd;
}
Passwords must be encoded by the crypt(3) function. I used a web based utility to generate my htpasswd file http://www.tools.dynamicdrive.com/password/
Top