Skip to content

fix(config): resolve the Caldera REST connection from core config - #33

Merged
HackedRico merged 1 commit into
mitre:mainfrom
HackedRico:fix/caldera-connection-resolution
Aug 28, 2026
Merged

fix(config): resolve the Caldera REST connection from core config#33
HackedRico merged 1 commit into
mitre:mainfrom
HackedRico:fix/caldera-connection-resolution

Conversation

@HackedRico

Copy link
Copy Markdown
Collaborator

Description

app/config.py hardcoded ADMIN123 as the API key fallback and localhost as the host, ignoring what CALDERA is actually configured with.

CALDERA argon2-hashes api_key_red and api_key_blue at boot (server.py calls apply_config(..., apply_hash=True), which also rewrites conf/<env>.yml), then authenticates by verifying the submitted plaintext against the stored hash. So ADMIN123 works only on a --insecure boot, and only because the stored hash happens to be a hash of it. Any server whose conf/local.yml was generated gets a random key instead, and every MCP call 401s.

That 401 was close to undiagnosable. make_get_request wraps it in a successful tool result, and core_health_check reported "Caldera API is UP!" because the error object it type-checked is itself a dict.

This PR:

  • Builds the REST URL from the host and port CALDERA binds to, mapping wildcard binds to loopback (0.0.0.0 is not a routable destination) and bracketing IPv6 literals.
  • Picks the API key by asking CALDERA's own hasher the same question its auth service asks, in process and without a request.
  • Reports a rejected key at boot and on the splash page instead of failing silently later.
  • Fixes core_health_check reporting UP on a non 200.

The key check is diagnostic only. The key is returned either way, and an explicitly configured one is never swapped for a value the operator did not choose. It is skipped when CALDERA_URL points elsewhere, since a remote server's keys are not ours to verify.

Both env vars keep top precedence. get_env() now forwards the resolved values rather than re-reading the environment, so subprocesses receive a normalised URL. Previously a CALDERA_URL without the /api/v2/ suffix reached the child raw, and the cti_pipeline server (which only appends) hit the login page and got a JSONDecodeError on a 200 HTML body.

Also drops the two Caldera entries from .env.example. CALDERA_URL=http://localhost:8888 there pinned port 8888 and outranked the port CALDERA actually listens on, so copying the file broke any deployment that had moved off the default.

Note for reviewers: this deliberately does not read api_key_red as a credential. That approach cannot work on current CALDERA, and a regression test asserts the resolver never returns an $argon2id$ value.

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • This change requires a documentation update (included)

How Has This Been Tested?

Against a live CALDERA 5.3.0 on conf/default.yml:

  • Zero config, no .env: resolves http://127.0.0.1:8888/api/v2/, GET /api/v2/health returns 200 with "access": "RED".
  • The resolved key is accepted by core's own auth_svc.request_has_valid_api_key, and the resolver's verdict matches it.
  • Sending api_key_red's raw hash returns 401, confirming a hashed config value is never a usable credential.
  • caldera_core spawned over stdio exactly as the workflow spawns it: 20 tools exposed, core_health_check returns UP.
  • core_health_check with a deliberately wrong key: main returns "Caldera API is UP!", this branch returns "... is not usable: ... 401: Unauthorized".
  • cti_pipeline with a suffixless CALDERA_URL: main gets 200 text/html and a JSONDecodeError, this branch gets 200 application/json.
  • Splash template renders both the resolved and rejected states.

Suite: 28 new tests in tests/test_config.py covering wildcard and IPv6 and narrowed binds, port and URL overrides, hashed and plaintext and unreadable configs, and subprocess env propagation. Full run shows no regressions (failures are a strict subset of the main baseline, all pre-existing spaCy and CTI issues). flake8 --select=E9,F63,F7 passes with no new findings.

Not verified: hook.py's enable() on a live boot, since the running server loads the unmodified plugin. The resolution and log formatting were tested directly.

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have made corresponding changes to the documentation
  • I have added tests that prove my fix is effective or that my feature works

The plugin hardcoded ADMIN123 as its API key fallback and localhost as its
host. Caldera hashes api_key_red and api_key_blue at boot and authenticates
by verifying the submitted plaintext against the stored hash, so ADMIN123
only works on a --insecure boot, and only because the stored hash happens to
be a hash of it. Every server whose conf/local.yml was generated gets a
random key instead, and the resulting 401 was close to undiagnosable: the
tool wrapped it in a successful result and core_health_check reported
"Caldera API is UP!" because the error object it type-checked was a dict.

Build the URL from the host and port caldera binds to, mapping a wildcard
bind to loopback since 0.0.0.0 is not a routable destination and bracketing
IPv6 literals. Check the API key by asking caldera's own hasher the same
question its auth service asks, in-process and without a request, and report
a rejected key at boot and on the splash page instead of failing silently
later.

The check is diagnostic only. The key is returned either way, and an
explicitly configured one is never swapped for a value the operator did not
choose. It is also skipped when CALDERA_URL points elsewhere, since a remote
server's keys are not ours to verify.

Both env vars keep top precedence for deployments where MCP cannot reach the
address caldera binds to. get_env now forwards the resolved values rather
than re-reading the environment, so subprocesses receive a normalised URL;
previously a CALDERA_URL without the /api/v2/ suffix reached the child raw,
and the cti_pipeline server hit the login page and got a JSONDecodeError on
a 200 HTML body.

Drop the two Caldera entries from .env.example. CALDERA_URL there pinned
port 8888 and outranked the port caldera actually listens on, so copying the
file broke any deployment that had moved off the default port.
@HackedRico
HackedRico merged commit 91709db into mitre:main Aug 28, 2026
3 checks passed
@HackedRico
HackedRico deleted the fix/caldera-connection-resolution branch August 28, 2026 15:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant