Recently (thanks to Gabry89 and AndreaDraghetti) I discovered BookStack, a simple and easy to use platform for storing information.

What/why BookStack?

As mentioned before, it’s a wiki-system that provides a pleasant and simple out of the box experience. It’s free & open, configurable, searchable and connected.

As I'm a sysadmin, I was searching something to to help me maximize the efficiency, transparency and consistency of my work, keeping a documentation of everything I do for me and my clients.

Mention of honor: please, don’t copy & paste everything without knowing what you are doing. My environment may differs from yours and you can screw up your installation.

Installing it

The app is very light and resource efficient and doesn’t require a lot of computational power.

For my setup I use Vultr (thank you for using my referral) and their new High Frequency are more enough.

Spawn a $6 VPS in the region that you prefer with Ubuntu 18.04 and you’re good to go!

I will not going in depth in securing a VPS, nowadays it’s a standard, plus it’s not part of the guide, but there are lots of guides out there like this one on Vultr

After you have secured your VPS, create a dedicated BookStack user (mine will be bookstack) and proceed. The installation of the software is simple as:

$ cd /home/bookstack
$ wget https://raw.githubusercontent.com/BookStackApp/devops/master/scripts/installation-ubuntu-18.04.sh
$ chmod a+x installation-ubuntu-18.04.sh
$ sudo ./installation-ubuntu-18.04.sh

It’s better to disable ssh login for this user (add DenyUsers bookstack to your sshd_config) and proceed with the initial securing of the app (as mentioned in the documentation): because you’re a lazy guy, I’ll paste it below :)

  1. Ensure you change the password and email address for the initial [email protected] user.
  2. Ensure only the public folder is being served by your webserver. Serving files above this folder opens up a lot of code that does not need to be public. Triple check this if you have installed BookStack within the commonly used /var/www folder.
  3. Ensure the database user you’ve used for BookStack has limited permissions for only accessing the database used for BookStack data.
  4. Within BookStack, go through the settings to ensure registration and public access settings are as you expect.
  5. Review the user roles in the settings area.

Hardening it

Elevate to root, we have a lot of work to do now..

Firewall

In simple setups, I'm lazy, I like to use UFW. Customize <ssh-port> with yours

$ ufw allow 80/tcp
$ ufw allow 443/tcp
$ ufw allow <ssh-port>/tcp
$ ufw enable

Enable SSL via Let’s Encrypt

Install CertBot (if repo add fails, install software-properties-common)..

$ add-apt-repository ppa:certbot/certbot
$ apt clean all && apt update
$ apt install -y certbot python-certbot-apache

..certify the domain with $ certbot --apache -d domain.tld

.. and check the autorenewal is present and working

$ cat /etc/cron.d/certbot
$ certbot renew --dry-run

Now, CertBot will create other config files in Apache2. I like to have everything in one place so I merged those settings in the bookstack.conf and remove the CertBot auto-created conf (don’t think this will break anything).

Mine looks like this; also, I like to add a rewrite rule for redirect all requests to HTTPS.

$ vim /etc/apache2/sites-enabled/bookstack.conf

<VirtualHost *:80>
 ServerName domain.tld
 RewriteEngine On
 RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]
</VirtualHost>
<VirtualHost *:443>
 ServerName domain.tld
 ServerAdmin [email protected]
 DocumentRoot /var/www/bookstack/public/
 SSLEngine On
 SSLCertificateFile /etc/letsencrypt/live/domain.tld/fullchain.pem
 SSLCertificateKeyFile /etc/letsencrypt/live/domain.tld/privkey.pem
 Include /etc/letsencrypt/options-ssl-apache.conf
 ErrorLog /error.log
 CustomLog /access.log combined
 <Directory /var/www/bookstack/public/>
  ...
 ...
</VirtualHost>

MySQL

Run the secure installation script, set a root password, remove anon users/test db and disallow root remotely login with $ mysql_secure_installation

Also, it’s better to create a password for bookstack MySQL user, then edit the .env file.

ModSecurity + OWASP Ruleset

Bookstack constantly updates, so you need to review those rules. If something doesn't work as expected, please disable

Install ModSecurity (an awesome WAF) and download the OWASP rules

$ apt install libapache2-mod-security2
$ cd /etc/modsecurity/
$ cp /etc/modsecurity/modsecurity.conf-recommended modsecurity.conf
$ git clone https://github.com/SpiderLabs/owasp-modsecurity-crs.git
$ cp owasp-modsecurity-crs/crs-setup.conf.example /etc/modsecurity/crs-setup.conf
$ cp owasp-modsecurity-crs/rules/ /etc/modsecurity/

Now include the conf/rules into Apache2..

$ vim /etc/apache2/mods-available/security2.conf

<IfModule security2_module>
  SecDataDir /var/cache/modsecurity
  IncludeOptional /etc/modsecurity/*.conf
  Include /etc/modsecurity/rules/*.conf
</IfModule>

..enable ModSecurity in bookstack.conf adding it after VirtualHost clause

$ vim /etc/apache2/sites-available/bookstack.conf

...
<VirtualHost *:443>
 ServerName domain.tld
 ServerAdmin [email protected]
 DocumentRoot /var/www/bookstack/public/
 
 SecRuleEngine On
 ...
 ...
</VirtualHost>

Fixing OWASP rules

With the standard configuration, ModSecurity will gave us some errors on BookStack (save revision, attachments, etc) because of strict security. After some troubleshooting I was able to fix those.

First we will add PUT and DELETE to the allowed methods..

$ vim /etc/modsecurity/rules/REQUEST-901-INITIALIZATION.conf

...
# Default HTTP policy: allowed_methods (rule 900200)
SecRule &TX:allowed_methods "@eq 0" \
    "id:901160,\
    phase:1,\
    pass,\
    nolog,\
    setvar:'tx.allowed_methods=PUT DELETE GET HEAD POST OPTIONS'"
...

.. then, because I need to upload some custom files that aren’t recognized by ModSecurity, I will loosen the security of multipart boundary (maybe it’s not your case) by commenting those lines

$ nano /etc/modsecurity/modsecurity.conf
...
#SecRule MULTIPART_UNMATCHED_BOUNDARY "!@eq 0" \
#"id:'200004',phase:2,t:none,log,deny,msg:'Multipart parser detected a possible unmatched boundary.'"
...
$ service apache2 restart

Apache2

Out of the box, I didn’t like the results on securityheaders.com

Let’s make a green A editing the Apache2 configuration. Before we do that, locate the mod_headers.so library and, accordingly, customize it

$ vim /etc/apache2/apache2.conf

# Hide apache/server version
ServerTokens Prod
ServerSignature Off

# Security headers
LoadModule headers_module /usr/lib/apache2/modules/mod_headers.so

Header set X-XSS-Protection "1; mode=block"
Header set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
Header always append X-Frame-Options DENY
Header set Content-Security-Policy "default-src https: 'unsafe-inline' 'unsafe-eval';"
Header set X-Content-Type-Options nosniff

$ service apache2 restart

Bookstack App

I will enable secure uploads, you will act accordingly your requirements

BookStack > Settings

Optimizing it

Enable HTTP2

Fairly simple, enable the module and add the clause in your Apache2 bookstack.conf file

$ a2enmod http2
$ vim /etc/apache2/sites-enabled/bookstack.conf

...
<VirtualHost *:443>
 ServerName domain.tld
 ServerAdmin [email protected]
 DocumentRoot /var/www/bookstack/public/
 
 Protocols h2 http/1.1
 ...
...
</VirtualHost>

$ service apache2 restart

Use Redis as cache

Install Redis from PPA..

$ add-apt-repository ppa:chris-lea/redis-server
$ apt-get clean && apt update
$ apt install redis-server

.. then edit the configuration, limiting his resources.

It’s better to protect Redis with a password (and edit the BookStack .env file accordingly) but in this guide I will not do that

$ vim /etc/redis/redis.conf

loglevel warning
databases 2
always-show-logo no
stop-writes-on-bgsave-error no
maxmemory 64mb
maxmemory-policy allkeys-lfu

Then enable the configuration in BookStack

$ vim /var/www/bookstack/.env

# Cache
CACHE_DRIVER=redis
SESSION_DRIVER=redis
REDIS_SERVERS=127.0.0.1:6379:0

More BookStack .env configs

$ vim /var/www/bookstack/.env

# Secure Cookies
SESSION_LIFETIME=60
SESSION_SECURE_COOKIE=true

# Disallow robots
ALLOW_ROBOTS=false

# Enable Draw.io integration
DRAWIO=true

# Timezone
APP_TIMEZONE=<YOURS>

# Mail system to use
<WHAT YOU NEED>

# Storage system to use
STORAGE_TYPE=local_secure

What I’d like to see next in BookStack

  • Integration of 2FA or TOTP Auth
  • A secure/encrypted storage for sensitive data (like credentials)

What I will integrate

  • Mutual client-server SSL auth (currently not possible by Let’s Encrypt)
  • IP Blacklist based on public ban lists

The project is very active and I invite you to give it a try! Let me know if you like this guide or you have some improvements

Buy Me A Coffee