NextCloud and Cloudflare Tunnels
Believe me! This his good, wholesome fun!
This write up are instructions on how to successfully set up Nextcloud and cloudflare tunnels resulting, hopefully, in
working Cockpit
working NextCloud
well behaved iOS and Android apps.
persistent, authorized tunnels
a pod that will start automagically after a reboot
First, lets start with Fedora server 39. yeah.. I've got the exact same setup on Fedora server 38 - with the exception that SELinux works perfectly on 38. Not so much on 39. Is it fixable? Yes, yes it is. It's just annoying.
Anyways. Why Fedora server? Because of Cockpit and podman. Not much to it. I happen to like fedora. Get acquainted with Cockpit. It will take some time and will be well worth it. And no matter what anyone may or may not tell you. Cockpit works, it works in production. It is not particularly fragile. It's a good piece of tech. Use it.
This installation guide requires you to get a free Cloudflare account and a domain. You might not need a domain, but this tutorial assumes that you've got one.
Prepare Cloudflare by adding your domain to the Cloudflare dashboard -> Websites. If you haven't got a domain I'm certain Cloudflare can provide you with one. The docs should be able to tell you how to do this.
Cloudflared
Download cloudflared from this page https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/
I'd recommend downloading the binary version and stuffing it in ~/.local/bin with a chmod +x to make cloudflared executable. No one but you should need to have access to it.
$ cloudflared login # follw the instructions.
# after successfull authentication you will get a certificate,
# and an ID. Take note of both
$ cloudflared tunnel create nextcloud # tunnel name can be anything
$ cloudflared tunnel route dns <your-tunnel-id> <subdomain>
# You did set up Cloudflare to host your domain.. right.
# chose a subdomain.. cloud, for example. That's imaginative.
Create a new file config.yml - this can be placed anywhere. Your home folder is a good choice (It will eventually get copied to /etc/cloudflared/config.yml)
# contents of config.yml
tunnel: <your-tunnel-id>
credentials-file: /home/<you>/.cloudflared/<your-tunnel-id>.json
Ingress:
- hostname: subdomain.yourdomain.com
service: http://localhost:8080
- hostname: cockpit.yourdomain.com
service: http://localhost:9090
originRequest:
noTLSVerify: true
Let's detour and fix Cockpit. As root, create the file /etc/cockpit/cockpit.conf
[WebService]
Origins = https://cockpit.yourdomain.com wss://cockpit.yourdomain.com
ProtocolHeader = X-Forwarded-Proto
AllowUnencrypted = true
This will let cockpit be proxied without error through the cloudflared tunnel. Remember to restart cockpit.
$ sudo systemctl restart cockpit.service
We're mostly done with the cloudflared stuff.. just one more thing.
$ sudo /home/<you>/.local/bin/cloudflared \
--config /path/to/config.yml service install # as mentioned, this will
# copy your config.yml to /etc/cloudflared/config.yml
$ sudo systemctl start cloudflared
$ sudo systemctl status cloudflared
$ sudo systemctl enable cloudflared
Now our cloudflared tunnel configuration is installed and enabled. It will survive a reboot. Excellent!
On to NextCloud.
This is the Kube file that will create the pod with all the containers.
Be sure to change out the various passwords and correct the paths, exchanging <you> with your actual account name (I'm trying to make this as beginner friendly as possible, seriously).
# nextcloud.yaml
apiVersion: v1
kind: Pod
metadata:
labels:
app: nextcloud
name: nextcloud
spec:
containers:
- name: db
args:
- --transaction-isolation=READ-COMMITTED
- --binlog-format=ROW
command:
- docker-entrypoint.sh
env:
- name: MYSQL_ROOT_PASSWORD
value: <secret password>
- name: MYSQL_DATABASE
value: nextcloud
- name: MYSQL_USER
value: nextcloud
- name: MYSQL_PASSWORD
value: <secret password>
image: docker.io/library/mariadb:latest
securityContext:
allowPrivilegeEscalation: true
capabilities:
drop:
- CAP_MKNOD
- CAP_NET_RAW
- CAP_AUDIT_WRITE
privileged: false
readOnlyRootFilesystem: false
seLinuxOptions: {}
volumeMounts:
- mountPath: /var/lib/mysql:Z
name: home-<you>-containers-nextcloud-data-db
workingDir: /
- name: app
env:
- name: REDIS_HOST_PASSWORD
value: <secret password>
- name: MYSQL_HOST
value: 127.0.0.1
- name: MYSQL_DATABASE
value: nextcloud
- name: REDIS_HOST
value: 127.0.0.1
- name: MYSQL_USER
value: nextcloud
- name: MYSQL_PASSWORD
value: <the mysql password>
image: docker.io/library/nextcloud:latest
resources: {}
ports:
- containerPort: 80
hostPort: 8080
protocol: TCP
securityContext:
allowPrivilegeEscalation: true
capabilities:
drop:
- CAP_MKNOD
- CAP_NET_RAW
- CAP_AUDIT_WRITE
privileged: false
readOnlyRootFilesystem: false
seLinuxOptions: {}
volumeMounts:
- mountPath: /var/www/html:Z
name: home-<you>-containers-nextcloud-data-html
workingDir: /var/www/html
- name: redis
command:
- redis-server
- --requirepass
- <secret password>
image: docker.io/library/redis:alpine
resources: {}
securityContext:
allowPrivilegeEscalation: true
capabilities:
drop:
- CAP_MKNOD
- CAP_NET_RAW
- CAP_AUDIT_WRITE
privileged: false
readOnlyRootFilesystem: true
seLinuxOptions: {}
volumeMounts:
- mountPath: /tmp:Z
name: tmpfs
- mountPath: /var/tmp:Z
name: tmpfs
- mountPath: /run:Z
name: tmpfs
workingDir: /data
- name: cron
image: docker.io/library/nextcloud:latest
command: ["/cron.sh"]
resources: {}
securityContext:
allowPrivilegeEscalation: true
capabilities:
drop:
- CAP_MKNOD
- CAP_NET_RAW
- CAP_AUDIT_WRITE
privileged: false
readOnlyRootFilesystem: false
seLinuxOptions: {}
volumeMounts:
- mountPath: /var/www/html:Z
name: home-<you>-containers-nextcloud-data-html
workingDir: /var/www/html
restartPolicy: Always
volumes:
- hostPath:
path: /home/<you>/containers/nextcloud/data/db
type: Directory
name: home-<you>-containers-nextcloud-data-db
- hostPath:
path: /home/<you>/containers/nextcloud/data/html
type: Directory
name: home-<you>-containers-nextcloud-data-html
- hostPath:
path: tmpfs
type: DirectoryOrCreate
name: tmpfs
Now, create the necessary directories.
$ cd $HOME; \
mkdir -p containers/nextcloud/data/html ;\
mkdir -p containers/nextcloud/data/db
Great, nearly there. We will be running this as rootless containers, ie. as an unprivileged user (you).. and we will run into trouble for not being allowed to create ports below 1024. So we need to fix that.
# edit /etc/sysctl.conf as root
# add this line at the end of the file.
net.ipv4.ip_unprivileged_port_start=80
You can now either chose to reboot or
$ sudo sysctl net.ipv4.ip_unprivileged_port_start=80
Which will temporarily let you use ports below 1024 without rebooting.
It's time to create the pod.
$ # remember, do this a you, the user. Not root!
$ podman kube play path/to/nexcloud.yaml
This will churn along for quite some time depending on your hardware and internet connection. With fair winds and Odins luck (which you really shouldn't put to much faith in) everything just works and
$ podman pod ps
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS
96exxxxxxxxx nextcloud Running About an hour ago 6adxxxxxxxxx 5
$ podman ps
6ade3c848dc4 localhost/podman-pause:4.9.0-1706090847 About an hour ago Up About an hour 0.0.0.0:8080->80/tcp 96e88382ac54-infra
59865dbccd75 docker.io/library/mariadb:latest --transaction-iso... About an hour ago Up About an hour 0.0.0.0:8080->80/tcp nextcloud-db
f015ddcdb8db docker.io/library/nextcloud:latest apache2-foregroun... About an hour ago Up About an hour 0.0.0.0:8080->80/tcp nextcloud-app
1a22384699bf docker.io/library/redis:alpine About an hour ago Up About an hour 0.0.0.0:8080->80/tcp nextcloud-redis
12b1bb3f2e0e docker.io/library/nextcloud:latest About an hour ago Up About an hour 0.0.0.0:8080->80/tcp nextcloud-cron
Yeah.. Now, most stuff has configured it self. But there are .. rough edges.
You'll notice that the IOS and Android app won't be able to connect to your server, so lets fix that.
As root, edit this file /home/<you>/containers/nextcloud/data/html/config/config.php
/* Add the following structures to your file,
* update the scructures if they allready exists
*/
'trusted_proxies' =>
array (
0 => '<the ip address of your machine>',
),
'overwriteprotocol' => 'https',
'trusted_domains' =>
array (
0 => 'localhost',
1 => '<the ip address of your machine>',
2 => 'subdomain.yourdomain.com',
),
/* Also, find and update this variable */
'overwrite.cli.url' => 'http://subdomain.yourdomain.com'
Now, restart your pod.
Did you think we were done? We're not. We need to get the pod to start up again in case of catastrophic failure.. like a reboot or what ever. Podman switched to something called Quadlets .. not long ago. It's not difficult, but there's scarce information about how to use it with pods. Well.. not exactly - as long as you realize that pods are Kubes. In either case..
Create a directory
$ cd $HOME; mkdir -p .config/containers/systemd
In this directory you will create one file named nexcloud.kube
# $HOME/.config/containers/systemd/nextcloud.kube
[Unit]
Description=A kubernetes yaml based service
Before=local-fs.target
[Kube]
Yaml=/path/to/nextcloud.yaml
[Install]
WantedBy=multi-user.target default.target
Now run
$ loginctl enable-linger <you> # for good measure
$ /usr/libexec/podman/quadlets -dryrun -user # to test the .kube
$ systemctl --user daemon-reload # this creates the nextcloud.service
$ systemctl --user start nextcloud.service
And, we're done.
Now you can enjoy the glory of Cockpit and NextCloud even after a reboot.
'till next time.