Nextcloud vous permet la mise en place d’un espace de stockage centralisé, disposant d’applications de bureautique utiles selon vos documents.

Par ce document, nous allons mettre en place cet outil avec docker-compose, derrière Nginx et le reverse-proxy Traefik. La configuration HTTPS effectuée par le fichier traefik_dynamic.yml et les labels correspondants.

Logiciels exploités :

  • Debian 10 (buster)
  • Docker CE = 19.03.x
  • Docker-compose = 1.28.x
  • Traefik = 2.4 « livarot »
  • Nginx = 1.19.x (version stable)
  • MariaDB = 10.15 (focal)
  • Nextcloud =  21.x
  • PHP = 7.4x-fpm
  • Redis = 6.x
nextcloud logo

Mise en place de la stack Nextcloud

J’utilise le dossier /srv/docker pour mettre en place ces conteneurs. Libre à vous de modifier l’emplacement. A l’intérieur de ce dossier, plusieurs choses sont faites : création des dossiers nécessaires (conf + conf/traefikdynamic, logs, fichier acme.json (pour Let’s Enycrypt))

mkdir -p /srv/docker/conf/traefikdynamic
mkdir /srv/docker/conf/nginx-nextcloud
mkdir /srv/docker/logs

touch /srv/docker/logs/traefik.log
touch /srv/docker/conf/acme.json

chmod 0600 /srv/docker/conf/acme.json

Pour une meilleure lisibilité de l’article, les blocs de code pour la configuration de Traefik et Nextcloud ont été enlevés. A la place, des liens vers le dépôt GitHub sont mis.

Sans plus attendre, voici les fichiers de configuration pour Traefik :

---
global:
  sendAnonymousUsage: false
  checkNewVersion: false

api:
  dashboard: true

pilot:
  dashboard: true

log:
  level: ERROR

providers:
  docker:
    endpoint: unix:///var/run/docker.sock
    exposedByDefault: false
    watch: true
    swarmMode: false

  file:
    directory: "/etc/traefik/dynamic"
    watch: true

entryPoints:
  web:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https
  websecure:
    address: ":443"

certificatesResolvers:
  letsencrypt:
    acme:
      email: [email protected]
#      caServer: https://acme-staging-v02.api.letsencrypt.org/directory
      caServer: https://acme-v02.api.letsencrypt.org/directory
      storage: acme.json
      keyType: EC256
      httpChallenge:
        entryPoint: web

---
tls:
  options:
    default:
      minVersion: VersionTLS12
      sniStrict: true
      cipherSuites:
        - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
        - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
        - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
        - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
        - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
        - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305
        - TLS_AES_128_GCM_SHA256
        - TLS_AES_256_GCM_SHA384
        - TLS_CHACHA20_POLY1305_SHA256
      curvePreferences:
        - CurveP521
        - CurveP384

http:
  middlewares:
    compression:
      compress:
        excludedContentTypes:
          - text/event-stream

    security:
      headers:
        accessControlAllowMethods:
          - GET
          - OPTIONS
          - PUT
        accessControlMaxAge: 100
        addVaryHeader: true
        browserXssFilter: true
        contentTypeNosniff: true
        forceSTSHeader: true
        frameDeny: true
        sslRedirect: true
        sslForceHost: true
        stsPreload: true
        customFrameOptionsValue: SAMEORIGIN
        referrerPolicy: "same-origin"
        featurePolicy: "camera 'none'; microphone 'none'; payment 'none'; usb 'none';"
        stsSeconds: 315360000
        hostsProxyHeaders:
          - "X-Forwarded-Host"

    authentification:
      basicAuth:
        users:
        - admin:$2y$10$byzmasKak2LLmvY86KcnGO0pX8UaaNDxQZMJW.wMmaoHRhjNXqOwG # admin/admin

  routers:
    rt-traefik:
      entryPoints:
      - websecure
      middlewares:
      - authentification
      service: [email protected]
      rule: Host (`traefik.czs.local`)
      tls:
        certResolver: letsencrypt

    rt-nextcloud:
      entryPoints:
      - websecure
      middlewares:
      - security
      - compression
      service: sc-nextcloud
      rule: Host (`czs.local`)
      tls:
        certResolver: letsencrypt

  services:
    sc-nextcloud:
      loadBalancer:
        servers:
        - url: "http://nginxnextcloud:80"


Maintenant, les fichiers de configuration pour Nginx :

worker_processes auto; # use "grep processor /proc/cpuinfo | wc -l" and type the number here, or stay with automatic configuration

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024; # use "ulimit -n" and type the number here
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;
    server_tokens   off;
    keepalive_timeout  65;

    set_real_ip_from  10.0.0.0/8;
    set_real_ip_from  172.16.0.0/12;
    set_real_ip_from  192.168.0.0/16;
    real_ip_header    X-Real-IP;

    upstream php-handler {
        server nextcloudfpm:9000;
    }

    server {
        listen 80;

        add_header Referrer-Policy "no-referrer" always;
        add_header X-Content-Type-Options "nosniff" always;
        add_header X-Download-Options "noopen" always;
        add_header X-Frame-Options "SAMEORIGIN" always;
        add_header X-Permitted-Cross-Domain-Policies "none" always;
        add_header X-Robots-Tag "none" always;
        add_header X-XSS-Protection "1; mode=block" 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:$server_port/remote.php/dav;
        }

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

        ############## Let NGINX see client real IPs
        real_ip_header X-Forwarded-For;
        set_real_ip_from traefik;

        client_max_body_size 10G;
        fastcgi_buffers 64 4K;

        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;

        location / {
            rewrite ^ /index.php;
        }

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

        location ~ ^\/(?:index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+)\.php(?:$|\/) {
            fastcgi_split_path_info ^(.+?\.php)(\/.*|)$;
            set $path_info $fastcgi_path_info;
            try_files $fastcgi_script_name =404;
            include fastcgi.conf;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            fastcgi_param PATH_INFO $path_info;
            fastcgi_param HTTPS on;
            fastcgi_param modHeadersAvailable true;
            fastcgi_param front_controller_active true;
            fastcgi_pass php-handler;
            fastcgi_intercept_errors on;
            fastcgi_request_buffering off;
            fastcgi_read_timeout 600;
        }

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

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

        location ~ \.(?:png|html|ttf|ico|jpg|jpeg|bcmap|mp4|webm)$ {
            try_files $uri /index.php$request_uri;
            access_log off;
        }
    }
}

types {
  text/html                             html htm shtml;
  text/css                              css;
  text/xml                              xml rss;
  image/gif                             gif;
  image/jpeg                            jpeg jpg;
  image/svg+xml                         svg svgz;
  application/x-javascript              js;
  text/plain                            txt;
  text/x-component                      htc;
  text/mathml                           mml;
  image/png                             png;
  image/x-icon                          ico;
  image/x-jng                           jng;
  image/vnd.wap.wbmp                    wbmp;
  application/java-archive              jar war ear;
  application/mac-binhex40              hqx;
  application/pdf                       pdf;
  application/x-cocoa                   cco;
  application/x-java-archive-diff       jardiff;
  application/x-java-jnlp-file          jnlp;
  application/x-makeself                run;
  application/x-perl                    pl pm;
  application/x-pilot                   prc pdb;
  application/x-rar-compressed          rar;
  application/x-redhat-package-manager  rpm;
  application/x-sea                     sea;
  application/x-shockwave-flash         swf;
  application/x-stuffit                 sit;
  application/x-tcl                     tcl tk;
  application/x-x509-ca-cert            der pem crt;
  application/x-xpinstall               xpi;
  application/zip                       zip;
  application/octet-stream              deb;
  application/octet-stream              bin exe dll;
  application/octet-stream              dmg;
  application/octet-stream              eot;
  application/octet-stream              iso img;
  application/octet-stream              msi msp msm;
  audio/mpeg                            mp3;
  audio/x-realaudio                     ra;
  video/mpeg                            mpeg mpg;
  video/quicktime                       mov;
  video/x-flv                           flv;
  video/x-msvideo                       avi;
  video/x-ms-wmv                        wmv;
  video/x-ms-asf                        asx asf;
  video/x-mng                           mng;
}

fastcgi_param  CONTENT_LENGTH $content_length;
fastcgi_param  CONTENT_TYPE $content_type;
fastcgi_param  DOCUMENT_ROOT $document_root;
fastcgi_param  DOCUMENT_URI $document_uri;
fastcgi_param  GATEWAY_INTERFACE CGI/1.1;
fastcgi_param  QUERY_STRING $query_string;
fastcgi_param  REDIRECT_STATUS 200;
fastcgi_param  REMOTE_ADDR  $remote_addr;
fastcgi_param  REMOTE_PORT  $remote_port;
fastcgi_param  REQUEST_METHOD $request_method;
fastcgi_param  REQUEST_URI $request_uri;
fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param  SCRIPT_NAME $fastcgi_script_name;
fastcgi_param  SERVER_ADDR $server_addr;
fastcgi_param  SERVER_NAME $server_name;
fastcgi_param  SERVER_PORT $server_port;
fastcgi_param  SERVER_PROTOCOL $server_protocol;
fastcgi_param  SERVER_SOFTWARE nginx/$nginx_version;

fastcgi_index  index.php;

Enfin, les fichiers nécessaires pour docker

fichier .env

MYSQLUSER=nextcloudsqluser
MYSQLPASSWORD=nextcloudsqlpassword
MYSQLDB=nextclouddb
NEXTCLOUDVERSION=21-fpm
MARIADBVERSION=focal
REDISVERSION=6
NGINXVERSION=stable
TRAEFIKVERSION=livarot

fichier docker-compose.yml

---
version: '3.7'
services:
  traefik:
    image: traefik:${TRAEFIKVERSION}
    restart: unless-stopped
    ports:
      - target : 80
        published : 80
        protocol: tcp
        mode : host
      - target : 443
        published : 443
        protocol: tcp
        mode : host
    volumes:
      - ./conf/acme.json:/acme.json
      - ./conf/traefik.yml:/etc/traefik/traefik.yml:ro
      - ./conf/traefikdynamic:/etc/traefik/dynamic:ro
      - ./logs/traefik.log:/etc/traefik/applog.log
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
    environment:
      TRAEFIK_PILOT_DASHBOARD: false
      TZ: "Europe/Paris"

  nginxnextcloud:
    image: nginx:${NGINXVERSION}
    restart: unless-stopped
    volumes:
      - ./conf/nginx-nextcloud:/etc/nginx
      - /etc/localtime:/etc/localtime:ro
      - datanextcloud:/var/www/html
      - datanginxnextcloudlogs:/var/log/nginx

  nextcloud:
    image: nextcloud:${NEXTCLOUDVERSION}
    restart: unless-stopped
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - datanextcloud:/var/www/html
    environment:
      MYSQL_HOST: sqlnextcloud
      MYSQL_USER: ${MYSQLUSER}
      MYSQL_PASSWORD: ${MYSQLPASSWORD}
      MYSQL_DATABASE: ${MYSQLDB}
      REDIS_HOST: redisnextcloud
      TZ: "Europe/Paris"
    links:
      - mariadb

  mariadb:
    image: mariadb:${MARIADBVERSION}
    command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW
    restart: unless-stopped
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - datadbnextcloud:/var/lib/mysql
    environment:
      MYSQL_RANDOM_ROOT_PASSWORD: '1'
      MYSQL_USER: ${MYSQLUSER}
      MYSQL_PASSWORD: ${MYSQLPASSWORD}
      MYSQL_DATABASE: ${MYSQLDB}
      TZ: "Europe/Paris"

  redis:
    image: redis:${REDISVERSION}
    restart: unless-stopped
    environment:
      TZ: "Europe/Paris"
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - dataredisnextcloud:/data

volumes:
  datadbnextcloud:
  datanextcloud:
  datanginxnextcloudlogs:
  dataredisnextcloud:

Avec cet ensemble de fichiers, vous êtes en mesure d’avoir votre serveur Nextcloud avec nginx derrière Traefik, en SSL (automatique et let’s encrypt) ayant une faible empreinte logicielle. Un petit docker-compose up -d et le tour est joué. Ne vous restera plus qu’à finaliser la configuration des services, créer les comptes nécessaires, ajouter les sécurités et extensions…

La mise à jour de de l’article a été effectué le 19/11/2021, il se peut qu’un décalage soit présent entre cette page et le dépôt git concerné. Vous pouvez retrouver l’intégralité des scripts sur le GitHub à cette adresse : https://github.com/Mettmett/docker-compose/tree/master/traefik-nginx-nextcloud-sql-redis.

Auteur

Bercé par l'informatique depuis mon plus jeune âge, je transforme ma passion en expertise.

1 commentaire

  1. Merci pour cet article.
    J’ai neanmoins un probleme a l’utilisation : chaque requete genere un grand nombre de 302 avant de parvenir a la page desiree…

Écrire un commentaire