Deployment guide

NetAlertX

Overview

NetAlertX (formerly Pi.Alert) is an open-source network presence scanner and asset discovery framework. It sweeps its subnet on a schedule, keeps an inventory of every device it has seen, records a presence timeline for each one, and raises an alert when a new device appears or a known device stops answering.

This image runs NetAlertX 26.7.1 on Ubuntu 24.04 LTS. It answers a question most networks cannot answer honestly: what is on this network right now, and what changed?

What you get:

  • 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

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

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 that 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 you cannot bypass the gate 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.

Scan only networks you are authorised to scan

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

Before you deploy this image, you need:

  • 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 SecureAnyCloud.
  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 the management interface for a network scanner, so do not open it to the internet.

Step 2 - Deploy from the Azure CLI

Create a resource group, accept the Marketplace terms, and create the VM:

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

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

Connect over SSH:

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. Check that all three are active:

systemctl is-active netalertx nginx php8.3-fpm

Expected output:

active active active

Confirm the network listeners. This is the most important security property: 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",...))

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

Step 5 - Retrieve your per-VM password

The password was generated on this VM's first boot and is not in the image. Print the credentials file:

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 sac
# image and sac 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>

Step 6 - Confirm the gate is protecting the interface

Prove that 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 running:

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

Expected output:

200

Step 7 - Confirm the scan scope

On first boot, the appliance read your VM's own network interface and limited the scan to that subnet only. Check the configured scope:

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, derived on your VM from your VM's address. The image does not ship a scan range.

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 prompts for the username and password. The dashboard shows how many devices are known, how many are present right now, and what has changed recently.

Step 9 - Review the device inventory

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

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, spread across the Details, Tools, Sessions, Presence, Events and Plugins tabs.

Step 11 - Review the presence timeline

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

Why this image scans with ICMP rather than ARP

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

Azure virtual networks do not work that way. The Azure 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 the NetAlertX ICMP scanner with synthetic addressing (ICMP_FAKE_MAC). It derives a stable, clearly marked fa:ce:... identifier for each address, so each host on your virtual network is tracked as its own device. Hosts whose real hardware 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, enable ARPSCAN in Settings.

Step 12 - Widen or change the scan scope

Read Scanning scope and authorisation first.

The supported way to change the scope 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 select 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 that it reloads 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 receive 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.

The credentials you enter here are yours. They are stored in /app/config/app.conf on your VM only.

Step 14 - Use the API

An API backs every view in the interface, and you can use it directly. It authenticates with the bearer token from your credentials file. It listens on the loopback interface only, 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 that describes 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 does not scan beyond the network it was deployed into unless you widen the scope.
  • 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, because 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, using your Network Security Group. This is a management interface.
  • Basic authentication 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

unattended-upgrades applies Ubuntu security updates automatically.

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

The NetAlertX project publishes NetAlertX under the GNU General Public License version 3. SecureAnyCloud packages it as a virtual machine image and 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'

To see every change SecureAnyCloud made to the upstream code, view the live diff:

git -C /app diff --stat

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

ls /usr/share/doc/netalertx/

Expected output:

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

Support

SecureAnyCloud maintains this image. For help with the image, contact SecureAnyCloud support at sales@secureanycloud.com.

For questions about NetAlertX itself, see the NetAlertX project.

Deploy on Azure

Launch NetAlertX on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure