> Computationally Offloading Nextcloud

 > Notes

Home

Metadata:

How to configure Nextcloud and Apache to use a domain server as a reverse proxy to access a Nextcloud instance on some external server.
- Initially published on 12-27-2024.
- Parent note: How to install Nextcloud on Ubuntu Server


Computationally offloading a domain's Nextcloud instance

Operating Context

Prior writing discussed how to install Nextcloud server for use as a web-app. This was done on a server running Ubuntu Server 24.04. Here, the conclusion left users in a state where they could access the web-app through their web browser within a local area network.

Having access to a Nextcloud instance provides a lot of perks. One can think of it as an open source version of Microsoft's Office 365 suite of offerings. It can be used as a mail client, a photo hub, contact manager, digital calendar, word processor, file server, etc. All these options are supported as apps that can be set and monitored by the administrator of an instance. Each app comes with the typical set of features we may take for granted for offerings such as Office 365 or Google's Office suite. The fact that Nextcloud is open source and can be self-hosted gives users privacy and complete data control.

Access to this data through a local area network is a bit too restrictive for most. This could be mitigated by using a virtual private network using tools such as Wireguard or Tailscale. This is a reasonable alternative while considering the fact that access to something like Nextcloud usually requires an administrator to create their account in the first place - thus the administrator will know who to add to the virtual private network and will know which virtual IP addresses are being used within said network. Using a virtual private network might not be feasible as the user base grows, though. This may cause too much administrative overhead as the amount of users that can access the network reaches triple digits.

Exposing the instance to the world wide web would be the next intuitive approach. To accomplish this is as simple as ensuring port 443 is forwarded for the server with respect to the router that is acting as a gateway. This will allow external https access to the instance.

The title of this writing includes the phrase "computational offloading". The need to offload computation would imply having the instance installed within an environment that has limited resources. The web server that this website is being hosted on is a virtual server using Digital Ocean as a cloud provider. The virtual server is allotted a 1ghz single-core processor with 1gb of ram and 25gb of disk space. This does well for running services to serve up web pages, but is not sufficient in both computational speed and storage space for running something like Nextcloud.

Following the path of parent notes presented by this writing's meta section will highlight the motive of this page - making use of a Raspberry Pi 4. The use-cases of using this system-on-a-chip has changed over the years, but it is now being used to host a Nextcloud instance. Its 1.8ghz quad core ARM processor and 4gb of ram is plenty for stable access. Storage amount can be arbitrarily increased and tuned for expectations. The initial investment of 70 dollars plus storage cost may be higher than a cloud-based alternative, but the long-term investment is better. This is especially true if the hardware is inherited from some other use-case where it is no longer needed.

Prepping the Nextcloud server to receive traffic

There are two acts to providing computational offload. The first is to prepare the Nextcloud server to receive traffic and the second is to prepare the domain server to forward traffic to the Nextcloud server. Discussion will start from the Nextcloud server's point of view.

Forwarding ports

The first step to ensuring Nextcloud can receive external traffic is by enabling port forwarding for the server within its local network. This is done through the gateway router. The process is dependent on the model and brand of the router in use. Regardless of this, it will follow a generalized process.

Forwarding ports involves signing into a router's dashboard. This involves using the web browser to access the router's IP address using the address bar. This is typically 192.168.1.1 or 192.168.0.1. If uncertain, it will be listed in the router's documentation. To access the dashboard, a password must be given - the default of which is also in the router's documentation.

Once the dashboard is accessed, look for a section labeled Port Forwarding or NAT. This section might be nested within another section, such as Security. Here, add a new port forwarding rule. Start entering the rule's details by giving it a good name or description, such as "HTTPS Port Forwarding". Set the port range value to 443. Be sure to do this for both external and internal ports. The protocol of choice is TCP/UDP. Finally, associate the server's local IP address to the port forwarding rule.

Be sure to apply the new rule. It might be necessary to restart the router once the rule is applied. Testing whether the rule was correctly applied would be by discovering your external IP address, (which can be done here), and accessing the server using the web browser by navigating to https://<external-ip>.

Ensuring ports stay forwarded

It's possible that the router's restart assigned the server to a different local IPv4 address. This can occur throughout various maintenance stages of the router; the Dynamic Host Configuration Protocol (DHCP) server is a subsystem that may exist on the router which handles address assignment dynamically. An IP reassignment might not align with the port forwarding rule that was set up. This can be solved by adding a DHCP rule to ensure the server is always assigned to a particular address.

To do this, locate the DHCP settings through the router dashboard. This could be located in a section labeled DHCP Settings, LAN Settings, Connectivity Settings, or Address Reservation. In the case of the prior three, DHCP reservation may be nested in a Local Network subcategory.

Create a DHCP reservation rule by entering the device's MAC Address. The MAC Address can be physically noted on a server's network interface card. Alternatively it can be discovered by running ip address within a shell. The MAC address will be listed within the relevant interface following the link/ether string

After the MAC Address is filled, manually assign an IP address for reservation. Give a description of the rule, if applicable, and then apply the change. Once applied, give the router a restart.

Configuring Nextcloud

Once the server's network is properly set up, Nextcloud needs to be configured to understand the context in which it is receiving traffic. This is done by navigating to the config folder from the location of the instance's working directory. Using the initial tutorial, this will be located at /var/www/nextcloud/config/. Here, config.php needs to be edited. Before doing this, make a backup of the configuration file. This can be used should anything go awry during this process.

It is worth noting that this copy is being created using the superuser-do command. The result of this copy may produce a file whose permissions are set with respect to the root user. Nextcloud expects the user to be www-data. This means that should a restoration of this backup file be made using the mv or cp command, that the permissions will need to be reset. This can be monitored by running ls -l while observing the ownership of said file.

To change ownership back to www-data, run the following command:

This command can also be run generically by setting the ownership to the nextcloud folder itself.

Observing the contents of config.php

Now that a backup of the configuration file has been created, let's take a look at the contents of its origin. The default configuration will have the following form:

There seems to be a global variable named $CONFIG whose value is an array of values. Some of the values in the above code block have been generalized and censored. Any value who is surrounded by a set of angle brackets is indicative of this. This syntax is motivated by the study of language syntax which is motivated by linguistics' notion of a context-free grammar. Here, <STRING> is indicates a set of values which constitutes a string. This is a generalized way of saying an arbitrary string can exist here.

As a prelude to editing this configuration file, it's worth considering the instanceid, passwordsalt, and secret set of keys for this array. These values are indeed arbitrary. They are randomly generated and purposefully unique for security reasons.

Nextcloud documentation gives an example of instanceid being assigned the value of 'd3c944a9a',. It defines the key-value pair as follows:

This is a unique identifier for your Nextcloud installation, created automatically by the installer. This example is for documentation only, and you should never use it because it will not work. A valid instanceid is created when you install Nextcloud.

The other two key-value pairs, (which the documentation recommends against editing), are also defined. They are as follows - starting with passwordsalt and then following with secret:

The salt used to hash all passwords, auto-generated by the Nextcloud installer. (There are also per-user salts.) If you lose this salt you lose all your passwords. This example is for documentation only, and you should never use it.

Secret used by Nextcloud for various purposes, e.g. to encrypt data. If you lose this string there will be data corruption.

There is no sense in sharing these values to the reader, thus they have been censored here. The takeaway is that these need not be disclosed and they need not be changed within the configuration file. The same logic applies to datadirectory, dbuser, and dbpassword.

Making changes to config.php

The key-value pair that proceeds the first three is an array assigned to the trusted_domains key. This acts a list of domain names and IPv4 addresses in which Nextcloud can trust access.

Reconsider the use-case in which the Nextcloud server can only be accessed through a virtual private network using a tool like Tailscale. In this case, the virtual private network would have a static IP assigned to the Nextcloud server. This IP address would need to be added to the trusted_domains array to allow other clients within the virtual private network to access its IP address within a web browser. The array would thus look like:

Seeing this change naturally brings the question, "What IP address resides at key 0?" This <IPv4_Address> is the local IP address of the server when Nextcloud was initially installed. This should correlate with the IP address that was set for the port-forwarding rule and the DHCP rule established in the networking section described above. If that's not the case, change it here.

If this server is to be accessed from some other server that acts as a gateway to a domain name, then it can be inferred that its address should also be included here. This is the external IP address for a given web host.

For clarification, the Nextcloud documentation for trusted_domains reads:

Your list of trusted domains that users can log into. Specifying trusted domains prevents host header poisoning. Do not remove this, as it performs necessary security checks.

You can specify:

  • the exact hostname of your host or virtual host, e.g. demo.example.org.

  • the exact hostname with permitted port, e.g. demo.example.org:443. This disallows all other ports on this host

  • use as a wildcard, e.g. ubos-raspberry-pi.local will allow ubos-raspberry-pi.local and ubos-raspberry-pi-2.local

  • the IP address with or without permitted port, e.g. [2001:db8::1]:8080 Using TLS certificates where commonName=<IP address> is deprecated

IP addresses have been so far been captured within trusted_domains. The domain name of the external server should also be included. This changes the array to read:

Here, <Domain_Name> is some string that represents the domain name used to access <Domain_Server_IPv4_Address>.

Referring to Nextcloud's documentation on the configuration file, there exists a specific key that is not inherit in the default setup. This is another list akin to trusted_domains, but instead factors a set of proxy servers. Its description reads as follows:

List of trusted proxy servers

You may set this to an array containing a combination of - IPv4 addresses, e.g. 192.168.2.123 - IPv4 ranges in CIDR notation, e.g. 192.168.2.0/24 - IPv6 addresses, e.g. fd9e:21a7:a92c:2323::1 - IPv6 ranges in CIDR notation, e.g. 2001:db8:85a3:8d3:1319:8a20::/95

When an incoming request’s REMOTE_ADDR matches any of the IP addresses specified here, it is assumed to be a proxy instead of a client. Thus, the client IP will be read from the HTTP header specified in forwarded_for_headers instead of from REMOTE_ADDR.

So if you configure trusted_proxies, also consider setting forwarded_for_headers which otherwise defaults to HTTP_X_FORWARDED_FOR (the X-Forwarded-For header).

Defaults to an empty array.

What ultimately will be happening here is to ensure that the external web server will act as a proxy to access the Nextcloud server. Adding an IPv4 address to this list will flag Nextcloud to inspect the HTTP header a bit more closely. The domain server's IPv4 address will instead be placed within this list.

The configuration file will thus read as follows:

Prepping the domain's server to send traffic

The server where Nextcloud resides will be forwarding data to a domain access point. It is reliant on the domain server for communicating with clients on the internet. The domain server enables any client to access the Nextcloud server - a server which resides on what could be considered a private network. This makes the domain server a reverse proxy for the Nextcloud server which allows anyone to access it using the domain name within a web browser.

Luckily, Apache's configuration schema allows the option for a server to act as a reverse proxy for another server. This is the configuration that will need to occur on the domain's server.

Configuring Apache

Apache operates off a set of directives set within a configuration file. When installing the Nextcloud instance, it was assumed that the installation of Apache was a fresh install. This made looking at the default set of directives deterministic. This may not be the case with some external web server which is acting as a reverse proxy.

Thus far it can be assumed that the web server runs Apache. Assume that it is also a LAMP server. Also assume that the LAMP server has leveraged Let's Encrypt's certbot to create a signed https certificate from the certificate authority.

The location of the configuration file should be in /etc/apache2/sites-enabled/. With the assumption of using certbot to create a signed https certificate, then there should exist at least two files in this directory - <domain-name>-le-ssl.conf and <domain-name>.conf.

The two configuration files provide a set of directives for Apache to enact dependent on the port that is used to access the server. When an individual uses http to access a website, they are using port 80. When an individual uses https, port 443 is used. Recall that only port 443 was forwarded within the router for the Nextcloud server. This informs that any new directives to allow the domain's server to be a reverse proxy should associated with the configuration file that handles requests on port 443. Taking a look at both of these configuration files using head will reveal their port association:

$head -c 18 <domain-name>.conf produces <VirtualHost *:80>

$head -c 53 <domain-name>-le-ssl.conf> produces <VirtualHost *:443> on the second line.

This intuition can be confirmed by observing the ssl suffix in the filename configuration file. If it is the case that the administrator flagged the option to disable http access while certbot was being used, then intuition can be shored up by observing the following set of lines within <domain-name>.conf:

These capture an http access with any given subdirectory and then 'redirects' a visitor to the https equivalent.

Exploring the directives of a configuration file

The contents of <domain-name>.conf follows:

Each line within the the <VirtualHost *:443> set of tags acts as a directive for Apache to operate on within the context of an access to the virtual host. This schema allows Apache to operate on virtual environments. This interface rectifies the decoupling of name logic from server logic.

The first directive is ServerAdmin. The default value that may have been filled by certbot is webmaster@localhost. This field essentially acts as a contact address that the server may include when displaying any error messages it returns to the client.

The second directive ServerName is the field which uniquely identifies a virtual host. It is the default domain of the virtual server. This should be a singular value using <scheme>://<domain-name><port> as its schema, where the optional <port> is defined as :<port-number> if it is included.

As ServerName should be a singular value, ServerAlias can be a set of values. These are additional domains that should also resolve to the same virtual host. This captures the case where alternative subdomains may be registered to direct to the same server.

DocumentRoot informs Apache where the root folder of the website is. The default location in an Ubuntu environment is /var/www/html. The DocumentRoot of the Nextcloud instance is /var/www/nextcloud. When accessing the server through the web browser, the contents of the folder indicated in this directive will be presented should no subdirectory be given.

ErrorLog and CustomLog are fields which can be changed to set the location of an error and access logs. These are both using an Apache variable that is defined elsewhere. The default value for APACHE_LOG_DIR on Ubuntu is /var/log/apache2.

The last three set of directives help Apache locate the SSLCertificateFile, the SSLCertificateKeyFile, while also importing a set of directives set up by certbot via the include directive.

Enabling the proxy engine

A change to the configuration file needs to occur. This is so that an access to a subdirectory on the domain server will act as a proxy for the Nextcloud server. The domain server is to act as a reverse proxy allowing a client to access it as if it were on a private network. The first directive to add to the configuration is to enable the proxy engine. This is done using the SSLProxyEngine directive and giving it the value of on.

Documentation notes that the SSLProxyEngine directive should not, in general, be included in a virtual host that will be acting as a forward proxy. This tracks as a forward proxy is typically used in the context of obscuring client devices where their access point is the proxy server.

This intuition can be shored up by considering Apache's documentation on the next directive - ProxyPass:

This directive allows remote servers to be mapped into the space of the local server. The local server does not act as a proxy in the conventional sense but appears to be a mirror of the remote server. The local server is often called a reverse proxy or gateway.

In this context, the local server is the domain's primary server where the remote server is the Nextcloud server. ProxyPass takes two arguments: a local path that is indicative of the subfolder which is used to access the remote server and the address of the remote server. The directive within the configuration file will be as follows:

This is to say, that when a visitor navigates to https://<domain-name>/nextcloud/ that the Nextcloud server will be invoked and serve up the contents of its root directory. It should be noted that if the Nextcloud server had the Nextcloud instance installed in a subdirectory of it's root, such as /var/www/html/nextcloud, then the second argument to the directive would read "https://<nextcloud-server-IPv4-address>/nextcloud/".

ProxyPass usually has a supplement. It is often placed in tandem with ProxyPassReverse. There are times when a visitor of the remote server performs an action where the server responds with an internal redirect. When this redirect occurs, the remote server's own IP address is used. This will cause situations where the remote server's IP address is presented in the visitor's address bar. ProxyPassReverse ensures this doesn't occur. It's parameters are the same as ProxyPass.

There is one last directive to consider. Documentation recommends that ProxyRequests be set to Off. It prevents Apache from functioning as a forward proxy server in this context.

With these directives in place, the configuration file will be as follows:

Considering SSL certificate authorization

Several assumptions have been made about the environment of the domain server. It was assumed that it is a LAMP server where certbot was used to provide an SSL certificate and reconfigure any default Apache configuration files to make use of the certificate. Changes were made to this configuration file to allow the domain server to act as a reverse proxy for the Nextcloud server. This reverse proxy is used when a visitor navigates through the /nextcloud subdirectory of the its root. This configuration file switches on SSLProxyEngine which informs Apache to expect an SSL certificate from the Nextcloud server.

Enabling SSLProxyEngine implies that if the Nextcloud server doesn't have a valid SSL certificate that communication between the two servers will fail. Because of this, it must also be assumed that the Nextcloud server has a valid SSL certificate. Intuitively, this likely was done by leveraging certbot in its own environment.

Recalling the parent note of this writing, certbot was not used. Instead, a self-signed certificate was generated using Apache's SSL mod by envoking a2enmod ssl followed by a2ensite default-ssl. If this is the context in which the Nextcloud server is operating, another set of directives need to be added to the configuration file.

The new set of directives essentially act as flags to disable validation of certificate authority. The first directive is to set SSLProxyVerify to optional_no_ca. Documentation describes this directive as:

When a proxy is configured to forward requests to a remote SSL server, this directive can be used to configure certificate verification of the remote server.

The remote server may present a valid Certificate but it need not to be (successfully) verifiable when set to optional_no_ca.

This is followed by setting SSLProxyCheckPeerCN to off. Which is described as:

This directive sets whether the remote server certificate's CN field is compared against the hostname of the request URL.

This is necessary as mod ssl does not include information about the common name by default.

Similarly, SSLProxyCheckPeerName is set to off:

This directive configures host name checking for server certificates when mod_ssl is acting as an SSL client. The check will succeed if the host name from the request URI matches one of the CN attribute(s) of the certificate's subject.

Finally, SSLProxyCheckPeerExpire is set to off, as it is the case that mod ssl has no expiry date.

These new directives lead to a configuration file that looks as such:

These are necessary to allow Apache to play nice when the remote (Nextcloud) server is using a self-signed certificate using mod ssl. This ensures that the communication between the domain server and the Nextcloud server is encrypted, which cannot be easily verified using a web browsers certificate inspector.

With the above configuration file in place, the domain server can now allow access to the Nextcloud server by accessing the domain and navigating through the nextcloud/ subdirectory. Communication between all parties will be encrypted and the typical user will be unaware that any computational offload has occurred. This makes good use of hardware resources an individual may have on hand instead of footing a larger bill in cloud hosting to have access to a virtual server that can handle the productivity suite.

It should be emphasized that a typical user may be unaware. There are more avenues to shore up security within this access point between servers to mind adversarial visitors who are a bit too curious. This will be discussion for a later note. Hopefully what was discussed in this note helps gain a better understanding of how Nextcloud and Apache configuration works.