NextCloud on OpenBSD

by on under communication
10 minute read

NextCloud on OpenBSD

NextCloud and OpenBSD are complimentary to one another. NextCloud is an awesome, secure and private alternative for propietary platforms, whereas OpenBSD forms the most secure and solid foundation to serve it on. Setting it up in the best way isn’t hard, especially using this step by step tutorial.

General

Setting up NextCloud manually offers some room for improvements:

  • PostgreSQL as the database engine. This benefits a multi-user instance when it comes to performance.
  • PHP 7.2 for even more increased performance – it is way faster than PHP 5.6.
  • Caching and file-locking through Redis.

Caveats:

  • PostgreSQL is more performant for a multi user setup. It is possible to switch database engines afterwards.
  • FFS might not be the best fit for bulky data (>8TB) or when high IOPS are a requirement.
  • OpenBSD doesn’t support RAID stacking (eg, RAID1 and FDE) – but NextCloud offers server- and user level encryption.

Step 1. Preparations.

This guide should work with the latest -stable and the most recent -current. You might prefer a clean and fresh OpenBSD installation to prevent existing configurations from interfering. Without further ado, brew yourself a cup of coffee, disable any distractions and let’s go!

We’re going to install NextCloud on the subdomain nc.h3artbl33d.nl and are using regular user johndoe. Take note of this and replace these values in commands and configuration files.

1.1: Login and set some defaults

After having logged in to the target machine – that is going to run your NextCloud machine – we’ll first set some sane defaults.

  1. Enable doas: # echo 'permit johndoe' >> /etc/doas.conf
  2. Edit /etc/ssh/sshd_config and set these values – unless you explicitly need them:
     LogLevel VERBOSE
     PermitRootLogin no
     MaxAuthTries 2
     MaxSessions 2
     AllowAgentForwarding no
     AllowTcpForwarding no
     TCPKeepAlive no
     Compression no
     ClientAliveInterval 2
     ClientAliveCountMax 2
    
  3. Download the pf ruleset I’ve prepared for you and edit it (change the interface and enable the inet6 lines if you are using IPv6). When you are done, move it to the default config and check the syntax.
     ftp https://blog.openbsd.rocks/examples/pf-nc.txt
     doas mv pf-nc.txt /etc/pf.conf
     doas pfctl -nf /etc/pf.conf
    

1.2: Download/install the prerequisites

We’ll need some files further on, let’s make sure we have everything ready.

  1. Install PostgreSQL server:
     doas pkg_add postgresql-server
    
  2. Install PHP and some modules. If you are asked which PHP-version to install, answer PHP 7.2 each time.
     doas pkg_add php php-curl php-gd php-intl php-pdo_pgsql php-xml php-zip redis pecl72-redis
    
  3. Enable the PHP modules.
     doas cp /etc/php-7.2.sample/* /etc/php-7.2
    
  4. Automatically start these services. Do not start these services yet.
     doas rcctl enable postgresql php72_fpm redis
    
  5. Install a file so the webserver can resolve DNS queries from inside its chroot and another file so NextCloud can verify HTTPS certificates:
     doas mkdir -p /var/www/etc/ssl
     doas cp /etc/resolv.conf /var/www/etc/resolv.conf
     doas cp /etc/ssl/cert.pem /var/www/etc/ssl/cert.pem
     doas cp /etc/ssl/openssl.cnf /var/www/etc/ssl/openssl.cnf
     doas chown -R www:www /var/www/etc
    

Step 2. Preparations.

We need to prepare the webserver. Obviously, we’re using httpd(8) – see this article if you prefer nginx for some reason. We’re going to kick this chapter off with httpd itself.

2.1: Enable and configure httpd

Use this configuration as a starting point for your /etc/httpd.conf:

server "nc.h3artbl33d.nl" {
        listen on * port 80
        root "/nextcloud"
        location "/.well-known/acme-challenge/*" {
                root { "/acme" }
                request strip 2
        }
}

Check whether the configuration is deemed valid with doas httpd -n. Moving on to preparing acme-client for the SSL certificates, courtesy of Let’s Encrypt. Edit /etc/acme-client:

authority letsencrypt {
        api url "https://acme-v01.api.letsencrypt.org/directory"
        account key "/etc/acme/letsencrypt-privkey.pem"
}
authority letsencrypt-staging {
        api url "https://acme-staging.api.letsencrypt.org/directory"
        account key "/etc/acme/letsencrypt-staging-privkey.pem"
}
domain nc.h3artbl33d.nl {
        alternative names { www.nc.h3artbl33d.nl }
        domain key "/etc/ssl/private/nc.h3artbl33d.nl.key"
        domain certificate "/etc/ssl/nc.h3artbl33d.nl.crt"
        domain full chain certificate "/etc/ssl/nc.h3artbl33d.nl.pem"
        sign with letsencrypt
}

And create the corresponding directories:

doas mkdir -p -m 700 /etc/acme
doas mkdir -p -m 700 /etc/ssl/acme/private
doas mkdir -p -m 755 /var/www/acme

Time to fetch the certificates!

doas rcctl enable httpd && doas rcctl start httpd && doas acme-client -vAD nc.h3artbl33d.nl

If that went successful, grab the OCSP stapling file.

doas ocspcheck -N -o /etc/ssl/nc.h3artbl33d.nl.ocsp.pem /etc/ssl/nc.h3artbl33d.nl.pem

Edit the crontab to automatically renew the certificates and stapling file. Append the following in the crontab (doas crontab -e).

0 0 * * * acme-client nc.h3artbl33d.nl && rcctl reload httpd
0 * * * * ocspcheck -N -o /etc/ssl/nc.h3artbl33d.nl.ocsp.pem /etc/ssl/nc.h3artbl33d.nl.pem && rcctl reload httpd

2.2: Configuring httpd further

Edit /etc/httpd.conf with these values. Do not restart httpd afterwards!

server "nc.h3artbl33d.nl" {
        listen on * tls port 443
        hsts {
                preload
                subdomains
        }
        root "/nextcloud"
        directory index "index.php"
        tls {
                certificate "/etc/ssl/nc.h3artbl33d.nl.pem"
                key "/etc/ssl/private/nc.h3artbl33d.nl.key"
                ocsp "/etc/ssl/nc.h3artbl33d.nl.ocsp.pem"
                ciphers "ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA"
                dhe "auto"
                ecdhe "P-384"
        }
        connection max request body 537919488
                
        location "/.well-known/acme-challenge/*" {
                root { "/acme" }
                request strip 2
        }

        # First deny access to the specified files
        location "/db_structure.xml" { block }
        location "/.ht*"             { block }
        location "/README"           { block }
        location "/data*"            { block }
        location "/config*"          { block }
                location "/build*"              { block }
                location "/tests*"              { block }
                location "/config*"             { block }
                location "/lib*"                { block }
                location "/3rdparty*"           { block }
                location "/templates*"          { block }
                location "/data*"               { block }
                location "/.ht*"                { block }
                location "/.user*"              { block }
                location "/autotest*"           { block }
                location "/occ*"                { block }
                location "/issue*"              { block }
                location "/indie*"              { block }
                location "/db_*"                { block }
                location "/console*"            { block }

        location "/*.php*" {
                fastcgi socket "/run/php-fpm.sock"
        }
}

server "nc.h3artbl33d.nl" {
        listen on * port 80
        block return 301 "https://nc.h3artbl33d.nl$REQUEST_URI"
}

Step 3: PHP kung fu and SQL jiujitsu

In the first step, we’ve installed PostgreSQL-server and PHP along with some extensions. We’ve met the preconditions, time to get this all up and running.

3.1: Kicking PostgreSQL online

Since this is the first time we’re using PostgreSQL on this machine, we’ll need to initialize the database.

doas su - _postgresql
mkdir /var/postgresql/data
initdb -D /var/postgresql/data -U postgres -A md5 -W

Switch back to our regular user, johndoe, by typing exit twice. Now, let’s start the database by issuing a single command.

doas rcctl enable postgresql && doas rcctl start postgresql

If you expect to house a busy NextCloud instance, you might want to do some configuration tweaking according to the instructions in cat /usr/local/share/doc/pkg-readmes/postgresql-server-{ver}.

3.2: PHP configuration

Now we have to change the PHP configuration with some higher limits – as the default only allows uploading of files that are two MB at max. Open /etc/php-7.2.ini and edit these values.

memory_limit = 512M
max_input_time = 180
upload_max_filesize = 512M
post_max_size = 32M
opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=10000
opcache.revalidate_freq=1
opcache.save_comments=1

Next, edit /etc/php-fpm.conf.

security.limit_extensions =

Step 4: Finally, installing NextCloud

We’ve come quite a long way. Fortunately, we’re almost there! Grab the most current version of NextCloud and extract it to /var/www/nextcloud.

ftp https://download.nextcloud.com/server/releases/nextcloud-15.0.5.zip
doas unzip -d /var/www nextcloud-15.0.5.zip
doas chown -R www:www /var/www/nextcloud

Before we can use NextCloud, we need to create a database to store the data. Replace secret-password with a strong passphrase of your liking.

doas su - _postgresql
psql -d template1 -U postgres
template1=# CREATE USER nextcloud WITH PASSWORD 'secret-password';
template1=# CREATE DATABASE nextcloud;
template1=# GRANT ALL PRIVILEGES ON DATABASE nextcloud to nextcloud;
template1=# \q

Check whether you are still running as the postgresql user with whoami – if so, just give it an exit to switch back to johndoe. Start the required services. We’ve set these services earlier to enabled, so with a restart, the services will also start on boot time.

doas rcctl start httpd redis php72_fpm postgresql

Fire up your browser and head to your subdomain. You should be greeted there with the installation wizard. Select PostgreSQL as your database, using 127.0.0.1:5432 as the server, nextcloud as the user and the password you’ve set. Take caution to place the datafolder outside htdocs, eg, in /var/www/ncdata.

After the installation, edit /var/www/nextcloud/config/config.php and add these lines before the closing );.

  'memcache.local' => '\OC\Memcache\Redis',
  'memcache.locking' => '\OC\Memcache\Redis',
  'redis' => array(
    'host' => '127.0.0.1',
    'port' => 6379,
  ),

And last but not least, a cronjob for some regular housekeeping and indexing. By default, it runs one task with each pageload. The preferred way here is to set this via a cronjob.

doas crontab -e
*/15    *   *   *   *   /usr/bin/ftp -Vo - https://nc.h3artbl33d.nl/nextcloud/cron.php >/dev/null

FAQ – Frequently Asked Questions (almost completely made-up by the author)

Q: PHP throws an error about failing due to a missing pool

A: most likely, the shower in your condo doesn’t satisfy NC’s dependency on a pool. Nah, just kidding. Still researching when this error occurs, the fix is to define a pool in /etc/php-fpm.conf, I’ve written a short example found here: https://blog.openbsd.rocks/examples/nc-php.txt

Q: The error log mentiones: the Redis server went away

A: somewhat cryptic error message, I agree. Luckily, the Redis server didn’t go on holiday nor went anywhere else out of your reach. This message merely means that the Redis server is most likely offline. Check with rcctl check redis.

Q: I got a nasty 500/520 error while trying to access NextCloud

A: Most likely, PHP went belly up or it won’t start due to an error in the configuration. Check the daemon status with rcctl check php72_fpm or try to restart it with rcctl restart php72_fpm.

Q: How about security, how safe is my data?

A: OpenBSD is the most secure OS out there. Having said that, nothing is one hundred percent secure. If someone says otherwise, they are flat-out lying to you. This guide follows best practice methods. There is an additional step you could take, that this guide doesn’t cover: using encryption from within the NextCloud webinterface. Be cautious, losing either your passphrase or your keys will result in inaccessible data. There is a recovery method, but each user has to allow that in the individual profile. It’s disabled by default, being the sanest default.

Furthermore, NextCloud offers a security scan you can use to see whether your instance is safe and sound. You might need to click the refresh icon (on the result page) if you are doing a recheck.

This guide is updated occasionally, with help of the community - thank you all!

openbsd, nextcloud, tech