Skip to content

Sync Secrets from Vault#

To sync secrets from Vault using the External Secrets Operator, there are two steps needed:

  1. Vault configuration - we need to create a Vault role and policy that grants access to an application running in the cluster to access the desired secret(s)
  2. Kubernetes manifests - we need to define manifests for ExternalSecret, SecretStore, ServiceAccount, Role, and RoleBinding

Vault Configuration#

As of right now, all Vault config is done using Terraform in the es/Vault/config repo on code.vt.edu. To grant access to applications running in the platform, we've cre ated a Terraform module to minimize the amount of config you need to write.

  1. After cloning the repo locally, create a file named app-<org>-<app-name>.tf in the root directory.

    • Replace <org> with your unit's name (eg, dit-es, dit-middleware, dit-platform)
    • Replace <app-name> with the name of the app you're working with (e.g., hello-world)
  2. In that newly created file, place the following Terraform config:

    module "app_<org>_<app_name>" {
      source = "./modules/platform-access"
    
      app_name             = "<org>-<app-name>"
      tenant_identifier    = "<your-tenant-identifier>"
      service_account_name = "vault-<app-name>"
      secret_paths         = ["path/to/your/secret"]
      cluster_loc          = "<cluster-location>"
      cluster_env          = "<cluster-environment>"
    }
    
    • Replace <org>_<app_name> with the same values you used in the filename, just with underscores to separate words (per TF naming conventions)
    • Replace <org>-<app-name> with the same values you used in the filename
    • Replace <your-tenant-identifier> with the name of your platform tenant, which is the same as the cluster namespace
    • Replace <app-name> with the same value you used in the filename
    • Replace path/to/your/secret with the path to your secret. Important note: the second segment should be data. If I were to use the Vault UI and navigate to a secret using the path dit.platform/demo/registry-credential, I would set the path to dit.platform/data/demo/registry-credential
    • Replace <cluster-location> with eksa or aws
    • Replace <cluster-environment> with dvlp, pprd, or prod

    Example config:

    module "app_dit_platform_hello_world" {
      source = "./modules/platform-access"
    
      app_name             = "dit-platform-hello-world"
      tenant_identifier    = "it-common-platform-hello-world"
      service_account_name = "vault-hello-world"
      secret_paths         = ["dit.platform/data/demo/registry-credential"]
      cluster_loc          = "eksa"
      cluster_env          = "pprd"
    }
    
  3. Commit the change on a new branch, push the branch, and create a merge request. The Vault admin team will review it and it will be merged when approved.

Defining the Kubernetes manifests#

  1. In your platform manifest repo, create a file named external-secrets.yml. In that file, place the following:

    apiVersion: external-secrets.io/v1beta1
    kind: ExternalSecret
    metadata:
      name: <module.app_name>
    spec:
      refreshInterval: "24h"
      secretStoreRef:
        name: vault-backend
        kind: SecretStore
      target:
        # Kubernetes Secret to be created
        name: <key-name>
      dataFrom:
      - extract:
          key: path/to/key
    
    ---
    apiVersion: external-secrets.io/v1beta1
    kind: SecretStore
    metadata:
      name: vault-backend
    spec:
      provider:
        vault:
          server: "https://vault.es.cloud.vt.edu:8200"
          path: "<secret-engine>"
          version: "v2"
          auth:
            # Authenticate against Vault using a Kubernetes ServiceAccount
            # token stored in a Secret.
            # https://www.vaultproject.io/docs/auth/kubernetes
            kubernetes:
              # Path where the Kubernetes authentication backend is mounted in Vault
              mountPath: "<platform-(cluster-location)-(cluster-environment)>"
              # A required field containing the Vault Role to assume.
              role: "it-platform-<module.app_name>"
              # Optional service account field containing the name
              # of a kubernetes ServiceAccount
              serviceAccountRef:
                name: "vault-<module.app_name>"
    
    ---
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: "vault-<module.app_name>"
    
    ---
    kind: Role
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
      name: "vault-<module.app_name>"
    rules:
    - apiGroups: [""]
      resources: ["secrets"]
      verbs: ["list", "get", "create", "delete", "patch"]
    
    ---
    kind: RoleBinding
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
      name: "vault-<module.app_name>"
    subjects:
    - kind: ServiceAccount
      name: "vault-<module.app_name>"
    roleRef:
      kind: Role
      name: "vault-<module.app_name>"
      apiGroup: rbac.authorization.k8s.io
    
    • Replace <module.app_name> with the same value you put in the Vault Terraform config in the app_name field
    • Replace <path/to/key> with the path to the registry credential. Important note: this does not include the key name.
    • Replace <secret-engine> with your Vault secret engine
    • Replace <platform-(cluster-location)-(cluster-environment)> with one of
      • platform-eksa-pprd
      • platform-eksa-prod
      • platform-prod
  2. Commit the manifest and push it to your manifest repo. After a moment, you should see a Pod startup that runs and creates the secret!

Resources#