[docker-compose] Traefik, nginx et Nextcloud

Tut's Linux avr. 14, 2020

Objectif de l'article : obtenir un serveur Nextcloud derrière Nginx et le reverse-proxy Traefik. La configuration HTTPS effectuée par le fichier "traefik_dynamic.toml" et les labels correspondants.

  • Mise à jour le 04/07/2020 : ajout des configurations en .yml pour Traefik
  • Mise à jour le 20/07/2020 : suppression des fichiers .toml, mise à jour intégrale des fichiers (ajout des manquants), ajout de quelques commentaires et précisions quant à la mise en place
  • Mise à jour le 12/08/2020 : correction du montage du dossier de configuration nginx + suppression des labels "container_name"

Logiciels exploités :

  • Ubuntu 18.04 LTS
  • Docker CE = 19.03.8
  • Docker-compose = 1.25.4
  • Traefik = 2.2 "chevrotin"
  • Nginx = 1.17.x "alpine" (version stable)
  • MariaDB = 10.14.12
  • Nextcloud =  19.0.4
  • PHP = 7.3.16-fpm "alpine"
  • Redis = 6

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

Fichier "traefik.yml" (/srv/docker/conf/traefik.yml)

global:
  sendAnonymousUsage: false
  checkNewVersion: false

api:
  #insecure: true
  dashboard: true
  #debug: true

log:
  filePath: "/etc/traefik/applog.log"
  level: INFO

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

  file:
    directory: /traefikdynamic
    watch: true

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

certificatesResolvers:
  letsencrypt:
    acme:
      email: contact@czs.local
#      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

Fichier "traefik_dynamic.yml" (/srv/docker/conf/traefikdynamic/traefik_dynamic.yml)

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
        #accessControlAllowOriginList = "*"
        accessControlMaxAge: 100
        addVaryHeader: true
        browserXssFilter: true
        contentTypeNosniff: true
        forceSTSHeader: true
        frameDeny: true
        sslRedirect: true
        sslForceHost: true
        stsIncludeSubdomain: true
        stsPreload: true
        #ContentSecurityPolicy = "default-src 'self' 'unsafe-inline'"
        customFrameOptionsValue: SAMEORIGIN
        referrerPolicy: same-origin
        featurePolicy: vibrate 'self'
        stsSeconds: 315360000

    authentification:
      basicAuth:
        users: # admon / totolasticot
        - admon:$2y$10$4IgUsvlCwENsF18B9t7AoegQ7eWZVfWxPFRhNfBWz7F00h1X2oZFe

Ci-dessous le fichier docker-compose :

---
version: '3.8'
services:
  traefik:
    image: traefik:${TRAEFIKVERSION}
    restart: unless-stopped
    ports:
      - 80:80
      - 443:443
      - 8080:8080 # used to have the traefik dashboard
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
      - ./conf/traefik.yml:/etc/traefik/traefik.yml:ro
      - ./conf/traefikdynamic:/traefikdynamic:ro
      - ./logs/traefik.log:/etc/traefik/applog.log
      - ./conf/acme.json:/acme.json

  nginx:
    image: nginx:${NGINXVERSION}
    restart: unless-stopped
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
      - datanextcloud:/var/www/html
      - datanginxnextcloudlogs:/var/log/nginx/
      - ./conf/nginx-nextcloud:/etc/nginx/
    links:
      - nextcloud
    labels:
      traefik.enable: true
      traefik.http.routers.nginxnextcloud-https.entrypoints: "websecure"
      traefik.http.routers.nginxnextcloud-https.rule: "Host(`nextcloud.czs.local`)"
      traefik.http.routers.nginxnextcloud-https.middlewares: "security@file, compression@file"
      traefik.http.routers.nginxnextcloud-https.tls: "true"
      traefik.http.routers.nginxnextcloud-https.tls.certresolver: "letsencrypt"

  nextcloud:
    image: nextcloud:${NEXTCLOUDVERSION}
    restart: unless-stopped
    links:
      - mariadb
    volumes:
      - datanextcloud:/var/www/html
    environment:
      MYSQL_HOST: dbnextcloud
      MYSQL_USER: ${MYSQLUSER}
      MYSQL_PASSWORD: ${MYSQLPASSWORD}
      MYSQL_DATABASE: ${MYSQLDB}
      REDIS_HOST: redisnextcloud
    labels:
      traefik.enable: false

  mariadb:
    image: mariadb:${MARIADBVERSION}
    command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW
    restart: unless-stopped
    volumes:
      - datadbnextcloud:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: '1'
      MYSQL_USER: ${MYSQLUSER}
      MYSQL_PASSWORD: ${MYSQLPASSWORD}
      MYSQL_DATABASE: ${MYSQLDB}
    labels:
      traefik.enable: false

  redis:
    image: redis:${REDISVERSION}
    restart: unless-stopped
    volumes:
      - dataredisnextcloud:/data
    labels:
      traefik.enable: false

volumes:
  datanextcloud:
  datadbnextcloud:
  datanginxnextcloudlogs:
  dataredisnextcloud:

Le fichier de variables d'environnement (à mettre à la racine, dans /srv/docker, à côté du fichier docker-compose.yml en somme)

MYSQLUSER=nextcloudsqluser
MYSQLPASSWORD=nextcloudsqlpassword
MYSQLDB=nextclouddb
NEXTCLOUDVERSION=19-fpm-alpine
MARIADBVERSION=focal
REDISVERSION=6
NGINXVERSION=stable-alpine
TRAEFIKVERSION=v2.2.8

Fichiers de configuration pour nginx/nextcloud :

nginx.conf (/srv/docker/conf/nginx-nextcloud/nginx.conf)

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

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

http {
############# NGINX conf
  include /etc/nginx/mime.types;
  include /etc/nginx/fastcgi.conf;

  sendfile     on;
  tcp_nopush   on;
  server_names_hash_bucket_size 128;

############## NGINX security
  server_tokens off;
  proxy_hide_header X-Powered-By;
  client_body_buffer_size 10K;
  client_header_buffer_size 1k;
  client_max_body_size 8M;
  large_client_header_buffers 2 1k;

  client_body_timeout 12;
  client_header_timeout 12;
  keepalive_timeout 15;
  send_timeout 10;

  gzip             on;
  gzip_comp_level  2;
  gzip_min_length  1000;
  gzip_proxied     expired no-cache no-store private auth;
  gzip_types       text/plain application/x-javascript text/xml text/css application/xml;

############# NEXTCLOUD conf
  include /etc/nginx/nextcloud.conf;
}

mime.types (/srv/docker/conf/nginx-nextcloud/mime.types)

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.conf (/srv/docker/conf/nginx-nextcloud/fastcgi.conf)

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

fastcgi_param  REDIRECT_STATUS    200;

fichier nextcloud.conf pour nginx (/srv/docker/conf/nginx-nextcloud/nextcloud.conf)

server {
    listen 80;
    server_name nextcloud.czs.local;

    root /var/www/html;
    index index.php;

    client_max_body_size 16M;

    access_log /var/log/nginx/nextcloud-access.log;
    error_log /var/log/nginx/nextcloud-error.log;

    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass nextcloud:9000;
        fastcgi_index index.php;
        include fastcgi.conf;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
}

Avec toute cette configuration, désormais, vous êtes en mesure d'avoir votre serveur Nextcloud derrière Traefik, en SSL (automatique) et avec une "empreinte logicielle" la plus faible possible !

Vous pouvez retrouver l'intégralité des scripts sur le GitHub à cette adresse.

Mettmett/docker-compose
Some examples used on ComputerZ Solutions... Contribute to Mettmett/docker-compose development by creating an account on GitHub.

Mots clés

Julien HOMMET

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

Super ! Vous vous êtes inscrit avec succès.
Super ! Effectuez le paiement pour obtenir l'accès complet.
Bon retour parmi nous ! Vous vous êtes connecté avec succès.
Parfait ! Votre compte est entièrement activé, vous avez désormais accès à tout le contenu.