Ne
Networking Azure

NetAlertX on Ubuntu 24.04 on Azure User Guide

| Product: NetAlertX on Ubuntu 24.04 LTS on Azure

Overview

This image runs NetAlertX 26.7.1 on Ubuntu 24.04 LTS. NetAlertX (the project formerly known as Pi.Alert) is an open source network presence scanner and asset discovery framework. It sweeps the subnet it is attached to on a schedule, keeps an inventory of every device it has ever seen, records a presence timeline for each one, and raises an alert when a new device appears or a known device stops answering.

It answers a question most networks cannot answer honestly: what is actually on this network right now, and what changed?

What is included:

  • NetAlertX 26.7.1, installed from the pinned upstream source tree, with its Python scanning and API server and its PHP web interface
  • nginx on port 80 as the authentication gate, with a password generated uniquely on your VM's first boot
  • SQLite as the device inventory and presence store
  • The scanner toolchain: fping, nmap, arp-scan, dnsutils, nbtscan, avahi-utils, traceroute, mtr
  • A scan scope automatically limited to your VM's own subnet on first boot

How this image is secured

NetAlertX upstream ships with its login switched off by default, and when you switch it on it hashes with unsalted SHA256 against a password published in its own documentation. Its web interface is also the control plane for a tool that scans your network. That is a reasonable default for a device on a trusted home LAN. It is not a reasonable default for a virtual machine with a public IP address.

So this image does not rely on it:

  • nginx is the authentication gate. Every page requires a username and password that was generated on your VM, on its first boot, and written to a file only root can read. Nothing in the image knows it, and no two VMs share one.
  • The application's API is bound to the loopback interface, so the gate cannot be bypassed by connecting to the API port directly.
  • There is no default password anywhere in this image, and the services refuse to start if a credential has not been generated.

A note on what this product does

NetAlertX scans networks. Read the Scanning scope and authorisation section before you widen the scan scope. In short: only scan networks you own or are explicitly authorised to scan.

Prerequisites

  • An Azure subscription
  • The Azure CLI, if you deploy from the command line
  • An SSH key pair
  • A VM size of Standard_B2s or larger

Step 1 - Deploy from the Azure Marketplace

  1. In the Azure portal, search the Marketplace for NetAlertX on Ubuntu 24.04 LTS by cloudimg.
  2. Select Create.
  3. Choose your subscription, resource group and region.
  4. Set the VM size to Standard_B2s or larger.
  5. Set the authentication type to SSH public key and the username to azureuser.
  6. On the Networking tab, allow inbound SSH (22) and HTTP (80).
  7. Select Review + create, then Create.

Restrict inbound port 80 to the addresses you will administer from. This is a management interface for a network scanner and it should not be open to the internet.

Step 2 - Deploy from the Azure CLI

az group create --name netalertx-rg --location eastus

az vm create \
  --resource-group netalertx-rg \
  --name netalertx-vm \
  --image cloudimg:netalertx-ubuntu-24-04:default:latest \
  --size Standard_B2s \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-sku Standard

Then open the ports you need, scoped to your own address:

# SSH and the web interface, restricted to your own public IP
MYIP="$(curl -s https://ifconfig.me)"
az vm open-port --resource-group netalertx-rg --name netalertx-vm --port 22 --priority 1001
az network nsg rule create \
  --resource-group netalertx-rg \
  --nsg-name netalertx-vmNSG \
  --name allow-web-from-me \
  --priority 1002 \
  --source-address-prefixes "${MYIP}/32" \
  --destination-port-ranges 80 \
  --access Allow --protocol Tcp

Step 3 - Connect to your VM

ssh azureuser@<vm-public-ip>

Step 4 - Confirm the services are running

Three services make up the appliance: the NetAlertX scanner and API server, the PHP processor, and the nginx gate in front of them.

systemctl is-active netalertx nginx php8.3-fpm

Expected output:

active
active
active

Confirm the network listeners. This is the security property that matters most: nginx is on port 80, and the application's API is on the loopback interface only.

sudo ss -lntp | grep -E ':80|:20212'

Expected output (process details trimmed):

LISTEN 0  511        0.0.0.0:80      0.0.0.0:*    users:(("nginx",...))
LISTEN 0  128      127.0.0.1:20212   0.0.0.0:*    users:(("python3",...))

127.0.0.1:20212 is the important line. The API is not reachable from anywhere but the VM itself, so the nginx password gate is the only way in.

The three NetAlertX services reporting active, with nginx listening on port 80 and the application API bound to 127.0.0.1:20212 so the authentication gate cannot be bypassed

Step 5 - Retrieve your per-VM password

The password was generated on this VM's first boot. It is not in the image.

sudo cat /root/netalertx-credentials.txt

Expected output (your values will differ):

# NetAlertX -- credentials for THIS VM, generated on first boot.
# Generated: 2026-07-16T18:33:04Z
#
# These values are unique to this virtual machine. They are not present in the cloudimg
# image and cloudimg does not know them.

netalertx_url=http://<vm-public-ip>/
netalertx_admin_user=admin
netalertx_admin_password=<admin-password>

# The web interface is protected by the reverse proxy using the username and password
# above. The NetAlertX API uses its own bearer token:
netalertx_api_token=<api-token>

The per VM NetAlertX credentials file, showing the admin user, the generated password redacted, the API bearer token redacted, and the dashboard URL for this instance

Step 6 - Confirm the gate is protecting the interface

Prove the authentication gate works before you rely on it. Without credentials the interface returns 401:

curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1/devices.php

Expected output:

401

With your per-VM password it returns 200:

PW="$(sudo awk -F= '/^netalertx_admin_password=/{print $2; exit}' /root/netalertx-credentials.txt)"
curl -s -o /dev/null -w '%{http_code}\n' -u "admin:${PW}" http://127.0.0.1/devices.php

Expected output:

200

A wrong password is rejected:

curl -s -o /dev/null -w '%{http_code}\n' -u "admin:not-the-password" http://127.0.0.1/devices.php

Expected output:

401

The health endpoint is deliberately anonymous, so an Azure Load Balancer probe can reach it without a credential. It reports only that nginx is alive:

curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1/healthz

Expected output:

200

The NetAlertX web interface returning 401 without credentials and 200 with the per VM password, a wrong password rejected with 401, and the anonymous health endpoint returning 200

Step 7 - Confirm the scan scope

On first boot the appliance read your VM's own network interface and scoped the scan to that subnet only.

grep '^SCAN_SUBNETS=' /app/config/app.conf
ip -4 -o addr show dev eth0 | awk '{print "interface network: " $4}'

Expected output (your subnet will differ):

SCAN_SUBNETS=['10.0.0.0/24 --interface=eth0']
interface network: 10.0.0.12/24

The configured scope is the network of the VM's own interface. It was derived on your VM, from your VM's address. The image does not ship a scan range.

The scan scope limited to the VM's own subnet, matching the network of its own interface, with the ICMP scanner enabled and synthetic MAC addressing on for Azure virtual networks

Step 8 - Open the dashboard

Browse to your VM and sign in with the username and password from Step 5:

http://<vm-public-ip>/

Your browser will prompt for the username and password. The dashboard shows how many devices are known, how many are present right now, and what has changed recently.

The NetAlertX dashboard showing the device counts for the scanned subnet, with connected, new and down tiles and the recent activity summary

Step 9 - Review the device inventory

Select Devices. Every host the scanner has found on your subnet is listed, with its address, its name where one could be resolved, and whether it is present right now.

The NetAlertX Devices view listing the hosts discovered on the VM's own subnet, each with its own address, resolved name and current presence state

Step 10 - Inspect a device

Select any device to open its detail view. Each device has its own identity, session count, notification settings and network details, across the Details, Tools, Sessions, Presence, Events and Plugins tabs.

A NetAlertX device detail view for a discovered host, showing its synthetic MAC identifier, its address, its session count and the notification and network settings for that device

Step 11 - Review the presence timeline

Select Monitoring > Presence. This is the heart of the product: a per-device timeline of when each host has actually been on your network, built from every scan the appliance has run.

The NetAlertX presence view showing the device presence chart over successive scans, and a per device presence calendar covering the week for every discovered host

Why this image scans with ICMP rather than ARP

NetAlertX's usual default scanner is arp-scan, and on a normal physical LAN it is the right choice: it returns each device's real hardware address, which is a stable identity even when addresses change.

Azure virtual networks do not work that way. Azure's software defined network answers every ARP request with the same synthetic hardware address:

sudo arp-scan --localnet --interface=eth0 2>/dev/null | head -6

Example output from an Azure VM:

Interface: eth0, type: EN10MB, MAC: 00:0d:3a:9c:4b:73, IPv4: 10.0.0.12
10.0.0.1        12:34:56:78:9a:bc       (Unknown: locally administered)
10.0.0.4        12:34:56:78:9a:bc       (Unknown: locally administered)
10.0.0.5        12:34:56:78:9a:bc       (Unknown: locally administered)

Every host reports 12:34:56:78:9a:bc. Because NetAlertX keys its inventory on the hardware address, arp-scan on Azure would collapse your entire network into a single device.

This image therefore uses NetAlertX's ICMP scanner with synthetic addressing (ICMP_FAKE_MAC), which derives a stable, clearly marked fa:ce:... identifier per address, so each host on your virtual network is tracked as its own device. Hosts whose real address the appliance can resolve, such as the VM itself, keep it.

If you attach this VM to a network where ARP does return real hardware addresses, you can enable ARPSCAN in Settings.

Step 12 - Widen or change the scan scope

Read Scanning scope and authorisation first.

The supported way is Settings > Core > Scan subnets in the web interface. Set the scan scope to the networks you are authorised to scan, each with the interface to reach it on, then Save:

SCAN_SUBNETS=['10.0.0.0/24 --interface=eth0', '10.1.0.0/24 --interface=eth0']

The same setting lives in /app/config/app.conf if you prefer to manage it with a configuration tool. Confirm what is currently configured:

grep '^SCAN_SUBNETS=' /app/config/app.conf

If you edit the file directly, restart the scanner so it re-reads the configuration into its settings store:

sudo systemctl restart netalertx

To scan a peered virtual network, an on-premises network over a VPN or ExpressRoute, or any other subnet, the VM needs a network route to it and your Network Security Groups must permit ICMP.

Step 13 - Set up alerting

NetAlertX raises events when a device appears or disappears. To be told about them, enable a notification plugin in Settings. Email, webhooks, MQTT, ntfy, Pushover, Pushsafer, Telegram and Apprise are all included.

For email, set SMTP_SERVER, SMTP_PORT, SMTP_USER and SMTP_PASS in Settings > SMTP, then set SMTP_RUN to on_notification. Send yourself a test event from Maintenance > Test notifications.

Credentials you enter here are yours and are stored in /app/config/app.conf on your VM only.

Step 14 - Use the API

Every view in the interface is backed by an API that you can use directly. It authenticates with the bearer token from your credentials file, and it listens on the loopback interface, so use it from the VM or over an SSH tunnel.

TOKEN="$(sudo awk -F= '/^netalertx_api_token=/{print $2; exit}' /root/netalertx-credentials.txt)"
curl -s -H "Authorization: Bearer ${TOKEN}" http://127.0.0.1:20212/devices | head -c 200; echo

The API returns JSON describing the devices the appliance knows about. Without the token it returns 403:

curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:20212/devices

Expected output:

403

Scanning scope and authorisation

This product scans networks. Only scan networks you own or have explicit, documented authorisation to scan.

  • Scanning networks you do not control may breach the Microsoft Azure Acceptable Use Policy, your agreement with your own network providers, and computer misuse law in many countries. It can also be detected as hostile reconnaissance and get your subscription suspended.
  • This image ships scoped to your VM's own subnet and resolves that scope on your VM at first boot. It never ships a scan range and it will not scan beyond the network it was deployed into unless you widen it.
  • If you widen the scope, the responsibility for authorisation is yours. Get it in writing before you scan a network you do not own.
  • Discovery here is ICMP based. It is passive in intent, it does not attempt to exploit anything, and it does not perform port scanning unless you explicitly enable a plugin that does.

Security

  • No default password. The administrator password and the API token are generated on your VM's first boot. Nothing in the image knows them.
  • The gate is the reverse proxy. nginx authenticates every request to the interface. The application's own login is a separate, weaker mechanism and is left off; the reverse proxy already authenticates you.
  • The API is on loopback. It cannot be reached from the network, so the gate cannot be bypassed.
  • Fail closed. Both nginx and the scanner refuse to start if the per-VM credentials have not been generated, or if the scan scope has not been resolved for this VM.
  • Restrict port 80 to the addresses you administer from, with your Network Security Group. This is a management interface.
  • Basic Auth is only as private as the transport. Port 80 is plain HTTP, so credentials cross the network base64 encoded, not encrypted. For anything beyond a trusted management network, terminate TLS with Azure Application Gateway or a TLS terminating reverse proxy, and restrict port 80 in your Network Security Group to the networks that need it.
  • Rotate the administrator password whenever you need to, with sudo htpasswd -B /etc/nginx/.netalertx.htpasswd admin followed by sudo systemctl reload nginx. The password is also recorded in /root/netalertx-credentials.txt; update it there, or delete the file, once you have stored the password somewhere safe.

Maintenance

Ubuntu security updates are applied automatically by unattended-upgrades.

Check the scanner's log:

sudo tail -20 /app/log/app.log

Back up the device inventory and its history. The whole data tier is one SQLite file:

sudo sqlite3 /app/db/app.db ".backup '/root/netalertx-backup.db'"

Restart the scanner:

sudo systemctl restart netalertx

Licence and source code

NetAlertX is published by the NetAlertX project under the GNU General Public License version 3. cloudimg packages it as a virtual machine image; cloudimg is not the NetAlertX project.

NetAlertX is written in Python and PHP and runs from source, so the complete corresponding source for the version running on your VM is the /app directory itself. It is a git checkout pinned to the exact upstream commit this image was built from:

git -C /app log -1 --format='%H %d'

You can see every change cloudimg made to upstream's code as a live diff:

git -C /app diff --stat

The licence, the written offer for the corresponding source, and a description of every cloudimg modification are on the image:

ls /usr/share/doc/netalertx/

Expected output:

CLOUDIMG-CHANGES.txt  LICENSE.txt  SOURCE-OFFER.txt  pip-audit.json  python-requirements.lock.txt

Support

This image is maintained by cloudimg. For help with the image, contact cloudimg support at support@cloudimg.co.uk.

For questions about NetAlertX itself, see the NetAlertX project.