Déploiement et usage de acme2certifier sous Kubernetes avec une PKI Microsoft

En bref
  • Le chiffrement est partout en entreprise, y compris sur les flux internes.
  • acme2certifier : un proxy ACME devant une PKI Microsoft.
  • Installation et déploiement sous Kubernetes.
  • Démonstration avec Cert-Manager ; fichiers de configuration disponibles.

Introduction

Comme aujourd’hui il est devenu plus risqué de brancher un PC sur un réseau que de rouler à contre sens sur l’autoroute, le chiffrement de l’information et le contrôle de nos échanges numériques est désormais une priorité, surtout en entreprise (avec une petite aide du marketing et du consulting…).

C’est un standard, surtout dans les accès web (mais pas que), d’imposer le chiffrement pour tous type d’accès.

Qu’il s’agisse de consulter l’intranet local de l’entreprise via le réseau interne, ou le site institutionnel exposé publiquement sur le net…en passant par les nombreuses communications inter services: frontend avec backend, backend avec BDD, webservice avec API…toutes les communications doivent être protégées.

Seul les participants légitimes à une connexion sont censés connaitre le contenu et la nature des informations échangées… (même si ce n’est pas parce qu’on voit un petit cadenas à coté de son URL qu’on n’a plus à se poser de questions).

Pour ça, on n’a pas attendu la hype (et la facture) de la cybersécurité pour proposer des solutions.

L’une des méthodes privilégiées pour authentifier et chiffrer des échanges n’a que très peu changé en dehors de l’amélioration des algorithmes utilisés: l’usage de certificats !

Je ne vais pas me lancer dans une explication du fonctionnement des certificats, mais c’est toujours l’une des solutions principales pour:

  • Garantir l’identité des personnes ou des services avec qui on échange.
  • Chiffrer les échanges de manière à ce que seul le destinataire légitime de l’information puisse déchiffrer la donnée.

Les certificats en eux même ne sont que l’implémentation de méthodes cryptographique comme le chiffrement asymétrique, avec les biens connus Alice et Bob.

Principes des certificats et du chiffrement par clef asymétrique (source : https://www.lex-persona.com/certificat-numerique/)

Cliquez sur l'image pour l'agrandir.

Viennent s’ajouter des protocoles qu’on a souvent l’habitude de mélanger: SSL/TLS/HTTPS…

Il est d’ailleurs bon de rappeler que ce bon vieux SSL même dans sa version 3.0 n’est plus considéré comme sûr, on devrait plutôt parler à chaque fois de TLS.

HTTPS utilise TLS (et anciennement SSL) pour sécuriser les communications entre un client (navigateur) et un serveur.

Aspect SSL TLS HTTPS
Type Protocole de chiffrement obsolète Protocole de chiffrement moderne Protocole applicatif sécurisé par SSL/TLS
Fonction principale Sécuriser les communications (anciennement) Sécuriser les communications (actuellement) Transfert sécurisé de données sur le web
Statut Dépassé Actif (standard actuel) Utilise TLS pour fonctionner
Exemple d'usage Ancien HTTPS HTTPS moderne, e-mails Sites web sécurisés

Même si la mode du moment est d’agiter la menace de l’ordinateur quantique, pour l’instant l’implémentation de tout ce petit monde via une logique de certificats reste la pratique la plus courante.

Seulement, avec l’explosions du nombre d’asset, les architectures micro services et les contraintes de plus en plus fortes autour de la cyber, la gestion des certificats est devenue complexe.

Les durées de vie de ces derniers doivent être réduites, il ne faut plus utiliser de certificats génériques pouvant couvrir des suffixes de domaine complet.

Avec tout ça, on n’arrive vite, même pour de petits écosystèmes, à un grand nombre de certificats. Leur gestion devient une vraie préoccupation de production.

Le protocole ACME

L’arrivée du protocole ACME (Automatic Certificate Management Environment) en 2015 a grandement amélioré les choses.

Introduit avec le projet Let's Encrypt a l’initiative de l'Internet Security Research Group (ISRG), il a permis de standardiser et d’automatiser la gestion des certificats.

Le fonctionnement d’ACME est le suivant:

  1. Le client (au sens de celui qui veut obtenir un certificat) génère une clef privée et soumet un CSR (Certificat Signing Request) à une autorité de certification (CA) supportant le protocole ACME.
  2. L’autorité de certification, envoie un défie au client pour prouver que celui-ci est bien le possesseur du service pour lequel il demande un certificat. Ce défie peut être proposé sous trois formes:
    • HTTP-01: le client doit placer un fichier spécifique sur un serveur web accessible via une URL vérifiable par l’autorité de certification portée par le nom DNS couvert par le certificat demandé. Le contenu du fichier doit correspondre à ce que l’autorité de certification a soumis au client.
    • DNS-01: le client ajoute un enregistrement DNS type TXT dans la zone correspondante au service pour lequel le certificat est demandé.
    • TLS-ALPN-01: le client configure un certificat TLS temporaire sur le serveur en lien avec une attente de l’autorisé de certification (je ne suis pas très au fait sur cette méthode).

    La nature du défi choisi est dépendante de la configuration du client et de l’implémentation qu’il a retenue d'ACME.

  3. Si le défie est réussi, la CA délivre le certificat.
Principes de ACME

Cliquez sur l'image pour l'agrandir.

Jusque-là, je viens de décrire en gros la mécanique classique de l’obtention d’un certificat, sauf qu’avec ACME tout cela est automatisé. La génération du CSR, la négociation du défi, la configuration du client pour répondre au défi et la délivrance du certificat jusqu’à sa mise en œuvre sur le service finale sont totalement automatiques.

Par la suite, le cycle de vie du certificat est géré également automatiquement, avec un renouvellement qui s’effectue de lui-même avant l’expiration du certificat.

Let's Encrypt

Lorsqu’on parle ACME, on parle souvent de Let's Encrypt. Cette CA (autorité de certification) publique et gratuite est aujourd’hui à l’origine de bon nombre de certificats.

Logo de letsencrypt.webp

Cliquez sur l'image pour l'agrandir.

Elle est extrêmement populaire, mais reste une CA externe. Son usage impose une exposition publique de son domaine et d’une logique de défi rendu accessible à l’extérieur.

Ce n’est pas un problème dans une entreprise lorsqu’il s’agit de couvrir, par exemple, son site institutionnel publié sur le web mais c’est plus problématique pour des usages purement internes. Il n’est pas toujours possible de communiquer avec une CA externe comme Let's Encrypt.

CA privée

Les entreprises disposent souvent d’une autorité de certification interne. Cette CA s’inscrit dans une PKI pour Public Key Infrastructure (alors qu’elle n’est pas publique, mais l’avantage c’est que le P fonctionne, aussi avec le mot private).

Les certificats qu’elles délivrent ne sont pas reconnus en dehors des assets de l’entreprise, mais c’est parfaitement exploitable pour les échanges privés entres applications spécifiques et devices sous gestion de la société. Ces derniers sont configurés pour reconnaitre les certificats émis par la CA privée.

La PKI en plus d'une (ou plus plusieurs) CA inclut tout un ensemble d’outils et de services pour simplifier et centraliser la gestion des certificats.

PKI Microsoft

Cette PKI peut être déployée sous gestion d’un ou plusieurs Windows Server de Microsoft. Je vois déjà les maximalistes des amoureux du pingouin crier au scandale, mais il faut reconnaitre que la CA de Microsoft est bel et bien utilisée.

Elle est rarement seule, mais fait souvent partie de la combinaison Active Directory/DNS. En effet, Microsoft propose une intégration profonde de son service de PKI dans Active Directory. Ce n’est pas la seule configuration possible, mais c’est la plus courante. Par exemple, une CA enregistrée dans l’annuaire MS rend les certificats qu’elles délivrent automatiquement reconnus sur tout devices du domaine.

Microsoft Enterprise Certificate Authority

Cliquez sur l'image pour l'agrandir.

Par contre, malgré le fait que ACME existe depuis 2015, Microsoft n’a jamais encore eu l’idée, à ma connaissance, de rendre sa Microsoft Enterprise Certificate Authority compatible avec le protocole.

acme2certifier

C’est bien dommage, surtout quand on dispose d’un cluster Kubernetes sur lequel on n’a déployé CertManager.

J’ai déjà eu l’occasion d’évoquer ce formidable outil qu’est CertManager dans cet article. J'y présente comment tirer partir de CertManager, Lets’encrypt et ACME pour automatiser tout le provisionning et le cycle de vie de ses certificats présents sur son cluster K8S.

Malheureusement tout cela ne fonctionne que pour mes applications en DMZ. En interne, je ne peux (veux) pas me baser sur Lets’encrypt et donc tirer partie du support de ACME par CertManager.

Mais, grâce au projet acme2certifier, il est possible de lever cette limitation.

acme2certifier est un formidable outil open source, qui permet d’agir comme un proxy ACME. Il donne la capacité d’utiliser ACME, même si sa CA privée ne le supporte pas. C’est acme2certifier qui se charge de dialoguer avec CertManager, lui laissant croire que c’est lui la CA.

Il va s’occuper d’échanger en backend avec la CA finale de la PKI ne supportant pas ACME, en utilisant cette fois-ci une méthode compatible pour récupérer le certificat et le délivrer à CertManager si le défi ACME est réussi.

Dans mon exemple, je vais utiliser acme2certifier pour tirer partir d’une PKI MS interne et pouvoir exploiter cette dernière pour générer des certificats à destination de mes applications hébergées sur mes worker Kubernetes déployés sur le LAN.

Microsoft Enterprise Certificate Authority n’est pas la seule CA à être prises en compte par acme2certifier, d’autres CA sont compatibles dont la liste est directement consultable sur le github du projet.

Liste des CA compatibles avec acme2certifier

Cliquez sur l'image pour l'agrandir.

Je ne vais pas forcément rentrer dans le détail pour toutes les étapes, notamment sur la partie PKI Windows, car le but n’est pas de faire un tutoriel dédié. J’essayerais néanmoins de rendre la chose compréhensible et vous devriez, même si vous n’êtes pas utilisateur de la CA MS, pouvoir tirer parti de l’article pour déployer acme2certifier et l’adapter à votre besoin.

Voici d'ailleur un résumé des composants et de la logique à déployer:

Schéma global du tutoriel

Cliquez sur l'image pour l'agrandir.

À noter que pour fonctionner avec acme2certifier, il faut que le serveur Windows hébergeant la CA ait tous ces composants d’installer au niveau du rôle Active Directory Certificat Services.

Liste des dépendances MS à installer

Cliquez sur l'image pour l'agrandir.

Installation

Prérequis

Création du compte de service

Je vais démarrer par la création d’un compte de service Active Directory (AD). C’est lui qui va être utilisé pour échanger avec la CA MS depuis acme2certifier (je le nomme arbitrairement svc_k8s_cert).

Création du compte de service

Cliquez sur l'image pour l'agrandir.

Création du template

Je crée ensuite un template de certificat TPL-SRV-WEB-K8S depuis mon serveur MS qui fait office de CA.

Template de certificat

Cliquez sur l'image pour l'agrandir.

Un template fournit un ensemble de paramètres et d’informations de compatibilités supportées/exigées par le certificat qui va être délivré.

C’est au niveau du template que l’on va décider par exemple du type de chiffrement souhaité, des contraintes d’obtention du certificat, et des fonctions qui vont être supportées.

Il est important de noter qu’un certificat peut être utilisé à différentes fins: sécuriser et authentifier un serveur web (le client s’assure qu’il discute avec le bon serveur), identifier un client (le serveur s’assure qu’il discute avec le bon client), signer du code, émettre un ticket Kerberos…bref on peut avoir au sein de sa PKI plusieurs templates correspondant à plusieurs besoins.

C’est aussi au niveau du template qu’on définit les périodes de validité des certificats.

Côté MS c’est dans le template qu’on va également déclarer qui ou quel device peut demander un certificat issu de ce dernier.

Dans mon cas, j’autorise mon compte de service svc_k8s_cert.

Autorisation sur le template

Cliquez sur l'image pour l'agrandir.

Très important, j’impose les paramètres de chiffrement. Par exemple, je souhaite une clef de chiffrement d’au moins 4096 bits et utiliser l’algorithme RSA (et une emprunte en SHA256). On n’a d’ailleurs pas encore parlé de ce dernier.

paramétrage des contraintes sur les algorithme et les protocoles supportés par le template

Cliquez sur l'image pour l'agrandir.

Formé de l’initiale de chacun de ses trois créateurs Rivest, Shamir, et Adleman RSA est un algorithme de chiffrement asymétrique, lui-même utilisé par les protocoles SSL et TLS.

Ces derniers peuvent se baser sur RSA pour établir une communication sécurisée entre un client et un serveur.

RSA est encore largement utilisé, même si dans TLS 1.3 il est remplacé par ECDHE (Elliptic-curve Diffie–Hellman).

L’objectif pour moi est d’avoir un template qui impose un minimal syndical pour une bonne sécurité tout en conservant une bonne compatibilité. Il ne faut pas oublier que si votre certificat impose des contraintes algorithmiques trop fortes, il se peut qu’il ne soit pas supporté par les serveurs qui vont le présenter, ou par les clients qui vont devoir le valider.

De même, les algorithmes de chiffrement retenus ont un impact sur les performances. Chiffrer les communications n’est pas sans impliquer une consommation plus élevée des ressources coté serveur, comme coté client même si aujourd’hui, y’a très souvent largement ce qu’il faut sous le capot pour ne pas sentir de différences. Attention tout de même, sur une large échelle ça peut jouer.

J’autorise enfin ce template à être proposé en interne par ma PKI.

Chaine de certification associée à la CA privée

Avant de m’attaquer à acme2certifier, je récupère le certificat public associé à ma CA MS, celui si se trouve dans le magasin de certificats de tous mes devices enregistré sur mon domaine AD (coolcorp.priv).

Il suffit d’aller au niveau de Trusted Root Certification (sous Windows) pour exporter en base64 dans un fichier le certificat de ma CA. Je sauvegarde cela dans un fichier CA-COOLCORP.pem.

Récupération du certificat public de la CA

Cliquez sur l'image pour l'agrandir.

Récupération du certificat public de la CA

Cliquez sur l'image pour l'agrandir.

À noter que dans mon exemple, je n’ai qu’une seule CA racine. Dans la réalité, cette configuration n’est pas conseillée.

Généralement on emploie au moins une CA intermédiaire. La logique étant d’avoir une CA root, qui n’est utilisé que pour générer des certificats pour les CA intermédiaires, puis on éteint cette CA root pour éviter tous risques de compromission.

On garde seulement actives les intermédiaires et c’est elles qui vont délivrer les certificats.

Dans ce cas nous aurions besoin de toute la chaine de certification, c’est-à-dire le certificat public racine plus le(s) certificat(s) intermédiaire(s). En travaillant en base64, on peut tout regrouper dans un seul fichier.

Cette hiérarchie permet d’éviter de maintenir en ligne la CA root.

En effet, cela reste la faille des solutions de certificats. On reste dépendant d’un composant tiers et centralisé, en l’occurrence principalement la CA de plus haut niveau. Pour une CA privée, c'est un serveur interne, pour une CA publique c'est l'organisme auprès de qui on n'a demandé un certificat (Lets’encrypt par exemple).

Si une CA est compromise, toute la chaine basée sur cette CA est compromise donc tout les certificats associés.

C’est avant tout à la CA racine qu'on fait confiance. Dans les bonnes pratiques, cette confiance est ensuite déléguée à des CA intermédiaires. On part ensuite du principe que tout certificat qui part de cette chaine est un certificat valide, et donc qu’on peut avoir confiance dans le contenu du serveur qui utilise ce certificat.

Avec des CA intermédiaires si l’une venait à se faire corrompre, on rallume la CA root et on révoque le certificat de la CA compromise. La liste des certificats révoqués (qui elle reste toujours en ligne) est mise à jour et automatiquement tous les certificats générés par cette intermédiaire corrompue se retrouvent non valides.

La CA root, elle, reste fiable et on peut redéployer un nouveau certificat pour une nouvelle CA intermédiaire avant d'à nouveau mettre hors ligne la CA racine.

La bonne pratique est même d'utiliser une architecture 3 tiers

Architecture d'une PKI en 3 tiers (source Microsoft)

Cliquez sur l'image pour l'agrandir.

Je m’égare sans doute un peu, mais il est important de comprendre qu’à ce niveau du tutoriel avec acme2certifier, il vous faudra la chaine de certification complète associée à la CA que vous interrogerez par la suite avec acme2certifier. Dans mon exemple et par simplicité, ma chaine est réduite à une CA racine.

Déploiement de acme2certifier

On peut enfin attaquer le déploiement de acme2certifier

acme_srv.cfg

Pour ça on prépare un fichier de configuration acme_srv.cfg.

Pour moi son contenu est adapté à une CA Microsoft, n’hésitez pas à consulter la doc du projet pour récupérer la configuration associée à votre PKI cible.

[DEFAULT]
debug: False

[Nonce]
# disable nonce check. THIS IS A SEVERE SECURTIY ISSUE! Please do only for testing/debugging purposes
nonce_check_disable: False

[CAhandler]
handler_file: examples/ca_handler/mscertsrv_ca_handler.py
host: PRDMUTWIN501.coolcorp.priv
user: svc_k8s_cert
password: mon_password
ca_bundle: /var/www/acme2certifier/config/CA-COOLCORP.pem
auth_method: gssapi
template: TPL-SRV-WEB-K8S
krb5_config: /var/www/acme2certifier/config/krb5.conf

[DBhandler]
#dbfile: /var/lib/acme/db.sqlite3

[Certificate]
revocation_reason_check_disable: False

[Challenge]
# when true disable challenge validation. Challenge will be set to 'valid' without further checking
# THIS IS A SEVERE SECURTIY ISSUE! Please do only for testing/debugging purposes
challenge_validation_disable: False

[Order]
tnauthlist_support: False
  

Pour MS, il faut reprendre:

  • Le nom DNS qui renvoie vers le serveur ou la CA est installé. Dans l’exemple ma CA est sur le serveur PRDMUTWIN501.coolcorp.priv. Il est important que ce nom DNS soit en cohérence avec le certificat public en base64 récupéré précédemment.
  • Le compte de service à utiliser pour se présenter à la CA. En l’occurrence pour moi svc_k8s_cert.
  • Le mot de passe de ce compte: le point faible de la conf: un password en clair. Attention donc à la manipulation de ce fichier de configuration et à son stockage (pas de git 😊).
  • ca_bundle: on vient faire pointer vers le fichier qui contient sa chaine de certification public, dans mon cas le certificat que j’ai récupéré précédemment et qui contient en base64 le certificat public de ma CA root. Le path que j’utilise est celui qui sera visible dans le contexte du conteneur de acme2certificer. Parce bien entendu on va faire tourner acme2certifier en conteneur et sous K8S.
  • template: le nom du template à utiliser auprès de la PKI. Je reprends donc celui que j’ai créé précédemment: TPL-SRV-WEB-K8S
  • auth_method: quelle méthode d’authentification utiliser pour se présenter auprès de la CA MS. Privilégier gssapi qui sous-entend l’usage de kerberos, bien plus sécurisé et surtout toujours supporté par MS la ou ntlm est déprécié depuis des années.
  • krb5_config: le fichier de configuration kerberos qui va contenir les données nécessaires au protocole pour s’authentifier via cette méthode via le compte de service.

krb5.conf

On passe justement sur le fichier krb5.conf. Son contenu est le suivant:

[libdefaults]
default_realm = COOLCORP.PRIV
[realms]
COOLCORP.PRIV = {
  kdc = PRDMUTWIN501.coolcorp.priv
  admin_server = PRDMUTWIN501.coolcorp.priv
  default_domain = COOLCORP.PRIV
}
[domain_realm]
.coolcorp.priv = COOLCORP.PRIV
coolcorp.priv = COOLCORP.PRIV    

C’est une conf classique quand on exploite kerberos sur un OS linux. On n’y retrouve les noms de domaines à utiliser et les contrôleurs de domaines à solliciter.

Dans mon cas, le domaine est coolcorp.priv et n’ayant qu’un seul DC (c’est un lab ne l’oublions pas), j’indique mon contrôleur de domaine PRDMUTWIN501.coolcorp.priv (qui est aussi ma CA pour ceux qui l’aurait remarqué…encore une fois c’est un lab…dans la vraie vie, c’est strictement interdit de déployer ce type de service sur un DC…).

Partage NFS

Je vais placer ce fichier de conf krb5.conf, ainsi que le certificat public de ma CA CA-COOLCORP.pem sur mon NAS (Network-attached storage), dans un dossier qui sera visible via un partage NFS (Network File System) accessible à mes worker Kubernetes.

Mise à disposition des fichiers sur le partage NFS

Cliquez sur l'image pour l'agrandir.

Objets Kubernetes

Le déploiement de acme2certifier va se faire sous K8S.

01-pv-acme2certifier-default.yml

Je vais donc démarrer par justement créer le PersistentVolume (PV) associé à mon partage NFS pour rendre les deux fichiers précédents accessibles à mon pod.

Voici le contenu du fichier 01-pv-acme2certifier-default.yml. Je ne vais pas renter dans le détail, je vous invite à suivre mon cookbook Kubernetes si jamais vous n’êtes pas familier avec K8S et ses objets.

---
apiVersion: v1
kind: PersistentVolume
metadata:
  annotations:
    pv.kubernetes.io/provisioned-by: nfs.csi.k8s.io
  name: pv-acme2certifier-default
  namespace: inf-cert-lan
  labels:
    environment: prd
    network: lan
    app: acme2certifier
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  csi:
    driver: nfs.csi.k8s.io
    volumeHandle: /Volume1/nfsshare/kub.coolcorp.priv/namespaces/inf-cert-lan/acme2certifier
    volumeAttributes:
      server: 192.168.10.152
      share: /Volume1/nfsshare/kub.coolcorp.priv/namespaces/inf-cert-lan/acme2certifier
    

On déploie avec la commande kubectl apply -f 01-pv-acme2certifier-default.yml.

N’hésitez pas à lire mon article sur le CSI (container Storage Interface) NFS, puis que c’est lui que j’utilise dans mon pv.

À noter que j'exploite le namespace inf-cert-lan, déjà existant et que j’avais crée dans le cas de mon installation de CertManager.

En effet, j’ai décidé arbitrairement de déployer acme2certifier, dans le même namespace que les composants de base de CertManager. Je regroupe ainsi tout ce qui touche au certificat pour mon cluster.

02-pvc-acme2certifier-default.yml

Ensuite du pv on enchaine bien évidemment sur le PersistentVolumeClaim (pvc) via le fichier 02-pvc-acme2certifier-default.yml.

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-cme2certifier-default
  namespace: inf-cert-lan
  labels:
    environment: prd
    network: lan
    application: acme2certifier
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1Gi
  volumeName: pv-acme2certifier-default
  storageClassName: ""
  share: /Volume1/nfsshare/kub.coolcorp.priv/namespaces/inf-cert-lan/acme2certifier

On déploie avec la commande kubectl apply -f 02-pvc-acme2certifier-default.yml.

Pareil, je ne reviens pas dans le détail, cet article vous précisera davantage d’informations sur le fonctionnement du storage sous Kubernetes.

Application du PV et du PVC

Cliquez sur l'image pour l'agrandir.

ConfigMap cm-acme2certifier

Avant de passer au déployement, on va d’abord créer un ConfigMap (cm). C’est un objet natif de Kubernetes en charge de l’hébergement de la configuration d’une application présente dans un pod. Plutôt que de présenter un fichier de conf, on stocke ce fichier dans un cm , puis on mappe ce cm dans le pod.

Un cm n’est pas offusqué et propose son contenu en clair directement. Attention donc aux éléments qu’on n’y fait figurer. S’ils sont trop sensibles, on préféra utiliser un objet secret, même si celui-ci n’est en réalité pas bien mieux protégé puisqu’il ne fait que coder la donnée en base64.

Le cm va permettre de stocker le fichier de config acme_srv.cfg préparé précédemment. Ceci même s’il contient un password. Je sais que ce n’est pas formidable, mais si vous limitez les accès au namespace qui contient CertManager en jouant des rôles custom Kubernetes, vous pouvez réduire le risque.

Placez-vous à l’emplacement où se trouve le fichier acme_srv.cfg et tapez la commande suivante:

kubectl create configmap cm-acme2certifier --from-file=acme_srv.cfg -n inf-cert-lan

Création du cm pour la config de acme2certifier

Cliquez sur l'image pour l'agrandir.

L’objet cm cm-acme2certifier sera créé dans le namespace inf-cert-lan à partir du contenu de acme_srv.cfg.

03-deploy-acme2certifier-default.yml

On peut enchainer avec le Deployment via le fichier 03-deploy-acme2certifier-default.yml.

Le fichier est très basique et ne donne lieu qu’a un pod, chargé de déployer une image 2certifier:0.36-nginx-wsgi (la version pourrait changer avec le temps).

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: acme2certifier
  namespace: inf-cert-lan
  labels:
    environment: prd
    network: lan
    app: acme2certifier
spec:
  replicas: 1
  selector:
    matchLabels:
      environment: prd
      network: lan
      app: acme2certifier
  template:
    metadata:
      labels:
        environment: prd
        network: lan
        app: acme2certifier
    spec:
      containers:
      - name: acme2certifier
        image: grindsa/acme2certifier:0.36-nginx-wsgi
        ports:
        - containerPort: 80
        volumeMounts:
              - name: pv-democsishare-nfs
                mountPath: "/var/www/acme2certifier/config"
              - name: acme2certifier-conf
                mountPath: "/var/www/acme2certifier/acme_srv/acme_srv.cfg" 
                subPath: acme_srv.cfg 
      volumes:
        - name: acme2certifier-conf
          configMap:
            name: cm-acme2certifier
        - name: pv-democsishare-nfs
          persistentVolumeClaim:
            claimName: pvc-cme2certifier-default   
    

On présente au pod un volume pv-democsishare-nfs pour l’accès aux fichiers hébergé sur NFS. On fait en sorte que les chemins correspondent avec ce qui a été mis dans le fichier acme_srv.cfg.

On présente le cm issu du fichier acme_srv.cfg pour que le fichier de config soit mappé sous ce nom dans le pod.

On déploie avec la commande kubectl apply -f 03-deploy-acme2certifier-default.yml.

Déploiement de acme2certifier

Cliquez sur l'image pour l'agrandir.

Arrivé à ce stade on peut vérifier si le pod acme2certifier est bien up.

kubectl get pod -n inf-cert-lan

Controle du deploiement

Cliquez sur l'image pour l'agrandir.

C’est bien le cas, on peut continuer.

04-svc-acme2certifier-default.yml

acme2certifier doit être accessible au sein du cluster, il faut donc déclarer un service (svc) via le fichier 04-svc-acme2certifier-default.yml.

---
  kind: Service
  apiVersion: v1
  metadata:
    name: svc-acme2certifier-default
    namespace: inf-cert-lan
    labels:
      environment: prd
      network: lan
      application: acme2certifier
  spec:
    ports:
      - name: http
        port: 80
        protocol: TCP
        targetPort: 80
    selector:
      environment: prd
      network: lan
      app: acme2certifier
    

Rien de spécial à dire, comme d’habitude on fait attention au label utilisé dans la section selector pour que le service se rattache bien au pod acme2certifier, sur le port 80.

On déploie avec la commande kubectl apply -f 04-svc-acme2certifier-default.yml.

Déploiement du service

Cliquez sur l'image pour l'agrandir.

05-clusterissuer-acme2certifier.yml

Vient ensuite le fichier 05-clusterissuer-acme2certifier.yml.

Celui-ci est un peu particulier, puisqu’il déploie un ClusterIssuer.

C’est un objet transverse aux namespace rattaché à CertManager.

C’est lui qui va être en charge de discuter avec acme2certifier. Je vous invite à lire mon article sur CertManager, ou j’explique ce qu’est un issuer. J’en avais déjà créé un pour l’associer à letsencrypt.

kubectl get clusterissuer

ClusterIssuer pour letsencrypt

Cliquez sur l'image pour l'agrandir.

Ici, je veux l’associer à acme2certifier, voici donc le contenu du fichier:

---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: clusterissuer-acme2certifier
spec:
  acme:
    email: [email protected]
    server: http://svc-acme2certifier-default.inf-cert-lan/directory
    privateKeySecretRef:
      # Secret resource that will be used to store the account's private key.
      name: clusterissuer-acme2certifier-account-key
    solvers:
    - http01:
        ingress:
          ingressClassName: traefik-lan
          podTemplate:
            metadata:
              labels:
                network: lan
          ingressTemplate:
            metadata:
              labels:
                network: lan     
  

je vais donc y faire figurer l’url vers le service que j’ai déployé juste avant, rendant acme2certifier accessible en interne du cluster. C’est ce que qui figure dans l’option server, dans les specs du ClusterIssuer. J’utilise le nom complet du service, incluant le namespace.

J’y définis aussi le type de défi acme que je souhaite utiliser, à savoir http-01.

Pour pouvoir traiter ce défi, CertManager, via ce ClusterIssuer va devoir provisionner un pod chargé d’héberger le fichier du défi.

L’URL visée sera toujours associée à l’URL du service couvert par l’application qui demandera un certificat.

Il faudra donc créer un Ingress sous la charge d'un IngressControler. Si cela ne vous parle pas, je vous invite à suivre cet article ou j’explique ce qu’est un Ingress et surtout comment on déploie la solution d’IngressControler Traefik. En gros c’est ce qui permet de rendre le service associé au pod joignable depuis l’extérieur du cluster.

Si vous ne spécifiez rien à ce niveau, CertManager exploite l’ IngressControler par défaut.

Dans mon cluster , j’ai deux IngressControler, l’un pour le LAN l’autre pour la DMZ. En l’occurrence, pour ce tutoriel, je dois exploiter celui sur le LAN.

Pour m’assurer que le pod et l’ingress qui seront montés dynamiquement pour répondre au défi http-01 soient corrects, je positionne les bons labels.

Il suffit ensuite de déployer le ClusterIssuer: kubectl apply -f 05-clusterissuer-acme2certifier.yml.

À ce niveau la chose intéressante à observer est le log de acme2certifier (kubectl logs nom_pod_acme2certifier -n inf-cert-lan).

Consultation des logs du pod acme2certifier

Cliquez sur l'image pour l'agrandir.

Au moment où le ClusterIssuer se déploie, on observe qu’il s’enregistre auprès de acme2certifier avec l’email renseigné dans le yaml.

On n’a donc bien un Issuer qui communique en ACME avec acme2certifier qui lui répond comme s’il s’agissait d’une CA supportant le protocole.

le cluster issuer est en ligne

Cliquez sur l'image pour l'agrandir.

Demonstration

Désormais, on n’est en mesure de valider le bon fonctionnement de l’ensemble.

Application test

Pour ça, je vais reprendre mon application démo que j’avais mis en place pour démontrer le bon fonctionnement de CertManager.

Dans l’article d’origine, j’avais exploité Lets’encrypt et une exposition en DMZ; ici je vais reprendre exactement la même base, un simple serveur nginx, mais que je vais déployer dans un namespace dev-demoacme2certifier-lan (le namespace est créé avec la commande kubectl create ns dev-demoacme2certifier-lan).

Je redétaille pas le fichier 01-deploy-demoacme2certifier-front.yaml qui permet de décrire l’objet Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: deploy-demoacme2certifier-front
  namespace: dev-demoacme2certifier-lan
  labels:
    environment: dev
    network: lan
    application: demoacme2certifier
spec:
  strategy:
    type: Recreate
  selector:
    matchLabels:
      environment: dev
      network: lan
      application: demoacme2certifier
  template:
    metadata:
      labels:
        environment: dev
        network: lan
        application: demoacme2certifier
    spec:
      containers:
      - name: demo-nginx-traefik-lan
        image: nginx:1.27
        resources:
          requests:
            memory: "8Mi"
            cpu: "250m"
          limits: 
            memory: "256Mi"
            cpu: 1  
        ports:
        - containerPort: 80
      

Ni le fichier 02-svc-demoacme2certifier-front.yaml pour l’objet service.

---
  kind: Service
  apiVersion: v1
  metadata:
    name: svc-demoacme2certifier-front
    namespace: dev-demoacme2certifier-lan
    labels:
      environment: dev
      network: lan
      application: demoacme2certifier
  spec:
    ports:
      - name: http
        port: 80
        protocol: TCP
        targetPort: 80
    selector:
      environment: dev
      network: lan
      application: demoacme2certifier
    

Je vais m’arrêter un peu sur le fichier 03-ing-demoacme2certifier-front.yaml.

---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ing-demoacme2certifier-default
  namespace: dev-demoacme2certifier-lan
  labels:
      environment: dev
      network: lan
      application: demoacme2certifier
  annotations:
    traefik.ingress.kubernetes.io/router.entrypoints: web,websecure
    ingressClassName: traefik-lan
    traefik.ingress.kubernetes.io/router.tls: "true"
spec:
  ingressClassName: traefik-lan
  tls:
  - hosts:
    - demoacme2certifier.coolcorp.priv
    secretName: sec-certificate-demoacme2certifier
  rules:
  - host: demoacme2certifier.coolcorp.priv
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: svc-demoacme2certifier-front
            port:
              number: 80
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: cert-demoacme2certifier-front
  namespace: dev-demoacme2certifier-lan
  labels:
      environment: dev
      network: lan
      application: demoacme2certifier 
spec:
  dnsNames:
    - demoacme2certifier.coolcorp.priv
  commonName: demoacme2certifier.coolcorp.priv
  secretName: sec-certificate-demoacme2certifier
  privateKey:
    algorithm: RSA
    size: 4096
  issuerRef:
    name: clusterissuer-acme2certifier 
    kind: ClusterIssuer
  

Comme dans l’article sur traefik ou sur l’article final de mon cookbook, je créer un objet Ingress.

Celui-ci est paramétré pour tirer partir de l’IngressControler traefik-lan. N’hésitez pas à faire tour par le tutoriel traitant de traefik pour bien comprendre ce point.

L’ingress doit répondre à l’URL externe en demoacme2certifier.coolcorp.priv.

Il doit renvoyer vers le service svc-demoacme2certifier-front et proposer un certificat contenu dans le secret sec-certificate-demoacme2certifier.

Ce certificat est déclaré un peu plus bas via l’objet cert-demoacme2certifier-front.

Il reprend dans ses caractéristiques, le dnsname et le commonname correspondant à l’URL du site de démo : demoacme2certifier.coolcorp.priv.

On y retrouve également les caractéristiques de la clef associée qui va être générée, soit 4096 bits, et l’usage de l’algo RSA. Ceci doit être en cohérence avec le template de certificat de la PKI expliqué dans les prérequis.

Si on ne précise rien à ce niveau, le certificat va être demandé avec les options par défaut. Par exemple une clef de 2048 bits…ce qui ne correspondra pas à la taille minimale imposée par le templaté crée précédemment au niveau de la CA Windows.

Le plus intéressant reste la section issuerRef, puisque c’est ici qu’on indique quel Issuer doit être sollicité pour l’obtention du certificat. On va donc renseigner la ClusterIssuer créée juste avant et qui c’est enregistré sur acme2certifier.

Ne pas oublier de créer l’enregistrement DNS pour que l’URL https://demoacme2certifier.coolcorp.priv soit bien renvoyée vers la VIP HAproxy qui expose les instances traefik (plus de détails ici).

Création de l'enregistrement DNS

Cliquez sur l'image pour l'agrandir.

Il ne reste plus qu’a lancer la création de tous les objets K8S via la commande kubectl apply -f .\dev-demoacme2certifier-lan\ (tous les yaml vont être pris en une fois, dev-demoacme2certifier-lan étant le dossier dans lequel j'ai mis tous les yaml ) et laisser la magie opérer.

Déploiement de l'application test

Cliquez sur l'image pour l'agrandir.

Controle et explications

Si tout va bien, au bout de quelques minutes, on devrait pouvoir voire apparaitre un « true » en face de la sortie de la commande:

kubectl get certificate -n dev-demoacme2certifier-lan

Controle du certificat

Cliquez sur l'image pour l'agrandir.

Si c’est le cas, cela implique que le certificat est prêt et disponible. On peut le vérifier en allant consulter l’url https://demoacme2certifier.coolcorp.priv..

Controle du certificat

Cliquez sur l'image pour l'agrandir.

On n’a bien un certificat ! et quand on regarde la CA qui l’a distribué, on tombe sur ma CA MS.

D’ailleurs si on regarde justement du côté de la console de management de la CA en question, dans la section des certificats issues, on retrouve la trace du certificat, avec le nom du compte de service qui l’a demandé et le template rattaché: tout est cohérent 😊.

Controle du certificat au niveau de la CA

Cliquez sur l'image pour l'agrandir.

alors qu’est ce qui s’est passé exactement, ben ça on peut le voir avec la commande:

kubectl get event -n dev-demoacme2certifier-lan

En partant du bas de la sortie de la commande, on peut retracer tout ce qui s’est passé.

Liste des evenements dans le namespace

Cliquez sur l'image pour l'agrandir.

  1. Le pod nginx représentant l’application démo a été déployé et le conteneur a démarré.
  2. Puis la création d’une requête de certificat a automatiquement été générée par CertManager.
  3. Cette requête a été soumise au ClusterIssuer.
  4. Le Challenge http-01 a été lancé, ce qui a généré la création de pod basé sur l’image quay.io/jetstack/CertManager-acmesolver:v1.16.1.. Ce pod n’est la que pour créer un serveur web chargé d’héberger le fichier du défi en répondant à une requête sur l’url https://demoacme2certifier.coolcorp.priv suivi d’une sous arborescence /.well-know/acme-challenge/…. C’est cette sous-arborescence qui permet à Traefik de rediriger la requête vers le pod monté dynamiquement par CertManager. Si on n’est assez rapide on peut voir l’ingress associé dans la console Traefik.
  5. Apparition de l'ingress

    Cliquez sur l'image pour l'agrandir.

  6. acme2certifier va interroger cette URL pour contrôler le défi et si il est réussi, va de l’autre coté demander le certificat à la CA Windows pour le renvoyer à CertManager afin qu’il soit stocké dans le secret adéquat et exploitable par l’ingress final.
  7. Le certificat ayant été obtenu, le pod certmanager dynamique est retiré (ainsi que l’ingress).

Conclusion

acme2certifier est l’exemple typique des petites pépites proposées bénévolement par des développeurs, qui se reposent eux-mêmes sur d’autres projets open source d’autres développeurs, pour offrir un service venant compenser un manque fonctionnel d’un outil dont on dispose dans notre architecture.

L’usage de certificat, même s’il n’est pas nouveau, reste toujours un challenge dans les entreprises. Chaque année, les contraintes se font plus fortes, obligeant à revoir régulièrement ses politiques de délivrance des certificats.

La pression d’éditeur de navigateur comme Google, qui parfois annonce la fin du support de tel ou tel protocole ou algorithme dans son browser phare Chrome, impose d’être en capacité de redélivrer tout un ensemble de certificats basés sur de nouvelles normes…au risque de bloquer les updates de Chrome.

Ce qui ne tardera pas à faire grincer les dents de votre RSSI, les patchs de sécurités sur les browsers tombant plus vite que la pluie en Normandie (très belle région au demeurant).

Avoir une gestion automatisée de ses certificats est devenue une nécessité. Basé ses stratégies sur ACME est clairement une bonne solution…Encore faut-il pouvoir l’utiliser avec son legacy…et c’est là que l’écosystème Kubernetes avec des outils comme CertManager apporte une vraie plus-value, surtout quand des développeurs de talents proposent des applications comme acme2certifier ! Là il n’y a plus d’excuse pour ne plus générer ses certificats à la main 😊

Pour ceux qui le souhaitent, une version des fichiers de configurations et des yamls utilisés dans cet article sont disponible dans mon repo github