Après 2 mois d’utilisation, je peux dire que la synchronisation avec gmail fonctionne de manière très efficace. Pour me connecter au serveur IMAP dovecot (avec le client Thunderbird), j’estimais ne pas avoir besoin d’exposer le service lorsque je ne l’utilisais pas, et qu’une redirection de port ponctuelle me suffisait. En pratique, je vérifie de temps en temps cette boîte gmail à partir de mon serveur local (puisque la boîte gmail est vidée après chaque synchronisation), et devoir ouvrir la redirection de port est finalement déplaisante (même si ce n’est qu’à un clic de bouton dans l’outil Kube Forwarder).
La solution la plus simple aurait été d’exposer le service dovecot dans un
service Kubernetes de type NodePort (dont un exemple est inclus dans l’article
référencé). Mais il est aussi possible d’utiliser un IngressRouteTCP avec
Traefik.
Pourquoi IngressRouteTCP plutôt que NodePort ?
J’avoue que pour l’instant, j’ai peu d’arguments sur l’intérêt d’un
IngressRouteTCP versus un NodePort dans ma configuration actuelle,
c’est-à-dire un cluster Kubernetes d’un seul noeud, sur lequel une seule
instance dovecot tourne (donc pas besoin de load balancing).
J’aime cependant l’idée de savoir que dans mon cluster Kubernetes, tout ce qui
vient de l’extérieur est routé par un seul outil (Traefik en l’occurence) et que
tous mes services sont configurés comme locaux (de type ClusterIP). Je trouve
que ça simplifie les choix que je fais, en n’ayant « qu’une seule façon » de
faire quelque chose, et réduire ma charge cognitive. C’est d’ailleurs, je pense,
l’idée principale derrière
Kubernetes Gateway API qui est encore un
projet en cours de maturation (mais utilisable en mode expérimental).
Le fait d’utiliser Traefik permet aussi d’utiliser des middlewares. Pour TCP, ils sont réduits à deux pour le moment: InFlightConn (limiter le nombre de connexions concurrentes) et IPAllowList (limiter les adresses IP client).
Si plus tard je configure le protocol TLS sur dovecot, alors il se peut que le routeur Traefik (ou Gateway API si je passe à celle-ci) soit très intéressant car il supporte l’extension TLS SNI, qui permettrait de router le même port TCP exposé vers différents services Kubernetes en fonction du nom de domaine spécifié par le client qui se connecte. Sans SNI, on reste limité à un port exposé par service.
Mise en oeuvre
Bien que pas super pratique à configurer, le principe et la mise en oeuvre sont assez simple:
- Modifier le fichier de configuration de Traefik pour ajouter un
EntryPoint. En pratique, il s’agit donc d’éditer un HelmChartConfig sur le serveur Kubernetes. Traefik va détecter ce changement et se redéployer automatiquement en ouvrant le ou les nouveaux ports (ports en écoute sur le serveur). - Déployer un nouveau manifeste de type
IngressRouteTCPpour le lien entre l’EntryPointet notre service de typeClusterIP. - Il faut aussi modifier la configuration par défaut de dovecot pour qu’il autorise les connexions distantes non sécurisées avec TLS (en dehors de localhost).
Pour rappel, voici le manifeste du service dovecot (serveur IMAP) que je souhaite exposer:
apiVersion: v1
kind: Service
metadata:
name: dovecot-service
labels:
app.kubernetes.io/name: dovecot
spec:
selector:
app.kubernetes.io/name: dovecot
type: ClusterIP
ports:
- name: imap
port: 143
protocol: TCP
targetPort: 31143
Il n’y a aucun changement à faire sur celui-ci.
Pour que dovecot accepte les connexions distantes sans TLS, il faut ajouter son
réseau dans le paramètre login_trusted_networks. Concrètement, il s’agit de
mettre à jour le ConfigMap:
apiVersion: v1
kind: ConfigMap
metadata:
name: dovecot-config
namespace: default
labels:
app.kubernetes.io/name: dovecot
data:
conf.d: |
# For unsecure remote connection
login_trusted_networks = 192.168.0.0/16
# https://doc.dovecot.org/main/core/config/service.html#service_vsz_limit
service_vsz_limit = unlimited
doveadm_api_key = $ENV:DOVEADM_API_KEY
doveadm_password = $ENV:DOVEADM_PASSWORD
Ci-dessus, j’ai ajouté login_trusted_networks = 192.168.0.0/16 qu’il faut
adapter en fonction du réseau de chacun. J’ai mis « 192.168.0.0/16 » comme
exemple (ça ne correspond pas non plus à mon réseau), qui correspond à la plage
de 192.168.0.0 à 192.168.255.255.
On peut ensuite ajouter un IngressRouteTCP:
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRouteTCP
metadata:
name: dovecot-ingress-tcp
spec:
entryPoints:
- imap
routes:
- match: HostSNI(`*`)
services:
- name: dovecot-service
port: 143
Les deux choses importantes: entryPoints définit « imap » qui est le point
d’entrée que l’on va devoir ajouter dans la configuration de Traefik pour que ça
fonctionne. match définit « HostSNI(`*`) » qui est la seule valeur
possible quand on n’utilise pas TLS (avec TLS, on aurait eu l’avantage de
pouvoir router vers ce service seulement en fonction du domaine indiqué par le
client qui se connecte, et donc ça aurait permis d’utiliser le même port exposé
pour différents services).
Le reste du manifeste est évident à comprendre. C’est un exemple très simple mais fonctionnel. La documentation de toutes les options est ici.
Avant de déployer ce manifeste (ou si on le déploie, avant que ça puisse
fonctionner…): il faut ajouter l’entryPoint « imap » à la configuration de
Traefik. Dans un cluster K3S, il s’agit d’un HelmChartConfig à cet emplacement
dans le serveur (via une connexion SSH):
/var/lib/rancher/k3s/server/manifests/traefik-config.yaml
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
name: traefik
namespace: kube-system
spec:
# Structure traefik v2: https://github.com/traefik/traefik-helm-chart/blob/master/traefik/values.yaml
valuesContent: |-
ports:
imap:
# for dovecot
port: 143
exposedPort: 143
expose: true
L’exemple ci-dessus est partiel et ne contient que la partie intéressante: la section « ports » (qui correspond aux « entryPoints ») et la nouvelle section « imap » qui expose le port 143.
Un point d’attention à avoir: bien connaître la version de Traefik car la configuration peut différer en fonction des versions majeures. Dans mon cas, j’utilise encore la version 2.
Quand c’est fait, cela devrait fonctionner immédiatement. Il reste à
reconfigurer le client (Thunderbird dans mon cas) pour ne plus se connecter en
localhost (avec la redirection de port) mais avec le nom du serveur K3S. Aucun
changement n’est nécessaire côté client si on remplace un service de type
NodePort.