Security Azure

Web Check on Ubuntu 24.04 on Azure User Guide

| Product: Web Check on Ubuntu 24.04 LTS on Azure

Overview

Web Check is an open-source, self-hosted OSINT and security-analysis dashboard for websites. You enter a URL and it gathers what is publicly observable about that site into a single report of around 35 cards: DNS and TXT records, SSL/TLS certificate detail and TLS connection information, HTTP security headers, HSTS, open ports, DNSSEC, redirect chains, cookies, detected technology stack, WHOIS and registration data, mail configuration, crawl rules, social tags and an estimated carbon footprint.

The cloudimg image runs the Web Check Node.js server behind nginx on port 80, hardened and ready on first boot. Web Check has no authentication of its own, and its analysis API will fetch arbitrary URLs on a requester's behalf, so this image is secure by default: the Node server is bound to the loopback interface, nginx gates the whole dashboard and its API behind per-VM HTTP Basic-Auth, and a unique password is generated on the first boot of every instance and written to a root-only file. A public, unauthenticated health endpoint is exposed at /health. Built from Web Check 2.2.2 (MIT), backed by 24/7 cloudimg support.

What is included:

  • Web Check 2.2.2, built from the official source at /opt/web-check/src
  • Node.js 22 running the analysis server as the non-login webcheck user, bound to 127.0.0.1:3000
  • nginx on :80 as the single public listener, gating the dashboard and API behind per-VM HTTP Basic-Auth
  • A pinned headless Chrome at /opt/web-check/puppeteer, so the screenshot and quality checks work out of the box
  • A first-boot service that generates a unique admin password into a root-only 0600 file
  • An SSRF guard that blocks analysis of cloud instance metadata and private network ranges
  • A public, unauthenticated health endpoint at /health
  • web-check.service, nginx.service and web-check-firstboot.service as systemd units, enabled on every boot
  • 24/7 cloudimg support

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 a comfortable starting point. NSG inbound: allow 22/tcp from your management network and 80/tcp for the dashboard. Outbound HTTPS and DNS must be permitted, because Web Check reaches out to the sites you analyse. Web Check serves plain HTTP on port 80; for production, terminate TLS in front of it with your own domain.

Step 1 - Deploy from the Azure Marketplace

In the Azure portal choose Create a resource, search for Web Check on Ubuntu 24.04 LTS by cloudimg, and select Create. Pick your subscription, resource group and region, choose the Standard_B2s size, and supply your SSH public key for the azureuser account. On the Networking tab allow inbound 22/tcp from your management network and 80/tcp for the dashboard, then review and create.

Step 2 - Deploy from the Azure CLI

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

Step 3 - Connect to your VM

ssh azureuser@<vm-public-ip>

Step 4 - Confirm the platform and version

Confirm the base image, the Node.js runtime, the Web Check release and the bundled headless browser:

. /etc/os-release && echo "$PRETTY_NAME"
node --version
jq -r '.name + " " + .version' /opt/web-check/src/package.json

You see the Ubuntu release, the Node.js 22 runtime and Web Check 2.2.2:

Ubuntu 24.04.4 LTS
v22.23.2
web-check 2.2.2

The Ubuntu 24.04 release, Node.js 22 runtime, Web Check 2.2.2 and the bundled headless Chrome version

Step 5 - Confirm the services are running

Web Check runs as a systemd service behind nginx, and a one-shot first-boot service prepares the per-VM login. Confirm all three, and that the analysis server is listening on loopback only:

systemctl is-active web-check.service nginx.service
systemctl is-enabled web-check-firstboot.service web-check.service nginx.service
ss -ltn | grep -E ':(80|3000)\b'
test -f /var/lib/cloudimg/web-check-firstboot.done && echo "first boot complete"

Both services report active, all three are enabled, and note that port 3000 is bound to 127.0.0.1 only while nginx owns 0.0.0.0:80 - the analysis API is not reachable except through the authenticated proxy:

active
active
enabled
enabled
enabled
LISTEN 0      511          0.0.0.0:80        0.0.0.0:*
LISTEN 0      511        127.0.0.1:3000      0.0.0.0:*
LISTEN 0      511             [::]:80           [::]:*
first boot complete

Both services active and enabled, with Web Check bound to 127.0.0.1:3000 and nginx on port 80

Step 6 - Verify the authentication gate

Web Check has no login of its own, so nginx gates the dashboard and the analysis API behind HTTP Basic-Auth. The public /health endpoint returns 200 without credentials, while both the dashboard and the API return 401 without credentials and 200 with the per-VM login:

curl -s -o /dev/null -w 'health:    %{http_code}\n' http://127.0.0.1/health
curl -s -o /dev/null -w 'unauth UI: %{http_code}\n' http://127.0.0.1/
curl -s -o /dev/null -w 'unauth API: %{http_code}\n' 'http://127.0.0.1/api/dns?url=https://example.com'
sudo bash -c 'U=$(grep ^WEB_CHECK_USERNAME= /var/lib/cloudimg/web-check-credentials.txt | cut -d= -f2-); P=$(grep ^WEB_CHECK_PASSWORD= /var/lib/cloudimg/web-check-credentials.txt | cut -d= -f2-); curl -sL -o /dev/null -w "auth UI:   %{http_code}\n" -u "$U:$P" http://127.0.0.1/'

You see 200 for the public health probe, 401 for both the unauthenticated dashboard and the unauthenticated API, and 200 once the per-VM credential is supplied:

health:    200
unauth UI: 401
unauth API: 401
auth UI:   200

The health endpoint returns 200 while the dashboard and API return 401 without credentials and 200 with the per-VM login

Step 7 - Read your per-VM login

The unique per-VM credential (username admin) is written to a root-only file on first boot:

sudo stat -c 'file mode: %a  owner: %U:%G' /var/lib/cloudimg/web-check-credentials.txt
sudo grep -c . /var/lib/cloudimg/web-check-credentials.txt

The file is mode 600, owned by root:root. Read it to get your password:

file mode: 600  owner: root:root

Print the file when you need the password (keep it off shared terminals):

sudo cat /var/lib/cloudimg/web-check-credentials.txt

# Web Check 2.2.2 on Ubuntu 24.04 (cloudimg Azure Marketplace image)
# Per-VM HTTP Basic-Auth credentials - UNIQUE to this VM, minted at first boot.

WEB_CHECK_URL=http://<vm-public-ip>/
WEB_CHECK_USERNAME=admin
WEB_CHECK_PASSWORD=<generated-per-vm-password>

Step 8 - Open Web Check

Browse to http://<vm-public-ip>/ in any modern browser. Your browser prompts for the HTTP Basic-Auth login: enter admin and the password from Step 7. Web Check opens on its analyse screen with a single URL box, and lists every supported check underneath so you can see what the report will cover.

The Web Check analyse screen with the URL input, the Analyze button and the list of supported checks

Step 9 - Run your first analysis

Type a domain into the box - example.com is a good first target - and select Analyze!. Web Check fans out around 38 checks in parallel and streams the results in as each one answers, so the report fills in progressively over roughly ten to thirty seconds.

The report opens with an Advisory panel that groups what it found by severity: Critical, Issues, Warnings, Informational and Passes. This is the fastest way to see what is wrong with a site - here it has flagged an imminent domain expiry and five missing security headers.

The Web Check advisory panel showing critical findings, security header issues, warnings and passes for the analysed domain

Step 10 - Read the certificate, header and DNS cards

Scroll down and the individual report cards carry the detail behind the advisory. The SSL Certificate card shows the subject, issuer, trust status, curve, serial number, fingerprint and both the expiry and renewal dates. The Headers card lists the live response headers exactly as the server returned them, DNS Records shows the resolved A, AAAA, MX, NS, SOA and TXT records, and HTTP Security grades the site against the standard protective headers.

Populated SSL certificate, live response headers, DNS records and HTTP security cards for the analysed domain

Step 11 - Inspect TLS, DNS infrastructure and DNSSEC

Further down, the TLS Connection card reports the negotiated protocol, cipher suite, forward secrecy, session resumption and OCSP stapling. DNS Server resolves the authoritative nameservers to their addresses, Firewall identifies any WAF in front of the site, Threats checks reputation feeds, and DNSSEC lists the zone signing keys with their algorithms.

TLS connection detail, authoritative DNS servers, firewall and threat checks, and DNSSEC signing keys

Step 12 - Query the analysis API directly

Every check is also a JSON endpoint, so you can drive Web Check from scripts and pipelines. Each endpoint takes a url parameter, and the same per-VM credential authenticates it:

sudo bash -c 'U=$(grep ^WEB_CHECK_USERNAME= /var/lib/cloudimg/web-check-credentials.txt | cut -d= -f2-); P=$(grep ^WEB_CHECK_PASSWORD= /var/lib/cloudimg/web-check-credentials.txt | cut -d= -f2-); curl -s -u "$U:$P" "http://127.0.0.1/api/dns?url=https://example.com" | jq "{A,NS}"'
sudo bash -c 'U=$(grep ^WEB_CHECK_USERNAME= /var/lib/cloudimg/web-check-credentials.txt | cut -d= -f2-); P=$(grep ^WEB_CHECK_PASSWORD= /var/lib/cloudimg/web-check-credentials.txt | cut -d= -f2-); curl -s -u "$U:$P" "http://127.0.0.1/api/ssl?url=https://example.com" | jq "{subject,issuer}"'

Each endpoint returns the same data the matching dashboard card displays:

{
  "A": [
    "104.20.23.154",
    "172.66.147.243"
  ],
  "NS": [
    "elliott.ns.cloudflare.com",
    "hera.ns.cloudflare.com"
  ]
}
{
  "subject": {
    "CN": "example.com"
  },
  "issuer": {
    "C": "US",
    "O": "SSL Corporation",
    "CN": "Cloudflare TLS Issuing ECC CA 3"
  }
}

The Web Check API returning resolved DNS records and certificate subject and issuer as JSON

Request /api with no check name to run every check at once and return the whole report as a single JSON document.

Step 13 - Review the built-in SSRF guard

Because Web Check fetches whatever URL it is given, this image ships a blocklist so the appliance cannot be aimed at cloud instance metadata or at your own private network. Confirm it is in place:

grep '^API_BLOCKED_HOSTS=' /etc/web-check/web-check.env

The blocklist covers the Azure instance metadata endpoint and every RFC1918, CGNAT, loopback and link-local range:

API_BLOCKED_HOSTS=169.254.0.0/16,127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,100.64.0.0/10,localhost

To analyse a host inside your own network deliberately, remove that range from the list and restart the service - but only do so on an instance you keep private.

Step 14 - Enable the optional third-party checks

Around 35 of the checks work immediately with no configuration. A small number call external services that need a free API key of your own - Shodan, Google Safe Browsing threat data, global ranking, subdomain discovery and page quality. Without a key those cards report that the key is missing and the rest of the report is unaffected; no key is baked into this image.

To enable one, add it to the runtime environment file and restart:

grep -E '^#\s+[A-Z_]+_API_KEY|^#\s+TRANCO|^#\s+WHO_API_KEY' /etc/web-check/web-check.env

The commented key names are listed ready to fill in:

#   GOOGLE_CLOUD_API_KEY=
#   SHODAN_API_KEY=
#   SECURITY_TRAILS_API_KEY=
#   URL_SCAN_API_KEY=
#   CLOUDMERSIVE_API_KEY=
#   WHO_API_KEY=
#   TORRENT_IP_API_KEY=

Uncomment the one you want, paste in your key, then run sudo systemctl restart web-check to pick it up.

Maintenance

Web Check is stateless - it stores no database and no report history - so maintenance is limited to the service and the OS.

systemctl status web-check.service --no-pager | head -5
journalctl -u web-check.service --no-pager -n 10

Restart the analysis server with sudo systemctl restart web-check, and reload nginx with sudo systemctl reload nginx after editing /etc/nginx/sites-available/web-check. The OS receives unattended security updates automatically.

To rotate the dashboard password, write a new one with htpasswd:

sudo htpasswd -B /etc/nginx/.web-check-htpasswd admin

Note that the rate limiter upstream ships for its public instance is deliberately disabled here (API_ENABLE_RATE_LIMIT=false in /etc/web-check/web-check.env), because a single full analysis makes around 38 API calls and the public limits would throttle you on your own instance. If you expose this VM to a wider audience, set it to true and restart the service.

Support

cloudimg provides 24/7 technical support for this image by email at support@cloudimg.co.uk and via live chat, with a one hour average response time for critical issues. We help with deployment, reverse proxy and TLS configuration, managing the per-instance login, enabling the optional third-party integrations, tuning the blocked host rules, headless browser troubleshooting and version upgrades.