writes.values : double rendu Handlebars (vynil + kuberest) — les expressions avec ${...} ou backticks échouent au parsing du second pass #2

Open
opened 2026-06-18 15:42:05 +02:00 by xmortelette · 0 comments

Contexte

En utilisant kuberest dans une box vynil, le champ writes[].values passe par deux rendus Handlebars distincts :

  1. Premier pass (vynil/templateGen) : rend le template .hbs → stocke le résultat dans le spec du RestEndPoint dans Kubernetes
  2. Second pass (kuberest au runtime) : rend à nouveau les values avec son propre moteur Handlebars, permettant d'y utiliser des expressions comme {{ base64_decode input.xxx }}

Ce comportement est documenté implicitement par l'usage de {{{{raw}}}}...{{{{/raw}}}} dans vynil pour passer des expressions Handlebars au second pass.

Problème

Lors du second pass (kuberest), le parser Handlebars tente de parser tout le contenu du bloc {{raw}}...{{/raw}}, y compris le contenu à l'intérieur. Pour HashiCorp Vault, les policies utilisent des templates vault qui contiennent des expressions avec ${} et des backticks :

{{ identity.entity.aliases.${ accessor `kubernetes/` }.metadata.service_account_namespace }}

Même protégée par {{{{raw}}}}...{{{{/raw}}}} dans le template vynil, cette expression produit après le premier pass :

{{ identity.entity.aliases.${ accessor `kubernetes/` }.metadata.service_account_namespace }}

Ce contenu (dans un bloc {{raw}}...{{/raw}}) est alors soumis au parser Handlebars de kuberest qui échoue avec :

HbsRenderError(RenderError { reason: TemplateError(TemplateError {
  reason: InvalidSyntax("expected trailing_tilde_to_omit_whitespace"),
  line_no: Some(2), column_no: Some(53),
  segment: Some("path \"namespaces/data/{{ identity.entity.aliases.${...")
}) })

Le parser Handlebars essaie d'interpréter ${ accessor \kubernetes/` }comme de la syntaxe Handlebars et échoue, même à l'intérieur d'un bloc{{raw}}`.

Comportement observé

# Dans vault_config.yaml.hbs (template vynil)
- name: allow_namespaced_secrets
  values: |
    policy: |
      path "namespaces/data/{{{{raw}}}}{{ identity.entity.aliases.${ accessor `kubernetes/` }.metadata.service_account_namespace }}{{{{/raw}}}}/*" {
        capabilities = ["create", "read", "update", "delete", "list"]
      }

Après premier pass (vynil) → stocké dans le RestEndPoint :

policy: |
  path "namespaces/data/{{ identity.entity.aliases.${ accessor `kubernetes/` }.metadata.service_account_namespace }}/*" {
    capabilities = ["create", "read", "update", "delete", "list"]
  }

Au second pass (kuberest) → TemplateFailed avec InvalidSyntax.

Contournement actuel

Utiliser le script post (Rhai) qui n'est rendu que par vynil (pas de second pass kuberest) :

client.post("sys/policies/acl/allow_namespaced_secrets", #{
  policy: "path \"namespaces/data/{{ identity.entity.aliases.${ accessor `kubernetes/` }.metadata.service_account_namespace }}/*\" {\n  capabilities = [\"create\", \"read\", \"update\", \"delete\", \"list\"]\n}\n"
});

Ici {{ identity... }} est du texte Rhai littéral (pas traité par Handlebars) → vault reçoit la policy avec le template correct.

Questions / demandes

  1. Est-ce documenté que writes.values passe par deux rendus Handlebars ?
  2. Le bloc {{raw}} de kuberest devrait-il vraiment parser son contenu (causant l'erreur) ou le passer entièrement en raw ?
  3. Piste d'évolution : serait-il possible d'avoir un mode values qui ne passe pas par le second rendu Handlebars (raw pur), pour les cas où le contenu doit être transmis verbatim à l'API cible ?

Versions testées :

  • kuberest : v0.7.x
  • HashiCorp Vault : 1.17.3 LTS (les templates {{ identity.entity... }} sont la syntaxe native de Vault pour les policies HCL)
## Contexte En utilisant kuberest dans une box vynil, le champ `writes[].values` passe par **deux rendus Handlebars distincts** : 1. **Premier pass (vynil/templateGen)** : rend le template `.hbs` → stocke le résultat dans le spec du RestEndPoint dans Kubernetes 2. **Second pass (kuberest au runtime)** : rend à nouveau les `values` avec son propre moteur Handlebars, permettant d'y utiliser des expressions comme `{{ base64_decode input.xxx }}` Ce comportement est **documenté implicitement** par l'usage de `{{{{raw}}}}...{{{{/raw}}}}` dans vynil pour passer des expressions Handlebars au second pass. ## Problème Lors du second pass (kuberest), le parser Handlebars tente de parser **tout le contenu** du bloc `{{raw}}...{{/raw}}`, y compris le contenu à l'intérieur. Pour HashiCorp Vault, les policies utilisent des templates vault qui contiennent des expressions avec `${}` et des backticks : ``` {{ identity.entity.aliases.${ accessor `kubernetes/` }.metadata.service_account_namespace }} ``` Même protégée par `{{{{raw}}}}...{{{{/raw}}}}` dans le template vynil, cette expression produit après le premier pass : ``` {{ identity.entity.aliases.${ accessor `kubernetes/` }.metadata.service_account_namespace }} ``` Ce contenu (dans un bloc `{{raw}}...{{/raw}}`) est alors soumis au parser Handlebars de kuberest qui échoue avec : ``` HbsRenderError(RenderError { reason: TemplateError(TemplateError { reason: InvalidSyntax("expected trailing_tilde_to_omit_whitespace"), line_no: Some(2), column_no: Some(53), segment: Some("path \"namespaces/data/{{ identity.entity.aliases.${...") }) }) ``` Le parser Handlebars essaie d'interpréter `${ accessor \`kubernetes/\` }` comme de la syntaxe Handlebars et échoue, même à l'intérieur d'un bloc `{{raw}}`. ## Comportement observé ```yaml # Dans vault_config.yaml.hbs (template vynil) - name: allow_namespaced_secrets values: | policy: | path "namespaces/data/{{{{raw}}}}{{ identity.entity.aliases.${ accessor `kubernetes/` }.metadata.service_account_namespace }}{{{{/raw}}}}/*" { capabilities = ["create", "read", "update", "delete", "list"] } ``` Après premier pass (vynil) → stocké dans le RestEndPoint : ``` policy: | path "namespaces/data/{{ identity.entity.aliases.${ accessor `kubernetes/` }.metadata.service_account_namespace }}/*" { capabilities = ["create", "read", "update", "delete", "list"] } ``` Au second pass (kuberest) → `TemplateFailed` avec `InvalidSyntax`. ## Contournement actuel Utiliser le script `post` (Rhai) qui n'est rendu que par vynil (pas de second pass kuberest) : ```rhai client.post("sys/policies/acl/allow_namespaced_secrets", #{ policy: "path \"namespaces/data/{{ identity.entity.aliases.${ accessor `kubernetes/` }.metadata.service_account_namespace }}/*\" {\n capabilities = [\"create\", \"read\", \"update\", \"delete\", \"list\"]\n}\n" }); ``` Ici `{{ identity... }}` est du texte Rhai littéral (pas traité par Handlebars) → vault reçoit la policy avec le template correct. ## Questions / demandes 1. **Est-ce documenté** que `writes.values` passe par deux rendus Handlebars ? 2. **Le bloc `{{raw}}` de kuberest** devrait-il vraiment parser son contenu (causant l'erreur) ou le passer entièrement en raw ? 3. **Piste d'évolution** : serait-il possible d'avoir un mode `values` qui ne passe pas par le second rendu Handlebars (raw pur), pour les cas où le contenu doit être transmis verbatim à l'API cible ? Versions testées : - kuberest : v0.7.x - HashiCorp Vault : 1.17.3 LTS (les templates `{{ identity.entity... }}` sont la syntaxe native de Vault pour les policies HCL)
Sign in to join this conversation.