Generic Agent: Secrets Management

Configure how the agent accesses secrets

The Generic Agent needs access to secrets for authentication and optionally for integration credentials and custom CA certificates. The authentication secret format depends on the method chosen during registration β€” Key/Token (mcd_id/mcd_token) or OAuth (client_id/client_secret). There are three approaches for managing these secrets in Kubernetes.

ℹ️

If you deployed using one of the Terraform modules (AWS EKS, Azure AKS, or GCP GKE), the secret store is configured automatically.

This page is for users managing their own Kubernetes infrastructure or customizing the secrets configuration.

Whenever possible it is recommended to use a secret manager.

Kubernetes Secrets (Direct)

The simplest approach is to create Kubernetes secrets directly with kubectl and skip the External Secrets Operator entirely. This is the approach used in the Kubernetes deployment guide.

Set skipExternalSecrets: true in your values.yaml:

skipExternalSecrets: true

Then create the authentication secret manually, matching the method you chose during registration:

# Key/Token
kubectl create secret generic mcd-agent-token-secret -n mcd-agent \
  --from-file=contents.json=agent-token.json
# OAuth β€” also set `oauthSecret.enabled: true` in your values.yaml, otherwise
# the agent uses key/token authentication and ignores this secret
kubectl create secret generic mcd-oauth-secret -n mcd-agent \
  --from-file=credentials.json=oauth-credentials.json

No integrations secret is required β€” mcd-integrations-secrets is optional and the agent starts without it. Create it only when you have self-hosted integration credentials to mount.

This approach is suitable mostly for development and testing. Secrets must be updated manually with kubectl when they change.

AWS Secrets Manager (Direct)

On EKS, the agent can read its own authentication credential straight from AWS Secrets Manager using the AWS identity attached to its service account. No Kubernetes secret is created, and the External Secrets Operator is not involved.

Nothing has to exist in the cluster before installing the chart, so the namespace can be left for the chart to create β€” unlike the manual approach above, where the authentication secret has to be created first and the namespace along with it.

Use this when ESO is not available in your cluster, or when storing the authentication credential in a Kubernetes secret is not acceptable. Where ESO is available, prefer it: it keeps the metrics collector working (see below), and it can also sync the firewall CA certificate, which this option does not.

⚠️

The metrics collector cannot read this source β€” it expects the mcd-agent-token-secret Kubernetes secret, which is never created here. Set metricsCollector.enabled: false. The only effect is that the agent's CPU and memory metrics are not reported to Monte Carlo.

Log shipping is unaffected: the default in-process mode has the agent ship its own logs.

Configuration

Store the credential in AWS Secrets Manager as a JSON object, then point values.yaml at it by name or ARN:

tokenSecret:
  awsSecretsManager:
    secretId: "mcd/agent/token"

skipExternalSecrets: true
metricsCollector:
  enabled: false

The secret value must be a JSON object:

{
  "mcd_id": "<your-mcd-id>",
  "mcd_token": "<your-mcd-token>"
}

skipExternalSecrets: true is required unless ESO is installed for another reason β€” without it the chart renders a SecretStore, which is an ESO custom resource, and deployment fails with no matches for kind "SecretStore" on a cluster that does not have ESO.

Add awsSecretsManager.region only if the secret is in a different region than the cluster. Both EKS Pod Identity and IRSA provide the region to the agent, so a same-region secret needs no override.

One Secret per Credential Field

Where your convention is one value per secret, each credential field can name its own secret instead. Each holds a bare value rather than JSON:

tokenSecret:
  awsSecretsManager:
    mcdIdSecretId: "mcd/agent/mcd-id"
    mcdTokenSecretId: "mcd/agent/mcd-token"

skipExternalSecrets: true
metricsCollector:
  enabled: false

Prefer a single secret where your convention allows it: its rotation is atomic, while per-field reads are separate API calls, so a rotation landing between them can briefly pair a new value with an old one.

secretId and the per-field keys cannot be combined, and a partially configured block is rejected β€” both fail at deployment time, naming the key at fault.

How the Payload Is Stored

Plain JSON is the default. Two other shapes are supported.

Binary secrets β€” written with --secret-binary, or Terraform's secret_binary β€” are read with no extra configuration. The stored bytes must be UTF-8 text.

Base64-encoded values need an explicit flag, which applies to every secret in the block, including the per-field shape:

tokenSecret:
  awsSecretsManager:
    secretId: "mcd/agent/token"
    base64Encoded: true

Write true unquoted β€” a quoted value is rejected at deployment time.

The release notes printed after helm install name the encoding when the flag is set β€” worth checking, since a mistyped key is otherwise silent.

If the agent cannot use what it read, the error names the cause:

ErrorCause
was not found β€” check the id and region, or it may exist with no value yetWrong name, region or account, or the secret exists with no value
not valid JSON … looks like base64-encoded JSONEncoded payload; decode it before storing, or set base64Encoded: true
not valid JSON with no base64 noteA bare value where JSON was expected, or malformed JSON
configured as base64-encoded but its value is not valid base64 textThe flag is set against a plain payload
holds binary data that is not UTF-8 textA binary payload that is not text, such as compressed or DER data

IAM Permissions

The agent's own service account needs secretsmanager:GetSecretValue on the secret. This is a different principal from the one ESO uses β€” with ESO it is the operator that reads the secret, so an existing deployment grants the permission to ESO rather than to the agent.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "secretsmanager:GetSecretValue",
      "Resource": ["<the-secret-arn>"]
    }
  ]
}

Copy the ARN from the secret's page in the AWS console, or retrieve it with:

aws secretsmanager describe-secret --secret-id mcd/agent/oauth \
  --query ARN --output text

Use the real ARN: Secrets Manager appends a random suffix to the name, so one assembled from the name alone does not match, and recreating the secret changes it. Scope the policy to the exact secrets β€” a prefix like mcd/* would also grant read access to every other secret under it. If the secret uses a customer managed KMS key, grant kms:Decrypt on that key as well.

With one secret per credential field, list each ARN in Resource. Granting one of two secrets rather than both produces an AccessDeniedException that looks the same as a missing kms:Decrypt.

The agent's service account normally already has a role for bucket access β€” add this policy to it. If it does not, bind one using EKS Pod Identity or IRSA with the steps in that same section; the agent resolves either.

Credential Rotation

The agent caches the credential for up to 15 minutes, so a rotated secret takes effect within that window without a restart. Revoking access at the IAM level does not stop a running agent immediately for the same reason β€” rotate or delete the credential in Monte Carlo to revoke it.

Cloud Secret Managers (via External Secrets Operator)

For production environments, the recommended approach is to store secrets in a cloud secret manager and use the External Secrets Operator (ESO) to sync them into Kubernetes. ESO automatically pulls secrets from your cloud provider and creates Kubernetes secrets that the agent can consume.

When using the Terraform modules (AWS EKS, Azure AKS, GCP GKE), ESO is installed automatically by default. To disable it (for example, if ESO is already installed in your cluster), set install_external_secrets_operator to false in the helm variable of the Terraform module. If managing the Helm release manually, install ESO separately before deploying the agent chart.

Configuring the Secret Store

The secretStore value in your values.yaml tells ESO which cloud secret manager to use and how to authenticate. Below are configurations for each supported provider.

AWS Secrets Manager

secretStore:
  provider:
    aws:
      role: "arn:aws:iam::<account-id>:role/<eso-secrets-access-role>"
      region: "us-east-1"
      service: SecretsManager

The IAM role must have permission to read secrets from Secrets Manager:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "secretsmanager:GetSecretValue",
        "secretsmanager:GetResourcePolicy",
        "secretsmanager:DescribeSecret",
        "secretsmanager:ListSecretVersionIds"
      ],
      "Resource": "arn:aws:secretsmanager:<region>:<account-id>:secret:mcd/*"
    }
  ]
}

The role must be assumable by ESO via EKS Pod Identity.

Azure Key Vault

secretStore:
  provider:
    azurekv:
      tenantId: "<your-tenant-id>"
      authType: WorkloadIdentity
      vaultUrl: "https://<your-keyvault>.vault.azure.net"
      serviceAccountRef:
        name: mcd-agent-service-account
        namespace: mcd-agent

The agent's managed identity must have the Key Vault Secrets User role on the Key Vault. Authentication uses Azure Workload Identity.

GCP Secret Manager

secretStore:
  provider:
    gcpsm:
      projectID: "<your-gcp-project-id>"
      auth:
        workloadIdentity:
          serviceAccountRef:
            name: mcd-agent-service-account

The GCP service account must have the roles/secretmanager.secretAccessor role. Authentication uses GKE Workload Identity.

Referencing Secrets

Once the secret store is configured, reference your secrets in values.yaml:

Agent Authentication

Configure the reference matching your chosen authentication method:

tokenSecret:
  remoteRef:
    key: "mcd/agent/token"

The secret value in the cloud secret manager must be a JSON object:

{
  "mcd_id": "<your-mcd-id>",
  "mcd_token": "<your-mcd-token>"
}

Integration Credentials

integrationsSecrets:
  data:
    - secretKey: "<integration>.json"
      remoteRef:
        key: "<secret-name-in-cloud>"

You can reference multiple integration secrets:

integrationsSecrets:
  data:
    - secretKey: postgres.json
      remoteRef:
        key: mcd/integrations/postgres
    - secretKey: snowflake.json
      remoteRef:
        key: mcd/integrations/snowflake

See the Self-Hosted Credentials documentation for the JSON format for each integration type.

Firewall CA Certificate

If your CA certificate is stored in the cloud secret manager, you can reference it via an ExternalSecret instead of inlining the PEM content:

firewallCa:
  externalSecretRef: "<external-secret-name>"

See the Proxies and Traffic Inspection guide for more details on custom CA certificates.


Did this page help you?