[docker-compose] Ghost 3 et Traefik v2

Tut's Linux févr. 24, 2020

Le but de cette documentation est de vous fournir un fichier .yml clé en main pour installer le CMS Ghost derrière Traefik.


Article mis à jour suite aux commentaires de ldez oncernant un soucis de configuration côté Traefik (mélange entre conf' statique et dynamique) 😉 (merci !!)

Mise à jour : 22/05/2020

Ghost est un CMS pour propulser des sites web type "blog", un peu comme ComputerZ Solutions ;). Cet outil nécessite une base de données dans laquelle y seront stockées toutes les données (pages, posts et configurations). J'utilise Traefik en frontal de Ghost - plus de détails à cette adresse (CZS).

Quelques pré-requis avant de se lancer : avoir Docker (pour Ubuntu ou CentOS 7) et docker-compose installé sur votre machine. J'utilise le chemin "/srv/docker" sur mon serveur et y ait créé un dossier "conf" où sont stockés les fichiers de configuration pour les différents services.

Sans plus attendre, voici le fichier docker-compose.yml :

---
version: '3.8'
services:
  traefik:
    container_name: traefik
    image: traefik:chevrotin
    restart: unless-stopped
    ports:
      - 80:80
      - 443:443
      - 8080:8080
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./conf/traefik.toml:/etc/traefik/traefik.toml:ro
      - ./conf/traefik_dynamic.toml:/etc/traefik/traefik_dynamic.toml:ro
      - ./conf/acme.json:/acme.json

  sqlghost:
    container_name: sqlghost
    image: mariadb:bionic
    restart: always
    command: --sql-mode=""
    volumes:
      - datasqlghost:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: '1'
      MYSQL_USER: ghost
      MYSQL_PASSWORD: sqlghostuserpassword
      MYSQL_DATABASE: ghost

  ghost:
    container_name: cmsghost
    image: ghost:3
    restart: always
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
      - dataghost:/var/lib/ghost/content
      - ./conf/config.production.json:/var/lib/ghost/config.production.json:ro
    depends_on:
      - sqlghost
    labels:
      traefik.enable: true
      traefik.http.routers.ghost-http.rule: Host(`ghost.czs.local`)
      traefik.http.routers.ghost-http.entrypoints: web
      traefik.http.services.ghost.loadbalancer.server.port: 2368
      traefik.http.routers.ghost-http.middlewares: https-redirect@file
      traefik.http.routers.ghost-https.rule: Host(`ghost.czs.local`)
      traefik.http.routers.ghost-https.entrypoints: websecure
      traefik.http.routers.ghost-https.middlewares: compression@file, security@file
      traefik.http.routers.ghost-https.tls: true
      traefik.http.routers.ghost-https.tls.certresolver: "letsencrypt"

volumes:
  datasqlghost:
  dataghost:
docker-compose.yml

Les lignes sont explicites : Création du service "traefik" pour le conteneur reverse-proxy, le service "sqlghost" à base de MariaDB et le service "webghost" pour le CMS Ghost. Seul Ghost a des labels correspondant pour Traefik - avec ces quelques lignes, votre Ghost sera "routable" vers le web et automatiquement géré en SSL.
Vous remarquerez aussi des volumes complémentaires comme "/etc/timezone" et "/etc/localtime" - utilisé pour être en relation avec le serveur hôte (qui est à l'heure via NTP) et donc avoir un horodatage cohérent. Les fichiers de configurations sont à côté du docker-compose.yml, libre à vous de les placer ailleurs (et de modifier le chemin dans la conf)

Les labels Traefik mis en place dans ce fichier docker-compose.yml sont dit en "configuration statique".


Avant de lancer les conteneurs, vous devez encore créer des fichiers, notamment deux pour Traefik :

traefik.toml

[global]
  sendAnonymousUsage = false
  checkNewVersion = false

[api]
  insecure = true
  dashboard = true
  #debug = true

[log]
  level = "WARNING"

[providers]
  [providers.docker]
    endpoint = "unix:///var/run/docker.sock"
    exposedByDefault = false
    watch = true
    swarmMode = false

  [providers.file]
    filename = "/etc/traefik/traefik_dynamic.toml"
    watch = true

[entryPoints]
  [entryPoints.web]
    address = ":80"

  [entryPoints.websecure]
    address = ":443"

[certificatesResolvers]
  [certificatesResolvers.letsencrypt]
    [certificatesResolvers.letsencrypt.acme]
      email = "contact@czs.local"
      caServer = "https://acme-v02.api.letsencrypt.org/directory"
      storage = "acme.json"
      keyType = "EC384"
        [certificatesResolvers.letsencrypt.acme.httpChallenge]
          entryPoint = "web"
traefik.toml

Le fichier de configuration Traefik se décompose sous forme de blocs - tout est assez explicite, je ne vais pas faire une description pour chaque ligne.

traefik_dynamic.toml

[tls]
  [tls.options]
    [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"]
    [tls.options.mintls13]
      minVersion = "VersionTLS13"


[http]
  [http.middlewares.compression.compress]
    excludedContentTypes = ["text/event-stream"]

  [http.middlewares.https-redirect.redirectScheme]
    scheme = "https"
    permanent = true

  [http.middlewares.security.headers]
    accessControlAllowMethods = ["GET", "OPTIONS", "PUT"]
    #accessControlAllowOriginList = "*"
    accessControlMaxAge = 100
    addVaryHeader = true
    browserXssFilter = true
    contentTypeNosniff = true
    forceSTSHeader = true
    frameDeny = true
    sslRedirect = true
    #sslForceHost = true
    stsIncludeSubdomains = true
    stsPreload = true
    #ContentSecurityPolicy = "default-src 'self' 'unsafe-inline'"
    customFrameOptionsValue = "SAMEORIGIN"
    referrerPolicy = "same-origin"
    featurePolicy = "vibrate 'self'"
    stsSeconds = 315360000

Ghost a aussi besoin d'un fichier de configuration pour être exploitable. Suivez bien le formalisme du fichier .json requis - ci-dessous un exemple de conf' :

{
    "url": "https://ghost.czs.local",
    "server": {
      "port": 2368,
      "host": "0.0.0.0"
    },
    "database": {
      "client": "mysql",
      "connection": {
        "host": "sqlghost",
        "user": "ghost",
        "port": "3306",
        "password": "sqlghostuserpassword",
        "database": "ghost"
      }
    },
    "privacy": {
      "useUpdateCheck": false,
      "useGravatar": false,
      "useRpcPing": false,
      "useStructuredData": true
    },
    "paths": {
      "contentPath": "/var/lib/ghost/content"
    }
  }
config.production.json

Maintenant que vos fichiers sont créés, vous pouvez initier un "docker-compose up -d" et voir la magie s'opérer... :) Le site web sous Ghost sera accessible au nom de domaine stipulé dans la conf', Traefik effectuera automatiquement le lien/la redirection et le tout avec du HTTPS via Let's Encrypt.

Vous pouvez trouver l'ensemble des scripts à cette adresse Github.

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

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.