Un environnement de développement local avec Caddy

Développement juil. 31, 2020

Architecture via services managés

Il est de plus en plus courant de concevoir des architectures full services managés. Les avantages sont nombreux : sécurité, réduction des coûts d'exploitation, gestion simplifiée, rapidité de déploiement, scalabilité, ...

Chez AWS par exemple, on peut déployer des services de type API REST via Elastic Container Services (ECS) ou Elastic Kubernetes Services (EKS), les exposer via une API Gateway pour gérer la sécurité via l'IdP de l'entreprise en OAuth2, déployer la partie Frontend dans un bucket S3 et utiliser un RDS Aurora pour la base de données. Aucun serveur n'est déployé, pas de machine à gérer. On ajoute une touche de CloudFront et de Certificate Manager pour gérer l'accès en HTTPS et le tour est joué !

Généralement on développe les services en local puis on les déploie sur un environnement d'intégration dans le Cloud. Pour certains services c'est assez simple : la base de données peut être locale, le contenu statique du front servi par npm-serve ou express, les services via express ou docker...

Intégration du fournisseur d'identité Open IDC de l'entreprise

Pour les autres et notamment les API, cela devient vite difficile de réaliser les tests et de développer des interactions en local sur son poste de développement dès lors que l'on utilise des briques de l'entreprise. Par exemple : un service qui récupère les informations de l'utilisateur authentifié. Il est difficile de connecter son environnement local sur le fournisseur d'identité (Identity Provider - IdP) de l'entreprise. Seules solutions :

  • Récupérer des tokens d'authentification (p.ex. AccessToken JWT) de l'IdP pour les utiliser localement. C'est fastidieux...
  • Connecter l'IdP avec un environnement local. C'est compliqué...

C'est ce dernier cas qui nous intéresse. Généralement, on utilise localhost ou 127.0.0.1 pour accéder à notre API. Avec express en NodeJS par exemple, on accéderait à notre service via http://127.0.0.1:3000/api/users/me. Inutile de vous dire qu'il n'est pas viable de ne pas utiliser le HTTPS même en local. Premièrement par ce que ce n'est pas une bonne pratique d'avoir un environnement de développement différent des autres environnements : cela amène des problématiques de sécurité (CORS, CSP, HSTS, ...) et que c'est généralement un prérequis pour s'intégrer à un IDP.

Caddy 2 pour simuler un environnement local

C'est ici que Caddy intervient. Caddy, actuellement dans sa version 2, est un serveur Web production-ready avec gestion du HTTPS par défaut. Il est écrit en Go et est sous licence Apache 2.0. L'avantage de Caddy par rapport à Apache ou Nginx voir Traefik, est qu'il ne nécessite qu'un binaire et un simple fichier Caddyfile pour le configurer.

La configuration est encore plus simple qu'un Nginx. Par exemple, pour servir des fichiers en HTTPS, il suffit de faire :

 caddy file-server --domain example.com
Servir des fichiers via HTTP avec Caddy

Une configuration plus poussée passe par l'écriture d'un fichier Caddyfile dont la syntaxe ressemble étroitement à du JSON mais sous forme de blocs d'options. Dans notre cas, voici la configuration qui nous intéresse :

www.dev.company.com {
  	tls webcert.crt webcert.key

	reverse_proxy localhost:5000 {
		header_up Host                {host}
	    header_up Origin              {host}
	    header_up X-Real-IP           {remote}
	    header_up X-Forwarded-Host    {host}
	    header_up X-Forwarded-Server  {host}
	    header_up X-Forwarded-Port    {port}
	    header_up X-Forwarded-For     {remote}
	    header_up X-Forwarded-Proto   {scheme}
	}
}

api.dev.company.com {
  	tls apicert.crt apicert.key

	reverse_proxy localhost:3000 {
		header_up Host                {host}
	    header_up Origin              {host}
	    header_up X-Real-IP           {remote}
	    header_up X-Forwarded-Host    {host}
	    header_up X-Forwarded-Server  {host}
	    header_up X-Forwarded-Port    {port}
	    header_up X-Forwarded-For     {remote}
	    header_up X-Forwarded-Proto   {scheme}
		header_down Access-Control-Allow-Origin       https://www.dev.company.com
	    header_down Access-Control-Allow-Credentials  true
	}
}
Caddyfile pour un environnement de développement local

Pour démarrer caddy avec cette configuration, il suffit de lancer caddy dans le répertoire qui contient la configuration ou de lui spécifier en ligne de commande via --config. Auparavant, il faudra générer les certificats SSL via une commande OpenSSL car les certificats Let's Encrypt ne sont pas utilisables en local :

openssl req -new -newkey rsa:2048 -sha256 -days 365 -nodes -x509 -keyout webcert.key -out webcert.crt
openssl req -new -newkey rsa:2048 -sha256 -days 365 -nodes -x509 -keyout apicert.key -out apicert.crt
Génération des certificats SSL en ligne de commande via OpenSSL

L'idée ici est d'exposer deux services :

Les services sont exposés par Caddy de manière sécurisée (HTTPS). Ce dernier proxifie les requêtes vers les différents instances de services locaux. A noter que www.dev.company.com et api.dev.company.com doivent rediriger vers 127.0.0.1. C'est faisable en ajoutant des entrées dans le fichier HOSTS local ou en créant une entrée A dans les enregistrements DNS du domaine company.com.

Le service ainsi exposé via https://www.dev.company.com est compatible avec le fournisseur d'authentification de l'entreprise car il est exposé en HTTPS et fait référence à un vrai domaine et non à localhost.

Caddy est vraiment une alternative à un Apache ou Nginx pour du développement local même si Traefik peut s'avérer plus intéressant dès lors que l'on souhaite disposer d'une infrastructure dynamique à base de Kubernetes ou Docker.

Yohann Ciurlik

Solution Architect - Gadget & innovation addict - 3D Printing - Raspπ Arduino - Maker - Photographer - SlashGen - Father - PGP 52F3EEA0103DAC2F