NextCloud on OpenBSD - updated

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 imphp-provements:

  • 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.

Coffee is needed

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
  1. 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
  2. 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://h3artbl33d.nl/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 pecl72-libsodium
  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: Prepare the webserver

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 =

There is an error in the PHP include for libsodium. Fix this with:

echo extension=sodium.so > /etc/php-7.2/libsodium.ini

Almost there, maestro

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-14.0.4.zip
$ doas unzip -d /var/www nextcloud-14.0.4.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

Troubleshooting

If something is amiss, there are two logs you should check first and foremost: /var/www/logs/error_log and nextcloud.log in your NextCloud data directory.

Feel free to reach out to me on Mastodon and via e-mail (hello@h3b.nl) if you require additional help.

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://h3artbl33d.nl/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 php70fpm 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.

Q: Why go to all this trouble to get NextCloud up and running - when there is a NextCloud package available?

A: As mentioned before, purely for performance reasons. At the time when the initial version of this guide was published, the package depended on PHP 5.6, which is no longer the case thanks to the awesome work by Gonzalo. However, this guide uses the most recent version of PHP (7.2 instead of 7.0) alongside with PostgreSQL and Redis - being the biggest differences to the package installation.

Q: Are you mad? Spending this much time on a tutorial?

A: Whether I am mad is arguable but off-topic. I like sharing knowledge, writing and am honored that at least someone got this far. Any and all feedback is appreciated but completely optional.

Q: Can you write a tutorial about X?

A: I can always check whether it matches with my interest and whether I deem it worth my time. Feel free to drop me a message.

Etcetera

Sweet ideas

  • Check the available apps - The functionality of NextCloud is extendable by using applications that run within NextCloud. The administrative user has access to an appstore that makes installing additional applications/functionality a breeze. Just try it ;)
  • Customize the branding - Using the Theming application you can setup custom branding - different colors, a custom logo, etc.
  • Enable two factor authentication - NextCloud allows setting up two factor authentication, using the Two Factor TOTP Provider application. You can install and configure it using the administrative user.
  • Setup ransomware protection - If you share your NextCloud instance with others, it might be worth considering using the Ransomware protection application - especially if Windows clients use the instance. This application will prevent uploading of ransomeware'd files and alert both the user and administrator as soon as this is triggered.

Changelog

20181130

[+] Updated to Nextcloud 14.0.4
[+] Edited a few lines due to experience with Nextcloud 15-RC1
[+] Used the correct postgresql restart syntax (tnx Jason Green)
[+] Added the redis dependency which I somehow forgot (tnx again Jason Green)
[+] Added a FAQ about the PHP-FPM pool error (tnx Teis Angel)

20181102

[+] Rewrite for 6.4 and NextCloud 14
[+] Changed the pf ruleset with IPv6 lines commented out by default
[+] Switched PHP 7.0 in favor of 7.2, as the 7.0 EOL is getting closer
[+] Switched to the Redis PECL package for more sustainability
[-] Dropped support for 6.3 entirely
[-] Took out APCu for standardization, after thorough testing with Redis.

20180905

[+] Refined the httpd.conf to block additional files and directories (tnx /u/g0nzalo)
[+] Added the crontab instead of the AJAX task runner

20180903

[+] Added a mention of the security scan in the QA section
[+] Added some sweet ideas
[+] Changed the NextCloud version to 13.0.6 in the download step
[+] Changed the PHP module config to copying instead of softlinking (tnx @minWu)
[+] Fixed a missing doas, combined several rcctl commands (tnx @minWu)