Mc
Security Azure

MITRE Caldera on Ubuntu 24.04 on Azure User Guide

| Product: MITRE Caldera on Ubuntu 24.04 LTS on Azure

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.service bound to 127.0.0.1:8888, running as the dedicated non-login caldera user, 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.service that 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:

  1. The nginx gateCALDERA_WEB_USER / CALDERA_WEB_PASSWORD, prompted by the browser before anything else loads.
  2. Caldera's own loginred (offensive) or blue (defensive), with CALDERA_RED_PASSWORD / CALDERA_BLUE_PASSWORD, entered on Caldera's login page.

Caldera services running and the per-VM credentials file

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.

Credential round-trip and auth gate proof

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.)

Listening sockets and listener audit

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.

Caldera login page reached through the per-VM reverse-proxy gate

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

The ATT&CK-mapped ability library

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.

  1. 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.
  2. SSH to the VM and paste that command (it downloads the compiled agent and starts it). Within a few seconds the agent registers.
  3. Back in agents, the new agent appears as alive and trusted, with its host, PID, platform and privilege:

Agents view showing a live agent checked in to the C2 server

  1. 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:

A completed operation showing the command output returned from the target host

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:

  1. Edit /opt/caldera/conf/local.yml and change the relevant app.contact.* binding from 127.0.0.1 to 0.0.0.0 (for the HTTP contact, also set host: 0.0.0.0). Keep every contact you are not using on loopback.
  2. Restart Caldera: sudo systemctl restart caldera.
  3. 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.
  4. 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

Health probe and pinned plugin commits

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.