2ème épisode des billets sur rancher (le premier billet est là)
J’ai discuté auparavant de l’installation d’un serveur rancher et d’un agent sur une seule machine, pour faire des tests ou héberger sa propre infrastructure docker sur une petite machine.
Dans ce billet, je vais discuter de la sécurisation du serveur rancher
Après l’installation, le conteneur du serveur rancher écoute sur le port 8080, sans SSL ni aucune protection pour le mot de passe d’administration (que je vous conseille assez fort - généralement, pour ce genre de trucs, j’utilise openssl rand -hex 32
et je stocke le résultat dans mon gestionnaire de mots de passe).
Puisqu’on parle d’une machine hostée - pour ma part, il s’agit d’une Dedibox, il est important de la sécuriser un minimum - ça serait dommage que votre machine devienne membre d’un spambot juste parce que vous n’avez pas pris ces précautions…
On pourrait ensuite très facilement déployer un conteneur haproxy dans rancher qui expose notre serveur d’admin rancher - mais évidemment, on a un problème de poule et d’oeuf… si votre rancher est en panne, et que le conteneur haproxy n’est pas correctement déployé… vous ne pouvez plus gérer votre cluster. C’est dommage…
On va donc déployer un conteneur nginx - pas géré par rancher, mais fonctionnant dans le serveur docker où fonctionne notre rancher - et qui écoute sur un port non standard (j’ai choisi le port 4430
).
le problème des certificats
si on veut protéger les communications avec l’extérieur, il nous faut un certificat valable pour notre serveur. J’utilise letsencrypt (package letsencrypt
) pour ça, c’est plutôt facile à mettre en place.
Attention cependant, pour ma part, j’utilise souvent l’option standalone
du client letsencrypt, et les méthodes de validation http-01
, tls-sni-01
, utilisées par défaut, ont besoin d’un serveur HTTP qui écoute sur les ports 80
et 443
(pour valider que le serveur pour lequel vous cherchez à émettre un certificat est bien le vôtre). Avec ces deux méthodes de validation, avant d’émettre des certificats, le serveur letsencrypt doit se connecter au serveur pour récupérer un secret partagé, la stack que vous allez déployer exposera probablement ces ports standards, et il faudra donc arrêter toute la stack pour obtenir et/ou renouveler le certificat de votre serveur rancher.
Il faut donc mettre en place et tester une autre méthode de validation - dns-01
(le challenge est inséré dans un record TXT
de la zone) pour letsencrypt si on ne veut pas avoir à arrêter tous les services pour renouveler le certificat du serveur rancher.
on pourrait parfaitement imaginer que la stack contienne un service pour gérer les certificats avec letsencrypt - ça se fait très facilement, j’en parlerai dans un prochain billet, mais pour ce certificat en particulier, faire ça reviendrait à créer un nouveau problème de poule et d’oeuf. Si le certificat de votre serveur expire, la stack entière risque de ne plus fonctionner, et avec elle le système de renouvellement automatique…
certificats auto-signés ?
D’expérience, il est difficile (voire impossible) de mettre en place un certificat auto-signé avec rancher - beaucoup de composant dans rancher (serveur, agent, serveur de meta-données, gestionnaire de réseau, healthcheck, les outils en ligne de commande, etc…) communiquent avec le serveur et chacun a besoin d’un moyen de valider le certificat (il faudrait donc le passer à tous ces composants, et il n’y a pas - à ma connaissance - d’option dans tous ces composants pour arrêter la validation des certificats).
L’effort nécessaire pour le faire est selon moi bien plus important qu’avec un certificat valide obtenu avec letsencrypt. Et comme tout bon admnistrateur système, je suis feignant à l’extrême, alors… ;-)
terminaison SSL avec nginx
Pour faire la terminaison SSL, on va lancer un conteneur nginx qui va s’occuper de forwarder tout au serveur rancher. Un truc auquel il faut faire attention, c’est que l’interface utilisateur de rancher utilise les web-sockets, donc votre nginx doit supporter les entêtes Upgrade: ...
et Connection: Upgrade
.
J’ai au préalable récupéré un certificat valable et sa clé que le client letsencrypt a placé dans /etc/letsencrypt/live/rancher.arzur.net/
.
La configuration suivante fonctionne bien - il y a bien sûr plein de variations possibles - j’ai simplement copié et adapté celle qui se trouve dans la documentation de rancher :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
|
On note:
- 1 upstream
rancher
qui se connecte à un serveur nommérancher
sur le port8080
. On va donc devoir faire en sorte que docker lie le conteneur du serveur rancher avec notre conteneur nginx en le nommant ‘rancher’ - le serveur écoute sur le port
8080
(pour faire la redirection HTTP -> HTTPS) et4430
. Dans le billet précédent, on avait exposé le port8080
du serveur rancher. Si on démarre le conteneur nginx en même temps, il va y avoir un conflit, il faut donc redémarrer le conteneurrancher-server
en n’exposant aucun port. La communication directe avec celui-ci ne sera alors possible que par l’intermédiaire du démon docker… - nginx va aller chercher les certificats dans
/etc/letsencrypt/live/...
, on va donc devoir monter un volume docker pour que nginx puisse lire les données depuis le répertoire où se trouvent le certificat et la clé privée. On ne peut pas utiliser de liens symboliques !!! il faut copier physiquement les fichiers ou monter le chemin dans le conteneur car celui-ci n’a pas accès au reste du système de fichiers et ne pourra donc pas lire la cible des liens éventuels…
On peut maintenant tirer l’image nginx depuis le docker hub. J’utilise généralement les conteneur basés sur alpine
car ils sont généralement tout petits (autour de 15MB) et ne contiennent que ce dont on a besoin pour faire fonctionner le service.
1 2 3 4 5 6 7 8 |
|
On arrête le serveur, on le redémarre après avoir fait un backup de l’ancien conteneur (toujours garder le truc qui fonctionne quand on fait des expériences comme ça…)
1 2 3 4 |
|
On peut vérifier qu’on peut se connecter sur le port 4430 du serveur et qu’on y a accès. C’est magique ! :-D On note l’option --link rancher-server:rancher
qui établit un lien entre rancher-nginx
et rancher-server
. Cela met en place ce qu’il faut pour que toute connexion dand le conteneur nginx a un serveur nommé rancher
sera envoyée au conteneur rancher-server
. On aussi qu’on peut utiliser l’option ro
(read-only) quand on monte des volumes, ainsi le conteneur ne pourra pas modifier la base des certificats gérés par letsencrypt.
Si vous avez suivi l’article précédent, comme on a mis en place une redirection du port 8080 en HTTP au port 4430 en HTTPS, tout devrait fonctionner normalement. On peut tout à fait démarrer un nouvel agent avec la nouvelle adresse (https://rancher.arzur.net:4430/…) en n’oubliant pas le token de validation que l’interface a fourni quand on l’a démarré, après avoir arrêté et supprimé l’ancien (docker stop rancher-agent
)1.
Une fois tout cela mis en place, on a un serveur rancher qui me semble suffisamment sécurisé. Avec le firewall, je restreint en plus l’accès aux quelques adresses IP d’où je pourrais me connecter…
On va pouvoir (enfin !) passer au déploiement de notre première stack de service… en commençant par mettre en place les outils en ligne de commande pour gérer le cluster…
-
quand on redémarre l’agent ou le serveur, les conteneurs déjà déployés par rancher ne sont pas perturbés. Ils continuent à fonctionner normalement - seuls les conteneurs qui ont besoin du service de méta-données de rancher seront perturbés - mais j’ai testé à de nombreuses reprises que ça ne perturbe pas le fonctionnement d’un jeu de services déjà déployé.↩