Après un article sur la mise en place de conteneur WordPress avec Docker, je me devais de réaliser la même chose mais cette fois avec un environnement Kubernetes !

Comme je ne suis pas ici pour faire un simple copier/coller d'une technologie à une autre, je vais en profiter pour expliquer quelques petites astuces de Traefik que je n'ai pas encore eu l'occasion d'évoquer sur le blog, notamment dans son utilisation avec Kubernetes !

Nous verrons, par exemple, comment appliquer des options TLS à toutes nos routes Ingress. Mais aussi l'application de Middleware !

Ce sera également l'occasion de revoir une installation de Traefik avec les CRD de Kubernetes et notamment la mise en place d'un filtrage IP pour l'accès à l'interface d'administration. Et oui, nous n'allons donc pas négliger la partie sécurité !

J'en profite également pour utiliser Kustomize pour gérer mes configurations !

Pré-requis 🏗️

Comme à mon habitude, je vais réaliser l'intégralité de ce tutoriel avec un environnement K3S. Pour réaliser cette installation, rien de plus simple :

curl -sfL https://get.k3s.io | sh -s - --no-deploy=traefik

Nous allons réaliser l'installation de Traefik par la suite.

Quickstart 🚀

Voici comment procéder à cette mise en place en quelques minutes !

Récupérez les fichier sur le dépôt Git :

git clone https://github.com/lfache/awesome-traefik-kubernetes

Afin de personnaliser votre installation, voici les éléments à modifier pour Traefik dans le dossier traefik-kustomize-sample/production :

Variable Fichier à modifier Example
email traefik-configmap.yaml foo.bar@mydomain.com
storage traefik-volumeclaim.yaml 20Gi

Ensuite vous pouvez lancer Traefik :

kubectl apply -k ./traefik-kustomize-sample/production

Pour personnaliser votre installation de WordPress, voici les éléments à modifier dans le dossier wordpress/ :

Variable Fichier à modifier Example
database kustomization.yaml YOUR_DATABASE
user kustomization.yaml YOUR_USER
password kustomization.yaml YOUR_PASSWORD

Enfin vous pouvez lancer WordPress :

kubectl apply -k ./wordpress

Enjoy :

Quelques explications 🤔

Pour réaliser ces déploiements, j'utilise Kustomize. J'ai réalisé une présentation de cet outil lors d'un précédent article que vous pouvez consulter ici :

Kubernetes : Kustomize & Traefik
Comment personnaliser ses déploiements dans un environnement Kubernetes à l’aide de Kustomize? Et conserver une base commune? Nous allons voir comment réaliser ceci avec un exemple concret !

J'utilise la version de Traefik 2.2, vous pouvez trouver plus de détails sur cette version sur le test de la version release candidate que j'avais pu effectuer il y a quelques semaines :

Traefik 2.2
La société Containous qui propose le projet Traefik a annoncé ce vendredi, la mise à disposition d’une version 2.2 en “Release candidate”. Je n’ai donc pas hésité !

Pour rappel, au menu de cette version, je vais utiliser :

  • Redirection dès la création de l'entrypoint HTTP vers le HTTPS,

Tout ceci est déclaré dans mon fichier ConfigMap :

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

Enfin nous allons aborder une "petite" modification de la 2.2 qui prend tout son sens avec Kubernetes.

Cet ajout est presque passé inaperçu mais heureusement je suis là pour vous faire découvrir tout ça ! 😂

Les options LTS

Habituellement - et le plus simple - pour déclarer des options TLS dans un environnement Kubernetes, on applique les options à la création de la route Ingress :

Exemple :

apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: traefik-tls
  namespace: default
spec:
  entryPoints:
    - websecure
  routes:
  - kind: Rule
    match: Host(``)
    services:
    - name: XXX
      port: 80
  tls:
    certResolver: le
    options:
      name: mytlsoption
      namespace: default

🚩 Cette façon de faire fonctionne, aucun soucis. Par contre je n'aime pas appliquer une option pour chacune de mes routes Ingress alors que je souhaite la voir appliquer à tout mes sites ! 🚩

Heureusement la documentation de la version 2.2 nous apporte une nouvelle piste :

entryPoints:
  EntryPoint0:
    address: foobar
    http:
      tls:
        options: foobar

Tout comme les middlewares, on peut déclarer des options TLS par défaut ! Je vais donc déclarer un CRD avec mes options TLS :

apiVersion: traefik.containo.us/v1alpha1
kind: TLSOption
metadata:
  name: mytlsoption
  namespace: default
spec:
  minVersion: VersionTLS12
  sniStrict: true

Et m’empresser de déclarer cette ressource dans ma configuration statique, donc dans ma ressource ConfigMap :

      websecure:
        address: :443
        http:
          tls:
            options: default-mytlsoption 
            certResolver: myresolver

🚩 Attention de préfixer le nom de votre option avec votre namespace, j'utilise ici le namespace default ! 🚩

Cette option pourra également ravir ceux qui utilisent Traefik avec Docker Swarm et souhaitent utiliser le moins possible de fichier de configuration !

Headers et middlewares

Enfin vous pourrez noter que mon score sur securityheaders.com n'est que de A ! Pourquoi ?

Il était très simple d'obtenir un A+ pour une capture d'écran mais certaines CSP ne sont pas conseillées avec WordPress. J'ai donc préféré utiliser des headers réalistes et utilisables en production ( attention toutefois, à valider en fonction de vos modules ) et dégrader d'un cran mon score.

L'instruction qui dégrade ma note est la suivante : This policy contains 'unsafe-inline' which is dangerous in the style-src directive.

Enfin je déclare deux middlewares pour WordPress :

apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: wp-headers
spec:
  headers:
    frameDeny: true
    contentTypeNosniff: true
    browserXssFilter: true
    #HSTS
    forceSTSHeader: true
    stsIncludeSubdomains: true
    stsSeconds: 31536000
    # remove Server and X-Powered-By headers
    customResponseHeaders:
      Server: ''
      X-Powered-By: ''
    # add some sec headers
    contentSecurityPolicy: "default-src 'self' data: ; script-src 'self' 'unsafe-inline' ; style-src 'self' 'unsafe-inline' fonts.googleapis.com https://code.jquery.com ; font-src 'self' fonts.gstatic.com data: ; img-src 'self' data: https://code.jquery.com"
    referrerPolicy: "same-origin"
    featurePolicy: "vibrate 'self'; geolocation 'self'; midi 'self'; notifications 'self'; push 'self'; microphone 'none'; camera 'none'; magnetometer 'none'; gyroscope 'none'; speaker 'none'; vibrate 'self'; fullscreen 'self'"

Celui-ci afin de manipuler les en-têtes de sécurité et de retirer certaines en-têtes renvoyées par WordPress ( Server et X-Powered-By ).

Enfin je déclare un middleware pour restreindre l'accès à l'administration :

apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: wp-ipwhitelist
spec:
  ipWhiteList:
    sourceRange:
      - X.X.X.X/32

Il ne reste plus qu'à appliquer ces middlewares à vos routes :

apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: ingressroutetls
spec:
  entryPoints:
    - websecure
  routes:
  - match: Host(`wordpress.mydomain.com`)
    kind: Rule
    services:
    - name: wordpress
      port: 80
    middlewares:
      - name: wp-headers

  - match: Host(`wordpress.mydomain.com`) && (Path(`/wp-admin`)|| Path(`/wp-login.php`))
    kind: Rule
    services:
    - name: wordpress
      port: 80
    middlewares:
      - name: wp-ipwhitelist


Nous avons donc pu installer WordPress dans un environnement Kubernetes à l'aide de Traefik ! Cet article est l'occasion d'affiner notre déploiement avec des options de sécurité que nous n'avions pas encore eu l'occasion de mettre en place.

Il nous a également permis de découvrir une nouvelle option de la version 2.2 qui va nous permettre d'activer des options TLS à toutes nos routes Ingress sans les rappeler de façon systématique !

En tout cas  n'hésitez pas à m'apporter des remarques ou des commentaires sur Twitter ! C'est toujours un plaisir d'avoir des retours ! 😘