DefangLabs / DefangLabs/defang

Azure: config names differing only in case silently collide in Key Vault

Open
#2,143 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Go
Stars
166
Forks
24
Avg merge
10h 8m
Merged PRs (30d)
33

Description

Summary

On the azure stack, two config keys that differ only in case — e.g. RECALL_API_KEY vs Recall_Api_Key — are stored as the same Azure Key Vault secret and silently overwrite
each other. defang config set reports success for both, but only the last write survives. Its recorded key becomes the surviving casing, so ListConfig no longer returns the
original name and deploy fails with a misleading error:

Error: missing configs ["RECALL_API_KEY"] (https://s.defang.io/config)

This is azure-only. AWS (SSM Parameter Store) and GCP (Secret Manager) store config names case-sensitively, so the same two names coexist correctly there.

Root cause

Azure Key Vault secret names are case-insensitive. Defang preserves case when encoding a config key into a secret name, and never checks for a collision before writing — so two
differently-cased keys collapse onto one secret on Azure's side.

  1. Encoding preserves caseToSecretName only maps _-; it does not change case. (The case-folding is done by Key Vault, not Defang.)
    src/pkg/clouds/azure/keyvault/keyvault.go:48
    func ToSecretName(key string) string {
        return strings.ReplaceAll(key, "_", "-")
    }
    RECALL_API_KEY → RECALL-API-KEY and Recall_Api_Key → Recall-Api-Key are distinct strings, but Key Vault treats them as the same secret.
    
    
  2. Write does no collision check — last write wins. SetSecret overwrites the existing secret and its defang-config tag.
    src/pkg/clouds/azure/keyvault/keyvault.go:311 (PutSecret), src/pkg/cli/client/byoc/azure/byoc.go:730-742 (PutConfig)
  3. ListConfig returns the surviving tag's casing (Recall_Api_Key, not RECALL_API_KEY).
    src/pkg/cli/client/byoc/azure/byoc.go:685-709
  4. Validation matches case-sensitively, so the original compose reference is reported missing.
    src/pkg/cli/compose/validation.go:473 (slices.Contains), also src/pkg/cli/configResolution.go:48

Reproduction

On the azure stack, with a compose file referencing ${RECALL_API_KEY}:

defang config set RECALL_API_KEY    # value A
 defang config set Recall_Api_Key    # value B (different case) — silently overwrites A
 defang config                       # lists only ONE entry
 defang compose up                   # Error: missing configs ["RECALL_API_KEY"]

Expected behavior

Since Key Vault cannot represent both names, defang config set should fail loudly on the collision (e.g. config "Recall_Api_Key" conflicts with existing "RECALL_API_KEY") instead of
silently overwriting and failing later at deploy.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in src/pkg/clouds/azure/keyvault/keyvault.go at ToSecretName and PutSecret, then trace PutConfig in src/pkg/cli/client/byoc/azure/byoc.go. Confirm how ListConfig records casing and how validation handles names in validation.go and configResolution.go; done means defang config set rejects a case-only collision instead of overwriting the existing Azure secret.

Written by the indexing model from the issue text.

Assessment

Tech stack
azure, go
Domain
cli, cloud
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
74/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.