Security Azure

OWASP Threat Dragon on Ubuntu 24.04 on Azure User Guide

| Product: OWASP Threat Dragon on Ubuntu 24.04 LTS on Azure

Overview

OWASP Threat Dragon is the OWASP Foundation's free and open-source threat modelling application. You draw a data-flow diagram of the system you are designing - processes, data stores, external actors and the trust boundaries between them - then record the threats against each element. Threats can be elicited with STRIDE, LINDDUN, CIA, DIE or PLOT4ai, and each threat carries a description, a severity, a status and the mitigation you intend to apply. The finished model renders as a report, and the model itself is plain JSON, so it can live alongside the code it describes.

This image ships the Threat Dragon web application (the td.server Express backend serving the td.vue single-page front end), built from the official source at tag v2.6.2. It runs in local-session mode: no OAuth client id or secret is baked into the image, so the appliance has no third-party dependency, and your threat models stay in your own browser and in the JSON files you save. Because local-session mode has no account store of its own, nginx gates the whole application behind per-VM HTTP Basic-Auth, with a unique password generated on the first boot of every instance. Backed by 24/7 cloudimg support.

What is included:

  • OWASP Threat Dragon 2.6.2 web application, built from the official source and served from /opt/threat-dragon
  • Node.js 24 from the official NodeSource repository - the runtime upstream builds and tests with
  • nginx on :80 as the single public listener, gating the app behind per-VM HTTP Basic-Auth
  • A port guard that drops the application's own :3000 on every interface except loopback
  • A public, unauthenticated health endpoint at /health
  • A first-boot service that generates the app's encryption key, both JWT signing keys and a unique admin password into root-only files
  • The upstream Apache-2.0 licence and attribution at /usr/share/threat-dragon
  • threat-dragon.service, nginx.service, threat-dragon-portguard.service and threat-dragon-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 - the diagram editing happens in your browser, so the server is light on resources. NSG inbound: allow 22/tcp from your management network and 80/tcp for the application. Threat Dragon serves plain HTTP on port 80 here; for production, terminate TLS in front of it with your own domain.

Step 1 - Deploy from the Azure Marketplace

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for OWASP Threat Dragon 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 and Create.

Step 2 - Deploy from the Azure CLI

az vm create \
  --resource-group <your-rg> \
  --name threat-dragon \
  --image <marketplace-image-urn> \
  --size Standard_B2s \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_ed25519.pub \
  --vnet-name <your-vnet> --subnet <your-subnet> \
  --public-ip-sku Standard

az vm open-port --resource-group <your-rg> --name threat-dragon --port 80 --priority 1010

Step 3 - Connect to your VM

ssh azureuser@<vm-public-ip>

Step 4 - Confirm the services are running

Threat Dragon runs as threat-dragon.service behind nginx.service. A one-shot first-boot service prepares the per-VM secrets, and a port guard keeps the application's own port private. Confirm all four, and that nginx is the only public listener:

for u in threat-dragon-firstboot.service threat-dragon-portguard.service threat-dragon.service nginx.service; do printf '%-36s %s\n' "$u" "$(systemctl is-active $u)"; done
ss -tln | awk 'NR==1 || $4 ~ /:(80|3000)$/ {print $1, $4}'
test -f /var/lib/cloudimg/threat-dragon-firstboot.done && echo "first boot complete"

All four units report active, nginx is listening on :80, the application is on :3000, and the sentinel confirms first boot finished.

All four Threat Dragon services active, nginx listening on port 80 and the application on port 3000

Step 5 - Verify the authentication gate

In local-session mode Threat Dragon has no login of its own, so nginx gates the whole application behind HTTP Basic-Auth. The public /health endpoint returns 200 without credentials; the app returns 401 without credentials or with a wrong password, 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:   %{http_code}\n' http://127.0.0.1/
sudo bash -c 'U=$(grep ^THREAT_DRAGON_USERNAME= /var/lib/cloudimg/threat-dragon-credentials.txt | cut -d= -f2-); P=$(grep ^THREAT_DRAGON_PASSWORD= /var/lib/cloudimg/threat-dragon-credentials.txt | cut -d= -f2-); curl -s -o /dev/null -w "wrongpw:  %{http_code}\n" -u "$U:wrong" http://127.0.0.1/; curl -s -o /dev/null -w "auth:     %{http_code}\n" -u "$U:$P" http://127.0.0.1/'

You see health: 200, unauth: 401, wrongpw: 401 and auth: 200 - proof the application is reachable only with the per-VM credential.

The application's own port is private as well. It answers on loopback, where nginx reaches it, and nowhere else:

curl -s -o /dev/null -w 'loopback :3000  -> %{http_code}\n' http://127.0.0.1:3000/healthz
curl -s -o /dev/null -m 5 -w 'off-box  :3000  -> %{http_code}  (000 = dropped by the port guard)\n' "http://$(hostname -I | awk '{print $1}'):3000/healthz" || true

The public health endpoint returns 200, the app returns 401 without credentials and 200 with the per-VM login

Step 6 - Read your per-VM login

The unique per-VM credential (username admin) is written to a root-only file on first boot, alongside the application's own per-VM secrets:

sudo cat /var/lib/cloudimg/threat-dragon-credentials.txt
sudo stat -c '%n  mode: %a  owner: %U:%G' /etc/threat-dragon/threat-dragon.env /etc/threat-dragon/encryption-keys.json

Example output (your password and keys are unique to this VM):

# OWASP Threat Dragon 2.6.2 on Ubuntu 24.04 (cloudimg Azure Marketplace image)
# Per-VM HTTP Basic-Auth credentials - UNIQUE to this VM, minted at first boot.

THREAT_DRAGON_URL=http://<vm-public-ip>/
THREAT_DRAGON_USERNAME=admin
THREAT_DRAGON_PASSWORD=3f9c1a7be2d045c8a1e6b0f4d7c2e9a1

The per-VM credentials file, mode 0600 root:root, and the root-only application secret files

Step 7 - Sign in to Threat Dragon

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 6. Threat Dragon's welcome page then offers Login to Local Session, which keeps your threat models in this browser and in the files you save locally - no external account is involved.

The Threat Dragon welcome page offering sign-in with a local session

Step 8 - The dashboard

After signing in you land on the dashboard, headed Welcome!. From here you can Open an existing threat model from a JSON file, Create a new, empty threat model, or Explore a sample threat model to see a finished model before building your own.

The Threat Dragon dashboard with the open, import and create threat model actions

Step 9 - Open a threat model

A threat model opens on its summary page: the title, owner, reviewer and high-level system description, followed by the diagrams it contains. The bundled Three Tier Web Application sample is a good place to start - its Three Tier Web App diagram already has threats recorded against its elements, shown on the thumbnail as a STRIDE model.

A loaded threat model showing its summary, description and the diagrams it contains

Step 10 - Edit the data-flow diagram and record threats

Click a diagram to open the editor. The canvas shows the system as a data-flow diagram - here a Web UI, a Web Service and a PostgresSQL store, joined by data flows and grouped inside the Internet and Public Cloud trust boundaries - with the element stencil (Process, Store, Actor, Data Flow, Boundaries) on the left, and Properties and Threats panels underneath. Drag new elements onto the canvas, connect them with data flows, and select any element to fill in its Properties and add threats to it from the Threats panel: choose the model type (STRIDE, LINDDUN, CIA, DIE or PLOT4ai), give the threat a title, severity, status and mitigation, then Save. Use Report on the model page to render the whole model, with every threat and mitigation, as a document you can share.

The Threat Dragon diagram editor with a rendered data-flow diagram, its trust boundaries and the element stencil

Step 11 - Optional: store threat models in your own git provider

Threat Dragon can also sign users in through GitHub, GitLab, Bitbucket or Google and read and write threat models directly in a repository. No provider credentials ship in this image. To enable one, register an OAuth application with your provider, point its callback at http://<vm-public-ip>/api/oauth/return, then add the client id and secret to the per-VM environment file and restart the service:

sudo tee -a /etc/threat-dragon/threat-dragon.env >/dev/null <<'EOF'
GITHUB_CLIENT_ID=<your-client-id>
GITHUB_CLIENT_SECRET=<your-client-secret>
GITHUB_SCOPE=public_repo
EOF
sudo systemctl restart threat-dragon.service

The provider then appears as a sign-in option on the welcome page. Keep the Basic-Auth gate in place, or replace it with your own TLS-terminating reverse proxy, before exposing the instance beyond your network.

Maintenance

  • Data: in local-session mode your threat models live in your browser and in the JSON files you save, so there is no server-side database to back up. Use Save in the editor to download a model as JSON, and Import to open it again.
  • Change the login: the per-VM password lives in the nginx htpasswd file. To set your own, run sudo htpasswd -B /etc/nginx/.threat-dragon-htpasswd admin and follow the prompts, then sudo systemctl reload nginx.
  • Licence and attribution: the upstream Apache-2.0 licence and attribution ship on disk at /usr/share/threat-dragon/.
  • TLS: the application serves plain HTTP on port 80; front it with TLS (for example certbot) and your own domain before production use.
  • Restart: sudo systemctl restart threat-dragon.service nginx.service if you need to bounce the application.
  • Logs: sudo journalctl -u threat-dragon.service -n 50 shows the application log.
  • Security patches: unattended-upgrades remains enabled so the OS continues to receive security updates automatically.

Support

cloudimg provides 24/7 expert support for this image. Contact support@cloudimg.co.uk.

This image is maintained by cloudimg with 24/7 support. OWASP and Threat Dragon are trademarks of the OWASP Foundation; this image repackages the upstream Apache-2.0 software and is not affiliated with or endorsed by the OWASP Foundation.