So I think I have managed to move my WP Traefik deployment from v1.7 to v2.0, so I should be save if they decide to retire the 1.7 docker image sometime in the future. Not that I see them removing v1.7 anytime soon, but should they decide to do that, I should be fine.

Anyway, here’s My swarm-friendly stack configuration:

version: "3.3"

services:
  traefik:
    image: "traefik:v2.2.1"
    networks:
      - wpnet
    command:
      - --entrypoints.web.address=:80
      - --entrypoints.websecure.address=:443
      - --providers.docker=true
      - --providers.docker.swarmMode=true
      - --providers.docker.network=wpnet
      - --providers.docker.exposedbydefault=false
      - --providers.file.directory=/configuration/
      - --providers.file.watch=true
    ports:
      - target: 80
        published: 80
        mode: host
      - target: 443
        published: 443
        mode: host
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "/app/wp/traefik/configuration/:/configuration/"
    deploy:
      placement:
        constraints:
          - node.role == manager

  wpdb:
    image: mysql
    networks:
      - wpnet
    volumes:
      - /app/wp/db/data:/var/lib/mysql
      - /etc/localtime:/etc/localtime:ro
    environment:
      - MYSQL_ROOT_PASSWORD=nomunomuchowa
    deploy:
      placement:
        constraints:
          - node.labels.db == true
  wpapp:
    image: wordpress:5.4.1-php7.2-apache
    networks:
      - wpnet
    depends_on:
      - wpdb
    volumes:
      - /app/wp/app/wp-content:/var/www/html/wp-content
      - /app/wp/app/conf/logformat.conf:/etc/apache2/conf-enabled/logformat.conf
      - /app/wp/app/conf/security.conf:/etc/apache2/conf-available/security.conf
      - /app/wp/app/conf/php.ini:/usr/local/etc/php/php.ini
    environment:
      - WORDPRESS_DB_HOST=wpdb
      - WORDPRESS_DB_NAME=journaldb
      - WORDPRESS_DB_USER=wpuser
      - WORDPRESS_DB_PASSWORD=dikalasibukmaupunsenggang
    deploy:
      replicas: 1
      labels:
        - "traefik.enable=true"
        - "traefik.http.routers.my-app.rule=Host(`journal.mach5.web.id`) || Host(`mach5.web.id`)"
        - "traefik.http.routers.my-app.entrypoints=websecure"
        - "traefik.http.routers.my-app.tls=true"
        - "traefik.http.services.my-app.loadbalancer.server.port=80"
        - "traefik.http.services.my-app.loadbalancer.sticky=true"
        - "traefik.http.services.my-app.loadbalancer.sticky.cookie.name=StickyCookie"
        - "traefik.http.services.my-app.loadbalancer.sticky.cookie.secure=true"
        - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
        - "traefik.http.routers.redirs.rule=hostregexp(`{host:.+}`)"
        - "traefik.http.routers.redirs.entrypoints=web"
        - "traefik.http.routers.redirs.middlewares=redirect-to-https"
        - "traefik.http.routers.my-app.middlewares=secheader"
        - "traefik.http.middlewares.secheader.headers.referrerPolicy=no-referrer"
        - "traefik.http.middlewares.secheader.headers.framedeny=true"
        - "traefik.http.middlewares.secheader.headers.stsSeconds=31536000"
        - "traefik.http.middlewares.secheader.headers.forceSTSHeader=true"
        - "traefik.http.middlewares.secheader.headers.stsPreload=true"
        - "traefik.http.middlewares.secheader.headers.stsIncludeSubdomains=true"
        - "traefik.http.middlewares.secheader.headers.browserXssFilter=true"
        - "traefik.http.middlewares.secheader.headers.contentTypeNosniff=true"
        - "traefik.http.middlewares.secheader.headers.featurePolicy=self"
        - "traefik.http.middlewares.secheader.headers.customRequestHeaders.X-Forwarded-Proto=https"


networks:
  wpnet:
     external: true

A few notable entries from the config above:

TLS/SSL Certificate configuration

As you can see from the configuration above

 - "traefik.http.routers.my-app.rule=Host(`journal.mach5.web.id`) || Host(`mach5.web.id`)"

I’ll publishing the blog under the URL https://mach5.web.id, and https://mach5.web.id, which can be configured with wildcard certificate, or one for each domain. To achive the later, I’ll be using Traefik’s file provider, which is configured here:

 - --providers.file.directory=/configuration/ 
 - --providers.file.watch=true

The path /configuration is configured to resolve to local directory through:

 volumes: 
   - "/var/run/docker.sock:/var/run/docker.sock:ro" 
   - "/app/wp/traefik/configuration/:/configuration/"

Inside the local directory, there are a couple of files:

surfer@DM1:/app/wp/traefik/configuration$ ls -la
total 44
drwxr-xr-x 2 root root 4096 Jun 8 15:15 .
drwxr-xr-x 3 root root 4096 May 7 07:15 ..
-rw-r--r-- 1 root root  249 Jun 8 15:15 certificates.toml
-rw------- 1 root root 1679 Jun 8 15:13 journal.mach5.web.id.key
-rw-r--r-- 1 root root 3574 Jun 7 15:32 journal.mach5.web.id.pem
-rw------- 1 root root 1675 Jun 8 15:12 mach5.web.id.key
-rw-r--r-- 1 root root 3550 Jun 8 15:11 mach5.web.id.pem
-rw-r--r-- 1 root root  184 May 7 17:50 tls.toml

The file certificates.toml configure the name and path for the certificates used on the deployment:

[[tls.certificates]]
   certFile = "/configuration/journal.mach5.web.id.pem"
   keyFile = "/configuration/journal.mach5.web.id.key"
[[tls.certificates]]
   certFile = "/configuration/mach5.web.id.pem"
   keyFile = "/configuration/mach5.web.id.key"

All you have to do to use multiple certificate is to rename the certificate pairs that you received from your provider in correspodence to subdomains and/or domains used on the deployment, and put the path in the toml file. To make it easier, use the extention “.key” for private key, and “.pem” or “.crt” for the fullchain certificate.

The other toml file on the directory contains the TLS configuration, I’m using to enforce the use of TLS v1.2 as a minimum version of TLS used on the deployment:

[tls.options]
  [tls.options.default]
    minVersion = "VersionTLS12"
    cipherSuites = [
      "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
    ]
    preferServerCipherSuites = true

Logging client’s real IP addresses

To get real client IP address logged, combine this configuration on Traefik:

    ports:
      - target: 80
        published: 80
        mode: host
      - target: 443
        published: 443
        mode: host

And on WordPress:

 volumes:

    volumes:
      - /app/wp/app/conf/logformat.conf:/etc/apache2/conf-enabled/logformat.conf

Which contains:

surfer@DM1:~$ more /app/wp/app/conf/logformat.conf 
LogFormat "%a %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined

Securing The Deployment

The last bit I want to talk about is secure http headers configuration, which are contained in these lines:

        - "traefik.http.routers.my-app.middlewares=secheader
        - "traefik.http.middlewares.secheader.headers.referrerPolicy=no-referrer"
        - "traefik.http.middlewares.secheader.headers.framedeny=true"
        - "traefik.http.middlewares.secheader.headers.stsSeconds=31536000"
        - "traefik.http.middlewares.secheader.headers.forceSTSHeader=true"
        - "traefik.http.middlewares.secheader.headers.stsPreload=true"
        - "traefik.http.middlewares.secheader.headers.stsIncludeSubdomains=true"
        - "traefik.http.middlewares.secheader.headers.browserXssFilter=true"
        - "traefik.http.middlewares.secheader.headers.contentTypeNosniff=true"
        - "traefik.http.middlewares.secheader.headers.featurePolicy=self"
        - "traefik.http.middlewares.secheader.headers.customRequestHeaders.X-Forwarded-Proto=https"

By ikhsan

Leave a Reply

Your email address will not be published. Required fields are marked *

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