Referència d'envilder validate

Com saps que un fitxer de mapatge funcionarà abans d'un pull, un desplegament o envilder exec? envilder validate comprova que el fitxer de mapatge és vàlid i que cada secret declarat existeix a AWS SSM o Azure Key Vault, i pot comparar-hi un fitxer .env local. No mostra mai cap valor de secret i surt amb 1 quan alguna cosa falla, així que encaixa com a pas de CI.

Requisits previs

  • La CLI d'envilder. --offline està disponible des de la versió 0.18 i la comprovació d'existència dels secrets des de la 0.20. --envfile, --compare-values i --format json arriben amb la propera versió.
  • Un fitxer de mapatge. Sense --offline, credencials del proveïdor i els permisos que es detallen a l'apartat Permisos.

Sinopsi

envilder validate [--offline] [--envfile <path> [--compare-values]] \
  [--format <text|json>] [--map <path>] [--provider <name>] \
  [--profile <name>] [--vault-url <url>]

Per defecte, validate comprova el fitxer de mapatge i després pregunta al proveïdor si existeix cada secret declarat. Amb --envfile també compara les claus d'un fitxer env local amb el fitxer de mapatge, i amb --compare-values en compara els valors amb els remots. Amb --offline només es llegeixen fitxers locals: no es crida cap proveïdor i no calen credencials del núvol. No mostra mai cap valor de secret.

envilder validate
envilder validate --map=config/envilder.json --profile=dev
envilder validate --provider=azure --vault-url=https://my-vault.vault.azure.net
envilder validate --offline
envilder validate --envfile=.env --compare-values
envilder validate --format=json

Opcions

OpcióDescripció
--offlineValida el fitxer de mapatge en local sense cridar el proveïdor
--envfileCompara les claus d'aquest fitxer env amb el fitxer de mapatge. No es llegeix si no s'indica
--compare-valuesCompara també cada valor declarat al fitxer env amb el seu valor remot. Requereix --envfile; no es pot combinar amb --offline
--formattext (per defecte) per a una sortida llegible, o json per a un únic document JSON a stdout
--mapFitxer de mapatge a comprovar (per defecte: envilder.json)
--provideraws o azure. Substitueix $config.provider
--profilePerfil d'AWS CLI. Substitueix $config.profile
--vault-urlURL d'Azure Key Vault. Substitueix $config.vaultUrl

Les opcions tenen els mateixos noms, valors per defecte i precedència que pull i exec: les opcions guanyen a $config, i una opció buida s'ignora. Posa-les després de validate; les que s'escriuen abans es rebutgen. Com a push, un valor buit de --envfile és un error.

--compare-values sense --envfile o amb --offline, i un --format que no sigui text ni json, es rebutgen abans de llegir cap fitxer o cridar cap proveïdor.

Què es comprova

El fitxer de mapatge (sempre)

El fitxer de mapatge es comprova primer, amb els mateixos missatges que fan servir pull, push i exec. S'informa de cada problema, no només del primer, amb la clau que el causa ($config.profile, --vault-url, DB_URL...). Els missatges anomenen claus i tipus, mai valors mapats.

  • El fitxer s'analitza com a JSON. Un error d'anàlisi indica la ruta i, quan l'analitzador la dona, la línia i la columna, però mai cita el text del voltant, perquè pot ser un secret enganxat al fitxer.
  • El document compleix l'esquema publicat del fitxer de mapatge (map-file.v1.json): camps de $config coneguts, noms de variable vàlids i mapatges de tipus string.
  • La configuració del proveïdor és coherent un cop aplicades les opcions: aws o azure, profile només amb aws, vaultUrl només amb azure, i azure té un vaultUrl.

Quan el fitxer de mapatge no és vàlid, validate s'atura aquí: no es crida cap proveïdor, amb --offline o sense.

Les claus remotes (sense --offline)

Cada ruta de secret declarada al fitxer de mapatge es comprova al proveïdor. Envilder no fa servir ni mostra mai cap valor: a AWS, els paràmetres String poden arribar a la resposta, i es descarten. Una ruta a la qual apunten diverses variables es comprova una sola vegada. Cada variable rep un estat:

EstatSignificat
existsEl secret existeix i la identitat el pot veure
missingEl proveïdor ha respost que aquest secret no existeix
access deniedLa identitat no té permís per comprovar el secret, així que no se sap si existeix. Mai s'informa com a missing
authentication failedEl proveïdor ha rebutjat les credencials, o no se n'ha trobat cap
provider errorQualsevol altra cosa, per exemple un proveïdor inaccessible o un nom de secret no vàlid

Les credencials d'AWS i les sessions SSO caducades s'informen amb el mateix missatge que pull i exec. En un terminal interactiu, una sessió SSO caducada ofereix executar aws sso login i després torna a provar.

Les claus del fitxer env (amb --envfile)

Disponible a partir de la propera versió. El fitxer env es llegeix amb el mateix analitzador que fan servir pull i push, així que els comentaris, export, les cometes i els valors multilínia es comporten igual. Les seves claus es comparen amb les variables que declara el fitxer de mapatge, pel nom exacte, distingint majúscules i minúscules:

TroballaEfecte
missing from .envUna variable declarada no és al fitxer env. Falla
duplicated in .envUna clau s'assigna més d'una vegada. Falla
not declared in map fileUna clau només és al fitxer env. Un avís que no canvia el codi de sortida

KEY= amb un valor buit compta com a present, i un fitxer env inexistent falla amb Env file not found. Aquesta comprovació només llegeix fitxers locals, així que s'executa abans de cap crida al proveïdor i també amb --offline. Els valors no es comparen ni es mostren.

Els valors del fitxer env (amb --compare-values)

Per a cada variable declarada que és al fitxer env i existeix al proveïdor, el valor remot es llegeix com ho fa pull i es compara amb el local. Quan una clau es repeteix, es compara l'última assignació, tal com la carrega dotenv:

EstatSignificat
matchesEls dos valors són idèntics
differsEls dos valors no són idèntics. Falla
unreadableEl secret existeix però el seu valor no s'ha pogut llegir, amb el motiu del proveïdor. Falla

Cap dels dos valors es mostra mai, i cada valor remot es descarta just després de la comparació. Les variables que ja s'han informat com a absents no es llegeixen. Sense --compare-values no es llegeix cap valor.

Permisos

La comprovació d'existència fa servir una crida al proveïdor que pull no fa, així que la identitat necessita un permís més que per a un pull. --offline no necessita cap credencial.

AWS SSM

Les rutes es comproven amb GetParameters, deu per crida, amb WithDecryption: false: els paràmetres SecureString no es desxifren mai. És una acció d'IAM diferent de la ssm:GetParameter que fa servir pull, així que afegeix-la a la política, limitada a les mateixes rutes:

{
  "Effect": "Allow",
  "Action": ["ssm:GetParameter", "ssm:GetParameters"],
  "Resource": "arn:aws:ssm:*:*:parameter/myapp/*"
}

La comprovació d'existència no necessita el permís kms:Decrypt. --compare-values llegeix els valors com pull, així que necessita el mateix accés a KMS que pull: kms:Decrypt sobre la clau de cada paràmetre SecureString xifrat amb una clau gestionada pel client. Amb kms:Decrypt, ssm:GetParameters també pot llegir valors desxifrats, així que limita'l a les mateixes rutes que ssm:GetParameter. Quan es denega una crida, cada ruta s'hi comprova per separat, així que access denied només s'informa per a les rutes que la identitat no pot llegir.

Azure Key Vault

Els noms dels secrets del vault es llisten una vegada (només metadades) i cada nom declarat, després de la conversió habitual de / a -, es compara amb aquesta llista sense distingir majúscules. Un secret desactivat s'informa com a missing, perquè pull no el pot llegir. Llistar requereix:

  • Azure RBAC: Microsoft.KeyVault/vaults/secrets/readMetadata/action sobre el vault, inclòs als rols integrats Key Vault Secrets User i Key Vault Reader.
  • Polítiques d'accés: el permís de secret List, a més de Get.
az role assignment create \
  --role "Key Vault Secrets User" \
  --assignee {YOUR_OBJECT_ID} \
  --scope /subscriptions/{SUB}/resourceGroups/{RG}/providers/Microsoft.KeyVault/vaults/{VAULT}
az keyvault set-policy \
  --name {VAULT_NAME} \
  --object-id {YOUR_OBJECT_ID} \
  --secret-permissions get list

La llista cobreix tot el vault, així que una identitat sense aquest permís rep access denied per a cada clau, amb el motiu que retorna Key Vault.

--compare-values també llegeix valors, així que necessita els mateixos permisos que un pull a més d'aquests.

Sortida

Tots els secrets existeixen (codi de sortida 0):

✓ Map file valid: envilder.json

☁ AWS identity · account=123456789012 · region=eu-west-1 · profile=dev

Checking 3 secrets in aws, profile=dev
  ✓ DB_URL        /myapp/db-url   exists
  ✓ DATABASE_URL  /myapp/db-url   exists
  ✓ API_KEY       /myapp/api-key  exists

✓ All 3 secrets exist

Alguns secrets falten o no es poden comprovar (codi de sortida 1; el resum va a stderr):

✓ Map file valid: envilder.json

Checking 3 secrets in aws, profile=dev
  ✓ DB_URL   /myapp/db-url   exists
  ✗ API_KEY  /myapp/api-key  missing
  ✗ TOKEN    /myapp/token    access denied (User: arn:aws:sts::123456789012:assumed-role/dev/ci is not authorized to perform: ssm:GetParameters ...)
✗ Validation failed: 1 missing, 1 access denied

Amb --offline:

✓ Map file valid: envilder.json
  Remote validation skipped (--offline): no provider was called, so the secrets were not checked.

Amb --envfile=.env --compare-values (codi de sortida 1):

✓ Map file valid: envilder.json
Checking env file .env
  ✗ TOKEN  missing from .env
  ⚠ DEBUG  not declared in map file

Checking 3 secrets in aws, profile=dev
  ✓ DB_URL   /myapp/db-url   exists
  ✓ API_KEY  /myapp/api-key  exists
  ✓ TOKEN    /myapp/token    exists
Comparing 2 values with aws, profile=dev
  ✓ DB_URL   matches
  ✗ API_KEY  differs
✗ Validation failed: 1 missing from .env, 1 differs

Quan totes les variables declarades són al fitxer env, el seu bloc acaba amb ✓ All 3 declared variables are in .env, i quan tots els valors comparats coincideixen, el bloc de valors acaba amb ✓ All 2 values match.

Un fitxer de mapatge no vàlid, a stderr:

✗ Map file invalid: envilder.json (3 problems)
  • $config.region is not a known field. Allowed fields: provider, profile, vaultUrl, projectId, namespace, path, name, description, owner, environment.
  • DB_PORT must map to a string secret path, not a number.
  • $config.profile is only supported with the aws provider (current provider: azure).

Sortida JSON (--format json)

Disponible a partir de la propera versió. Amb --format=json, validate escriu exactament un document JSON a stdout i res més, sigui quin sigui el resultat. Els logs, el bàner d'identitat d'AWS, els avisos, els errors i la sol·licitud d'inici de sessió SSO van a stderr, així que stdout es pot passar a jq o desar com a artefacte de CI. Els codis de sortida són els mateixos que amb la sortida de text. Els errors d'opcions com --format=yaml s'informen a stderr abans d'executar res, sense document.

{
  "valid": false,
  "offline": false,
  "mapFile": { "path": "envilder.json", "valid": true, "errors": [] },
  "keys": [
    { "name": "DB_URL", "provider": "aws", "path": "/myapp/db-url", "status": "exists" },
    { "name": "API_KEY", "provider": "aws", "path": "/myapp/api-key", "status": "exists" },
    {
      "name": "TOKEN",
      "provider": "aws",
      "path": "/myapp/token",
      "status": "access_denied",
      "reason": "User: arn:aws:sts::123456789012:assumed-role/dev/ci is not authorized to perform: ssm:GetParameters ..."
    }
  ],
  "envFile": {
    "path": ".env",
    "valid": false,
    "keys": [
      { "name": "DB_URL", "status": "present", "value": "matches" },
      { "name": "API_KEY", "status": "present", "value": "differs" },
      { "name": "TOKEN", "status": "missing" }
    ],
    "duplicates": [],
    "undeclared": ["DEBUG"]
  }
}
CampDescripció
validtrue exactament quan el codi de sortida és 0
offlinetrue amb --offline
mapFilepath, valid i errors (un { key, message } per problema, amb els mateixos missatges que la sortida de text). S'omet quan el fitxer de mapatge no s'ha pogut llegir
keysUna entrada per variable declarada, en l'ordre del fitxer de mapatge: name, provider, path, status i, quan el proveïdor en dona un, reason. status és exists, missing, access_denied, authentication_failed o provider_error. S'omet quan no s'han comprovat els secrets
envFileS'omet sense --envfile. Si no, path, valid, keys (cada variable declarada amb status present o missing, i value matches, differs o unreadable amb --compare-values), duplicates ({ name, count }) i undeclared
error{ message }, només quan l'execució s'ha aturat abans d'acabar totes les comprovacions (credencials caducades, un fitxer env inexistent, un inici de sessió SSO rebutjat). Les seccions completades abans continuen plenes

El document no conté mai cap valor de secret, ni local ni remot, i cap camp és mai null: una secció que no aplica s'omet. Una secció pot guanyar camps en versions futures; els camps existents mantenen el seu significat.

Codis de sortida

CodiSignificat
0El fitxer de mapatge és vàlid; sense --offline, cada secret declarat existeix; amb --envfile, cada variable declarada és al fitxer env exactament una vegada; amb --compare-values, cada valor comparat coincideix. Les claus que només són al fitxer env no el canvien
1Qualsevol altra cosa: fitxer de mapatge absent o no vàlid, opcions no vàlides, un secret que falta o no es pot comprovar, credencials absents o caducades, o un fitxer env que no existeix, no té una variable declarada, repeteix una clau o té un valor que difereix o no es pot comparar

A CI

Executa la comprovació offline a cada pull request, també des de forks, perquè no necessita credencials del núvol. Executa la comprovació d'existència on el job tingui les mateixes credencials que un pull, més els permisos d'aquesta pàgina:

- name: Check the map file
  run: npx envilder validate --offline --map=envilder.json

- name: Check that every secret exists
  run: npx envilder validate --map=envilder.json

A partir de la propera versió, per comprovar sense credencials que un .env.example versionat declara cada variable, afegeix --offline --envfile=.env.example al primer pas.

A partir de la propera versió, per conservar el resultat per a passos posteriors o altres eines, fes servir la sortida JSON. El pas continua fallant quan la validació falla:

- name: Check that every secret exists
  run: npx envilder validate --map=envilder.json --format=json > validate.json

validate surt amb 1 davant de qualsevol error, així que pot bloquejar un pas de desplegament abans que s'executi pull o envilder exec.

Des dels SDK

Els SDK de .NET, Python i Node.js (versió 1.1 o posterior) executen la mateixa comprovació del fitxer de mapatge i d'existència de claus des del codi de l'aplicació, amb els mateixos estats, sense la comparació del fitxer env:

SDKComprovació que retorna un resultatFer fallar la càrrega si no passa
Node.jsawait Envilder.fromMapFile(path).validateContract().withContractValidation()
PythonEnvilder.from_map_file(path).validate_contract().with_contract_validation()
.NETawait Env.FromMapFile(path).ValidateContractAsync(ct).WithContractValidation()

La validació del contracte és diferent de la validació de secrets: comprova el fitxer de mapatge i que cada secret existeix abans de carregar, mentre que la validació de secrets comprova que els valors ja resolts no siguin buits.

Què no fa validate

  • No mostra mai valors de secrets, i només els llegeix amb --compare-values.
  • Amb --offline no comprova que els secrets existeixin ni que les credencials els puguin llegir.
  • No corregeix el fitxer env: informa del que cal canviar.
  • No comprova la forma dels valors (tipus, URL, longitud).