<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>nginx on foosel.net</title><link>https://foosel.net/tags/nginx/</link><description>Recent content in nginx on foosel.net</description><generator>Hugo</generator><language>en-us</language><copyright>Gina Häußge (foosel)</copyright><lastBuildDate>Mon, 01 Jul 2024 00:00:00 +0000</lastBuildDate><atom:link href="https://foosel.net/tags/nginx/feed.xml" rel="self" type="application/rss+xml"/><item><title>TIL: How to check for cloud IPs in nginx</title><link>https://foosel.net/til/2024-07-01-how-to-check-for-cloud-ips-in-nginx/</link><pubDate>Mon, 01 Jul 2024 00:00:00 +0000</pubDate><guid>https://foosel.net/til/2024-07-01-how-to-check-for-cloud-ips-in-nginx/</guid><description>&lt;p&gt;I&amp;rsquo;m currently busy mitigating a &lt;a href="https://octoprint.org/blog/2024/06/28/stats-manipulation/"&gt;stats manipulation on OctoPrint&lt;/a&gt;, and one of the steps I&amp;rsquo;m taking is blocking off several cloud options from accessing the tracking endpoint - and &lt;em&gt;only&lt;/em&gt; that.&lt;/p&gt;
&lt;p&gt;Since we are talking about several thousand of IPs here in at least 1.5k of CIDR ranges, I was looking for the best way to do that that wouldn&amp;rsquo;t cause a lot of performance impact - the tracking server needs to be fast.&lt;/p&gt;</description><content:encoded><![CDATA[<p>I&rsquo;m currently busy mitigating a <a href="https://octoprint.org/blog/2024/06/28/stats-manipulation/">stats manipulation on OctoPrint</a>, and one of the steps I&rsquo;m taking is blocking off several cloud options from accessing the tracking endpoint - and <em>only</em> that.</p>
<p>Since we are talking about several thousand of IPs here in at least 1.5k of CIDR ranges, I was looking for the best way to do that that wouldn&rsquo;t cause a lot of performance impact - the tracking server needs to be fast.</p>
<p>A list of all CIDR ranges with <code>deny</code> turned out to not work thanks to my endpoint definition in nginx using <code>return</code> statements, and those are apparently evaluated before <code>allow</code> and <code>deny</code> statements. But then I got the hint to look at the <a href="https://nginx.org/en/docs/http/ngx_http_geo_module.html"><code>geo</code> module</a> and with that it was easy to build a map of IP ranges that should be matched and just combining that with an <code>if</code>.</p>
<p>For a first test I created a converter for <a href="https://github.com/PodderApps/ipcat">this list of IP ranges</a>, filtering for the really big players:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e">#!/bin/bash
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>URL<span style="color:#f92672">=</span>https://raw.githubusercontent.com/PodderApps/ipcat/main/datacenters.csv
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#f92672">[</span> $1 !<span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;&#34;</span> <span style="color:#f92672">]</span>; <span style="color:#66d9ef">then</span>
</span></span><span style="display:flex;"><span>	DATA<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>curl -s $URL | grep -E <span style="color:#e6db74">&#34;</span>$1<span style="color:#e6db74">&#34;</span><span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">else</span>
</span></span><span style="display:flex;"><span>	DATA<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>curl -s $URL<span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">fi</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>echo <span style="color:#e6db74">&#39;geo $is_cloud {&#39;</span>
</span></span><span style="display:flex;"><span>echo <span style="color:#e6db74">&#39;    default 0;&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">while</span> IFS<span style="color:#f92672">=</span> read -r line; <span style="color:#66d9ef">do</span>
</span></span><span style="display:flex;"><span>	start_ip<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>echo $line | cut -d, -f1<span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>	end_ip<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>echo $line | cut -d, -f2<span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>	comment<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>echo $line | cut -d, -f3<span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	script<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;import ipaddress; start_ip=ipaddress.IPv4Address(\&#34;</span>$start_ip<span style="color:#e6db74">\&#34;); end_ip=ipaddress.IPv4Address(\&#34;</span>$end_ip<span style="color:#e6db74">\&#34;); print(next(ipaddress.summarize_address_range(start_ip, end_ip)))&#34;</span>
</span></span><span style="display:flex;"><span>	cidr<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>python3 -c <span style="color:#e6db74">&#34;</span>$script<span style="color:#e6db74">&#34;</span><span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	echo <span style="color:#e6db74">&#34;    </span>$cidr<span style="color:#e6db74"> 1; # </span>$comment<span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">done</span> <span style="color:#f92672">&lt;&lt;&lt;</span> <span style="color:#e6db74">&#34;</span>$DATA<span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>echo <span style="color:#e6db74">&#39;}&#39;</span>
</span></span></code></pre></div><p>Calling this like this then created a <code>conf</code> file:</p>
<pre tabindex="0"><code class="language-prompt" data-lang="prompt">$ sudo ./generate_is_cloud_map &#34;AWS|DigitalOcean|Google&#34; &gt; /etc/nginx/snippets/is-cloud.conf
$ cat /etc/nginx/snippets/ip-cloud.conf
geo $is_cloud {
    default 0;
    3.0.0.0/15; # Amazon AWS
    # ...
}
</code></pre><p>which I then could use in my nginx <code>location</code> config through an include and an <code>if</code> statement:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#66d9ef">include</span> snippets/is-cloud.conf;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># ...
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">server</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">location</span> <span style="color:#e6db74">/mylocation</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">if</span> <span style="color:#e6db74">(</span>$is_cloud = <span style="color:#ae81ff">1</span><span style="color:#e6db74">)</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">return</span> <span style="color:#ae81ff">403</span>;
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># ...
</span></span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>For now this seems to work. I&rsquo;m going to give this a day or so and then look into further IP sources and also blocking off the IPv6 ranges.</p>
]]></content:encoded></item></channel></rss>