Security Azure

Sirius Scan on Ubuntu 24.04 on Azure User Guide

| Product: Sirius Scan on Ubuntu 24.04 LTS on Azure

Overview

Sirius Scan is an open source general purpose vulnerability scanner. Point it at a host or an address range and it discovers what is there, identifies the operating systems and services it finds, matches them against a CVE backed vulnerability database, and presents the result as a risk picture you can work through: severity across your estate, a per host inventory of open ports and detected software, and per finding detail with scoring and remediation guidance. Everything the console does is also available over a documented REST API, so vulnerability assessment can be scheduled and wired into an existing security workflow rather than run by hand.

The cloudimg image runs the complete Sirius Scan v1.1.0 release stack on one virtual machine: the Next.js web console, the Go REST API, the Go scan engine with a bundled Nmap 7.95, a PostgreSQL database for findings, a message broker that carries scan jobs to the engine, and a cache that tracks live scan progress. Every container is pinned to the exact published v1.1.0 release build by immutable digest, so the image is reproducible and cannot drift onto a moving tag. The upstream project publishes a working sample value for every shared secret in the stack, so this image generates its own on the first boot of each machine: the console administrator password, the database password, the session signing key and the internal service API key. None of them is baked into the image, the database ships empty with no administrator, and each published default is actively refused by the running services. Backed by 24/7 cloudimg support.

What is included:

  • Sirius Scan v1.1.0, every component pinned by immutable digest to the published release build
  • The web console on :80 behind nginx, with a per VM administrator account
  • The REST API on :9001, authenticated with a per VM X-API-Key, plus an unauthenticated readiness probe for load balancer health checks
  • The scan engine with a bundled Nmap 7.95 and the NET_RAW capability it needs for host discovery
  • The agent gRPC listener on :50051 for remote scanning agents
  • PostgreSQL 15 holding hosts, ports, software inventory and vulnerability findings
  • A message broker carrying scan jobs to the engine, and a Valkey cache holding scan state, templates and live progress, both confined to a private container network with no host port
  • A per VM administrator password, database password, session signing key and internal service API key, generated on first boot and never shared between machines
  • docker.service, sirius-scan.service and nginx.service as systemd units, enabled and active
  • 24/7 cloudimg support

The Sirius Scan services and all seven containers reported healthy on a freshly booted VM

Prerequisites

An active Azure subscription, an SSH key pair, and a VNet plus subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is the recommended size and runs the whole stack comfortably. NSG inbound: allow 22/tcp from your management network and 80/tcp for the web console. Allow 9001/tcp as well if you intend to use the REST API or the console's system monitoring panels from a browser, and 50051/tcp only if you deploy remote scanning agents.

Sirius Scan is an active scanner: it sends real probe traffic to whatever you point it at. Only scan systems you own or have explicit written authorisation to test. Microsoft does not require pre approval to run penetration tests against your own Azure resources, but the Microsoft Cloud Unified Penetration Testing Rules of Engagement still apply, and scanning anything outside your own estate may breach your provider's terms or the law in your jurisdiction.

Step 1 - Deploy from the Azure Marketplace

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Sirius Scan by cloudimg, and select Create. On Basics pick your subscription, resource group, region and size; under Administrator account choose SSH public key and paste your key; under Inbound port rules allow SSH (22) and HTTP (80). Then Review + create -> Create.

Step 2 - Deploy from the Azure CLI

az vm create \
  --resource-group <your-rg> \
  --name sirius-scan \
  --image <marketplace-image-urn> \
  --size Standard_B2s \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_rsa.pub \
  --public-ip-sku Standard

az vm open-port --resource-group <your-rg> --name sirius-scan --port 80   --priority 1001
az vm open-port --resource-group <your-rg> --name sirius-scan --port 9001 --priority 1002

Find the public address of the machine you just created:

az vm show --resource-group <your-rg> --name sirius-scan -d --query publicIps -o tsv

Step 3 - Confirm the stack is running

SSH to the VM as azureuser. On the very first boot the appliance generates its own secrets and starts the stack, which takes a little under two minutes; after that every boot is immediate. Check the three systemd units first:

systemctl is-active docker sirius-scan nginx

Expected output:

active
active
active

Then check the containers themselves. sirius-compose is a small wrapper that runs docker compose against this appliance's compose file with the per VM environment already wired in, so you never have to remember the paths:

sudo sirius-compose ps --format 'table {{.Service}}\t{{.Status}}'

Expected output:

SERVICE           STATUS
sirius-api        Up 9 minutes (healthy)
sirius-engine     Up 9 minutes (healthy)
sirius-postgres   Up 9 minutes (healthy)
sirius-rabbitmq   Up 9 minutes (healthy)
sirius-ui         Up 9 minutes (healthy)
sirius-valkey     Up 9 minutes (healthy)

The seventh service, sirius-migrate, is a one shot schema migrator. It applies the core database schema and exits before the API and the engine are allowed to start, so it is correct for it not to appear in the running list.

Step 4 - Retrieve the per VM credentials

Every secret this appliance uses was generated for your machine alone on its first boot and written to a file readable only by root.

sudo cat /root/sirius-scan-credentials.txt

The file records the console URL, the administrator username and password, the REST API URL and key, and the PostgreSQL user and password. Nothing in it exists in the published image, and no two machines launched from this image share a value.

The per VM credentials file, mode 0600 and owned by root

Read individual values without printing the whole file:

sudo grep -E '^SIRIUS_(URL|ADMIN_USERNAME)=' /root/sirius-scan-credentials.txt

Step 5 - Sign in to the web console

Open http://<vm-ip>/ in a browser. The sign in page asks for a username and a password: use the SIRIUS_ADMIN_USERNAME and SIRIUS_ADMIN_PASSWORD values from the credentials file.

The Sirius Scan sign in page served over the VM's public address

After signing in you land on the Security Command Center, the dashboard that summarises total vulnerabilities, critical issues, active hosts and severity breakdown across everything you have scanned.

The Security Command Center dashboard after a first scan, showing discovered hosts and the severity breakdown

You can confirm the same credential works from the shell before you open a browser:

curl -s -c /tmp/sirius.jar -b /tmp/sirius.jar http://127.0.0.1/api/auth/csrf | head -c 40; echo

Step 6 - Run your first scan

Open Scan Dashboard from the left hand rail. Type a target into the target box and press Enter to commit it. Sirius accepts single addresses, CIDR ranges such as 10.0.0.0/24, dash ranges such as 192.168.1.1-10, and domain names. Choose a scan profile, then click Start Scan.

The dashboard tracks the scan live: elapsed time, the number of hosts discovered so far, and a severity tally that fills in as findings arrive.

A scan in progress on the Scan Dashboard, showing elapsed time, the target and the hosts discovered so far

You can also watch the scan from the shell, which is useful when driving scans from automation:

sudo bash -c 'K=$(grep -E "^SIRIUS_API_KEY=" /root/sirius-scan-credentials.txt | cut -d= -f2-); curl -s -m 20 -H "X-API-Key: $K" http://127.0.0.1:9001/api/v1/scans/status | head -c 200'; echo

Step 7 - Read the host inventory

Environment lists every host Sirius knows about, with its detected operating system, its open port count, its vulnerability count and a risk level. This is the working inventory the scanner builds up over time.

The Environment page listing a discovered host with its detected operating system and risk level

Click a host to open its detail page. The overview gives you the vulnerability count, the package count, the number of open ports and a risk score, with the discovered ports listed on the right and separate tabs for vulnerabilities, system detail, network detail and scan history.

A discovered host's detail page showing its open ports, detected operating system and risk score

The same inventory is available over the REST API:

sudo bash -c 'K=$(grep -E "^SIRIUS_API_KEY=" /root/sirius-scan-credentials.txt | cut -d= -f2-); curl -s -m 20 -H "X-API-Key: $K" http://127.0.0.1:9001/host/ | head -c 400'; echo

Step 8 - Use the REST API

The API listens on :9001. Every route except the readiness probe requires the per VM key in an X-API-Key header, so a caller without the key gets 401 rather than data.

curl -s -o /dev/null -w 'GET /host/ without a key -> HTTP %{http_code}\n' -m 15 http://127.0.0.1:9001/host/

Expected output:

GET /host/ without a key -> HTTP 401

With the key, the same route answers normally:

sudo bash -c 'K=$(grep -E "^SIRIUS_API_KEY=" /root/sirius-scan-credentials.txt | cut -d= -f2-); curl -s -o /dev/null -w "GET /host/ with the per VM key -> HTTP %{http_code}\n" -m 15 -H "X-API-Key: $K" http://127.0.0.1:9001/host/'

Expected output:

GET /host/ with the per VM key -> HTTP 200

To call the API from another machine, read the key out of the credentials file and send it as a header. Replace <vm-ip> with your machine's address:

curl -s -H "X-API-Key: <SIRIUS_API_KEY>" http://<vm-ip>:9001/host/

Step 9 - Health checks and load balancer probes

Two health endpoints exist and they answer different questions. /health is unauthenticated and reports only that the API process is up, which is exactly what an Azure Load Balancer probe should ask:

curl -s -m 10 http://127.0.0.1:9001/health

Expected output:

{"status":"healthy","timestamp":"2026-08-07T10:53:43.032454986Z","service":"sirius-api","version":"1.1.0"}

/api/v1/system/health is the one to use when you actually want to know whether the appliance is working. It requires the API key and reports each of the six services separately, so a dead database or a dead broker shows up instead of hiding behind a green process check:

sudo bash -c 'K=$(grep -E "^SIRIUS_API_KEY=" /root/sirius-scan-credentials.txt | cut -d= -f2-); curl -s -m 25 -H "X-API-Key: $K" http://127.0.0.1:9001/api/v1/system/health | head -c 400'; echo

The unauthenticated readiness probe, the dependency aware health report showing all six services up, and the API refusing an unauthenticated caller

Step 10 - Attach a remote scanning agent

The console's Get Agent button downloads the Sirius agent for your platform. Agents connect back to the engine's gRPC listener on port 50051 and let one console scan networks it cannot reach directly, such as a segmented subnet or a branch office.

If you deploy agents, open 50051/tcp in the network security group and restrict it to the source addresses your agents connect from:

az vm open-port --resource-group <your-rg> --name sirius-scan --port 50051 --priority 1003

Confirm the listener is up before you deploy an agent:

ss -tln | grep -E ':50051 ' || echo "agent gRPC listener not found"

Server Components

Component Version Purpose
Sirius Scan 1.1.0 Vulnerability scanner: console, REST API and scan engine
Nmap 7.95 Host, port and service discovery, bundled inside the scan engine
PostgreSQL 15 Hosts, ports, software inventory and vulnerability findings
RabbitMQ 3.12 Scan job queue between the API and the scan engine
Valkey 7.2 Scan state, scan templates and live scan progress
nginx 1.24 Reverse proxy fronting the web console on port 80
Docker Engine 29.x Container runtime for the whole stack
Ubuntu 24.04 LTS Base operating system

Port Reference

Port Bound to Purpose
22 all interfaces SSH
80 all interfaces Web console, through nginx
9001 all interfaces REST API, authenticated with X-API-Key
50051 all interfaces Agent gRPC listener for remote scanning agents
5432 127.0.0.1 PostgreSQL, protected by the per VM password
3000 127.0.0.1 The Next.js console process, reached through nginx
broker and cache private container network No host listener at all; reachable only by the Sirius services

The digest pinned images and the host port surface, showing the broker and cache are not published at all

Filesystem Layout

Mount Size Purpose
/ 29 GB Root filesystem, holding the container images and the PostgreSQL volume
/boot 881 MB Operating system kernel files
/boot/efi 105 MB UEFI boot partition (Gen2 Hyper V)
/mnt 7.8 GB Azure temporary resource disk, used for swap

Key directories:

Path Purpose
/etc/sirius-scan/compose.yaml The appliance compose file, with every image pinned by digest
/etc/sirius-scan/sirius-scan.env The per VM secrets consumed by compose (0600 root:root)
/etc/sirius-scan/secrets/sirius_api_key.txt The internal service API key mounted into the containers
/etc/sirius-scan/rabbitmq.conf The message broker configuration
/root/sirius-scan-credentials.txt The per VM credentials, readable only by root (0600 root:root)
/var/lib/docker/volumes/sirius_postgres_data The PostgreSQL data directory holding your findings

Managing the services

systemctl status sirius-scan --no-pager | head -5

Stop and start the whole stack through systemd:

sudo systemctl stop    sirius-scan
sudo systemctl start   sirius-scan
sudo systemctl restart sirius-scan

Work with individual containers through the wrapper:

sudo sirius-compose ps
sudo sirius-compose logs -f sirius-engine
sudo sirius-compose restart sirius-ui

Follow the appliance logs:

sudo journalctl -u sirius-scan -f
sudo journalctl -u sirius-scan-firstboot -n 50

Connect to the findings database with the per VM password:

PGPASSWORD='<SIRIUS_POSTGRES_PASSWORD>' psql -h 127.0.0.1 -U postgres -d sirius -c '\dt'

Scripts and Log Files

Path Purpose
/usr/local/sbin/sirius-scan-firstboot.sh Generates every per VM secret and brings the stack up on first boot
/usr/local/sbin/sirius-scan-roundtrip.sh Proves the per VM credentials work and every published default is refused
/usr/local/bin/sirius-compose Runs docker compose against the appliance with its environment wired in
/usr/local/sbin/cloudimg-mnt-swap.sh Creates swap on the ephemeral resource disk on every boot
/var/lib/cloudimg/sirius-scan-firstboot.done First boot sentinel; its presence stops firstboot re running
/var/lib/cloudimg/sirius-scan-bootstrap.ready Bootstrap marker the application unit is gated on
journalctl -u sirius-scan Appliance service log
sudo sirius-compose logs <service> Per container logs
/var/log/nginx/access.log nginx access log

On Startup

On the very first boot, sirius-scan-firstboot.service runs before the application. It resolves the machine's address, generates the administrator password, the database password, the session signing key and the internal service API key, writes them to /etc/sirius-scan/sirius-scan.env and /root/sirius-scan-credentials.txt, brings the stack up, proves the datastores are genuinely reachable and that the published default credentials are refused, and only then creates /var/lib/cloudimg/sirius-scan-bootstrap.ready.

That last step matters. sirius-scan.service carries ConditionPathExists on that marker, so on a first boot it physically cannot start until the secrets exist. The image ships without the marker, which is what guarantees the stack never comes up against an upstream default credential. On every subsequent boot the sentinel is present, firstboot exits immediately, and the containers restart normally.

You can confirm the gate is in place on your own machine:

grep -c ConditionPathExists /etc/systemd/system/sirius-scan.service

Expected output:

1

Troubleshooting

The console does not answer straight after launch. On a first boot the appliance is generating its secrets and initialising the database, broker and cache. Give it two minutes, then check sudo journalctl -u sirius-scan-firstboot -n 50.

systemctl status sirius-scan shows the unit as inactive with a condition failure. The bootstrap marker is missing, which means firstboot has not completed. Check the firstboot journal as above. Once firstboot succeeds the marker appears and the unit starts.

The console returns 502. nginx is up but the Next.js console is not yet listening. Check sudo sirius-compose ps and sudo sirius-compose logs --tail 60 sirius-ui.

/health says healthy but nothing works. /health reports only that the API process is alive. Ask the dependency aware endpoint instead, as in Step 9, and look for any service that is not up.

Sign in is refused. Use the username and password from /root/sirius-scan-credentials.txt on this machine. Credentials are unique per machine and none of the upstream sample credentials will work by design. sudo bash /usr/local/sbin/sirius-scan-roundtrip.sh walks the whole chain and prints which part is failing.

A scan starts but never produces findings. The engine consumes scan jobs off the message broker, so check it is running and attached with sudo sirius-compose ps and sudo sirius-compose logs --tail 60 sirius-engine.

Scanning a remote network finds nothing. Confirm the network security groups and firewalls between this machine and the target allow the probe traffic, and remember the engine scans from this machine's own network position. For networks it cannot reach directly, deploy a remote agent as in Step 10.

Security Recommendations

  • Restrict the NSG so that 22/tcp, 80/tcp, 9001/tcp and 50051/tcp are reachable only from the networks that need them. A vulnerability scanner holds a map of what is weak in your estate, so treat it as sensitive infrastructure.
  • Put the console behind your own TLS terminating load balancer or reverse proxy, or install a certificate on this machine, before exposing it beyond a trusted network.
  • Change the console password from the account menu after your first sign in, and create named accounts for your team rather than sharing the administrator.
  • Back up /root/sirius-scan-credentials.txt somewhere safe, then consider removing it from the machine. The appliance reads its secrets from /etc/sirius-scan/sirius-scan.env, not from that file.
  • Keep PostgreSQL on loopback and leave the broker and cache unpublished. Neither needs to be reachable from the network for this appliance to work.
  • Treat the API key as a full access credential and rotate it if it is ever exposed.
  • Only scan systems you own or are explicitly authorised to test, and keep a record of that authorisation.
  • Leave unattended security updates enabled so the operating system keeps receiving patches.

Support

cloudimg provides 24/7 support for this image. Contact support@cloudimg.co.uk with your Azure subscription id and the VM name. For questions about Sirius Scan itself, see the Sirius Scan GitHub repository.