TL;DR
Cilium 1.18 a ajouté un champ log aux CiliumNetworkPolicies pour taguer les verdicts de flux (FORWARDED, DROPPED, AUDIT, …) avec des labels personnalisés. Dans l’idée, c’est la fonctionnalité parfaite pour éviter de logger le trafic bloqué que l’on connait dans nos dashboards de monitoring !
Mais il y a un hic, sans rapport avec cette fonctionnalité, qui rend cette idée inutilisable : on ne peut pas l’utiliser avec egressDeny + toFQDNs.
ET, il y a un bug, qui fait que le “log” n’est visible que sur le trafic “autorisé”.
Je vous raconte…
Le problème : monitor all the things (mais pas trop)
Comme toute bonne équipe “ops” qui se respecte, nous monitorons/loggons les flux réseau de notre cluster Kubernetes avec Hubble pour une analyse ultérieure et de l’alerting. Nous (en particulier mon collègue Nicolas Nativel) poussons tous les flux AUDIT et DROPPED vers un dashboard Grafana pour pouvoir rapidement repérer quand quelque chose est bloqué et décider :
- C’est légitime ? → On ouvre le flux
- C’est suspect ? → On déclenche l’alarme 🚨

Ça fonctionne plutôt bien… jusqu’à ce qu’on commence à bloquer explicitement des choses qu’on sait devoir être bloquées.
Dans notre cas, nous voulions empêcher une application tierce d’envoyer les données de “télémétrie” (ouais, appelons ça comme ça 😏). On parle d’appels HTTPS vers des domaines de tracking externes.
Le problème ? Si on bloque simplement ces flux, ils apparaîtront comme DROPPED dans Hubble, déclencheront notre monitoring, et on se retrouvera avec des alertes pour quelque chose qu’on a intentionnellement bloqué.
C’est du bruit dont on ne veut pas.
Et donc, ce champ log des network policies de Cilium 1.18 ?
Bonne nouvelle ! Cilium 1.18 a introduit exactement ce dont nous avions besoin : la possibilité d’ajouter des champs de log personnalisés aux verdicts sur les network policies.
L’idée est simple : vous ajoutez un champ log.value à votre CiliumNetworkPolicy :
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: my-policy
spec:
endpointSelector:
matchLabels:
app: my-app
egress:
- toFQDNs:
- matchName: "example.com"
log:
value: "my-custom-log-tag"
Ensuite, quand vous observez les flux dans Hubble, vous pouvez les filtrer en utilisant CEL (Common Expression Language) :
hubble observe \
--verdict AUDIT \
--not \
--cel-expression "(_flow.policy_log.endsWith('my-custom-log-tag'))" \
--print-raw-filters
Output :
allowlist:
- '{"verdict":["AUDIT"]}'
denylist:
- '{"experimental":{"cel_expression":["(_flow.policy_log.endsWith(''my-custom-log-tag''))"]}}'
Parfait ! Vous savez maintenant taguer les calls que vous avez explicitement bloqués et les exclure de votre monitoring. 🎉
Le plan : bloquer la télémétrie élégamment
À l’aide de cette nouvelle fonctionnalité, nous avons élaboré notre stratégie :
- Utiliser
egressDenypour bloquer explicitement les domaines de télémétrie - Ajouter un champ de log personnalisé :
app-explicit-traffic-blocked - Configurer Hubble pour filtrer les verdicts de flux avec ce tag
- Profit ! 🎉
Voici ce que nous avons essayé :
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: app-external-block-policy
namespace: my-namespace
spec:
endpointSelector:
matchLabels:
app.kubernetes.io/name: my-app
# note: egressDeny prend la précédence sur les règles egress
# https://docs.cilium.io/en/stable/security/policy/language/#deny-policies
egressDeny:
# Bloquer tout le trafic externe et le logger avec un champ de log arbitraire
# Ceci est utilisé pour empêcher l'app d'envoyer des données de télémétrie à l'extérieur
# sans déclencher d'alerte AUDIT/DROPPED
# fonctionnalité ajoutée dans cilium 1.18.0 https://github.com/cilium/cilium/pull/39902
- toFQDNs:
- matchPattern: "*.telemetry.example.com"
toPorts:
- ports:
- port: "443"
protocol: TCP
- port: "80"
protocol: TCP
log:
value: "app-explicit-traffic-blocked"
Ça devrait fonctionner, non ? On utilise egressDeny (qui prend la précédence sur les autres règles, ce qui est une bonne chose !), et on le tague avec notre log personnalisé.
Retour à la réalité
Et puis… patatra.
En lisant la documentation Cilium sur les deny policies, nous sommes tombés sur cette petite note de rien du tout :
Deny policies do not support:
- policy enforcement at L7, i.e., specifically denying an URL
- toFQDNs, i.e., specifically denying traffic to a specific domain name.
Attendez, quoi ?
On ne peut pas utiliser toFQDNs avec egressDeny. Tout notre plan vient de s’effondrer 😱.
Pourquoi c’est un problème, déjà ?
Le problème, c’est le modèle de précédence dans Cilium :
- Les règles
egressDenyprennent la précédence sur les règlesegress(by design, et c’est bien !) - Mais si on utilise
egressDeny, on ne peut pas utilisertoFQDNspour cibler intelligemment le domaine incriminé, on doit bloquer par IP ou CIDR - Ces services de télémétrie utilisent probablement des IPs dynamiques pour leurs endpoints (bonne chance pour maintenir une liste…)
- Et si on bloque tout le trafic 80/443 dans
egressDeny, on ne peut pas faire d’exceptions pour le trafic légitime dans les règlesegresscar… deny prend la précédence sur allow !
On est coincé entre le marteau et l’enclume :
- Utiliser
egressavectoFQDNs→ ça marche, mais on ne peut pas bloquer, seulement autoriser - Utiliser
egressDenyavec des IPs → on va jouer au chat et à la souris avec des plages IP qui changent - Utiliser
egressDenypour bloquer tout le 80/443 → on bloque tout, y compris le trafic légitime
Solutions de contournement potentielles
En attendant que Cilium supporte toFQDNs dans les policies egressDeny, voici quelques approches alternatives que vous pourriez envisager :
Trouver un moyen de désactiver la télémétrie directement dans l’app
C’est la meilleure option, mais malheureusement pas toujours sur la table.
Blocage basé sur le DNS
Configurer le serveur DNS pour retourner NXDOMAIN pour les domaines de télémétrie, comme un serveur pi-hole personnel le ferait avec les pubs. L’application échouera à résoudre le domaine et n’enverra pas de données.
Utiliser egressDeny basé sur les IPs (avec un overhead de maintenance)
Résoudre les FQDNs de télémétrie vers leurs plages IP actuelles et les bloquer avec egressDeny :
egressDeny:
- toCIDRSet:
- cidr: 203.0.113.0/24 # Exemple de plage IP de télémétrie
toPorts:
- ports:
- port: "443"
Si la liste n’évolue pas trop souvent, c’est une bonne option.
OK, mais imaginons qu’il n’y ait pas de trafic légitime. Peut-on utiliser la fonctionnalité pour ajouter un log sur le trafic droppé ?
Malheureusement non, pas pour le moment.
Il y a un bug dans cette nouvelle fonctionnalité de Cilium qui ne log le champ policy_log que sur les flux “autorisés”, pas sur les flux audit/dropped.
- Policy log does not work for DROPPED/AUDIT flow ouverte par mon collègue Nicolas :
When defining a CiliumNetworkPolicy with the spec.log field configured, I expect the relevant hubble flows to have the policy_log field. It works for allowed flow.
But for denied/audited flow resulting from the rule (implicit or explicit), policy_log is never available.
Note: I observe the same issue with
--print-policy-namesoption of hubble, the k8s:io.cilium.k8s.policy.derived-from label is not set for denied flows (but correctly set for allowed flows).
- et une autre issue [Hubble CLI] –print-policy-names flag does not do anything ouverte par quelqu’un d’autre.
Puisque 2 tickets sont ouverts et que les mainteneurs ont commencé à répondre dessus, on peut espérer que ce soit corrigé un jour.
Conclusion
Dans notre cas d’usage, nous n’avons finalement pas utilisé cette nouvelle fonctionnalité de Cilium. Mais donner la possibilité d’ajouter des détails (et permettre de filtrer dessus également) est toujours une feature sympa.
Un grand merci à mon collègue Nicolas Nativel, qui a fait la majorité du travail autour des CiliumNetworkPolicies, incluant les dashboards, le travail exploratoire sur cette fonctionnalité, et a pris le temps de créer l’issue sur le repo Cilium.