MITRE Caldera on Ubuntu 24.04 on Azure User Guide
Overview
MITRE Caldera is an open source adversary emulation platform maintained by MITRE. It automates the work of a red team: you choose an adversary profile assembled from a library of abilities mapped to MITRE ATT&CK, Caldera plans the sequence, deploys agents to the hosts in scope, executes each step and collects the results. Blue-team plugins support the defensive side of the same exercise. It is the tooling security teams use to test, repeatably, whether their detection and response actually work.
Caldera is, functionally, command-and-control (C2) infrastructure: the server issues instructions that agents execute on target hosts. That is exactly what makes it powerful and exactly why it must be run carefully. Upstream's own documentation states the web interface is "not hardened and thoroughly pentested" and should be deployed on isolated networks rather than exposed to the public internet. The cloudimg image is built around that reality.
The cloudimg image installs Caldera 5.3.0 at /opt/caldera in a dedicated Python virtual environment, running as the non-login caldera user, bound to loopback 127.0.0.1:8888, behind nginx on port 80. The VueJS console is pre-built into the image, and the Go toolchain the sandcat agent compiler needs is installed, so you can build an adversary profile, deploy an agent and run an operation immediately after launch.
Ships closed, because this is C2. Upstream's sample configuration publishes working logins (red/admin, admin/admin, blue/admin) and API keys (ADMIN123, BLUEADMIN123). Every one of those is treated as compromised: this image removes them from the tree, refuses them at runtime, and empties the seeded user map so that even starting the server insecurely yields no working login. On the first boot of every instance, a one-shot service generates seven per-VM secrets — the reverse-proxy gate password, the red and blue account passwords, the red and blue REST API keys, the encryption key and the salt — and writes them to a root-only file.
No open interface. HTTP Basic Authentication is enforced at the nginx reverse proxy across the whole platform — the console, the entire REST API, agent tasking, ability execution and file upload — with only a static, unauthenticated /healthz probe left open for load balancers. Behind the gate, Caldera enforces its own per-VM accounts and API keys.
No published C2 port. Caldera's defaults expose six agent-contact listeners on all interfaces (HTTP 8888, DNS 8853, SSH-tunnel 8022, FTP 2222, TCP 7010, UDP 7011, WebSocket 7012). In this image every one is bound to 127.0.0.1, so a freshly launched VM publishes no command-and-control port. Opening a contact for a real engagement is a deliberate, documented step covered later in this guide.
Authorised use only. Caldera is adversary-emulation infrastructure. Use it only against systems you are explicitly authorised to test. Before you expose this VM to anything, restrict its Azure network security group to your own source IP address (covered in the first section below).
What is included:
- MITRE Caldera 5.3.0 (Apache-2.0), pinned to the release tag with its plugin submodules pinned to the exact commits that tag records
caldera.servicebound to127.0.0.1:8888, running as the dedicated non-logincalderauser, gated so it cannot start before per-VM secrets exist- The pre-built magma VueJS console, the ATT&CK-mapped stockpile ability library, and the Go toolchain the sandcat agent compiler requires
- nginx reverse proxy on port 80 with per-VM HTTP Basic Auth over everything except
/healthz, ready for your TLS certificate caldera-firstboot.servicethat generates seven per-VM secrets, resolves the instance URL, and proves the auth gate is closed before completing- Ubuntu 24.04 LTS base, fully patched, unattended security upgrades enabled
- 24/7 cloudimg support, 24h response SLA
Before you begin: scope the network security group to your own IP
This is the single most important step, and it comes first. Because Caldera is C2 infrastructure, the VM should never be reachable by anyone but you. In the Azure portal, open your VM's Networking blade and edit the inbound rule for port 80 (and 22) so the Source is your own IP address rather than Any/0.0.0.0/0. From the Azure CLI:
# Replace with YOUR public IP (find it with: curl -s https://ifconfig.me)
MY_IP="203.0.113.10"
az network nsg rule update \
--resource-group <your-rg> \
--nsg-name <your-nsg> \
--name <the-inbound-web-rule> \
--source-address-prefixes "${MY_IP}/32"
Do this before you retrieve credentials or open the console.
Connect and retrieve your per-VM credentials
SSH to the VM as azureuser, then read the credentials file. It is generated uniquely on this instance at first boot and is readable only by root.
sudo cat /root/caldera-credentials.txt
Expected output (the values shown are placeholders — yours are unique to this VM):
# MITRE Caldera 5.3.0 - Per-VM Credentials
# Generated on first boot by caldera-firstboot.service. Unique to THIS VM. Store them safely.
CALDERA_URL=http://<this-vm-public-ip>/
CALDERA_WEB_USER=caldera
CALDERA_WEB_PASSWORD=<CALDERA_WEB_PASSWORD>
CALDERA_RED_USER=red
CALDERA_RED_PASSWORD=<CALDERA_RED_PASSWORD>
CALDERA_BLUE_USER=blue
CALDERA_BLUE_PASSWORD=<CALDERA_BLUE_PASSWORD>
CALDERA_API_KEY_RED=<CALDERA_API_KEY_RED>
CALDERA_API_KEY_BLUE=<CALDERA_API_KEY_BLUE>
There are two authentication layers, by design:
- The nginx gate —
CALDERA_WEB_USER/CALDERA_WEB_PASSWORD, prompted by the browser before anything else loads. - Caldera's own login —
red(offensive) orblue(defensive), withCALDERA_RED_PASSWORD/CALDERA_BLUE_PASSWORD, entered on Caldera's login page.

Verify the service and the health endpoint
Confirm both services are active and the unauthenticated health probe answers:
sudo systemctl status caldera nginx --no-pager | head -18
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1/healthz
caldera.service and nginx.service should both report active (running), and the /healthz probe should print 200.
Confirm the platform is closed to unauthenticated callers
The /healthz probe is the only path reachable without a credential. Everything else — the console, the REST API, agent tasking, ability execution, file upload — returns 401 unless you present the per-VM gate credential. The image ships a helper that proves this end-to-end, including a positive control (the same requests succeed with the real credential) and a check that the published upstream defaults are refused:
sudo /usr/local/sbin/caldera-cred-roundtrip.sh
Expected output:
OK: gate closed unauthenticated (UI 401, agents 401, operations 401, file-upload 401, wrong-pass 401);
OK: positive control passed (UI 200 and /api/v2/agents 200 with the per-VM credential);
OK: published upstream default API key ADMIN123 refused (401);
OK: published upstream default login red/admin issued NO session, while the per-VM red
OK: credential DID - asserted on the API_SESSION cookie, not on /enter's always-302 status.

Confirm no command-and-control port is published
Enumerate every listening socket and confirm only SSH (22) and the nginx gate (80) are bound to a non-loopback address. Every Caldera agent-contact listener is bound to 127.0.0.1:
sudo ss -lntu
sudo /usr/local/sbin/caldera-listener-audit.sh | tail -3
The audit prints LISTENER-AUDIT OK: only tcp:22 tcp:80 udp:68 are publicly bound; every Caldera agent contact is loopback-only. (udp:68 is the OS DHCP client, not an application listener.)

Open the console
In a browser, go to CALDERA_URL (http://<this-vm-public-ip>/). You will first be challenged by the nginx gate — enter CALDERA_WEB_USER / CALDERA_WEB_PASSWORD. Then Caldera's own login page appears; sign in as red with CALDERA_RED_PASSWORD.

The abilities view shows the ATT&CK-mapped stockpile library that adversary profiles are built from — nearly 2,000 abilities ship in the image:

Run your first operation against a local agent
The quickest way to see Caldera work end-to-end is to deploy an agent on the VM itself and run a small operation against it. This is a self-contained demonstration on a host you already control.
- In the console, open agents and click Deploy an agent. Choose the Sandcat agent, platform linux, and set the server address to
http://127.0.0.1:8888. Copy the generated one-line delivery command. - SSH to the VM and paste that command (it downloads the compiled agent and starts it). Within a few seconds the agent registers.
- Back in agents, the new agent appears as
aliveandtrusted, with its host, PID, platform and privilege:

- Open operations, click New Operation, choose an adversary profile (for example a simple discovery profile) and the group your agent joined, and start it. When it finishes, expand a link and click View Output to see the actual command output the agent returned through the C2 channel:

That round trip — the agent ran a real command and the result came back — is the core of the platform.
Drive Caldera from the REST API
Every action in the console is available over the REST API, which is gated by both the nginx credential and a Caldera API key. Send the API key in the KEY header:
# From the VM (loopback bypasses the nginx gate); off-box, add -u "$CALDERA_WEB_USER:$CALDERA_WEB_PASSWORD"
KEY=$(sudo grep '^CALDERA_API_KEY_RED=' /root/caldera-credentials.txt | cut -d= -f2-)
curl -s -H "KEY: ${KEY}" http://127.0.0.1:8888/api/v2/agents | head -c 400
This returns the JSON list of registered agents. Full API documentation is available in the console under api docs.
Running against remote hosts (advanced)
By default this image publishes no C2 port, so agents can only reach the server over loopback (as in the demonstration above). To run an engagement against remote hosts you are authorised to test, you deliberately expose the contact you need:
- Edit
/opt/caldera/conf/local.ymland change the relevantapp.contact.*binding from127.0.0.1to0.0.0.0(for the HTTP contact, also sethost: 0.0.0.0). Keep every contact you are not using on loopback. - Restart Caldera:
sudo systemctl restart caldera. - Add an inbound Azure NSG rule for that specific port, scoped to the source ranges of the hosts in scope — never
0.0.0.0/0. - Generate agents pointed at your VM's public address from the agents view.
Treat an exposed contact as live C2: keep the NSG tightly scoped, tear it down when the engagement ends, and never leave a contact open to the internet.
Verify the shipped software provenance
The exact Caldera version and the pinned plugin submodule commits shipped in this image are recorded on disk:
head -12 /opt/caldera/.cloudimg-plugin-pins.txt

TLS
nginx serves plain HTTP on port 80 out of the box so you can reach the console immediately behind your scoped NSG. For any real use, put TLS in front of it. Point a DNS name at the VM and issue a certificate with your own ACME client or certificate, then add a listen 443 ssl; server block to the nginx site at /etc/nginx/sites-available/cloudimg-caldera and reload nginx. Because Caldera derives its URLs from the request host, no application reconfiguration is needed.
Support
This image includes 24/7 cloudimg support with a 24-hour response SLA. Our engineers help with Caldera deployment, per-instance credential management and the reverse-proxy gate, agent deployment and contact configuration, building adversary profiles and custom abilities, running and interpreting operations, REST API automation, plugin and version upgrades, and reverse-proxy and TLS configuration. Email support@cloudimg.co.uk.
This is a repackaged open source software product with additional charges for cloudimg support services. Caldera and ATT&CK are trademarks of The MITRE Corporation. All product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.