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: trueThen 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.jsonNo 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-secretKubernetes secret, which is never created here. SetmetricsCollector.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-processmode 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: falseThe secret value must be a JSON object:
{
"mcd_id": "<your-mcd-id>",
"mcd_token": "<your-mcd-token>"
}oauthSecret:
enabled: true
awsSecretsManager:
secretId: "mcd/agent/oauth"
skipExternalSecrets: true
metricsCollector:
enabled: falseenabled: true selects OAuth authentication; awsSecretsManager tells the agent where to read the credentials from.
The secret value must be a JSON object:
{
"client_id": "<your-client-id>",
"client_secret": "<your-client-secret>"
}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: falseoauthSecret:
enabled: true
awsSecretsManager:
clientIdSecretId: "mcd/agent/client-id"
clientSecretSecretId: "mcd/agent/client-secret"
skipExternalSecrets: true
metricsCollector:
enabled: falsePrefer 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: trueWrite 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:
| Error | Cause |
|---|---|
was not found β check the id and region, or it may exist with no value yet | Wrong name, region or account, or the secret exists with no value |
not valid JSON β¦ looks like base64-encoded JSON | Encoded payload; decode it before storing, or set base64Encoded: true |
not valid JSON with no base64 note | A bare value where JSON was expected, or malformed JSON |
configured as base64-encoded but its value is not valid base64 text | The flag is set against a plain payload |
holds binary data that is not UTF-8 text | A 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 textUse 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: SecretsManagerThe 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-agentThe 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-accountThe 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>"
}oauthSecret:
enabled: true
remoteRef:
key: "mcd/agent/oauth"enabled: true selects OAuth authentication; remoteRef tells the External Secrets Operator where to read the credentials from. Do not also set tokenSecret β configuring both fails at deployment time, since the agent uses one authentication method at a time.
The secret value in the cloud secret manager must be a JSON object:
{
"client_id": "<your-client-id>",
"client_secret": "<your-client-secret>"
}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/snowflakeSee 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.
Updated 5 days ago
