Back to blog
·8 min read·BitAtlas

Secure Agent Environment Variable Injection with Encrypted Vaults

Best practices for safely injecting sensitive environment variables into AI agents using HashiCorp Vault and other encrypted secret management systems.

environment variablessecrets injectionVaultagent initializationsecurityHashiCorp Vaultsecrets management

AI agents operating in production environments need access to sensitive credentials—API keys, database passwords, encryption keys, and more. But embedding these secrets in code, configuration files, or environment variables at deployment time is a security antipattern that violates the principle of least privilege and leaves your agent infrastructure vulnerable to credential leakage.

This post explores how to implement secure environment variable injection for AI agents using encrypted vault systems, ensuring secrets remain encrypted until the moment an agent needs them, and never at rest in your codebase or container images.

The Problem: Secrets in Plain Sight

Traditional deployment workflows often handle secrets carelessly:

  • Secrets in Dockerfiles or base images: Once built, they're immutable and visible to anyone with image access.
  • Secrets in CI/CD logs: Unmasked environment variable output exposes credentials to anyone reviewing pipelines.
  • Secrets in configuration files: YAML, JSON, or .env files checked into version control are a liability, even in private repos.
  • Long-lived tokens: Static credentials with no expiration create a large attack surface if compromised.

When an agent receives these secrets, they often sit in memory, environment variables, or process listings—places where attackers or other processes on the same host can find them.

The Solution: Vault-Based Secret Injection

A secrets management system like HashiCorp Vault acts as a single source of truth for sensitive data, providing:

  • Encryption at rest: Secrets stored in encrypted form, protected by master keys.
  • Just-in-time delivery: Secrets are retrieved only when needed, never baked into artifacts.
  • Audit trails: Every access to secrets is logged, creating accountability and forensic capability.
  • Rotation and expiration: Secrets can be rotated automatically; time-limited credentials reduce blast radius.
  • Role-based access control: Fine-grained policies ensure agents only access the secrets they need.

Implementation Patterns

1. Vault Agent Caching

Vault Agent runs as a proxy on the same host as your AI agent, handling authentication and secret retrieval:

// Agent initialization with Vault Agent proxy
import http from 'node:http';

async function getSecret(path) {
  const response = await fetch('http://127.0.0.1:8200/v1/secret/data/' + path, {
    headers: { 'X-Vault-Token': process.env.VAULT_TOKEN }
  });
  const data = await response.json();
  return data.data.data; // Vault v2 secrets engine structure
}

const dbCreds = await getSecret('database/prod');
const agent = new AIAgent({
  database: {
    host: dbCreds.host,
    password: dbCreds.password,
    username: dbCreds.username
  }
});

Vault Agent caches secrets locally for a configurable TTL, reducing load on Vault while ensuring secrets remain short-lived. The cache is protected by Vault Agent's own authentication and can be restricted to only the agent process.

2. Init Container Pattern (Kubernetes)

In Kubernetes, use an init container to fetch secrets before the agent container starts:

apiVersion: v1
kind: Pod
metadata:
  name: ai-agent
spec:
  serviceAccountName: agent-sa
  initContainers:
  - name: vault-init
    image: vault:latest
    env:
    - name: VAULT_ADDR
      value: "https://vault.default.svc.cluster.local:8200"
    - name: VAULT_ROLE
      value: "ai-agent"
    volumeMounts:
    - name: secrets
      mountPath: /secrets
    command:
    - sh
    - -c
    - |
      VAULT_TOKEN=$(vault write -field=token auth/kubernetes/login role=$VAULT_ROLE jwt=$(<${VAULT_SA_TOKEN_PATH:-/var/run/secrets/kubernetes.io/serviceaccount/token}))
      vault kv get -format=json secret/ai-agent > /secrets/config.json
      chmod 400 /secrets/config.json
  containers:
  - name: agent
    image: my-agent:latest
    volumeMounts:
    - name: secrets
      mountPath: /secrets
      readOnly: true
    env:
    - name: AGENT_SECRETS_PATH
      value: /secrets/config.json
  volumes:
  - name: secrets
    emptyDir: {}

The init container runs with elevated privileges, retrieves secrets, and writes them to a temporary volume. The agent container then reads the secrets with restricted permissions. The volume is ephemeral—secrets never persist beyond pod lifetime.

3. Lambda / Serverless with Vault Lambda Auth

For serverless agents, use Vault's Lambda auth method to authenticate based on AWS Lambda role:

import crypto from 'node:crypto';
import https from 'node:https';

async function vaultLambdaAuth() {
  // Get AWS SigV4 signature for Vault auth
  const request = await fetch('http://169.254.169.254/latest/api/token', {
    headers: { 'X-aws-ec2-metadata-token-ttl-seconds': '21600' }
  });
  const token = await request.text();
  
  const response = await fetch('http://169.254.169.254/latest/dynamic/instance-identity/document', {
    headers: { 'X-aws-ec2-metadata-token': token }
  });
  const identity = await response.json();
  
  // Sign and send to Vault Lambda auth endpoint
  const vaultResponse = await fetch(
    'https://vault.example.com/v1/auth/aws/login',
    {
      method: 'POST',
      body: JSON.stringify({
        role: 'ai-agent-lambda',
        iam_http_request_method: 'POST',
        iam_request_body: JSON.stringify(identity),
        iam_request_url: Buffer.from('https://sts.amazonaws.com/').toString('base64')
      })
    }
  );
  
  const { auth } = await vaultResponse.json();
  return auth.client_token;
}

// In handler:
export async function handler(event) {
  const vaultToken = await vaultLambdaAuth();
  const secrets = await fetch('https://vault.example.com/v1/secret/data/lambda-agent', {
    headers: { 'X-Vault-Token': vaultToken }
  }).then(r => r.json());
  
  // Agent runs with fresh secrets
}

This approach ensures each Lambda invocation gets fresh, short-lived credentials derived from the Lambda's IAM role—no static tokens or hardcoded secrets.

4. Environment Variable Templating

Use Vault to dynamically generate environment variables at runtime:

#!/bin/bash
# vault-env-setup.sh

export VAULT_ADDR="https://vault.internal:8200"
export VAULT_TOKEN=$(vault login -method=oidc -path=oidc role=ai-agent -token-only)

# Fetch all secrets and export as variables
vault kv list secret/ai-agent | grep -v '^secrets' | while read secret; do
  value=$(vault kv get -field=value secret/ai-agent/$secret)
  export "AGENT_$(echo $secret | tr '[:lower:]' '[:upper:]')"="$value"
done

# Start agent with populated environment
exec node agent.js

This script fetches all secrets from Vault, sets them as environment variables with a safe prefix, then launches the agent. Secrets exist only in the shell environment and the agent process—they're never written to disk or logs.

Best Practices

1. Principle of least privilege: Grant each agent role access only to the secrets it actually needs. Use Vault policies to enforce this:

path "secret/data/ai-agent/*" {
  capabilities = ["read", "list"]
}

path "secret/data/other-team/*" {
  capabilities = []  # Explicitly denied
}

2. Use dynamic secrets when possible: Rather than static credentials, request time-limited secrets from Vault:

// Every agent request fetches fresh creds
const dbCreds = await fetch('https://vault.internal:8200/v1/database/creds/agent-role', {
  headers: { 'X-Vault-Token': vaultToken }
}).then(r => r.json());

// Credentials expire automatically after 1 hour
const agent = new DatabaseClient(dbCreds.data);

3. Encrypt secrets in logs: Even when logging is enabled, ensure any credential that appears in logs is redacted or encrypted:

const logger = pino({
  redact: {
    paths: ['*.password', '*.api_key', '*.token', 'AGENT_*'],
    censor: '[REDACTED]'
  }
});

4. Audit secret access: Review Vault audit logs regularly to catch unauthorized retrieval attempts or anomalous access patterns.

5. Rotate credentials regularly: Set automatic rotation schedules for long-lived secrets, and use Vault's rotation endpoints for managed secrets like database passwords.

Conclusion

Secure environment variable injection transforms how AI agents handle sensitive credentials. By deferring secret retrieval to runtime, using short-lived credentials, and implementing audit trails, you eliminate entire classes of credential-leakage vulnerabilities. Combined with proper RBAC, encryption at rest, and regular rotation, this approach provides defense-in-depth for agent infrastructure in production.

Start with Vault Agent or Kubernetes init containers—both are battle-tested patterns that integrate smoothly into existing deployment pipelines while significantly improving your security posture.

Encrypt your agent's data today

BitAtlas gives your AI agents AES-256-GCM encrypted storage with zero-knowledge guarantees. Free tier, no credit card required.