Module certs
Le module certs fournit une gestion générique de slots de certificats aux scripts Lua : génération de clé, création de CSR (Certificate Signing Request), stockage atomique des certificats émis, et inspection de leur validité.
Il est agnostique au protocole : les échanges d'enrollment (HTTP, CMP, EST, etc.) sont réalisés en Lua, tandis que le module se charge de la cryptographie et du stockage. La clé privée ne quitte jamais les fichiers du module (elle n'est jamais exposée à Lua).
Ce module fait partie des modules chargés par défaut : il est disponible sans aucune configuration.
Import
Pour importer le module en Lua :
Aucun suffixe d'instance n'est nécessaire : le module est toujours enregistré sous le nom certs.
Configuration
Le module ne nécessite aucune configuration. Chaque appel identifie son slot à partir de la table d'options passée en argument.
Un slot est un jeu de fichiers nommé, stocké dans un dossier :
| Fichier | Contenu |
|---|---|
<name>.key |
Clé privée (jamais exposée à Lua), permissions 0600 |
<name>.crt |
Certificat émis (PEM) |
<name>-ca.pem |
Chaine CA (optionnelle) |
<name>.json |
Métadonnées (marqueur de slot complet) |
- Le dossier par défaut est
/etc/alemca/certs, surchargeable via le champdirde la table d'options. - Le nom du slot doit correspondre à
^[A-Za-z0-9._-]+$.
API Lua
Toutes les fonctions prennent une table d'options en argument et retournent (résultat, err), ou err vaut nil en cas de succès.
Liste des fonctions
| Fonction | Signature Lua | Rôle |
|---|---|---|
csr |
csr_pem, err = certs.csr(opts) |
Génère (ou réutilise) une clé et retourne un CSR. |
store |
ok, err = certs.store(opts) |
Stocke un certificat émis dans le slot. |
info |
info, err = certs.info(opts) |
Retourne les infos du slot (validité, CN...). |
delete |
ok, err = certs.delete(opts) |
Supprime les fichiers du slot. |
| Champs | certs.name, certs.type |
Métadonnées du module. |
certs.csr(opts)
Génère la clé privée du slot (si absente) et retourne un CSR au format PEM. Si la clé existe déjà, elle est réutilisée.
- Options :
name(string, requis) : nom du slot.cn(string, requis) : Common Name du sujet.dir(string, optionnel) : dossier du slot (défaut:/etc/alemca/certs).type(string, optionnel) : type de clé,"ecdsa-p256"(défaut) ou"rsa-2048".san(table, optionnel) : liste de noms DNS pour les Subject Alternative Names.- Retour : le CSR au format PEM, ou
nil, "msg"en cas d'échec.
local certs = require("certs")
local csr_pem, err = certs.csr({
name = "gateway",
cn = "gateway-001.alemca.io",
type = "ecdsa-p256",
san = { "gateway-001.alemca.io", "gw001.local" },
})
if not csr_pem then
printError("Génération CSR échouée : " .. err)
return
end
-- csr_pem est ensuite envoyé à l'autorité de certification (via http, etc.)
certs.store(opts)
Persiste un certificat émis dans le slot, apres avoir vérifié qu'il correspond bien à la clé privée du slot. Les fichiers sont écrits de manière atomique ; le fichier .json est écrit en dernier (marqueur de complétude).
- Options :
name(string, requis) : nom du slot.cert(string, requis) : certificat émis au format PEM.ca(string, optionnel) : chaine CA au format PEM.meta(table, optionnel) : métadonnées libres (clé → valeur) à conserver.dir(string, optionnel) : dossier du slot.- Retour :
true, nilen cas de succès ;nil, "msg"si le certificat ne correspond pas à la clé ou en cas d'erreur.
local ok, err = certs.store({
name = "gateway",
cert = cert_pem, -- reçu de l'autorité de certification
ca = ca_pem,
meta = { issuer = "alemca-ca", profile = "gateway" },
})
if not ok then
printError("Stockage certificat échoué : " .. err)
end
certs.info(opts)
Retourne les informations d'un slot complet (clé + certificat + métadonnées présents).
- Options :
name(string, requis) : nom du slot.dir(string, optionnel) : dossier du slot.- Retour : une table, ou
nil, "raison"si le slot est incomplet.
{
key_file = "/etc/alemca/certs/gateway.key",
cert_file = "/etc/alemca/certs/gateway.crt",
ca_file = "/etc/alemca/certs/gateway-ca.pem", -- présent seulement si stocké
not_after = 1776247200, -- expiration (horodatage Unix)
cn = "gateway-001.alemca.io",
meta = { issuer = "alemca-ca", profile = "gateway" }
}
Le champ not_after permet de décider d'un renouvellement avant expiration.
certs.delete(opts)
Supprime tous les fichiers du slot (clé, certificat, CA, métadonnées). Les fichiers absents sont ignorés.
- Options :
name(string, requis) : nom du slot.dir(string, optionnel) : dossier du slot.- Retour :
true, nilen cas de succès.
Exemple complet : enrollment et renouvellement
local certs = require("certs")
local http = require("http1")
local SLOT = "gateway"
local RENEW_BEFORE = 30 * 24 * 3600 -- renouveler 30 jours avant expiration
-- 1) Vérifier si un renouvellement est nécessaire
local info = certs.info({ name = SLOT })
if info and (info.not_after - os.time()) > RENEW_BEFORE then
printInfo("Certificat encore valide, rien à faire")
return
end
-- 2) Générer un CSR (la clé est créée/réutilisée par le module)
local csr_pem, err = certs.csr({
name = SLOT,
cn = "gateway-001.alemca.io",
type = "ecdsa-p256",
})
if not csr_pem then
printError("CSR échoué : " .. err)
return
end
-- 3) Envoyer le CSR à l'autorité de certification (flow HTTP en Lua)
local resp, err = http.post("https://ca.example.com/enroll", csr_pem)
if not resp then
printError("Enrollment échoué : " .. tostring(err))
return
end
-- 4) Stocker le certificat émis
local ok, err = certs.store({
name = SLOT,
cert = resp, -- certificat PEM retourné par la CA
})
if not ok then
printError("Stockage échoué : " .. err)
end
Champs Lua associés
certs.name: Retourne le nom de l'instance du module ("certs").certs.type: Retourne le type du module ("certs").