Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

HEXORA

Pentester Console by AWHADI

Version: v1.23.0 · Changelog

A self-hosted web dashboard for running common Kali Linux pentesting tools against targets you are authorized to test. It's a docker-compose stack with two containers:

  • kali — a Kali Linux image with the kali-linux-headless metapackage (a large slice of everything Kali ships) plus openssh-server, reachable only from the web container over an internal Docker network. It also refreshes its own packages and vulnerability databases in the background on every start — see Keeping Kali up to date.
  • web — a Node.js/Express backend + static frontend. It supports multiple accounts with roles (see User management), optional single sign-on via any OpenID Connect provider (see Single sign-on), and outgoing email via SMTP (see Email (SMTP)) — all configurable from an admin-only Settings page. It shows a dashboard of tools with input forms and runs the chosen command inside the kali container over SSH, streaming output back to the browser live via WebSockets. A "Custom Command" tool lets you run any other binary installed in the image.

HEXORA dashboard

Features

  • 21 tools across 9 categories — Nmap, Ping, Traceroute, tcpdump, ARP Scan, Whois, dig, theHarvester, DNSRecon, Nikto, Gobuster, ffuf, sqlmap, WPScan, Hydra, John the Ripper, Hashcat, SSLScan, Enum4linux, SMBMap, SearchSploit — plus an Automated Vulnerability Scan wizard and a Custom Command escape hatch for anything else installed in the image. Every tool also has a raw-arguments field to type the exact command yourself instead of using the structured form. See Available tools.
  • Multi-session dashboard — run several tools at once, each in its own panel; minimize any of them to the bottom dock and keep working. A search box in the top bar jumps straight to any tool by name. Sessions survive a page refresh (they keep running and reconnect), and the maximum number of concurrent sessions is admin-configurable.
  • Accounts, roles, and SSO — local accounts with admin/user roles, or sign in via any OpenID Connect provider (Google, Microsoft/Entra ID, Keycloak, Okta, ...), with auto-provisioning and optional group-to-admin mapping.
  • Fine-grained access control — disable whole tool categories or individual tools for non-admins, with per-user overrides on top, plus a centrally-enforced password policy.
  • Optional AI-assisted analysis — send a completed run's output to your own configured OpenAI-compatible provider (or a local one, e.g. Ollama) for a plain-language summary that calls out anything security-relevant; off by default, admin-configured, and IPs/MAC addresses are redacted before anything leaves the server. See Settings page.
  • Email-backed self-service — password reset, one-time-code sign-in, and self-registration with confirmation, all opt-in and gated on a verified SMTP connection.
  • Four built-in themes — Dark, Light, Aurora Glass, and Matrix, set as a site-wide default and overridable per-user.
  • Runs anywhere — a single docker compose up, an interactive installer, subpath deployment behind a reverse proxy, a mobile-friendly layout, and a non-root, structured-logging-by-default container for web.
  • Local-network access is opt-in — by default the kali container can only reach the public internet, not your LAN; the installer asks, and it can be changed anytime via KALI_ALLOW_LOCAL_NETWORK. See Security notes.

⚠️ Authorized use only

This stack executes real offensive-security tools (port scanners, brute forcers, SQL injection testers, password crackers, etc.) against whatever target you point it at. Only use it against systems and networks you own or have explicit written authorization to test. Do not expose this site to the public internet — see Security notes below.

Contents

Prerequisites

  • Docker and Docker Compose v2 (docker compose version)
  • ~10–20 GB free disk space (the kali-linux-headless metapackage is large)

Setup

Quick start (recommended)

./installer.sh

This interactive installer checks Docker (and Docker Compose) is available and running (offering to start Docker Desktop on macOS), asks which port to use and whether you're serving this from a subpath behind a reverse proxy, lets you pick how much of Kali to install, asks whether kali should be allowed to reach your local network (LAN) in addition to the public internet (see Security notes), and optionally builds and starts the stack. Re-run it anytime — it shows your current settings and lets you update any of them instead of starting over.

Skip to Manual setup if you'd rather do each step yourself, or if you're troubleshooting something the wizard already ran.

Manual setup

  1. Configure environment variables:

    cp .env.example .env

    Edit .env and set SESSION_SECRET to a long random string, e.g. openssl rand -hex 32. WEB_PORT and KALI_META have sensible defaults — see Choosing how much of Kali to install.

  2. Start the stack:

    docker compose up --build -d

    The first build installs the Kali metapackage and can take a long time depending on your connection and disk speed. The kali container generates its own SSH keypair on first boot (shared with web over a Docker volume) — nothing to set up for that yourself.

  3. Visit http://localhost:3000 (or whatever WEB_PORT you set). Since no accounts exist yet, you'll be asked to create the first admin account right there in the browser — see User management.

Running behind a subpath

By default the app expects to be served from a domain's root (https://example.com/). To serve it from a subpath instead (https://example.com/pentester/), set BASE_PATH in .env (no trailing slash, e.g. BASE_PATH=/pentester) — installer.sh asks for this too. Every internal link, API call, and the WebSocket connection automatically adjust to it, so the reverse proxy just needs to pass the subpath straight through, with no path-rewriting:

location /pentester/ {
  proxy_pass http://web:3000/pentester/;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection "upgrade";
}

If you're also using SSO, set PUBLIC_URL to the full external URL including the subpath (e.g. https://example.com/pentester) so the OIDC redirect_uri matches what's registered with your identity provider — see Single sign-on (OIDC).

Using it

A tool running with live output

  • The dashboard groups tools by category (Reconnaissance, Web, Password Attacks, etc.). Click a tool card to open its input form, or use the search box in the top bar to jump straight to one by name.
  • Every tool's form has a raw-arguments field at the bottom — fill in the structured fields above it as usual, or type the exact arguments yourself there instead (it overrides the fields above when used).
  • Output streams live in the panel on the right; use Stop to kill a long-running scan.
  • Run several tools at once — each opens its own panel. Minimize a panel to shrink it to a chip in the dock at the bottom of the screen (it keeps running and buffering output in the background); click the chip to bring it back, or Close all to tear down every open session at once.
  • A green/red dot on each card shows whether that tool's binary is actually present in the kali image (useful if you swap KALI_META for a smaller metapackage).
  • Custom Command (under "Advanced") runs any binary installed in the Kali image with arbitrary arguments — use this for anything not covered by a dedicated form.
  • Automated Vulnerability Scan (under "Automated Scan") is a wizard: give it a target, optionally a login (HTTP Basic/Digest, or a web form's login URL + field names + username/password), and it chains Nmap → Nikto → Gobuster → WPScan (and SQLMap, if you give it a URL with a parameter to test) against the target automatically, then ends with a report of what ran and whether each step found something. Form login works by POSTing your credentials to the login URL with curl and reusing the session cookie in later steps — it only handles simple POST forms (no CSRF token, no JavaScript-rendered login) and does not itself verify the login succeeded, so check the HTTP status in the "Log in" step's output. The report's summaries are automated heuristics (counting lines, matching keywords) — always check the raw output too. Click Download on the report to save it as a text file.
  • Update Kali Tools (under "Maintenance", admin only) — see Keeping Kali up to date.
  • Tools that operate on hash files or wordlists (John, Hashcat, Hydra) expect the file to already exist inside the kali container. Kali's bundled rockyou.txt wordlist is auto-unzipped on first start. To add your own files, docker cp yourfile.txt pentester-kali:/root/ or mount a volume in docker-compose.yml.

Available tools

Category Tool What it does
Reconnaissance Nmap Port scanning and service/version detection.
Reconnaissance Ping Check whether a host responds and measure round-trip latency.
Reconnaissance Traceroute Show the network path (hop by hop) to a host.
Reconnaissance tcpdump Live packet capture on a network interface.
Reconnaissance ARP Scan Discover live hosts via ARP — only on this container's own Docker network, not the wider LAN.
Reconnaissance Whois Domain/IP registration lookup.
Reconnaissance DNS Lookup (dig) Query DNS records for a domain.
Reconnaissance theHarvester OSINT: harvest emails, subdomains and names from public sources.
Reconnaissance DNSRecon DNS enumeration and zone transfer testing.
Web Nikto Web server vulnerability scanner.
Web Gobuster (dir) Directory/file brute-forcing on a web server.
Web ffuf Fast web fuzzer for directories, parameters and vhosts.
Web sqlmap Automated SQL injection detection and exploitation.
Web WPScan WordPress vulnerability scanner.
Password Attacks Hydra Online login brute-forcer for network services.
Password Attacks John the Ripper Offline password hash cracker.
Password Attacks Hashcat GPU/CPU-accelerated hash cracking.
SSL/TLS SSLScan Enumerate SSL/TLS ciphers and certificate info.
Windows/SMB Enum4linux Enumerate SMB shares, users and groups on Windows/Samba hosts.
Windows/SMB SMBMap Enumerate SMB share permissions.
Exploitation SearchSploit Search the offline Exploit-DB archive for known exploits.
Maintenance Update Kali Tools (admin only) Refresh installed packages and the Exploit-DB/Nikto/WPScan databases.
Automated Scan Automated Vulnerability Scan Wizard: optional login, then chains Nmap → Nikto → Gobuster → WPScan (+ SQLMap) with a report at the end.
Advanced Custom Command Run any binary installed in the Kali image with arbitrary arguments.

See Adding more tools to add your own.

User management

Accounts have a role: admin or user. Admins can manage other accounts and see admin-only tools (currently just "Update Kali Tools"); everyone can run the rest of the dashboard and change their own password.

  • First run: since no accounts exist yet, opening the site shows a "create the first admin account" form instead of a login form — pick whatever username and password you want there. That account (or the first person to sign in via SSO, if you've set that up — see below) becomes the first admin.

  • Scripted/headless first admin: for automated deployments where nobody will click through the browser, set INITIAL_ADMIN_USERNAME and INITIAL_ADMIN_PASSWORD (or INITIAL_ADMIN_PASSWORD_FILE, pointing at a file/Docker secret) in .env before first start. If no accounts exist yet when the web container boots, it creates that admin automatically and logs it. This only ever fires once — once any account exists, these variables are ignored, so it's safe to leave them set. If your password contains a literal $, double it ($ → $$) — see the Compose $-interpolation note further down; this is exactly why the _FILE variant (reading from a mounted file, untouched by Compose) is the safer choice for automation.

  • Adding/removing users: an admin opens the round avatar button in the top bar → Manage Users (or Settings → Users) and clicks + Add User, which opens a popup to fill in username, email, password, and role. From the Users table an admin can also edit anyone's email, reset anyone's password, promote/demote between user and admin, or delete an account. You can't delete or demote the last remaining admin, and you can't delete or change the role of the account you're currently logged in as (avoids accidental lockouts).

  • Changing your own password/email/photo: any logged-in user can do this via My Account from the avatar menu (password change requires the current password; admins do this from their own row in Manage Users instead). The same dialog lets you upload a profile photo (shown in the avatar button instead of your initials) or remove it.

  • Password reset via email: the login page's Forgot password? link emails a reset link (valid 45 minutes, single-use) to the address on file for that account. This link only appears once an admin turns on Allow password reset via email in Settings → Email (SMTP) — turning it on re-verifies the configured SMTP server actually connects first, so it can't be enabled with a mail server that doesn't work. The response is always the same generic message regardless of whether the account/email exists, so the login page can't be used to enumerate accounts.

  • One-time-code sign-in: the login page's Use a one-time code instead link emails a 6-digit code (valid 10 minutes, single-use, locked out after 5 wrong attempts) instead of asking for a password — same SMTP-verified opt-in (Allow one-time-code sign-in via email in Settings → Email (SMTP)) and anti-enumeration behavior as password reset.

  • Optional public self-registration: disabled by default. An admin can turn it on in Settings → General ("Allow public self-registration"), which - like password reset and one-time codes - re-verifies SMTP first; when on, the login page shows a Register link. Self-registered accounts are always created with the user role - the request body's role, if any, is ignored server-side, not just hidden in the UI - and must confirm their email (a link valid for 24 hours, sent on registration) before they can sign in; the account doesn't exist in a usable form until then. Signing in beforehand fails with a clear error and a Resend confirmation email link. An admin can see who's still pending in Settings → Users (tagged "pending confirmation") and activate an account manually with Activate now, e.g. if the email never arrives.

  • Single sign-on kill switch: Settings → General also has Enable single sign-on, on by default. It doesn't touch any configured provider - it just hides the "Sign in with ..." buttons and blocks the callback routes while off, so you can temporarily turn off SSO without deleting/reconfiguring providers.

  • Locked out / recovery: create or reset an account directly, without the browser:

    docker compose exec web node create-admin.js <username> <password> [admin|user]

    If the username exists, this resets its password (and role, if given); otherwise it creates a new account (admin, unless you specify user).

Accounts (including email addresses) are stored in backend/data/users.json inside a named Docker volume (web-data), so they survive docker compose up --build. Sessions and pending password-reset/OTP codes are in-memory only, so everyone is logged out and any not-yet-used reset link or code is invalidated if the web container restarts — that's unchanged from before and just means signing back in or requesting a new code/link, not losing any accounts.

Settings page

The Settings page

Everything admin-only lives on its own page at /settings.html, opened from the round avatar button in the top bar (admins see extra links there; non-admins just get My Account and Log out) — it has its own sidebar with six sections: General Settings, Users, Single Sign-On, Email (SMTP), AI Analysis, Analytics, Privacy & Cookies (with the cookie scanner), and Access Control. Each avatar-menu item deep-links straight to its section (e.g. Email (SMTP) opens /settings.html#smtp). Non-admins are redirected away from the page if they navigate there directly (the underlying API routes are admin-gated too, so this isn't just a UI hint). Users and SSO providers are both added through a popup (+ Add User / + Add Provider) rather than an always-visible inline form, so the page stays uncluttered until you actually want to add one.

Access control

Settings → Access Control lets an admin disable entire tool categories (e.g. "SSL/TLS", "Windows/SMB") or individual tools within an otherwise enabled category (e.g. keep "Web" on but turn off just "sqlmap") for everyone except admins. Disabling a whole category always wins even if a tool's own checkbox is on. Disabled categories/tools disappear from non-admins' dashboards and are rejected server-side if requested directly (e.g. by calling the WebSocket API without going through the UI), not just hidden client-side. Admins always see and can run everything regardless of this setting, and admin-only tools (like "Update Kali Tools") are unaffected — this is a separate, coarser control on top of that.

Per-user overrides: in Settings → Users, each non-admin account has a Manage Access button opening the same tool list, but with a three-way choice per tool — Default (follow the settings above), Always allow, or Always block. A per-user override always wins for that one tool, in either direction, over the general Access Control settings; a tool left on "Default" just falls back to them. This lets you, for example, keep a whole category off for everyone but make an exception for one person.

AI-assisted analysis

Settings → AI Analysis lets an admin connect an OpenAI-compatible endpoint (OpenAI itself, a local server like Ollama or LM Studio, or any other provider speaking the same chat-completions API) and turn on an Analyze button that appears once a tool run finishes. Sending a run's output for analysis is a per-run, explicit action — nothing is sent automatically. Off by default; requires both enabling it and a successful connection test. Before anything is sent, IP addresses and MAC addresses in the output are redacted. The prompt asks the model to specifically call out anything security-relevant (vulnerabilities, misconfigurations, credentials, interesting findings) rather than just restate the output. Available to every signed-in user once an admin has configured and enabled it — a non-admin never sees the provider's endpoint or API key.

Analytics

Settings → Analytics lets an admin measure visits with Google Analytics 4, Plausible, Umami, Matomo or Cloudflare Web Analytics: pick the provider, enter its ID (and its URL for Plausible / Umami / Matomo), switch it on, and save. No code is pasted — the server adds the provider's script itself, escaped and with the page's CSP nonce, and widens that page's Content-Security-Policy by exactly the provider's own domains (shown on the page under What will be added to pages). Off by default; turning it off stops loading the script everywhere and keeps the saved IDs.

It always runs on the public pages (homepage, sign-in, legal pages, check-email). Also track signed-in pages (dashboard and My Account) is a separate, off-by-default switch. It never runs on the Settings pages, the email-confirmation and password-reset pages, or any page opened from a link carrying a one-time token / invitation (analytics services record the full page address). Respect Do Not Track and Global Privacy Control (on by default) skips visitors whose browser sends either signal. Update your Privacy Policy under Settings → Pages when you turn it on.

Privacy and cookies

Settings → Privacy & Cookies makes the app work for the GDPR / ePrivacy rules in Europe — and the UK, Swiss, Brazilian, Quebec and US rules too — without any setup. The page is five cards, built like the other settings pages: a Compliance check (a "Ready" / "n to fix" pill, with a one-click fix for anything that isn't), the Cookie banner (always previewed, with its title and text), Who runs this site, Visitors' data and the Cookie scanner. What regulators fix is not a setting: the choices that used to be here (how long a choice is remembered, how long records are kept, the always- visible Cookie settings button, relaxing the rule outside Europe) keep their safe defaults. These are tools to help; they are not legal advice.

  • One strict rule for everyone. Optional cookies (analytics) only run after the visitor says yes, wherever they are; Reject all, Choose and Accept all are on the first screen with identical styling; Do Not Track and Global Privacy Control are always honoured (the panel shows a "signal honoured" note). That is what the EU/EEA, UK, Switzerland, Brazil and Quebec require, and being stricter it also satisfies the US opt-out laws, so no country detection is needed. An install that already relaxed the rule for visitors outside Europe keeps working, and the Compliance check offers a one-click fix back to "ask everyone first".
  • A cookie banner built from what the site really sets. A compact card in the bottom-left corner, in the app's own look (same corners, mono label, theme colours - it follows Spectrum, Midnight and Daylight - and a full-width sheet on phones). It never covers the page or locks its scrolling. It has a short explanation, one row per group — Functional (always active) and Statistics (a switch; Marketing appears only if anything is ever found in it) — each opening to list its cookies, provider, purpose and lifetime, and two equal buttons, Accept and Deny. Save my selection appears once a switch has been changed. The X before a first choice counts as Deny, and Escape counts only while the card has focus. Functional can't be switched off — the login cookie, the cookie that remembers the choice, bot protection, interface preferences, and the free-usage limit for visitors without an account (applied by IP address on the server, no cookie, so declining statistics never gives anyone more free runs). A small always-visible Cookie settings button (plus a footer link) reopens the card, so withdrawing is as easy as consenting.
  • Trying it out. The preview on the page always shows the banner (with a sample Statistics entry while Analytics is off). Visitors only get it while Analytics is on; then See it on the site opens your homepage with the banner showing on every page load, for you alone while signed in as an admin — nothing you choose is recorded. Stop testing switches it off.
  • Consent you can prove, and re-ask when things change. Every choice is recorded (time, categories, version, a random id — no IP address) for 36 months and can be downloaded as CSV. The stored choice is tied to the site's optional cookies: when the scan finds a new one, visitors are asked again. Choices are remembered for 6 months.
  • Operator details and a complete Privacy Policy. Enter who is responsible (name and contact email are required; address, DPO, hosting country and governing law are optional) once; the policy fills its "[Operator contact email]"-style gaps and gains a generated section — who is responsible, every cookie, why data is used and the legal basis, how long each kind of data is kept (from the app's real limits), who else receives data, your rights and the right to complain. It stays accurate as you turn analytics, bot protection or the AI assistant on or off.
  • Users' data rights. Signed-in users can download my data (a JSON file: account, sign-ins, activity, usage) and delete my account (password- confirmed; SSO-only accounts type their username) from My Account. Deleting removes the account, its sign-in history and its activity and usage records, and ends its sessions. The last administrator can't delete their account. An admin switch turns both off.
  • Upgrading from an older version. The previous consent "modes" are gone; an existing setting is replaced by the strict rule (so an install only gets stricter, and the page says so), and every visitor sees the panel once more.

Cookie scanner

The cookie scanner is part of Settings → Privacy & Cookies. It requests the public pages (homepage, sign-in, legal pages, check-email) as five visitors — a new one from an EU country, one from the US, one who accepted analytics, one who rejected it, and one sending Global Privacy Control — reads each response (scripts, frames, images, the cookie panel, Set-Cookie headers) and looks every cookie and outside host up in a built-in catalogue. About 30 requests, not counted as visits. It runs by itself after the app starts and a moment after any setting that affects cookies changes; Scan site now runs it on demand.

Its checks feed the status at the top of the page: nothing optional loads before anyone agrees, the panel is on every page with an equal Reject and a Cookie settings button, analytics stays off after a "no" and for Global Privacy Control, no cookie lasts longer than 13 months, no unknown cookies or third-party addresses, outside-EU transfers, and reCAPTCHA / Google Fonts warnings. The scan also supplies the cookie list the panel and the Privacy Policy show, plus tables of every cookie and storage item, every third-party service and what each visitor type receives, and a card listing the cookies this browser is sending right now (names only, including HttpOnly ones).

Limits: it can't see cookies a third-party script sets in a visitor's browser at run time, so those come from the catalogue for the services it detects; signed-in pages and Settings aren't scanned. Google Analytics cookies are set to last 13 months instead of Google's 2-year default.

Password policy

Settings → General Settings has a password policy (minimum length, and optionally requiring a number and/or a special character) that's enforced centrally everywhere a password is set — self-registration, password reset via email, admin-created accounts, admin password resets, and a user changing their own password — so there's no path that bypasses it. The registration and change-password forms show the current requirements and set their minlength accordingly.

Themes

The four built-in themes

Four built-in themes — Dark, Light, Aurora Glass (a frosted, translucent look, the default), and Matrix (a green-on-black terminal look) — selectable in Settings → General Settings as the site-wide default for accounts that haven't chosen one, and overridable by any user for themselves in My Account. Applied via a data-theme attribute on <html> (internal theme keys are dark/light/glass/hacker), so a new theme just needs a CSS custom-property block in style.css — no per-component overrides required except where a theme changes the overall page treatment (see the glass/hacker blocks for examples).

Single sign-on (OIDC)

The login page can offer "Sign in with ..." buttons for any OpenID Connect provider — Google, Microsoft/Entra ID, Keycloak, Okta, Authentik, etc. It's generic: the app talks standard OIDC discovery/authorization-code-with-PKCE, so anything spec-compliant works, not just the three below. It's entirely optional and additive — local username/password login keeps working alongside it.

How accounts work with SSO: the first person to sign in via SSO when no accounts exist yet becomes the first admin, same as the browser bootstrap form. After that, a new IdP identity signing in for the first time gets a new local account (role user, unless mapped to admin — see the admin group setting below), linked to that identity by its subject (sub) claim, not just the username, so it keeps working if the display name/email changes upstream. Admins can manage these accounts in Settings → Users like any other, except there's no password to reset — they can only sign in through that provider (shown as "via <provider>" in the list).

Enabling a provider — from the Settings page (recommended)

Go to Settings → Single Sign-On → + Add Provider. Type a display label (e.g. "Keycloak") and its sign-in key is generated for you automatically as you type — edit it directly if you'd rather pick your own. Fill in the issuer URL, client ID and secret, pick which ID-token claim (preferred username, email, full name, or subject ID) becomes the new account's local username under Login field from SSO, then look at the highlighted Redirect URI box that appears: that's the exact URL to register with your identity provider (see the per-provider notes below for where). Click Copy, register it, save, and the provider immediately appears as a "Sign in with ..." button on the login page. A provider added here always takes priority over one configured via environment variables with the same key, and editing later never requires touching .env or restarting the container.

Enabling a provider — via environment variables

  1. Pick a short key for the provider (e.g. keycloak, google, microsoft, or any name you like) and add it to OIDC_PROVIDERS in .env (comma-separated if enabling more than one).

  2. Register this app with the IdP and set its redirect URI to:

    <PUBLIC_URL or http://localhost:3000>/api/oidc/<key>/callback
    

    Set PUBLIC_URL in .env if you're behind a reverse proxy or a different host/port than http://localhost:3000 — it must match exactly what you register.

  3. Fill in OIDC_<KEY>_ISSUER, OIDC_<KEY>_CLIENT_ID, and OIDC_<KEY>_CLIENT_SECRET (or _CLIENT_SECRET_FILE) in .env. See the commented examples in .env.example for the three providers below. Optionally set OIDC_<KEY>_USERNAME_CLAIM to preferred_username (default), email, name, or sub to control which ID-token claim becomes the new account's username.

  4. docker compose up -d web to apply.

Google (console.cloud.google.com/apis/credentials): create an OAuth 2.0 Client ID (type "Web application"), add the redirect URI from step 2 under "Authorized redirect URIs". Issuer is always https://accounts.google.com.

Microsoft / Entra ID (entra.microsoft.com → App registrations): register a new app, add the redirect URI as a "Web" platform redirect, and create a client secret under "Certificates & secrets". Issuer is https://login.microsoftonline.com/<tenant-id>/v2.0 (use your tenant ID from the app's Overview page, or common to allow any Microsoft account).

Keycloak: in your realm, create a client with "Client authentication" on (confidential), add the redirect URI from step 2 under "Valid redirect URIs", and copy the client secret from the client's "Credentials" tab. Issuer is https://<your-keycloak-host>/realms/<realm>. Set OIDC_KEYCLOAK_ADMIN_GROUP=<realm-role-name> to automatically grant the admin role to anyone with that realm role — it's re-checked on every login, so revoking it in Keycloak takes effect the next time they sign in.

Email (SMTP)

Settings → Email (SMTP) lets an admin configure an outgoing mail server (host, port, TLS, credentials, from address/name) so the app can send email. This powers password-reset links and one-time sign-in codes (see User management), and the Send a Test Email button sends a real message to an address you give it to confirm the whole chain works before relying on it. As with SSO, a configuration saved here always takes priority over the equivalent SMTP_* environment variables (see .env.example), and you never need to restart the container to change it. Leaving the password field blank while editing keeps the currently saved password.

Keeping Kali up to date

Stale tools and vulnerability databases mean missed findings, so this stack refreshes them in two ways:

  • Automatically, in the background, every time the kali container starts: it runs apt-get update && apt-get upgrade, then refreshes SearchSploit's Exploit-DB copy, Nikto's plugin database, and WPScan's vulnerability data. This happens in the background so it never delays SSH/the dashboard becoming available. Progress and errors are logged to /var/log/kali-auto-update.log inside the container (docker compose exec kali cat /var/log/kali-auto-update.log).
  • On demand: an admin can click Update Kali Tools in the dashboard to run the same refresh and watch it happen live, instead of waiting for the next restart. It needs the kali container to have internet access and can take several minutes.

Backup and restore

./backup.sh                 # writes to backups/<timestamp>/
./restore.sh backups/<timestamp>

backup.sh dumps the Postgres database (accounts, settings, custom tools, logs) and archives the web-data/ssh-keys volumes (legacy not-yet-migrated JSON files, and the SSH keypair the web app uses to reach Kali) into a timestamped directory under backups/ (gitignored — never committed). It needs the stack already running (docker compose up -d).

It deliberately leaves out two things: .env (host-specific secrets — SESSION_SECRET, POSTGRES_PASSWORD, API keys — never bundled into something that might get copied elsewhere) and the kali-home volume (just the Kali container's own OS/package state, not app data — rebuilt by docker compose build). A restore target needs its own .env already in place first.

restore.sh <backup-dir> is destructive — it overwrites the current database and both volumes with the backup's contents, after a type RESTORE to confirm prompt (skip it non-interactively with --yes). It briefly stops the web container during the restore (Postgres and Kali stay up) and starts it back up when done.

Adding more tools

Tool definitions live in backend/src/tools/definitions.js. Each entry declares an id, name, category, description, the binary to run, the input fields to render as a form, and a buildArgs(params) function that validates user input and returns the argument array (never build a raw shell string — always return an array so the backend can safely quote it before sending it to Kali over SSH). Add adminOnly: true to restrict a tool to admin accounts (enforced both when listing tools and when a run is requested, not just hidden in the UI). The automated scan wizard is the exception — it's a multi-step pipeline defined in backend/src/pipeline.js instead of a single buildArgs, since later steps (Gobuster, WPScan, SQLMap) need the session cookie an earlier login step captures.

Choosing how much of Kali to install

KALI_META in .env controls the metapackage installed in the kali image (see kali/Dockerfile). Options, roughly smallest to largest:

  • kali-linux-core — minimal base.
  • kali-linux-default — the standard desktop tool set.
  • kali-linux-headless — default here; broad tool coverage, no GUI/desktop cruft, reasonable for a container.
  • kali-linux-large / kali-linux-everything — essentially all Kali packages; multi-tens-of-GB and a long build. Only use if you have the disk space and need something obscure.

You can also docker compose exec kali apt-get install -y <package> to add one specific tool without rebuilding.

Security notes

  • Accounts and roles. See User management above. Sessions are signed with SESSION_SECRET and held in memory (a single web process, not distributed) — rotate SESSION_SECRET to invalidate all sessions at once.
  • Don't expose this publicly. It runs privileged scanning/attack tools on demand. If you need remote access, put it behind a VPN or an authenticated reverse proxy with TLS, and restrict source IPs.
  • Local-network (LAN) access is opt-in. By default KALI_ALLOW_LOCAL_NETWORK=false, and the kali container's entrypoint installs iptables/ip6tables rules that reject outbound traffic to RFC1918/link-local/ULA address ranges — it can still reach the public internet, just not devices on your LAN — with an explicit exception for loopback and already-established connections (so the web→kali SSH control channel keeps working). Set it to true (or answer yes in installer.sh) if you need to scan your own local network; on a shared or work network, leaving it off means a compromised or misused tool run can't reach anything beyond the kali container itself.
  • AI-assisted analysis sends data to a third party, if you turn it on. See AI-assisted analysis — it's off by default. Once enabled, running an analysis sends that run's (redacted) output to whatever endpoint is configured — a provider you control the trust relationship with, including a fully local option like Ollama if you'd rather nothing leave the host at all.
  • The kali container runs as root with NET_ADMIN/NET_RAW capabilities so tools like nmap -sS work. It only accepts SSH connections authenticated by a keypair it generates itself on first boot and shares with web over the ssh-keys Docker volume (never written to the host filesystem) — password auth is disabled in kali/sshd_config. Its own connection logging is intentionally quiet (LogLevel ERROR) since the app opens a fresh SSH connection per command — otherwise every tool run would add an "Accepted publickey" / "Disconnected" triplet to docker compose logs.
  • The web container runs as the unprivileged node user, not root. If you're upgrading an existing deployment whose web-data volume was created by an older (root) image, fix its ownership once after pulling the new image: docker compose exec -u root web chown -R node:node /app/data. A fresh deployment doesn't need this — the image creates the directory with the right owner itself.
  • Input handling. Every form field is validated against a strict allowlist regex (host/IP, port range, path, safe token) before being turned into a command, and the final argument list is shell-escaped with shell-quote before being sent over SSH — user input is never interpolated into a shell string directly. The "Custom Command" tool is intentionally more open (it runs whatever binary/args you type) — treat it like a shell prompt into the Kali box.
  • Logging. The web container logs to stdout only (no log files to rotate or lose on a rebuild) - watch it with docker compose logs -f web. Every line is tagged so you can grep for one kind: [ACCESS] (every API request: method, path, status, IP, response time, signed-in user), [AUTH] (login/registration/token events, including failed logins, OTP failures, and rate-limit hits - this is what to watch for brute-force or account-enumeration attempts), and [AUDIT] (every command run, including each step of the automated scan and "Update Kali Tools" - username, tool, command, timestamp).
  • Panel-stored secrets (OIDC client secrets, SMTP password) are kept in plaintext JSON files (backend/data/oidc-providers.json, backend/data/smtp-config.json) on the same persistent volume as everything else — the same trust model as the SSH keypair on the ssh-keys volume (see above). There's no separate encryption-at-rest layer, so treat the host's Docker volumes/backups accordingly.
  • Wireless tools (aircrack-ng, reaver, etc.) need monitor-mode access to a physical wireless NIC, which a standard Docker container doesn't have. They're not wired up with forms here for that reason; you'd need to pass through a USB Wi-Fi adapter (--device) and run in a privileged/host-network configuration to use them.
  • Password-reset links and one-time codes are cryptographically random (a 256-bit token for reset links, a 6-digit code for sign-in), stored in memory only as a SHA-256 hash (never in plaintext, never written to disk), compared in constant time, single-use, short-lived (45 minutes for reset links, 10 minutes for codes), and the 6-digit code locks out after 5 wrong attempts. Both the request endpoints (which send an email) and the code-verification endpoint are separately rate-limited per IP on top of that. Requesting one for an account that doesn't exist, or that has no email on file, returns the exact same response as a real request — the login page can't be used to enumerate which accounts exist.

Troubleshooting

  • SSH connection refused / auth failure from web to kali: the kali container generates its own keypair on first boot and shares it with web over the ssh-keys Docker volume - if this is a deployment upgraded from an older version that used a host-side keys/ folder, remove that from your old docker-compose.yml bind mounts and let the new named volume take over; docker compose up -d will recreate both containers with it. Check docker compose logs kali for the actual SSH error if it still fails.
  • A tool's status dot is red: the configured KALI_META metapackage doesn't include that binary. Install it manually (see above) or rebuild with a larger metapackage.
  • Locked out / forgot a password: see the recovery command in User management.
  • "Sign in with X" fails or the IdP shows a redirect_uri error: the registered redirect URI must match <PUBLIC_URL or the URL you open the site at>/api/oidc/<key>/callback exactly, including scheme and port. Set PUBLIC_URL in .env if you're behind a proxy or a non-default port, and check docker compose logs web for the specific OIDC error.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages