Résolution mail
Adressez les bonnes personnes au bon moment, sans copier vos mailing-lists.
PKIFactor associe chaque certificat à une équipe propriétaire (champ team_email). Lorsqu'un événement de cycle de vie survient (émission, expiration imminente, renouvellement, révocation), la plateforme résout dynamiquement l'adresse mail d'équipe en liste de destinataires via votre annuaire d'entreprise. Deux backends sont supportés : Microsoft Graph (Microsoft 365, Azure AD / Entra ID) et LDAP / Active Directory (on-premise).
Cela évite d'avoir à maintenir des mailing-lists statiques dans PKIFactor : la source de vérité reste votre annuaire.
- Cert eventcertificate.expires_soon
- PKIFactorlookup team_email
- ResolverGraph / LDAP query
- AD / AADgroup members
- SMTPrender + send mail
5.2. Configuration Microsoft Graph (Microsoft 365 / Entra ID)
Pré-requis Azure
- Inscrire une application dans Azure AD → App registrations
- Accorder les permissions Application suivantes (consentement admin) :
| Permission | Type | Justification |
|---|---|---|
Group.Read.All | Application | Lister les groupes |
GroupMember.Read.All | Application | Lire les membres d'un groupe |
User.Read.All | Application | Résoudre les emails des membres |
Mail.Send | Application | (optionnel) envoi via Graph plutôt que SMTP |
- Générer un client secret ou un certificat client (recommandé en production).
Configuration dans PKIFactor
# /etc/pkifactor/config.yaml
team_resolver:
backend: graph
graph:
tenant_id: "00000000-0000-0000-0000-000000000000"
client_id: "11111111-1111-1111-1111-111111111111"
# Choisir l'une des méthodes :
client_secret: "${ENTRA_CLIENT_SECRET}" # secret manager
# ou bien :
# client_certificate_path: /etc/pkifactor/secrets/entra-app.pem
# client_certificate_thumbprint: "AB:CD:EF:..."
cache_ttl_seconds: 600 # cache des résolutions de groupe (10 min)
timeout_seconds: 10
# Stratégie de résolution
resolution:
mode: distribution_list # ou: mail_enabled_security_group, m365_group, dynamic
fallback_to_owner: true # si la liste est vide, notifier le propriétaire du groupe
Test de configuration
sudo pkifactor-ctl team-resolver test \
--backend graph \
--address "pki-team@acme.onmicrosoft.com"
# Sortie attendue :
# ✓ Auth OK (token expires in 3599s)
# ✓ Group found: 'PKI Team' (id=aaaa-bbbb-cccc)
# ✓ Members resolved: 7 user(s)
# - alice@acme.com
# - bob@acme.com
# - charlie@acme.com
# ...
# Total resolution time: 412 ms
Comportement
- Si
team_emailcorrespond à une liste de distribution ou un groupe M365, PKIFactor récupère la liste transitive des membres (viatransitiveMembers). - Si l'adresse pointe vers un groupe dynamique, la résolution honore les filtres dynamiques.
- Si le groupe est mail-enabled security, l'adresse principale (
mail) du groupe est utilisée directement comme destinataire (pas de fan-out).
5.3. Configuration LDAP / Active Directory (on-premise)
Configuration
# /etc/pkifactor/config.yaml
team_resolver:
backend: ldap
ldap:
uri: "ldaps://dc01.acme.local:636"
bind_dn: "CN=svc-pkifactor,OU=Service Accounts,DC=acme,DC=local"
bind_password: "${LDAP_BIND_PASSWORD}"
# Vérification TLS
tls:
verify: true
ca_bundle: /etc/pkifactor/secrets/ad-root-ca.pem
# Recherche du groupe par adresse mail
group_search:
base_dn: "OU=Groups,DC=acme,DC=local"
filter: "(&(objectClass=group)(mail={team_email}))"
attributes:
members_attr: "member" # AD : liste de DN
members_format: "dn" # ou "rfc2307" pour OpenLDAP
# Résolution des membres → email
member_search:
base_dn: "DC=acme,DC=local"
filter: "(&(objectClass=user)(distinguishedName={dn}))"
mail_attr: "mail"
# Filtre d'exclusion (utilisateurs désactivés)
exclude_filter: "(userAccountControl:1.2.840.113556.1.4.803:=2)"
# Récursion pour les groupes imbriqués
nested_groups: true
max_depth: 5
cache_ttl_seconds: 600
timeout_seconds: 10
Test de configuration
sudo pkifactor-ctl team-resolver test \
--backend ldap \
--address "pki-team@acme.local"
# Sortie attendue :
# ✓ Bind OK as CN=svc-pkifactor,OU=Service Accounts,DC=acme,DC=local
# ✓ Group found: CN=PKI Team,OU=Groups,DC=acme,DC=local
# ✓ Direct members: 5
# ✓ Nested members (depth 2): 3
# ✓ After exclude filter (active only): 7
# - alice@acme.com
# - bob@acme.com
# ...
Test depuis ldapsearch (pour debug)
ldapsearch -H ldaps://dc01.acme.local:636 \
-D "CN=svc-pkifactor,OU=Service Accounts,DC=acme,DC=local" \
-W \
-b "OU=Groups,DC=acme,DC=local" \
"(&(objectClass=group)(mail=pki-team@acme.local))" \
member
5.4. Affectation d'une équipe à un certificat
Via API
curl -X POST https://pki.exemple.com/api/v1/certificates \
-H "Authorization: Bearer $TOKEN" \
-d '{
"template_code": "web-tls",
"subject": { "common_name": "www.acme.com" },
"team_email": "pki-team@acme.com"
}'
Via UI
Issue a new certificate
5.5. Événements déclenchant une notification
| Événement | Destinataires | Délai |
|---|---|---|
certificate.issued | Équipe + demandeur | Immédiat |
certificate.approval_pending | Approbateurs de l'org | Immédiat |
certificate.expires_soon | Équipe | T-30j, T-7j, T-1j (configurable) |
certificate.expired | Équipe | À l'expiration |
certificate.revoked | Équipe + demandeur | Immédiat |
certificate.renewed | Équipe | Immédiat |
5.6. Modèle de données
-- Champs additionnels sur la table certificates
ALTER TABLE certificates ADD COLUMN team_email VARCHAR(255);
ALTER TABLE certificates ADD COLUMN team_id VARCHAR(128); -- AD objectGUID ou Graph group id
ALTER TABLE certificates ADD COLUMN team_resolver_backend VARCHAR(16); -- 'graph' | 'ldap'
CREATE INDEX idx_certs_team_email ON certificates(team_email);
-- Cache de résolution (TTL court)
CREATE TABLE team_resolution_cache (
team_email VARCHAR(255) PRIMARY KEY,
backend VARCHAR(16) NOT NULL,
group_id VARCHAR(128),
member_emails JSONB NOT NULL,
resolved_at TIMESTAMPTZ NOT NULL,
expires_at TIMESTAMPTZ NOT NULL
);
5.7. Gestion des erreurs
PKIFactor applique une stratégie de résilience à trois niveaux :
- Cache valide (TTL non expiré) → utilisé directement
- Cache expiré + backend disponible → rafraîchissement
- Backend indisponible (timeout, 5xx) → fallback sur le cache expiré (jusqu'à 24h) + alerte SIEM
2026-04-18T10:34:12Z WARN team_resolver=graph status=fallback_to_stale_cache
team_email=pki-team@acme.com cache_age=3h backend_error="timeout after 10s"
recipients=7 source=cache_24h_grace
Le cache de résolution a un TTL court (10 min par défaut) pour rester cohérent avec votre annuaire. En cas d'indisponibilité Graph/LDAP, PKIFactor bascule sur le cache 24h pour garantir que les notifications critiques (expiration, révocation) partent toujours.