Debian: Setup owncloud with TLS (LetsEncrypt) and protected by AppArmor

Hi,

this Tuturial describes the steps to setup owncloud on top of debian/raspian with lighttpd as webserver and https encryption with certificates sign by Let’s encrypt campain.

Operating System

Install a minimal debian jessie system.

Login as root. On raspbian login as user pi change to root.

pi@raspown ~ $ sudo su -

If you want to setup the cloud by ssh and you want to login with root an password authentification set “PermitRootLogin” to “yes” in /etc/ssh/sshd_config.

Don’t forget to set it back to “without-password” after finishing.

Install required packages

root@raspown:~# apt-get -y install php5 php5-common php5-gd php-xml-parser php5-intl php5-json php5-mcrypt
root@raspown:~# apt-get -y install php5-sqlite php5-mysql php5-pgsql smbclient php5-curl curl libcurl3 libcurl3-dev
root@raspown:~# apt-get -y install lighttpd php5-cgi
root@raspown:~# apt-get -y remove apache2 apache2-bin apache2-data libaprutil1-dbd-sqlite3 libaprutil1-ldap liblua5.1-0
root@raspown:~# apt-get -y install python-pip
root@raspown:~# apt-get -y install git bash

For Raspbian Users. Do not start graphical desktop, just boot in text mode

root@raspown:~#  systemctl get-default
root@raspown:~#  systemctl set-default -f multi-user.target

Set the hostname respectily full qualified domain name

root@raspown:~#  hostnamectl set-hostname yourhost.yourdomain.net

Configure a static IP Address. Disable old (init) config style by saving /etc/network/interfaces and creating an empty file

root@raspown:~# mv /etc/network/interfaces /etc/network/interfaces.save
root@raspown:~# touch /etc/network/interfaces

Setting up eth0. Create a file /etc/systemd/network/eth0.network, Name it as you want.

[Match]
# You can also use wildcards. Maybe you want enable dhcp
# an all eth* NICs
Name=eth0
[Network]
#DHCP=v4
# static IP
# 192.168.100.2 netmask 255.255.255.0
Address=192.168.100.2/24
Gateway=192.168.100.1
DNS=192.168.100.1

and enable systemd-networkd and resolved

root@raspown:~# systemctl enable systemd-networkd.service
root@raspown:~# systemctl enable systemd-resolved.service

Define DNS Server. You can add the DNS Server either in the Network section of the NIC adapter config (see above) or in /etc/systemd/resolved.conf
Create a symlink

root@raspown:~# mv /etc/resolv.conf /etc/resolv.conf.save
root@raspown:~# ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf

Enable lighttpd on Port 80

Start the webserver and see if its running

root@raspown # lighty-enable-mod fastcgi-php
root@raspown # systemctl enable lighttpd.service
root@raspown # systemctl start lighttpd.service
root@raspown # journalctl -xn

https certificates

Lets Encrypt

Create the certificates. Get project sources

root@raspown:~# git clone https://github.com/letsencrypt/letsencrypt

Install additional packages needed by letsencrypt create the virtual environment. Do not use the standard shell on Debian 8 based systems. Also the Python Package Index must be reachable:

root@raspown:~/letsencrypt# bash letsencrypt-auto -v


If you want to use your own key and signing request:
Create your server key and csr by openssl. Do not enter a challenge password at the end of the creation process otherwise lighttpd will prompt at each start for it.

root@raspown:~# mkdir -p /etc/lighttpd/ssl

Create Key and Signing request. For a better quality of random numbers consider to generate the key on a “real” PC. Note: The CommonName and the the subjectAltName of the siging request must set to the domain name to which the certificate should belongs to. The output format of the csr must set to DER.
Create an x509 v3 exentension file /etc/lighttpd/ssl/ssl_v3.cfg

[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[ v3_req ]
basicConstraints = CA:FALSE
nsCertType = server,client
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage=serverAuth,clientAuth,emailProtection,codeSigning,timeStamping
subjectAltName = @alt_names
[alt_names]
DNS.1 = yourhost.yourdomain.net
# DNS.2 =
[req_distinguished_name]

Generate Key and signing request

root@raspown:~# openssl req -nodes -new -newkey rsa:2048 -sha256 -outform DER -out /etc/lighttpd/ssl/server.der  -keyout /etc/lighttpd/ssl/server.key -subj '/C=DE/ST=Franken/L=Nuremberg/O=Your Company/OU=Your OU/emailAddress=webmaster@yourdomain.net/CN=yourhost.yourdomain.net/' -config /etc/lighttpd/ssl/ssl_v3.cfg
# Sets the following parameters
# Country Name (2 letter code) [AU]:DE
# State or Province Name (full name) [Some-State]:Franken
# Locality Name (eg, city) []:Nuremberg
# Organization Name (eg, company) [Internet Widgits Pty Ltd]:Your Company
# Organizational Unit Name (eg, section) []:Your OU
# Common Name (e.g. server FQDN or YOUR name) []:yourhost.yourdomain.net
# Email Address []:webmaster@yourdomain.net
# A challenge password []:
# An optional company name []:

Verify that your csr contains all your parameter:

root@raspown:~# openssl req -inform der -in /etc/lighttpd/ssl/server.der -noout -text

Get the certificate from the LetsEncrypt CA. This example is based on the webroot authenticator to validate the domain (DV). Means, the letsencrypt-auto script creates a file in the webroot of your webserver which will be read during the domain validation process by the LetsEncrypt webservice.

Before you call the script make sure your webserver is reachable from the internet by your dns name at port 80 (letsencrypt http-01 challenge).
If your Webserver is behind a DSL Router ensure you have configured a port forwarding rule from port 80 to the internal ip address of your webserver. The default lighttpd Webroot for Port 80 is at /var/www/html.
If you want to use your own csr and webroot authentification you have to specify a webroot-map in letsencrypts config file to define a conjunction between domain name and webroot path otherwise an error “PluginError–webroot-path must be set” occurs.
Create a config file /etc/letsencrypt/cli.ini

authenticator = webroot
webroot-path = /var/www/html
webroot-map = {"yourhost.yourdomain.net" : "/var/www/html"}

root@raspown:~/letsencrypt# bash letsencrypt-auto  certonly --config /etc/letsencrypt/cli.ini  --agree-tos --csr /etc/lighttpd/ssl/server.der --cert-path /etc/lighttpd/ssl/server.pem

And set restricted permissions for csr, certificate and key.

root@raspown:~# cat /etc/lighttpd/ssl/server.key /etc/lighttpd/ssl/server.crt > /etc/lighttpd/ssl/server.pem
root@raspown:~# chown -R www-data:root /etc/lighttpd/ssl/
root@raspown:~# chmod 460 /etc/lighttpd/ssl/*
root@raspown:~# chown -R www-data:www-data /etc/lighttpd/ssl/server.key 
root@raspown:~# chmod 400 /etc/lighttpd/ssl/server.key

For testing

For testing https you can use selfsigned certificates

root@raspown:~# mkdir -p /etc/lighttpd/ssl/
root@raspown:~# openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/lighttpd/ssl/server.key -out /etc/lighttpd/ssl/server.crt
root@raspown:~# cat /etc/lighttpd/ssl/server.key /etc/lighttpd/ssl/server.crt > /etc/lighttpd/ssl/server.pem
root@raspown:~# chown -R www-data:root /etc/lighttpd/ssl/
root@raspown:~# chmod 460 /etc/lighttpd/ssl/*

Configure Lighttpd

Append the following lines to your config file /etc/lighttpd/lighttpd.conf , ensure that the owncloud data directory is not readable from the web.


# If you want to use HTTP logging (troubleshooting) insert both lines below
# Consider to mount the log folder /var/log/lighttpd as 
# tmpfs (but logs are lost after reboot) to avoid excessive writing to the SD Card.
# Add fstab
# tmpfs           /var/log/lighttpd   tmpfs  rw,size=256M,noexec,nodev,nosuid,uid=33,gid=33   0   0
server.modules += ( "mod_accesslog" )
accesslog.filename = "/var/log/lighttpd/access.log"
# SSL Server settings
# redirect all to https
$HTTP["scheme"] == "http" {
     # redirect all http traffic to https, expect the letsencrypt webroot auth requests
     $HTTP["url"] !~ "^/\.well-known/" {
         # capture vhost name with regex conditiona -> %0 in redirect pattern
         # must be the most inner block to the redirect rule
         $HTTP["host"] =~ ".*" {
             url.redirect = (".*" => "https://%0$0")
         }
    }
}
$SERVER["socket"] == ":443" {
  ssl.engine = "enable"
  ssl.pemfile = "/etc/lighttpd/ssl/server.pem"
  # ssl.ca-file = "/etc/lighttpd/ssl/server.crt"
  # Enter your hostname
  server.name = "yourhost.yourdomain.net"
  server.document-root = "/var/www/owncloud"
  ssl.use-sslv2 = "disable"
  ssl.use-sslv3 = "disable"
  ssl.use-compression = "disable"
  ssl.honor-cipher-order = "enable"
  # A more secure cipher list. Not working with older Webbrowsers!
  ssl.cipher-list = "EECDH+AESGCM:EDH+AESGCM:AES128+EECDH:AES128+EDH"
  server.errorlog = "/var/log/lighttpd/serror.log"
  accesslog.filename = "/var/log/lighttpd/saccess.log"
}
# Owncloud Data directory
$HTTP["url"] =~ "^/oc/data/" {
     url.access-deny = ("")
}
# Owncloud root
$HTTP["url"] =~ "^/oc($|/)" {
     dir-listing.activate = "disable"
}
# Letsencrypt webauth root
$HTTP["url"] =~ "^/\.well-known/" {
     dir-listing.activate = "disable"
}
# Not Owncloud or Webauth
$HTTP["url"] !~ "^/oc$|^/oc/|^/\.well-known/" {
     url.access-deny = ("")
}

Create the webserver root directory for your owncloud instance

root@raspown # mkdir -p /var/www/owncloud
root@raspown # chown www-data:www-data /var/www/owncloud

Start the webserver and see if its running

root@raspown # lighty-enable-mod fastcgi-php
root@raspown # systemctl enable lighttpd.service
root@raspown # systemctl start lighttpd.service
root@raspown # journalctl -xn

Owncloud

Installing owncloud(In this tutorial 8.2)

root@raspown # cd /var/www/owncloud
root@owncloud:/var/www/owncloud#  wget https://download.owncloud.org/community/owncloud-8.2.1.tar.bz2
root@owncloud:/var/www/owncloud#  tar -xvjf owncloud-8.2.1.tar.bz2

Owncloud is now extracted to the subfolder owncloud in the webserver root /var/www/owncloud . I renamed this folder to oc. If you plan another name alter also the deny and dir-listing directive in lighttpd.conf to your folder.

root@owncloud:/var/www/owncloud# mv owncloud oc

Remove owncloud sources

root@owncloud:/var/www/owncloud# rm owncloud-8.2.1.tar.bz2

Set Folder permissions

root@owncloud:/var/www/owncloud# cd oc
root@owncloud:/var/www/owncloud/oc# mkdir -p data
root@owncloud:/var/www/owncloud/oc# chmod -R o-wxr /var/www/owncloud
root@owncloud:/var/www/owncloud/oc# chown -R www-data:www-data /var/www/owncloud

Database

The default database backend of owncloud is sqlite. If you plan to synchronize your Data with the Ownclound Desktop Client a real Database System is recommended.
Here are the steps to setup a mysql Datebase. Install the server and choose secure password.

root@raspown # apt-get -y install mysql-server

Secure your installation

root@raspown # mysql_secure_installation
Remove anonymous users? [Y/n] y
 ... Success!
Disallow root login remotely? [Y/n] y
 ... Success!
Remove test database and access to it? [Y/n] y
 - Dropping test database...
 ... Success!
Reload privilege tables now? [Y/n] y
 ... Success!

Open the MySQL Console as Database root

root@raspown #  mysql -u root -p

Create a User ocuser (Replace ocpassword with your Password 🙂 ) and the database owncloud and permit the user to the database.

mysql> create database owncloud;
mysql> CREATE USER 'ocuser'@'localhost' identified by 'ocpassword';
mysql> grant all on owncloud.* to 'ocuser'@'localhost';
mysql> FLUSH PRIVILEGES;

If you want to change the password

mysql> use mysql;
mysql> SET PASSWORD FOR 'ocuser'@'localhost' = PASSWORD('ocpassword');

Securing the installation

AppArmor

I use AppArmor to secure lighttpd and mysql. The raspbian kernel does not support AppArmor out of the box. To use AppArmor you have to recompile the kernel. My post provides a script which supports you :-). Debian Jessie has the support for AppArmor already included.

Installing and activating, on a PC with grub booloader add apparmor=1 security=apparmor to the kernel parameters in /etc/default/grub

root@raspown # apt-get install apparmor apparmor-profiles apparmor-utils
root@raspown # cp /etc/default/grub /etc/default/grub.org
root@raspown # perl -pi -e 's,GRUB_CMDLINE_LINUX="(.*)"$,GRUB_CMDLINE_LINUX="$1 apparmor=1 security=apparmor",' /etc/default/grub
root@raspown # update-grub
root@raspown # reboot

On an Raspberry edit /boot/cmdline.txt

root@raspown # sed -e's/\( root=\/dev\/mmcblk0p2\)/\1 apparmor=1 security=apparmor/g' /boot/cmdline.txt --in-place=.bak

Check if apparmor is correct installed

root@raspown # aa-status

Note: If error “apparmor filesystem is not mounted.” occurs you have to forgot to call update-grub 😉
By default a mysql profiles is loaded and enforced. Create a lighttpd profile /etc/apparmor.d/usr.sbin.lighttpd This profile is based on the example shipped with debian /usr/share/doc/apparmor-profiles/extras/usr.sbin.lighttpd and OpenSUSE


# ------------------------------------------------------------------
#
#    Copyright (C) 2002-2005 Novell/SUSE
#
#    This program is free software; you can redistribute it and/or
#    modify it under the terms of version 2 of the GNU General Public
#    License published by the Free Software Foundation.
#
# ------------------------------------------------------------------
# vim:syntax=apparmor

#include 

/usr/sbin/lighttpd {
  #include 
  #include 
  #include 
  # needed to change max file descriptors
  capability sys_resource,
  # network service ;)
  capability net_bind_service,
  # changing the uid/gid on startup
  capability setgid,
  capability setuid,
  /etc/ld.so.cache r,
  /etc/lighttpd r,
  /etc/lighttpd/*.conf r,
  /etc/lighttpd/conf.d/*.conf r,
  /etc/lighttpd/auth.d/* r,
  /etc/lighttpd/vhosts.d r,
  /etc/lighttpd/vhosts.d/* r,
  /usr/sbin/lighttpd mix,

  /usr/lib/lighttpd/*.so mr,
  /usr/lib64/lighttpd/*.so mr,

  /etc/ssl/private/*.pem r,
  /etc/lighttpd/ssl/server.pem r,
  
  # home dir. e.g. used for sockets.
  /var/lib/lighttpd/ r,
  /var/lib/lighttpd/** rwl,
  
  # mod_compress cache
  /var/cache/lighttpd/ r,
  /var/cache/lighttpd/** rwl,
  
  # pid
  /{,var/}run/lighttpd.pid rwl,
  
  # log files
  /var/log/lighttpd/*.log rw,
  
  # Socket
  /run/lighttpd/* w,
  
  # exec 
  # /usr/bin/php-cgi Cx,
  /usr/bin/php5-cgi Cx,
  
  # include_shell
  /bin/bash mix,
  /bin/dash mix,
  /bin/zsh mix,
  /bin/cat mix,

  # http docs
  /var/www/ r,
  /var/www/** r,
	
  
  # Debian/Ubuntu integration in default installation
  #include 
  /etc/mime.types r,
  /usr/share/lighttpd/ r,
  /usr/share/lighttpd/*.pl rmix,
  /etc/lighttpd/conf-available/ r,
  /etc/lighttpd/conf-available/*.conf r,
  /etc/lighttpd/conf-enabled/ r,
  /etc/lighttpd/conf-enabled/*.conf r,
  
  profile /usr/bin/php5-cgi {
    #include 
    /etc/* r,
    /etc/ssl/openssl.cnf r,
    /etc/php/** r,
    /etc/php5/** r,
    /lib/lib*so* mr,
    /var/lib/php5/sessions/* rwk,
    /var/www/ r,
    /var/www/** r,
    /var/www/owncloud/oc/apps/** rwk,
    /var/www/owncloud/oc/config/** rwk,
    /var/www/owncloud/oc/data/** rwk,	
    /tmp/ r,
    /tmp/* rwk,
    /usr/share/mysql/charsets/Index.xml r,
    /usr/bin/php5-cgi r,
    /usr/lib/lib*so* mr,
    /usr/lib{,32,64}/** mr,
  }  
}

Set it in enforce mode

root@raspown # aa-enforce /usr/sbin/lighttpd
root@raspown # systemctl restart lighttpd.service

Add the line

/etc/ld.so.cache r,

also into /usr/sbin/mysqld {} Section of /etc/apparmor.d/usr.sbin.mysqld

root@raspown # aa-enforce /usr/sbin/mysqld

If have already tested the apparmor profile for a while. If owncloud shows unexpected behaviours check the syslog for apparmor DENIES and send it to me:

root@raspown # journalctl -x --since yesterday|grep 'apparmor="DENIED"'

Tripwire

Next step in security is to monitor filesystem changes. Here are the steps to handle this with tripwire. For further information see documention
Install tripwire

root@raspown # apt-get install tripwire

Create a site key. The site key is used for creating and signing the policy

root@raspown # twadmin -m G -S /etc/tripwire/site.key  -Q YourSecureSiteKeyPassword

Create a local key. The local key is used checking file integrity and update database on changes (OS Update..)

root@raspown # twadmin -m G -L /etc/tripwire/${HOSTNAME}-local.key  -P  -Q YourSecureLocalKeyPassword

Adjust the config /etc/tripwire/twcfg.txt as needed and create the config file from the text template

root@raspown # twadmin --create-cfgfile -S /etc/tripwire/site.key /etc/tripwire/twcfg.txt

Adjust the default policy /etc/tripwire/twpol.txt to your system. This is an good overview.
and append the rules for owncloud to the end of the file /etc/tripwire/twpol.txt


#
# owncloud
#
#
(
  rulename = "owncloud Directories",
  severity = $(SIG_MED)
)
{
        /var/www/                         -> $(SEC_BIN) (recurse = 1) ;
        /var/www/owncloud/oc/config       -> $(SEC_CONFIG) (recurse = 1) ;
        !/var/www/owncloud/oc/data;
}

Create the policy file from text file

root@raspown # twadmin --create-polfile /etc/tripwire/twpol.txt

Do the init filesystem scan and create the database and eleminate all errors

root@raspown # tripwire --init

Checking your system is simple, the report is stored at /var/lib/tripwire/report/.

root@raspown # tripwire --check

If your database has to be updated, maybe you update your Linux OS, run “tripwire –update” against the latest report file. This opens an text editor. Edit only the changes (removing the [X] => [ ]) which should not be written to the database. Save the file and you will prompted for the local passphrase.

root@raspown # tripwire --update -r /var/lib/tripwire/report/report-20151118.twr

After updating the database its an good idea to copy the database file /var/lib/tripwire/*.twd to an storage which is readonly. Before each tripwire check command check also if the database in the /var/lib/tripwire and on the readonly storage is identically.
Setup an cron job which periodically starts an tripwire check

Owncloud Configuration

All backend service are now prepared and running. Open a brower and connect to your Owncloud Installation. Navigate to

https://yourFQDNHostname/oc/

Owncloud is now ready to configure and use 🙂

Michael

Advertisment to support michlstechblog.info

AppArmor lighttpd Profile
AppArmor lighttpd Profile
usr.sbin_.lighttpd
Version: 1.1.2
2.5 KiB
1100 Downloads
Details...

Leave a Reply

Your email address will not be published. Required fields are marked *

Time limit is exhausted. Please reload CAPTCHA.