Maltrail: indicator-based network traffic detection with a Rust sensor and a Python server
Malicious traffic detection system
At a glance
- What is it?
- Maltrail matches domains, URLs, IP addresses, IP:port pairs and User-Agent values seen on the wire against a trail set built from bundled static files and public feeds. It splits into a Rust/libpcap sensor and a Python reporting server, and the README is explicit that heuristics supplement trail matching rather than replace endpoint telemetry.
- Who is it for?
- Adopt Maltrail if you can place a sensor on a span or mirror port and want trail matching plus a browser interface for triage, with events optionally forwarded to syslog or Logstash. Do not adopt it as an endpoint agent, as an intrusion prevention system, or where you cannot capture traffic: the README states it is not a replacement for endpoint telemetry or a general-purpose intrusion prevention system.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What Maltrail matches, and who ends up running it
Maltrail is a network traffic detection system. It looks for communication with known malicious infrastructure and reports selected traffic anomalies. The matching targets are domains, URLs, IP addresses, IP:port pairs and User-Agent values observed on the network, compared against a set of indicators the project calls trails.
A detection is one event carrying the source, destination, protocol, matched trail, classification and trail source. The README gives this example line:
"2026-08-07 09:14:22.117034" gw 10.13.13.2 57809 1.1.1.1 53 UDP DNS malware.bakewithdavid.com "asyncrat (malware)" (static)Read that line closely and the design intent is clear. The event is not a verdict about a host; it is a record that a name resolved through DNS, plus which trail matched and where that trail came from. The parenthetical (static) is provenance, and provenance matters because a full trail build combines more than 3,000 bundled static files, 42 public-feed integrations, and optional operator-supplied trails. Three sources with three different reliability profiles end up in the same event stream, and the operator is expected to weigh them.
The audience follows from that. This is for people who already have a network capture point: a span port, a mirror, a gateway. It is not for someone who wants an agent on a laptop. The README states plainly that Maltrail is designed for indicator-based network monitoring and that its heuristic detections supplement trail matching, but it is not a replacement for endpoint telemetry or a general-purpose intrusion prevention system. That sentence should decide a lot of adoption questions on its own.
Sensor and server: two processes that do not have to share a host
The architecture is two independently deployable processes. The sensor detects suspicious traffic. The server stores and exposes events for reporting and API access. They can run on the same host or on separate hosts.
The sensor is multithreaded Rust using libpcap, with optional Linux PACKET_FANOUT capture workers. It performs trail matching and heuristic analysis and produces events. From there it has four output paths, and they are not mutually exclusive. It can write events locally to LOG_DIR. It can send them to a remote Maltrail server through LOG_SERVER. It can do both. Separately it can emit CEF over syslog with SYSLOG_SERVER and JSON to Logstash with LOGSTASH_SERVER.
The server accepts remote events, stores them in the event logs, reads locally available daily logs, and serves the HTTP API and the browser interface. The important consequence is stated in the README: the sensor does not depend on the reporting interface and can be deployed independently when events are forwarded to another system. If you already run a SIEM, the sensor alone is a viable component and the Python server is optional.
The reporting interface is served by server.py at HTTP_ADDRESS:HTTP_PORT. It is plain JavaScript with a single third-party runtime dependency (PapaParse, for CSV parsing) and no build step. One day is viewed at a time, chosen through a date picker that doubles as an event-density grid over the available daily logs. Events stream from /events and are aggregated in the browser into threats, defined as one row per distinct (source, trail) pair. Aggregation happens client-side, which keeps the server thin but means the browser is doing the grouping work.
Installing Maltrail and getting a first event out of it
The repository ships install.sh and install.ps1 at the top level, alongside a docker/ directory, packaging/ and a maltrail.conf at the root. The README's installation section lists four routes: installer, building from source, systemd, and Docker. The README does not document rollback for any of them, so plan how you would remove the service before you start.
The configuration file is maltrail.conf. Keys referenced in the README include LOG_DIR, LOG_SERVER, SYSLOG_SERVER, LOGSTASH_SERVER, HTTP_ADDRESS, HTTP_PORT, HOME_LAT and HOME_LON. The README does not print a full maltrail.conf, so read the shipped file rather than copying keys from an article.
Before pointing the sensor at production traffic, validate the deployment. The README names a validation switch:
maltrail-sensor -TThat is the check to run first, because a sensor that cannot see packets produces an empty event stream that looks identical to a quiet network.
For a container deployment, the repository carries a docker/ directory and a .dockerignore. The README lists Docker as one of the four installation routes but does not, in the text available here, publish an image name or a compose file, so treat the directory contents as the source of truth.
Once events are flowing, the first real use is the reporting interface at HTTP_ADDRESS:HTTP_PORT. The README documents live mode: appended events arrive over Server-Sent Events on /live and are merged into the current view. When SSE is unavailable, or the stream cannot serve the session, the interface falls back to polling byte ranges of the daily log. That fallback is worth knowing about, because a proxy that buffers or blocks SSE will silently change how the interface behaves rather than break it outright.
The second thing to try is retro hunting. /hunt searches all retained daily logs for one indicator rather than only the selected day. Searches are bounded by configurable limits, and an optional per-day sidecar index accelerates the sweep and makes /counts exact. If you are chasing a single domain across weeks of logs, that index is the difference between a usable query and a long wait.
Search selectors, triage state, and the limits of the interface
The search box is field-aware. The README lists src:, dst:, port:, proto:, type:, trail:, info:, family:, tag:, uid:, sev:, dir: and status: selectors, plus exclusions, wildcards, CIDR matching and numeric comparisons. Terms separated by spaces combine as AND. Active filters appear as removable chips. The README text available here ends mid-sentence on the AND rule, so the OR behaviour is not documented.
Triage is per threat, not per event: status values are new, investigating, resolved and false positive, with free-text notes, tags and hiding. Whitelist rules and OSINT pivots are reachable from the row context menu. Because a threat is one (source, trail) pair, resolving a threat resolves every event behind it. That is the right granularity for a busy analyst and the wrong one if you need per-event disposition.
Three interface details are worth flagging. /sensors shows which sensors are reporting, the version each runs and how long ago it last checked in. That is the operational view that tells you whether silence means clean or blind. /geo shows per-country event density for the selected day using the external endpoint of each event, and unattributable events remain unmapped rather than being guessed. HOME_LAT and HOME_LON can be set to draw origin arcs. Export produces the current filtered view as CSV, JSON, or defanged indicators.
There is a real constraint in the day-at-a-time model. The date picker is an event-density grid over available daily logs, and one day is viewed at a time. Cross-day questions go through /hunt, which searches one indicator rather than a compound query. If your workflow is "show me every host that talked to this family over the last month", the interface is not built for that shape of question.
Where Maltrail is the wrong tool
The clearest limitation is the one the project states itself. Maltrail is not a replacement for endpoint telemetry or a general-purpose intrusion prevention system. It observes network traffic and matches indicators. It does not inspect process trees, it does not stop packets, and it cannot tell you which binary on a host made a request. If your investigation needs that, the network layer is the wrong layer.
Detection quality is bounded by the trail set. A full build combines more than 3,000 bundled static files, 42 public-feed integrations and optional operator-supplied trails. Public feeds carry false positives and stale entries, and the README gives no accuracy figures for any feed. The event line's provenance field is the only tool you get for weighing a match, which means whitelisting is not optional maintenance; it is the mechanism by which the system stays usable. The README describes custom trails and whitelists as plain text that can be reviewed and version-controlled, which is a genuine advantage over opaque blacklist formats, but it also means someone has to do the reviewing.
Encrypted traffic is a structural boundary. Domain, URL, IP:port and User-Agent matching depends on what is visible. The heuristics cover scanning, DNS exhaustion, DGA-like lookups, suspicious downloads, proxy probes and suspicious User-Agent values. Those are behavioural signals, and the README frames them as supplements to trail matching, not as a detection engine in their own right.
Finally, retention. The README has an event retention section under Operations, and the interface reads daily logs. Long-horizon hunting is bounded by how many daily logs you keep, and the /hunt search limits are configurable, which is another way of saying unbounded sweeps are not the intended mode.
How it differs from a full IDS and from a feed-only blocklist
The natural comparison is a signature-based network IDS such as Suricata. The difference in approach is what gets matched. Suricata rules describe protocol behaviour and payload patterns; Maltrail matches indicators (domains, URLs, IPs, IP:port pairs, User-Agents) and adds heuristics on top. A Suricata rule set requires rule-writing skill and tuning against your own traffic. Maltrail's trail set is largely assembled from external feeds, so the effort shifts from authoring detections to curating and whitelisting them. Neither approach subsumes the other, and running both means two event streams to correlate.
The second comparison is a plain blocklist or DNS sinkhole. A blocklist answers one question: is this indicator listed. Maltrail answers a different one: which internal host talked to this indicator, when, over which protocol, and with what classification and provenance. That context is the reason to run a sensor instead of querying a list. The cost is deployment: you need a capture point, a LOG_DIR or a LOG_SERVER target, and enough storage for daily logs.
A third option worth naming is forwarding the sensor's output and skipping the Python server entirely. The README supports CEF over syslog and Logstash JSON, and the sensor can run independently when events are forwarded elsewhere. If your team already lives in a SIEM, the reporting interface may be redundant, and the sensor becomes a detector feeding an existing pipeline.
Maintenance, release cadence, and what the MIT licence leaves to you
The repository is not archived, and the last push was on 2026-09-21. Releases 3.4, 3.3 and 3.2 landed on 2026-09-04, 2026-08-31 and 2026-08-27 respectively, which is a dense cadence over a short window rather than a steady drumbeat, and the README gives no compatibility policy between them. Pin a version and read CHANGELOG before upgrading.
The upgrade surface is split, which is the practical cost. The sensor is Rust and needs Rust 1.74 or newer according to the badge in the README. The server is Python and needs Python 3.6 or newer. A deployment that builds from source has two toolchains to keep current, and a sensor built against one libpcap may behave differently from one built against another. The Docker route in the docker/ directory is the alternative when you would rather not own that.
The trail set is the ongoing operational cost, and it is not a one-time install. Feeds change, entries go stale, and operator-supplied trails and whitelists are plain text you review and version-control. Budget for that work, because it is what keeps the event stream readable.
On licensing, Maltrail is MIT. That is permissive: it allows commercial and closed-source use, modification and redistribution, with the licence and copyright notice retained. It gives no patent grant language beyond what MIT itself contains and no warranty. This is not legal advice; check the LICENSE file and your own obligations, particularly if you redistribute a modified sensor or bundle the trail data into another product.
Editorial conclusion
Adopt Maltrail if you can place a sensor on a span or mirror port and want trail matching plus a browser interface for triage, with events optionally forwarded to syslog or Logstash. Do not adopt it as an endpoint agent, as an intrusion prevention system, or where you cannot capture traffic: the README states it is not a replacement for endpoint telemetry or a general-purpose intrusion prevention system. Before deploying, confirm the trail build you intend to run, the LOG_DIR the sensor writes to, and whether your capture point sees the traffic you care about; then run maltrail-sensor -T to validate the deployment.
Frequently asked questions
What are the default login credentials for Maltrail?
The README does not document default login credentials for the reporting interface. It describes the interface being served by server.py at HTTP_ADDRESS:HTTP_PORT, but it does not state an authentication scheme or any default account.
How do I install Maltrail?
The README lists four installation routes: the installer, building from source, systemd, and Docker. The repository ships install.sh and install.ps1 at the top level, plus a docker/ directory and a maltrail.conf configuration file.
How do I check that the Maltrail sensor is working before relying on it?
The README names a deployment validation switch, maltrail-sensor -T. The /sensors view in the reporting interface also shows which sensors are reporting, the version each runs and how long ago it last checked in.
What trail sources does Maltrail use for its indicators?
A full trail build combines more than 3,000 bundled static files, 42 public-feed integrations, and optional operator-supplied trails. Each event records the trail source, shown as (static) in the README's example event line.
Can Maltrail replace an intrusion prevention system or endpoint agent?
No. The README states that Maltrail is designed for indicator-based network monitoring, that its heuristic detections supplement trail matching, and that it is not a replacement for endpoint telemetry or a general-purpose intrusion prevention system.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/stamparm-maltrail)