Secrets Management Strategies for Cloud-Native Applications

The proliferation of microservices and distributed architectures has dramatically increased the complexity of managing sensitive information in cloud-native applications. Database credentials, API keys, encryption keys, and other secrets must be securely stored, distributed, and rotated across potentially hundreds of services and environments. Traditional approaches of hardcoding secrets or storing them in configuration files are not only insecure but fundamentally incompatible with the dynamic nature of cloud-native deployments.

Modern secrets management requires a comprehensive strategy that addresses the entire lifecycle of sensitive information, from generation and distribution to rotation and revocation. This strategy must account for the ephemeral nature of cloud-native workloads, the need for automated operations, and the security requirements of handling sensitive data across network boundaries.

The Secrets Management Lifecycle

Effective secrets management encompasses the complete lifecycle of sensitive information within an organization. This lifecycle begins with the secure generation of secrets using cryptographically secure random number generators and appropriate entropy sources. The generation process must ensure that secrets meet the strength requirements for their intended use and cannot be predicted or reproduced by attackers.

Once generated, secrets must be securely distributed to the applications and services that require them. This distribution mechanism should minimize exposure during transit and at rest while providing the accessibility needed for automated deployment and scaling operations. The distribution system must also handle the challenge of bootstrapping trust, ensuring that services can authenticate themselves to the secrets management system without creating circular dependencies.

// Comprehensive secrets management system with lifecycle support
import { SecretsManagerClient, GetSecretValueCommand, CreateSecretCommand, UpdateSecretCommand, RotateSecretCommand } from "@aws-sdk/client-secrets-manager";
import { KMSClient, EncryptCommand, DecryptCommand, GenerateDataKeyCommand } from "@aws-sdk/client-kms";
import { CloudWatchLogsClient, PutLogEventsCommand } from "@aws-sdk/client-cloudwatch-logs";
import { randomBytes, createHash, createCipher, createDecipher } from 'crypto';

interface SecretMetadata {
  secretId: string;
  secretName: string;
  description: string;
  secretType: SecretType;
  rotationSchedule?: RotationSchedule;
  accessPolicy: AccessPolicy;
  encryptionKeyId: string;
  createdDate: Date;
  lastRotated?: Date;
  nextRotation?: Date;
  tags: Record<string, string>;
}

interface RotationSchedule {
  enabled: boolean;
  intervalDays: number;
  automaticRotation: boolean;
  rotationWindow: TimeWindow;
  failureNotification: NotificationConfig;
}

interface AccessPolicy {
  allowedServices: string[];
  allowedEnvironments: string[];
  allowedRoles: string[];
  accessPatterns: AccessPattern[];
  ipWhitelist?: string[];
}

interface AccessPattern {
  pattern: string;
  maxRequestsPerHour: number;
  allowedOperations: SecretOperation[];
}

enum SecretType {
  DATABASE_CREDENTIALS = 'DATABASE_CREDENTIALS',
  API_KEY = 'API_KEY',
  ENCRYPTION_KEY = 'ENCRYPTION_KEY',
  CERTIFICATE = 'CERTIFICATE',
  OAUTH_TOKEN = 'OAUTH_TOKEN',
  SYMMETRIC_KEY = 'SYMMETRIC_KEY'
}

enum SecretOperation {
  READ = 'READ',
  CREATE = 'CREATE',
  UPDATE = 'UPDATE',
  DELETE = 'DELETE',
  ROTATE = 'ROTATE'
}

export class CloudNativeSecretsManager {
  private secretsClient: SecretsManagerClient;
  private kmsClient: KMSClient;
  private auditLogger: CloudWatchLogsClient;
  private secretCache: Map<string, CachedSecret> = new Map();
  private rotationTasks: Map<string, NodeJS.Timeout> = new Map();

  constructor(region: string, private defaultKmsKeyId: string) {
    this.secretsClient = new SecretsManagerClient({ region });
    this.kmsClient = new KMSClient({ region });
    this.auditLogger = new CloudWatchLogsClient({ region });
    
    // Initialize automatic cleanup and rotation monitoring
    this.initializeBackgroundTasks();
  }

  async createSecret(metadata: Omit<SecretMetadata, 'secretId' | 'createdDate'>): Promise<string> {
    try {
      // Generate unique secret ID
      const secretId = this.generateSecretId(metadata.secretName);
      
      // Generate the actual secret value based on type
      const secretValue = await this.generateSecretValue(metadata.secretType);
      
      // Encrypt the secret value
      const encryptedValue = await this.encryptSecretValue(secretValue, metadata.encryptionKeyId || this.defaultKmsKeyId);
      
      // Create secret in AWS Secrets Manager
      const createCommand = new CreateSecretCommand({
        Name: secretId,
        Description: metadata.description,
        SecretString: JSON.stringify(encryptedValue),
        KmsKeyId: metadata.encryptionKeyId || this.defaultKmsKeyId,
        ReplicationRegions: await this.getReplicationRegions(),
        Tags: this.formatTags(metadata.tags)
      });

      const result = await this.secretsClient.send(createCommand);
      
      // Store metadata
      const fullMetadata: SecretMetadata = {
        ...metadata,
        secretId,
        createdDate: new Date()
      };
      
      await this.storeSecretMetadata(secretId, fullMetadata);
      
      // Schedule rotation if enabled
      if (metadata.rotationSchedule?.enabled) {
        await this.scheduleRotation(secretId, metadata.rotationSchedule);
      }
      
      // Log creation event
      await this.auditSecretOperation(secretId, SecretOperation.CREATE, {
        userId: 'system',
        operation: 'create_secret',
        metadata: { secretType: metadata.secretType, hasRotation: !!metadata.rotationSchedule?.enabled }
      });
      
      return secretId;
      
    } catch (error) {
      throw new Error(`Failed to create secret: ${error instanceof Error ? error.message : 'Unknown error'}`);
    }
  }

  async getSecret(secretId: string, requestContext: SecretRequestContext): Promise<SecretValue> {
    try {
      // Validate access
      await this.validateAccess(secretId, requestContext, SecretOperation.READ);
      
      // Check cache first
      const cached = this.getCachedSecret(secretId);
      if (cached && !this.isCacheExpired(cached)) {
        await this.auditSecretOperation(secretId, SecretOperation.READ, {
          userId: requestContext.userId,
          operation: 'get_secret_cached',
          metadata: { cacheHit: true }
        });
        return cached.value;
      }
      
      // Retrieve from AWS Secrets Manager
      const getCommand = new GetSecretValueCommand({
        SecretId: secretId,
        VersionStage: 'AWSCURRENT'
      });
      
      const result = await this.secretsClient.send(getCommand);
      
      if (!result.SecretString) {
        throw new Error('Secret value not found');
      }
      
      // Decrypt the secret value
      const encryptedValue = JSON.parse(result.SecretString);
      const decryptedValue = await this.decryptSecretValue(encryptedValue);
      
      // Cache the secret for performance
      this.cacheSecret(secretId, decryptedValue, requestContext);
      
      // Log access event
      await this.auditSecretOperation(secretId, SecretOperation.READ, {
        userId: requestContext.userId,
        operation: 'get_secret',
        metadata: { 
          sourceIP: requestContext.sourceIP,
          userAgent: requestContext.userAgent,
          cacheHit: false
        }
      });
      
      return decryptedValue;
      
    } catch (error) {
      await this.auditSecretOperation(secretId, SecretOperation.READ, {
        userId: requestContext.userId,
        operation: 'get_secret_failed',
        metadata: { error: error instanceof Error ? error.message : 'Unknown error' }
      });
      throw new Error(`Failed to retrieve secret: ${error instanceof Error ? error.message : 'Unknown error'}`);
    }
  }

  async rotateSecret(secretId: string, options: RotationOptions = {}): Promise<RotationResult> {
    try {
      const metadata = await this.getSecretMetadata(secretId);
      if (!metadata) {
        throw new Error('Secret metadata not found');
      }
      
      // Pre-rotation validation
      await this.validateRotationEligibility(secretId, metadata);
      
      // Generate new secret value
      const newSecretValue = await this.generateSecretValue(metadata.secretType);
      
      // For database credentials, test connectivity before rotation
      if (metadata.secretType === SecretType.DATABASE_CREDENTIALS) {
        await this.validateDatabaseConnectivity(newSecretValue);
      }
      
      // Encrypt new value
      const encryptedNewValue = await this.encryptSecretValue(newSecretValue, metadata.encryptionKeyId);
      
      // Initiate rotation in AWS Secrets Manager
      const rotateCommand = new RotateSecretCommand({
        SecretId: secretId,
        ForceRotateSecretOnUpdate: true,
        RotationRules: {
          AutomaticallyAfterDays: metadata.rotationSchedule?.intervalDays || 30
        }
      });
      
      const rotationResult = await this.secretsClient.send(rotateCommand);
      
      // Update metadata
      metadata.lastRotated = new Date();
      metadata.nextRotation = new Date(Date.now() + (metadata.rotationSchedule?.intervalDays || 30) * 24 * 60 * 60 * 1000);
      await this.storeSecretMetadata(secretId, metadata);
      
      // Invalidate cache
      this.invalidateSecretCache(secretId);
      
      // Notify dependent services if configured
      if (options.notifyDependentServices !== false) {
        await this.notifyDependentServices(secretId, metadata);
      }
      
      // Log rotation event
      await this.auditSecretOperation(secretId, SecretOperation.ROTATE, {
        userId: options.initiatedBy || 'system',
        operation: 'rotate_secret',
        metadata: { 
          automatic: options.automatic || false,
          versionId: rotationResult.VersionId
        }
      });
      
      return {
        success: true,
        newVersionId: rotationResult.VersionId,
        rotatedAt: new Date(),
        nextRotation: metadata.nextRotation
      };
      
    } catch (error) {
      await this.auditSecretOperation(secretId, SecretOperation.ROTATE, {
        userId: options.initiatedBy || 'system',
        operation: 'rotate_secret_failed',
        metadata: { error: error instanceof Error ? error.message : 'Unknown error' }
      });
      
      return {
        success: false,
        error: error instanceof Error ? error.message : 'Unknown error',
        rotatedAt: new Date()
      };
    }
  }

  private async generateSecretValue(secretType: SecretType): Promise<SecretValue> {
    switch (secretType) {
      case SecretType.DATABASE_CREDENTIALS:
        return this.generateDatabaseCredentials();
      
      case SecretType.API_KEY:
        return this.generateApiKey();
      
      case SecretType.ENCRYPTION_KEY:
        return await this.generateEncryptionKey();
      
      case SecretType.SYMMETRIC_KEY:
        return this.generateSymmetricKey();
      
      case SecretType.CERTIFICATE:
        return await this.generateCertificate();
      
      default:
        return this.generateGenericSecret();
    }
  }

  private generateDatabaseCredentials(): SecretValue {
    const password = this.generateStrongPassword(32);
    return {
      type: SecretType.DATABASE_CREDENTIALS,
      value: {
        username: `app_user_${this.generateRandomString(8)}`,
        password: password,
        engine: 'postgres', // This would be configurable
        host: process.env.DB_HOST,
        port: 5432,
        dbname: process.env.DB_NAME
      },
      metadata: {
        passwordComplexity: 'high',
        generatedAt: new Date().toISOString()
      }
    };
  }

  private generateApiKey(): SecretValue {
    const keyPrefix = 'ak_';
    const keyBody = this.generateRandomString(32, 'base62');
    const checksum = createHash('sha256').update(keyBody).digest('hex').substring(0, 8);
    
    return {
      type: SecretType.API_KEY,
      value: `${keyPrefix}${keyBody}_${checksum}`,
      metadata: {
        keyFormat: 'prefixed_with_checksum',
        generatedAt: new Date().toISOString()
      }
    };
  }

  private async generateEncryptionKey(): Promise<SecretValue> {
    // Generate a 256-bit encryption key using AWS KMS
    const command = new GenerateDataKeyCommand({
      KeyId: this.defaultKmsKeyId,
      KeySpec: 'AES_256'
    });
    
    const result = await this.kmsClient.send(command);
    
    return {
      type: SecretType.ENCRYPTION_KEY,
      value: {
        keyMaterial: result.Plaintext ? Buffer.from(result.Plaintext).toString('base64') : '',
        encryptedKey: result.CiphertextBlob ? Buffer.from(result.CiphertextBlob).toString('base64') : '',
        keyId: this.defaultKmsKeyId
      },
      metadata: {
        keyLength: '256',
        algorithm: 'AES',
        generatedAt: new Date().toISOString()
      }
    };
  }

  private generateSymmetricKey(): SecretValue {
    const keyBytes = randomBytes(32); // 256-bit key
    return {
      type: SecretType.SYMMETRIC_KEY,
      value: keyBytes.toString('base64'),
      metadata: {
        keyLength: '256',
        encoding: 'base64',
        generatedAt: new Date().toISOString()
      }
    };
  }

  private generateStrongPassword(length: number): string {
    const charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-=[]{}|;:,.<>?';
    let password = '';
    
    // Ensure at least one character from each required category
    const categories = [
      'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
      'abcdefghijklmnopqrstuvwxyz',
      '0123456789',
      '!@#$%^&*()_+-=[]{}|;:,.<>?'
    ];
    
    // Add one character from each category
    for (const category of categories) {
      password += category[Math.floor(Math.random() * category.length)];
    }
    
    // Fill remaining length with random characters
    for (let i = password.length; i < length; i++) {
      password += charset[Math.floor(Math.random() * charset.length)];
    }
    
    // Shuffle the password to avoid predictable patterns
    return password.split('').sort(() => Math.random() - 0.5).join('');
  }

  private generateRandomString(length: number, charset: string = 'base64'): string {
    const charsets = {
      base64: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
      base62: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',
      hex: '0123456789abcdef'
    };
    
    const chars = charsets[charset as keyof typeof charsets] || charsets.base64;
    const bytes = randomBytes(length);
    let result = '';
    
    for (let i = 0; i < length; i++) {
      result += chars[bytes[i] % chars.length];
    }
    
    return result;
  }

  private async validateAccess(secretId: string, context: SecretRequestContext, operation: SecretOperation): Promise<void> {
    const metadata = await this.getSecretMetadata(secretId);
    if (!metadata) {
      throw new Error('Secret not found');
    }
    
    // Check service authorization
    if (!metadata.accessPolicy.allowedServices.includes(context.serviceId)) {
      throw new Error('Service not authorized to access this secret');
    }
    
    // Check environment authorization
    if (!metadata.accessPolicy.allowedEnvironments.includes(context.environment)) {
      throw new Error('Environment not authorized for this secret');
    }
    
    // Check operation authorization
    const allowedOps = metadata.accessPolicy.accessPatterns
      .filter(pattern => new RegExp(pattern.pattern).test(context.requestPath))
      .flatMap(pattern => pattern.allowedOperations);
    
    if (!allowedOps.includes(operation)) {
      throw new Error('Operation not allowed by access policy');
    }
    
    // Check rate limits
    await this.checkRateLimit(secretId, context);
    
    // Check IP whitelist if configured
    if (metadata.accessPolicy.ipWhitelist && metadata.accessPolicy.ipWhitelist.length > 0) {
      if (!metadata.accessPolicy.ipWhitelist.includes(context.sourceIP)) {
        throw new Error('IP address not in whitelist');
      }
    }
  }

  private async scheduleRotation(secretId: string, schedule: RotationSchedule): Promise<void> {
    if (!schedule.enabled || !schedule.automaticRotation) {
      return;
    }
    
    const intervalMs = schedule.intervalDays * 24 * 60 * 60 * 1000;
    
    const timeout = setTimeout(async () => {
      try {
        await this.rotateSecret(secretId, { automatic: true, initiatedBy: 'system' });
        // Reschedule for next interval
        await this.scheduleRotation(secretId, schedule);
      } catch (error) {
        console.error(`Automatic rotation failed for secret ${secretId}:`, error);
        // Implement notification logic here
      }
    }, intervalMs);
    
    this.rotationTasks.set(secretId, timeout);
  }

  private initializeBackgroundTasks(): void {
    // Clean up expired cache entries every 5 minutes
    setInterval(() => {
      this.cleanupExpiredCache();
    }, 5 * 60 * 1000);
    
    // Check for missed rotations every hour
    setInterval(() => {
      this.checkMissedRotations();
    }, 60 * 60 * 1000);
  }

  private cleanupExpiredCache(): void {
    const now = Date.now();
    for (const [secretId, cached] of this.secretCache.entries()) {
      if (now > cached.expiresAt) {
        this.secretCache.delete(secretId);
      }
    }
  }

  private async checkMissedRotations(): Promise<void> {
    // This would query the metadata store for secrets with missed rotation schedules
    // Implementation would depend on your metadata storage solution
  }

  async getSecretUsageMetrics(secretId: string, timeRange: TimeRange): Promise<UsageMetrics> {
    // Implementation would query audit logs to generate usage metrics
    return {
      totalAccesses: 0,
      uniqueServices: 0,
      averageRequestsPerHour: 0,
      peakUsageTime: new Date(),
      accessPatterns: []
    };
  }

  async revokeSecret(secretId: string, reason: string): Promise<void> {
    try {
      // Mark secret as revoked in metadata
      const metadata = await this.getSecretMetadata(secretId);
      if (metadata) {
        metadata.tags['status'] = 'revoked';
        metadata.tags['revokedAt'] = new Date().toISOString();
        metadata.tags['revokeReason'] = reason;
        await this.storeSecretMetadata(secretId, metadata);
      }
      
      // Cancel any scheduled rotations
      const rotationTask = this.rotationTasks.get(secretId);
      if (rotationTask) {
        clearTimeout(rotationTask);
        this.rotationTasks.delete(secretId);
      }
      
      // Invalidate cache
      this.invalidateSecretCache(secretId);
      
      // Log revocation
      await this.auditSecretOperation(secretId, SecretOperation.DELETE, {
        userId: 'system',
        operation: 'revoke_secret',
        metadata: { reason }
      });
      
    } catch (error) {
      throw new Error(`Failed to revoke secret: ${error instanceof Error ? error.message : 'Unknown error'}`);
    }
  }
}

Dynamic Secret Injection Patterns

Modern cloud-native applications require sophisticated mechanisms for injecting secrets into containers and serverless functions without exposing them in configuration files or environment variables. Dynamic injection patterns ensure that secrets are provided to applications at runtime through secure channels and are automatically refreshed when rotations occur.

These patterns must address the challenges of container orchestration platforms like Kubernetes, where pods may be created and destroyed frequently, and serverless environments where function instances have limited lifecycle control. The injection mechanism must be efficient enough to avoid impacting application startup times while providing the security guarantees needed for production environments.

// Dynamic secret injection system for cloud-native workloads
import { ECSClient, DescribeTasksCommand, DescribeTaskDefinitionCommand } from "@aws-sdk/client-ecs";
import { LambdaClient, GetFunctionCommand, UpdateFunctionConfigurationCommand } from "@aws-sdk/client-lambda";
import { SSMClient, GetParametersCommand, PutParameterCommand } from "@aws-sdk/client-ssm";

interface SecretInjectionConfig {
  injectionMethod: InjectionMethod;
  targetWorkload: WorkloadTarget;
  secretMappings: SecretMapping[];
  refreshPolicy: RefreshPolicy;
  failureHandling: FailureHandlingPolicy;
}

interface SecretMapping {
  secretId: string;
  targetKey: string;
  transformationRule?: TransformationRule;
  validationRule?: ValidationRule;
}

interface RefreshPolicy {
  automaticRefresh: boolean;
  refreshInterval: number; // minutes
  refreshTriggers: RefreshTrigger[];
  gracefulFailover: boolean;
}

enum InjectionMethod {
  ENVIRONMENT_VARIABLE = 'ENVIRONMENT_VARIABLE',
  MOUNTED_FILE = 'MOUNTED_FILE',
  IN_MEMORY_CACHE = 'IN_MEMORY_CACHE',
  INIT_CONTAINER = 'INIT_CONTAINER',
  SIDECAR_PROXY = 'SIDECAR_PROXY'
}

enum RefreshTrigger {
  SECRET_ROTATION = 'SECRET_ROTATION',
  SCHEDULE = 'SCHEDULE',
  MANUAL = 'MANUAL',
  HEALTH_CHECK_FAILURE = 'HEALTH_CHECK_FAILURE'
}

export class DynamicSecretInjector {
  private ecsClient: ECSClient;
  private lambdaClient: LambdaClient;
  private ssmClient: SSMClient;
  private secretsManager: CloudNativeSecretsManager;
  private injectionJobs: Map<string, InjectionJob> = new Map();

  constructor(
    region: string,
    secretsManager: CloudNativeSecretsManager
  ) {
    this.ecsClient = new ECSClient({ region });
    this.lambdaClient = new LambdaClient({ region });
    this.ssmClient = new SSMClient({ region });
    this.secretsManager = secretsManager;
    
    this.initializeRefreshScheduler();
  }

  async injectSecrets(config: SecretInjectionConfig): Promise<InjectionResult> {
    try {
      // Validate configuration
      await this.validateInjectionConfig(config);
      
      // Retrieve all required secrets
      const secretValues = await this.retrieveSecretsForInjection(config.secretMappings);
      
      // Apply transformations
      const transformedSecrets = await this.applyTransformations(secretValues, config.secretMappings);
      
      // Inject based on method
      const injectionResult = await this.performInjection(config, transformedSecrets);
      
      // Set up refresh monitoring if enabled
      if (config.refreshPolicy.automaticRefresh) {
        await this.setupRefreshMonitoring(config, injectionResult.jobId);
      }
      
      return injectionResult;
      
    } catch (error) {
      throw new Error(`Secret injection failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
    }
  }

  private async performInjection(config: SecretInjectionConfig, secrets: ProcessedSecret[]): Promise<InjectionResult> {
    switch (config.injectionMethod) {
      case InjectionMethod.ENVIRONMENT_VARIABLE:
        return await this.injectAsEnvironmentVariables(config, secrets);
      
      case InjectionMethod.MOUNTED_FILE:
        return await this.injectAsMountedFiles(config, secrets);
      
      case InjectionMethod.IN_MEMORY_CACHE:
        return await this.injectToMemoryCache(config, secrets);
      
      case InjectionMethod.INIT_CONTAINER:
        return await this.injectViaInitContainer(config, secrets);
      
      case InjectionMethod.SIDECAR_PROXY:
        return await this.injectViaSidecarProxy(config, secrets);
      
      default:
        throw new Error(`Unsupported injection method: ${config.injectionMethod}`);
    }
  }

  private async injectAsEnvironmentVariables(config: SecretInjectionConfig, secrets: ProcessedSecret[]): Promise<InjectionResult> {
    const jobId = this.generateJobId();
    
    try {
      if (config.targetWorkload.type === 'ECS_TASK') {
        return await this.injectToECSTask(config, secrets, jobId);
      } else if (config.targetWorkload.type === 'LAMBDA_FUNCTION') {
        return await this.injectToLambdaFunction(config, secrets, jobId);
      } else {
        throw new Error(`Unsupported workload type for environment variable injection: ${config.targetWorkload.type}`);
      }
    } catch (error) {
      throw new Error(`Environment variable injection failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
    }
  }

  private async injectToECSTask(config: SecretInjectionConfig, secrets: ProcessedSecret[], jobId: string): Promise<InjectionResult> {
    // For ECS, we update the task definition with secrets from AWS Systems Manager Parameter Store
    const parameterPromises = secrets.map(async (secret) => {
      const parameterName = `/secrets/${config.targetWorkload.identifier}/${secret.targetKey}`;
      
      await this.ssmClient.send(new PutParameterCommand({
        Name: parameterName,
        Value: typeof secret.value === 'string' ? secret.value : JSON.stringify(secret.value),
        Type: 'SecureString',
        Overwrite: true,
        Description: `Secret for ${config.targetWorkload.identifier}`,
        Tags: [
          { Key: 'InjectionJob', Value: jobId },
          { Key: 'TargetWorkload', Value: config.targetWorkload.identifier },
          { Key: 'ManagedBy', Value: 'DynamicSecretInjector' }
        ]
      }));
      
      return {
        name: secret.targetKey,
        valueFrom: parameterName
      };
    });
    
    const parameterReferences = await Promise.all(parameterPromises);
    
    // Create injection job record
    const injectionJob: InjectionJob = {
      jobId,
      config,
      createdAt: new Date(),
      status: 'ACTIVE',
      parameterReferences,
      lastRefresh: new Date()
    };
    
    this.injectionJobs.set(jobId, injectionJob);
    
    return {
      jobId,
      status: 'SUCCESS',
      injectionMethod: config.injectionMethod,
      secretCount: secrets.length,
      targetWorkload: config.targetWorkload.identifier,
      parameterReferences
    };
  }

  private async injectToLambdaFunction(config: SecretInjectionConfig, secrets: ProcessedSecret[], jobId: string): Promise<InjectionResult> {
    const functionName = config.targetWorkload.identifier;
    
    // Get current function configuration
    const getFunctionResult = await this.lambdaClient.send(new GetFunctionCommand({
      FunctionName: functionName
    }));
    
    const currentEnvironment = getFunctionResult.Configuration?.Environment?.Variables || {};
    
    // Add secrets to environment variables
    const updatedEnvironment = { ...currentEnvironment };
    
    for (const secret of secrets) {
      updatedEnvironment[secret.targetKey] = typeof secret.value === 'string' ? secret.value : JSON.stringify(secret.value);
    }
    
    // Update function configuration
    await this.lambdaClient.send(new UpdateFunctionConfigurationCommand({
      FunctionName: functionName,
      Environment: {
        Variables: updatedEnvironment
      }
    }));
    
    // Create injection job record
    const injectionJob: InjectionJob = {
      jobId,
      config,
      createdAt: new Date(),
      status: 'ACTIVE',
      environmentVariables: secrets.map(s => s.targetKey),
      lastRefresh: new Date()
    };
    
    this.injectionJobs.set(jobId, injectionJob);
    
    return {
      jobId,
      status: 'SUCCESS',
      injectionMethod: config.injectionMethod,
      secretCount: secrets.length,
      targetWorkload: config.targetWorkload.identifier,
      environmentVariables: secrets.map(s => s.targetKey)
    };
  }

  private async injectAsMountedFiles(config: SecretInjectionConfig, secrets: ProcessedSecret[]): Promise<InjectionResult> {
    // This would integrate with Kubernetes secrets or EFS/FSx for file-based injection
    const jobId = this.generateJobId();
    
    // Create temporary directory structure
    const secretsPath = `/tmp/secrets/${jobId}`;
    
    // For each secret, create a file
    const filePromises = secrets.map(async (secret) => {
      const filePath = `${secretsPath}/${secret.targetKey}`;
      const fileContent = typeof secret.value === 'string' ? secret.value : JSON.stringify(secret.value, null, 2);
      
      // In a real implementation, this would write to a shared volume or mounted filesystem
      // For demonstration, we'll simulate the file creation
      return {
        path: filePath,
        size: Buffer.byteLength(fileContent, 'utf8'),
        permissions: '600' // Read-write for owner only
      };
    });
    
    const files = await Promise.all(filePromises);
    
    const injectionJob: InjectionJob = {
      jobId,
      config,
      createdAt: new Date(),
      status: 'ACTIVE',
      mountedFiles: files,
      lastRefresh: new Date()
    };
    
    this.injectionJobs.set(jobId, injectionJob);
    
    return {
      jobId,
      status: 'SUCCESS',
      injectionMethod: config.injectionMethod,
      secretCount: secrets.length,
      targetWorkload: config.targetWorkload.identifier,
      mountPath: secretsPath,
      files
    };
  }

  private async injectViaSidecarProxy(config: SecretInjectionConfig, secrets: ProcessedSecret[]): Promise<InjectionResult> {
    const jobId = this.generateJobId();
    
    // Create a sidecar proxy configuration that serves secrets via HTTP API
    const proxyConfig = {
      port: 8080,
      endpoints: secrets.map(secret => ({
        path: `/secrets/${secret.targetKey}`,
        method: 'GET',
        auth: 'bearer_token',
        response: secret.value
      })),
      healthCheck: '/health',
      metrics: '/metrics'
    };
    
    // In a real implementation, this would deploy a sidecar container
    // that provides an API for accessing secrets
    
    const injectionJob: InjectionJob = {
      jobId,
      config,
      createdAt: new Date(),
      status: 'ACTIVE',
      sidecarConfig: proxyConfig,
      lastRefresh: new Date()
    };
    
    this.injectionJobs.set(jobId, injectionJob);
    
    return {
      jobId,
      status: 'SUCCESS',
      injectionMethod: config.injectionMethod,
      secretCount: secrets.length,
      targetWorkload: config.targetWorkload.identifier,
      sidecarEndpoint: `http://localhost:${proxyConfig.port}`
    };
  }

  async refreshSecrets(jobId: string): Promise<RefreshResult> {
    const job = this.injectionJobs.get(jobId);
    if (!job) {
      throw new Error(`Injection job ${jobId} not found`);
    }
    
    try {
      // Retrieve updated secrets
      const updatedSecrets = await this.retrieveSecretsForInjection(job.config.secretMappings);
      const transformedSecrets = await this.applyTransformations(updatedSecrets, job.config.secretMappings);
      
      // Perform refresh based on injection method
      const refreshResult = await this.performRefresh(job, transformedSecrets);
      
      // Update job record
      job.lastRefresh = new Date();
      job.refreshCount = (job.refreshCount || 0) + 1;
      
      return refreshResult;
      
    } catch (error) {
      job.lastError = error instanceof Error ? error.message : 'Unknown error';
      job.status = 'ERROR';
      
      throw new Error(`Secret refresh failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
    }
  }

  private async performRefresh(job: InjectionJob, secrets: ProcessedSecret[]): Promise<RefreshResult> {
    switch (job.config.injectionMethod) {
      case InjectionMethod.ENVIRONMENT_VARIABLE:
        return await this.refreshEnvironmentVariables(job, secrets);
      
      case InjectionMethod.MOUNTED_FILE:
        return await this.refreshMountedFiles(job, secrets);
      
      case InjectionMethod.SIDECAR_PROXY:
        return await this.refreshSidecarProxy(job, secrets);
      
      default:
        throw new Error(`Refresh not supported for injection method: ${job.config.injectionMethod}`);
    }
  }

  private async refreshEnvironmentVariables(job: InjectionJob, secrets: ProcessedSecret[]): Promise<RefreshResult> {
    if (job.config.targetWorkload.type === 'LAMBDA_FUNCTION') {
      // For Lambda, we need to update the function configuration
      const result = await this.injectToLambdaFunction(job.config, secrets, job.jobId);
      return {
        success: true,
        updatedSecrets: secrets.length,
        method: 'environment_variable_update',
        timestamp: new Date()
      };
    } else {
      // For ECS, update the parameter store values
      for (const secret of secrets) {
        const parameterName = `/secrets/${job.config.targetWorkload.identifier}/${secret.targetKey}`;
        await this.ssmClient.send(new PutParameterCommand({
          Name: parameterName,
          Value: typeof secret.value === 'string' ? secret.value : JSON.stringify(secret.value),
          Type: 'SecureString',
          Overwrite: true
        }));
      }
      
      return {
        success: true,
        updatedSecrets: secrets.length,
        method: 'parameter_store_update',
        timestamp: new Date()
      };
    }
  }

  private initializeRefreshScheduler(): void {
    // Check for jobs that need refresh every minute
    setInterval(async () => {
      const now = new Date();
      
      for (const [jobId, job] of this.injectionJobs.entries()) {
        if (!job.config.refreshPolicy.automaticRefresh) {
          continue;
        }
        
        const minutesSinceLastRefresh = (now.getTime() - job.lastRefresh.getTime()) / (1000 * 60);
        
        if (minutesSinceLastRefresh >= job.config.refreshPolicy.refreshInterval) {
          try {
            await this.refreshSecrets(jobId);
          } catch (error) {
            console.error(`Scheduled refresh failed for job ${jobId}:`, error);
          }
        }
      }
    }, 60 * 1000); // Every minute
  }

  async revokeInjection(jobId: string): Promise<void> {
    const job = this.injectionJobs.get(jobId);
    if (!job) {
      throw new Error(`Injection job ${jobId} not found`);
    }
    
    try {
      // Clean up based on injection method
      switch (job.config.injectionMethod) {
        case InjectionMethod.ENVIRONMENT_VARIABLE:
          await this.cleanupEnvironmentVariables(job);
          break;
        
        case InjectionMethod.MOUNTED_FILE:
          await this.cleanupMountedFiles(job);
          break;
        
        case InjectionMethod.SIDECAR_PROXY:
          await this.cleanupSidecarProxy(job);
          break;
      }
      
      // Remove job record
      this.injectionJobs.delete(jobId);
      
    } catch (error) {
      throw new Error(`Failed to revoke injection: ${error instanceof Error ? error.message : 'Unknown error'}`);
    }
  }

  async getInjectionStatus(jobId: string): Promise<InjectionStatus> {
    const job = this.injectionJobs.get(jobId);
    if (!job) {
      return {
        jobId,
        status: 'NOT_FOUND',
        error: 'Job not found'
      };
    }
    
    return {
      jobId,
      status: job.status,
      createdAt: job.createdAt,
      lastRefresh: job.lastRefresh,
      refreshCount: job.refreshCount || 0,
      secretCount: job.config.secretMappings.length,
      method: job.config.injectionMethod,
      targetWorkload: job.config.targetWorkload.identifier,
      error: job.lastError
    };
  }
}

Zero-Trust Secret Distribution

Zero-trust architecture principles apply directly to secrets management, requiring that every access request be authenticated, authorized, and encrypted regardless of the source or destination. This approach ensures that secrets are protected even when network boundaries are compromised or when dealing with untrusted environments.

Implementing zero-trust secret distribution requires sophisticated identity verification mechanisms, encrypted communication channels, and comprehensive audit logging. The system must assume that any component could be compromised and design security controls accordingly.

// Zero-trust secret distribution system
interface ZeroTrustContext {
  identity: VerifiedIdentity;
  networkContext: NetworkContext;
  deviceAttestation: DeviceAttestation;
  environmentFactors: EnvironmentFactors;
  riskAssessment: RiskAssessment;
}

interface VerifiedIdentity {
  principalId: string;
  principalType: PrincipalType;
  authenticationMethods: AuthMethod[];
  verificationLevel: VerificationLevel;
  certificates: Certificate[];
  attestationData: string;
}

interface NetworkContext {
  sourceIP: string;
  destinationIP: string;
  networkZone: SecurityZone;
  encryptionLevel: EncryptionLevel;
  intermediaryNodes: string[];
  latency: number;
}

interface DeviceAttestation {
  deviceId: string;
  platformType: PlatformType;
  firmwareVersion: string;
  securityFeatures: SecurityFeature[];
  integrityMeasurements: IntegrityMeasurement[];
  trustScore: number;
}

enum PrincipalType {
  SERVICE = 'SERVICE',
  USER = 'USER',
  CONTAINER = 'CONTAINER',
  FUNCTION = 'FUNCTION',
  PIPELINE = 'PIPELINE'
}

enum VerificationLevel {
  BASIC = 'BASIC',
  ENHANCED = 'ENHANCED',
  HIGH_ASSURANCE = 'HIGH_ASSURANCE'
}

export class ZeroTrustSecretDistributor {
  private identityVerifier: IdentityVerifier;
  private riskEngine: RiskAssessmentEngine;
  private encryptionManager: EncryptionManager;
  private auditLogger: AuditLogger;
  private distributionCache: Map<string, DistributionSession> = new Map();

  constructor(
    identityVerifier: IdentityVerifier,
    riskEngine: RiskAssessmentEngine,
    encryptionManager: EncryptionManager,
    auditLogger: AuditLogger
  ) {
    this.identityVerifier = identityVerifier;
    this.riskEngine = riskEngine;
    this.encryptionManager = encryptionManager;
    this.auditLogger = auditLogger;
  }

  async distributeSecret(
    secretId: string,
    distributionRequest: SecretDistributionRequest
  ): Promise<DistributionResult> {
    
    // Step 1: Comprehensive identity verification
    const verifiedContext = await this.verifyZeroTrustContext(distributionRequest);
    
    // Step 2: Risk assessment
    const riskAssessment = await this.assessDistributionRisk(secretId, verifiedContext);
    
    // Step 3: Policy evaluation
    const policyDecision = await this.evaluateDistributionPolicy(secretId, verifiedContext, riskAssessment);
    
    if (policyDecision.decision !== 'PERMIT') {
      await this.auditLogger.logDistributionDenial(secretId, verifiedContext, policyDecision.reason);
      throw new Error(`Distribution denied: ${policyDecision.reason}`);
    }
    
    // Step 4: Establish secure channel
    const secureChannel = await this.establishSecureChannel(verifiedContext);
    
    // Step 5: Retrieve and prepare secret
    const secretValue = await this.retrieveSecretForDistribution(secretId, verifiedContext);
    
    // Step 6: Apply additional security measures based on risk
    const securityMeasures = this.determineSecurityMeasures(riskAssessment);
    const preparedSecret = await this.applySecurityMeasures(secretValue, securityMeasures);
    
    // Step 7: Distribute secret through secure channel
    const distributionResult = await this.performSecureDistribution(
      preparedSecret,
      secureChannel,
      securityMeasures
    );
    
    // Step 8: Create distribution session for monitoring
    await this.createDistributionSession(secretId, verifiedContext, distributionResult);
    
    // Step 9: Log successful distribution
    await this.auditLogger.logDistributionSuccess(secretId, verifiedContext, distributionResult);
    
    return distributionResult;
  }

  private async verifyZeroTrustContext(request: SecretDistributionRequest): Promise<ZeroTrustContext> {
    // Verify identity using multiple authentication factors
    const identity = await this.identityVerifier.verifyMultiFactor(request.credentials);
    
    // Assess network context
    const networkContext = await this.assessNetworkContext(request.networkInfo);
    
    // Perform device attestation
    const deviceAttestation = await this.performDeviceAttestation(request.deviceInfo);
    
    // Collect environmental factors
    const environmentFactors = await this.collectEnvironmentFactors(request);
    
    // Perform comprehensive risk assessment
    const riskAssessment = await this.riskEngine.assessContext({
      identity,
      networkContext,
      deviceAttestation,
      environmentFactors
    });
    
    return {
      identity,
      networkContext,
      deviceAttestation,
      environmentFactors,
      riskAssessment
    };
  }

  private async performDeviceAttestation(deviceInfo: DeviceInfo): Promise<DeviceAttestation> {
    // Verify device identity and integrity
    const attestationChallenge = await this.generateAttestationChallenge();
    const attestationResponse = await this.requestDeviceAttestation(deviceInfo.deviceId, attestationChallenge);
    
    // Validate attestation response
    const isValid = await this.validateAttestationResponse(attestationResponse, attestationChallenge);
    if (!isValid) {
      throw new Error('Device attestation failed');
    }
    
    // Extract device security features
    const securityFeatures = await this.extractSecurityFeatures(attestationResponse);
    
    // Perform integrity measurements
    const integrityMeasurements = await this.performIntegrityMeasurements(deviceInfo);
    
    // Calculate trust score
    const trustScore = await this.calculateDeviceTrustScore(securityFeatures, integrityMeasurements);
    
    return {
      deviceId: deviceInfo.deviceId,
      platformType: deviceInfo.platformType,
      firmwareVersion: deviceInfo.firmwareVersion,
      securityFeatures,
      integrityMeasurements,
      trustScore
    };
  }

  private async establishSecureChannel(context: ZeroTrustContext): Promise<SecureChannel> {
    // Select encryption level based on risk assessment
    const encryptionLevel = this.selectEncryptionLevel(context.riskAssessment);
    
    // Generate ephemeral keys for this session
    const ephemeralKeys = await this.encryptionManager.generateEphemeralKeyPair();
    
    // Establish mutually authenticated TLS connection
    const tlsConfig = {
      version: 'TLSv1.3',
      cipherSuite: this.selectCipherSuite(encryptionLevel),
      certificateVerification: 'MUTUAL',
      clientCertificate: context.identity.certificates[0],
      ephemeralKeys
    };
    
    const secureChannel = await this.encryptionManager.establishTLSConnection(tlsConfig);
    
    // Add additional encryption layer for highly sensitive secrets
    if (encryptionLevel === EncryptionLevel.QUANTUM_RESISTANT) {
      const additionalEncryption = await this.encryptionManager.createQuantumResistantLayer();
      secureChannel.addEncryptionLayer(additionalEncryption);
    }
    
    return secureChannel;
  }

  private async applySecurityMeasures(
    secretValue: SecretValue,
    measures: SecurityMeasure[]
  ): Promise<PreparedSecret> {
    let preparedSecret: PreparedSecret = {
      value: secretValue,
      metadata: {
        originalSize: this.calculateSecretSize(secretValue),
        preparationTime: new Date(),
        securityMeasures: measures.map(m => m.type)
      }
    };
    
    for (const measure of measures) {
      switch (measure.type) {
        case SecurityMeasureType.TIME_BOUND_ACCESS:
          preparedSecret = await this.applyTimeBounds(preparedSecret, measure.parameters);
          break;
        
        case SecurityMeasureType.GEOGRAPHIC_RESTRICTION:
          preparedSecret = await this.applyGeographicRestriction(preparedSecret, measure.parameters);
          break;
        
        case SecurityMeasureType.USAGE_LIMITATION:
          preparedSecret = await this.applyUsageLimitation(preparedSecret, measure.parameters);
          break;
        
        case SecurityMeasureType.FORWARD_SECRECY:
          preparedSecret = await this.applyForwardSecrecy(preparedSecret, measure.parameters);
          break;
        
        case SecurityMeasureType.SPLIT_KNOWLEDGE:
          preparedSecret = await this.applySplitKnowledge(preparedSecret, measure.parameters);
          break;
      }
    }
    
    return preparedSecret;
  }

  private async applyTimeBounds(secret: PreparedSecret, parameters: any): Promise<PreparedSecret> {
    const expirationTime = new Date(Date.now() + parameters.validityPeriod * 1000);
    
    // Encrypt secret with time-bound key
    const timeBoundKey = await this.encryptionManager.generateTimeBoundKey(expirationTime);
    const encryptedValue = await this.encryptionManager.encrypt(secret.value, timeBoundKey);
    
    return {
      ...secret,
      value: encryptedValue,
      metadata: {
        ...secret.metadata,
        expiresAt: expirationTime,
        timeBoundKeyId: timeBoundKey.keyId
      }
    };
  }

  private async applySplitKnowledge(secret: PreparedSecret, parameters: any): Promise<PreparedSecret> {
    const threshold = parameters.threshold || 2;
    const totalShares = parameters.totalShares || 3;
    
    // Split the secret using Shamir's Secret Sharing
    const shares = await this.encryptionManager.splitSecret(secret.value, threshold, totalShares);
    
    // Distribute shares to different storage locations
    const shareLocations = await this.distributeShares(shares, parameters.shareRecipients);
    
    return {
      ...secret,
      value: {
        type: 'SPLIT_SECRET',
        threshold,
        totalShares,
        shareLocations,
        reconstructionHint: shares.reconstructionHint
      },
      metadata: {
        ...secret.metadata,
        splitIntoShares: totalShares,
        reconstructionThreshold: threshold
      }
    };
  }

  private async performSecureDistribution(
    preparedSecret: PreparedSecret,
    secureChannel: SecureChannel,
    securityMeasures: SecurityMeasure[]
  ): Promise<DistributionResult> {
    
    // Create distribution envelope with integrity protection
    const envelope = await this.createDistributionEnvelope(preparedSecret, securityMeasures);
    
    // Sign envelope for non-repudiation
    const signedEnvelope = await this.encryptionManager.signEnvelope(envelope);
    
    // Transmit through secure channel
    const transmissionId = await secureChannel.transmit(signedEnvelope);
    
    // Verify receipt and integrity
    const receipt = await secureChannel.waitForReceipt(transmissionId);
    
    if (!receipt.integrityVerified) {
      throw new Error('Distribution integrity verification failed');
    }
    
    return {
      distributionId: this.generateDistributionId(),
      transmissionId,
      status: 'SUCCESS',
      timestamp: new Date(),
      securityMeasures: securityMeasures.map(m => m.type),
      integrityHash: receipt.integrityHash,
      encryptionLevel: secureChannel.getEncryptionLevel()
    };
  }

  private async createDistributionSession(
    secretId: string,
    context: ZeroTrustContext,
    result: DistributionResult
  ): Promise<void> {
    
    const session: DistributionSession = {
      sessionId: result.distributionId,
      secretId,
      principalId: context.identity.principalId,
      distributedAt: result.timestamp,
      riskScore: context.riskAssessment.overallScore,
      securityMeasures: result.securityMeasures,
      networkContext: context.networkContext,
      deviceTrustScore: context.deviceAttestation.trustScore,
      expiresAt: this.calculateSessionExpiry(result.securityMeasures),
      isActive: true
    };
    
    this.distributionCache.set(session.sessionId, session);
    
    // Schedule session cleanup
    this.scheduleSessionCleanup(session);
  }

  async validateSecretUsage(
    distributionId: string,
    usageContext: SecretUsageContext
  ): Promise<UsageValidationResult> {
    
    const session = this.distributionCache.get(distributionId);
    if (!session || !session.isActive) {
      return {
        isValid: false,
        reason: 'Distribution session not found or expired'
      };
    }
    
    // Validate usage context against distribution constraints
    const violations = await this.checkUsageViolations(session, usageContext);
    
    if (violations.length > 0) {
      return {
        isValid: false,
        reason: `Usage violations detected: ${violations.join(', ')}`
      };
    }
    
    // Update session usage tracking
    session.lastUsed = new Date();
    session.usageCount = (session.usageCount || 0) + 1;
    
    return {
      isValid: true,
      remainingUsages: this.calculateRemainingUsages(session),
      expiresAt: session.expiresAt
    };
  }

  async revokeDistribution(distributionId: string, reason: string): Promise<void> {
    const session = this.distributionCache.get(distributionId);
    if (session) {
      session.isActive = false;
      session.revokedAt = new Date();
      session.revocationReason = reason;
      
      await this.auditLogger.logDistributionRevocation(distributionId, reason);
    }
  }

  private scheduleSessionCleanup(session: DistributionSession): void {
    const timeUntilExpiry = session.expiresAt.getTime() - Date.now();
    
    setTimeout(() => {
      this.distributionCache.delete(session.sessionId);
    }, timeUntilExpiry);
  }
}

Conclusion

Secrets management in cloud-native applications requires a fundamental shift from traditional approaches to comprehensive lifecycle management that addresses the unique challenges of distributed, dynamic environments. The strategies explored in this post demonstrate how organizations can implement robust secrets management systems that provide security, scalability, and operational efficiency.

The implementation of these secrets management patterns requires careful consideration of the trade-offs between security and operational complexity. Comprehensive lifecycle management provides the control and auditability needed for enterprise environments but requires sophisticated orchestration and monitoring systems. Dynamic injection patterns enable secure secret distribution without exposing sensitive information in configuration files, but they introduce dependencies on additional infrastructure components. Zero-trust distribution models provide the highest levels of security through continuous verification and risk assessment, but they require significant investment in identity management and monitoring capabilities.

As cloud-native applications continue to evolve, secrets management systems must adapt to support new deployment patterns, emerging threats, and changing regulatory requirements. Organizations implementing these patterns should focus on creating flexible, extensible systems that can integrate with existing security infrastructure while providing the automation and scalability needed for modern development practices. The foundation established by these secrets management strategies will prove essential as we continue to explore additional security considerations in the remaining posts of this series.

Comments