<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"><channel><title>LINICKX.com</title><link>https://www.linickx.com/</link><description></description><lastBuildDate>Sat, 27 Sep 2025 12:08:00 +0100</lastBuildDate><item><title>DNSCrypt Proxy on Home Assistant</title><link>https://www.linickx.com/dnscrypt-proxy-on-home-assistant</link><description>&lt;p&gt;I have published a Home Assistant Addon for DNSCrypt Proxy.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;TLDR:&lt;/code&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In the HA Addon-store add my repository 👉🏻 https://github.com/linickx/ha-addons&lt;/li&gt;
&lt;li&gt;Install DNSCrypt Proxy (&lt;em&gt;and Start the addon&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;Edit &lt;code&gt;/addon_configs/ba53f40c-dnscrypt-proxy/dnscrypt-proxy.toml&lt;/code&gt; to suit your needs (&lt;em&gt;restart addon to take effect&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;Profit?!&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h2&gt;What is DNSCrypt Proxy?&lt;/h2&gt;
&lt;p&gt;By default DNS traffic (&lt;em&gt;TCP &amp;amp; UDP 53&lt;/em&gt;) is not encrypted, i.e. visible to snoopers; not all devices, especially IoT devices support modern encrypted DNS protocols like &lt;a href="https://en.wikipedia.org/wiki/DNS_over_HTTPS"&gt;DoH&lt;/a&gt; or &lt;a href="https://en.wikipedia.org/wiki/DNS_over_TLS"&gt;DoT&lt;/a&gt; so DNSCrypt Proxy is a way of securing DNS for those devices -- they send clear text DNS to the proxy, it encrypts the traffic up to your chosen DNS provider.&lt;/p&gt;
&lt;h2&gt;Why DNSCrypt Proxy?&lt;/h2&gt;
&lt;p&gt;This is choice depending on your security preferences/requirements, do you need or want encrypted DNS? Generally encrypting as much of your traffic as you can is a good thing, but if you don't agree that's ok:&lt;/p&gt;
&lt;p&gt;&lt;img src="/files/2025/09/threat-model-sm.png" class="img-fluid" &gt;&lt;/p&gt;
&lt;h2&gt;How to install DNSCrypt Proxy.&lt;/h2&gt;
&lt;p&gt;DNSCrypt Proxy is presented as an addon, which are Home Assistant containers, get 3rd party addons you first have to setup a repository,&lt;/p&gt;
&lt;h3&gt;Step 1 - My Addon Repository&lt;/h3&gt;
&lt;p&gt;Within Home Assistant, perform the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;Settings -&amp;gt; Add-ons -&amp;gt; ADD-ON STORE&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;In the top right &lt;code&gt;3x Dots&lt;/code&gt;, select &lt;code&gt;Repositories&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Add https://github.com/linickx/ha-addons&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;... Using the same top right &lt;code&gt;3x Dots&lt;/code&gt;, click check for updates. It also doesn't hurt to refresh your browser.&lt;/p&gt;
&lt;h3&gt;Step 2 - Install&lt;/h3&gt;
&lt;p&gt;With a little luck, now in Home Assistant under &lt;code&gt;Settings -&amp;gt; Add-ons -&amp;gt; ADD-ON STORE&lt;/code&gt;, if you scroll to the bottom you should see this:&lt;/p&gt;
&lt;p&gt;&lt;img src="/files/2025/09/linickx-addons-dnscrypt.png" class="img-fluid" &gt;&lt;/p&gt;
&lt;p&gt;✨ Click it! ✨&lt;/p&gt;
&lt;p&gt;Then, click &lt;code&gt;Install&lt;/code&gt;, and once it's installed, click &lt;code&gt;Start&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;How to configure DNSCrypt Proxy.&lt;/h2&gt;
&lt;p&gt;DNSCrypt &lt;a href="https://github.com/DNSCrypt/dnscrypt-proxy/wiki/Configuration"&gt;configuration is a .toml file&lt;/a&gt;; the easiest way to edit it in Home Assistant is with &lt;a href="https://github.com/hassio-addons/addon-vscode"&gt;the VSCode Add-on&lt;/a&gt;, if your a terminal ninja use Vi in SSH.&lt;/p&gt;
&lt;p&gt;Edit 👉🏻 &lt;code&gt;/addon_configs/ba53f40c-dnscrypt-proxy/dnscrypt-proxy.toml&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;File Path Warning!&lt;/h3&gt;
&lt;p&gt;The DNSCrypt proxy runs as a container, the path you edit (from VSCode/SSH) is different to what the container sees, i.e.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/addon_configs/ba53f40c-dnscrypt-proxy/dnscrypt-proxy.toml&lt;/code&gt; == &lt;code&gt;/config/dnscrypt-proxy.toml&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;So, if you need to include any external files, such as blocklists the use the later &lt;em&gt;container&lt;/em&gt; path of &lt;code&gt;/config/&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;/Done!&lt;/h2&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Nick Bettison</dc:creator><pubDate>Sat, 27 Sep 2025 12:08:00 +0100</pubDate><guid isPermaLink="false">tag:www.linickx.com,2025-09-27:dnscrypt-proxy-on-home-assistant</guid><category>Security</category><category>HomeAssistant</category></item><item><title>IoT DNS Anomaly Detection with Home Detector</title><link>https://www.linickx.com/iot-dns-anomaly-detection-with-home-detector</link><description>&lt;p&gt;After implementing &lt;a href="https://www.linickx.com/honeypot-running-open-canary-on-home-assistant"&gt;an Open-Canary based home honeypot in part 1&lt;/a&gt;, for my research project I moved onto implementing a DNS based anomaly detection intrusion detection system ... try saying that 3 times fast after a few pints! 😆&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;📝 This post assumes that you already &lt;a href="https://github.com/linickx/HomeDetector/blob/main/docs/INSTALL.md"&gt;have Home Detector installed&lt;/a&gt; on your Home Assistant server.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The idea is an internal, home network honeypot would only trigger if there was already a threat on your LAN, in order to &lt;em&gt;attempt&lt;/em&gt; to catch someone/something earlier I built a simple DNS anomaly detection engine; there's an important gotcha here, although the honeypot was low-effort-drop-in (plug-n-play), DNS inspection is a bit more &lt;em&gt;technical&lt;/em&gt; you will need a method of updating the DNS settings on your IoTs to make this work, either by changing your DHCP setting, doing something on your router, or by touching each device (if they support custom DNS that is!).&lt;/p&gt;
&lt;h2&gt;How it works&lt;/h2&gt;
&lt;p&gt;Home Detector acts as a local DNS forwarder, point your IoT/Smart-Things at it and for 30days it'll start recording the DNS lookups. After 30days, it'll consider what it saw as &lt;em&gt;the baseline&lt;/em&gt; and therefore anything new after that point will generate an alert.&lt;/p&gt;
&lt;p&gt;The baseline is per  &lt;em&gt;IOT thing&lt;/em&gt;, which can be a single host IP, a range of IPs or a subnet depending on your home setup, so you're going to need the real-source-ip of your &lt;em&gt;IOT thing&lt;/em&gt; hitting Home Assistant (The Home Detector Add-on)&lt;/p&gt;
&lt;h2&gt;Enable DNS Interception&lt;/h2&gt;
&lt;p&gt;The DNS interception is &lt;strong&gt;opt-in&lt;/strong&gt; which means there's 2x steps:&lt;/p&gt;
&lt;h3&gt;Step 1&lt;/h3&gt;
&lt;p&gt;The first is point your system at it, i.e. update the DNS settings of the IOT device to have Home Assistant's IP as a DNS server. &lt;/p&gt;
&lt;p&gt;How you do this will vary, you might be able to update your router to use Home Assistant as the DNS server for all your LAN. You might be able to change the DNS server on the IoT device it's self. Or if you cannot change DNS &lt;em&gt;anywhere&lt;/em&gt;, disable DHCP on your router and &lt;a href="https://github.com/home-assistant/addons/blob/master/dhcp_server/DOCS.md"&gt;install a DHCP server add-on&lt;/a&gt; and set the DNS server IP to that of Home Assistant.&lt;/p&gt;
&lt;h3&gt;Step 2&lt;/h3&gt;
&lt;p&gt;It's ok if &lt;em&gt;all&lt;/em&gt; your DNS hits Home Detector (Home Assistant) as by default, the add-on will pass through, not touching the responses and everything should continue to work.&lt;/p&gt;
&lt;p&gt;To enable the detection, under add-on settings, enable interception for specific IP addresses, like this:&lt;/p&gt;
&lt;p&gt;Tweak any other &lt;a href="https://github.com/linickx/ha-addons/blob/main/homedetector/DOCS.md"&gt;settings you fancy from the Docs&lt;/a&gt; as necessary.&lt;/p&gt;
&lt;h2&gt;Enjoy the DNS goodness&lt;/h2&gt;
&lt;p&gt;Within the Home Detector Dashboard, there are 3x views.&lt;/p&gt;
&lt;h3&gt;(i) The Alerts&lt;/h3&gt;
&lt;p&gt;By default, Home Detector will generate &lt;code&gt;dns-domain&lt;/code&gt; alerts, there are alarms for new &lt;em&gt;domains&lt;/em&gt; being queried, the SOA record is used to determine domain ownership.&lt;/p&gt;
&lt;p&gt;If you enabled &lt;em&gt;Detected on Host Query&lt;/em&gt; then Home Detector will generate &lt;code&gt;dns-query&lt;/code&gt; alerts, these are alarms for new &lt;em&gt;host queries&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Here you can see a bunch of Amazon Alexa alerts on my console, these are new domains that the very talkative little box has started calling out to...&lt;/p&gt;
&lt;p&gt;&lt;img src="/files/2025/01/home-detector-dns-alerts.png" class="img-fluid" &gt;&lt;/p&gt;
&lt;h3&gt;(ii) The DNS Domains&lt;/h3&gt;
&lt;p&gt;On the Domains page, you can review the domains that are in &lt;code&gt;pass&lt;/code&gt; state or &lt;code&gt;block&lt;/code&gt; state, for the first 30days (by default) all domains will be in pass, only after the baseline is created will &lt;em&gt;blocks&lt;/em&gt; start to appear.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;🔥 By Default, &lt;strong&gt;&lt;em&gt;Block does not mean Block&lt;/em&gt;&lt;/strong&gt;, it means 🚨 alarm 🚨 (&lt;em&gt;perhaps not my best choice&lt;/em&gt;). Only if you enable the DNS Firewall under settings will the DNS be blocked, Home Detector will send &lt;code&gt;NXDOMAIN&lt;/code&gt; (&lt;em&gt;domain not found&lt;/em&gt;) responses.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is what my domains page looks like, the first range for &lt;code&gt;ntp.org&lt;/code&gt; is for my shelly devices, the next 5x are for my heating/themostat, and then the bottom ranges are for alexa's... as you can see there's 135 row, quite a lot of learnt traffic in my LAN.... and this is &lt;strong&gt;JUST&lt;/strong&gt; the IoT devices, Phones/Laptops/etcs do not log here and in my documentation I warn against doing that 😬&lt;/p&gt;
&lt;p&gt;&lt;img src="/files/2025/01/home-detector-dns-domains.png" class="img-fluid" &gt;&lt;/p&gt;
&lt;h3&gt;(iii) The Queries&lt;/h3&gt;
&lt;p&gt;Like the domains page, the queries page allows you to review all the look-ups from a device in either &lt;code&gt;pass&lt;/code&gt; or &lt;code&gt;block&lt;/code&gt; state.&lt;/p&gt;
&lt;h2&gt;Why am I doing this?&lt;/h2&gt;
&lt;p&gt;If you've got this far, 👍🏼 nice 👍🏼 !&lt;/p&gt;
&lt;p&gt;The point of this is to monitor what your IoT/Smart-Things are doing, you should get an idea quickly of what is normal, and &lt;strong&gt;IF&lt;/strong&gt; a device got compromised it would certainly start doing &lt;em&gt;different&lt;/em&gt; queries, like downloading malware-firmware, or downloading scripts from compromised sites... Command-and-Control needs DNS to function.&lt;/p&gt;
&lt;h2&gt;A word of Warning&lt;/h2&gt;
&lt;p&gt;The noise from my home stuff has been low, shelly, etc have a couple of domains and everything works as expected, but with 👾 Amazon Alexa devices there be monsters ahead! 👾  ... those things seem to be constantly using new hosts and new domains, it's eye-opening but also annoying, so intercept those things with care.&lt;/p&gt;
&lt;h2&gt;Next Steps&lt;/h2&gt;
&lt;p&gt;There's a fair amount of &lt;em&gt;future work&lt;/em&gt; I'd like to look at,  changing domains from &lt;em&gt;pass&lt;/em&gt; to &lt;em&gt;block&lt;/em&gt; by typing instead of drop-downs is quite annoying 💩 ... also, I'd like to implement some built-in signatures to assert what is normal before the learning phase is completed. Pull requests are welcome but the first thing I need to do is update all the dependencies, as since finishing the project, github's dependabot has been going mental! &lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Nick Bettison</dc:creator><pubDate>Sun, 05 Jan 2025 18:01:00 +0000</pubDate><guid isPermaLink="false">tag:www.linickx.com,2025-01-05:iot-dns-anomaly-detection-with-home-detector</guid><category>Security</category><category>Home Assistant</category><category>Home Detector</category></item><item><title>Honeypot: Running Open Canary on Home Assistant</title><link>https://www.linickx.com/honeypot-running-open-canary-on-home-assistant</link><description>&lt;p&gt;This project, 🎉 &lt;a href="https://github.com/linickx/HomeDetector"&gt;Home Detector&lt;/a&gt; 🎉 , started life as an academic project and is something I hope to continue, if you're interested in some home intrusion detection, read-on! &lt;/p&gt;
&lt;h2&gt;Some Background &amp;amp; Context&lt;/h2&gt;
&lt;p&gt;In my 40s, I have decided to go back to school and for my MSc final thesis I researched intrusion detection for &lt;em&gt;home networks&lt;/em&gt; 😱 . The lens was simple do we &lt;em&gt;know&lt;/em&gt; if our home LAN/Wi-Fi is under attack, does your Dad, Nan, Uncle or Aunt... even as a &lt;em&gt;nerd&lt;/em&gt; reading this blog, do you have any intrusion detection at home?! (Hint: I didn't!)&lt;/p&gt;
&lt;p&gt;It turns out, intrusion detection at home is &lt;em&gt;hard&lt;/em&gt;, sure you can install HIDS (&lt;em&gt;Host Intrusion Detection Systems&lt;/em&gt;) on your Laptop/PC but what about your phone, table or internet-enabled-fridge! 🤔 &lt;/p&gt;
&lt;p&gt;The last-one is the kicker, as a Home Assistant fan, I've got more and more smart-home, IoT stuff, how would I detect Mira-worm style compromise? ... do I even know what my IoT stuff is doing when I'm not looking?! NIDS (&lt;em&gt;Network Intrusion Detection Systems&lt;/em&gt;) of course are &lt;em&gt;an&lt;/em&gt; answer but not &lt;em&gt;that&lt;/em&gt; practical, do you have a pinch-point to inspect/tap... what about networks with all-in-one Wi-Fi+Router, how do you inspect that?&lt;/p&gt;
&lt;p&gt;In my paper, I proposed 2x solutions, this is &lt;strong&gt;Part 1 - A Honeypot&lt;/strong&gt;. &lt;/p&gt;
&lt;p&gt;A honeypot is know as Intrusion Detection &lt;em&gt;via Deception&lt;/em&gt;, that is you place a juicy target on your network to draw attention away from your real stuff, when the threat actor trips the alarm you know about it; of course the threat actor is &lt;em&gt;already&lt;/em&gt; on a device, but this is the point, how would you know? The honeypot approach is a something-is-better-than-nothing approach, if your CCTV is compromised, and the threat is looking for another device and they fire your honeypot you have &lt;strong&gt;an alarm&lt;/strong&gt; to respond to. 🚨&lt;/p&gt;
&lt;h2&gt;Installing my Home Detector Add-On&lt;/h2&gt;
&lt;p&gt;I've wrapped up &lt;a href="https://github.com/thinkst/opencanary"&gt;Open Canary&lt;/a&gt; into a Home Assistant Add-on, so installation is easy:&lt;/p&gt;
&lt;h3&gt;1. Install my Add-on Repository&lt;/h3&gt;
&lt;p&gt;To get my add-on, you can either &lt;a href="https://my.home-assistant.io/redirect/supervisor_add_addon_repository/?repository_url=https%3A%2F%2Fgithub.com%2Flinickx%2Fha-addons"&gt;click this link to add my repository&lt;/a&gt; or follow these manual steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Click Settings,&lt;/li&gt;
&lt;li&gt;Click Add-ons,&lt;/li&gt;
&lt;li&gt;Click Add-On Store,&lt;/li&gt;
&lt;li&gt;Click The three dots in the top-right,&lt;/li&gt;
&lt;li&gt;Click Repositories,&lt;/li&gt;
&lt;li&gt;Type in: &lt;code&gt;https://github.com/linickx/ha-addons&lt;/code&gt; and Click Add&lt;/li&gt;
&lt;li&gt;Click The three dots in the top-right,&lt;/li&gt;
&lt;li&gt;Click Check for Updates&lt;/li&gt;
&lt;li&gt;Refresh the page a couple of times&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. Install Home Detector&lt;/h3&gt;
&lt;p&gt;Once you've got &lt;a href="https://github.com/linickx/ha-addons"&gt;the repo&lt;/a&gt;' setup, scroll down to the bottom and install Home Detector.&lt;/p&gt;
&lt;p&gt;&lt;img src="/files/2025/01/linickx-addons-home-detector.png" class="img-fluid" &gt;&lt;/p&gt;
&lt;p&gt;Make sure the add-on is started &amp;amp; you're done.&lt;/p&gt;
&lt;h2&gt;Look Ma! I'm running a Telnet Honey Pot&lt;/h2&gt;
&lt;p&gt;With no config, and just a running add-on you have a Telnet honeypot. If you try to connect (&amp;amp; fail auth) you'll get a pop-up notification in Home Assistant.&lt;/p&gt;
&lt;p&gt;&lt;img src="/files/2025/01/home-detector-ha-notification-for-telnet.png" class="img-fluid" &gt;&lt;/p&gt;
&lt;p&gt;And an entry in the Home Detector dashboard.&lt;/p&gt;
&lt;p&gt;&lt;img src="/files/2025/01/home-detector-telnet-alert.png" class="img-fluid" &gt;&lt;/p&gt;
&lt;p&gt;If you have SSH running, telnet will look like a weaker target for anyone running a sneaky port-scan on your LAN.&lt;/p&gt;
&lt;h2&gt;Enabling the HTTP (NAS Like) Honeypot&lt;/h2&gt;
&lt;p&gt;If you want to step-up, you can enable The HTTP honeypot which presents a NAS like log-in page.&lt;/p&gt;
&lt;p&gt;Enabling is simply opening the port and re-starting the add-on!&lt;/p&gt;
&lt;p&gt;To enable the port, &lt;em&gt;within Home Assistant&lt;/em&gt; go to &lt;code&gt;Settings&lt;/code&gt; -&amp;gt; &lt;code&gt;Add-Ons&lt;/code&gt; -&amp;gt; &lt;code&gt;Home Detector&lt;/code&gt; -&amp;gt; &lt;code&gt;Configuration&lt;/code&gt; , scroll to the bottom to &lt;code&gt;Network&lt;/code&gt; and &lt;em&gt;toggle&lt;/em&gt; &lt;code&gt;Show disabled ports&lt;/code&gt;, then type in &lt;em&gt;80&lt;/em&gt; where it says &lt;code&gt;HoneyPot (Fake) WebServer Port&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;The cyber-kill-chain&lt;/h2&gt;
&lt;p&gt;At this point, it's important to remember the modus operandi of a threat actor. The first step is always some kind of reconnaissance even after compromise, there's usually another round of reconnaissance to pivot and further secure the footing, so if your CCTV is compromised, the default port-scan of home assistant (without SSH by default) doesn't look that &lt;em&gt;interesting&lt;/em&gt;, the default admin port (8123) is above nmap's default range, by enabling the Telnet (&amp;amp; HTTP) honeypot, these &lt;em&gt;interesting&lt;/em&gt; ports will appear in a default nmap, so the likelihood of detection increases and therefore the opportunity to &lt;em&gt;kill&lt;/em&gt; the threat actors chain of actions.&lt;/p&gt;
&lt;h2&gt;Contributing&lt;/h2&gt;
&lt;p&gt;Open Canary has ✨ &lt;em&gt;loads&lt;/em&gt; ✨ of features &amp;amp; customisation, none of which have been implemented here yet, but I would certainly love to implement some more features... top of my list, custom Web Theme for the HTTP Honeypot, pull requests welcome, I am only &lt;em&gt;just&lt;/em&gt; about ready to start accepting merges, I had to wait for marking of my paper to be completed and 😊&lt;/p&gt;
&lt;h2&gt;Coming up in Part 2...&lt;/h2&gt;
&lt;p&gt;For my paper I moved onto &lt;code&gt;DNS anomaly detection&lt;/code&gt;, as a way of detecting compromised IoT earlier than a threat actor trying to pivot, please take a look 👉🏻 &lt;a href="iot-dns-anomaly-detection-with-home-detector"&gt;HERE&lt;/a&gt; 👈🏼 if you're interested in home cyber security.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Nick Bettison</dc:creator><pubDate>Wed, 01 Jan 2025 12:43:00 +0000</pubDate><guid isPermaLink="false">tag:www.linickx.com,2025-01-01:honeypot-running-open-canary-on-home-assistant</guid><category>Security</category><category>Home Assistant</category><category>Open Canary</category><category>Home Detector</category></item><item><title>UniFi, OpenWRT, freeRADIUS: Mac Address Dynamic VLAN Assignment for WPA2-PSK</title><link>https://www.linickx.com/unifi-openwrt-freeradius-mac-address-dynamic-vlan-assignment-for-wpa2-psk</link><description>&lt;p&gt;This is much simpler than I imagined...&lt;/p&gt;
&lt;h2&gt;Scenario&lt;/h2&gt;
&lt;p&gt;For my home Wi-Fi, I want a single SSID but I want to place devices (Such as IOT) into different VLANs, but since this is personal use I don't want the headache of certificates that comes with 802.1x / WPA-Enterprise, thus want to keep the simplicity of passwords (WPA2-PSK).&lt;/p&gt;
&lt;p&gt;UniFi APs support Dynamic VLAN Assignment, but it wasn't obvious what needs to be done on the RADIUS side... well, it'd be simple for Cisco's ISE but I don't have that at home, so freeadius it is 😜&lt;/p&gt;
&lt;p&gt;In this solution there's two key things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;MAC Authorization isn't a security feature; it's more of a convenience feature, Windows &amp;amp; Linux easily allow changing the MAC address of a network card&lt;/li&gt;
&lt;li&gt;The config below has a &lt;em&gt;default allow&lt;/em&gt; that is unknown Mac addresses are permitted&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The idea is, I have one SSID for all my devices, new devices "just work" and they can be moved around afterwards. The &lt;em&gt;security&lt;/em&gt; of the SSID hasn't changed the strength of the &lt;code&gt;PSK&lt;/code&gt; is the key.&lt;/p&gt;
&lt;p&gt;The example below assumes you have a working Wi-Fi, with working VLANs and a working firewall/router to connect them together, we start with installing freeradius onto OpenWRT.&lt;/p&gt;
&lt;h2&gt;Install Freeradius&lt;/h2&gt;
&lt;p&gt;On your OpenWRT box, install the default packages...&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;opkg update &amp;amp;&amp;amp; opkg install freeradius3-default freeradius3-utils
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, if you want to be fancy and install just the bare minimum you &lt;em&gt;only&lt;/em&gt; need the below packages, this will exclude all the EAP stuff, but you'll have to hack about with the default site and config files to get the service to start properly... you've been warned! &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;freeradius3 freeradius3-common freeradius3-mod-always freeradius3-mod-attr-filter freeradius3-mod-chap freeradius3-mod-detail freeradius3-mod-exec freeradius3-mod-expiration freeradius3-mod-files freeradius3-mod-logintime freeradius3-mod-pap freeradius3-mod-preprocess freeradius3-utils 
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Edit Config files&lt;/h3&gt;
&lt;p&gt;The first step is to define your &lt;em&gt;RADIUS Clients&lt;/em&gt;, the things that send authentication requests. The file you need to edit is &lt;code&gt;/etc/freeradius3/clients.conf&lt;/code&gt;; the RADIUS packets come directly from the Access-Point so you can either add them one at a time, or add a subnet like this:&lt;/p&gt;
&lt;p&gt;Place at the bottom of the file.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;client vlan1 {
        ipaddr = 192.168.1.0/24
        secret = correcthorsebatterystaple
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, add the &lt;em&gt;WI-FI Clients&lt;/em&gt; to &lt;code&gt;/etc/freeradius3/mods-config/files/authorize&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Place at the top of the file.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;de:ad:be:ef:00:01       Cleartext-Password := &amp;quot;de:ad:be:ef:00:01&amp;quot;
                        Tunnel-Type = VLAN,
                        Tunnel-Medium-Type = 6,                         
                        Tunnel-Private-Group-Id = 2

DEFAULT                 Auth-Type := Accept
                        Tunnel-Type = VLAN,
                        Tunnel-Medium-Type = 6,
                        Tunnel-Private-Group-Id = 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Where &lt;code&gt;de:ad:be:ef:00:01&lt;/code&gt; is the MAC Address of the device, such as your phone or notebook and &lt;code&gt;Tunnel-Private-Group-Id = 2&lt;/code&gt; places the device into &lt;code&gt;VLAN 2&lt;/code&gt;. This example places all unknown MAC addresses into &lt;code&gt;VLAN 1&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;Testing &amp;amp; Logging&lt;/h3&gt;
&lt;p&gt;With the config files changed, I recommend you stop the radius service : &lt;code&gt;/etc/init.d/radiusd stop&lt;/code&gt; and then start freeradius in debug mode: &lt;code&gt;radiusd -X&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;In debug mode, you'll need to keep your SSH open but you'll see the authentication requests on the screen, if you've made any mistakes it's easier to spot this way.&lt;/p&gt;
&lt;p&gt;After testing is complete, you can run &lt;code&gt;/etc/init.d/radiusd start&lt;/code&gt; to run it as a background service.&lt;/p&gt;
&lt;p&gt;While you are running &lt;code&gt;radiusd -X&lt;/code&gt; no logs are produced, the output is to the screen; by default when you re-enable the service the logs will output to disk: &lt;code&gt;/var/log/radius.log&lt;/code&gt;. If you want to see the logs in &lt;code&gt;luci&lt;/code&gt; then, edit &lt;code&gt;/etc/freeradius3/radiusd.conf&lt;/code&gt; change &lt;code&gt;destination = files&lt;/code&gt; ➡️ &lt;code&gt;destination = syslog&lt;/code&gt; you may also want to set &lt;code&gt;auth = yes&lt;/code&gt; so that you see both failed &amp;amp; passed authentications.&lt;/p&gt;
&lt;h2&gt;UniFi update the SSID&lt;/h2&gt;
&lt;p&gt;Before changing the SSID, a RADIUS server needs to be defined under &lt;code&gt;Settings &amp;gt; Profiles &amp;gt; RADIUS&lt;/code&gt; . The &lt;em&gt;secret&lt;/em&gt; is the same secret which was defined above in clients.conf; the IP address is that of your OpenWRT box. Account is optional, enable if logging disconnect events is important to you.&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2023/05/UniFi_RADIUS.png"&gt;&lt;img src="/files/2023/05/UniFi_RADIUS.png" alt="Screenshot of UniFi RADIUS Profile" class="img-fluid" style="max-width: 80%;"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Finally edit your SSID under &lt;code&gt;Settings &amp;gt; Wireless Networks&lt;/code&gt; , scroll to the bottom and enable RADIUS MAC Auth, selecting the profile you just created.&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2023/05/Unifi_Radius_macauth.png"&gt;&lt;img src="/files/2023/05/Unifi_Radius_macauth.png" alt="Screenshot of UniFi SSID Mac Auth section" class="img-fluid" style="max-width: 80%;"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you have freeradius running in debug mode you'll see events when devices try to connect!&lt;/p&gt;
&lt;h2&gt;/End&lt;/h2&gt;
&lt;p&gt;Adding new devices is as simple as putting entries into the &lt;code&gt;authorize&lt;/code&gt; file with your desired VLAN.&lt;/p&gt;
&lt;p&gt;References:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;https://openwrt.org/docs/guide-user/network/wifi/freeradius&lt;/li&gt;
&lt;li&gt;https://neilzone.co.uk/2021/09/using-freeradius-to-assign-vlans-for-unifi-wi-fi&lt;/li&gt;
&lt;li&gt;https://help.ui.com/hc/en-us/articles/360015268353-UniFi-Gateway-Configuring-RADIUS-Server&lt;/li&gt;
&lt;/ul&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Nick Bettison</dc:creator><pubDate>Sun, 07 May 2023 17:55:00 +0100</pubDate><guid isPermaLink="false">tag:www.linickx.com,2023-05-07:unifi-openwrt-freeradius-mac-address-dynamic-vlan-assignment-for-wpa2-psk</guid><category>Security</category><category>RADIUS</category><category>UniFi</category><category>OpenWRT</category></item><item><title>Ansible: Let's Encrypt's Certbot with Cloudflare DNS Challenge</title><link>https://www.linickx.com/ansible-lets-encrypts-certbot-with-cloudflare-dns-challenge</link><description>&lt;p&gt;There's quite a few options/roles in ansible-galaxy or github to generate a Let's Encrypt Certificate, before running a random role it's good practice to review the tasks to get an idea of what it's doing. &lt;a href="https://twitter.com/geerlingguy"&gt;Jeff Geerling&lt;/a&gt; is a very reputable source for ansible roles however &lt;a href="https://github.com/geerlingguy/ansible-role-certbot"&gt;his certbot&lt;/a&gt; role by default is for Internet facing systems, that is a server that can directly respond to a either a standalone challenge or apache/nginx with &lt;code&gt;.well-known&lt;/code&gt; setup correctly.&lt;/p&gt;
&lt;p&gt;A DNS challenge allows Certbot to issue a cert from behind a firewall, like at home, without creating any DMZ or port-forwarding; after reviewing a few roles on offer to do this with ansible I realized it's actually quite straightforward!&lt;/p&gt;
&lt;p&gt;To start with, use ansible-galaxy to install &lt;a href="https://galaxy.ansible.com/geerlingguy/certbot"&gt;geerlingguy.certbot&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ansible-galaxy install geerlingguy.certbot
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now the plan here is to make your system / playbook require the role, but not issue the certificate as part of the role, instead issue the cert as a follow up task.  Here's the initial playbook:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
- hosts: internal_servers
  become: true
  #check_mode: yes

  vars:
    certbot_auto_renew: true
    certbot_auto_renew_user: root
    certbot_email: &amp;quot;certbot-notifications@something.com&amp;quot;
    certbot_cloudflare_api_token: 'xxxxx'

  roles:
  - geerlingguy.certbot

  tasks:
  - name: Install Certbot Cloudflare
    package: 
        name: python3-certbot-dns-cloudflare
        state: present

  - name: Create Certbot folder - /etc/letsencrypt
    file:
      path: /etc/letsencrypt
      state: directory
      owner: root
      group: root
      mode: 0700

  - name: Certbot Template
    template:
      src: &amp;quot;{{ item.src }}&amp;quot;
      dest: &amp;quot;{{ item.dest }}&amp;quot;
      owner: root
      group: root
      mode: 0600
    with_items:
      - { src: 'dnscloudflare.ini.j2', dest: '/etc/letsencrypt/dnscloudflare.ini' }

  - name: Certbot | Generate Certificate
    command: certbot certonly --non-interactive --agree-tos --dns-cloudflare --dns-cloudflare-credentials /etc/letsencrypt/dnscloudflare.ini -m {{certbot_email}} -d {{ansible_host}}
    args:
      creates: /etc/letsencrypt/renewal/{{ansible_host}}.conf

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What's gonna happen here is &lt;code&gt;geerlingguy.certbot&lt;/code&gt; will install the certbot package and setup the renewal cron task... and that's it. Once the role is done, it'll move into our tasks, so let's step thru those:&lt;/p&gt;
&lt;h3&gt;1. Install Certbot Cloudflare&lt;/h3&gt;
&lt;p&gt;Cloudflare support in Certbot is an optional add0on that you need to install. I'm running this on Redhat Enterprise Linux 8, for me the package for &lt;a href="https://certbot-dns-cloudflare.readthedocs.io/en/stable/"&gt;certbot-dns-cloudflare&lt;/a&gt; is called &lt;code&gt;python3-certbot-dns-cloudflare&lt;/code&gt;, so if you're running this on Ubuntu/Alpine etc you will need to change that.&lt;/p&gt;
&lt;h3&gt;2/3. Create Certbot folder &amp;amp; Template&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;certbot-dns-cloudflare&lt;/code&gt; plug-in needs credentials, since we haven't issued any certs the files &amp;amp; folders are not in place. So first ensure the folder is there and then you need a template file: &lt;code&gt;dnscloudflare.ini.j2&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Cloudflare API credentials used by Certbot
dns_cloudflare_api_token = {{certbot_cloudflare_api_token}}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As above it's actually just one line, so probably could do it with line-in-file task but I like the template approach when the file is being created by me and I'm not just editing an existing file 😉&lt;/p&gt;
&lt;p&gt;As you can see, the template simply writes your API Token in a format that the plug-in expects.&lt;/p&gt;
&lt;h3&gt;4. Generate Certificate&lt;/h3&gt;
&lt;p&gt;Finally we run the command manually in a way that calls the DNS challenge with all the information it needs. In my example, the certificate domain matches the ansible hostname but you can change that to what ever you need!&lt;/p&gt;
&lt;h3&gt;...Final thoughts &amp;amp; Renewal&lt;/h3&gt;
&lt;p&gt;As you can see, it was simple no need to install multiple roles/etc to achieve our goal. A quick point will be as we've issued the cert manually we might need to manage a renewal hook, on option is to drop a script in &lt;code&gt;/etc/letsencrypt/renewal-hooks/post&lt;/code&gt; that will be run each time certbot runs, the other is a task to put a line in the file...&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;- name: Check Certbot File - post_hook
  lineinfile:
    dest: /etc/letsencrypt/renewal/{{ansible_host}}.conf
    line: &amp;quot;post_hook = /bin/systemctl restart nginx.service&amp;quot;
    regexp: &amp;quot;^post_hook =&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Enjoy!&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Nick Bettison</dc:creator><pubDate>Sat, 15 May 2021 17:25:00 +0100</pubDate><guid isPermaLink="false">tag:www.linickx.com,2021-05-15:ansible-lets-encrypts-certbot-with-cloudflare-dns-challenge</guid><category>Ansible</category><category>Security</category><category>Cloudflare</category><category>DNS</category><category>LetsEncrypt</category><category>Certificate</category></item><item><title>DNSCrypt Proxy on a UniFi Cloud Key</title><link>https://www.linickx.com/dnscrypt-proxy-on-a-unifi-cloud-key</link><description>&lt;p&gt;There's quite a few posts online around setting up dnscrypt-proxy on a USG or EdgeRouter but what about a CloudKey? This post will cover some high level instructions, I'm going to assume that if the reader is willing to customise their UniFi by installing non-standard packages that they are comfortable with CLI!&lt;/p&gt;
&lt;p&gt;I've got a cloudkey+ running firmware version 2 and after enabling SSH, &lt;code&gt;/etc/debian_version&lt;/code&gt; shows the box is based off quite an &lt;a href="https://github.com/DNSCrypt/dnscrypt-proxy/wiki/Installation-on-Debian-and-Ubuntu"&gt;old version of debian that doesn't ship&lt;/a&gt; with the latest dnscrypt-proxy packages, so this is going to take a little more than apt-get install 🙄&lt;/p&gt;
&lt;p&gt;Before installing any non-standard packages onto any appliance you should check for conflicts, a quick &lt;code&gt;netstat -nap | grep 53&lt;/code&gt; will show you that systemd-resolve is already listening on UDP/53, so to start with &lt;a href="https://unix.stackexchange.com/a/358485"&gt;disable that&lt;/a&gt; by adding &lt;code&gt;DNSStubListener=no&lt;/code&gt; to &lt;code&gt;/etc/systemd/resolved.conf&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The github documentation describes how to enable the testing repo and continue the installation, however that feels like a long way off the standard, what about just downloading the single .deb file? According to &lt;code&gt;uname -a&lt;/code&gt; the processor of the cloudkey+ is a 64bit Arm therefore the latest compatible package can be downloaded directly from here:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;https://packages.debian.org/bullseye/arm64/dnscrypt-proxy/download&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Copy the file (via SCP), and run &lt;code&gt;dpkg -i&lt;/code&gt; ....&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;root@UniKey:~# dpkg -i dnscrypt-proxy_2.0.45+ds1-1+b1_arm64.deb 
Selecting previously unselected package dnscrypt-proxy.
(Reading database ... 35495 files and directories currently installed.)
Preparing to unpack dnscrypt-proxy_2.0.45+ds1-1+b1_arm64.deb ...
Unpacking dnscrypt-proxy (2.0.45+ds1-1+b1) ...
Setting up dnscrypt-proxy (2.0.45+ds1-1+b1) ...
Removing obsolete conffile /etc/dnscrypt-proxy/dnscrypt-proxy.conf ...
root@UniKey:~# 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(Notice in my output, I had tried apt-get before realizing how old the package was!)&lt;/p&gt;
&lt;p&gt;A quick check of the service will show it's running...&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;root@UniKey:~# service dnscrypt-proxy status
● dnscrypt-proxy.service - DNSCrypt client proxy
   Loaded: loaded (/lib/systemd/system/dnscrypt-proxy.service; enabled; vendor preset: enabled)
   Active: active (running) since Mon 2021-03-15 13:02:26 GMT; 17s ago
     Docs: https://github.com/DNSCrypt/dnscrypt-proxy/wiki
 Main PID: 18613 (dnscrypt-proxy)
   Memory: 8.0M
      CPU: 630ms
   CGroup: /system.slice/dnscrypt-proxy.service
           └─18613 /usr/sbin/dnscrypt-proxy -config /etc/dnscrypt-proxy/dnscrypt-proxy.toml

Mar 15 13:02:26 UniKey dnscrypt-proxy[18613]: [2021-03-15 13:02:26] [NOTICE] dnscrypt-proxy 2.0.45
Mar 15 13:02:26 UniKey dnscrypt-proxy[18613]: [2021-03-15 13:02:26] [NOTICE] Network connectivity detected
Mar 15 13:02:26 UniKey dnscrypt-proxy[18613]: [2021-03-15 13:02:26] [WARNING] Systemd sockets are untested and unsupported - use at your own risk
Mar 15 13:02:26 UniKey dnscrypt-proxy[18613]: [2021-03-15 13:02:26] [NOTICE] Wiring systemd TCP socket #0, dnscrypt-proxy.socket, 127.0.2.1:53
Mar 15 13:02:26 UniKey dnscrypt-proxy[18613]: [2021-03-15 13:02:26] [NOTICE] Wiring systemd UDP socket #1, dnscrypt-proxy.socket, 127.0.2.1:53
Mar 15 13:02:26 UniKey dnscrypt-proxy[18613]: [2021-03-15 13:02:26] [NOTICE] Source [public-resolvers] loaded
Mar 15 13:02:26 UniKey dnscrypt-proxy[18613]: [2021-03-15 13:02:26] [NOTICE] Firefox workaround initialized
Mar 15 13:02:27 UniKey dnscrypt-proxy[18613]: [2021-03-15 13:02:27] [NOTICE] [cloudflare] OK (DoH) - rtt: 27ms
Mar 15 13:02:27 UniKey dnscrypt-proxy[18613]: [2021-03-15 13:02:27] [NOTICE] Server with the lowest initial latency: cloudflare (rtt: 27ms)
Mar 15 13:02:27 UniKey dnscrypt-proxy[18613]: [2021-03-15 13:02:27] [NOTICE] dnscrypt-proxy is ready - live servers: 1
root@UniKey:~# 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;... But you'll find it's useless as by default it's only listening on localhost, that's because of Systemd.&lt;/p&gt;
&lt;p&gt;To change the port number (&lt;em&gt;or IP&lt;/em&gt;), you need to create &lt;code&gt;/etc/systemd/system/dnscrypt-proxy.socket.d/listen.conf&lt;/code&gt; (&lt;em&gt;the directory won't exist&lt;/em&gt;), as this is the place to customise systemd. The contents should be:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Socket]
ListenStream=
ListenStream=53
ListenDatagram=
ListenDatagram=53
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The line duplication above is deliberate, systemd requires you to clear any previous values with a blank entry, then you can assign a new one. If you just add "ListenStream=5454" then it will add a port, not replace one!&lt;/p&gt;
&lt;p&gt;...restart the &lt;em&gt;socket&lt;/em&gt; and you should be good to go!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;root@UniKey:~#systemctl status dnscrypt-proxy.socket
● dnscrypt-proxy.socket - dnscrypt-proxy listening socket
   Loaded: loaded (/lib/systemd/system/dnscrypt-proxy.socket; enabled; vendor preset: enabled)
  Drop-In: /etc/systemd/system/dnscrypt-proxy.socket.d
           └─listen.conf
   Active: active (running) since Tue 2021-04-13 12:24:34 BST; 2 days ago
     Docs: https://github.com/DNSCrypt/dnscrypt-proxy/wiki
   Listen: [::]:53 (Datagram)
   CGroup: /system.slice/dnscrypt-proxy.socket

Apr 13 12:24:34 UniKey systemd[1]: Stopping dnscrypt-proxy listening socket.
Apr 13 12:24:34 UniKey systemd[1]: dnscrypt-proxy.socket: TCP_DEFER_ACCEPT failed: Protocol not available
Apr 13 12:24:34 UniKey systemd[1]: dnscrypt-proxy.socket: TCP_NODELAY failed: Protocol not available
Apr 13 12:24:34 UniKey systemd[1]: Listening on dnscrypt-proxy listening socket.
root@UniKey:~# 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notes: *&lt;em&gt;Drop-In&lt;/em&gt;, if you box still isn't listening on the port, check to see if your file is created/read correctly.&lt;/p&gt;
&lt;h3&gt;Resolver Lists&lt;/h3&gt;
&lt;p&gt;By default the package selects Cloudflare's public DNS. The settings are stored in &lt;code&gt;/etc/dnscrypt-proxy/dnscrypt-proxy.toml&lt;/code&gt;, so if you don't want to use Cloudflare, for example to use Cisco Umbrella (OpenDNS) change &lt;code&gt;server_names = ['cloudflare']&lt;/code&gt; to &lt;code&gt;server_names = ['cisco']&lt;/code&gt;. &lt;a href="https://dnscrypt.info/public-servers"&gt;The full list of resolvers is on DNSCrypt's website&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Logging&lt;/h3&gt;
&lt;p&gt;By default the package didn't do any logging, that's a fairly easy fix, drop the following into the config file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;log_level = 2
log_files_max_size = 10
log_files_max_age = 7
log_files_max_backups = 1

[query_log]
  file = '/var/log/dnscrypt-proxy/query.log'

[nx_log]
  file = '/var/log/dnscrypt-proxy/nx.log'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Restart the service and then you can do a &lt;code&gt;tail -f /var/log/dnscrypt-proxy/query.log&lt;/code&gt; to validate your resolver is working, if the file doesn't get created run &lt;code&gt;chown _dnscrypt-proxy /var/log/dnscrypt-proxy&lt;/code&gt; to correct folder permission issues.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Nick Bettison</dc:creator><pubDate>Thu, 15 Apr 2021 14:04:00 +0100</pubDate><guid isPermaLink="false">tag:www.linickx.com,2021-04-15:dnscrypt-proxy-on-a-unifi-cloud-key</guid><category>Security</category><category>DNS</category></item><item><title>PowerShell: A little shortcut for signing Scripts</title><link>https://www.linickx.com/powershell-a-little-shortcut-for-signing-scripts</link><description>&lt;p&gt;It's good practice to sign your powershell scripts, especially in an enterprise environment with an internal CA as it allows you to quickly identify internal code and if it's been modified. I've &lt;a href="https://twitter.com/linickx/status/1107663408265154560?s=20"&gt;tweeted before&lt;/a&gt; that the process is a little cumbersome and have been meaning to post a little tip for making it easier.&lt;/p&gt;
&lt;p&gt;I'm going to start from the point assuming you have had the cert issued to you by your admin, and it's already installed into your personal store, so you have something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;PS C:\Users\nick.bettison&amp;gt; Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert


   PSParentPath: Microsoft.PowerShell.Security\Certificate::CurrentUser\My

Thumbprint                                Subject
----------                                -------
1111111111111111111111111111111111111111  CN=Nick Bettison, OU=GROUP, OU=Users, OU=BLAH, OU=BLAH...



PS C:\Users\nick.bettison&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you have multiple, take note of the one you want, the first one is &lt;code&gt;[0]&lt;/code&gt;, the 2nd is &lt;code&gt;[1]&lt;/code&gt; and so on.&lt;/p&gt;
&lt;p&gt;The process for signing a script (&lt;em&gt;the long way&lt;/em&gt;) is:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$acert =(dir cert:\currentuser\My -CodeSigningCert)[0]
Set-AuthenticodeSignature &amp;quot;.\hello.ps1&amp;quot; -Certificate $acert
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, you can shorten this with a function in your profile. Powershell has a built in variable &lt;code&gt;$profile&lt;/code&gt;, which is a predefined file path:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;PS C:\Users\nick.bettison&amp;gt; write-host $profile
C:\Users\nick.bettison\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1
PS C:\Users\nick.bettison&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Edit that file (&lt;em&gt;create it, if it doesn't exist&lt;/em&gt;) and drop in the following quick little function:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function Sign-Script {
    $file = $args[0]
    $acert =(dir cert:\currentuser\My -CodeSigningCert)[0]
    Set-AuthenticodeSignature $file -Certificate $acert
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(&lt;em&gt;Remember the &lt;code&gt;[0]&lt;/code&gt; from above, update &lt;code&gt;-CodeSigningCert)[0]&lt;/code&gt; as appropriate for your cert store&lt;/em&gt;)&lt;/p&gt;
&lt;p&gt;Save the file, and now you have &lt;code&gt;Sign-Script&lt;/code&gt; shortcut, it's easy to use see the gif below 🙂&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2021/03/sign-script-ps1.gif"&gt;&lt;img alt="PowerShell Signing" src="/files/2021/03/sign-script-ps1.gif" title="PowerShell Signing" /&gt;&lt;/a&gt;&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Nick Bettison</dc:creator><pubDate>Fri, 05 Mar 2021 15:59:00 +0000</pubDate><guid isPermaLink="false">tag:www.linickx.com,2021-03-05:powershell-a-little-shortcut-for-signing-scripts</guid><category>PowerShell</category><category>Scripting</category><category>Security</category></item><item><title>Teltonika RUT240 Let's Encrypt on RUTOS</title><link>https://www.linickx.com/teltonika-rut240-lets-encrypt-on-rutos</link><description>&lt;p&gt;I &lt;a href="https://twitter.com/linickx/status/1319228876556537856"&gt;recently purchased&lt;/a&gt; a Teltonika RUT240 to use as a home 4G router, and very quickly I got irritated by the self-signed certificate on the WebUI! 😬   It's been a very long time since I've needed to really delve back into &lt;a href="https://letsencrypt.org"&gt;let's encrypt&lt;/a&gt;, certbot on centos meets my webserver needs and for home network previously I didn't have much else.&lt;/p&gt;
&lt;p&gt;The Teltonika RUT240 is built on a very old version of OpenWRT therefore prebuilt package options are limited; I'd heard good things about &lt;a href="https://github.com/acmesh-official/acme.sh"&gt;acme.sh&lt;/a&gt; as it allows DNS based challanges. For internal devices, DNS based certificated issuance are the way forward, it allows let's encrypt  to issue you a certificate without you having to run the relevant web server (no port 80/443 needing to be opened, all the traffic is outbound).&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/12/rut240_and_the_clouds.png"&gt;&lt;img src="/files/2020/12/rut240_and_the_clouds.png"/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Getting going on RutOS was surprisingly easy, of course to start with you need to SSH onto your box, achme.sh recommend running as root so that's fine for me.&lt;/p&gt;
&lt;p&gt;Run &lt;code&gt;curl https://get.acme.sh | sh&lt;/code&gt; of course you shouldn't run/pipe random shell scripts into a root shell, so have a read of the code, read some of the github issues/feedback acme.sh is well established, so we're good to go.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;root@rut240:~# curl https://get.acme.sh | sh
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   775    0   775    0     0    168      0 --:--:--  0:00:04 --:--:--   208
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  202k  100  202k    0     0   150k      0  0:00:01  0:00:01 --:--:--  157k
[Tue Dec  8 17:43:58 GMT 2020] Installing from online archive.
[Tue Dec  8 17:43:58 GMT 2020] Downloading https://github.com/acmesh-official/acme.sh/archive/master.tar.gz
[Tue Dec  8 17:44:04 GMT 2020] Extracting master.tar.gz
[Tue Dec  8 17:44:14 GMT 2020] It is recommended to install socat first.
[Tue Dec  8 17:44:14 GMT 2020] We use socat for standalone server if you use standalone mode.
[Tue Dec  8 17:44:14 GMT 2020] If you don't use standalone mode, just ignore this warning.
[Tue Dec  8 17:44:14 GMT 2020] Installing to /root/.acme.sh
[Tue Dec  8 17:44:16 GMT 2020] Installed to /root/.acme.sh/acme.sh
[Tue Dec  8 17:44:17 GMT 2020] No profile is found, you will need to go into /root/.acme.sh to use acme.sh
[Tue Dec  8 17:44:24 GMT 2020] Installing cron job
[Tue Dec  8 17:44:24 GMT 2020] OK
[Tue Dec  8 17:44:24 GMT 2020] Install success!
root@rut240:~# 

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;RutOS runs the ash shell, so next up you need to tweak (&lt;em&gt;or create&lt;/em&gt;) your &lt;code&gt;/root/.profile&lt;/code&gt; file, I'm using the cloudflare DNS challange so mine looks like this (&lt;em&gt;replace the xxx with your details&lt;/em&gt;)...&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;root@rut240:~# cat /root/.profile
export CF_Token=&amp;quot;xxxx&amp;quot;
export CF_Account_ID=&amp;quot;xxxx&amp;quot;
export CF_Zone_ID=&amp;quot;xxx&amp;quot;

. &amp;quot;/root/.acme.sh/acme.sh.env&amp;quot;
root@rut240:~# 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Close your SSH session and re-log back in, everything should now be ready to go.&lt;/p&gt;
&lt;p&gt;This badboy is the command you want to run, but before you do, take a backup of your old self-signed cert &amp;amp; key (/etc/uhttpd.crt &amp;amp; /etc/uhttpd.key)... you know, just in case! &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;acme.sh --force --issue --dns dns_cf -d rut240.linickx.com --fullchainpath /etc/uhttpd.crt --keypath /etc/uhttpd.key  --reloadcmd &amp;quot;/etc/init.d/uhttpd restart&amp;quot; --log
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tweak as needed, replace rut240.linickx.com with the FQDN you want to use if you're not on cloudflare &lt;a href="https://github.com/acmesh-official/acme.sh/wiki/dnsapi"&gt;select a different provider&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If all goes well, you'll get something similar to this...&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Wed Dec  9 09:09:20 GMT 2020] Using CA: https://acme-v02.api.letsencrypt.org/directory
[Wed Dec  9 09:09:21 GMT 2020] Single domain='rut240.linickx.com'
[Wed Dec  9 09:09:22 GMT 2020] Getting domain auth token for each domain
[Wed Dec  9 09:09:33 GMT 2020] Getting webroot for domain='rut240.linickx.com'
[Wed Dec  9 09:09:34 GMT 2020] rut240.linickx.com is already verified, skip dns-01.
[Wed Dec  9 09:09:34 GMT 2020] Verify finished, start to sign.
[Wed Dec  9 09:09:34 GMT 2020] Lets finalize the order.
[Wed Dec  9 09:09:34 GMT 2020] Le_OrderFinalize='https://acme-v02.api.letsencrypt.org/acme/finalize/1234/1234'
[Wed Dec  9 09:09:37 GMT 2020] Downloading cert.
[Wed Dec  9 09:09:37 GMT 2020] Le_LinkCert='https://acme-v02.api.letsencrypt.org/acme/cert/abc123'
[Wed Dec  9 09:09:45 GMT 2020] Cert success.
-----BEGIN CERTIFICATE-----
xxx
-----END CERTIFICATE-----
[Wed Dec  9 09:09:45 GMT 2020] Your cert is in  /root/.acme.sh/rut240.linickx.com/rut240.linickx.com.cer 
[Wed Dec  9 09:09:45 GMT 2020] Your cert key is in  /root/.acme.sh/rut240.linickx.com/rut240.linickx.com.key 
[Wed Dec  9 09:09:45 GMT 2020] The intermediate CA cert is in  /root/.acme.sh/rut240.linickx.com/ca.cer 
[Wed Dec  9 09:09:45 GMT 2020] And the full chain certs is there:  /root/.acme.sh/rut240.linickx.com/fullchain.cer 
[Wed Dec  9 09:09:46 GMT 2020] Installing key to:/etc/uhttpd.key
[Wed Dec  9 09:09:46 GMT 2020] Installing full chain to:/etc/uhttpd.crt
[Wed Dec  9 09:09:46 GMT 2020] Run reload cmd: /etc/init.d/uhttpd restart
[Wed Dec  9 09:09:47 GMT 2020] Reload success
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A couple of points to make about that output... in the command the post hook restarts the RutOS WebUI (&lt;code&gt;"/etc/init.d/uhttpd restart&lt;/code&gt;) so as soon as it's finished it should &lt;em&gt;just work&lt;/em&gt;, also I'm on a bit of a flaky 4G connection, you'll see "&lt;em&gt;already verified&lt;/em&gt;" in my output, the first time I ran these I got the following errors..&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Tue Dec  8 18:01:51 GMT 2020] Using CA: https://acme-v02.api.letsencrypt.org/directory
[Tue Dec  8 18:02:02 GMT 2020] Create account key ok.
[Tue Dec  8 18:02:03 GMT 2020] Registering account: https://acme-v02.api.letsencrypt.org/directory
[Tue Dec  8 18:02:19 GMT 2020] Please refer to https://curl.haxx.se/libcurl/c/libcurl-errors.html for error code: 35
[Tue Dec  8 18:02:32 GMT 2020] Registered
[Tue Dec  8 18:02:33 GMT 2020] ACCOUNT_THUMBPRINT='xxx'
[Tue Dec  8 18:02:33 GMT 2020] Creating domain key
[Tue Dec  8 18:02:38 GMT 2020] The domain key is here: /root/.acme.sh/rut240.linickx.com/rut240.linickx.com.key
[Tue Dec  8 18:02:38 GMT 2020] Single domain='rut240.linickx.com'
[Tue Dec  8 18:02:39 GMT 2020] Getting domain auth token for each domain
[Tue Dec  8 18:02:49 GMT 2020] Please refer to https://curl.haxx.se/libcurl/c/libcurl-errors.html for error code: 35
[Tue Dec  8 18:02:49 GMT 2020] Getting webroot for domain='rut240.linickx.com'
[Tue Dec  8 18:02:49 GMT 2020] get to authz error.
[Tue Dec  8 18:02:49 GMT 2020] _authorizations_map=',
'
[Tue Dec  8 18:02:49 GMT 2020] Please add '--debug' or '--log' to check more details.
[Tue Dec  8 18:02:49 GMT 2020] See: https://github.com/acmesh-official/acme.sh/wiki/How-to-debug-acme.sh
root@rut240:~#
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I didn't do anything to fix it, I simply went to bed and tried the next morning! 😂 &lt;/p&gt;
&lt;p&gt;Renewals &lt;em&gt;should&lt;/em&gt; be already taken care of by acme, &lt;code&gt;crontab -l&lt;/code&gt; shows a job already setup, I'll update this post if it doesn't work! &lt;/p&gt;
&lt;p&gt;References:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;https://community.teltonika-networks.com/10995/pem-ca-changed-https-ui-not-updating-certificate?show=11013#a11013&lt;/li&gt;
&lt;li&gt;https://www.jamesridgway.co.uk/auto-renewing-ssl-certificate-unifi-cloud-key-lets-encrypt-cloudflare-dns-validation/&lt;/li&gt;
&lt;/ul&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Nick Bettison</dc:creator><pubDate>Sat, 12 Dec 2020 09:19:00 +0000</pubDate><guid isPermaLink="false">tag:www.linickx.com,2020-12-12:teltonika-rut240-lets-encrypt-on-rutos</guid><category>letsencrypt</category><category>teltonika</category><category>certificate</category><category>security</category></item><item><title>Example Azure Web Application Firewall (WAF)</title><link>https://www.linickx.com/example-azure-web-application-firewall-waf</link><description>&lt;p&gt;I quite enjoyed my recent foray into setting up an &lt;a href="https://www.linickx.com/example-hub--spoke-azure-firewall"&gt;example Azure Firewall&lt;/a&gt;, so he's a sequel! 🙃&lt;/p&gt;
&lt;p&gt;As before, the post will be screenshot heavy but not all screenshots, the plan is to deploy a vulnerable web application behind the WAF in blocking mode so we can see basic exploits being blocked.&lt;/p&gt;
&lt;h2&gt;Step 1 - Planning&lt;/h2&gt;
&lt;p&gt;Here's a basic picture of what we want to achieve...&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/09/WAF-Example-Drawing1.png"&gt;&lt;img src="/files/2020/09/WAF-Example-Drawing1.png"/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For the Web Server, we're going to run &lt;a href="http://www.dvwa.co.uk"&gt;Damn Vulnerable Web Application (DVWA)&lt;/a&gt; in an &lt;a href="https://azure.microsoft.com/en-gb/services/container-instances/"&gt;Azure Container Instance&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;Everything will be deployed into a single resource group to make cleanup/delete easier at the end.&lt;/p&gt;
&lt;h3&gt;Naming Convention&lt;/h3&gt;
&lt;p&gt;Here's a gotcha for Naming Conventions, in my last post I used hyphens, but container instances not allow that, so I'll have to plan a new way. &lt;/p&gt;
&lt;p&gt;I still want to follow the region followed by a primary identifier approach...&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;uksrgwaf → Resource Group&lt;br /&gt;
ukswaf1 → Azure Web Application Firewall&lt;br /&gt;
uksweb1 → 1st Web Server (Docker DVWA)&lt;br /&gt;
uksvnetwaf1 → WAF Network  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Something like that should do the trick!&lt;/p&gt;
&lt;h3&gt;Networking&lt;/h3&gt;
&lt;p&gt;Two networks will be needed, one for the Security Gateway and one for the Docker Web App, we'll then peer them together.... if you want to by-pass the WAF and access the App directly, plan yourself a 3rd network for a Virtual Windows box.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;uksvnetwaf1 → 172.16.1.0/24&lt;/li&gt;
&lt;li&gt;uksvnetweb1 → 172.16.2.0/24&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Step 2 - Create a Container Instance&lt;/h2&gt;
&lt;p&gt;The build takes a little while, so kick that off first.&lt;/p&gt;
&lt;p&gt;We're going to pull an image &lt;code&gt;vulnerables/web-dvwa&lt;/code&gt; from &lt;a href="https://hub.docker.com/r/vulnerables/web-dvwa/"&gt;the public docker hub&lt;/a&gt; based on linux.&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/09/01-uksweb1.png"&gt;&lt;img src="/files/2020/09/01-uksweb1.png" style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We want the networking type to be &lt;code&gt;Private&lt;/code&gt; as we're protecting this behind the WAF; I'm going to create a new vNet to meet my planning above, you are welcome to accept the defaults if you like.&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/09/01-uksweb1-vnet.png"&gt;&lt;img src="/files/2020/09/01-uksweb1-vnet.png" style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" /&gt;&lt;/a&gt;
&lt;a href="/files/2020/09/01-uksweb1-vnet-2.png"&gt;&lt;img src="/files/2020/09/01-uksweb1-vnet-2.png" style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Make no changes to Advanced Settings, skip the tags and hit create.&lt;/p&gt;
&lt;h2&gt;Step 3 - Create Log Analytics workspace&lt;/h2&gt;
&lt;p&gt;If you don't have one already, you need somewhere to send the logs...&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/09/02-ukslogs.png"&gt;&lt;img src="/files/2020/09/02-ukslogs.png" style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Step 4 - Create the WAF... well actually the Application Gateway.&lt;/h2&gt;
&lt;p&gt;So here's the thing, the WAF is part of the &lt;a href="https://azure.microsoft.com/services/application-gateway/"&gt;Azure Application Gateway&lt;/a&gt; product, which is actually a load balancer... so we're going to setup a basic load balancer and then enable the WAF functionaility. Of course we've only deployed "1x Web Server" (&lt;em&gt;docker container instance&lt;/em&gt;) but you get the gist!&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/09/03-ukswaf1.png"&gt;&lt;img src="/files/2020/09/03-ukswaf1.png" style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Change the Tier to &lt;code&gt;WAF V2&lt;/code&gt;, set the firewall mode to &lt;code&gt;Prevention&lt;/code&gt;, scroll down to networking and create a new vNet...&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/09/03-ukswaf1-vnet.png"&gt;&lt;img src="/files/2020/09/03-ukswaf1-vnet.png" style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For the frontend, setup a new PublicIP&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/09/03-ukswaf1-pubip.png"&gt;&lt;img src="/files/2020/09/03-ukswaf1-pubip.png" style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Next we need to setup a backend pool, for now select &lt;code&gt;Add backend pool without targets&lt;/code&gt; we'll come back to that later!&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/09/03-ukswaf1-pool.png"&gt;&lt;img src="/files/2020/09/03-ukswaf1-pool.png" style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;On the configuration page, notice how the left &amp;amp; right sides are already completed, we just need to add some rules in the middle...&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/09/03-ukswaf1-config.png"&gt;&lt;img src="/files/2020/09/03-ukswaf1-config.png" style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For the listener, setup a basic HTTP routing rule..&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/09/03-ukswaf1-rule-listener.png"&gt;&lt;img src="/files/2020/09/03-ukswaf1-rule-listener.png" style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And then select Backend targets, assign our pool and add a new HTTP Setting; the HTTP settings will be 100% defaults, this is all load balancer stuff that won't apply to us as we have 1x Web Server.&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/09/03-ukswaf1-rule-httpsettings.png"&gt;&lt;img src="/files/2020/09/03-ukswaf1-rule-httpsettings.png" style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" /&gt;&lt;/a&gt;
&lt;a href="/files/2020/09/03-ukswaf1-rule-backend.png"&gt;&lt;img src="/files/2020/09/03-ukswaf1-rule-backend.png" style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Skip over tags and we can finally create.&lt;/p&gt;
&lt;h3&gt;Step 5 - Start joining the dots.&lt;/h3&gt;
&lt;p&gt;At this stage we have two islands, a docker container and a WAF, they're not connected. The first thing you want to do it peer the vNets.&lt;/p&gt;
&lt;h4&gt;Peering&lt;/h4&gt;
&lt;p&gt;Open one of the vNets, I've selected &lt;code&gt;uksvnetwaf1&lt;/code&gt;, select peering on the left and add a peering. Give it a name like &lt;code&gt;uksvnetwaf1-to-uksvnetweb1&lt;/code&gt;, select &lt;code&gt;uksvnetweb1&lt;/code&gt; from the dropdown list and give that a name like &lt;code&gt;uksvnetweb1-to-uksvnetwaf1&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/09/04-vnet-peering.png"&gt;&lt;img src="/files/2020/09/04-vnet-peering.png" style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Scroll down to the bottom, and allow external traffic in...&lt;/p&gt;
&lt;h4&gt;Update the Pool&lt;/h4&gt;
&lt;p&gt;Remember that step we skipped when creating the WAF (Application Gateway)... time to fix that.&lt;/p&gt;
&lt;p&gt;Go into your container instance and grab the private IP&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/09/04-container-ip.png"&gt;&lt;img src="/files/2020/09/04-container-ip.png" style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now, go into your application gateway → backend pool and set the IP, mine is &lt;code&gt;172.16.2.4&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/09/04-waf-pool.png"&gt;&lt;img src="/files/2020/09/04-waf-pool.png" style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Diagnostic Settings&lt;/h4&gt;
&lt;p&gt;Remember that Log Analytics workspace we created; within your application gateway add some diagnostics pointing the logs to the workspace.&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/09/04-diagnostics.png"&gt;&lt;img src="/files/2020/09/04-diagnostics.png" style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" /&gt;&lt;/a&gt;
&lt;a href="/files/2020/09/04-diagnostics-settings.png"&gt;&lt;img src="/files/2020/09/04-diagnostics-settings.png" style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Step 6 - Bootstrap DVWA&lt;/h3&gt;
&lt;p&gt;If all has gone well, in a new tab/window you can browse to your public IP http://a.b.c.d and you should get the DVWA log in screen. &lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/09/06-dvwa-login.png"&gt;&lt;img src="/files/2020/09/06-dvwa-login.png" style="display: block;margin-left: auto;margin-right: auto;max-width: 50%;"/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The default credentials are &lt;code&gt;admin&lt;/code&gt; &amp;amp; &lt;code&gt;password&lt;/code&gt;, scroll to the bottom and click &lt;code&gt;create / reset database&lt;/code&gt; , you'll be redirected to the login screen where you can log back in.&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/09/06-dvwa-setup.png"&gt;&lt;img src="/files/2020/09/06-dvwa-setup.png" style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;At this stage you might want to change the password (under the CSRF menu) so you can play in peace, but just remember this thing is designed to be hacked 🙃&lt;/p&gt;
&lt;h3&gt;Step 7 - Lets test!&lt;/h3&gt;
&lt;p&gt;To prove the functionality we're going to do a couple of basic tests, a command injection and an SQL Inject, we're also going to disable a WAF RULE to show how to switch stuff off.&lt;/p&gt;
&lt;h4&gt;How to disable a rule&lt;/h4&gt;
&lt;p&gt;The WAF comes with a bunch of rules enabled by default, if you're deploying this into production the developers are going to be very concerned about the WAF blocking legitimate requests. To demonstrate the steps, we're going to disable the rule that flags "IP in header"; by default you should browser a site by it's name, but in our test we don't have one so let's switch off that rule.&lt;/p&gt;
&lt;p&gt;Go into your logs, and run this query to find WAF events...&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;AzureDiagnostics
| where ResourceType == &amp;quot;APPLICATIONGATEWAYS&amp;quot; and OperationName == &amp;quot;ApplicationGatewayFirewall&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Scroll to the right you should see message &lt;code&gt;Host header is a numeric IP address&lt;/code&gt; with the rule ID &lt;code&gt;920350&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/09/07-HostHeader-Logs.png"&gt;&lt;img src="/files/2020/09/07-HostHeader-Logs.png" style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now go into Web Application Firewall → Rules and enable &lt;code&gt;advanced configuration&lt;/code&gt;, search for 920350 and untick the box... then click save.&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/09/07-disable-920350.png"&gt;&lt;img src="/files/2020/09/07-disable-920350.png" style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Job done, that should clean out your logs a bit for the next test.&lt;/p&gt;
&lt;h4&gt;Blocking Command Injection&lt;/h4&gt;
&lt;p&gt;We're going nice and simple, from your DVWA tab, select &lt;em&gt;Command Injection&lt;/em&gt; and type in &lt;code&gt;127.0.0.1;cat /etc/password&lt;/code&gt; and click submit.&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/09/07-command-injection.png"&gt;&lt;img src="/files/2020/09/07-command-injection.png" style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You should get a forbidden page, notice how it says &lt;em&gt;Microsoft-Azure-Application-Gateway/v2&lt;/em&gt; ... the WAF blocked the attack. At this point be a little patient, it can take a while for logs to show up in the azure portal, but this time run:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;AzureDiagnostics
| where ResourceType == &amp;quot;APPLICATIONGATEWAYS&amp;quot; and ruleId_s == &amp;quot;932100&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can see the command matched a rule that caused the block.&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/09/07-rce-logs.png"&gt;&lt;img src="/files/2020/09/07-rce-logs.png" style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Blocking SQL Injection&lt;/h4&gt;
&lt;p&gt;For the 2nd test, browse to SQL injection and type in &lt;code&gt;%' or '0'='0&lt;/code&gt; and click submit.&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/09/07-SQL-Injection.png"&gt;&lt;img src="/files/2020/09/07-SQL-Injection.png" style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Once again, you should see the blank Azure 403 forbidden page! &lt;/p&gt;
&lt;p&gt;Run this log search:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;AzureDiagnostics
| where ResourceType == &amp;quot;APPLICATIONGATEWAYS&amp;quot; and OperationName == &amp;quot;ApplicationGatewayFirewall&amp;quot; and Message contains &amp;quot;SQL&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can see the SQL Injection was detected!&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/09/07-sqli-logs.png"&gt;&lt;img src="/files/2020/09/07-sqli-logs.png" style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;/End&lt;/h3&gt;
&lt;p&gt;That was another long post but we got there in the end, there's a lot to unpack there from setting up a docker container to disabling a WAF rule. As a reader there's a couple of area's you can follow up with, if you're not familiar with the basic exploits, set the WAF to &lt;em&gt;detection mode&lt;/em&gt; to view DVWA get compromised, you will also want to spend some time looking at the logs and preparing &lt;em&gt;better&lt;/em&gt; searches as  my examples are very specific to this post.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Nick Bettison</dc:creator><pubDate>Sat, 05 Sep 2020 09:16:00 +0100</pubDate><guid isPermaLink="false">tag:www.linickx.com,2020-09-05:example-azure-web-application-firewall-waf</guid><category>Security</category><category>Azure</category><category>WAF</category><category>Firewall</category></item><item><title>Example Hub &amp; Spoke Azure Firewall</title><link>https://www.linickx.com/example-hub--spoke-azure-firewall</link><description>&lt;p&gt;It's been a while since I've done a Technical How-to, recently I wanted to get my head around Azure's firewall appliance, here's an example setup and in true MicroSoft style it comes complete with Point-n-Click screenshots! I'm assuming you at least know how to navigate Azure so post doesn't contain all the possible screenshots, for example I will skip screenshots for creating tags, but that all being said, it's still going to be a long one, hold on tight!&lt;/p&gt;
&lt;h2&gt;Step1 - Planning&lt;/h2&gt;
&lt;p&gt;Whilst reading the doc's and getting to grips with the &lt;a href="https://portal.azure.com"&gt;Azure Portal&lt;/a&gt; it became clear to me, you really need to plan before clicking, it's sooo easy to get stuff running that I'm confident that most orgs will have objects with generic names like "monitoring server" and then forget details like, which region is the server hosted!&lt;/p&gt;
&lt;p&gt;This picture is basically what I want to achieve, 2 servers separated by a firewall appliance for access to the Internet and each other; spoke 1 and 2 are isolated zones.&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/08/AZEGFW-01-Overview.png"&gt;&lt;img alt="Firewall Overview Diagram" src="/files/2020/08/AZEGFW-01-Overview.png" title="Firewall Overview Diagram"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Naming Convention&lt;/h3&gt;
&lt;p&gt;To get started I planned a basic naming standard using region followed by a primary identifier and then a description... &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;UKS-FW-vNET → UK (South) Firewall Virtual Network
or
UKS-Route-Spoke1-to-Hub → UK (South) Routing table, Spoke 1 static routes to Hub
or
UKS-Spoke1-VM1 → UK (South), Spoke 1, Virtual Machine
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I'm sure it could be better or more rigid but it should be enough for me to know what's what. In the GUI you can group by type &amp;amp; region so you don't really need these in your naming convention, but it's useful to know priority, in my examples the type "Route" is more important that if it's for Spoke1 or Spoke2 ... but for the VM, the location is more important... it's just a lab, do whatever you want 😉&lt;/p&gt;
&lt;h3&gt;Resource Group(s)&lt;/h3&gt;
&lt;p&gt;For this example, I'm creating a single resource group and putting it all there; in the real world we would expect to see different networks &amp;amp; VM's in different groups; I'm calling mine &lt;code&gt;FW-Test&lt;/code&gt; plan what you need.&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/08/AZEGFW-02-ResourceGroup.png"&gt;&lt;img style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" alt="Resource Group" src="/files/2020/08/AZEGFW-02-ResourceGroup.png" title="Resource Group"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Networking (IP Addressing)&lt;/h3&gt;
&lt;p&gt;Before defining the IP structure, think about DNS, by default all networks/devices use Microsoft's DNS but now-a-days we expect our DNS provider to add some security filtering &amp;amp; protection, so I'll be using Cisco's Umbrella (OpenDNS): &lt;code&gt;208.67.222.222&lt;/code&gt; &amp;amp; &lt;code&gt;208.67.220.220&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Each &lt;em&gt;Virtual Network&lt;/em&gt; is like a zone, it lives in a region and it can be calved up into smaller subnets when needed. I'll be working in &lt;code&gt;/23&lt;/code&gt; chunks, with the first &lt;code&gt;/24&lt;/code&gt; as the "main" subnet leaving the 2nd &lt;code&gt;/24&lt;/code&gt; to be used for &lt;em&gt;other stuff&lt;/em&gt;, I'm also going to use the &lt;code&gt;172.16&lt;/code&gt; RFC1918 space instead of the &lt;code&gt;10.x&lt;/code&gt; generated by Azure to really help show how networking hangs together in this example.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;UKS-FW-vNET1 → &lt;code&gt;172.16.0.0/24&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;UKS-vNET-Spoke1 → &lt;code&gt;172.16.2.0/23&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;UKS-Spoke1-Subnet1 → &lt;code&gt;172.16.2.0/24&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;UKS-vNET-Spoke2 → &lt;code&gt;172.16.4.0/23&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;UKS-Spoke2-Subnet1 → &lt;code&gt;172.16.4.0/24&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Create UKS-vNET-Spoke1 &amp;amp; UKS-vNET-Spoke2&lt;/h3&gt;
&lt;p&gt;Create the networks spoke networks first but not the firewall vNET, we'll do that after with the FW Appliance; There's nothing special about the Spokes at this stage, here is Spoke 1&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/08/AZEGFW-03-SpokevNet1.png"&gt;&lt;img style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" alt="Create First vNet" src="/files/2020/08/AZEGFW-03-SpokevNet1.png" title="Create First vNet"&gt;&lt;/a&gt;
&lt;a href="/files/2020/08/AZEGFW-03-SpokevNet2.png"&gt;&lt;img style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" alt="Add A Subnet" src="/files/2020/08/AZEGFW-03-SpokevNet2.png" title="Add A Subnet"&gt;&lt;/a&gt;
&lt;a href="/files/2020/08/AZEGFW-03-SpokevNet3.png"&gt;&lt;img style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" alt="Disable Security Settings" src="/files/2020/08/AZEGFW-03-SpokevNet3.png" title="Disable Security Settings"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Repeat for Spoke2.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;After both vNets are built, if you want to use Custom DNS make that change...&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/08/AZEGFW-03-SpokevNet4-DNS.png"&gt;&lt;img style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" alt="Update DNS" src="/files/2020/08/AZEGFW-03-SpokevNet4-DNS.png" title="Update DNS"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Step 2 - Create the Firewall&lt;/h2&gt;
&lt;p&gt;The firewall build takes a little time, let's kick that off! &lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/08/AZEGFW-04-CreateFW1.png"&gt;&lt;img style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" alt="Create FW" src="/files/2020/08/AZEGFW-04-CreateFW1.png" title="Create FW"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Under the firewall settings, we're going to now define the vNET... notice how the subnet has a special (reserved) name; we also need to create and assign a public IP.&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/08/AZEGFW-04-CreateFW2.png"&gt;&lt;img style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" alt="FW Settings" src="/files/2020/08/AZEGFW-04-CreateFW2.png" title="FW Settings"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Whilst you're waiting for the deployment...&lt;/p&gt;
&lt;h3&gt;Setup Logging&lt;/h3&gt;
&lt;p&gt;By default you don't get firewall logs, we need to switch that on. Before you start you need a Log Analytics workspace, go and create that.&lt;/p&gt;
&lt;h3&gt;Create Log Analytics workspace&lt;/h3&gt;
&lt;p&gt;This is how you access or visualise the FW logs, if you're using the eval/free subscription like my screenshots you'll need to select &lt;em&gt;pay-as-you-go&lt;/em&gt; pricing, I've not been charged anything 😬&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/08/AZEGFW-05-CreateLAW1.png"&gt;&lt;img style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" alt="Log Workspace Settings" src="/files/2020/08/AZEGFW-05-CreateLAW1.png" title="Log Workspace Settings"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Setup Diagnostics&lt;/h4&gt;
&lt;p&gt;Back on the Firewall, if it's done, setup logging, it's under Diagnostics Settings.... &lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/08/AZEGFW-06-FWDiag1.png"&gt;&lt;img style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" alt="FW Diag Settings" src="/files/2020/08/AZEGFW-06-FWDiag1.png" title="FW Diag Settings"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Add a new Diagnostics Setting.&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/08/AZEGFW-06-FWDiag2.png"&gt;&lt;img style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" alt="FW Diag Settings2 " src="/files/2020/08/AZEGFW-06-FWDiag2.png" title="FW Diag Settings 2"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Tick all the lefthand boxes, we want all them logs! On the right, setup Log Analytics to the workspace you created. At this point, don't expect to see anything under the FW logs section, not only do we not have any traffic yet but it can take ~10mins before any hits appear in the logs.&lt;/p&gt;
&lt;h3&gt;Create some Rules&lt;/h3&gt;
&lt;p&gt;We're ready at this point to create some rules, we're only going to work with &lt;em&gt;Add network rule collection&lt;/em&gt; rules.&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/08/AZEGFW-07-Rules1.png"&gt;&lt;img style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" alt="FW Rules 1 " src="/files/2020/08/AZEGFW-07-Rules1.png" title="FW Rules 1"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is where we start and are aiming for, so click &lt;em&gt;add network rule collection&lt;/em&gt; and fill in these details...&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Name: Basic-Networking&lt;/li&gt;
&lt;li&gt;Priority: 100&lt;/li&gt;
&lt;li&gt;Action: Allow&lt;/li&gt;
&lt;li&gt;Rule 1: &lt;ul&gt;
&lt;li&gt;Name: DNS&lt;/li&gt;
&lt;li&gt;Protocol: TCP &amp;amp; UDP&lt;/li&gt;
&lt;li&gt;Source Type: IP Address&lt;/li&gt;
&lt;li&gt;Source: 172.16.0.0/12&lt;/li&gt;
&lt;li&gt;Destination Type: IP Address&lt;/li&gt;
&lt;li&gt;Destination Address: 208.67.222.222, 208.67.220.220&lt;/li&gt;
&lt;li&gt;Destination Ports:&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Rule 2:&lt;ul&gt;
&lt;li&gt;Name: NTP&lt;/li&gt;
&lt;li&gt;Protocol: UDP&lt;/li&gt;
&lt;li&gt;Source Type: IP Address&lt;/li&gt;
&lt;li&gt;Source: 172.16.0.0/12&lt;/li&gt;
&lt;li&gt;Destination Type: IP Address&lt;/li&gt;
&lt;li&gt;Destination Address: *&lt;/li&gt;
&lt;li&gt;Destination Ports: 123&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href="/files/2020/08/AZEGFW-07-Rules2.png"&gt;&lt;img style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" alt="FW Rules 2 " src="/files/2020/08/AZEGFW-07-Rules2.png" title="FW Rules 2"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We want a nice and simple policy to test basic functionality; repeat the process for the following rules...&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Name: HTTP-HTTPS&lt;/li&gt;
&lt;li&gt;Priority: 1000&lt;/li&gt;
&lt;li&gt;Action: Allow&lt;/li&gt;
&lt;li&gt;Rule 1: &lt;ul&gt;
&lt;li&gt;Name: HTTP&lt;/li&gt;
&lt;li&gt;Protocol: TCP&lt;/li&gt;
&lt;li&gt;Source Type: IP Address&lt;/li&gt;
&lt;li&gt;Source: 172.16.0.0/12&lt;/li&gt;
&lt;li&gt;Destination Type: IP Address&lt;/li&gt;
&lt;li&gt;Destination Address: *&lt;/li&gt;
&lt;li&gt;Destination Ports: 80&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Rule 2:&lt;ul&gt;
&lt;li&gt;Name: HTTPS&lt;/li&gt;
&lt;li&gt;Protocol: TCP&lt;/li&gt;
&lt;li&gt;Source Type: IP Address&lt;/li&gt;
&lt;li&gt;Source: 172.16.0.0/12&lt;/li&gt;
&lt;li&gt;Destination Type: IP Address&lt;/li&gt;
&lt;li&gt;Destination Address: *&lt;/li&gt;
&lt;li&gt;Destination Ports: 443&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;and&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Name: Default-Deny&lt;/li&gt;
&lt;li&gt;Priority: 65000&lt;/li&gt;
&lt;li&gt;Action: Deny&lt;/li&gt;
&lt;li&gt;Rule 1: &lt;ul&gt;
&lt;li&gt;Name: Deny&lt;/li&gt;
&lt;li&gt;Protocol: Any&lt;/li&gt;
&lt;li&gt;Source Type: IP Address&lt;/li&gt;
&lt;li&gt;Source: *&lt;/li&gt;
&lt;li&gt;Destination Type: IP Address&lt;/li&gt;
&lt;li&gt;Destination Address: *&lt;/li&gt;
&lt;li&gt;Destination Ports: *&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You should end up with something like this:&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/08/AZEGFW-07-Rules3.png"&gt;&lt;img style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" alt="FW Rules 3 " src="/files/2020/08/AZEGFW-07-Rules3.png" title="FW Rules 3"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Azure &amp;amp; ICMP&lt;/h4&gt;
&lt;p&gt;Ok, a bit of an Azure quirk here! There's a few google hits of people complaining that you cannot "ping out" from Azure to the Internet, but if you want to be able to ping between spokes you can do that... weirdly tho, restricting the rule by IP/Subnet doesn't work, you have to setup an "any" rule 😒&lt;/p&gt;
&lt;p&gt;If you want one, create a rule using these settings...&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Name: ICMP&lt;/li&gt;
&lt;li&gt;Priority: 101&lt;/li&gt;
&lt;li&gt;Action: Allow&lt;/li&gt;
&lt;li&gt;Rule 1: &lt;ul&gt;
&lt;li&gt;Name: ICMP&lt;/li&gt;
&lt;li&gt;Protocol: Any&lt;/li&gt;
&lt;li&gt;Source Type: IP Address&lt;/li&gt;
&lt;li&gt;Source: *&lt;/li&gt;
&lt;li&gt;Destination Type: IP Address&lt;/li&gt;
&lt;li&gt;Destination Address: *&lt;/li&gt;
&lt;li&gt;Destination Ports: *&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Step 3 Peering&lt;/h2&gt;
&lt;p&gt;Now we're onto some good stuff! You have 3x Virtual networks, and they're isolated from each other, our target is to route these via our firewall, we do this by setting up peering. We need two peering configs:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Peering on UKS-FW-vNET1 to UKS-vNET-Spoke1&lt;/li&gt;
&lt;li&gt;Peering on UKS-FW-vNET1 to UKS-vNET-Spoke2&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In new versions of the GUI (&lt;em&gt;you'll not some differences between what you see and the official documentation&lt;/em&gt;) this will create Peering back the other way, I guess this had to be done manually before!&lt;/p&gt;
&lt;p&gt;Navigate to the vNET: &lt;code&gt;UKS-FW-vNET1&lt;/code&gt; and click &lt;em&gt;Add Peering&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/08/AZEGFW-08-AddPeering1.png"&gt;&lt;img style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" alt="vNET Peering!" src="/files/2020/08/AZEGFW-08-AddPeering1.png" title="Peering"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Take note of the bottom &lt;strong&gt;Enabled&lt;/strong&gt; if that's not on, then Spoke 2 won't be able to speak to Spoke 1. If your vNET has an onward connect to a VPN gateway then you'll need to tick the bottom box.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Repeat for Spoke2.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Step 4 Create Some Routes&lt;/h2&gt;
&lt;p&gt;We need 2x routes, I reckon we could probably get away with bundling it together as one, but this feels cleaner and I know it works! 😝&lt;/p&gt;
&lt;p&gt;The plan is to setup a default route for UKS-Spoke1-Subnet1/2 to the Firewall, we don't need static routes back the other way, that's sorted by the peering, these static routes give us Internet access and Spoke-to-Spoke connectivity.&lt;/p&gt;
&lt;p&gt;Create a new Route Table: UKS-Route-Spoke1-to-Hub&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/08/AZEGFW-09-CreateRouteTable1.png"&gt;&lt;img style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" alt="Create Route Table" src="/files/2020/08/AZEGFW-09-CreateRouteTable1.png" title="Routing"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For the next step, you need to pop back to the firewall and grab the private IP... in my example it's &lt;code&gt;172.16.0.4&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/08/AZEGFW-09-CreateRouteTable2-FWIP.png"&gt;&lt;img style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" alt="FW Private IP" src="/files/2020/08/AZEGFW-09-CreateRouteTable2-FWIP.png" title="FW Private IP"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;No go to the Route Table and add a default route, the gateway IP is going to the the private IP you just copied from the FW.&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/08/AZEGFW-09-CreateRouteTable3.png"&gt;&lt;img style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" alt="Add Default Route" src="/files/2020/08/AZEGFW-09-CreateRouteTable3.png" title="DF RT"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Then under Subnets, associate the Spoke1 Subnet.&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/08/AZEGFW-09-CreateRouteTable4.png"&gt;&lt;img style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" alt="Associate Subnets" src="/files/2020/08/AZEGFW-09-CreateRouteTable4.png" title="Associate Subnets"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;All that done, now &lt;strong&gt;Repeat for UKS-Route-Spoke2-to-Hub!&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Step 5 Add some machines&lt;/h2&gt;
&lt;p&gt;Foundations laid, we have something to stand a computer on lets build a couple of test machines. We'll use a windows server as a GUI client and a linux box as a server.&lt;/p&gt;
&lt;h3&gt;Windows - Spoke 1&lt;/h3&gt;
&lt;p&gt;Build a basic Windows 2019 Server...&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/08/AZEGFW-10-CreateVM1.png"&gt;&lt;img style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" alt="Build a VM" src="/files/2020/08/AZEGFW-10-CreateVM1.png" title="Build a VM"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Default Disk Settings is fine, but Networking settings are important, assign the server to Spoke1, Subnet1 and &lt;strong&gt;disable public IPS&lt;/strong&gt; ...&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/08/AZEGFW-10-CreateVM2.png"&gt;&lt;img style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" alt="VM Networking" src="/files/2020/08/AZEGFW-10-CreateVM2.png" title="VM Networking"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Default settings for Management and advanced also fine.&lt;/p&gt;
&lt;h3&gt;Linux - Spoke 2&lt;/h3&gt;
&lt;p&gt;Build a Linux Server, pick whatever distro makes you happy; you can build a 2nd windows box if you like, but you'll have to install IIS afterwards for some sensible testing.&lt;/p&gt;
&lt;p&gt;We're basically repeating the steps from above, the networking tab is the detail to pay attention to...&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/08/AZEGFW-11-CreateVM1.png"&gt;&lt;img style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" alt="Build another VM" src="/files/2020/08/AZEGFW-11-CreateVM1.png" title="Build another VM"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Step 6 - Bastions, get access to the machines.&lt;/h2&gt;
&lt;p&gt;As those servers build/deploy you'll be thinking, how am I going to access them?! Well, remember we left some space in the Subnets/vNETs, we'll use &lt;a href="https://docs.microsoft.com/en-gb/azure/bastion/bastion-overview"&gt;Microsoft's Bastion solution&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Go to your VM, scroll to the bottom and select &lt;em&gt;Bastion&lt;/em&gt;, you'll notice there's a red error!&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/08/AZEGFW-12-Bastion1.png"&gt;&lt;img style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" alt="Bastions!" src="/files/2020/08/AZEGFW-12-Bastion1.png" title="Bastions!"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Click on &lt;em&gt;Manage Subnet Configuration&lt;/em&gt; ... you need to create a subnet called &lt;code&gt;AzureBastionSubnet&lt;/code&gt; ← That's a special/reserved name. Bastion's only need a small &lt;code&gt;/27&lt;/code&gt; subnet.&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/08/AZEGFW-12-Bastion2.png"&gt;&lt;img style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" alt="AzureBastionSubnet" src="/files/2020/08/AZEGFW-12-Bastion2.png" title="AzureBastionSubnet"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Once the subnet has been created you can then click the create button with no errors. Repeat for Spoke 2.&lt;/p&gt;
&lt;h2&gt;Test&lt;/h2&gt;
&lt;p&gt;Finally! After all that setup, we're ready to log into a server. Connect to your linux box first (&lt;em&gt;Using the Bastion Option&lt;/em&gt;), install apache &amp;amp; start it.&lt;/p&gt;
&lt;p&gt;Now connect to your Windows box:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;open Internet Explorer you should be able to browse the web. &lt;a href="https://knowledge.autodesk.com/search-result/caas/sfdcarticles/sfdcarticles/Disable-IE-Enhanced-Security-on-Windows-Server.html"&gt;Disable Enhanced Security&lt;/a&gt; if you want/need to! (Note: That's why I'm using Umbrella as a compensating control)&lt;/li&gt;
&lt;li&gt;Now connect to your linux box, that should work to&lt;/li&gt;
&lt;li&gt;Open a Command Prompt, if you created an ICMP rule, ping should work. If you are pining from the linux box, remember windows servers come with host based firewalls, you'll need a rule there to allow ICMP as well.&lt;/li&gt;
&lt;li&gt;Finally, download putty and try to SSH to your linux box, that should fail!&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;(&lt;em&gt;No Screenshots here, I'm sure you can figure that out&lt;/em&gt; 😅)&lt;/p&gt;
&lt;h3&gt;Check the firewall logs!&lt;/h3&gt;
&lt;p&gt;If all your tests went as designed, you'll need to filter the firewall logs to see them as any Internet traffic will be filling up the logs.&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/2020/08/AZEGFW-13-FW-Logs1.png"&gt;&lt;img style="display: block;margin-left: auto;margin-right: auto;max-width: 90%;" alt="Internal FW Traffic" src="/files/2020/08/AZEGFW-13-FW-Logs1.png" title="FW Logs!"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;END&lt;/h2&gt;
&lt;p&gt;I hope this is useful to someone, it took me a little while and &lt;a href="https://twitter.com/linickx/status/1294934305014153216"&gt;some twitter frustration&lt;/a&gt; to get there, but I'm pleased with the result! If I can find the time, I'd like to re-create this in powershell and automate all the things!&lt;/p&gt;
&lt;p&gt;References:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;https://docs.microsoft.com/en-us/azure/firewall/overview&lt;/li&gt;
&lt;li&gt;https://docs.microsoft.com/en-us/azure/firewall-manager/secure-hybrid-network&lt;/li&gt;
&lt;li&gt;https://docs.microsoft.com/en-us/azure/firewall/logs-and-metrics&lt;/li&gt;
&lt;/ul&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Nick Bettison</dc:creator><pubDate>Wed, 19 Aug 2020 11:49:00 +0100</pubDate><guid isPermaLink="false">tag:www.linickx.com,2020-08-19:example-hub--spoke-azure-firewall</guid><category>Security</category><category>Azure</category><category>Firewall</category></item></channel></rss>