Ubuntu LTS 18.04 Base Server Setup Using Proxmox LXC Container


All of our servers will start with this install. This base server is based on Ubuntu 18.04 LTS Server.  This setup starts with a Proxmox LXC container instead of installing from an ISO.  I don’t explain much in the howto so if you have a question leave a comment or use Google.

Downloading the Template

Login to your Proxmox server.  Navigate to the storage where you store container templates.  Click the ‘add template’ button and select and download the Ubuntu 18.04 standard template.

Create the Container

Creating an LXC container is easy.  Click the ‘Create CT’ button.  Follow the wizard entering the requested info.

First boot

Boot the container.
The container will boot. Log in. At this point there is only the root user.  So lets create a user with root privileges.  Replace MYUSER with the user name you wish to use.
> sudo useradd -m -U -s /bin/bash -c “Admin User” MYUSER
> sudo passwd MYUSER
> sudo usermod -a -G sudo MYUSER

Logout of the root account and log back in using the new user account you created.

Get everything updated and install a couple of items.
> sudo apt update
> sudo apt dist-upgrade

WARNING: My server isn’t directly connected to the internet. The firewall is disabled to help with installation, configuration and testing easier. My servers sit behind a firewall.  Once everything is working, turn on the firewall and configure it. I wil remind you to secure your server at the end of this howto.

now reboot the server.

The Second Boot – Installing Additional Packages

We need quite a few other packages. In this howto I’m installing packages regardless if they were already installed by another dependency. This guards against package changes that could cause a package to not be installed. Once again log in to your server.

Install the following packages. Multiple lines to make cut and paste easier.
> sudo apt install make screen snmp composer libcurl3 unzip
> sudo apt install apache2 php7.2-fpm libapache2-mod-php7.2 mysql-server mysql-client
> sudo apt install libapache2-mod-fcgid php7.2-opcache php-apcu

Install some extra PHP libraries.
> sudo apt install php7.2-gd php7.2-snmp php7.2-mbstring php7.2-mysql
> sudo apt install php7.2-odbc php7.2-imap
> sudo apt install php7.2-xmlrpc php7.2-dba
> sudo apt install php7.2-soap php7.2-zip php7.2-intl php7.2-curl

Configure Apache and PHP

Enable some needed modules.
> sudo a2enmod rewrite actions fcgid alias proxy_fcgi expires headers

Enable Apache.
> sudo systemctl enable apache2.service

Reload apache.
> sudo systemctl restart apache2.service

Configuring MySQL

Configure mysql.
> sudo mysql_secure_installation

Installing and Configuring phpMyAdmin

I prefer to phpMyAdmin to manage my MySQL databases.

Now install phpMyAdmin.
> sudo apt install phpmyadmin

Restart Apache.
> sudo systemctl restart apache2.service

Getting root’s and other’s mail

You need to get some local system user’s mail. We’ll use postfix’s virtual file to get the emails to the right place.

Add the following to /etc/postfix/virtual

root admin@yourdomain.tld
postmaster admin@yourdomain.tld
abuse admin@yourdomain.tld

Now add the configuration option to main.cf
> sudo postconf -e “virtual_alias_maps = hash:/etc/postfix/virtual”
Just a couple commands to wrap everything up.
> sudo postmap /etc/postfix/virtual
> sudo systemctl enable postfix
> sudo systemctl restart postfix

Final Settings

You may want to enable the linux firewall.  You can also use the Proxmox firewall.
Set your timezone in /etc/php.ini


That’s it for the basic server setup. This is an example of a standard linux server setup. See my other pages for info on configuring servers for virtual webhosting or virtual email hosting.

Fix phpMyAdmin and PHP 7.2 Issues on Ubuntu 18.04


The version of phpmyadmin for ubuntu doesn’t play nice with php 7.2.  The problems were fixed in newer versions of phpmyadmin.

The Fix

Just a few steps and things will be fixed.

First visit www.phpmyadmin.net and download the latest version. My example is version 4.8.1.
> cd /tmp
> wget https://files.phpmyadmin.net/phpMyAdmin/4.8.1/phpMyAdmin-4.8.1-all-languages.zip

Unzip and rename the folder.
> unzip phpMyAdmin-4.8.1-all-languages.zip
> mv phpMyAdmin-4.8.1-all-languages phpmyadmin

Backup the old version of phpmyadmin and move over the new version.
> mv /usr/share/phpmyadmin /usr/share/phpmyadmin.save
> mv phpmyadmin /usr/share/

Edit /usr/share/phpmyadmin/libraries/vendor_config.php and make the following change.


define('CONFIG_DIR', '');


define('CONFIG_DIR', '/etc/phpmyadmin/');

That’s it.


This is a bug that will eventually get fixed.

HOWTO Virtual Mail Hosting on CentOS 6.x – Postfix MySQL Dovecot PostfixAdmin Amavisd-new Spamassassin Clamav


First things first. If you are a novice at linux I recommend that you should get help with setting this up. This is an advanced email server configuration. This configuration will allow you to serve multiple domains on one server. This howto will allow you to setup a server that is one of four mail server types:

  • Mail server with spam and virus checking (Most people. Do everything in the howto)
  • Mail server w/o spam and virus checking (Someone else is doing email filtering for you)
  • Backup MX / spam and virus filtering server (you want to divide things up)
  • Backup MX (You want to receive and hold mail while your main server is down)

You will get the following features:

  • Postfix: the workhorse behind the mail receiving and sending
  • smtp authentication
  • secure smtp using TLS
  • Dovecot: imap and pop3 mailbox service
  • secure imap and pop3
  • server side filtering of flagged spam to a spam folder
  • mysql: handle all the virtual domains and users
  • PostfixAdmin: GUI for domain administration
  • roundcube: web mail access
  • spam/virus filtering using amavisd-new, spamassassin and clamav

Installing CentOS

Start with my HOWTO: CentOS 6.x base server. That howto will get CentOS installed and ready for this howto.

WARNING: Not following the base server howto will cause you grief. Due to 6.4+ changes things will break.

Installing The Software

We’ll start with the yum installs.
> yum install roundcubemail dovecot dovecot-mysql dovecot-pigeonhole cyrus-sasl-devel cyrus-sasl-sql subversion
> yum install perl-MailTools perl-MIME-EncWords perl-MIME-Charset perl-Email-Valid perl-Test-Pod perl-TimeDate
> yum install perl-Mail-Sender perl-Log-Log4perl imapsync offlineimap
> yum install amavisd-new clamav clamd razor-agents perl-Convert-BinHex

Postfix.Admin doesn’t have an rpm so we need to download it and put it where we want it.
> wget http://sourceforge.net/projects/postfixadmin/files/latest/download
> tar -xzvf postfixadmin-2.3.5.tar.gz
> mv postfixadmin-2.3.5 /usr/share/postfixadmin

Configuring The Server

Setup SSL Certificate

Now generate an SSL certificate for postfix and dovecot to have TLS support. Replace mail.example.com with your server hostname.
> genkey –days 3650 mail.example.com

Setup the Virtual Mail User

Next we’ll configure the mail store directory. We put it in the /home directory to make backups and other item easy. So type the following.
> mkdir /home/vmail
> chmod 770 /home/vmail
> useradd -r -u 101 -g mail -d /home/vmail -s /sbin/nologin -c “Virtual mailbox” vmail
> chown vmail:mail /home/vmail

Configuring Postfix Admin

Create the apache config file for postfixadmin and restart apache.

alias /mailadmin /usr/share/postfixadmin
<Directory "/usr/share/postfixadmin">
  AllowOverride AuthConfig

> service httpd restart

Now we need to setup the mysql database for postfixadmin. We only need to create the database and user. The setup file will create the rest.
> mysql -u root -p -e “CREATE DATABASE postfix;”
> mysql -u root -p -e “CREATE USER postfix@localhost IDENTIFIED BY ‘choose_a_password’;”
> mysql -u root -p -e “GRANT ALL PRIVILEGES ON postfix . * TO postfix@localhost;”

Now its time to setup the config file. Don’t forget to set your password. Paste the following into the file.
> cd /usr/share/postfixadmin
> nano -w config.local.php

 * Contains configuration options that override the default config file

 *  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
 * You have to set $CONF['configured'] = true; before the
 * application will run!
 * Doing this implies you have changed this file as required.
 * i.e. configuring database etc; specifying setup.php password etc.
$CONF['configured'] = true;

// In order to setup Postfixadmin, you MUST specify a hashed password here.
// To create the hash, visit setup.php in a browser and type a password into the field,
// on submission it will be echoed out to you as a hashed value.
$CONF['setup_password'] = 'changeme';
$CONF['postfix_admin_url'] = '/mailadmin';
$CONF['database_type'] = 'mysql';
$CONF['database_host'] = 'localhost';
$CONF['database_user'] = 'postfix';
$CONF['database_password'] = 'changeme';
$CONF['database_name'] = 'postfix';
$CONF['admin_email'] = 'postmaster@change-this-to-your.domain.tld';
$CONF['encrypt'] = 'md5crypt';
$CONF['dovecotpw'] = "/usr/sbin/dovecotpw";
$CONF['min_password_length'] = 6;
$CONF['page_size'] = '20';
$CONF['domain_path'] = 'YES';
$CONF['domain_in_mailbox'] = 'NO';
$CONF['aliases'] = '50';
$CONF['mailboxes'] = '50';
$CONF['maxquota'] = '100';
$CONF['quota'] = 'YES';
$CONF['quota_multiplier'] = '1024000';
$CONF['transport'] = 'YES';
$CONF['transport_options'] = array (
    'virtual',  // for virtual accounts
    'local',    // for system accounts
    'relay'     // for backup mx
$CONF['transport_default'] = 'virtual';
$CONF['vacation'] = 'YES';
$CONF['vacation_domain'] = 'autoreply.change-this-to-your.domain.tld';
$CONF['vacation_control'] ='YES';
$CONF['vacation_control_admin'] = 'YES';
$CONF['special_alias_control'] = 'YES';
$CONF['user_footer_link'] = "http://change-this-to-your.domain.tld/main";
$CONF['show_footer_text'] = 'YES';
$CONF['footer_text'] = 'Return to change-this-to-your.domain.tld';
$CONF['footer_link'] = 'http://change-this-to-your.domain.tld';
$CONF['used_quotas'] = 'YES';
$CONF['new_quota_table'] = 'YES';
// $CONF['create_mailbox_subdirs_hostoptions']=array('notls');


Next we need to run the setup.php script in a web browser. Enter the url in your browser. Ex.

If everything shows OK then create the admin user using the form displayed. Follow the instructions for setting the setup password.

Log into the web interface and follow the directions.

Configuring Postfix

Here we go with more config files. You’ll have to be sure to change some settings to match your host. The config files will have sections commented out. Don’t worry about it. These sections are for spam/virus/sympa configuration. Just copy and past to create the config files. What ever you see here replaces what already exists.

The main postfix config files.

# postfix config file

# uncomment for debugging if needed

# postfix main
mail_owner = postfix
setgid_group = postdrop
delay_warning_time = 4

# postfix paths
html_directory = no
command_directory = /usr/sbin
daemon_directory = /usr/libexec/postfix
queue_directory = /var/spool/postfix
sendmail_path = /usr/sbin/sendmail.postfix
newaliases_path = /usr/bin/newaliases.postfix
mailq_path = /usr/bin/mailq.postfix
manpage_directory = /usr/share/man

# network settings
inet_interfaces = all
mydomain = yourdomain.com
myhostname = host.yourdomain.com
mynetworks = $config_directory/mynetworks
mydestination = $myhostname, localhost.$mydomain, localhost 
relay_domains = proxy:mysql:/etc/postfix/mysql-relay_domains_maps.cf

# mail delivery
recipient_delimiter = + 

# mappings
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
transport_maps = hash:/etc/postfix/transport
#local_recipient_maps = 

# virtual setup
virtual_alias_maps = proxy:mysql:/etc/postfix/mysql-virtual_alias_maps.cf,
virtual_mailbox_base = /home/vmail
virtual_mailbox_domains = proxy:mysql:/etc/postfix/mysql-virtual_domains_maps.cf
virtual_mailbox_maps = proxy:mysql:/etc/postfix/mysql-virtual_mailbox_maps.cf
virtual_mailbox_limit_maps = proxy:mysql:/etc/postfix/mysql-virtual_mailbox_limit_maps.cf
virtual_minimum_uid = 101
virtual_uid_maps = static:101
virtual_gid_maps = static:12
virtual_transport = dovecot
dovecot_destination_recipient_limit = 1

# debugging
debug_peer_level = 2
debugger_command =
         xxgdb $daemon_directory/$process_name $process_id & sleep 5

# authentication
smtpd_sasl_auth_enable = yes
smtpd_sasl_security_options = noanonymous
smtpd_sasl_local_domain = $myhostname
broken_sasl_auth_clients = yes
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth

# tls config
smtp_use_tls = yes
smtpd_use_tls = yes 
smtpd_tls_security_level = may
smtpd_tls_loglevel = 1
smtpd_tls_received_header = yes
smtpd_tls_session_cache_timeout = 3600s
tls_random_source = dev:/dev/urandom
smtp_tls_session_cache_database = btree:$data_directory/smtp_tls_session_cache
# Change mail.example.com.* to your host name 
smtpd_tls_key_file = /etc/pki/tls/private/mail.example.com.key
smtpd_tls_cert_file = /etc/pki/tls/certs/mail.example.com.crt
# smtpd_tls_CAfile = /etc/pki/tls/root.crt

# rules restrictions 
smtpd_client_restrictions = 
smtpd_helo_restrictions = 
smtpd_sender_restrictions = 
smtpd_recipient_restrictions = permit_sasl_authenticated, 
# uncomment for realtime black list checks
#	,reject_rbl_client zen.spamhaus.org
#	,reject_rbl_client bl.spamcop.net
#	,reject_rbl_client dnsbl.sorbs.net

smtpd_helo_required = yes
unknown_local_recipient_reject_code = 550
disable_vrfy_command = yes
smtpd_data_restrictions = reject_unauth_pipelining

# Other	options
# email	size limit ~20Meg
message_size_limit = 204800000


# Postfix master process configuration file.  For details on the format
# of the file, see the Postfix master(5) manual page.
# ***** Unused items removed *****
# ==========================================================================
# service type  private unpriv  chroot  wakeup  maxproc command + args
#               (yes)   (yes)   (yes)   (never) (100)
# ==========================================================================
smtp      inet  n       -       n       -       -       smtpd
#  -o content_filter=smtp-amavis:
#  -o receive_override_options=no_address_mappings
pickup    fifo  n       -       n       60      1       pickup
  -o content_filter= 
  -o receive_override_options=no_header_body_checks
cleanup   unix  n       -       n       -       0       cleanup
qmgr      fifo  n       -       n       300     1       qmgr
#qmgr     fifo  n       -       n       300     1       oqmgr
tlsmgr    unix  -       -       n       1000?   1       tlsmgr
rewrite   unix  -       -       n       -       -       trivial-rewrite
bounce    unix  -       -       n       -       0       bounce
defer     unix  -       -       n       -       0       bounce
trace     unix  -       -       n       -       0       bounce
verify    unix  -       -       n       -       1       verify
flush     unix  n       -       n       1000?   0       flush
proxymap  unix  -       -       n       -       -       proxymap
smtp      unix  -       -       n       -       -       smtp
# When relaying mail as backup MX, disable fallback_relay to avoid MX loops
relay     unix  -       -       n       -       -       smtp
        -o fallback_relay=
#       -o smtp_helo_timeout=5 -o smtp_connect_timeout=5
showq     unix  n       -       n       -       -       showq
error     unix  -       -       n       -       -       error
discard   unix  -       -       n       -       -       discard
local     unix  -       n       n       -       -       local
virtual   unix  -       n       n       -       -       virtual
lmtp      unix  -       -       n       -       -       lmtp
anvil     unix  -       -       n       -       1       anvil
scache    unix  -       -       n       -       1       scache
# ====================================================================
# Interfaces to non-Postfix software. Be sure to examine the manual
# pages of the non-Postfix software to find out what options it wants.
# ====================================================================
maildrop  unix  -       n       n       -       -       pipe
  flags=DRhu user=vmail argv=/usr/local/bin/maildrop -d ${recipient}
uucp      unix  -       n       n       -       -       pipe
  flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)
ifmail    unix  -       n       n       -       -       pipe
  flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient)
bsmtp     unix  -       n       n       -       -       pipe
  flags=Fq. user=foo argv=/usr/local/sbin/bsmtp -f $sender $nexthop $recipient
# spam/virus section
smtp-amavis  unix  -    -       y       -       2       smtp
  -o smtp_data_done_timeout=1200
  -o disable_dns_lookups=yes
  -o smtp_send_xforward_command=yes inet n  -       y       -       -       smtpd
  -o content_filter=
  -o smtpd_helo_restrictions=
  -o smtpd_sender_restrictions=
  -o smtpd_recipient_restrictions=permit_mynetworks,reject
  -o mynetworks=
  -o smtpd_error_sleep_time=0
  -o smtpd_soft_error_limit=1001
  -o smtpd_hard_error_limit=1000
  -o receive_override_options=no_header_body_checks
  -o smtpd_bind_address=
  -o smtpd_helo_required=no
  -o smtpd_client_restrictions=
  -o smtpd_restriction_classes=
  -o disable_vrfy_command=no
  -o strict_rfc821_envelopes=yes
# Dovecot LDA
dovecot   unix  -       n       n       -       -       pipe
  flags=DRhu user=vmail:mail argv=/usr/libexec/dovecot/deliver -d ${recipient}
# Vacation mail
vacation    unix  -       n       n       -       -       pipe
  flags=Rq user=vacation argv=/var/spool/vacation/vacation.pl -f ${sender} -- ${recipient}


# This specifies the list of subnets that Postfix considers as
# "trusted" SMTP clients that have more privileges than "strangers".
# In particular, "trusted" SMTP clients are allowed to relay mail
# through Postfix.
# Be sure to add your public ip address block if needed.

The postfix / mysql config files.

hosts = localhost
user = postfix
password = postfix
dbname = postfix
query = SELECT goto FROM alias WHERE address='%s' AND active = '1'


hosts = localhost
user = postfix
password = postfix
dbname = postfix
query = SELECT domain FROM domain WHERE domain='%s' AND backupmx = '0' AND active = '1'


hosts = localhost
user = postfix
password = postfix
dbname = postfix
query = SELECT domain FROM domain WHERE domain='%s' and backupmx = '1'


hosts = localhost
user = postfix
password = postfix
dbname = postfix
query = SELECT maildir FROM mailbox WHERE username='%s' AND active = '1'


hosts = localhost
user = postfix
password = postfix
dbname = postfix
query = SELECT quota FROM mailbox WHERE username='%s' AND active = '1'

We need to touch a file. So type the follwoing.
> touch /etc/postfix/virtual_regexp

Configure Vacation Email Functionallity

Lets finish the postfix configuration with vacation mail. Don’t forget to fill in your domain name where needed. Type the following:
> useradd -r -d /var/spool/vacation -s /sbin/nologin -c “Virtual vacation” vacation
> mkdir /var/spool/vacation
> chmod 770 /var/spool/vacation
> cp /usr/share/postfixadmin/VIRTUAL_VACATION/vacation.pl /var/spool/vacation/
> echo “autoreply.yourdomain.com vacation:” > /etc/postfix/transport
> postmap /etc/postfix/transport
> chown -R vacation:vacation /var/spool/vacation
> echo “ autoreply.yourdomain.com” >> /etc/hosts
> mkdir /etc/postfixadmin

Create /etc/postfixadmin/vacation.conf with the following:

# ========== begin configuration ==========
$db_type = 'mysql';
$db_username = 'user';
$db_password = 'password';    
$db_name     = 'postfix';    
$vacation_domain = 'autoreply.example.org';  

Configuring Dovecot

Now for the dovecot config file. Dovecot now uses multiple config files to break things up. We’re going to only use a couple config files. So cut and paste the following files.
> mv /etc/dovecot/dovecot.conf /etc/dovecot/dovecot.conf.save
> nano -w /etc/dovecot/dovecot.conf

## Dovecot config file

protocols = imap pop3 lmtp sieve
auth_mechanisms = plain login
passdb {
  driver = sql
  args = /etc/dovecot/dovecot-mysql.conf
userdb {
  driver = prefetch
userdb {
  driver = sql
  args = /etc/dovecot/dovecot-mysql.conf
mail_location = maildir:/home/vmail/%d/%n
first_valid_uid = 101
#last_valid_uid = 0
first_valid_gid = 12
#last_valid_gid = 0
#mail_plugins =
mailbox_idle_check_interval = 30 secs
maildir_copy_with_hardlinks = yes
service imap-login {
  inet_listener imap {
    port = 143
  inet_listener imaps {
    port = 993
    ssl = yes
service pop3-login {
  inet_listener pop3 {
    port = 110
  inet_listener pop3s {
    port = 995
    ssl = yes
service lmtp {
  unix_listener lmtp {
    #mode = 0666
service imap {
  vsz_limit = 256M
service pop3 {
service auth {
  unix_listener auth-userdb {
    mode = 0666
    user = vmail
    group = mail

  # Postfix smtp-auth
  unix_listener /var/spool/postfix/private/auth {
    mode = 0666
    user = postfix
    group = postfix
service auth-worker {
service dict {
  unix_listener dict {
    mode = 0666
    user = vmail
    group = mail
service managesieve-login {
  inet_listener sieve {
    port = 4190
  service_count = 1
  process_min_avail = 0
  vsz_limit = 64M
service managesieve {
ssl = yes
ssl_cert = </etc/pki/tls/certs/your-server.your-domain.tld.crt 
ssl_key = </etc/pki/tls/private/your-server.your-domain.tld.key
ssl_verify_client_cert = no
#ssl_ca =
lda_mailbox_autocreate = yes         
lda_mailbox_autosubscribe = yes
protocol lda {
  mail_plugins = quota sieve
  postmaster_address = postmaster@your-domain.tld
protocol imap {
  mail_plugins = quota imap_quota trash
  imap_client_workarounds = delay-newmail
lmtp_save_to_detail_mailbox = yes
protocol lmtp {
  mail_plugins = sieve
protocol pop3 {
  mail_plugins = quota
  pop3_client_workarounds = outlook-no-nuls oe-ns-eoh
protocol sieve {
  managesieve_max_line_length = 65536
  managesieve_implementation_string = Dovecot Pigeonhole
  managesieve_max_compile_errors = 5
dict {
  quotadict = mysql:/etc/dovecot/dovecot-dict-quota.conf
plugin {
  quota = dict:user::proxy::quotadict
  acl = vfile:/etc/dovecot/acls
  trash = /etc/dovecot/trash.conf
  sieve_global_path = /home/sieve/globalfilter.sieve
  sieve = ~/dovecot.sieve
  sieve_dir = ~/sieve
  sieve_global_dir = /home/sieve/
  #sieve_extensions = +notify +imapflags
  sieve_max_script_size = 1M
  #sieve_max_actions = 32
  #sieve_max_redirects = 4

Now for trash.conf
> nano -w /etc/dovecot/trash.conf

1 Spam
# Uncomment if you want trash as well
# 2 Trash

Next we configure Dovecot to access mysql. Create the following file.
NOTE: password_query and user_query were formatted to fit on the webpage. Each one should only be one line in the file.

driver = mysql
connect = host=localhost dbname=postfix user=postfix password=yourpassword
default_pass_scheme = MD5-CRYPT

# following should all be on one line.
password_query = SELECT username as user, password, concat('/home/vmail/', maildir) as userdb_home, 
concat('maildir:/home/vmail/', maildir) as userdb_mail, 101 as userdb_uid, 12 as userdb_gid FROM mailbox 
WHERE username = '%u' AND active = '1'

# following should all be on one line
user_query = SELECT concat('/home/vmail/', maildir) as home, concat('maildir:/home/vmail/', maildir) as mail, 
101 AS uid, 12 AS gid, CONCAT('*:messages=10000:bytes=', quota) as quota_rule FROM mailbox WHERE 
username = '%u' AND active = '1'

and /etc/dovecot/dovecot-dict-quota.conf

connect = host=localhost dbname=postfix user=postfix password=password
map {
  pattern = priv/quota/storage
  table = quota2
  username_field = username
  value_field = bytes
map {
  pattern = priv/quota/messages
  table = quota2
  username_field = username
  value_field = messages

Finally set Dovecot to boot at startup.

Now Create the sieve filter for SPAM filtering.
> mkdir /home/sieve
> nano -w /home/sieve/globalfilter.sieve
> chown -R vmail:mail /home/sieve

require "fileinto";
  if exists "X-Spam-Flag" {
          if header :contains "X-Spam-Flag" "NO" {
          } else {
          fileinto "Spam";      
  if header :contains "subject" ["***SPAM***"] {
    fileinto "Spam";      

Configuring Roundcube mail

Edit the roundcube apache config file to look like the following:
>nano -w /etc/httpd/conf.d/roundcubemail.conf

# Round Cube Webmail is a browser-based multilingual IMAP client

# Force https here instead of in Round Cube 
RewriteEngine On

# This checks to make sure the connection is not already HTTPS
RewriteCond %{HTTPS} !=on 

# These rules will redirect all users who are using any part of /secure/ to the same location but using HTTPS.
# i.e.  http://www.example.com/secure/ to https://www.example.com/secure/
RewriteRule ^/?roundcubemail/(.*) https://%{SERVER_NAME}/roundcubemail/$1 [R,L]
RewriteRule ^/?webmail/(.*) https://%{SERVER_NAME}/webmail/$1 [R,L]

Alias /roundcubemail /usr/share/roundcubemail
Alias /webmail /usr/share/roundcubemail

<Directory /usr/share/roundcubemail/>
        Order Deny,Allow
        Deny from all
        Allow from all
        php_value suhosin.session.encrypt Off 

Create the database for roundcube.
> mysql -u root -p -e “CREATE DATABASE roundcubemail;”
> mysql -u root -p -e “GRANT ALL PRIVILEGES ON roundcubemail.* TO roundcube@localhost IDENTIFIED BY ‘password’;”

Create the tables.
> mysql -u root -p roundcubemail < /usr/share/doc/roundcubemail-0.5.4/SQL/mysql.initial.sql

Edit /etc/roundcubemail/db.inc.php and find the line:

$rcmail_config['db_dsnw'] = 'mysql://roundcube:pass@localhost/roundcubemail';

Change ‘pass’ to your passowrd.

Edit /etc/roundcubemail/main.inc.php and find the lines and make the changes below:


$rcmail_config['default_host'] = '';

change to:

$rcmail_config['default_host'] = 'localhost';


$rcmail_config['smtp_server'] = '';

change to:

$rcmail_config['smtp_server'] = 'localhost';


$rcmail_config['force_https'] = false;

change to:

$rcmail_config['force_https'] = true;


$rcmail_config['plugins'] = array();

change to:

$rcmail_config['plugins'] = array('managesieve');


$rcmail_config['quota_zero_as_unlimited'] = false;

change to:

$rcmail_config['quota_zero_as_unlimited'] = true;

Now lets configure the manage sieve plugin.
> cd /usr/share/roundcubemail/plugins/managesieve/
> cp config.inc.php.dist config.inc.php

Edit config.inc.php and change the following:

$rcmail_config['managesieve_port'] = 2000;


$rcmail_config['managesieve_port'] = 4190;

Restart apache.
> service httpd restart

Configuring the Little Things That Drive You MAD

Be sure your /etc/hosts looks similar to the following.

# Do not remove the following line, or various programs
# that require network functionality will fail.       localhost   host.domain.com

Preparing and Testing the Postoffice

First things first. Reboot the system. If everything went well we all should be at the same point.

No errors? Lets keep going.

Setup a test domain and account. Setup your favorit mail client and send some test emails.

Setting up Spam and Virus Filtering (Optional)

Lets cover installing and configuring spam and virus filtering. Optional? Huh? Some people use a 3rd party or use a seperate server for filtering.

Here’s the clamav config file. Replace /etc/clamd.conf with the following:

## Cconfig file for the Clam AV daemon
## Please read the clamd.conf(5) manual before editing this file.

# Logfile
LogFile /var/log/clamav/clamd.log
LogFileMaxSize 20M
LogTime yes
LogSyslog yes

# Pid
PidFile /var/run/clamav/clamd.pid

# Paths
TemporaryDirectory /var/tmp
DatabaseDirectory /var/clamav
LocalSocket /var/run/clamav/clamd

# Sets the group ownership on the unix socket.
# Default: disabled (the primary group of the user running clamd)
#LocalSocketGroup virusgroup

# Misc
FixStaleSocket yes
TCPSocket 3310
MaxConnectionQueueLength 50
MaxThreads 50
ReadTimeout 240
User clamav
AllowSupplementaryGroups yes

# Exe
ScanPE yes
ScanELF yes
DetectBrokenExecutables yes

# Docs
ScanOLE2 yes
ScanPDF yes

# Mail
ScanMail yes
PhishingSignatures yes
PhishingScanURLs yes

# Data Loss Prevention (DLP)

# Enable the DLP module
# Default: No
#StructuredDataDetection yes

# This option sets the lowest number of Credit Card numbers found in a file
# to generate a detect.
# Default: 3
#StructuredMinCreditCardCount 5

# This option sets the lowest number of Social Security Numbers found
# in a file to generate a detect.
# Default: 3
#StructuredMinSSNCount 5

# With this option enabled the DLP module will search for valid
# SSNs formatted as xxx-yy-zzzz
# Default: yes
#StructuredSSNFormatNormal yes

# With this option enabled the DLP module will search for valid
# SSNs formatted as xxxyyzzzz
# Default: no
#StructuredSSNFormatStripped yes

# Archives
ScanArchive yes
ArchiveBlockEncrypted no

Configure Razor. Type the following:
> razor-admin -register -user=some_user -pass=somepas

Update and restart clamav:
> freshclam
> service clamd restart

Configuring Amavisd-new

You need to edit /etc/amavisd.conf
Here is a list of items you should change. just scroll through the file to find each item.

  • $mydomain = ‘example.com’; # set to your domain name
  • $log_level = 1; # set the log leve to one
  • $sa_tag_level_deflt = -99; # i want to see the headers so change to -99
  • $sa_tag2_level_deflt = 5.0; # start with 5
  • $sa_kill_level_deflt = 9; # change to 9
  • $sa_dsn_cutoff_level = 9; # change to 9
  • $sa_quarantine_cutoff_level = 50; # remove the starting # and change to 50
  • $myhostname = ‘lightning.campworld.net’; # remove the starting # and enter your host name
  • $notify_method = ‘smtp:[]:10025’; # uncomment the line
  • $forward_method = ‘smtp:[]:10025’; # uncomment the line
  • $final_banned_destiny = D_DISCARD; # change to D_DISCARD

Now enable clamav:
Change the following:

# ### http://www.clamav.net/
# ['ClamAV-clamd',
#   \&ask_daemon, ["CONTSCAN {}\n", "/var/run/clamav/clamd"],
#   qr/\bOK$/m, qr/\bFOUND$/m,
#   qr/^.*?: (?!Infected Archive)(.*) FOUND$/m ],


 ### http://www.clamav.net/
   \&ask_daemon, ["CONTSCAN {}\n", "/var/run/clamav/clamd"],
   qr/\bOK$/m, qr/\bFOUND$/m,
   qr/^.*?: (?!Infected Archive)(.*) FOUND$/m ],

Now update spamassassin and start amavisd-new.
> sa-update
> service amavisd-new start

Be sure to set amavisd-new to start at boot.

Telling Postfix to Start Filtering SPAM

To get postfix going we need to un-comment a couple lines in /etc/postfix/master.cf

smtp      inet  n       -       n       -       -       smtpd
#  -o content_filter=smtp-amavis:
#  -o receive_override_options=no_address_mappings

Change to:

smtp      inet  n       -       n       -       -       smtpd
  -o content_filter=smtp-amavis:
  -o receive_override_options=no_address_mappings

Restart postfix and you’re done.

Using The Roundcubemail Password Plugin (Optional)

Let your users change their password using roundcubemail instead of postfixadmin.

Edit /etc/roundcubemail/main.inc.php and find the lines and make the changes below:


$rcmail_config['plugins'] = array('managesieve');

change to:

$rcmail_config['plugins'] = array('managesieve','password');

Now lets configure the password plugin.
> cd /usr/share/roundcubemail/plugins/password/
> cp config.inc.php.dist config.inc.php

Edit config.inc.php


$rcmail_config['password_db_dsn'] = '';

change to:

$rcmail_config['password_db_dsn'] = 'mysql://postfix:your-postfixadmin-password@localhost/postfix';


$rcmail_config['password_query'] = 'SELECT update_passwd(%c, %u)';

change to:

$rcmail_config['password_query'] = 'UPDATE mailbox SET password=%c WHERE username=%u limit 1;';

Restart apache.
> service httpd restart

Notes About Security And Clear Text Passwords From Mail Clients

My howto has been written to allow clear text passwords. This can and does cause security problem of sending of clear text passwords through the internet. This covers sending of the password from the client to the server. Passwords are stored in the database encrypted.

The configuration doesn’t require clients to use SSL/TLS. If you use SSL/TLS then the passwords are encrypted in the SSL connection. POPS, IMAPS and SMTPS all use SSL/TLS connection. So as long as your client supports secure connections to the mail server your clear text passwords will be secure.


With a bit of work you come out with a robust server.

HOWTO CentOS 6.x Virtual Web Hosting With Apache MYSQL and ProFTPD

There are many ways to do virtual websites under linux. The technique I am going to use is multiple domains on one ip address. I also didn’t want to use system users for the virtual hosts. So I decided to use a mysql database to store the virtual user information.

Setting Up The Base Server

For a dedicated server start with the base server setup: HOWTO CentOS 6.x Base Server.
NOTE: If you don’t follow the base server setup then you may run into problems with this howto.

Install Softwre

We need to install is proftpd with mysql support. Type:
> yum -y install proftpd-mysql

Now lets download and install proftpd admin.
> wget http://downloads.sourceforge.net/proftpd-adm/proftpd_admin_v1.2.tar.gz
> tar -xzvf proftpd_admin_v1.2.tar.gz
> mv proftpd_admin_v1.2 /usr/share/proftpd_admin

Configuring Apache

First we need to create a user called virtwww. We will also make the directory world writable.
> useradd -r -d /home/virtwww -s /sbin/nologin -c “Virtual websites” virtwww
> mkdir /home/virtwww
> chmod a+rwx /home/virtwww

Create the virtual host config file. We will use the name the brouser passes to us to determin the website to load. We also turns on compression. Create /etc/httpd/conf.d/virutal.conf with the following:

# compress all text & html:
#AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE image/svg+xml
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/atom_xml
AddOutputFilterByType DEFLATE application/x-javascript
AddOutputFilterByType DEFLATE application/x-httpd-php
AddOutputFilterByType DEFLATE application/x-httpd-fastphp
AddOutputFilterByType DEFLATE application/x-httpd-eruby
AddOutputFilterByType DEFLATE text/html

UseCanonicalName Off
RewriteEngine On
RewriteOptions Inherit

VirtualDocumentRoot /home/virtwww/%0

Restart apache
> service httpd restart

Make a directory with your server’s ipaddress as its name. Example
> mkdir /home/virtwww/

Create a test index.html page in your new directory.

Configuring ProFTPD

Create the following proftpd config file
> mv /etc/proftpd.conf /etc/proftpd.conf.old
> nano -w /etc/proftpd.conf

# This is the ProFTPD configuration file

ServerName			"ProFTPD server"
ServerIdent			on "FTP Server ready."
ServerAdmin			root@localhost
ServerType			standalone
DefaultServer			on
AccessGrantMsg			"User %u logged in."
DeferWelcome			off

DefaultRoot			~ !adm
IdentLookups			off
UseReverseDNS			off
Port				21
Umask				022
ListOptions			"-a"
MaxLoginAttempts		3
MaxInstances                    15
MaxClientsPerHost               3               "Only %m connections per host allowed"
MaxClients                      10              "Only %m total simultanious logins allowed"
MaxHostsPerUser                 1
AllowRetrieveRestart		on
AllowStoreRestart		on
User				nobody
Group				nobody
UseSendfile			no
ScoreboardFile			/var/run/proftpd.score

# Normally, we want users to do a few things.
  AllowOverwrite		yes

# Define the log formats
LogFormat			default	"%h %l %u %t \"%r\" %s %b"
LogFormat			auth	"%v [%P] %h %t \"%r\" %s"

# Explained at http://www.castaglia.org/proftpd/modules/mod_tls.html
#TLSEngine			on
#TLSRequired			on
#TLSRSACertificateFile		/etc/pki/tls/certs/proftpd.pem
#TLSRSACertificateKeyFile	/etc/pki/tls/certs/proftpd.pem
#TLSCipherSuite			ALL:!ADH:!DES
#TLSOptions			NoCertRequest
#TLSVerifyClient		off
##TLSRenegotiate		ctrl 3600 data 512000 required off timeout 300
#TLSLog				/var/log/proftpd/tls.log

# SQL authentication Dynamic Shared Object (DSO) loading
# See README.DSO and howto/DSO.html for more details.
<IfModule mod_dso.c>
   LoadModule mod_sql.c
   LoadModule mod_sql_mysql.c

# Define log-files to use
TransferLog                     /var/log/proftpd/xferlog
ExtendedLog                     /var/log/proftpd/access_log    WRITE,READ write
ExtendedLog                     /var/log/proftpd/auth_log      AUTH auth
ExtendedLog                     /var/log/proftpd/paranoid_log  ALL default
SQLLogFile                      /var/log/proftpd/mysql

# Set up authentication via SQL
# ===========
AuthOrder                       mod_sql.c
SQLAuthTypes                    Backend
SQLConnectInfo                  proftpd_admin@localhost proftpd password
SQLUserInfo                     usertable userid passwd uid gid homedir shell
SQLGroupInfo                    grouptable groupname gid members
SQLUserWhereClause              "disabled=0 and (NOW()<=expiration or expiration=-1 or expiration=0)"
CreateHome on

# Log the user logging in
SQLLog PASS counter
SQLNamedQuery counter UPDATE "lastlogin=now(), count=count+1 WHERE userid='%u'" usertable

# logout log
SQLLog EXIT time_logout
SQLNamedQuery time_logout UPDATE "lastlogout=now() WHERE userid='%u'" usertable

# display last login time when PASS command is given
SQLNamedQuery login_time SELECT "lastlogin from usertable where userid='%u'"
SQLShowInfo PASS "230" "Last login was: %{login_time}"

# xfer Log in mysql
SQLLog RETR,STOR transfer1
SQLNamedQuery  transfer1 INSERT "'%u', '%f', '%b', '%h', '%a', '%m', '%T', now(), 'c', NULL" xfer_stat
SQLNamedQuery  transfer2 INSERT "'%u', '%f', '%b', '%h', '%a', '%m', '%T', now(), 'i', NULL" xfer_stat

AllowStoreRestart               on
AllowRetrieveRestart            on
RequireValidShell               off

Configuring ProFTPD Administrator

First lets create the apache config file and restart apache. The file has been set to only allow access from the local host. Change the access to meet your needs.


alias /ftpadmin /usr/share/proftpd_admin

<Location /ftpadmin>
    Order deny,allow
    Deny from all
    Allow from
    Allow from ::1
    Allow from 192.168.
    Allow from 10.
    # Allow from .example.com

> service httpd restart

Edit the file /usr/share/proftpd_admin/misc/database_structure_mysql/db_structure.sql
Skip to the last three lines. Enter the desired password in these three lines.

Lets create the database and tables. Type the following.
> mysql -u root -p < /usr/share/proftpd_admin/misc/database_structure_mysql/db_structure.sql

Now type the following to do a little housekeeping for php5.
> chmod o+w /usr/share/proftpd_admin/configuration.xml

Now start proftpd. Be sure to also have it start at boot time.
> service proftpd start

Now go to the web interface configuration screen http://yourserver.tld/ftpadmin/configure.php
You will need to configure database access and some other settings.


That’s the complete setup. Proftpd Admin dose almost everything we need. You will need to create links for multiple host names pointing to the same directory. For example if your directory is ‘/home/virtwww/yourdomain.com’ and you want www.yourdomain.com to work then you’ll have to create a link called ‘/home/virtwww/www.yourdomain.com/’ which points to ‘/home/virtwww/yourdomain.com’.

HOWTO CentOS 6.x Base Server Setup


All of our servers will start with this install. This base server is based on CentOS 6.x. There have been some changes since my 5.x howtos.

Downloading the ISO

Visit the CentOS website and download the minimum install ISO. The filename is CentOS-6.7-i386-minimal.iso as an example for this howto.

Initial Install

Boot the install DVD.

The graphical install loads and we’re ready to go.

  • Choose your language and keyboard.
  • Choose “Basic Storage Device”. Then click next.
  • The first complaint it will have is about your hard drive. When it asks you about data click “Yes, discard any data”.
  • Enter a host name for the computer.
  • Click the configure network button. Make any changes you may have.
  • When done click next.
  • Choose your time zone.
  • set the root password.
  • Next select “Use All Space”.
  • Click next. Sit back and watch the install go.

First boot

Reboot the machine when the install finishes. The OS will boot. Log in.

Get everything updated.
> yum -y upgrade

Now install the base system.
> yum -y groupinstall core base

Now we need to disable selinux.
Edit /etc/selinux/config and change SELINUX=enforcing to SELINUX=disabled

Edit /boot/grub/grub.conf and add selinux=0 to the kernel line. Here’s an example grub file with the change.

# grub.conf generated by anaconda
# Note that you do not have to rerun grub after making changes to this file
# NOTICE:  You have a /boot partition.  This means that
#          all kernel and initrd paths are relative to /boot/, eg.
#          root (hd0,0)
#          kernel /vmlinuz-version ro root=/dev/VolGroup00/LogVol00
#          initrd /initrd-version.img
title Fedora (
        root (hd0,0)
        kernel /vmlinuz- ro root=/dev/VolGroup00/LogVol00 selinux=0
        initrd /initrd-

Now reboot the server.

The Second Boot – Installing Additional Packages

We need quite a few other packages. A change in this howto is that I’m installing RPMs reguardless if they were already installed by another dependency. This guards against RPM changes that could cause a package to not be installed.

We need to add and enable a few repositories first.

Type nano -w /etc/yum.repos.d/CentOS-Base.repo There are 2 lines in the file that say ‘enable=0’. Change the 0 to a 1

Lets add the rpmforge repo.
> cd /root
> wget http://pkgs.repoforge.org/rpmforge-release/rpmforge-release-0.5.2-2.el6.rf.i686.rpm
> rpm -Uhv rpmforge*

We need to enable the rpmforge extras repo. The section of the file below should have the enabled=0 changed to enabled=1
> nano -w /etc/yum.repos.d/rpmforge.repo

name = RHEL $releasever - RPMforge.net - extras
baseurl = http://apt.sw.be/redhat/el6/en/$basearch/extras
mirrorlist = http://apt.sw.be/redhat/el6/en/mirrors-rpmforge-extras
#mirrorlist = file:///etc/yum.repos.d/mirrors-rpmforge-extras
enabled = 1
protect = 0
gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-rpmforge-dag
gpgcheck = 1

We need the webmin repo. Create webmin.repo with the text below.
> nano -w /etc/yum.repos.d/webmin.repo

name=Webmin Distribution Neutral

And finally the EPEL repo.
> rpm -Uhv http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm

We need to exclude a couple of RPMs that have version problems with the rpmforge repo. So lets edit the CentOS base repo
> nano -w /etc/yum.repos.d/CentOS-Base.repo

Add the following below the ‘base’ and ‘updates’ sections.


Now bring everything up to date.
> yum -y update

Install the following RPMs.
> yum -y install gcc gcc-c++ wget bison nano make createrepo screen libmcrypt proftpd caching-nameserver

Install mysql.
> yum -y groupinstall ‘MySQL Database client’ ‘MySQL Database server’

Install Apache and PHP.
> yum -y groupinstall ‘Web Server’ ‘PHP Support’
> yum -y install php-gd php-ncurses php-snmp php-mbstring php-mysql php-devel php-imap
> yum -y install php-odbc php-pear php-xml
> yum -y install php-xmlrpc php-dba php-pear-DB php-process php-pear-DB php-mcrypt

Now lets install webmin. We need SSL support in perl. Setup is easier if you get this installed before webmin.
> yum -y install perl-Net-SSLeay
> rpm –import http://www.webmin.com/jcameron-key.asc
> yum -y install webmin
> service webmin start

Now run setup and disable the firewall. I’m assuming that you are setting up the server behind a firewall. When everything is working then the firewall can be configured and turned on.
> setup

Webmin Configuration

Connct to the webmin server. Use the ip assigned to your server. An example URL would be
Now configure your server.

Installing phpMyAdmin

I prefer to phpMyAdmin to manage my MySQL databases.

If you haven’t already done so, start MySQL.
Its time to set the root password.
> /usr/bin/mysqladmin -u root password ‘thepassword’
> /usr/bin/mysqladmin -p -u root -h localhost.localdomain password ‘thepassword’

Now install phpMyAdmin. NOTE: there is a package called phpmyadmin. Don’t install it.
> yum -y install phpMyAdmin

You will need to add access to phpMyAdmin. By default only the local server can access it. Edit /etc/httpd/conf.d/phpmyadmin.conf to look like the following.

#  Web application to manage MySQL

<Directory "/usr/share/phpmyadmin">
  Order Deny,Allow
  Deny from all
  Allow from
  Allow	from 192.168.
  Allow	from 10.

Alias /phpmyadmin /usr/share/phpmyadmin
Alias /phpMyAdmin /usr/share/phpmyadmin
Alias /mysqladmin /usr/share/phpmyadmin

Restart Apache.
> service httpd restart

Now test it out.

Getting root’s and other’s mail

You need to get some local system user’s mail. We’ll use postfix’s virtual file to get the emails to the right place.

Add the following to /etc/postfix/virtual

root       admin@yourdomain.com
postmaster admin@yourdomain.com
abuse      admin@yourdomain.com

Now add the configuration option to main.cf
> postconf -e “virtual_alias_maps = hash:/etc/postfix/virtual”

Just a couple commands to wrap everything up.
> postmap /etc/postfix/virtual
> /etc/init.d/postfix restart

Securing tmp and shm

Unless you customized your partion layout we’ve got some work to do. The /tmp and /var/tmp directories need to be secured. We will do this by mounting a file as a loop device.

First lets start with shm. change the following line in /etc/fstab

tmpfs /dev/shm tmpfs defaults 0 0


tmpfs /dev/shm tmpfs defaults,nosuid,noexec,rw 0 0

Remount shm.
> mount -o remount /dev/shm

Now for the tmp directories. We’ll be creating a 2gig sparse disk image.
> dd if=/dev/null of=/var/tmpfs.img bs=1 count=1 seek=2G
> /sbin/mkfs.ext3 /var/tmpfs.img

Now mount the /tmp and set permissions.
> mount -o loop,noexec,nosuid,rw /var/tmpfs.img /tmp
> chmod 1777 /tmp

Edit /etc/fstab and add the following:

/var/tmpfs.img /tmp ext3 loop,nosuid,noexec,rw 0 0

Now test the change.
> mount -o remount /tmp

Lets secure /var/tmp
> mv /var/tmp /var/vartmp
> ln -s /tmp /var/tmp

Reboot the system.

Moving Mysql’s Databases (Optional)

This covers moving Mysql’s database storage to a different location.

First we need to stop mysql if it is running. Type the following:
> service mysqld stop

Next copy the data to the new location and fix the ownership.
> cp -R /var/lib/mysql /home/
> mv /home/mysql /home/databases
> chown -R mysql:mysql /home/databases

Save the old databases directory.
> mv /var/lib/mysql/ /var/lib/mysql_old

Now we need to take care of the needs of some scripts that may not know the data has been moved.
> ln -s /home/databases/ /var/lib/mysql
> chown mysql:mysql /var/lib/mysql

Next edit /etc/my.cnf to look like the folloeing

# Disabling symbolic-links is recommended to prevent assorted security risks


Finally we can restart Mysql. Type the follwoing:
> service mysqld start

Notes On Cloning The Server in a Virtual Machine Enviroment

Some misc notes on using the base server.

No more network after cloning

Edit the following files:

Final Settings

  • You may want to enable the linux firewall.
  • Set your timezone in /etc/php.ini


That’s it for the basic server setup. This is an example of a standard linux server setup. Be sure to use setup or webmin to set which services you want to start at boot time. See the other pages for info on configuring servers for virtual webhosting or virtual email hosting. Remember to configure the firewall on the server.