Installing Nextcloud on OpenBSD

So today I went about tearing down my old NextCloud server and rebuilding it on an OpenBSD base. My previous server was based on FreeBSD 12.2.

The process was very different from FreeBSD and I wanted to document this down for future reference.

Software

Software versions used

  • OpenBSD 7.0
  • OpenBSD httpd
  • Postgresql 13.4p0
  • PHP 7.4.25
  • Redis 6.2.6
  • Nextcloud 12.2.2

Preparation

  1. Prepare the OpenBSD server as normal, keeping in mind to give /var the largest partition you can give. OpenBSD’s HTTPd is chrooted to the /var/www directory by default.

  2. Configure /etc/pf.conf to accept incoming http and https requests

webports = "{http, https}"
pass proto tcp from any to $ext_if port $webports

Postgres

  1. Install Postgres and configure a database
# pkg_add postgresql-server

# su - _postgresql
$ mkdir /var/postgresql/data
$ initdb -D /var/postgresql/data -U postgres -A scram-sha-256 -E UTF8 -W
$ exit

# rcctl enable postgresql
# rcctl start postgresql

# psql -U postgres
Password for user postgres:
postgres=# create database nextcloud;
postgres=# create user nextcloud with encrypted password 'topsecret';
postgres=# grant all privileges on database nextcloud to nextcloud;
postgres=# \q

PHP

  1. Install PHP and the various modules. Choose the 7.4 version each time. Enable the extensions by copying them into the /etc/php-7.4/ directory. Link the PHP executables.
# pkg_add php php-pdo_pgsql php-bz2 php-curl php-gd php-intl php-gmp php-pcntl php-xml php-zip pecl74-imagick
# cd /etc/php-7.4.sample
# for i in *; do ln -sf ../php-7.4.sample/$i ../php-7.4/; done

# ln -sf /usr/local/bin/php-7.4 /usr/local/bin/php
# ln -sf /usr/local/bin/php-config-7.4 /usr/local/bin/php-config
# ln -sf /usr/local/bin/phpize-7.4 /usr/local/bin/phpize
  1. Edit /etc/php-7.4.ini. I left /etc/php-fpm.conf at its defaults.
[PHP]
expose_php = Off
max_execution_time = 70
memory_limit = 512M
post_max_size = 512M
upload_max_filesize = 512M

[opcache]
opcache.enable=1
opcache.enable_cli=1
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=10000
opcache.memory_consumption=512
opcache.save_comments=1
opcache.revalidate_freq=1
  1. Restart PHP-FPM
# rcctl enable php74_fpm
# rcctl start php74_fpm

Redis

  1. Install Redis as the memory cache server.
# pkg_add redis pecl74-redis
# rcctl enable redis
# rcctl start redis
  1. Redis will be configured as a UNIX socket in the chrooted /var/www directory
# mkdir /var/www/redis
# chown -R www:www /var/www/redis
# chmod 0775 /var/www/redis
# usermod -g www _redis
# cat /etc/redis/redis.conf
port 0
unixsocket /var/www/redis/redis.sock
unixsocketperm 770
logfile "redis.log"

HTTPd and acme-client

  1. Create the /var/www/nextcloud directory and populate it with the contents of the nextcloud zip file downloaded from NextCloud.
# ftp https://download.nextcloud.com/server/releases/nextcloud-22.2.2.zip
# cp /root/nextcloud-22.2.2.zip /var/www/
# cd /var/www/
# pkg_add unzip
# unzip ./nextcloud-22.2.2.zip
# chown -R www:www /var/www/nextcloud
  1. Configure httpd. Don’t restart it yet - this will be left after configuring acme-client.
prefork 4

ext_ip="X.X.X.X"

server "nextcloud.server.domain" {
        listen on $ext_ip port 80

        location "/.well-known/acme-challenge/*" {
        root "/acme"
        request strip 2
        }

        location * {
        block return 302 "https://nextcloud.server.domain$REQUEST_URI"
        }
}

server "nextcloud.server.domain" {
	listen on $ext_ip tls port 443
        root "/nextcloud"
	directory index index.php

	hsts {
	        preload
	        subdomains
		max-age 15768000
	}

        tls {
                  certificate "/etc/ssl/nextcloud.server.domain.fullchain.pem"
                  key "/etc/ssl/private/nextcloud.server.domain.key"
        }

        # Set max upload size to 513M (in bytes)
        connection max request body 537919488
        connection max requests 1000
        connection request timeout 3600
        connection timeout 3600

        location "/.well-known/acme-challenge/*" {
        root "/acme"
        request strip 2
        }

	# First deny access to the specified files
	location "/.ht*"             { block }
        location "/.user*"           { block }
	location "/README"           { block }
	location "/data*"            { block }
	location "/config*"          { block }
        location "/lib*"             { block }
        location "/3rdparty*"        { block }
        location "/occ*"             { block }
        location "/console*"         { block }

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

        location "/apps/*" {
                pass
        }

        location "/core/*" {
                pass
        }

        location "/.well-known/carddav" {
                block return 301 "/remote.php/dav"
        }

        location "/.well-known/caldav" {
                block return 301 "/remote.php/dav"
        }

        location "/.well-known/webfinger" {
                block return 301 "/public.php?service=webfinger"
        }

	location "/.well-known/host-meta" {
		block return 301 "/public.php?service=host-meta"
   	}

	location "/.well-known/host-meta.json" {
		block return 301 "/public.php?service=host-meta-json"
   	}

        location match "/oc[ms]%-provider/*" {
                directory index index.php
                pass
        }
}

  1. Configure our HTTPS certificates in /etc/acme-client.conf to be generated by acme-client
authority letsencrypt {
	api url "https://acme-v02.api.letsencrypt.org/directory"
	account key "/etc/acme/letsencrypt-privkey.pem"
}

authority letsencrypt-staging {
	api url "https://acme-staging-v02.api.letsencrypt.org/directory"
	account key "/etc/acme/letsencrypt-staging-privkey.pem"
}

domain nextcloud.server.domain {
        domain key "/etc/ssl/private/nextcloud.server.domain.key"
        domain full chain certificate "/etc/ssl/nextcloud.server.domain.fullchain.pem"
        sign with letsencrypt
}
  1. Since httpd is chrooted, we need to ensure that hostnames are resolvable and certificates can be verified within the chroot.
# mkdir -p /var/www/etc/ssl
# install -m 444 -o root -g bin /etc/resolv.conf /var/www/etc
# install -m 444 -o root -g bin /etc/ssl/cert.pem /etc/ssl/openssl.cnf /var/www/etc/ssl/
# chown -R www:www /var/www/etc
  1. Run acme-client to generate the HTTPS certificates and restart httpd
# acme-client -v nextcloud.server.domain
# ocspcheck -N -o /etc/ssl/nextcloud.server.domain.ocsp.pem /etc/ssl/nextcloud.server.domain.pem
# rcctl restart php74_fpm
# rcctl restart httpd

Configuration

  1. Open a web browser and surf to your Nextcloud installation. Complete the installation.
NextCloud data : /nextcloud/data
Database type: PostgreSQL
Database user : nextcloud
Database password : topsecret
Database name : nextcloud
Datebase server : localhost:5432
  1. After completing the installation, edit /var/www/nextcloud/config/config.php to include the Redis memory cache
'filelocking.enabled' => true,
'memcache.local' => '\\OC\\Memcache\\Redis',
'memcache.locking' => '\\OC\\Memcache\\Redis',
'memcache.distributed' => '\\OC\\Memcache\\Redis',
'redis' =>
    array (
	   'host' => '/redis/redis.sock',
	   'port' => 0,
	   'timeout' => 1.5,
	  ),
  1. Add a cron job for Nextcloud and certificate renewal.
# crontab -u www -e
#*/5 *   *   *   *   /usr/local/bin/php-7.4 -f /var/www/nextcloud/cron.php
*/5 *   *   *   *   /usr/bin/ftp -Vo - https://nextcloud.server.domain/cron.php >/dev/null
0   0   *   *   *   acme-client nextcloud.server.domain && rcctl reload httpd
0   *   *   *   *   ocspcheck -N -o /etc/ssl/nextcloud.server.domain.ocsp.pem /etc/ssl/nextcloud.server.domain.pem && rcctl reload httpd

Caveats of adding ocspcheck in your scripts from the manpage.

 While ocspcheck could possibly be used in scripts to query responders for
 server certificates seen on client connections, this is almost always a
 bad idea.  God kills a kitten every time you make an OCSP query from the
 client side of a TLS connection.

Testing

  1. To test the cronjobs, you can issue either command.
# su -m www -c /usr/local/bin/php-7.4 -f /var/www/nextcloud/cron.php
# doas -u www /usr/bin/ftp -Vo - https://nextcloud.server.domain/cron.php
  1. Go to Nextcloud Scan to do a security check on your server.

  2. Under Settings -> Administration -> Overview, you can view any further security and setup warnings.

  3. Now you should have a fully functional Nextcloud server on OpenBSD.