Howto: Configure a minimal Debian Wheezy (7.5) as Web and Mailserver using Apache, PHP-fpm, MySQL, Postfix and Dovecot with virtual-mail-users based upon flat-files instead of a Database

In the following article I will document steps on how to setup a web and mailserver on Debian Wheezy (7.5). The Mailserver will provide connections using SSL/TLS. Every Mail-Users account should only be a virtual user account which is stored in a text-file instead of a MySQL database. A Database is not needed for less than 200 Mailboxes with near zero configuration changes in months. Flatfiles will serve well for that purpose.

Regards: Make sure your host is secure by disabling ssh-root-password-login but rely on key-authentication. Also consider fail2ban and setup a firewall to restrict bogus scripts to “call home” or load malicious code. Search for “my first 5 minutes on a server” for more details.

Target System overview

Program Versions

  • Debian 7.5 (Wheezy)
  • Postfix 2.9.6
  • Dovecot 2.1.7
  • Apache 2.2.22
  • PHP 5.4.4

Domains and Subdomains

  • example.com (redirects to www.example.com)
  • www.example.com (company homepage, static sites, no fancy stuff)
  • shop.example.com (ssl-enabled, ssl-forced, php-enabled via php-fpm)

Mailserver overview

  • Postfix for sending Mails, using TLS transport encryption with self-signed-certificate
  • Dovecot for receiving Mails (imap,pop), using TLS transport encryption
  • Users, Mailboxes, forwardings are stored within (3) flatfiles, no Database needed
  • FQDN is mail.example.com, serving the following mailboxes: *@example.com
  • reverse DNS-Record (PTR) points to mail.example.com

Webserver overview

  • Apache in Worker mode (using threads)
  • several virtual Hosts serving multiple domains
  • a few enabled modules (rewrite, header, expire, ssl)
  • ssl is served via self-signed-certificates
  • utilize php scripting via PHP-fpm (fastcgi)

Example DNS Zonefile

According to our target System overview I present the (anonymized) DNS-Zonefile that can be used as reference to determine configurations. Thats just reference.

$TTL 900
@   IN SOA ns1.xxxxxx.de. postmaster.xxxxxx.de. (
    2014061502   ; serial
    14400        ; refresh
    1800         ; retry
    604800       ; expire
    900 )        ; minimum

@                        IN NS      ns1.xxxxxx.de.
@                        IN NS      robotns2.xxxxxx.de.
@                        IN NS      robotns3.xxxxxx.com.

@                        IN A       11.22.33.44
mail                     IN A       11.22.33.44
www                      IN A       11.22.33.44
@                        IN MX 10   mail

As you can see, we have a MX-Record pointing to mail.example.com as well as several other subdomains configured:

  • *.example.com (@ IN A …)
  • mail.example.com
  • www.example.com

Server Preparation

First of all we will set a correct hostname by editing the hostname file:

vim /etc/hostname

A few lines must be changed within the hosts file too, pointing our localhost ip-address to the correct hostname too:

vim /etc/hosts

Install Base System Features

First we want to install basic system services. Therefore we update our package-database and upgrade any installed programs. After that, we install apache, php, mysql, postfix and dovecot.

apt-get update
apt-get upgrade
apt-get install \
    apache2-mpm-worker \
    apache2-utils \
    libapache2-mod-auth-plain \
    libapache2-mod-upload-progress \
    libapache2-svn \
    \
    php5-xsl \
    php5-xmlrpc \
    php5-sqlite \
    php5-mysql \
    php5-mcrypt \
    php5-imap \
    php5-imagick \
    php5-gd \
    php5-fpm \
    php5-curl \
    php5-cli \
    php5-rrd \
    php5-svn \
    php5 \
    \
    mysql-server \
    mysql-client \
    mysql-utilities \
    \
    postfix \
    \
    dovecot-common \
    dovecot-imapd \
    dovecot-pop3d \
    \
    libsasl2-2 \
    libsasl2-modules \
    sasl2-bin

When asked, enter a MySQL Root-Password. Also set the Postfix-Type as “Internet Mailserver”.

Remove unwanted, automatically installed packages if that happend:

apt-get remove dovecot-mysql dovecot-pgsql dovecot-sqlite

Let’s stop installed services as they are not configured correctly yet.

/etc/init.d/apache2 stop
/etc/init.d/dovecot stop
/etc/init.d/mysql stop
/etc/init.d/php5-fpm stop
/etc/init.d/postfix stop
/etc/init.d/saslauthd stop

Configure SASL

Sasl is used as a authentication backend/Service on the server. Dovecot can use sasl, we only need to install and activate the sasl service.

vim /etc/default/saslauthd

modify

START=yes
THREADS=3

Now open or create

vim /etc/postfix/sasl/smtpd.conf

and enter

pwcheck_method: saslauthd

At the end start the sasl daemon manually. On the next boot it will be started automatically.

/etc/init.d/saslauthd start

Generate an SSL certificate for Postfix and Dovecot

To enable SSL for our users, you need to generate an ssl certificate. Some mail clients may have problems with self-signed certificates, because they are not signed by a recognized certificate authority. In this case one should use commercial certificates.

It’s important that you set the Common Name to the full domain your users connect to authenticate and send mails. If you use mail.example.com as smtp host for your users, the Common Name of your SSL certificate has to be mail.example.com!

mkdir -p /etc/postfix/ssl
openssl req -x509 -newkey rsa:2048 -keyout /etc/postfix/ssl/postfix.key -out /etc/postfix/ssl/postfix.cert -days 1460 -nodes

Output:

Country Name (2 letter code) [AU]: DE
State or Province Name (full name) [Some-State]: .
Locality Name (eg, city) []: .
Organization Name (eg, company) [Internet Widgits Pty Ltd]: Example
Organizational Unit Name (eg, section) []: .
Common Name (e.g. server FQDN or YOUR name) []: mail.example.com
Email Address []: .

Read here for more details: https://www.openssl.org/docs/apps/req.html

req
PKCS#10 certificate request and certificate generating utility.

-x509
this option outputs a self signed certificate instead of a certificate request. This is typically used to generate a test certificate or a self signed root CA.

-newkey arg
this option creates a new certificate request and a new private key. The argument takes one of several forms. rsa:nbits, where nbits is the number of bits, generates an RSA key nbits in size.

-keyout filename
this gives the filename to write the newly created private key to.

-out filename
This specifies the output filename to write to or standard output by default.

-days n
when the -x509 option is being used this specifies the number of days to certify the certificate for. The default is 30 days.

-nodes
if this option is specified then if a private key is created it will not be encrypted.

Create system user for virtual mails

For handling the incoming mails we should create a separate user who has only access to the mailboxes. We create this user with groupid 5000 and userid 5000.

groupadd vmail -g 5000

The user should have been created as a system-account (flag -r), so he has no login and is not saved in /etc/shadow.

useradd vmail -r -c "virtual mail user" -d /home/vmail -m -g 5000 -u 5000

System accounts also don’t get a home directory. Because of this we need to set and create an own. We want to save our mails in /home/vmail, so we use /home/vmail as our new home directory. That folder must be created also, so we add flag -m.

Now check if everything was ok:

id vmail

This sould return you the username, userid and groupid as mentioned above.

Last we have to give user vmail all rights for its home directory:

chown -R vmail:vmail /home/vmail

Configure Postfix

Next we have to set up the configuration of postfix in its main configuration file. Therefore we backup the original configuration file first…

cp /etc/postfix/main.cf /etc/postfix/main.cf.ORIGINAL

Now lets edit the Postfix main configuration file:

vim  /etc/postfix/main.cf

Contents:

# should be the FQDN of your machine
myhostname = mail.example.com

# internet domain name of your system.
# Default is to use $myhostname without the sub-domain
mydomain = example.com

# the domain from which local emails will be from: root@example.org
myorigin = $mydomain

# in master.cf we use the flag D for single delivered-to headers
# so we need to tell dovecot that every recipient should get its own mail
dovecot_destination_recipient_limit = 1

# set to localhost, because we handle all domains via virtual_mailbox_domains
mydestination = $myhostname, localhost.$mydomain, localhost
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
inet_interfaces = all
inet_protocols = ipv4
mailbox_size_limit = 0
# 50MB message size limit
message_size_limit = 52428800
recipient_delimiter = +

# debug level for messages in log. level 2 is the default one
debug_peer_level = 2

# debug request only coming from this IP
#debug_peer_list = 127.0.0.1

# the banner your mail system will greet a client who opens a socket to us
smtpd_banner = $myhostname ESMTP $mail_name

# disable information messages to local users about new emails
biff = no

# we don't use relayhost, so set it empty
relayhost =

# disable showing why the user does not exist in your system
show_user_unknown_table_name = no

# appending .domain is the MUA's job.
append_dot_mydomain = no

# Uncomment the next line to generate "delayed mail" warnings
delay_warning_time = 4h

# local aliases for postmaster, root, ...
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases


# virtual domain options
###############################

# save destination of the incoming mails
virtual_mailbox_base = /home/vmail

# the following 3 files are 2-columns files. I will call the left column 
# the left side handed (LSH) and the right column the right side handed (RSH)
# contains the domain names for which emails will be accepted. The LSH 
# contains the names, the RSH any string but at least one char (e.g. OK)
virtual_mailbox_domains = hash:/etc/postfix/virtual_mailbox_domains

# contains the virtual mailboxes for which emails will be saved on this 
# system. 
virtual_mailbox_maps = hash:/etc/postfix/virtual_mailbox_maps

# contains the redirections. 
virtual_alias_maps = hash:/etc/postfix/virtual_alias_maps

# userid of the virtual mailbox user we created
virtual_uid_maps = static:5000

# groupid of the virtual mailbox user we created
virtual_gid_maps = static:5000

# delivery agent we use to save the mails
virtual_transport = dovecot


# TLS parameters
########################

# our self-created certificate
smtpd_tls_cert_file = /etc/postfix/ssl/postfix.cert
smtpd_tls_key_file = /etc/postfix/ssl/postfix.key

# accept certificates from other mail servers while their certificates are signed by a Certificate Authority
smtpd_tls_CAfile = /etc/ssl/certs/ca-certificates.crt
smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt

# allow using tls to connect to our mail server
smtp_use_tls = yes
smtpd_use_tls = yes

# outcomment this to allow only tls connections
#smtpd_tls_auth_only = yes

# set log level
smtpd_tls_loglevel = 1

smtpd_tls_received_header = yes
tls_random_source = dev:/dev/urandom
smtp_tls_note_starttls_offer = yes

smtpd_tls_session_cache_timeout = 3600s
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache


# SASL Authentication Settings
####################################
queue_directory = /var/spool/postfix
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
broken_sasl_auth_clients = yes

smtpd_sasl_security_options = noanonymous
smtpd_sasl_tls_security_options = $smtpd_sasl_security_options
smtpd_sasl_local_domain = $myhostname
#smtpd_sasl_application_name = smtpd

smtpd_delay_reject = yes
smtpd_helo_required = yes
smtpd_helo_restrictions = permit_mynetworks,
  #reject_non_fqdn_helo_hostname,
  reject_invalid_helo_hostname,
  permit

smtpd_sender_restrictions = permit_sasl_authenticated,
  permit_mynetworks,
  reject_non_fqdn_sender,
  reject_unknown_sender_domain,
  permit

smtpd_recipient_restrictions = reject_unauth_pipelining,
  reject_non_fqdn_recipient,
  reject_unknown_recipient_domain,
  permit_mynetworks, 
  permit_sasl_authenticated, 
  reject_unauth_destination,
  reject_unknown_sender_domain,
  reject_non_fqdn_sender,
  reject_unverified_recipient,
  permit

smtpd_client_restrictions = permit_mynetworks,
  permit_sasl_authenticated,
  permit

Now we have to tell postfix that it should deliver incoming mails to dovecot. We also want postfix listen on smtps/submission too.

For this open /etc/postfix/master.cf, not the main.cf!

vim /etc/postfix/master.cf

Contents:

...
smtp       inet n       -       -       -       -       smtpd
submission inet n       -       -       -       -       smtpd
  -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 smtpd_tls_wrappermode=yes
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
  #-o milter_macro_daemon_name=ORIGINATING

On the bottom add the dovecot deliverer:

...
# ---bottom---
# Dovecot LDA
dovecot   unix  -       n       n       -       -       pipe
  flags=DRhu user=vmail:vmail argv=/usr/lib/dovecot/deliver -f ${sender} -d ${recipient}

The 2 white spaces in front of “flags=DRhu” are important!

virtual mail users, mailboxes, domains, etc

We don’t want to create an system-user account on our server for every single mailbox. So we use virtual mailboxes and virtual users defined in files to handle incoming e-mails.

We are using: virtual_mailbox_domains with virtual_mailbox_maps.

First we need a list of all accepted domains. If you accept emails also coming from a sub-domain of a domain, you have to list it explicitly! The list has 2 columns on which the domains are listed on the left-handed side. The content of the right-handed side is not needed, so you can place there comments or something else.

vim /etc/postfix/virtual_mailbox_domains

# domain                    comment
example.com                 OK

The file virtual_mailbox_maps contains all mailboxes you want to store on your server. You can use any directory structure you want, but I prefer to sort it by the domain names. A mailbox for @example.com should be found in the directory /home/vmail/example.com.

The file is also a 2 columns file. On the left side you have to give the full e-mail address. On the right side you have to give the relative path to the mailbox. The folder structure is important for dovecot. If you have 2 mail addresses for a single mailbox, the user will only be able to login with the first one defined in the file.

vim /etc/postfix/virtual_mailbox_maps

# e-mail address                    relative save location
info@example.com                    example.com/info
support@example.com                 example.com/support
alice@example.com                   example.com/alice
bob@example.com                     example.com/bob

The third file /etc/postfix/virtual_alias_maps contains all e-mail addresses which you want to redirect to another one. It’s again a 2 columns file with the incoming e-mail address on the left side and the e-mail addresses you want to redirect on the right side. You can separate multiple destinations by a comma.

vim /etc/postfix/virtual_alias_maps

# e-mail address                        redirect to (multiple separated by ", ")
root                                    info@example.com
postmaster                              info@example.com
admin@example.com                       info@example.com
office@example.com                      info@example.com
license@example.com                     alice@example.com

To check whether your virtual mail information is valid you can call

postmap -q postmaster hash:/etc/postfix/virtual_alias_maps

Whenever we need to change the virtual_* files above, we also need to tell postfix of it:

postmap hash:/etc/postfix/virtual_mailbox_domains
postmap hash:/etc/postfix/virtual_mailbox_maps
postmap hash:/etc/postfix/virtual_alias_maps
/etc/init.d/postfix reload

Configure Dovecot Service

We have to add the supported protocols and who’s responsible for authentication. The lda protocol is needed for interacting with postfix. Dovecot comes with a bunch of configuration files in ./conf.d subdirectory i will not mess with. Instead we will create a custom-file which holds all our settings:

vim /etc/dovecot/conf.d/99-custom.conf


listen = *
protocols = imap pop3
log_timestamp = "%Y-%m-%d %H:%M:%S "
disable_plaintext_auth = yes

# Outlook Express and Windows Mail works only with LOGIN mechanism, not the standard PLAIN
auth_mechanisms = plain login

mail_access_groups = vmail
default_login_user = vmail
first_valid_uid=5000
last_valid_uid=5000
first_valid_gid=5000
last_valid_gid=5000
mail_privileged_group = vmail
mail_location = maildir:/home/vmail/%d/%n/Maildir
postmaster_address = postmaster@example.com

passdb {
    driver = passwd-file
    args = scheme=SHA1 /etc/dovecot/users.conf
}
userdb {
    driver = static
    args = uid=5000 gid=5000 home=/home/vmail/%d/%n/Maildir allow_all_users=yes
}
service auth {
    # places the Dovecot SASL socket in the given path
    unix_listener /var/spool/postfix/private/auth {
        mode = 0660
        # Assuming the default Postfix user and group
        user = postfix
        group = postfix        
    }
    user = root
}
service imap-login {
    process_min_avail = 1
    user = vmail
}

# begin of ssl configuration<
ssl_cert = </etc/postfix/ssl/postfix.cert
ssl_key = </etc/postfix/ssl/postfix.key
# end of ssl configuration>

Add Dovecot users list

Now we need to create the users database. Create an empty text-file

touch  /etc/dovecot/users.conf

It is important that we set the correct permissions on /etc/dovecot/users.conf and /etc/dovecot/dovecot.conf.

# vmail must be able to read the dovecot configuration
chgrp vmail /etc/dovecot/dovecot.conf
chmod g+r /etc/dovecot/dovecot.conf

# don't let the world read your dovecot users and passwords
chown root:root /etc/dovecot/users.conf
chmod 600 /etc/dovecot/users.conf

Now run the following command and enter a password.

doveadm pw -s SHA1

It outputs you a hash like {SHA1}Za3aQyaRRXZAXJ46YvSQTZZXOGM= Which is the SHA1 representation of your give password.

Next open /etc/dovecot/users.conf and add an user:

alice@example.com:{SHA1}Za3aQyaRRXZAXJ46YvSQTZZXOGM=

Every e-mail address defined in /etc/postfix/virtual_mailbox_maps should be found in the users.conf!

Setup / Prepare Webserver environment

In our setup we will serve two different websites: - a website with static files (www.example.com) - a shop utilizing php-fpm and MySQL via SSL (shop.example.com)

First lets create a shell user:

useradd -m -s /bin/bash user1
passwd user1

This will create a users homefolder which will hold our websites files and apache logfiles. Lets create some directories used from apache/php-fpm:

mkdir -p /home/user1/www/www.example.com
mkdir -p /home/user1/www/shop.example.com
mkdir -p /home/user1/logs/www.example.com
mkdir -p /home/user1/logs/shop.example.com

user1 must be able to read and write into his www-folders:

chown -R user1:user1 /home/user1/www

Feel free to put the static website files (html, css, js) into ~/www/www.example.com/ and put your shop-related files into ~/www/shop.example.com.

Setup and configure PHP as FPM (FastCGI)

Just Basics:

rm /etc/php5/fpm/pool.d/www.conf
vim /etc/php5/fpm/php-fpm.conf


...
...
[global]
; alert, error, warning, notice, debug
log_level = notice

; If 10 PHP-FPM child processes exit with SIGSEGV or SIGBUS 
; within 1 minute then PHP-FPM restart automatically. This configuration also sets 
; 10 seconds time limit for child processes to wait for a reaction on signals from 
; master.
emergency_restart_threshold = 10
emergency_restart_interval = 1m
process_control_timeout = 10s
...
...

Create a PHP-FPM Worker Pool for our shop-website: shop.example.com

vim /etc/php5/fpm/pool.d/shop.example.com.conf


[shop.example.com]

; Unix user/group of processes
; Note: The user is mandatory. If the group is not set, the default user's group
;       will be used.
user = user1
group = user1

; The address on which to accept FastCGI requests.
; Valid syntaxes are:
;   'ip.add.re.ss:port'    - to listen on a TCP socket to a specific address on
;                            a specific port;
;   'port'                 - to listen on a TCP socket to all addresses on a
;                            specific port;
;   '/path/to/unix/socket' - to listen on a unix socket.
; Note: This value is mandatory.
listen = 127.0.0.1:9001

; Set listen(2) backlog.
; Default Value: 128 (-1 on FreeBSD and OpenBSD)
listen.backlog = 128

; List of ipv4 addresses of FastCGI clients which are allowed to connect.
; Equivalent to the FCGI_WEB_SERVER_ADDRS environment variable in the original
; PHP FCGI (5.2.2+). Makes sense only with a tcp listening socket. Each address
; must be separated by a comma. If this value is left blank, connections will be
; accepted from any ip address.
; Default Value: any
listen.allowed_clients = 127.0.0.1

; Choose how the process manager will control the number of child processes.
; Possible Values:
;   static  - a fixed number (pm.max_children) of child processes;
;   dynamic - the number of child processes are set dynamically based on the
;             following directives. With this process management, there will be
;             always at least 1 children.
;             pm.max_children      - the maximum number of children that can
;                                    be alive at the same time.
;             pm.start_servers     - the number of children created on startup.
;             pm.min_spare_servers - the minimum number of children in 'idle'
;                                    state (waiting to process). If the number
;                                    of 'idle' processes is less than this
;                                    number then some children will be created.
;             pm.max_spare_servers - the maximum number of children in 'idle'
;                                    state (waiting to process). If the number
;                                    of 'idle' processes is greater than this
;                                    number then some children will be killed.
;  ondemand - no children are created at startup. Children will be forked when
;             new requests will connect. The following parameter are used:
;             pm.max_children           - the maximum number of children that
;                                         can be alive at the same time.
;             pm.process_idle_timeout   - The number of seconds after which
;                                         an idle process will be killed.
; Note: This value is mandatory.
pm = dynamic

; The number of child processes to be created when pm is set to 'static' and the
; maximum number of child processes when pm is set to 'dynamic' or 'ondemand'.
; This value sets the limit on the number of simultaneous requests that will be
; served. Equivalent to the ApacheMaxClients directive with mpm_prefork.
; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP
; CGI. The below defaults are based on a server without much resources. Don't
; forget to tweak pm.* to fit your needs.
; Note: Used when pm is set to 'static', 'dynamic' or 'ondemand'
; Note: This value is mandatory.
pm.max_children = 10

; The number of child processes created on startup.
; Note: Used only when pm is set to 'dynamic'
; Default Value: min_spare_servers + (max_spare_servers - min_spare_servers) / 2
pm.start_servers = 2

; The desired minimum number of idle server processes.
; Note: Used only when pm is set to 'dynamic'
; Note: Mandatory when pm is set to 'dynamic'
pm.min_spare_servers = 1

; The desired maximum number of idle server processes.
; Note: Used only when pm is set to 'dynamic'
; Note: Mandatory when pm is set to 'dynamic'
pm.max_spare_servers = 5

; The number of requests each child process should execute before respawning.
; This can be useful to work around memory leaks in 3rd party libraries. For
; endless request processing specify '0'. Equivalent to PHP_FCGI_MAX_REQUESTS.
; Default Value: 0
pm.max_requests = 5000

; The number of seconds after which an idle process will be killed.
; Note: Used when pm is set to 'ondemand'
; 900 = 15m
pm.process_idle_timeout = 900

; The timeout for serving a single request after which the worker process will
; be killed. This option should be used when the 'max_execution_time' ini option
; does not stop script execution for some reason. A value of '0' means 'off'.
; Available units: s(econds)(default), m(inutes), h(ours), or d(ays)
; Default Value: 0
request_terminate_timeout = 310s

; Set open file descriptor rlimit.
; Default Value: system defined value
;rlimit_files = 1024

; Set max core size rlimit.
; Possible Values: 'unlimited' or an integer greater or equal to 0
; Default Value: system defined value
;rlimit_core = 0

; Chdir to this directory at the start.
; Note: relative path can be used.
; Default Value: current directory or / when chroot
chdir = /

; Redirect worker stdout and stderr into main error log. If not set, stdout and
; stderr will be redirected to /dev/null according to FastCGI specs.
; Note: on highloaded environement, this can cause some delay in the page
; process time (several ms).
; Default Value: no
catch_workers_output = yes

; Pass environment variables like LD_LIBRARY_PATH. All $VARIABLEs are taken from
; the current environment.
; Default Value: clean env
env[HOSTNAME] = $HOSTNAME
env[PATH] = /usr/local/bin:/usr/bin:/bin
env[TMP] = /tmp
env[TMPDIR] = /tmp
env[TEMP] = /tmp

;php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f user1@example.com
php_flag[display_errors] = off
php_admin_flag[log_errors] = on
php_admin_value[error_log] = /home/user1/logs/shop.example.com/error_log
php_admin_value[date.timezone] = Europe/Berlin
php_admin_value[memory_limit] = 128M
php_admin_value[upload_max_filesize] = 50M
php_admin_value[post_max_size] = 50M
php_admin_value[max_execution_time] = 300
php_admin_value[upload_tmp_dir] = /tmp

Setup Apache Webserver, Modules and Vhost

Activate some basic apache modules

a2enmod rewrite
a2enmod ssl
a2enmod headers
a2enmod fastcgi
a2enmod actions
a2enmod expires

Make several security related enhancements

vim /etc/apache2/conf-enabled/security.conf

ServerTokens Prod
ServerSignature Off

<DirectoryMatch "/\.svn">
   Require all denied
</DirectoryMatch>

Create a self-signed ssl certificate for shop.example.com

mkdir -p /etc/apache2/ssl
cd /etc/apache2/ssl

openssl genrsa 2048 > shop.example.com.key
openssl req -new -x509 -nodes -sha1 -days 365 -key shop.example.com.key > shop.example.com.cert
...[enter shop.example.com for the Common Name]...
openssl x509 -noout -fingerprint -text < shop.example.com.cert > shop.example.com.info
cat shop.example.com.cert shop.example.com.key > shop.example.com.pem
chmod 400 shop.example.com.key shop.example.com.pem

Create a VirtualHost for www.example.com

That Apache VirtualHost serves files from the directory

/home/user1/www/www.example.com

Visitors will be redirected to www.example.com if they called example.com without www (RewriteRule…). That VirtualHost will have no PHP-capabilities. .htaccess files are fully enabled (AllowOverride All).

vim /etc/apache2/sites-enabled/www.example.com.conf


<VirtualHost 11.22.33.44:80>
    ServerName example.com

    RewriteEngine On
    RewriteCond %{HTTP_HOST} !^www\.$ [NC]
    RewriteRule ^.*$ http://www.%{SERVER_NAME}%{REQUEST_URI} [L,R=permanent]
</VirtualHost>

<VirtualHost 11.22.33.44:80>
    ServerName www.example.com

    LogLevel warn
    CustomLog /home/user1/logs/www.example.com/access_log combined
    ErrorLog  /home/user1/logs/www.example.com/error_log

    DocumentRoot /home/user1/www/www.example.com/
    <Directory /home/user1/www/www.example.com/>
        Options -Indexes FollowSymLinks MultiViews
        AllowOverride All
    </Directory>
</VirtualHost>

You may invoke the following command to test the apache configuration at any time:

apachectl -t

Create a VirtualHost for shop.example.com that is only accessible via ssl

That Apache VirtualHost serves files from the directory

/home/user1/www/shop.example.com

Visitors will be redirected to https://shop.example.com if they called shop.example.com without https (RewriteRule…). That VirtualHost will have PHP-capabilities via PHP-fpm. .htaccess files are fully enabled (AllowOverride All).

vim /etc/apache2/sites-enabled/shop.example.com.conf

<VirtualHost 11.22.33.44:80>
    ServerName shop.example.com

    LogLevel warn
    CustomLog /home/user1/logs/shop.example.com/access_log combined
    ErrorLog  /home/user1/logs/shop.example.com/error_log

    RewriteEngine On
    RewriteRule ^.*$ https://%{SERVER_NAME}%{REQUEST_URI} [L,R=permanent]
</VirtualHost>

<VirtualHost 11.22.33.44:443>
    ServerName shop.example.com

    LogLevel warn
    CustomLog /home/user1/logs/shop.example.com/access_log combined
    ErrorLog  /home/user1/logs/shop.example.com/error_log

    DocumentRoot /home/user1/www/shop.example.com/
    <Directory /home/user1/www/shop.example.com/>
        AllowOverride All
        Options FollowSymLinks -Indexes IncludesNoExec
    </Directory>

    FileETag None
    <IfModule mod_headers.c>
        Header unset Pragma
        Header unset ETag
        Header append Cache-Control "public"
    </IfModule>
    <IfModule mod_expires.c>
        <FilesMatch "\.(ico|pdf|flv|jpg|jpeg|png|gif|swf|mp3|mp4|css|js|svg)$">
            ExpiresActive On
            ExpiresDefault "access plus 1 week"
        </FilesMatch>
    </IfModule>

    <IfModule mod_fastcgi.c>
        AddHandler php5-fcgi .php
        Action php5-fcgi /php5-fcgi
        Alias /php5-fcgi /usr/lib/cgi-bin/php5-fcgi
        FastCgiExternalServer /usr/lib/cgi-bin/php5-fcgi -host 127.0.0.1:9001 -pass-header Authorization -flush -idle-timeout 330
    </IfModule>

    <IfModule mod_ssl.c>
        SSLEngine on
        SSLCertificateFile /etc/apache2/ssl/shop.example.com.cert
        SSLCertificateKeyFile /etc/apache2/ssl/shop.example.com.key

        SSLProtocol -ALL +SSLv3 +TLSv1 +TLSv1.1 +TLSv1.2
        SSLCipherSuite ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-ECDSA-RC4-SHA:AES128:AES256:RC4-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK
        SSLHonorCipherOrder on
        SSLCompression off

        # Add six earth month HSTS header for all users...
        # Enable this if your want HSTS (recommended, but be careful)
        Header add Strict-Transport-Security "max-age=15768000"
    </IfModule>
</VirtualHost>

You may invoke the following command to test the apache configuration at any time:

apachectl -t

Configure logrotate for specific apache logfiles

As our apache virtual hosts sites are located within each users www-subdirectory and their logfiles are located within a logs subfolder under their home directory, we need to tell logrotate to rotate apache logfiles within those folders.

example of our example.com logfile target:

/home/user1/logs/www.example.com/{error_log,access_log}

Rotate files on a weekly basis, compress those files, keep 1024 files, reload apache after rolling files. chmod 604 created files and assign those files to user root, group adm. That is a 100% copy from /etc/logrotate.de/apache but with an altered logfile location…

vim /etc/logrotate.d/apache2-homefolders

/home/*/logs/*/*_log {
    weekly
    missingok
    rotate 1024
    compress
    delaycompress
    notifempty
    create 640 root adm
    sharedscripts
    postrotate
    if [ -f "`. /etc/apache2/envvars ; echo ${APACHE_PID_FILE:-/var/run/apache2.pid}`" ]; then
        /etc/init.d/apache2 reload > /dev/null
    fi
    endscript
}

We’re done at this point. The following topics are some leftovers, URLs, trial and errors, etc. Left here for further reading.

TODO ocsp stapling

# OCSP Stapling, only in httpd 2.3.3 and later
SSLUseStapling on
SSLStaplingResponderTimeout 5
SSLStaplingReturnResponderErrors off
SSLStaplingCache shmcb:/var/run/ocsp(128000)

GMail certificate error

The following error occurs if your server does not have the current root certificate of Googles CA.
December 23 21:38:15 vps4718 postfix/smtp[17581]: certificate verification failed for gmail-smtp-in.l.google.com: num=20:unable to get local issuer certificate
December 23 21:38:15 vps4718 postfix/smtp[17581]: certificate verification failed for gmail-smtp-in.l.google.com: num=27:certificate not trusted

Reason: Gmail switched the Certificate Authority to Equifax.
But this certificate does not exist in the certificate directory in Debian.
So we need to download it separately, convert it, save it into the certificate directory and add it to the certificates-file.

We can download the certificate from: https://www.geotrust.com/resources/root-certificates/index.html (download the Root 1 – Equifax Secure Certificate Authority as DER encoded X.509)
After downloading it we have to convert it to a pem file:
openssl x509 -inform der -in Equifax_Secure_Certificate_Authority_DER.cer -out Equifax_Secure_Certificate_Authority_DER.pem

Now we can verfiy the certificate:
openssl verify Equifax_Secure_Certificate_Authority_DER.pem

Last we have to add the pem to our certificate folder. The location is /etc/ssl/certs.
Because we added /etc/ssl/certs/ca-certificates.crt in our postfix configuration, we have to add the content of the new pem file with
cat Equifac_Secure_Certificate_Authority_DER.pem >> /etc/ssl/certs/ca-certificates.crt

explain PFC, Perfect forward secrecy, apache 2.4 is needed for that!

https://www.ssllabs.com/ssltest/
https://sslcheck.globalsign.com/

http://www.heise.de/security/artikel/Zukunftssicher-Verschluesseln-mit-Perfect-Forward-Secrecy-1923800.html
https://community.qualys.com/blogs/securitylabs/2013/08/05/configuring-apache-nginx-and-openssl-for-forward-secrecy
http://www.lorrin.org/blog/2013/07/03/configuring-apache-for-perfect-forward-secrecy/
https://wiki.mozilla.org/Security/Server_Side_TLS

register debian jessie repositories

vim /etc/apt/sources.list

deb http://http.debian.net/debian jessie main
deb-src http://http.debian.net/debian jessie main

register dotdeb.org repositories

deb http://packages.dotdeb.org wheezy all
deb-src http://packages.dotdeb.org wheezy all
deb http://packages.dotdeb.org wheezy-php55 all
deb-src http://packages.dotdeb.org wheezy-php55 all

wget http://www.dotdeb.org/dotdeb.gpg
apt-key add dotdeb.gpg

check mailserver configuration using external services

https://www.wormly.com/test_smtp_server

Notes on apache + ssl + virtual hosts

Due to the nature of the SSL layer in HTTPS, negotiating a secure connection 
happens before the HTTP protocol is initiated. That means that at the time the 
SSL layer is in play, the "Host" header has not been sent and, therefore, apache 
cannot determine which NameVirtualHost to use. But, frankly, if you're 
self-signing your certificates, the browser is going to throw a warning anyway. 
Might as well just make it as generic as possible and then all traffic running 
on through the HTTPS port will share the same certificate.