Hosting Nextcloud with Docker

This is a guide how to host Nextcloud in single node docker swarm on Centos 7 behind HAproxy.

Install and configure iptables

Here is a guide for Basic iptables rules

Add following before COMMIT

-A INPUT -p tcp -m conntrack --ctstate NEW -m multiport --dports 80,443 -m comment --comment "HAproxy" -j ACCEPT

Reload iptables rules

sudo systemctl reload iptables

Bundle certs into one file

HAproxy want all certs bundled into one file. I use Let’s Encrypt certs as an example. Change DOMAIN on following commands.
Generate new dhparam if you don’t have one.

sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048
sudo chmod 600 /etc/ssl/certs/dhparam.pem
sudo cat DOMAIN.crt DOMAIN.key DOMAIN.ca /etc/ssl/certs/dhparam.pem > /etc/ssl/certs/DOMAIN/ha.bundle
sudo chmod 600 /etc/ssl/certs/DOMAIN/ha.bundle
sudo restorecon -R /etc/ssl/certs/

You can find Let’s Encrypt Intermediate Certificate ( DOMAIN.ca ) from https://letsencrypt.org/certificates/

Install and configure HAproxy

sudo yum install -y haproxy
sudo systemctl enable haproxy
sudo cp -av /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg.default
sudo vi /etc/haproxy/haproxy.cfg

Change DOMAIN

global
  ssl-default-bind-ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256
  ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets
  ssl-default-server-ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256
  ssl-default-server-options no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets
  user haproxy 
  group haproxy 
  daemon

defaults
  mode http
  option http-server-close
  option forwardfor except 127.0.0.0/8
  option redispatch
  retries 3
  timeout http-request 10s
  timeout queue 1m
  timeout connect 10s
  timeout client 1m
  timeout server 1m
  timeout http-keep-alive 10s
  timeout check 10s
  maxconn 30

frontend http
  bind :80
  redirect scheme https code 301 if !{ ssl_fc }

frontend public
  bind :443 ssl crt /etc/ssl/certs/DOMAIN/ha.bundle
  # Enable HSTS if you know you want it
#  http-response set-header Strict-Transport-Security max-age=15768000
  http-request set-header X-Forwarded-Port %[dst_port]

  acl host_nextcloud hdr(host) -i DOMAIN
  use_backend nextcloud if host_nextcloud    
  default_backend deny_backend

backend deny_backend
  http-request deny
backend nextcloud
  option httpchk HEAD / HTTP/1.1\r\nHost:DOMAIN 
  server nextcloud 127.0.0.1:8008 check

Use https://mozilla.github.io/server-side-tls/ssl-config-generator/ to generate up to date configuration.

Be sure you know what HSTS does and then decide if you want to use it.

sudo haproxy -c -f /etc/haproxy/haproxy.cfg
sudo systemctl start haproxy

Install docker and docker-compose

sudo yum install -y yum-utils device-mapper-persistent-data lvm2
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo yum install -y docker-ce docker-ce-cli containerd.io
sudo curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose

https://docs.docker.com/install/linux/docker-ce/centos/
https://docs.docker.com/compose/install/

Change docker default location (Optional)

sudo vi /etc/docker/daemon.json

{
"data-root":"/NEWLOCATION"
}

Start docker and init swarm

sudo systemctl enable docker
sudo systemctl start docker
sudo docker swarm init

Deploy Nextcloud stack

Create secrets

Use strong passwords.

printf "PASSWORD" | sudo docker secret create POSTGRES_PASSWORD -

https://docs.docker.com/engine/reference/commandline/secret_create/

Create compose file

vi docker-compose.yml

version: '3.7'
services:

  cloud:
    image: nextcloud:production
    ports:
      - 8008:80
    volumes:
      - nextcloud:/var/www/html
    deploy:
      replicas: 1
      restart_policy:
        condition: any

  postgres:
    image: postgres:11.2
    secrets:
      - POSTGRES_PASSWORD
    environment:
      - POSTGRES_PASSWORD_FILE=/run/secrets/POSTGRES_PASSWORD
    volumes:
      - postgresql:/var/lib/postgresql/data
    deploy:
      replicas: 1
      restart_policy:
        condition: any

volumes:
  nextcloud:
  postgresql:

secrets:
  POSTGRES_PASSWORD:
    external: true

https://docs.docker.com/compose/compose-file/

vi deployNextCloudStack.sh

#!/bin/bash
docker pull nextcloud:production
docker pull postgres:11.2
docker stack deploy --compose-file docker-compose.yml cloud
chmod +x deployNextCloudStack.sh
sudo ./deployNextCloudStack.sh

Update Nextcloud and Postgres containers

sudo ./deployNextCloudStack.sh

Nextcloud first time setup

Click Storage & Database open

Select PostgreSQL tab

Database user: postgres
Database password: USERPASSWORD
Database name: nextcloud
Database host: postgres

Database host is just postgres because it is postgreSQL containers name and docker swarm handles internal dns.

Backup with Duplicity and pg_dumpall

Generate PGP key for root user, with strong password

sudo su –

gpg --gen-key
# (1) RSA and RSA (default)
# 4096
# 2y

Export keys to safe location

pub 4096R/XXXXXXXX

gpg -a --export XXXXXXXX > duplicityGPGpublic.key
gpg -a --export-secret-keys XXXXXXXX > duplicityGPGprivate.key

remember to remove .key files from server after you moved them to safe location.

Sign new PGP key with you master PGP key (Optional)

# where you store your master key
gpg --import duplicityGPGpublic.key
gpg --sign-key --ask-cert-level XXXXXXXX
# 3
# y
gpg -a --export XXXXXXXX > signedDuplicityGPGpublic.key
# move back to server
gpg --import signedDuplicityGPGpublic.key

Backup script

I use SFTP to move files and i have added this servers root users SSH publickey to my backup server.

sudo vi backupNextcloud.sh

#!/bin/bash
export PASSPHRASE='GPGPASSWORD'
duplicity  --encrypt-key=XXXXXXXX --sign-key=XXXXXXXX --file-prefix=CLOUDNEXTCLOUD --volsize 1024 --full-if-older-than 1M PATHTO/docker/volumes/cloud_nextcloud/ sftp://[email protected]/location
duplicity remove-all-but-n-full 6 --force sftp://[email protected]/location
docker exec $(docker ps -q -f name=cloud_postgres) /usr/bin/pg_dumpall -cU postgres | gzip | gpg --batch --encrypt --sign --passphrase $PASSPHRASE --output PATHTO/cloud_postgres_backup-$(date +"%m-%d-%Y_%H-%M-%S").sql.gz.gpg -r XXXXXXXX
printf "put PATHTO/cloud_postgres_backup-*\nquit" | sftp -b- [email protected]:/location/
rm -f PATHTO/cloud_postgres_backup-* 
unset PASSPHRASE

Daily cron

sudo vi /etc/crontab

56 2 * * * root PATHTO/backupNextcloud.sh >> /var/log/backups/nextcloud.log 2>&1

sudo mkdir -p /var/log/backups/

Backup of the backups

My duplicity backups goes to my NAS. NAS will backup itself to AWS Glacier once a month.

Restore

Redo swarm if you lost it too.

  1. Empty nextcloud and postgres volumes.
  2. Start stack
  3. Move postgres backup into postgres volume
    1. cp -av cloud_postgres_backup.sql PATHTO/docker/volumes/cloud_postgresql/_data/
    2. connect to postgres container
      1. docker exec -i -t $(docker ps -q -f name=cloud_postgres) /bin/bash
      2. su - postgres
      3. psql < data/cloud_postgres_backup.sql
  4. Stop stack
  5. Move nextcloud files into nextcloud volume
    1. rm -Rf PATHTO/docker/volumes/cloud_nextcloud/_data/*
    2. rsync -av nextcloudbackup/* PATHTO/docker/volumes/cloud_nextcloud/_data/
    3. chown -R --reference=PATHTO/docker/volumes/cloud_nextcloud/_data/ PATHTO/docker/volumes/cloud_nextcloud/_data/*
    4. chcon -R --reference=PATHTO/docker/volumes/cloud_nextcloud/_data/ PATHTO/docker/volumes/cloud_nextcloud/_data/*
  6. Start stack
  7. Rescan files ( Optional )
    1. docker exec --user www-data $(docker ps -q -f name=cloud_cloud) php occ files:scan --all

Why did i choose all these components?

Nextcloud

I need some service to sync files across all my and my familys mobile devices. I used to use Mega but now i will try Nextcloud. Even if all data is encrypted in Mega i want to have all data on my own servers.

HAproxy

With HAproxy i can have multiple web service behind one IP. It is also easy to host all web service on same host with HTTP and use HAproxy to handle HTTPS. I don’t need to configure all services to work with HTTPS and i don’t need to give TLS private key to all web services.

Docker swarm

I could have just used docker-compose without swarm. But i wanted practice swarm and use secrets.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.