Deployment guide

Threat Dragon

Overview

OWASP Threat Dragon is the OWASP Foundation's free, open-source threat modelling application. You draw a data-flow diagram of the system you are designing, with its processes, data stores, external actors and the trust boundaries between them. You then record the threats against each element.

Threats can be elicited with STRIDE, LINDDUN, CIA, DIE or PLOT4ai. 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 SecureAnyCloud 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. Your threat models stay in your own browser and in the JSON files you save.

Local-session mode has no account store of its own, so nginx gates the whole application behind per-VM HTTP Basic-Auth, with a unique password generated on the first boot of every instance.

What you get:

  • 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 traffic to 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 license 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 SecureAnyCloud support

Prerequisites

You need an active Azure subscription, an SSH key pair, and a VNet with a subnet in the target region.

Standard_B2s (2 vCPU / 4 GiB RAM) is a comfortable starting point. Diagram editing happens in your browser, so the server needs few resources.

Configure the network security group (NSG) to allow inbound 22/tcp from your management network and 80/tcp for the application. Threat Dragon 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

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for OWASP Threat Dragon by SecureAnyCloud, and select Create.

On the Basics tab, 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).

Select Review + create, then Create.

Step 2 - Deploy from the Azure CLI

# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:threat-dragon-ubuntu-24-04:default:latest
az vm create \
  --resource-group <your-rg> \
  --name threat-dragon \
  --image sac1647283583153:threat-dragon-ubuntu-24-04:default:latest \
  --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

Connect to the VM over SSH as the administrator user set in Step 2.

ssh azureuser@<vm-public-ip>

Step 4 - Confirm the services are running

Threat Dragon runs as threat-dragon.service behind nginx.service. A oneshot first-boot service prepares the per-VM secrets, and a port guard keeps the application's own port private. Confirm that all four units are active 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/sac/data/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 file confirms that first boot finished.

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/sac/data/threat-dragon-credentials.txt | cut -d= -f2-); P=$(grep ^THREAT_DRAGON_PASSWORD= /var/lib/sac/data/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. This confirms that 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

Step 6 - Read your per-VM login

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

sudo cat /var/lib/sac/data/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 your VM.

# OWASP Threat Dragon 2.6.2 on Ubuntu 24.04 (sac 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=<set-at-first-boot>

Step 7 - Sign in to Threat Dragon

Browse to http://<vm-public-ip>/ in any modern browser. When 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. This keeps your threat models in this browser and in the files you save locally, and no external account is involved.

Step 8 - Use the dashboard

After you sign 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. The sample shows a finished model before you build your own.

Step 9 - Open a threat model

A threat model opens on its summary page. The page shows the title, owner, reviewer and high-level system description, followed by the diagrams the model 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, and the thumbnail shows it as a STRIDE model.

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. In the sample, a Web UI, a Web Service and a PostgresSQL store are joined by data flows and grouped inside the Internet and Public Cloud trust boundaries. The element stencil (Process, Store, Actor, Data Flow, Boundaries) is on the left, with the Properties and Threats panels underneath.

Drag new elements onto the canvas and connect them with data flows. Select any element to fill in its Properties, then 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 select Save.

Select Report on the model page to render the whole model, with every threat and mitigation, as a document you can share.

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 a provider, register an OAuth application with it and 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. Before you expose the instance beyond your network, keep the Basic-Auth gate in place or replace it with your own TLS-terminating reverse proxy.

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 run sudo systemctl reload nginx.
  • License and attribution. The upstream Apache-2.0 license and attribution ship on disk at /usr/share/threat-dragon/.
  • TLS. The application serves plain HTTP on port 80. Before you run it in production, front it with TLS (for example, certbot) and your own domain.
  • Restart. To restart the application, run sudo systemctl restart threat-dragon.service nginx.service.
  • 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

SecureAnyCloud provides 24/7 expert support for this image. For questions or help with your deployment, contact sales@secureanycloud.com.

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.

Deploy on Azure

Launch OWASP Threat Dragon 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