Install NextCloud Docker and Integrate with Nginx and LetsEncrypt SSL Certificate - NETSEC

Latest

Learning, Sharing, Creating

Cybersecurity Memo

Monday, March 1, 2021

Install NextCloud Docker and Integrate with Nginx and LetsEncrypt SSL Certificate

Previously, I had a couple of posts to present how to install NextCloud and how to configure it with some different storage options. This post is similar as one of them, but it will add two dockers to add SSL certificate for Nginx.

Here are all steps. For Dock Compose file, there are two options , one with MySQL DB, another without DB, which NextCloud will use built-in Sqlite for DB. 



Install Docker and Docker Compose

1  Update Package list and Upgrade Packages for Ubuntu 18.04

Update and Upgrade Ubuntu to latest 

$ sudo apt-get -y update && sudo apt-get -y upgrade

2  Install Docker on Ubuntu

sudo apt install docker.io -y
sudo -i
systemctl start docker
systemctl enable docker
docker version
3  Install Docker Compose on Ubuntu


root@ip-172-31-28-5:~# curl -L "https://github.com/docker/compose/releases/download/1.19.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   651  100   651    0     0   7076      0 --:--:-- --:--:-- --:--:--  7076
100 8288k  100 8288k    0     0  4671k      0  0:00:01  0:00:01 --:--:-- 7520k
root@ip-172-31-28-5:~# chmod +x /usr/local/bin/docker-compose

root@ip-172-31-28-5:~# docker-compose version
docker-compose version 1.19.0, build 9e633ef
docker-py version: 2.7.0
CPython version: 2.7.13
OpenSSL version: OpenSSL 1.0.1t  3 May 2016
root@ip-172-31-28-5:~#


Create NextCloud Project Folder and Nginx conf.d Folder

Create a conf.d folder for nginx configure files

mkdir -p /opt/nextcloud/conf.d

Create a nextcloud.conf nginx configuration file 

nano /opt/nextcloud/conf.d/nextcloud.conf


Copy following information into nextcloud.conf file:

upstream php-handler {
    server app:9000;
}

server {
    listen       80;
    listen       443 ssl http2;
    server_name  ${FQDN};
    # Max allowed upload file in web page
    client_max_body_size 20G;
    if ($server_port !~ 443){
        rewrite ^(/.*)$ https://$host$1 permanent;
    }

    # SSL and Support TLSv1.3
    ssl_certificate    /etc/letsencrypt/live/${FQDN}/fullchain.pem;
    ssl_certificate_key    /etc/letsencrypt/live/${FQDN}/privkey.pem;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
    ssl_ciphers TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:TLS13-AES-128-CCM-8-SHA256:TLS13-AES-128-CCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    error_page 497  https://$host$request_uri;

    # add header information to fix Nextcloud console warning messages
    add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Robots-Tag none;
    add_header X-Download-Options noopen;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Permitted-Cross-Domain-Policies none;
    add_header Referrer-Policy no-referrer;
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    fastcgi_hide_header X-Powered-By;

    root /var/www/html;

    location = /robots.txt {
        allow all;
        log_not_found off;
        access_log off;
    }

    location = /.well-known/carddav {
      return 301 $scheme://$host/remote.php/dav;
    }
    location = /.well-known/caldav {
      return 301 $scheme://$host/remote.php/dav;
    }

    # Enable gzip compression
    gzip on;
    gzip_vary on;
    gzip_comp_level 4;
    gzip_min_length 256;
    gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
    gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;

    # redirection and disable to access backend sensitive folders and resources
    location / {
        rewrite ^ /index.php$request_uri;
    }

    location ~ ^\/(?:build|tests|config|lib|3rdparty|templates|data)\/ {
        deny all;
    }
    location ~ ^\/(?:\.|autotest|occ|issue|indie|db_|console) {
        deny all;
    }

    # PHP configuration
    location ~ ^\/(?:index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|ocs-provider\/.+)\.php(?:$|\/) {
        fastcgi_split_path_info ^(.+?\.php)(\/.*|)$;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
        fastcgi_param HTTPS on;
        #Avoid sending the security headers twice
        fastcgi_param modHeadersAvailable true;
        fastcgi_param front_controller_active true;
        fastcgi_pass php-handler;
        fastcgi_intercept_errors on;
        fastcgi_request_buffering off;
    }

    location ~ ^\/(?:updater|ocs-provider)(?:$|\/) {
        try_files $uri/ =404;
        index index.php;
    }

    location ~ \.(?:css|js|woff2?|svg|gif)$ {
        try_files $uri /index.php$request_uri;
        add_header Cache-Control "public, max-age=15778463";
        add_header X-Content-Type-Options nosniff;
        add_header X-XSS-Protection "1; mode=block";
        add_header X-Robots-Tag none;
        add_header X-Download-Options noopen;
        add_header X-Permitted-Cross-Domain-Policies none;
        add_header Referrer-Policy no-referrer;
        access_log off;
    }

    location ~ \.(?:png|html|ttf|ico|jpg|jpeg)$ {
        try_files $uri /index.php$request_uri;
        # Optional: Don't log access to other assets
        access_log off;
    }
}



Create a new nextcloud docker-compose file 


nano /opt/nextcloud/docker-compose.yml

1. Create a new nextcloud docker-compose file without DB

Copy following information into compose file

version: '3'

volumes:
    letsencrypt:
    nextcloud:

services: 
    proxy:
        image: staticfloat/nginx-certbot
        ports:
            - 80:80
            - 443:443
        environment: 
            CERTBOT_EMAIL: example@51sec.com
            ENVSUBST_VARS: FQDN
            FQDN: nextcloud.51sec.org
        volumes:
            - ./conf.d:/etc/nginx/user.conf.d:ro
            - letsencrypt:/etc/letsencrypt
            - nextcloud:/var/www/html
        restart: unless-stopped
   
    app:
        image: nextcloud:fpm
        volumes:
            - nextcloud:/var/www/html
            - ./app/data:/var/www/html/data
            - ./app/config:/var/www/html/config
        restart: unless-stopped

    cron:
        image: rcdailey/nextcloud-cronjob
        environment:
            - NEXTCLOUD_CRON_MINUTE_INTERVAL=15
            - NEXTCLOUD_CONTAINER_NAME=app
            - NEXTCLOUD_PROJECT_NAME=nextcloud
        volumes:
            - /var/run/docker.sock:/var/run/docker.sock:ro
            - /etc/localtime:/etc/localtime:ro
        restart: always


2. Create a new nextcloud docker-compose file with DB

Copy following information into compose file

version: '3'

volumes:
    letsencrypt:
    nextcloud:

services: 
    proxy:
        image: staticfloat/nginx-certbot
        ports:
            - 80:80
            - 443:443
        environment: 
            CERTBOT_EMAIL: example@51sec.com
            ENVSUBST_VARS: FQDN
            FQDN: nextcloud.51sec.org
        volumes:
            - ./conf.d:/etc/nginx/user.conf.d:ro
            - letsencrypt:/etc/letsencrypt
            - nextcloud:/var/www/html
        restart: unless-stopped

    db:
        image: mariadb
        environment:
            - MYSQL_ROOT_PASSWORD=Password1234!@
            - MYSQL_PASSWORD=Password1234!@
            - MYSQL_DATABASE=nextcloud
            - MYSQL_USER=nextcloud
        volumes: 
            - ./mysql:/var/lib/mysql
        restart: unless-stopped
    
    app:
        image: nextcloud:fpm
        volumes:
            - nextcloud:/var/www/html
            - ./app/data:/var/www/html/data
            - ./app/config:/var/www/html/config
        restart: unless-stopped

    cron:
        image: rcdailey/nextcloud-cronjob
        environment:
            - NEXTCLOUD_CRON_MINUTE_INTERVAL=15
            - NEXTCLOUD_CONTAINER_NAME=app
            - NEXTCLOUD_PROJECT_NAME=nextcloud
        volumes:
            - /var/run/docker.sock:/var/run/docker.sock:ro
            - /etc/localtime:/etc/localtime:ro
        restart: always


You might want to change following configuration using your own settings:

            CERTBOT_EMAIL: example@51sec.com
            ENVSUBST_VARS: FQDN
            FQDN: nextcloud.51sec.org
           - MYSQL_ROOT_PASSWORD=Password1234!@
            - MYSQL_PASSWORD=Password1234!@
            - MYSQL_DATABASE=nextcloud
            - MYSQL_USER=nextcloud
Please make sure your dns record has been created for nextcloud.51sec.org, which is pointing to this virtual machine's public ip.

Please make sure your firewall ports 80 and 443 opened on your cloud security group and local virtual machine. By default, Ubuntu18.04 UFW status is inactive. 

After confirmed all configuration has ben set correctly, you can start up your docker using docker-compose command:

cd /opt/nextcloud
docker-compose up -d


Using your Browser to connect to the domain url, in my case, it is  https://nextcloud.51sec.org







After installation


After installation, There are a couple of things to clean up and to configure further. 

1  Install Dockerwe will have to run following two commands to fix settings warnings:

docker-compose exec -u www-data app php occ db:add-missing-indices
docker-compose exec -u www-data app php occ db:convert-filecache-bigint
2  Adding local file folder under your nextCloud user
For example, if your nextcloud user is admin, you would like to add your local files under this user, you will need to copy all files to following folder.
/opt/nextcloud/app/data/admin/files

After copied all files into this folder, using following command to scan them:

docker-compose exec -u www-data app php occ files:scan --all

3  If you would like to install ffmpeg, following these commands:

docker exec -it nextcloud_app_1 bash
apt -y update
apt -y install ffmpeg



Backup & Restore

Here are some steps to backup and restore your NextCloud system:

1  Stop all dockers running using docker-compose command.
cd /opt/nextcloud
docker-compose stop
2  Pack all files from /opt/nextcloud folder
cd /opt
tar -zcvf nextcloud.tar.gz nextcloud
3  Transfer tar file to new machine
scp nextcloud.tar.gz root@serverip:/opt
4  Install Docker and Docker Compose on new machine
cd /opt
tar -xzvf nextcloud.tar.gz

5  Bring dockers up, which should still keeps all of your files and configuration

cd nextcloud
docker-compose up -d



YouTube:







Another Way to Quickly Bring NextCloud Docker Up

Go to NextCloud office image site at Docker Hub: https://hub.docker.com/_/nextcloud

Go to "Base version - apache" section under "Running this image with docker-compose"

Copy the whole code block into a new Portainer's stack's editor.

Only change is to add "--innodb-read-only-compressed=OFF" into command line. 
version: '2'

volumes:
  nextcloud:
  db:

services:
  db:
    image: mariadb
    restart: always
    command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW --innodb-read-only-compressed=OFF
    volumes:
      - db:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=Password1!
      - MYSQL_PASSWORD=Password12!
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud

  app:
    image: nextcloud
    restart: always
    ports:
      - 8080:80
    links:
      - db
    volumes:
      - nextcloud:/var/www/html
    environment:
      - MYSQL_PASSWORD=Password12!
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud
      - MYSQL_HOST=db


Click "Deploy the stack" button.

You can now access Nextcloud by accessing the port 8080 on the server where Portainer is running.

http://<your_vm_ip>:8080




References









7 comments:

  1. Thank you, I just managed to set this up on Raspberry Pi 4b. Advice: remove http2 from nginx config to enhance upload speed.

    ReplyDelete
    Replies
    1. What images did you go with for your Pi 4 install? I haven't had any luck so far with getting the proxy service going. My DB seems to be working. The Nextcloud image log is showing some errors, but I haven't been able to access the interface to test it out.

      Delete
  2. Would you please be so kind and let me know how can I configure nginx to load from mysite.com/nexcloud instead from mystic.com? Thanks a lot in advance!

    ReplyDelete
  3. Would you please be so kind and let me know how can I configure nginx to load from mysite.com/nexcloud instead from mystic.com? Thanks a lot in advance!

    ReplyDelete
  4. Would you please be so kind and let me know how can I configure nginx to load from mysite.com/nexcloud instead from mystic.com? Thanks a lot in advance!

    ReplyDelete
  5. Thank you for this easy and brilliant guide, that worked like a charm. But it seems that the cert is not in auto renew mode... Any chance to have the command to renew it or set this as auto renew, please?

    ReplyDelete
  6. This comment has been removed by a blog administrator.

    ReplyDelete