<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>monitoring on Zwindler's Reflection</title><link>https://blog.zwindler.fr/en/categories/monitoring/</link><description>Recent content in monitoring on Zwindler's Reflection</description><generator>Hugo -- gohugo.io</generator><language>en</language><copyright>Licensed under CC BY-SA 4.0</copyright><lastBuildDate>Mon, 03 Nov 2025 12:00:00 +0200</lastBuildDate><atom:link href="https://blog.zwindler.fr/en/categories/monitoring/index.xml" rel="self" type="application/rss+xml"/><item><title>Cilium’s new policy log field: our use case</title><link>https://blog.zwindler.fr/en/2025/11/03/ciliums-new-policy-log-field-our-use-case/</link><pubDate>Mon, 03 Nov 2025 12:00:00 +0200</pubDate><guid>https://blog.zwindler.fr/en/2025/11/03/ciliums-new-policy-log-field-our-use-case/</guid><description>&lt;img src="https://blog.zwindler.fr/2025/10/cilium-hubble.webp" alt="Featured image of post Cilium’s new policy log field: our use case" /&gt;&lt;h2 id="tldr"&gt;TL;DR
&lt;/h2&gt;&lt;p&gt;Cilium 1.18 added a &lt;code&gt;log&lt;/code&gt; field to CiliumNetworkPolicies to tag flows with custom labels. Great for filtering out expected blocked traffic from your monitoring dashboards!&lt;/p&gt;
&lt;p&gt;But there&amp;rsquo;s a catch, unrelated to this feature, that made this irrelevant in our use case: you can&amp;rsquo;t use it with &lt;code&gt;egressDeny&lt;/code&gt; + &lt;code&gt;toFQDNs&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;AND, there is a bug, that makes the &amp;ldquo;log&amp;rdquo; only visible on &amp;ldquo;allowed&amp;rdquo; traffic.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s why we ran into this wall and what we learned.&lt;/p&gt;
&lt;h2 id="the-problem-monitoring-all-the-things-but-not-too-much"&gt;The problem: monitoring all the things (but not too much)
&lt;/h2&gt;&lt;p&gt;Like any good ops team should, we monitor our Kubernetes cluster network flows using Hubble. We (mostly my colleague Nicolas Nativel) push all &lt;code&gt;AUDIT&lt;/code&gt; and &lt;code&gt;DROPPED&lt;/code&gt; flows to a dashboard so we can quickly spot when something&amp;rsquo;s blocked and decide:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Is this legitimate? → Open the flow&lt;/li&gt;
&lt;li&gt;Is this suspicious? → Sound the alarm 🚨&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2025/10/cilium-hubble.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;This works pretty well&amp;hellip; until you start &lt;strong&gt;explicitly&lt;/strong&gt; blocking things that you &lt;em&gt;know&lt;/em&gt; should be blocked.&lt;/p&gt;
&lt;p&gt;In our case, we wanted to prevent a third-party application from phoning home with its &amp;ldquo;telemetry&amp;rdquo; (yeah, let&amp;rsquo;s call it that 😏). We&amp;rsquo;re talking about calls to external tracking domains.&lt;/p&gt;
&lt;p&gt;The issue? If we just block these flows, they&amp;rsquo;ll show up as &lt;code&gt;DROPPED&lt;/code&gt; in Hubble, trigger our monitoring, and we&amp;rsquo;ll end up with alerts for something we &lt;em&gt;intentionally&lt;/em&gt; blocked.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s noise we don&amp;rsquo;t want.&lt;/p&gt;
&lt;h2 id="enter-cilium-118s-policy-log-field"&gt;Enter Cilium 1.18&amp;rsquo;s policy log field
&lt;/h2&gt;&lt;p&gt;Good news! Cilium 1.18 introduced exactly what we needed: the ability to add custom log fields to your network policies.&lt;/p&gt;
&lt;p&gt;Check out the &lt;a class="link" href="https://isovalent.com/blog/post/cilium-1-18/#hubble-flow-policy-log-field" target="_blank" rel="noopener"
&gt;official announcement&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The idea is simple: you add a &lt;code&gt;log&lt;/code&gt; field to your CiliumNetworkPolicy:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;cilium.io/v2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;CiliumNetworkPolicy&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;my-policy&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;endpointSelector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;matchLabels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;my-app&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;egress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;toFQDNs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;matchName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;example.com&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;log&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;my-custom-log-tag&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then, when you observe flows in Hubble, you can &lt;strong&gt;filter them out&lt;/strong&gt; using &lt;a class="link" href="https://kubernetes.io/docs/reference/using-api/cel/" target="_blank" rel="noopener"
&gt;CEL (Common Expression Language)&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hubble observe &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --verdict AUDIT &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --not &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --cel-expression &lt;span class="s2"&gt;&amp;#34;(_flow.policy_log.endsWith(&amp;#39;my-custom-log-tag&amp;#39;))&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --print-raw-filters
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Output:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;allowlist:
- &amp;#39;{&amp;#34;verdict&amp;#34;:[&amp;#34;AUDIT&amp;#34;]}&amp;#39;
denylist:
- &amp;#39;{&amp;#34;experimental&amp;#34;:{&amp;#34;cel_expression&amp;#34;:[&amp;#34;(_flow.policy_log.endsWith(&amp;#39;&amp;#39;my-custom-log-tag&amp;#39;&amp;#39;))&amp;#34;]}}&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Perfect! &lt;strong&gt;This is exactly what we need&lt;/strong&gt;. We can now tag our &amp;ldquo;expected blocks&amp;rdquo; and exclude them from our monitoring.&lt;/p&gt;
&lt;h2 id="the-plan-block-telemetry-elegantly"&gt;The plan: block telemetry elegantly
&lt;/h2&gt;&lt;p&gt;Armed with this new feature, we crafted our strategy:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Use &lt;code&gt;egressDeny&lt;/code&gt; to explicitly block telemetry domains&lt;/li&gt;
&lt;li&gt;Add a custom log field: &lt;code&gt;app-explicit-traffic-blocked&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Configure Hubble to filter out flows with this tag&lt;/li&gt;
&lt;li&gt;Profit! 🎉&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here&amp;rsquo;s what we tried:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;cilium.io/v2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;CiliumNetworkPolicy&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;app-external-block-policy&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;my-namespace&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;endpointSelector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;matchLabels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;my-app&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# note: egressDeny takes precedence over egress rules&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# https://docs.cilium.io/en/stable/security/policy/language/#deny-policies&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;egressDeny&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Block all external traffic and log it with an arbitrary log field&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# This is used to prevent the app from sending telemetry data externally &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# without triggering an AUDIT/DROPPED alert&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# feature added in cilium 1.18.0 https://github.com/cilium/cilium/pull/39902&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;toFQDNs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;matchPattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;*.telemetry.example.com&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;toPorts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;443&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;protocol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;TCP&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;80&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;protocol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;TCP&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;log&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;app-explicit-traffic-blocked&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This should work, right? We&amp;rsquo;re using &lt;code&gt;egressDeny&lt;/code&gt; (which &lt;a class="link" href="https://docs.cilium.io/en/stable/security/policy/language/#deny-policies" target="_blank" rel="noopener"
&gt;takes precedence over other allow rules&lt;/a&gt;, for legitimate calls, which is good!), and we&amp;rsquo;re tagging it with our custom log.&lt;/p&gt;
&lt;h2 id="reality-check-you-cant-have-nice-things"&gt;Reality check: you can&amp;rsquo;t have nice things
&lt;/h2&gt;&lt;p&gt;And then&amp;hellip; &lt;strong&gt;patatra&lt;/strong&gt; (as we say in French 🇫🇷).&lt;/p&gt;
&lt;p&gt;While reading the &lt;a class="link" href="https://docs.cilium.io/en/stable/security/policy/language/#deny-policies" target="_blank" rel="noopener"
&gt;Cilium documentation on deny policies&lt;/a&gt;, we stumbled upon this little gem:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Deny policies do not support:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;policy enforcement at L7, i.e., specifically denying an URL&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;toFQDNs&lt;/strong&gt;, i.e., specifically denying traffic to a specific domain name.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;Wait, what?&lt;/p&gt;
&lt;p&gt;You &lt;strong&gt;cannot&lt;/strong&gt; use &lt;code&gt;toFQDNs&lt;/code&gt; with &lt;code&gt;egressDeny&lt;/code&gt;. Our entire plan just collapsed 😱.&lt;/p&gt;
&lt;h2 id="why-this-is-a-problem"&gt;Why this is a problem
&lt;/h2&gt;&lt;p&gt;The issue is the precedence model in Cilium:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;egressDeny&lt;/code&gt; rules take precedence over &lt;code&gt;egress&lt;/code&gt; rules (by design, and that&amp;rsquo;s good!)&lt;/li&gt;
&lt;li&gt;But if we use &lt;code&gt;egressDeny&lt;/code&gt; without &lt;code&gt;toFQDNs&lt;/code&gt;, we have to block by IP or CIDR&lt;/li&gt;
&lt;li&gt;These telemetry services &lt;em&gt;probably&lt;/em&gt; use dynamic IPs for their endpoints (good luck maintaining a list&amp;hellip;)&lt;/li&gt;
&lt;li&gt;If we block all 80/443 traffic in &lt;code&gt;egressDeny&lt;/code&gt;, we can&amp;rsquo;t make exceptions for legitimate traffic in &lt;code&gt;egress&lt;/code&gt; rules because&amp;hellip; &lt;em&gt;deny&lt;/em&gt; takes precedence to &lt;em&gt;allow&lt;/em&gt;!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We&amp;rsquo;re stuck between a rock and a hard place:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;egress&lt;/code&gt; with &lt;code&gt;toFQDNs&lt;/code&gt; → works, but we can&amp;rsquo;t &lt;strong&gt;deny&lt;/strong&gt;, only allow other traffic&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;egressDeny&lt;/code&gt; with IPs → we&amp;rsquo;ll be playing whack-a-mole with rotating IP ranges&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;egressDeny&lt;/code&gt; to block all 80/443 → we block everything, including legitimate traffic&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="potential-workarounds"&gt;Potential workarounds
&lt;/h2&gt;&lt;p&gt;While waiting for Cilium to support &lt;code&gt;toFQDNs&lt;/code&gt; in &lt;code&gt;egressDeny&lt;/code&gt; policies, here are some alternative approaches you might consider:&lt;/p&gt;
&lt;h3 id="find-a-way-to-disable-telemetry-in-the-app-directly"&gt;Find a way to disable telemetry in the app directly
&lt;/h3&gt;&lt;p&gt;That&amp;rsquo;s the best option but sadly not always on the table.&lt;/p&gt;
&lt;h3 id="dns-based-blocking"&gt;DNS-based blocking
&lt;/h3&gt;&lt;p&gt;Bend the DNS server to return NXDOMAIN for telemetry domains, like a personal &lt;a class="link" href="https://pi-hole.net/" target="_blank" rel="noopener"
&gt;pi-hole&lt;/a&gt; server would do with ads. The application will fail to resolve the domain and won&amp;rsquo;t send data.&lt;/p&gt;
&lt;h3 id="use-ip-based-egressdeny-with-maintenance-overhead"&gt;Use IP-based egressDeny (with maintenance overhead)
&lt;/h3&gt;&lt;p&gt;Resolve the telemetry FQDNs to their current IP ranges and block them with &lt;code&gt;egressDeny&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;egressDeny&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;toCIDRSet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;cidr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;203.0.113.0&lt;/span&gt;&lt;span class="l"&gt;/24 &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Example telemetry IP range&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;toPorts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;443&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If the list doesn&amp;rsquo;t evolve too often, this is a good option.&lt;/p&gt;
&lt;h2 id="ok-but-lets-assume-there-is-no-legitimate-traffic-can-we-use-the-feature-to-add-a-log-on-dropped-traffic"&gt;Ok, but let&amp;rsquo;s assume there is no legitimate traffic. Can we use the feature to add a log on dropped traffic?
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Sadly no, not right now.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;There is a bug in this new Cilium feature that only logs the &lt;code&gt;policy_log&lt;/code&gt; field on &amp;ldquo;allowed&amp;rdquo; flows, not on audit/dropped flows.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/cilium/cilium/issues/42044" target="_blank" rel="noopener"
&gt;Policy log does not work for DROPPED/AUDIT flow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;But for denied/audited flow resulting from the rule (implicit or explicit), policy_log is never available.&lt;/p&gt;
&lt;p&gt;Note: I observe the same issue with &lt;code&gt;--print-policy-names&lt;/code&gt; option of hubble, the k8s:io.cilium.k8s.policy.derived-from label is not set for denied flows (but correctly set for allowed flows).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;and a related issue &lt;a class="link" href="https://github.com/cilium/cilium/issues/41912" target="_blank" rel="noopener"
&gt;[Hubble CLI] &amp;ndash;print-policy-names flag does not do anything&lt;/a&gt; opened by someone else.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Since 2 tickets are opened and maintainers have started to acknowledge the issue, we can hope this will be fixed, though.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;In our use case, we finally didn&amp;rsquo;t use this new feature from Cilium, but adding details (and allowing filtering on them as well) is always nice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A shout-out to my colleague Nicolas Nativel, who did most of the work around CiliumNetworkPolicies, including the dashboards, exploratory work on this feature, and took the time to create the issue on the Cilium repository.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="references"&gt;References
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://isovalent.com/blog/post/cilium-1-18/#hubble-flow-policy-log-field" target="_blank" rel="noopener"
&gt;Cilium 1.18 announcement - Hubble flow policy log field&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.danielstechblog.io/ciliums-new-hubble-flow-policy-log-field/" target="_blank" rel="noopener"
&gt;Daniel&amp;rsquo;s Tech Blog - Cilium&amp;rsquo;s new Hubble flow policy log field&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://docs.cilium.io/en/stable/security/policy/language/#deny-policies" target="_blank" rel="noopener"
&gt;Cilium docs - Deny Policies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/cilium/cilium/pull/39902" target="_blank" rel="noopener"
&gt;GitHub PR #39902 - Add policy log field&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/cilium/cilium/issues/42044" target="_blank" rel="noopener"
&gt;GitHub Issue #42044 - Policy log does not work for DROPPED/AUDIT flow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Should we have containers ?</title><link>https://blog.zwindler.fr/en/2016/08/25/should-we-have-containers/</link><pubDate>Thu, 25 Aug 2016 16:00:21 +0000</pubDate><guid>https://blog.zwindler.fr/en/2016/08/25/should-we-have-containers/</guid><description>&lt;img src="https://blog.zwindler.fr/2016/08/Fullvirt_containers.webp" alt="Featured image of post Should we have containers ?" /&gt;&lt;h2 id="a-more-complete-answer-to-why-do-we-have-containers-by-fntlnz"&gt;A more complete answer to « Why do we have containers » by fntlnz
&lt;/h2&gt;&lt;p&gt;2 days ago, I stumbled apon &lt;em&gt;fntlnz&lt;/em&gt; article &lt;a class="link" href="https://web.archive.org/web/20161026190150/http://blog.fntlnz.wtf/post/why-containers/" target="_blank" rel="noopener"
&gt;Why do we have containers. (dead link, using Internet Archive)&lt;/a&gt; The article had been reposted by Docker official Twitter account and the first thing that struck me was the displayed image : a schema comparing full virtualization, para virtualization and linux containers.&lt;/p&gt;
&lt;p&gt;And of course, I say this because I didn’t entirely agree with it ;-). But then I read the article and I found many good points.&lt;/p&gt;
&lt;p&gt;So I decided to write « Should we have containers ? » as an answer to develop my remarks a little.&lt;/p&gt;
&lt;h2 id="full-virtualization-vs-paravirtualization-vs-os-level-virtualization"&gt;Full virtualization vs paravirtualization vs OS level virtualization
&lt;/h2&gt;&lt;p&gt;First, I’ll go with the schema. I know the point was to provide a really simple schema to help understand the differences between virtualization types but I don’t think it should be presented like that.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2016/08/Virtualization.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Hypervisor and host OS are both displayed in the full/para. I find this be misleading. Of course there is a Host OS and a virtualization engine on top of it. But most of the time, either the OS is really thin (ESXi fits in 8GB SD cards and runs nothing but virtualization) or the virt engine is a part of the kernel (KVM, Xen) just like LXC is a part of the Linux kernel.&lt;/p&gt;
&lt;p&gt;Also, the bins/libs are only displayed on OS level virt. This is interesting to put in the « host level virtualization schema » but it should be added on full and para as well because this is one of the advantages host level virt. has over full/para.&lt;br&gt;
Even if your VMs are all identicals, bins and libs have to be present (copied) in each VMs. On contrary, in OS Level virt, you CAN share identicals bins/libs between containers, thus reducing the storage footprint of the virtualized environment.&lt;/p&gt;
&lt;p&gt;That’s how I would have presented it&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2016/08/Fullvirt_containers.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;You can see that OS level virtualization and Hypervisors are not so different on this simplified view. The differences appear more clearly when you add x86 Security Rings (0-&amp;gt;3). The « overhead » of full virtualization comes from the calls that guest OSes do to hypervisor pilots and the translation of CPU instructions.&lt;/p&gt;
&lt;p&gt;On a side note, I put Docker on the left to show that it’s basically an administration suite that helps you run LXC containers&lt;/p&gt;
&lt;h2 id="the-rest-of-the-analysis"&gt;The rest of the analysis
&lt;/h2&gt;&lt;h3 id="security"&gt;Security
&lt;/h3&gt;&lt;p&gt;When talking about containers, the security point is often raised.&lt;/p&gt;
&lt;p&gt;VMs are really hermetics. Taking control of the hypervisor after hacking a VM should be MUCH harder than taking control of the host OS if you are inside a container with whom the kernel is shared.&lt;/p&gt;
&lt;p&gt;But I agree with &lt;strong&gt;fntlnz&lt;/strong&gt;: having to patch only one kernel for all your virtual environments instead of having to patch ALL kernels of your virtual machines is actually a good point.&lt;/p&gt;
&lt;p&gt;I encourage you to read &lt;a class="link" href="https://web.archive.org/web/20200925215559/https://2015.rmll.info/IMG/pdf/containers_docker_and_security__state_of_the_union.pdf" target="_blank" rel="noopener"
&gt;this (dead link, using Internet Archive)&lt;/a&gt; from Jérôme Petazzoni if you’d like a good presentation on the subject.&lt;/p&gt;
&lt;h3 id="performance"&gt;Performance
&lt;/h3&gt;&lt;p&gt;A point that’s not really developed in &lt;em&gt;fntlnz&lt;/em&gt; article is performance, but I read it/ear it &lt;strong&gt;all the time&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;General belief is that VMs are slow. That was true in the « early days » of x86 virtualization, because of the limitations of the x86 architectures which didn’t allow efficient OS virtualisation. Overhead for CPU instructions was known to be of about 15% (which is enormous). Thus, running processes directly on the OS are believed to be more efficent.&lt;/p&gt;
&lt;p&gt;But nowadays, modern hypervisors on modern hardware nearly generates no overhead at all over CPU and RAM consumption. You could argue that guest OSes consume ressources while containers don’t. But let’s be honest: with the 32 GB RAM tomcat instances I regularly see, don’t say to me that a Linux guest OS CPU&amp;amp;RAM consumption is significative.&lt;/p&gt;
&lt;p&gt;The real difference is the on the storage consumption. First, for N VMs, you save yourself N*xGB of guest OS files. If your Linux templates are good, you don’t consume that much (a few GB at most) but this can add up when we are talking about thousands of instances. [Edit jan. 2020]Not so true today if you host your own hardware and have so deduplicated storage[/Edit]&lt;/p&gt;
&lt;p&gt;From there, you still have some margin for gains, mostly through &lt;strong&gt;Copy on write and union filesystems&lt;/strong&gt; :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You make a modification to an official tomcat image to add you configuration files ? Only the delta will be stored.&lt;/li&gt;
&lt;li&gt;You launch thousands of containers from this image ? You only consume the container image once.&lt;/li&gt;
&lt;li&gt;The real data (user data, business data) is of course the same for both architectures&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="so-should-i-use-containers-"&gt;So, should I use containers ?
&lt;/h2&gt;&lt;p&gt;The problem here is that’s not the real question. &lt;strong&gt;Do you need containers ?&lt;/strong&gt; Here’s &lt;em&gt;fntlnz&lt;/em&gt; point of view.**&lt;br&gt;
**&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;But if at some extent your motivation consist in &lt;strong&gt;decreasing costs related to full virtualization overheads&lt;/strong&gt; while allowing &lt;strong&gt;developers to ship, develop and test code faster&lt;/strong&gt; we already found two.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;fntlnz&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;I won’t tell you not to use containers or Docker. Truth be told, we use it in dev and production. Docker is awesome.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Yes, Docker reduces overhead of full virtualization. Storage mostly, but it highly depends on your workload. For example if you have the need for millions of really tiny instances, then Guest OS overhead is significative  in full virtualization.&lt;/li&gt;
&lt;li&gt;Yes, Docker allows developers to ship, develop and test code faster. You still have to change your way to develop yours app but Docker and DevOps is the new thing. Better get on with it ;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But you shouldn’t use Docker just because of that. Docker containers need you to change your philosophy toward applications and administration.&lt;/p&gt;
&lt;h3 id="shared-kernel--same-kernel-"&gt;Shared kernel = same kernel~~ ~~
&lt;/h3&gt;&lt;p&gt;You need to understand that you’ll share a kernel. For a long time that ment a 100% Linux only which could be problematic in some cases. I just realized that Windows containers exists in Windows 2016 tech preview. And Solaris is coming.&lt;/p&gt;
&lt;p&gt;But independantly of platforms, you still you share a kernel. If you want Linux containers, you’ll need Linux Docker nodes. If you need Windows containers, you’ll have to have Windows Docker nodes.&lt;/p&gt;
&lt;h3 id="non-persistantcloud-ready-instances"&gt;Non-persistant/cloud-ready instances
&lt;/h3&gt;&lt;p&gt;Docker containers are by design non-persistant and by best practice single-process. This isn’t mandatory of course, but Docker works better that way.&lt;/p&gt;
&lt;p&gt;The whole idea is to help you make « cloud-ready » &amp;amp; atomic applications that don’t store anything in the container itself (persistant storage is presented to the container but outside it). This way, you can scale out really quickly: you just start up a few more instances if you need more firepower.&lt;/p&gt;
&lt;p&gt;This also modifies the way you do systems/applications administration. A container/application is spinning out of control ? Don’t try to repair it.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Kill it&lt;/li&gt;
&lt;li&gt;Launch a new one&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The fact that persistant storage is handled outside the containers also helps you to deal with lifecycle management.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Update your application inside the docker image&lt;/li&gt;
&lt;li&gt;Shutdown the old container&lt;/li&gt;
&lt;li&gt;Start the new container with the new image, plugged to the persistant storage&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All that requires of course applications that have been designed this way! This is a massive change in philosophy and old monolithic apps on a Docker platform won’t work as well as microservices will.&lt;/p&gt;
&lt;h3 id="system-administration"&gt;System administration
&lt;/h3&gt;&lt;p&gt;I don’t say solutions don’t exist because they do.&lt;/p&gt;
&lt;p&gt;But starting up and shutting down containers on the fly will give headaches to your « old school Nagios » monitoring administrator. What could be done easily in the physical/VM world (installing an agent in the template), each of those having an IP adress to query for health checks is a lot easier than checking on a single host a highly volatile number of isolated processes.&lt;/p&gt;
&lt;p&gt;You’ll probably have to adapt and use tools that are designed to handle such cases.&lt;/p&gt;
&lt;p&gt;Backup administrator may find it confusing at first to make application consistant data backups when applications are running in an variable number of containers.&lt;/p&gt;
&lt;p&gt;And so on&amp;hellip;&lt;/p&gt;
&lt;h2 id="final-thoughts"&gt;Final thoughts
&lt;/h2&gt;&lt;p&gt;Yes, you should definitely &lt;em&gt;look into&lt;/em&gt; containers. They are mature, stable, a great new way to speed up development and contribute to save money on hardware.&lt;/p&gt;
&lt;p&gt;But be aware that VMs and containers don’t answer all usecases; and that you won’t migrate all your applications overnight!&lt;/p&gt;</description></item><item><title>Create a plugin for Thruk using Thruk API, and work around permission issues</title><link>https://blog.zwindler.fr/en/2014/08/19/create-a-plugin-for-thruk-using-thruk-api-and-work-around-permission-issues/</link><pubDate>Tue, 19 Aug 2014 09:13:19 +0000</pubDate><guid>https://blog.zwindler.fr/en/2014/08/19/create-a-plugin-for-thruk-using-thruk-api-and-work-around-permission-issues/</guid><description>&lt;img src="https://blog.zwindler.fr/2014/08/perl.gif" alt="Featured image of post Create a plugin for Thruk using Thruk API, and work around permission issues" /&gt;&lt;p&gt;A friend of mine came to me with a problem. During one of his Shinken/Thruk deployment, he came with the idea of writing a plugin that could take advantage of Thruk statistics to graph the load of the monitoring engine.&lt;/p&gt;
&lt;p&gt;Though you can get this information from Nagios itself through nagiostats, it’s a bit more difficult with the couple Thruk/Shinken. You could emulate Nagios by adding the Shinken &lt;em&gt;old-cgi&lt;/em&gt; legacy module, or you could an &lt;em&gt;authenticated wget&lt;/em&gt; the web page of Thruk and parse it, but let’s face it, these « solutions » aren’t pretty. I guess that the prettiest one should be to plug into Shinken, and maybe I’ll cover this later, because I think it’s the best solution, but this may need a little « dev effort ».&lt;/p&gt;
&lt;p&gt;In order to avoid some useless effort, my friend asked Thruk developers how to do it the way Thruk does it. The main dev guided him to the « thruk » command line, which basically just generates the web page, but in a cleaner way as this is done internaly. You can get more information about Thruk command line with a simple « &lt;em&gt;thruk &amp;ndash;help&lt;/em&gt; » in your shell.&lt;/p&gt;
&lt;p&gt;With this information, my friend developped a perl script (because he is a perl addict ;-) ) to parse this page and get the statistics he needed. But when he deployed it *shock*: he couldn’t see a thing INSIDE the Thruk console.&lt;/p&gt;
&lt;p&gt;The main problem here isn’t the fact that the script doesn’t seem to work, but rather the lack of error output.&lt;/p&gt;
&lt;p&gt;When first idea was to add the shinken user in &lt;em&gt;apache&lt;/em&gt; group. This did help to make it work for the shinken user in shell, but for the web page of shinken/thruk it didn’t help.&lt;/p&gt;
&lt;p&gt;To reproduce, I wrote a really simple script&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;#/bin/perl
#Print your page with thruk CLI, and display error output with 2&amp;gt;&amp;amp;1
print `/usr/bin/thruk -A thrukadmin extinfo.cgi?type=4 2&amp;gt;&amp;amp;1 `;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Though now the output was correct (lots of generated HTML jibber jabber) in the shell, the error is still displayed in Thruk&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;open file /etc/thruk/thruk_local.conf failed (id: uid=500(shinken) gid=500(shinken)
groups=500(shinken),0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023,
pwd: /var/run/shinken): at /usr/share/thruk/lib/Thruk/Utils/CLI.pm line 272.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;AH-AH ! So, basically what I did was to add this at the first line of my script, to help me understand those groups/permissions issues.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;#/bin/perl
print `id shinken`;
#Print your page with thruk CLI, and display error output with 2&amp;gt;&amp;amp;1
print `/usr/bin/thruk -A thrukadmin extinfo.cgi?type=4 2&amp;gt;&amp;amp;1 `;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here is the output&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;uid=500(shinken) gid=500(shinken) groups=500(shinken),48(apache)
open file /etc/thruk/thruk_local.conf failed (id: uid=500(shinken) gid=500(shinken)
groups=500(shinken),0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023,
pwd: /var/run/shinken): at /usr/share/thruk/lib/Thruk/Utils/CLI.pm line 272.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is what helped me understand what was really happening here. The groups were &lt;em&gt;shinken&lt;/em&gt; and &lt;em&gt;root&lt;/em&gt;, but there where no mention of &lt;em&gt;apache&lt;/em&gt;, even though I had just added apache to shinken and restarted the whole lot.&lt;/p&gt;
&lt;p&gt;In fact, the groups are set at another level : during the deamonization. By reading the files, I managed to find 3 places where shinken can set it’s own group :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;/etc/default/shinken&lt;/em&gt;, but only used for run and var directories creation (first launch)&lt;/li&gt;
&lt;li&gt;&lt;em&gt;/etc/shinken/shinken.cfg&lt;/em&gt;, only the arbiter&lt;/li&gt;
&lt;li&gt;&lt;em&gt;/etc/daemons/*.ini&lt;/em&gt;, for the daemons&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You need to have read access for these 3 files (for my setup anyway)&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ll /etc/thruk/thruk*
-rw-r--r--. 1 apache apache 25933 17 mai 19:13 /etc/thruk/thruk.conf
-rw-rw----. 1 apache apache 671 18 août 15:36 /etc/thruk/thruk_local.conf
ll /var/lib/thruk/secret.key
-rw-r-----. 1 apache apache 32 13 août 13:13 /var/lib/thruk/secret.key
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Opening theses file to « &lt;em&gt;other&lt;/em&gt; » is obviously not an option, and I not a fan of changing the group of these conf files, even though this can/will work.&lt;/p&gt;
&lt;p&gt;So, as of today, my favorite working solution (I’m still searching for a better one, and maybe I’ll switch to Shinken integration instead of Thruk) is to change &lt;em&gt;shinken poller&lt;/em&gt; group in &lt;em&gt;poller.ini&lt;/em&gt; to &lt;em&gt;apache&lt;/em&gt; and restart the daemon. This way, the poller and the associated plugins can access those 3 files and your Thruk API will work in Thruk console as it does in the shell.&lt;/p&gt;</description></item></channel></rss>