Variables d'environnement Lambda : injection de configuration et chiffrement KMS
Passer un endpoint de base de données à une fonction Lambda sans le coder en dur est l'un des premiers problèmes concrets qu'on rencontre en production. Les variables d'environnement Lambda résolvent ce besoin, mais il existe deux approches de chiffrement distinctes — et les mélanger produit un comportement silencieusement incorrect que les logs n'exposent pas clairement.
TL;DR — Variables d'environnement Lambda et chiffrement KMS
| Aspect | Détail |
|---|---|
| Stockage | Chiffrées au repos par Lambda avec une clé AWS gérée par défaut |
Approche 1 — CMK via --kms-key-arn |
Lambda déchiffre automatiquement au démarrage ; valeurs accessibles en clair via os.environ |
| Approche 2 — Chiffrement manuel KMS | Valeur chiffrée stockée dans la variable ; le code appelle kms:Decrypt explicitement |
| Transit | Chiffrement en transit activable via la console (helper de chiffrement) |
| Taille max par variable | Vérifiez les quotas actuels dans la documentation AWS |
Comment fonctionnent les variables d'environnement Lambda
Lambda injecte les variables d'environnement dans le processus d'exécution au moment de l'initialisation du conteneur. Elles sont disponibles dès le début du handler via les mécanismes standards du langage (os.environ en Python, process.env en Node.js). Ce n'est pas un appel réseau à chaque invocation — la valeur est résolue une fois lors du cold start.
Par défaut, Lambda chiffre les variables au repos en utilisant une clé AWS gérée (aws/lambda). Ce chiffrement est transparent : vous ne le configurez pas, vous ne le voyez pas. Le problème surgit quand on veut utiliser sa propre clé CMK (Customer Managed Key) pour répondre à des exigences de conformité ou pour contrôler la rotation.
- Approche 1 (CMK automatique) : Lambda reçoit la clé KMS au déploiement, déchiffre les variables au cold start, et les expose en clair dans
os.environ. Le code ne voit jamais de valeur chiffrée. - Approche 2 (chiffrement manuel) : La valeur chiffrée est stockée telle quelle dans la variable d'environnement. Le code récupère le ciphertext et appelle explicitement
kms:Decryptpour obtenir la valeur en clair. - Transit : Le helper de chiffrement de la console Lambda permet de chiffrer les valeurs avant qu'elles transitent vers l'API, protégeant ainsi la valeur même pendant le déploiement.
Approche 1 : CMK via --kms-key-arn — déchiffrement automatique
C'est l'approche recommandée pour la majorité des cas. Vous associez une CMK à la fonction ; Lambda se charge du déchiffrement au démarrage. Le code reste identique à un accès sans chiffrement personnalisé.
Étape 1 — Créer une clé KMS CMK
Avant de configurer Lambda, il faut une CMK dédiée. Notez l'ARN retourné — vous en aurez besoin à chaque étape suivante.
aws kms create-key \
--description "Cle CMK pour variables env Lambda" \
--key-usage ENCRYPT_DECRYPT \
--region us-east-1
aws kms create-alias \
--alias-name alias/lambda-env-key \
--target-key-id <key-id-retourne-ci-dessus> \
--region us-east-1
Étape 2 — Déployer la fonction avec les variables d'environnement et la CMK
Le paramètre --kms-key-arn indique à Lambda quelle CMK utiliser pour chiffrer et déchiffrer les variables. Avec cette approche, os.environ['DB_ENDPOINT'] retourne directement la valeur en clair — Lambda a déjà déchiffré au cold start.
aws lambda create-function \
--function-name my-db-function \
--runtime python3.12 \
--role arn:aws:iam::123456789012:role/lambda-execution-role \
--handler app.handler \
--zip-file fileb://function.zip \
--environment 'Variables={DB_ENDPOINT=mydb.cluster-xyz.us-east-1.rds.amazonaws.com,DB_PORT=5432}' \
--kms-key-arn arn:aws:kms:us-east-1:123456789012:key/abcd1234-ab12-ab12-ab12-abcd12345678 \
--region us-east-1
Étape 3 — Code Python pour l'approche 1
Avec --kms-key-arn, le code ne fait aucun appel KMS explicite. Lambda a déjà déchiffré les variables avant que le handler s'exécute.
import os
def handler(event, context):
# Lambda a deja dechiffre via la CMK au cold start
db_endpoint = os.environ['DB_ENDPOINT']
db_port = os.environ['DB_PORT']
# Utilisation directe — aucun appel KMS dans le code
print(f"Connexion a {db_endpoint}:{db_port}")
return {"statusCode": 200}
Étape 4 — IAM : autoriser Lambda à utiliser la CMK
Le rôle d'exécution Lambda doit avoir la permission kms:Decrypt sur la CMK. Sans cela, le cold start échoue avec une erreur d'accès KMS — et la fonction ne démarre pas du tout.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowLambdaDecryptEnvVars",
"Effect": "Allow",
"Action": [
"kms:Decrypt"
],
"Resource": "arn:aws:kms:us-east-1:123456789012:key/abcd1234-ab12-ab12-ab12-abcd12345678"
}
]
}
Attachez cette politique au rôle d'exécution :
aws iam put-role-policy \
--role-name lambda-execution-role \
--policy-name LambdaKMSDecryptPolicy \
--policy-document file://kms-decrypt-policy.json
Approche 2 : chiffrement manuel KMS — contrôle explicite dans le code
Cette approche est utile quand vous voulez que la valeur reste chiffrée même dans la mémoire de la variable d'environnement, ou quand vous souhaitez un contrôle fin sur le moment du déchiffrement. Ici, vous chiffrez la valeur avant de la stocker dans la variable, et le code appelle kms:Decrypt explicitement à l'exécution.
C'est l'équivalent de mettre un coffre-fort dans une pièce déjà verrouillée. L'approche 1 déverrouille le coffre à l'entrée dans la pièce. L'approche 2 vous laisse le coffre verrouillé — vous choisissez quand l'ouvrir, et avec quelle clé dans votre poche.
Étape 1 — Chiffrer la valeur avant le déploiement
Chiffrez l'endpoint de base de données avec la CMK. Le résultat est un ciphertext en base64 que vous stockerez comme valeur de la variable d'environnement.
aws kms encrypt \
--key-id alias/lambda-env-key \
--plaintext "mydb.cluster-xyz.us-east-1.rds.amazonaws.com" \
--query CiphertextBlob \
--output text \
--region us-east-1
Copiez la valeur base64 retournée. C'est cette valeur chiffrée que vous passez à Lambda — pas l'endpoint en clair.
Étape 2 — Déployer sans --kms-key-arn
Dans cette approche, vous ne passez pas --kms-key-arn. La variable contient le ciphertext brut. Lambda ne déchiffre rien automatiquement.
aws lambda create-function \
--function-name my-db-function-manual \
--runtime python3.12 \
--role arn:aws:iam::123456789012:role/lambda-execution-role \
--handler app.handler \
--zip-file fileb://function.zip \
--environment 'Variables={DB_ENDPOINT_ENCRYPTED=AQICAHj...base64ciphertext...==,DB_PORT=5432}' \
--region us-east-1
Étape 3 — Code Python pour l'approche 2
Le code récupère le ciphertext depuis os.environ et appelle explicitement le SDK KMS pour déchiffrer. Pour éviter un appel KMS à chaque invocation, initialisez le client et mettez en cache la valeur déchiffrée au niveau du module (hors handler).
🔽 Cliquez pour afficher le code Python — déchiffrement manuel KMS
import os
import base64
import boto3
# Initialisation hors handler : execute une seule fois par conteneur (cold start)
kms_client = boto3.client('kms', region_name='us-east-1')
def _decrypt_env_var(encrypted_value: str) -> str:
"""Dechiffre une valeur chiffree KMS stockee en base64."""
ciphertext = base64.b64decode(encrypted_value)
response = kms_client.decrypt(
CiphertextBlob=ciphertext
)
return response['Plaintext'].decode('utf-8')
# Cache au niveau module — dechiffrement unique par cold start
_DB_ENDPOINT = None
def _get_db_endpoint() -> str:
global _DB_ENDPOINT
if _DB_ENDPOINT is None:
encrypted = os.environ['DB_ENDPOINT_ENCRYPTED']
_DB_ENDPOINT = _decrypt_env_var(encrypted)
return _DB_ENDPOINT
def handler(event, context):
db_endpoint = _get_db_endpoint()
db_port = os.environ['DB_PORT']
print(f"Connexion a {db_endpoint}:{db_port}")
return {"statusCode": 200}
Étape 4 — IAM pour l'approche 2
Le rôle d'exécution a besoin de kms:Decrypt sur la même CMK. La politique est identique à celle de l'approche 1 — seul le moment du déchiffrement diffère.
Mettre à jour les variables d'environnement sur une fonction existante
Pour modifier les variables sans redéployer le code, utilisez update-function-configuration. Attention : ce paramètre remplace l'intégralité du bloc Variables — toute variable omise est supprimée.
aws lambda update-function-configuration \
--function-name my-db-function \
--environment 'Variables={DB_ENDPOINT=newdb.cluster-abc.us-east-1.rds.amazonaws.com,DB_PORT=5432}' \
--region us-east-1
Pour vérifier les variables actuellement configurées (les valeurs sont masquées si une CMK est associée) :
aws lambda get-function-configuration \
--function-name my-db-function \
--query 'Environment' \
--region us-east-1
Diagnostic : symptôme, mauvais diagnostic, cause réelle
En production, une erreur classique ressemble à ceci : la fonction démarre, mais retourne une KeyError sur DB_ENDPOINT, ou pire, se connecte à l'ancien endpoint après une mise à jour de configuration.
Le mauvais réflexe est de vérifier le code. On passe du temps à chercher une faute de frappe dans le nom de la variable. En réalité, le problème vient presque toujours de l'une de ces deux causes :
- Conteneur chaud (warm container) : Lambda réutilise les conteneurs existants. Si vous avez mis en cache la valeur au niveau module (comme dans l'approche 2), les invocations sur des conteneurs chauds utilisent encore l'ancienne valeur — jusqu'au prochain cold start. La mise à jour de configuration ne force pas le recyclage immédiat des conteneurs.
- Remplacement partiel du bloc Variables :
update-function-configurationremplace tout le bloc. Si vous n'avez passé queDB_ENDPOINTsansDB_PORT,DB_PORTa été supprimé silencieusement.
Le fix pour le premier cas : après une mise à jour critique, forcez un nouveau déploiement ou attendez que les conteneurs chauds expirent. Pour le second : récupérez toujours la configuration existante avant de mettre à jour, et reconstruisez le bloc complet.
- Approche 1 — cold start : Lambda appelle
kms:Decryptautomatiquement. Si la permission manque, la fonction échoue au démarrage avec une erreur KMS, pas uneKeyError. - Approche 2 — première invocation : Le code appelle
kms:Decrypt. Les invocations suivantes sur le même conteneur utilisent la valeur mise en cache. - Conteneur chaud : Aucun appel KMS. La valeur en cache est réutilisée directement.
Comparaison des deux approches — quand choisir laquelle
| Critère | Approche 1 — CMK automatique | Approche 2 — Chiffrement manuel |
|---|---|---|
| Complexité du code | Aucune — os.environ direct |
Appel SDK KMS explicite requis |
| Moment du déchiffrement | Cold start (automatique) | Contrôlé par le code |
Valeur dans os.environ |
En clair | Ciphertext base64 |
| Appels KMS facturés | Par cold start | Par cold start (si mis en cache) |
| Cas d'usage typique | Configuration standard, endpoints, feature flags | Secrets haute sensibilité, contrôle fin du cycle de vie |
| Recommandation | ✅ Cas général | Cas spécifiques de conformité |
Variables d'environnement Lambda vs AWS Secrets Manager
Pour des secrets rotatifs (mots de passe de base de données, tokens OAuth), AWS Secrets Manager est plus adapté que les variables d'environnement. Les variables d'environnement ne supportent pas la rotation automatique — vous devez redéployer ou appeler update-function-configuration manuellement. Secrets Manager gère la rotation et expose une API de récupération que vous appelez dans le code.
Les variables d'environnement restent le bon outil pour la configuration non-secrète ou peu sensible : endpoints, noms de tables, feature flags, niveaux de log.
Conclusion et prochaines étapes — variables d'environnement Lambda
Les deux approches de chiffrement KMS pour les variables d'environnement Lambda répondent à des besoins distincts. L'approche 1 avec --kms-key-arn couvre la grande majorité des cas : Lambda gère le déchiffrement, le code reste simple. L'approche 2 avec chiffrement manuel donne un contrôle explicite sur le cycle de vie du secret dans le code, au prix d'une complexité accrue.
Ressources officielles pour aller plus loin :
- Documentation Lambda — Variables d'environnement
- Chiffrement des variables d'environnement Lambda
- Concepts AWS KMS
Glossaire
| Terme | Définition |
|---|---|
| CMK (Customer Managed Key) | Clé KMS créée et gérée par le client, offrant un contrôle sur la rotation et les politiques d'accès |
| Cold start | Première initialisation d'un conteneur Lambda ; les variables d'environnement sont résolues à ce moment |
| Ciphertext | Données chiffrées, généralement encodées en base64 dans le contexte KMS |
| Warm container | Conteneur Lambda réutilisé pour une invocation ultérieure ; les variables en cache module ne sont pas rechargées |
| kms:Decrypt | Permission IAM requise pour déchiffrer des données avec une clé KMS, que ce soit par Lambda ou par le code applicatif |
Commentaires
Enregistrer un commentaire