Pas intéressé par les explications ? Ticket express pour le tl;pl 🚀

Donc, vous avez un cluster Kubernetes et ingress-nginx pour ingress controller.

Vous avez peut-être utilisé des configuration snippets pour ajouter de la configuration NGINX spécifique à un ingress, comme par exemple des headers statiques :

nginx.ingress.kubernetes.io/configuration-snippet: |
  proxy_set_header foo "bar";

Comment ajouter des headers conditionnels, e.g. seulement si absents ?

On pourrait essayer un bloc Lua :

nginx.ingress.kubernetes.io/configuration-snippet: |
  rewrite_by_lua_block {
    headers = ngx.req.get_headers()
    if not headers["foo"] then
      ngx.req.set_header["foo"] = "bar"
    end
  }

Pour se rendre vite compte dans les journaux ingress-nginx que le rechargement de la configuration échoue :

[emerg] 31379#31379: is duplicate in /tmp/nginx-cfg011294657:1037
nginx: [emerg] is duplicate in /tmp/nginx-cfg011294657:1037
nginx: configuration file /tmp/nginx-cfg011294657 test failed

Et effectivement, le modèle de configuration NGINX par défaut contient déjà une directive rewrite_by_lua_block dans la même location :

rewrite_by_lua_block {
  lua_ingress.rewrite({{ locationConfigForLua $location $all }})
  balancer.rewrite()
  plugins.run()
}

On pourrait choisir l’une des solutions de contournement suivantes :

  • Utiliser un modèle personnalisé et retirer la directive rewrite_by_lua_block existante. Cependant, nous perdrions la configuration par défaut injectée via lua_ingress.rewrite(). Il manquerait également les configurations injectées via plugins Lua.
  • Écrire un plugin Lua, avec un filtre ngx.var.host pour savoir quels headers ajouter au rewrite() (de façon analogue au plugin d’exemple hello_world). Cependant, cela disjoint la définition d’un ingress de ses headers, ce qui est inélégant et peu maintenable.

S’il était possible de référencer une annotation personnalisée, nous pourrions l’utiliser à notre convenance :

rewrite_by_lua_block {
  lua_ingress.rewrite({{ locationConfigForLua $location $all }})
  balancer.rewrite()
  plugins.run()

  -- Custom headers
  {{ insert "my-annotations/custom-headers" }}
}

Malheureusement, les annotations ordinaires de ingress-nginx sont récupérées dans le code Go et référencées depuis le modèle NGINX via des objets et attributs dédiés, e.g. pour un configuration snippet :

{{/* Add any additional configuration defined */}}
{{ $location.ConfigurationSnippet }}

Modifier et recompiler ingress-nginx juste pour ajouter une annotation serait un peu excessif.

Heureusement, il existe une fonction permettant d’accéder aux informations brutes d’un ingress, et notamment à ses annotations. D’ailleurs, elle est déjà utilisée pour initialiser $ing tout en haut de la directive location :

{{ $ing := (getIngressInformation $location.Ingress $server.Hostname $location.Path) }}

tl;pl

Depuis un modèle NGINX, on peut accéder à des annotations personnalisées via $ing :

rewrite_by_lua_block {
  lua_ingress.rewrite({{ locationConfigForLua $location $all }})
  balancer.rewrite()
  plugins.run()

  -- Custom headers
  {{ if index $ing.Annotations "my-annotations/custom-headers" }}
  {{ index $ing.Annotations "my-annotations/custom-headers" }}
  {{ end }}
}

Il devient possible d’insérer des headers conditionnels directement dans la définition d’un ingress, de façon analogue à des headers statiques positionnés via un configuration snippet :

my-annotations/custom-headers: |
  headers = ngx.req.get_headers()
  if not headers["foo"] then
    ngx.req.set_header["foo"] = "bar"
  end

Article suivant