Dockerized HexStrike AI on the linuxserver.io Kali base image, with a new remote, streamable-HTTP MCP server so the full HexStrike toolset can be driven over the network (e.g. from Claude Code) instead of only from a local stdio client.
Warning
This exposes autonomous execution of offensive security tools over the
network. A client connected to the MCP endpoint can effectively perform
remote command execution across the container's tool arsenal. Read the
Security section before running this anywhere but an isolated
lab. Always set HEXSTRIKE_MCP_TOKEN, only run on a trusted/isolated
network or VPN, and only target systems you are explicitly authorized to
test.
Note
No prebuilt image is published. Because the full Kali toolset makes the image large (>10 GB — roughly ~17 GB built), it is not pushed to Docker Hub or any registry. You build it yourself from this repository (see Quick Start); make sure the Docker host has enough free disk.
- Features
- Overview
- Architecture
- Ports
- Quick Start
- Connecting from Claude Code
- Environment Variables
- Persistence
- Web Desktop Access
- Health Check
- Security
- Tool Coverage
- Updating the Upstream Pin
- Credits & License
- The full upstream HexStrike AI toolkit — 150+ security tools driven through its Flask API — pre-installed on a Kali-based image, no manual environment setup.
- New:
hexstrike_mcp_http.py, a streamable-HTTP MCP server that exposes every upstream@mcp.tool()definition over the network on port8889(path/mcp), so it can be driven remotely instead of only via local stdio. - Optional bearer-token authentication guarding the MCP endpoint.
- Browser-accessible KDE desktop (Selkies/KasmVNC, inherited from the base image) for interactive work alongside automated tool runs.
s6-overlayv3 process supervision with explicit service ordering — the MCP service waits for the HexStrike API server to be defined as a dependency.- Persistent
/configvolume: per-tool virtualenvs and file-manager state survive container restarts. - Reproducible builds:
uvmanages the Python environment, and upstream sources are pulled at a pinned git commit rather than vendored.
HexStrike AI upstream ships two Python entry points:
hexstrike_server.py— a Flask API server listening on port8888that executes 150+ external security tools viasubprocessand exposes their results over HTTP (including a/healthendpoint that reports tool availability).hexstrike_mcp.py— a FastMCP server that translates MCP tool calls into HTTP calls against the API server above. Upstream only runs it over the stdio transport, so it can only be driven by an MCP client on the same machine as the container.
This repository adds hexstrike_mcp_http.py, which imports upstream's
hexstrike_mcp.py module and reuses its setup_mcp_server() function
wholesale — every tool definition, unchanged — but serves it over the MCP
streamable-HTTP transport on port 8889 at path /mcp instead of stdio.
That's the only functional difference: the same tool surface, reachable
remotely (e.g. from Claude Code running on a different machine) rather than
only from a co-located process.
uv (Astral) manages the Python interpreter
and dependency environment and is used to run both processes at container
start.
- Base image:
lscr.io/linuxserver/kali-linux:latest, which provides a browser-accessible KDE desktop (Selkies/KasmVNC) on ports3000(HTTP) and3001(HTTPS), Kali-rolling apt repos, ands6-overlayv3 init. - Added services: two
s6-overlaylongrunservices are layered on top of the base image's init system and registered in theuserservice bundle:root/etc/s6-overlay/s6-rc.d/hexstrike-server— runsuv run --no-sync python hexstrike_server.py --port 8888as theabcuser.root/etc/s6-overlay/s6-rc.d/hexstrike-mcp— runsuv run --no-sync python hexstrike_mcp_http.py --server http://127.0.0.1:8888 --host 0.0.0.0 --port 8889as theabcuser. Itsdependencies.d/hexstrike-serverentry orders it to start after the API server.
- Upstream sources are not vendored. The
Dockerfilegit clones0x4m4/hexstrike-aiat build time and checks out a pinned commit (ARG HEXSTRIKE_REF, defaultd689933ff579d839c676c82b231f8e98326c5f04), copying onlyhexstrike_server.py,hexstrike_mcp.py, andhexstrike-ai-mcp.jsoninto/app. This keeps the repo small and the upstream bump a one-line change. - Python environment: a
uv-managed CPython 3.12 (pinned via.python-version), with dependencies frompyproject.tomlinstalled into/app/.venvviauv sync --no-devat build time. - Persistence bootstrap:
root/custom-cont-init.d/10-hexstrike-persistruns as root before the longrun services start (see Persistence).
| Port | Protocol | Description |
|---|---|---|
| 3000 | HTTP | Web desktop (Selkies/KasmVNC), inherited from the linuxserver Kali base image |
| 3001 | HTTPS | Web desktop, TLS |
| 8888 | HTTP | HexStrike AI API server (hexstrike_server.py) |
| 8889 | HTTP | HexStrike AI MCP server, streamable-HTTP transport, endpoint path /mcp |
All configuration lives directly in docker-compose.yml — there is no .env
file. Before the first launch, edit the environment: block and set:
HEXSTRIKE_MCP_TOKEN— the bearer token for the MCP endpoint. Generate a strong value withopenssl rand -hex 32and paste it in (or leave it empty for an open endpoint — lab/CTF on an isolated network only).CUSTOM_USER/PASSWORD— the web desktop basic-auth login.
Then build and start:
# generate a token to paste into docker-compose.yml:
openssl rand -hex 32
docker compose up -d --buildThe build is large and slow, and there is no prebuilt image to pull — the
resulting image is >10 GB (~17 GB), so it is not published to any registry
and must be built locally. It installs the kali-linux-headless metapackage
plus a long list of named tools, compiles angr / pwntools wheels (hence
build-essential + dev headers in the image), builds three Go tools from
source, and downloads a rustscan release package. Expect the first build to
take a significant amount of time and plenty of free disk (budget ~20 GB+
headroom, more while iterating).
docker build -t hexstrike-ai:local .
docker run -d \
--name hexstrike-ai \
--cap-add=NET_RAW --cap-add=NET_ADMIN \
--shm-size=1g \
-p 3000:3000 -p 3001:3001 -p 8888:8888 -p 8889:8889 \
-v "$(pwd)/config:/config" \
-e PUID=1000 \
-e PGID=1000 \
-e TZ=Etc/UTC \
-e CUSTOM_USER=kali \
-e PASSWORD=changeme \
-e HEXSTRIKE_MCP_TOKEN="$(openssl rand -hex 32)" \
hexstrike-ai:local--cap-add=NET_RAW --cap-add=NET_ADMIN and --shm-size=1g are required —
see Security and the docker-compose.yml comments for why.
Once the container is up, point Claude Code at the MCP endpoint on port
8889. If HEXSTRIKE_MCP_TOKEN is set (strongly recommended — see
Security), every request must include a matching
Authorization: Bearer <token> header; the endpoint only accepts
unauthenticated requests if the token is left unset.
Project/user MCP config (.mcp.json or Claude Code settings):
{
"mcpServers": {
"hexstrike-ai": {
"type": "http",
"url": "http://<HOST>:8889/mcp",
"headers": { "Authorization": "Bearer <HEXSTRIKE_MCP_TOKEN>" }
}
}
}Or via the CLI:
claude mcp add --transport http hexstrike-ai http://<HOST>:8889/mcp \
--header "Authorization: Bearer <TOKEN>"Replace <HOST> with the container host's address/hostname and
<HEXSTRIKE_MCP_TOKEN> / <TOKEN> with the value you generated above. If
you deliberately left HEXSTRIKE_MCP_TOKEN unset, omit the headers /
--header argument — the endpoint is open in that case.
Invalid Host header/ HTTP 421? The MCP streamable-HTTP transport has built-in DNS-rebinding protection that, by default, only trusts alocalhostHostheader. This image disables that check by default so you can reach the endpoint over the LAN by IP or hostname. If you instead see this error, either upgrade to this image (the default already handles it) or setHEXSTRIKE_MCP_ALLOWED_HOSTSto the exacthost:portvalue(s) your clients send.
| Variable | Where set | Default | Description |
|---|---|---|---|
PUID |
docker-compose.yml / -e |
1000 |
linuxserver convention: UID the abc service user runs as inside the container. |
PGID |
docker-compose.yml / -e |
1000 |
linuxserver convention: GID the abc service user runs as. |
TZ |
docker-compose.yml / -e |
Etc/UTC |
Container timezone. |
CUSTOM_USER |
docker-compose.yml |
kali |
Basic-auth username for the web desktop (linuxserver Kali base image). |
PASSWORD |
docker-compose.yml |
changeme |
Basic-auth password for the web desktop. |
HEXSTRIKE_MCP_TOKEN |
docker-compose.yml |
(empty) | Bearer token required on the :8889/mcp endpoint. Set it to a value from openssl rand -hex 32; leave empty only for an isolated/open lab setup. |
HEXSTRIKE_SERVER_PORT |
optional, commented example in docker-compose.yml |
8888 |
Port the HexStrike API server binds to. Both s6 run scripts read this; if you change it you must also update the published port mapping. |
HEXSTRIKE_MCP_PORT |
optional, commented example in docker-compose.yml |
8889 |
Port the MCP streamable-HTTP server binds to (same caveat as above). |
HEXSTRIKE_MCP_HOST |
optional | 0.0.0.0 |
Interface the MCP server binds to; passed straight through by the hexstrike-mcp run script and read as an env fallback by hexstrike_mcp_http.py. |
HEXSTRIKE_MCP_ALLOWED_HOSTS |
optional, -e |
(empty) | Comma-separated Host/Origin allowlist for the transport's DNS-rebinding protection (e.g. hexstrike.lan:8889,10.0.0.5:8889). When empty (default) the Host check is disabled, so the endpoint is reachable via any address/hostname. Set it to lock the endpoint to specific hostnames. |
HEXSTRIKE_MCP_TOOL_FILTER / _INCLUDE_ONLY / _EXCLUDE_TOOLS / _KEEP_TOOLS |
optional, -e |
auto / empty |
Control which MCP tools are exposed — see MCP tool filtering. |
hexstrike_mcp_http.py also accepts --server / HEXSTRIKE_SERVER_URL,
--path / HEXSTRIKE_MCP_PATH, --timeout / HEXSTRIKE_TIMEOUT, and
--allowed-hosts / HEXSTRIKE_MCP_ALLOWED_HOSTS overrides. The shipped s6
run script only passes --server, --host, and --port; the rest are read
from their environment fallbacks (so HEXSTRIKE_MCP_ALLOWED_HOSTS works when
set on the container) or keep their built-in defaults (/mcp, upstream's
default request timeout, and the Host check disabled).
The /config volume is the linuxserver-convention home directory and is the
only path that survives container recreation. The cont-init script
root/custom-cont-init.d/10-hexstrike-persist runs as root before the
longrun services start and:
- Creates
/config/hexstrike/envs,/config/hexstrike/files, and/config/hexstrike/output. - Replaces upstream's hardcoded
/tmp/hexstrike_envsand/tmp/hexstrike_filespaths with symlinks into/config/hexstrike/envsand/config/hexstrike/filesrespectively, so per-tool Python virtualenvs HexStrike builds on demand, and its file-manager read/write area, survive container restarts. chowns the persistent tree toabc:abc(respectingPUID/PGID) vialsiown.
Dynamic /tmp/<tool>_output directories created by individual tool runs are
not symlinked and remain ephemeral — point a given tool explicitly at
/config/hexstrike/output if you need its output to survive a restart.
Open https://<HOST>:3001/ in a browser and log in with the CUSTOM_USER /
PASSWORD credentials configured above (the HTTP desktop is also available
on port 3000 without TLS).
curl http://<HOST>:8888/healthReturns JSON from the HexStrike API server listing which of its tools were
detected as available on PATH inside the container.
Warning
The MCP endpoint on 8889 lets a connected client autonomously drive
execution of offensive security tools inside the container — this is
effectively remote command execution by design, gated only by whatever
bearer token you configure.
- Always set
HEXSTRIKE_MCP_TOKEN. Generate it withopenssl rand -hex 32and keep it secret. Leaving it empty makes the MCP endpoint unauthenticated. - Only expose this on a trusted/isolated network or VPN. Do not publish
port
8889(or8888) to the open internet. - Only target systems you are explicitly authorized to test. The container ships real offensive security tooling capable of active scanning, exploitation, credential attacks, and more.
- The container is granted
NET_RAWandNET_ADMINcapabilities (seedocker-compose.yml/ thedocker runexample) because several tools —nmapSYN scans,masscan,responder, etc. — need raw sockets. These are narrower than--privilegedbut still meaningfully elevated; grant them only if you need those scan types. - Auth is implemented as a minimal ASGI middleware
(
BearerTokenASGIMiddlewareinhexstrike_mcp_http.py) that inspects theAuthorizationheader with a constant-time comparison and rejects unauthenticated requests to the MCP path with401before they reach the MCP app — it does not buffer or otherwise interfere with the streamable-HTTP/SSE response stream.
The tool set is curated, not exhaustive:
- The
kali-linux-headlessmetapackage, plus a list of tools named explicitly in theDockerfile(the ones HexStrike's/healthendpoint probes, and common recon/web/exploitation utilities) — e.g.nmap,masscan,gobuster,feroxbuster,ffuf,dirb,dirsearch,nikto,sqlmap,wpscan,whatweb,wafw00f,hydra,john,hashcat,medusa,evil-winrm,amass,dnsenum,dnsrecon,fierce,theharvester,responder,nuclei,subfinder,httpx-toolkit,arjun,paramspider,hakrawler,getallurls(gau),netexec,enum4linux-ng,smbclient,seclists,wordlists,binwalk,foremost,steghide,exiftool,radare2, andgdb. - Three Go-built tools compiled from source at build time and then stripped
of the Go toolchain:
katana,dalfox,waybackurls. rustscan, installed from its latest GitHub release (the.debships inside arustscan.deb.zipasset, which the build unzips before installing; best-effort — the build continues without it if the asset can't be resolved).
Some heavy or licensed tools referenced by upstream HexStrike — Ghidra, IDA, Burp Suite Pro, Maltego, Nessus, and various cloud/forensics utilities — are not installed in this image to keep the build tractable.
Upstream registers ~150 MCP tools. That overflows the function-tool limit
of some model providers (OpenAI caps at ~128) and surfaces tools that can only
fail because their CLI binary isn't installed. To avoid this, the MCP server
hides any tool whose backing binary is not present (checked with
which at startup, since the MCP process runs in the same container as the
tools). A typical build exposes ~115 tools instead of 150. The startup log
prints the count and the hidden list.
Control it via environment variables on the container:
| Variable | Default | Effect |
|---|---|---|
HEXSTRIKE_MCP_TOOL_FILTER |
auto |
auto hides tools with a missing binary; off exposes all ~150. |
HEXSTRIKE_MCP_INCLUDE_ONLY |
(empty) | Comma-separated whitelist — expose only these tools (overrides auto). |
HEXSTRIKE_MCP_EXCLUDE_TOOLS |
(empty) | Comma-separated tools to always hide. |
HEXSTRIKE_MCP_KEEP_TOOLS |
(empty) | Comma-separated tools to always keep (protects them from auto-drop). |
Upstream sources are cloned at build time at the commit in
ARG HEXSTRIKE_REF (Dockerfile). To pick up newer upstream changes, bump
that pin and rebuild:
docker build -t hexstrike-ai:local --build-arg HEXSTRIKE_REF=<new-sha> .or, with Compose, uncomment and set the args: HEXSTRIKE_REF: entry under
the build: key in docker-compose.yml and run docker compose up -d --build.
- HexStrike AI by
0x4m4 — the security automation platform this
image packages, released under the MIT License. See upstream's
LICENSEfile for full terms. - linuxserver.io
kali-linuximage — the base image providing the Kali environment and web desktop.