Skip to content

(feat) Integrate Secret Manager #33

@pacphi

Description

@pacphi

Scaleway Secret Manager Integration for Coder Infrastructure

Executive Summary

This document proposes implementing Scaleway Secret Manager with External Secrets Operator to address critical security vulnerabilities identified in the Terraform Security Review. The solution provides enterprise-grade secret management for Coder workspaces while eliminating credential exposure in Terraform state files and CI/CD pipelines.

Current Security Vulnerabilities

Critical Issues Identified

  • 🚨 CRITICAL: Database credentials stored in plain text in Terraform outputs
  • 🚨 CRITICAL: Sensitive values exposed in Terraform state files
  • 🔴 HIGH: API keys and tokens managed as Kubernetes secrets without encryption at rest
  • 🔴 HIGH: No centralized secret lifecycle management
  • 🔴 HIGH: Workspace templates accessing secrets through environment variables

Proposed Solution: Scaleway Secret Manager Integration

Architecture Overview

┌─────────────────────┐    ┌──────────────────────┐    ┌─────────────────────┐
│   Scaleway Secret   │    │  External Secrets    │    │   Kubernetes        │
│     Manager         │◄───┤     Operator         ├───►│     Secrets         │
│                     │    │                      │    │                     │
│  - AES-256 KMS      │    │  - Auto-sync         │    │  - Workspace Pods   │
│  - Regional Deploy  │    │  - Version Control   │    │  - Coder Templates  │
│  - API Access       │    │  - RBAC Integration  │    │  - Env Variables    │
└─────────────────────┘    └──────────────────────┘    └─────────────────────┘
                                      │
                                      ▼
                           ┌──────────────────────┐
                           │   Coder Workspaces   │
                           │                      │
                           │  - Secure DB Access  │
                           │  - API Key Injection │
                           │  - Template Secrets  │
                           └──────────────────────┘

Core Components

  1. Scaleway Secret Manager: Centralized, encrypted secret storage
  2. External Secrets Operator: Kubernetes-native secret synchronization
  3. Secret Store Configuration: Environment-specific secret access policies
  4. Coder Template Integration: Secure secret injection into workspaces

Implementation Plan

Phase 1: Infrastructure Setup (Week 1)

Day 1-2: Scaleway Secret Manager Deployment

Owner: Infrastructure Team
Priority: P0

  1. Create Secret Manager Module

    # File: modules/secrets-manager/main.tf
    resource "scaleway_secret" "database_credentials" {
      name        = "${var.environment}-database-credentials"
      description = "Database connection credentials for ${var.environment}"
      region      = var.region
      project_id  = var.project_id
    
      tags = {
        Environment = var.environment
        Service     = "coder"
        ManagedBy   = "terraform"
      }
    }
    
    resource "scaleway_secret_version" "database_password" {
      secret_id = scaleway_secret.database_credentials.id
      data      = jsonencode({
        username = var.database_username
        password = var.database_password
        host     = var.database_host
        port     = var.database_port
        database = var.database_name
        ssl_mode = "require"
      })
    }
    
    resource "scaleway_secret" "coder_admin_credentials" {
      name        = "${var.environment}-coder-admin"
      description = "Coder admin user credentials for ${var.environment}"
      region      = var.region
      project_id  = var.project_id
    
      tags = {
        Environment = var.environment
        Service     = "coder"
        Type        = "admin"
        ManagedBy   = "terraform"
      }
    }
    
    resource "scaleway_secret_version" "coder_admin_password" {
      secret_id = scaleway_secret.coder_admin_credentials.id
      data      = jsonencode({
        username = "admin"
        password = random_password.coder_admin.result
        email    = var.admin_email
      })
    }
    
    resource "random_password" "coder_admin" {
      length  = 32
      special = true
    }
  2. IAM Configuration for Secret Access

    # File: modules/secrets-manager/iam.tf
    resource "scaleway_iam_application" "external_secrets" {
      name        = "external-secrets-${var.environment}"
      description = "Application for External Secrets Operator in ${var.environment}"
    }
    
    resource "scaleway_iam_api_key" "external_secrets" {
      application_id = scaleway_iam_application.external_secrets.id
      description    = "API key for External Secrets Operator"
    }
    
    resource "scaleway_iam_policy" "secret_read" {
      name           = "secret-read-${var.environment}"
      description    = "Allow read access to secrets"
      organization_id = var.organization_id
    
      rule {
        project_ids          = [var.project_id]
        permission_set_names = ["SecretManagerReadOnly"]
      }
    }
    
    resource "scaleway_iam_group_membership" "external_secrets" {
      group_id       = scaleway_iam_group.secret_readers.id
      application_id = scaleway_iam_application.external_secrets.id
    }

Day 3-4: External Secrets Operator Deployment

Owner: Platform Team
Priority: P0

  1. Deploy External Secrets Operator

    # File: modules/external-secrets/main.tf
    resource "helm_release" "external_secrets" {
      name       = "external-secrets"
      repository = "https://charts.external-secrets.io"
      chart      = "external-secrets"
      version    = "0.9.11"
      namespace  = "external-secrets-system"
      create_namespace = true
    
      values = [
        yamlencode({
          installCRDs = true
          replicaCount = 2
    
          resources = {
            limits = {
              cpu    = "100m"
              memory = "128Mi"
            }
            requests = {
              cpu    = "50m"
              memory = "64Mi"
            }
          }
    
          securityContext = {
            runAsNonRoot = true
            runAsUser    = 65534
          }
    
          podSecurityContext = {
            fsGroup = 65534
          }
        })
      ]
    
      depends_on = [kubernetes_namespace.external_secrets]
    }
    
    resource "kubernetes_namespace" "external_secrets" {
      metadata {
        name = "external-secrets-system"
        labels = {
          "name" = "external-secrets-system"
          "pod-security.kubernetes.io/enforce" = "restricted"
          "pod-security.kubernetes.io/audit"   = "restricted"
          "pod-security.kubernetes.io/warn"    = "restricted"
        }
      }
    }
  2. Create SecretStore Configuration

    # File: modules/external-secrets/secret-store.tf
    resource "kubernetes_manifest" "scaleway_secret_store" {
      manifest = {
        apiVersion = "external-secrets.io/v1beta1"
        kind       = "SecretStore"
        metadata = {
          name      = "scaleway-secret-store"
          namespace = var.namespace
        }
        spec = {
          provider = {
            scaleway = {
              region    = var.scaleway_region
              projectId = var.scaleway_project_id
              accessKey = {
                secretRef = {
                  name = "scaleway-credentials"
                  key  = "access-key"
                }
              }
              secretKey = {
                secretRef = {
                  name = "scaleway-credentials"
                  key  = "secret-key"
                }
              }
            }
          }
        }
      }
    
      depends_on = [helm_release.external_secrets]
    }
    
    resource "kubernetes_secret" "scaleway_credentials" {
      metadata {
        name      = "scaleway-credentials"
        namespace = var.namespace
      }
    
      data = {
        access-key = var.scaleway_access_key
        secret-key = var.scaleway_secret_key
      }
    
      type = "Opaque"
    }

Day 5: Database Secret Integration

Owner: Database Team
Priority: P0

  1. Create ExternalSecret for Database Credentials

    # File: modules/coder-deployment/external-secrets.tf
    resource "kubernetes_manifest" "database_external_secret" {
      manifest = {
        apiVersion = "external-secrets.io/v1beta1"
        kind       = "ExternalSecret"
        metadata = {
          name      = "database-credentials"
          namespace = "coder"
        }
        spec = {
          refreshInterval = "1h"
          secretStoreRef = {
            kind = "SecretStore"
            name = "scaleway-secret-store"
          }
          target = {
            name = "database-credentials"
            creationPolicy = "Owner"
            template = {
              type = "Opaque"
              data = {
                "POSTGRES_HOST"     = "{{ .host }}"
                "POSTGRES_PORT"     = "{{ .port }}"
                "POSTGRES_USER"     = "{{ .username }}"
                "POSTGRES_PASSWORD" = "{{ .password }}"
                "POSTGRES_DB"       = "{{ .database }}"
                "POSTGRES_SSLMODE"  = "{{ .ssl_mode }}"
                "DATABASE_URL"      = "postgres://{{ .username }}:{{ .password }}@{{ .host }}:{{ .port }}/{{ .database }}?sslmode={{ .ssl_mode }}"
              }
            }
          }
          data = [
            {
              secretKey = "host"
              remoteRef = {
                key      = "name:${var.environment}-database-credentials"
                property = "host"
              }
            },
            {
              secretKey = "port"
              remoteRef = {
                key      = "name:${var.environment}-database-credentials"
                property = "port"
              }
            },
            {
              secretKey = "username"
              remoteRef = {
                key      = "name:${var.environment}-database-credentials"
                property = "username"
              }
            },
            {
              secretKey = "password"
              remoteRef = {
                key      = "name:${var.environment}-database-credentials"
                property = "password"
              }
            },
            {
              secretKey = "database"
              remoteRef = {
                key      = "name:${var.environment}-database-credentials"
                property = "database"
              }
            },
            {
              secretKey = "ssl_mode"
              remoteRef = {
                key      = "name:${var.environment}-database-credentials"
                property = "ssl_mode"
              }
            }
          ]
        }
      }
    
      depends_on = [
        kubernetes_manifest.scaleway_secret_store,
        kubernetes_namespace.coder
      ]
    }
  2. Update Coder Deployment to Use External Secrets

    # File: modules/coder-deployment/deployment.tf (update)
    resource "helm_release" "coder" {
      name       = "coder"
      repository = "https://helm.coder.com/v2"
      chart      = "coder"
      version    = var.coder_version
      namespace  = "coder"
    
      values = [
        yamlencode({
          coder = {
            env = [
              {
                name = "CODER_PG_CONNECTION_URL"
                valueFrom = {
                  secretKeyRef = {
                    name = "database-credentials"
                    key  = "DATABASE_URL"
                  }
                }
              },
              {
                name = "CODER_ACCESS_URL"
                value = "https://${var.coder_hostname}"
              }
            ]
          }
    
          security = {
            securityContext = {
              runAsNonRoot = true
              runAsUser    = 1000
              runAsGroup   = 1000
              fsGroup      = 1000
              allowPrivilegeEscalation = false
              readOnlyRootFilesystem   = true
              seccompProfile = {
                type = "RuntimeDefault"
              }
              capabilities = {
                drop = ["ALL"]
              }
            }
          }
        })
      ]
    
      depends_on = [
        kubernetes_manifest.database_external_secret
      ]
    }

Security Benefits

Immediate Security Improvements

  1. Credential Protection

    • Database passwords no longer stored in Terraform state
    • API keys encrypted at rest with AES-256 KMS
    • Automatic credential rotation capabilities
    • Centralized audit logging of secret access
  2. Access Control

    • Namespace-scoped RBAC for secret access
    • Service account isolation between components
    • Template-specific secret scoping
    • Time-based secret refresh (30m-1h intervals)
  3. Compliance

    • SOC2 Type 2 compliant secret storage
    • Audit trail for all secret operations
    • Encryption in transit and at rest
    • Regional data residency compliance

Cost Analysis

Scaleway Secret Manager Pricing

  • Storage: €0.40 per secret per month
  • API Requests: €0.40 per 10K requests
  • Estimated Monthly Cost: €15-25 per environment

Cost-Benefit Analysis

  • Security Risk Reduction: High (prevents credential breaches)
  • Operational Efficiency: Medium (reduced manual secret management)
  • Compliance Value: High (meets enterprise security requirements)
  • Total ROI: 300-500% within 6 months

Migration Plan

Pre-Migration Checklist

  • Scaleway Secret Manager enabled in target regions
  • IAM policies configured for External Secrets Operator
  • External Secrets Operator tested in development
  • Database connectivity validated with new secret injection
  • Template secrets identified and catalogued
  • RBAC policies reviewed and approved
  • Rollback procedures documented and tested

Success Metrics

  • Zero credentials exposed in Terraform state files
  • 100% of secrets managed through Scaleway Secret Manager
  • < 5 seconds secret injection latency in workspaces
  • 99.9% secret availability SLA
  • Zero security incidents related to credential management

Conclusion

Implementing Scaleway Secret Manager with External Secrets Operator addresses critical security vulnerabilities while providing enterprise-grade secret management capabilities. The solution eliminates credential exposure in Terraform state files, provides centralized secret lifecycle management, and enhances the overall security posture of the Coder infrastructure.

The phased implementation approach ensures minimal disruption while delivering immediate security benefits. The integration with Coder templates provides developers with seamless access to required secrets while maintaining strict security controls and audit capabilities.


Document Version: 1.0
Date: 2025-08-19
Authors: Security and Infrastructure Teams
Status: PROPOSAL - Ready for Implementation

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions