Menggunakan Environment Variables di Lambda: Konfigurasi dan Enkripsi KMS

Saat pertama kali deploy Lambda yang perlu terhubung ke RDS, godaan terbesar adalah hardcode endpoint database langsung di kode — cepat, tapi berbahaya. Setiap rotasi endpoint, setiap perpindahan environment dari staging ke production, memaksa kamu ubah kode dan redeploy. Lambda Environment Variables hadir untuk memisahkan konfigurasi dari kode, dan dengan integrasi KMS, nilai sensitif seperti connection string bisa dienkripsi saat istirahat.

TL;DR: Lambda Environment Variables dan Enkripsi KMS

AspekDetail
Cara set variableConsole, CLI (--environment), atau IaC (SAM/CDK/Terraform)
Enkripsi defaultAWS mengelola enkripsi at-rest menggunakan kunci terkelola Lambda (aws/lambda)
Enkripsi dengan CMKBisa menggunakan Customer Managed Key (CMK) KMS — membutuhkan izin IAM tambahan
Akses di runtimeprocess.env.VAR_NAME (Node.js), os.environ['VAR_NAME'] (Python)
Batas ukuranTotal semua environment variables maksimal 4 KB
Enkripsi transitHelper enkripsi di console menggunakan KMS untuk enkripsi sebelum deployment

Bagaimana Lambda Environment Variables Bekerja

Environment variables di Lambda disimpan sebagai bagian dari konfigurasi fungsi, bukan di dalam deployment package. Saat Lambda service membuat execution environment baru (cold start), variabel-variabel ini di-inject ke dalam proses sebelum handler kamu dipanggil. Artinya, kamu bisa baca nilainya kapan saja selama lifecycle fungsi — termasuk di initialization code di luar handler.

Enkripsi bekerja di dua lapisan yang sering disalahpahami:

  • Encryption at rest: Secara default, Lambda mengenkripsi environment variables menggunakan kunci terkelola AWS (aws/lambda). Kamu bisa mengganti ini dengan CMK milikmu sendiri.
  • Encryption in transit (helper enkripsi): Fitur 'Encrypt in transit' di console memungkinkan kamu mengenkripsi nilai menggunakan KMS sebelum dikirim ke Lambda — sehingga nilai tidak pernah muncul sebagai plaintext di konfigurasi fungsi. Ini membutuhkan kode tambahan di Lambda untuk memanggil KMS Decrypt saat runtime.
graph TD A["Konfigurasi Lambda Environment Variables"] --> B{"Jenis Enkripsi?"} B --> |"Default"| C["Kunci Terkelola AWS (aws/lambda)"] B --> |"CMK"| D["Customer Managed Key (KMS CMK milikmu)"] C --> E["Lambda Service Dekripsi saat Cold Start"] D --> E E --> F["Inject ke Execution Environment sebagai variabel OS"] F --> G["Handler Code os.environ / process.env"] H["Helper Enkripsi Transit"] --> I["Nilai tetap ciphertext di konfigurasi"] I --> F F --> J["Kode panggil KMS Decrypt eksplisit"] J --> G
  1. Konfigurasi fungsi menyimpan environment variables dalam keadaan terenkripsi menggunakan kunci KMS yang dipilih.
  2. Saat cold start, Lambda service mendekripsi nilai dan meng-inject-nya ke execution environment sebagai variabel lingkungan OS biasa.
  3. Handler code membaca nilai via os.environ atau process.env — sudah dalam bentuk plaintext di memori.
  4. Jika menggunakan helper enkripsi, nilai tetap terenkripsi di memori sampai kode Lambda secara eksplisit memanggil KMS Decrypt.

Menyetel Environment Variables via AWS CLI

Cara paling reproducible untuk mengelola environment variables adalah lewat CLI atau IaC — bukan console — supaya konfigurasi bisa di-version control dan diaudit.

Saat membuat fungsi baru

aws lambda create-function \
  --function-name my-db-function \
  --runtime python3.12 \
  --role arn:aws:iam::123456789012:role/my-lambda-role \
  --handler app.handler \
  --zip-file fileb://function.zip \
  --environment 'Variables={DB_ENDPOINT=mydb.cluster-xxxx.us-east-1.rds.amazonaws.com,DB_PORT=5432,DB_NAME=appdb}'

Update environment variables pada fungsi yang sudah ada

Perlu diingat: update-function-configuration dengan flag --environment akan mengganti seluruh set environment variables, bukan menambahkan. Jika kamu hanya ingin mengubah satu variabel, kamu harus menyertakan semua variabel yang ada.

aws lambda update-function-configuration \
  --function-name my-db-function \
  --environment 'Variables={DB_ENDPOINT=mydb-new.cluster-yyyy.us-east-1.rds.amazonaws.com,DB_PORT=5432,DB_NAME=appdb}'

Membaca konfigurasi yang ada (untuk tidak kehilangan variabel lain)

aws lambda get-function-configuration \
  --function-name my-db-function \
  --query 'Environment.Variables'

Mengenkripsi Environment Variables dengan KMS CMK

Default encryption menggunakan aws/lambda sudah cukup untuk banyak kasus, tapi jika kamu perlu kontrol penuh atas key rotation, key policy, dan audit trail per-key, gunakan Customer Managed Key (CMK).

Langkah 1: Buat atau identifikasi CMK

aws kms create-key \
  --description 'CMK untuk Lambda environment variables' \
  --key-usage ENCRYPT_DECRYPT \
  --origin AWS_KMS

Catat KeyId atau ARN dari output. Buat alias agar mudah direferensikan:

aws kms create-alias \
  --alias-name alias/lambda-env-key \
  --target-key-id <KeyId-dari-output-sebelumnya>

Langkah 2: Berikan izin ke Lambda execution role

Lambda execution role membutuhkan izin kms:Decrypt untuk mendekripsi environment variables saat runtime. Tanpa ini, fungsi akan gagal saat cold start dengan error terkait KMS.

🔽 Klik untuk lihat IAM policy
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowLambdaDecryptEnvVars",
      "Effect": "Allow",
      "Action": [
        "kms:Decrypt"
      ],
      "Resource": "arn:aws:kms:us-east-1:123456789012:key/<your-key-id>"
    }
  ]
}

Attach policy ke execution role:

aws iam put-role-policy \
  --role-name my-lambda-role \
  --policy-name LambdaKMSDecryptPolicy \
  --policy-document file://kms-decrypt-policy.json

Langkah 3: Set fungsi Lambda untuk menggunakan CMK

aws lambda update-function-configuration \
  --function-name my-db-function \
  --kms-key-arn arn:aws:kms:us-east-1:123456789012:key/<your-key-id> \
  --environment 'Variables={DB_ENDPOINT=mydb.cluster-xxxx.us-east-1.rds.amazonaws.com,DB_PORT=5432,DB_NAME=appdb}'

Verifikasi enkripsi aktif

aws lambda get-function-configuration \
  --function-name my-db-function \
  --query '{KMSKeyArn:KMSKeyArn,EnvVars:Environment.Variables}'

Jika field KMSKeyArn muncul di output, enkripsi dengan CMK sudah aktif. Jika kosong, Lambda menggunakan kunci default terkelola AWS.

Membaca Environment Variables di Kode Lambda

Setelah konfigurasi selesai, akses di kode sangat straightforward — tidak ada SDK call yang diperlukan untuk enkripsi at-rest biasa.

Python

import os
import boto3

def handler(event, context):
    db_endpoint = os.environ['DB_ENDPOINT']
    db_port = os.environ['DB_PORT']
    db_name = os.environ['DB_NAME']
    
    # Gunakan nilai untuk koneksi database
    connection_string = f'postgresql://{db_endpoint}:{db_port}/{db_name}'
    # ... logika koneksi
    return {'statusCode': 200}

Node.js

exports.handler = async (event) => {
    const dbEndpoint = process.env.DB_ENDPOINT;
    const dbPort = process.env.DB_PORT;
    const dbName = process.env.DB_NAME;
    
    // Gunakan nilai untuk koneksi database
    const connectionString = `postgresql://${dbEndpoint}:${dbPort}/${dbName}`;
    // ... logika koneksi
    return { statusCode: 200 };
};

Pola Enkripsi Transit: Nilai Sensitif Tidak Pernah Plaintext di Konfigurasi

Ada skenario di mana enkripsi at-rest saja tidak cukup — misalnya password database atau API key yang tidak boleh muncul sebagai plaintext bahkan di konfigurasi Lambda (yang bisa dilihat siapa saja dengan akses lambda:GetFunctionConfiguration). Untuk ini, gunakan pola enkripsi sebelum penyimpanan.

sequenceDiagram participant Dev as Developer participant KMS as AWS KMS participant LambdaCfg as Lambda Config participant Exec as Execution Environment participant Code as Handler Code Dev->>KMS: kms:Encrypt(plaintext_password) KMS-->>Dev: ciphertext_base64 Dev->>LambdaCfg: set DB_PASSWORD_ENCRYPTED=ciphertext_base64 Note over LambdaCfg: Nilai tersimpan sebagai ciphertext
Tidak ada plaintext di konfigurasi LambdaCfg->>Exec: Cold start — inject ciphertext Exec->>KMS: kms:Decrypt(ciphertext) KMS-->>Exec: plaintext_password Note over Exec: Plaintext hanya ada
di memori, sekali per cold start Exec->>Code: DB_PASSWORD tersedia di memori
  1. Enkripsi nilai menggunakan KMS Encrypt sebelum disimpan ke konfigurasi Lambda.
  2. Simpan ciphertext (base64-encoded) sebagai nilai environment variable.
  3. Saat runtime, kode Lambda memanggil KMS Decrypt untuk mendapatkan plaintext.
  4. Nilai plaintext hanya ada di memori execution environment, tidak pernah di konfigurasi yang tersimpan.

Enkripsi nilai sebelum disimpan (dilakukan sekali, di luar Lambda)

aws kms encrypt \
  --key-id alias/lambda-env-key \
  --plaintext fileb://<(echo -n 'password-database-saya') \
  --query CiphertextBlob \
  --output text

Output adalah ciphertext base64. Simpan string ini sebagai nilai environment variable, misalnya DB_PASSWORD_ENCRYPTED.

Dekripsi di kode Lambda saat runtime

🔽 Klik untuk lihat kode dekripsi Python
import os
import boto3
import base64

kms_client = boto3.client('kms')

# Dekripsi dilakukan sekali saat initialization (di luar handler)
# untuk menghindari KMS call di setiap invocation
def decrypt_env_var(encrypted_value):
    response = kms_client.decrypt(
        CiphertextBlob=base64.b64decode(encrypted_value)
    )
    return response['Plaintext'].decode('utf-8')

# Initialization code — berjalan sekali per execution environment
DB_PASSWORD = decrypt_env_var(os.environ['DB_PASSWORD_ENCRYPTED'])

def handler(event, context):
    # DB_PASSWORD sudah tersedia sebagai plaintext di memori
    # Gunakan untuk koneksi database
    return {'statusCode': 200}

Pola ini mirip seperti menaruh kunci brankas di dalam brankas lain — nilai sensitif hanya 'terbuka' di dalam execution environment yang terisolasi, bukan di konfigurasi yang bisa dibaca via API.

Pengalaman Lapangan: Misdiagnosis yang Sering Terjadi

Skenario yang cukup sering ditemui: fungsi Lambda tiba-tiba gagal dengan AccessDeniedException dari KMS setelah berjalan normal berbulan-bulan. Log CloudWatch menunjukkan error di saat cold start, bukan di dalam handler. Asumsi pertama biasanya 'key KMS dihapus' atau 'key policy berubah'.

Ternyata penyebabnya berbeda: CMK dirotasi secara otomatis, dan Lambda execution role tidak memiliki izin kms:Decrypt yang cukup luas untuk mencakup semua versi key. Rotasi KMS membuat key material baru, tapi ciphertext lama tetap bisa didekripsi selama key tidak dihapus — asalkan policy mengizinkan akses ke semua versi.

Cara verifikasi cepat:

aws kms describe-key \
  --key-id alias/lambda-env-key \
  --query 'KeyMetadata.{Status:KeyState,Rotation:KeyRotationStatus}'
aws kms get-key-rotation-status \
  --key-id alias/lambda-env-key

Jika rotasi aktif, pastikan IAM policy di execution role menggunakan ARN key (bukan versi spesifik) agar berlaku untuk semua key material.

Alternatif: Kapan Gunakan SSM Parameter Store atau Secrets Manager

Environment variables cocok untuk nilai konfigurasi yang tidak terlalu sensitif dan jarang berubah. Untuk kebutuhan yang lebih kompleks, pertimbangkan alternatif berikut:

KebutuhanRekomendasi
Endpoint, nama bucket, flag konfigurasiLambda Environment Variables
Password, API key — rotasi manualSSM Parameter Store (SecureString)
Credentials database — rotasi otomatisAWS Secrets Manager
Nilai yang sama dipakai banyak fungsiSSM Parameter Store atau Secrets Manager

Environment variables memiliki batas total 4 KB untuk semua variabel. Jika konfigurasi kamu mendekati batas ini, itu sinyal untuk memindahkan sebagian ke Parameter Store.

Menggunakan Lambda Environment Variables dengan AWS SAM

Untuk deployment yang reproducible, definisikan environment variables langsung di template SAM:

🔽 Klik untuk lihat template SAM
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Resources:
  MyDbFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: my-db-function
      Handler: app.handler
      Runtime: python3.12
      CodeUri: ./src
      Role: !GetAtt LambdaExecutionRole.Arn
      KmsKeyArn: !GetAtt LambdaEnvKey.Arn
      Environment:
        Variables:
          DB_ENDPOINT: !Ref DbEndpoint
          DB_PORT: '5432'
          DB_NAME: appdb

Parameters:
  DbEndpoint:
    Type: String
    Description: RDS cluster endpoint

  LambdaEnvKey:
    Type: AWS::KMS::Key
    Properties:
      Description: CMK untuk Lambda environment variables
      KeyPolicy:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              AWS: !Sub 'arn:aws:iam::${AWS::AccountId}:root'
            Action: 'kms:*'
            Resource: '*'

Wrap-Up: Lambda Environment Variables dan Enkripsi KMS

Memisahkan konfigurasi dari kode adalah praktik dasar yang membuat Lambda function portabel antar environment. Untuk Lambda environment variables yang berisi nilai sensitif, enkripsi dengan CMK memberikan kontrol audit dan rotasi yang lebih granular dibanding kunci default. Pilih pola enkripsi transit jika nilai tidak boleh muncul sebagai plaintext di konfigurasi yang tersimpan.

Langkah selanjutnya yang direkomendasikan:

Glosarium

IstilahPenjelasan
Environment VariablePasangan key-value yang di-inject ke execution environment Lambda sebelum handler dipanggil, diakses via variabel lingkungan OS standar.
CMK (Customer Managed Key)Kunci KMS yang dibuat dan dikelola oleh pengguna, memberikan kontrol penuh atas key policy, rotasi, dan audit trail.
Encryption at RestEnkripsi data saat disimpan — dalam konteks ini, nilai environment variables dienkripsi di storage Lambda.
Execution EnvironmentSandbox terisolasi tempat Lambda menjalankan kode; environment variables di-inject di sini saat initialization.
Cold StartProses inisialisasi execution environment baru, termasuk dekripsi environment variables — titik di mana error KMS paling sering muncul.

Komentar

Postingan populer dari blog ini

EC2 Tidak Bisa Akses Internet di Custom VPC: Cara Pasang Internet Gateway dan Update Route Table