PostgreSQL+Postfix+Nginx+PHP+RoundCube+Dovecot+ SpamAssassin+Clamav+Spamd

When Installing software I will use portmaster from ports-mgmt/portmaster

Goal of this howto (unsorted):
  • configure mail server that will handle virtual mailboxes, virtual domains and/or relay mail to other hosts.
  • configure webmail
  • let users to connect to their accounts using imaps protocol
  • let user to send mail using smtps protocol
  • configure antivirus and spam filtering software
  • use only OpenSource software available in FreeBSD ports
  • where possible/nessacery encrypt connections with OpenSSL
  • use static uid:gid for mail
  • store mail in /mail/[red]domain[/red]/[red]username[/red]
  • use sockets where possible
  • [red]configure my own mailserver[/red]

Table Of Content

Preparing system
Install PostgreSQL
Install & configure Dovecot
Install & configure Postfix
Install & configure nginx
Install & configure RoundCube
Configure PostgreSQL
Configure PHP
Install and Configure Spamd
Install and Configure Clamav
Install and Configure SpamAssassin
Checking if it works
References
Change log

I've left some reserved posts for future use {perhaps security related or something else}
 
Preparing system

I'll use GENERIC kernel
To be able to run PostgreSQL in jail you need to {you may/will need to tune these values to your needs}
Code:
# cat < EOF >> [file]/boot/loader.conf[/file]
kern.ipc.semmni=40
kern.ipc.semmns=240
kern.ipc.seumr=40
kern.ipc.semmnu=120
EOF
# echo 'security.jail.sysvipc_allowed=1' >> [file]/etc/sysctl.conf[/file]

and
Code:
# echo 'jail_sysvipc_allow="YES"' >> [file]/etc/rc.conf[/file]


Sine we'll be using jail, we also want to use nullfs
Code:
# echo 'nullfs_load="YES"' >> [file]/boot/loader.conf[/file]


now reboot your FreeBSD, and create new jails, start it and jexec to it.
[red]Everything below will be executed in jail[/red]

Now in your jail
By default OpenSSL from Base system will create 1024 bit RSA keys, I don't like that, It's not enough.
So in /etc/ssl/openssl.cnf
find and set
Code:
default_bits            = 4096
 
Install PostgreSQL

Install and start postgresql
Code:
# portmaster [port]databases/postgresql84-server[/port]
# echo 'postgresql_enable="YES"' >> [file]/etc/rc.conf[/file]
# /usr/local/etc/rc.d/postgresql initdb
# /usr/local/etc/rc.d/postgresql start
 
Install & configure Dovecot

Install dovecot
Code:
# portmaster [port]mail/dovecot[/port]
make sure to select SSL, PGSQL

Create SSL/TLS certificate for secure connections:
Code:
# mkdir -p /etc/ssl/dovecot
# cd /etc/ssl/dovecot
# openssl req -new -x509 -nodes -out cert.pem -keyout key.pem -days 365
# chmod 640 /etc/ssl/dovecot/*
more info: http://forums.freebsd.org/showthread.php?t=6490

Edit /usr/local/etc/dovecot.conf
Code:
protocols = imaps
disable_plaintext_auth = no

ssl = yes
ssl_cert_file = /etc/ssl/dovecot/cert.pem
ssl_key_file = /etc/ssl/dovecot/key.pem

mail_privileged_group = mail
dotlock_use_excl = yes
mail_location = maildir:/mail/%d/%n
verbose_proctitle = yes

# mailnull user id is 26
first_valid_uid = 26
last_valid_uid = 26
mail_uid = mailnull

# mail goup id is 6
first_valid_gid = 6
last_valid_gid = 6
mail_gid = mail

maildir_copy_with_hardlinks = yes

protocol imap {
  imap_client_workarounds = delay-newmail netscape-eoh tb-extra-mailbox-sep
  mail_plugins = quota imap_quota
}

protocol managesieve {

}

protocol lda {
  postmaster_address = postmaster@example.com
  sendmail_path = /usr/sbin/sendmail
  mail_plugins = quota
}

auth_username_format = %Lu

auth default {
  mechanisms = plain

  passdb sql {
    args = /usr/local/etc/dovecot-sql.conf
  }
  userdb prefetch {
    # keep this, otherwise quota won't work
  }

  user = root

  socket listen {
    master {
      path = /var/run/dovecot/auth-master
      mode = 0600
      user = mailnull
      group = mail
    }
    client {
      path = /var/run/dovecot/auth-client
      mode = 0660
        user = postfix
        group = mail
    }
  }

}

dict {

}

plugin {
  quota = maildir:User quota
  quota_rule = *:storage=1GB
}

Edit /usr/local/etc/dovecot-sql.conf
Code:
driver = pgsql
connect = host=/tmp dbname=mail user=dovecot password=DovecotPassword
default_pass_scheme = PLAIN-MD5

password_query = \
  SELECT username, domain, password, '*:bytes=' || quota || '[red]M[/red]' AS userdb_quota_rule \
  FROM mailbox WHERE username = '%n' AND domain = '%d' AND active = true
M in red means that SQL query will return quota in Megabytes (Consider it as modifier)

TIP: host can be IP, hostname of path to PostgreSQL socket

Enable dovecot at jail startup
Code:
# echo 'dovecot_enable="YES"' >> [file]/etc/rc.conf[/file]

NOTE: you may want to install mail/dovecot-sieve, it will help you to automatically move different mails to different folders :)
 
Install & configure Postfix

Stop sendmail
Code:
# /etc/rc.d/sendmail stop

Install postfix
Code:
# portmaster [port]mail/postfix[/port]
...
Added group "postfix".
Added group "maildrop".
Added user "postfix".
You need user "postfix" added to group "mail".
Would you like me to add it [y]? [red][b]y[/b][/red]
...
Would you like to activate Postfix in /etc/mail/mailer.conf [n]? [red][b]y[/b][/red]
make sure to select DOVECOT, TLS, PGSQL, VDA

Make system use postfix instead of sendmail
Code:
# cat < EOF >> [file]/etc/rc.conf[/file]
sendmail_enable="NO"
sendmail_submit_enable="NO"
sendmail_outbound_enable="NO"
sendmail_msp_queue_enable="NO"
EOF
# cat < EOF >> [file]/etc/periodic.conf[/file]
daily_clean_hoststat_enable="NO"
daily_status_mail_rejects_enable="NO"
daily_status_include_submit_mailq="NO"
daily_submit_queuerun="NO"
EOF

Create and secure the SMTP SSL certificate:
Code:
# mkdir -p /etc/ssl/postfix
# cd /etc/ssl/postfix
# openssl req -new -x509 -nodes -out smtpd.pem -keyout smtpd.pem -days 365
# chmod 640 /etc/ssl/postfix/*
# chgrp -R postfix /etc/ssl/postfix

edit red test in /usr/local/etc/postfix/main.cf
Code:
...
# SOFT BOUNCE
#
# The soft_bounce parameter provides a limited safety net for
# testing.  When soft_bounce is enabled, mail will remain queued that
# would otherwise bounce. This parameter disables locally-generated
# bounces, and prevents the SMTP server from rejecting mail permanently
# (by changing 5xx replies into 4xx replies). However, soft_bounce
# is no cure for address rewriting mistakes or mail routing mistakes.
#[red]

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,
  reject_rbl_client bl.spamcop.net

smtpd_sender_restrictions = permit_sasl_authenticated, permit_mynetworks


virtual_mailbox_base = /mail
virtual_mailbox_maps = pgsql:/usr/local/etc/postfix/pgsql_virtual_mailbox_maps.cf
virtual_mailbox_domains = pgsql:/usr/local/etc/postfix/pgsql_virtual_mailbox_domains.cf
virtual_alias_maps = pgsql:/usr/local/etc/postfix/pgsql_virtual_alias_maps.cf
local_recipient_maps = $virtual_mailbox_maps
virtual_create_maildirsize = yes
virtual_mailbox_extended = yes

[b]# I use static uid:gid, dynamic ones caused problems for me {permission related}[/b]
virtual_uid_maps = static:26
virtual_gid_maps = static:6

virtual_transport = dovecot

smtpd_delay_reject = yes
smtpd_helo_required = yes


broken_sasl_auth_clients = yes
smtpd_sasl_auth_enable = yes
smtpd_sasl_type = dovecot
smtpd_sasl_path = /var/run/dovecot/auth-client
smtpd_sasl_security_options = noanonymous


smtp_use_tls = yes
smtpd_use_tls = yes
smtp_tls_note_starttls_offer = yes
smtpd_tls_key_file = /etc/ssl/postfix/smtpd.pem
smtpd_tls_cert_file = /etc/ssl/postfix/smtpd.pem
smtpd_tls_CAfile = /etc/ssl/postfix/smtpd.pem
smtpd_tls_loglevel = 0
smtpd_tls_received_header = yes
smtpd_tls_session_cache_timeout = 3600s
tls_random_source = dev:/dev/urandom
[/red]
...
myhostname = [red]example.com[/red]
...
mydomain = [red]example.com[/red]
...
mydestination = [red]localhost.$mydomain, localhost[/red]
...
#relay_domains = pgsql:/usr/local/etc/postfix/pgsql_relay_domains.cf
...


uncomment this in /usr/local/etc/postfix/master.cf
Code:
[red]smtps     inet  n       -       n       -       -       smtpd
  -o smtpd_tls_wrappermode=yes
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject[/red]
  -o message_size_limit=26214400
message_size_limit will change message size limit from 10240000B (default) to 25M

and add this to /usr/local/etc/postfix/master.cf
Code:
dovecot    unix -        n       n       -       -       pipe
  flags=DRhu user=mailnull:mail argv=/usr/local/libexec/dovecot/deliver -f ${sender} -d ${user}@${nexthop} -n -m ${extension}



/usr/local/etc/postfix/pgsql_relay_domains.cf
Code:
user = postfix
password = postfix_password
hosts = /tmp
dbname = mail
query = SELECT domain FROM mailbox_relay_domains WHERE domain = '%s' AND active = true

/usr/local/etc/postfix/pgsql_virtual_alias_maps.cf
Code:
user = postfix
password = postfix_password
hosts = /tmp
dbname = mail
query = SELECT dest_username || '@' || dest_domain FROM mailbox_aliases WHERE address = '%s' AND active = true

/usr/local/etc/postfix/pgsql_virtual_mailbox_domains.cf
Code:
user = postfix
password = postfix_password
hosts = /tmp
dbname = mail
query = SELECT domain FROM mailbox WHERE domain = '%s' AND active = true

/usr/local/etc/postfix/pgsql_virtual_mailbox_limit_maps.cf
Code:
user = postfix
password = postfix_password
hosts = /tmp
dbname = mail
query = SELECT quota FROM mailbox WHERE username = '%u' AND domain = '%d' AND active = true

/usr/local/etc/postfix/pgsql_virtual_mailbox_maps.cf
Code:
user = postfix
password = postfix_password
hosts = /tmp
dbname = mail
query = SELECT domain || '/' || username FROM mailbox WHERE username = '%u' AND domain = '%d' AND active = true
TIP: as host you can use IP, hostname or path to postgreSQL socket

Secure Postfix’s PGSQL files:
Code:
# chmod 640 /usr/local/etc/postfix/pgsql_*
# chgrp postfix /usr/local/etc/postfix/pgsql_*


Create our virtual mail directories:
Code:
# mkdir /mail
# chown mailnull:mail /mail


Enable postfix at jail startup
Code:
echo 'postfix_enable="YES"' >> [file]/etc/rc.conf[/file]
 
Install & configure nginx

Install nginx and spawn_fcgi
Code:
# portmaster [port]www/nginx[/port] [port]www/spawn-fcgi[/port]

enable service
Code:
# cat < EOF >> [file]/etc/rc.conf[/file]
nginx_enable="YES"
spawn_fcgi_enable="YES"
spawn_fcgi_bindaddr=""
spawn_fcgi_bindport=""
spawn_fcgi_bindsocket="/var/run/spawn_fcgi.socket"
spawn_fcgi_bindsocket_mode="0700"
EOF
this will start spawn_fcgi with socket, instead of listening to some port

/usr/local/etc/nginx/nginx.conf
Code:
#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;



    server {
[B]        # This will redirect http to https :D[/B]
        listen       80;
        server_name example.com;
        location / {
            rewrite ^ https://example.com/$request_uri? permanent;
        }
    }


    server {
        listen       443;
        server_name  example.com;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            root   /usr/local/www/roundcube;
            index  index.php;
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /usr/local/www/nginx-dist;
        }

               location ~ \.php$ {
            root           /usr/local/www/roundcube;
            fastcgi_pass   unix:/var/run/spawn_fcgi.socket;
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME  /usr/local/www/roundcube/$fastcgi_script_name;
            include        fastcgi_params;
        }

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #    deny  all;
        #}

        ssl                  on;
        ssl_certificate      /etc/ssl/www/www.pem;
        ssl_certificate_key  /etc/ssl/www/www.pem;
        ssl_session_timeout  5m;
        ssl_protocols  SSLv2 SSLv3 TLSv1;
        ssl_ciphers  ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
        ssl_prefer_server_ciphers   on;

        client_max_body_size 25m;
    }

}
This will listen on https port, it's a good idea to make another host to listen on port 80, and redirect to https

client_max_body_size 25m; this will set max body size to 25M which means, that you can upload up to 25M per file. It's important to set this variable, not only in php.ini {see Configure PHP section below

TIP: you can install nginx+RoundCube+PHP in different jail. I think this would add extra security layer to mail server. One thing I dislike about RoundCube is that It can't connect to db via socket
 
Install and configure RoundCube

Install
Code:
portmaster [port]mail/roundcube[/port]
Make sure to select PGSQL, SSL
When asked for php5 config make sure to select FASTCGI

in /usr/local/www/roundcube/config/db.inc.php set
Code:
[b]# you should be able to use hostname instead of IP as well
# unfortunatly it's not possible to specify socket :([/b]
$rcmail_config['db_dsnw'] = 'pgsql://roundcube:[red]RoundcubePassword[/red]@123.123.123.123/mail'

in /usr/local/www/roundcube/config/main.inc.php set
Code:
$rcmail_config['log_driver'] = 'syslog';
$rcmail_config['force_https'] = TRUE;
$rcmail_config['default_host'] = 'ssl://example.com';
$rcmail_config['default_port'] = 993;
$rcmail_config['imap_auth_type'] = auth;
$rcmail_config['username_domain'] = 'example.com';
$rcmail_config['mail_domain'] = 'example.com';
$rcmail_config['smtp_server'] = 'ssl://example.com';
$rcmail_config['smtp_port'] = 465;
$rcmail_config['smtp_user'] = '%u';
$rcmail_config['smtp_pass'] = '%p';
$rcmail_config['sendmail_delay'] = 20;
$rcmail_config['session_lifetime'] = 30;
[b]// set some random password[/b]
$rcmail_config['des_key'] = 'SomeRandom24charPassword';
$rcmail_config['language'] = lv_LV;
$rcmail_config['product_name'] = 'Example.com :)';
$rcmail_config['create_default_folders'] = TRUE;
$rcmail_config['default_charset'] = 'ISO-8859-13';
$rcmail_config['identities_level'] = 1;
$rcmail_config['max_pagesize'] = 100;
$rcmail_config['log_logins'] = true;
[b]// this doesn't seam to work.... perhaps a bug...[/b]
$rcmail_config['timezone'] = 'Europe/Riga';
$rcmail_config['show_images'] = 1;
[b]// most average users probably prefer html emails[/b]
$rcmail_config['htmleditor'] = TRUE;
to get info what these variables means read config file, it's pretty well commented
 
Configure PostgreSQL

PostreSQL configuration for RoundCube
Code:
# su pgsql
$ psql template1
-- create users
CREATE USER postfix ENCRYPTED password 'PostfixPassword';
CREATE USER dovecot ENCRYPTED password 'DovecotPassword';
CREATE ROLE mailman WITH USER postfix, dovecot;
-- create group
CREATE DATABASE mail OWNER mailman;

\c mail

-- virtual mailboxes
CREATE TABLE mailbox (
	username	VARCHAR(128) NOT NULL,
	domain		VARCHAR(128) NOT NULL,
	password	CHAR(32) NOT NULL,
	quota		INTEGER DEFAULT 1024 NOT NULL,
	active		BOOLEAN DEFAULT true NOT NULL,
	PRIMARY KEY (username, domain)
);

-- virtual mailbox aliases
CREATE TABLE mailbox_aliases (
	address		VARCHAR(256) PRIMARY KEY,
	dest_username	VARCHAR(128) NOT NULL,
	dest_domain	VARCHAR(128) NOT NULL,
	active		BOOLEAN DEFAULT true NOT NULL,
	FOREIGN KEY (dest_username, dest_domain) REFERENCES mailbox (username, domain) ON DELETE CASCADE
);

-- relay domains
CREATE TABLE mailbox_relay_domains (
	domain		VARCHAR(256) PRIMARY KEY,
	active		BOOLEAN DEFAULT true NOT NULL
);



-- grant permissions
GRANT SELECT ON mailbox TO dovecot;
GRANT SELECT ON mailbox,mailbox_aliases,mailbox_relay_domains TO postfix;

-- create virtual domain
-- currently virtual domains doesn't work for me
INSERT INTO domains_relay_domains VALUES ('example.com');

-- create user
INSERT INTO mailbox VALUES ('test','example.com','MD5 Hash of password');

-- create virtual aliases
INSERT INTO mailbox_aliases VALUES ('postmaster@example.com', 'test', 'example.com');
INSERT INTO mailbox_aliases VALUES ('root@example.com', 'test', 'example.com');
INSERT INTO mailbox_aliases VALUES ('example@example.com', 'test', 'example.com');
INSERT INTO mailbox_aliases VALUES ('abuse@example.com', 'test', 'example.com');

-- create roundcube and database
CREATE USER roundcube ENCRYPTED password 'RoundcubePassword';
CREATE DATABASE webmail WITH OWNER roundcube;
\c - roundcube

-- create tables etc for roundcube
\i /usr/local/www/roundcube/SQL/postgres.initial.sql

-- exit psql
\q


in /usr/local/pgsql/data/pg_hba.conf you need to set
who is allowed access postgresql and from where. This file is pretty well
written so, you should read it yourself :)

Add this entry
Code:
local   mail        postfix,dovecot                        md5
host    webmail     roundcube           [red]jail_IP[/red]/32         md5



If postfix and dovecot connects to database over net, then
Code:
host    mail        postfix,dovecot     [red]jail_IP[/red]/32         md5
host    webmail     roundcube           [red]jail_IP[/red]/32         md5
also for db security I suggest you change password for pgsql user and set password authentication for everything and everyone form anywhere :)
 
Configure PHP

To attach files in webmail interface (roundcube) we need to configure php to allow uploading bigger files {by default it's about 2MB, which is very, very small}

copy example config file
Code:
# cp /usr/local/etc/php.ini-recommended /usr/local/etc/php.ini

edit variables in /usr/local/etc/php.ini
Code:
; Maximum size of POST data that PHP will accept.
post_max_size = 25M

; Maximum allowed size for uploaded files.
upload_max_filesize = 20M

; Maximum number of files that can be uploaded via a single request
max_file_uploads = 10

EDIT: after editing /usr/local/etc/php.ini if spawn-fcgi is started, you need to restart it, for new setting to take effect
Code:
# /usr/local/etc/rc.d/spawn-fcgi restart
 
[red]This section must be done on host system, not in jail[/red]

Install and configure spamd (common)
Code:
# portmaster [port]mail/spamd[/port]

Code:
# cat < EOF >> [file]/etc/rc.conf[/file]
obspamlogd_enable="YES"
obspamd_enable="YES"
obspamd_flags="-l IP_mail_server -h example.com"
EOF
-l is optional, if you omit it, spamd will listen on all aliased IP's AFAIK

enable and configure pf { this one you figure out yourself :) }

you need to mount fdescfs to /dev/fs for graylisting to work.
Code:
# echo 'fdescfs /dev/fd fdescfs rw 0 0' >> [file]/etc/fstab[/file]

And you need to create spamd database
Code:
# touch /var/db/spamd
# chown _spamd:_spamd /var/db/spamd
# chmod ug=rw,o= /var/db/spamd


Configure spamd (blacklisting)

to obspamd_flags in /etc/rc.conf add -b flag

I haven't yet figured out how exactly and does it work at all.
Run # crontab -e and add this
Code:
48      *       *       *       *       /usr/local/sbin/spamd-setup
This should fetch blacklists

to /etc/pf.conf add this
Code:
table <spamd> persist
rdr pass inet proto tcp from <spamd> to any port smtp -> 127.0.0.1 port spamd

# let spamd-setup update blacklist
pass out on $e_if0 inet proto tcp from [red]Host_IP[/red] to any port { spamd, spamd-cfg } keep state
pass out on $e_if0 inet proto udp from [red]Host_IP[/red] to any port spamd-sync keep state



Configure spamd (graylisting)

grayisting may have a one serious dissadvangate. It may delay mail for more than 35 minutes... In case of gmail, it can be even longer, because gmail may try to deliver mail from different servers. You can probably resolve this by whitelisting all gmail IP's {but I don't have such a list, unfortunately}

to /etc/pf.confadd this
Code:
table <spamd-white> persist
rdr pass inet proto tcp from !<spamd-white> to any port smtp -> 127.0.0.1 port spamd


TIP: you can combine bough methods :D also you can create your own whitelist and use it as well
 
Install and configure Clamav

install clamsmtp and clamav
Code:
# portmaster [port]security/clamav[/port] [port]security/clamsmtp[/port]

copy example config file
Code:
# cp /usr/local/etc/clamd.conf.default /usr/local/etc/clamd.conf
# cp /usr/local/etc/clamsmtpd.conf-sample /usr/local/etc/clamsmtpd.conf
# cp /usr/local/etc/freshclam.conf.default /usr/local/etc/freshclam.conf

Edit /usr/local/etc/clamd.conf
Code:
TemporaryDirectory /tmp
LocalSocket /var/run/clamav/clamd.socket
User clamav

Edit /usr/local/etc/clamsmtpd.conf the way you like
Code:
ClamAddress: /var/run/clamav/clamd.socket
Header: X-Virus-Scanned: ClamAV using ClamSMTP
TempDirectory: /tmp
Action: drop
Quarantine: off
User: clamav
make sure that clamsmtpd and clamd use same user and socket


Now you need to add fallowing to /usr/local/etc/postfix/master.cf
Code:
# AV scan filter (used by content_filter)
scan      unix  -       -       n       -       16      smtp
        -o smtp_send_xforward_command=yes
        -o smtp_enforce_tls=no
# For injecting mail back into postfix from the filter
[red]IP_of_jail[/red]:10026 inet  n -       n       -       16      smtpd
        -o content_filter=
        -o receive_override_options=no_unknown_recipient_checks,no_header_body_checks
        -o smtpd_helo_restrictions=
        -o smtpd_client_restrictions=
        -o smtpd_sender_restrictions=
        -o smtpd_recipient_restrictions=permit_mynetworks,reject
        -o mynetworks_style=host
        -o smtpd_authorized_xforward_hosts=[red]IP_of_jail[/red]

to /usr/local/etc/postfix/main.cf add
Code:
content_filter = scan:[[red]IP_of_jail[/red]]:10025


enable clamav, freshclam and clamsmtpd at startup
Code:
cat < EOF >> [file]/etc/rc.conf[/file]
clamsmtpd_enable="YES"
clamav_clamd_enable="YES"
clamav_freshclam_enable="YES"
clamav_freshclam_flags="--quiet -a jail_ip -c 24"
EOF
-c 12 means, that freshclam should update virus database every 2 hours (valid values 1-50)
 
Install and configure SpamAssassin

install
Code:
# portmaster mail/p5-Mail-SpamAssassin

Configure daemon startup
Code:
# cat < EOF >> [file]/etc/rc.conf[/file]
spamd_enable="YES"
spamd_flags="-c --socketpath=/var/run/SpamAssassin.socket"
EOF
This will make spamassassin daemon to listen on socket, instead of opening port

Code:
# mkdir /var/spool/mqueue/.spamassassin
# chown _spamd:_spamd /var/spool/mqueue/.spamassassin

now run sa-update
Code:
# sa-update

add this to /usr/local/etc/postfix/master.cf
Code:
spamassassin unix  -       n       n       -       -       pipe
   user=nobody argv=/usr/local/bin/spamc -u mailnull -U /var/run/SpamAssassin.socket -e /usr/local/sbin/sendmail -oi -f ${sender} ${recipient}

and edit this line in /usr/local/etc/postfix/master.cf
Code:
smtp      inet  n       -       n       -       -       smtpd
to
Code:
smtp      inet  n       -       n       -       -       smtpd -o content_filter=spamassassin

edit /usr/local/etc/mail/spamassassin/local.cf as needed.
Personally I set required_score to 4.0
 
Checking if it works

You might want to restart server, to check it everything is started :D

Postfix
send mail to some user that is registered in database, mail should be delivered to /mail/domain/user/...
you can check status of mail queue with # mailq
Also send mail from jail to some other email server and see if you can send mails

Dovecot
Try logging in to your server (imap) with some mail client
Here's example mutt config:
Code:
set imap_user = "user@example.com"
set folder = "imaps://example.com:993"
set postponed = "+Drafts"
set spoolfile = "+INBOX"
set record = "+Sent"

Roundcube
This is very simple, simply open example.com in browser, and try to log in. If you can't login with roundcube, but can login with some email client, then roundcube is configured incorrectly
After you have logged in you need to check if you can send emails. Send some email to other server (gmail for example). If Roundcube won't be able to connect to smtps, than it will show error.

SpamAssassin
If SpamAssassin works, in received mail headers you should see something like this:
Code:
X-Spam-Checker-Version: SpamAssassin 3.3.0 (2010-01-18) on bsdroot.lv
X-Spam-Level: *
X-Spam-Status: No, score=1.8 required=5.0 tests=MISSING_SUBJECT,
    TVD_SPACE_RATIO autolearn=no version=3.3.0

Clamav
If clamav works, in received mail headers you should see something like this:
Code:
X-Virus-Scanned: ClamAV using ClamSMTP

NOTE: you my want to create 1 or 2 common certificates, and use it by all services (depending on your configuration). Certificates cost money (at least certificates signed by CA).

NOTE: passwords in config files are save unencrypted, later I'll think how to save them encrypted



More and much more detailed info in references
 
OK, it seams I've finished this Howto.
Suggestions and error correction {i'm just a human} are welcomed :D

also don't forget that you don't need to fallow this howto step by step, you can improvize :D

Originally I wanted to use lighttpd but later changed to nginx. I did that because nginx is much easier to set up, because if you make error in config, it's easier to find it.

[red]Don't expect this tutorial to work out of the box. You will need to work yourself. I wrote this from what I could remember.[/red]
But since then, I've rebuild my mail server many times, and this tutorial helped me a lot. When I find stuff that doesn't glue well, I try to fix it. So it's not perfect... :)
 
Change log
2010-02-18: Added few lines in nginx.conf to redirect http to https
2010-02-22: Added 2x pf rules to smapd blacklist section. This fixes spamd-setup not being able to update blacklist
2010-02-23: Fixed many console commands (echo), Syntax was wrong, sorry
2010-02-26: Fixed SpamAssassin. Now it scans mail :D
2010-02-26: added Checking if it works section
2010-03-09: Improved PostgreSQL queries in "Configure PostgreSQL" section [didn't test, but they should work, anyway, if it doesn't let me know]
2010-03-09: Fixed may typos pointed to me by osx-addict
2010-03-10: Fixed typo in Postfix section (thanks to osx-addict. Fixed one PostgreSQL query in postfix section, to match updated PostgreSQL query
2010-04-10: in Install & configure Postfix section s/chmod mailnull:mail/chown mailnull:mail/
2010-05-31: Fixed SQL insert query bug in Configure PostgreSQL section pointed out by zloidemon on jabber. Fix in Install and configure Clamav section
2010-11-05: Add some missing info to SpamAssasin section. Some other fixes
2011-01-20: Remove relay_domains from /usr/local/etc/postfix/main.cf, it was not needed, as it serves different purpose. Fix spamd section: you need to create database manually
2011-01-22: Fix beginning of spamd section
 
Great howto.
Makes me try using nginx instead of apache.

One note however, something that i miss, user management!

regards,
Johan
 
Great how-to killasmurf. Ran into a couple things when starting spamassassin, on initial startup SA wants you to run sa-update first and I believe the command

# chown _spamd:_spamd /var/spool/mqueue/.spamassassin

should be

# chown mailnull:mailnull /var/spool/mqueue/.spamassassin

My error logs complained about SA not having permission to create or write bayes files inside the directory and I noticed in master.cf your starting SA with -u mailnull. I made the change and SA runs perfect.

Hopefully you could add a section on automatically moving mail marked as spam. I created a script that greps inboxes for spam flag for half hour but it'd be nice to have something do it in real time.

Great how-to.
 
I read the entire thing.. End to end.. However, it might be nice on each step to indicate what is in a jail vs not.. I think you're running postgres in a jail..

Also, in the section where you're installing spamd and related 'mail' tools, you mention that they should be in the host environment.. I ran an 'smtp' jail that had everything but the roundcube/apache combination -- so it had sendmail, spamd, clamav, spamasassin, dovecot, and a few milters to glue it together. Worked just fine.. I'll be working on setting up my jails again this evening after I get home and want to set things up like :

  • smtp.example.com : (sendmail or postfix), spamd, clamav, spamasassin, dovecot, maildir folders for mail
  • http://www.example.com : (apache or nginx), roundcube (in a non-public directory--must know exact URL - discourage hackers), other pages, forward to other virtual domains,etc.
  • db.example.com : postgres -- if I can get it to behave in a jail -- otherwise it will be back in host environment
  • host.example.com : host environment -- user accounts,etc.
  • dns.example.com : dns lookups (eventually)
  • proxy.example.com: privoxy proxy server (or better?) - eventually
 
killasmurf86 said:
Preparing system
....
[red]Everything below will be executed in jail[/red]
....

killasmurf86 said:
[red]This section should be done on host system, not in jail {at least I did this on host}[/red]

Install and configure spamd (common)
....

Did you miss ^^^ :D

Yes, having postfix on host system, has some advantages, that I discovered later.
I wanted all jails to send daily messages to postfix jail, host couldn't do it, because it had ip alias which matched destination :D then I moved postfix, dovecot and postgresql to host (I have single server)
However I'll probably move postgresql back to jail later.

Bare with me, I'm new to this. This is my first mailserver configuration :D
I gained a lot of knowledge while doing this, however there are still many things I don't know, especially related to security. :D
 
Thanks for the update.. I did see the few notes about what was in jails but was looking for something (perhaps at the top) outlining the game plan (e.g. host + 4 jails with what is in each jail) or similar..

Are you interested in fixing typo's? There are a handful of places with typos such as the portmaster line for nginx (which says something like "portmaster ngnix" instead of "portmaster nginx")... I've also got an issue with one of the Dovecot SQL files with it complaining about the 'host' line not being recognized.. I've got to look into that further.

In my older FreeBSD 7.1 setup I had a jailed mail server with sendmail and the rest of what you've got (dovecot, spamd,excluding roundcube which was in an Apache jail) and all jails were able to talk to one another w/o any issues. I did have to install something from the mail tools area to forward outgoing messages to my jailed sendmail as I couldn't get the other minimal (e.g. host) sendmail to behave for this sort of 'forward' config... I don't recall what tool it was though. I'll have to look when I'm home later if there's interest, although postfix can probably be configured to do that.

I would not put roundcube (or any other PHP based site) outside of a jailed environment.. I've had one break-in due to an issue that was discovered last year (in roundcube) before I ran FreeBSD (was on Linux at the time) and a rootkit was installed and .... (you get the picture).. PHP scripts are what I'm scared most about -- they're an easy back door into a system to take control..
 
Back
Top