Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ follow [Semantic Versioning](https://semver.org/) from the first tagged release.
- CISA secure-by-design mapping in `docs/assurance.md`, including why monitor mode is the default.

### Changed
- README: a plain-language "At a glance" section (what it is, who it is for, how you use it) and common questions. Roadmap: measured results next to the targets, a corrected coverage floor, and an "After 0.1" list.
- README: v0.1.0 status, install section (container and release archive), a "What it does not do" section, governance and citation links. `docs/releasing.md` describes the signed-tag, pull-request release flow.
- The startup message that says monitor mode does not block is now a warning, so it shows at `-log-level warn`.
- `SECURITY.md` supported-versions table; `docs/releasing.md` states that breaking changes may land in a minor release below 1.0.
Expand Down
73 changes: 73 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,79 @@ reach the host. It is a **compensating control**, not a fix: patch, remove
> the public surface may still change in a minor release, always noted in the
> [changelog](CHANGELOG.md). Start in monitor mode before relying on it.

## At a glance

Say you run an old web application, such as a plant's reporting dashboard, that
uses a vulnerable Log4j. The vendor cannot patch it until next year, or the server
is too old to upgrade. An attacker who sends a request containing something like
`${jndi:ldap://...}` can make that application reach out and run what comes back.

LogShield is a gatekeeper you put **in front of** that application:

```mermaid
flowchart LR
A[Clients and attackers] --> L[LogShield<br/>inspects every request]
L -- clean --> V[Your vulnerable application]
L -- attack --> X[403, logged and counted<br/>never reaches the application]
```

Every request passes through LogShield first. It works out what Log4j would make
of each part of the request, including the disguised forms, and refuses the ones
that would trigger a lookup. Everything else goes through unchanged. You change
the network path, not the vendor's software.

**Who it is for:** teams that run Java applications they cannot patch yet,
especially in operational technology (OT/ICS) and other legacy environments, and
security engineers who need a documented compensating control for them.

**How you use it:**

1. [Install it](#install) (one container or one static binary) and give it the
address of the application it protects.
2. Make the application reachable **only** through LogShield: bind it to
localhost, or firewall it so only LogShield can reach it. This is the step that
makes the protection real.
3. Run in **monitor mode** for a few days. It records what it would block and
blocks nothing, so you can check it against your real traffic.
4. Switch to **block mode**. Attack requests now get a `403` and never reach the
application.
5. Watch the [metrics and logs](docs/observability.md), and keep a plan to patch
or retire the application, because this is a stopgap.

A longer walk-through, with deployment patterns for systemd, Kubernetes and nginx,
is in [docs/deployment.md](docs/deployment.md).

## Common questions

**Does it replace patching?** No. It is a compensating control for one attack
class. Patch Log4j (2.17.1 or later), remove the `JndiLookup` class, or retire the
host as soon as you can.

**Does it slow my application down?** In the load test it added about 0.7 ms at
the 99th percentile for a single client on a small machine
([load report](docs/load-report.md)). Measure it on your own traffic.

**What if LogShield goes down?** It sits in the request path, so a single instance
is a single point of failure. Run it under a supervisor (systemd or Kubernetes
restart policies), use the health endpoints, and run more than one instance where
availability matters. What it does when inspection itself fails is a setting you
choose: `on_error: closed` (refuse) or `open` (forward, counted in a metric).

**Does it work with HTTPS?** Yes. It can terminate TLS itself (HTTP/2 included) or
sit behind a TLS terminator such as nginx. Traffic it cannot decrypt cannot be
inspected.

**Which Log4j versions was it tested against?** The lab validation used Log4j
2.14.1 on OpenJDK 8. Other versions and JDKs are not covered by that result; see
the [limits](docs/lab.md).

**Is it a general web application firewall?** No. It looks for Log4Shell-class
payloads only, and it does not replace a WAF.

**I found a bypass.** Please report it privately, as described in
[SECURITY.md](SECURITY.md). Confirmed bypasses become regression tests and are
credited.

## What it does not do

Read this before deploying. LogShield is a compensating control for one attack
Expand Down
30 changes: 22 additions & 8 deletions docs/ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ measurable.

## Targets

These are goals to be confirmed by measurement, not claims.
These started as goals and were then confirmed by measurement (details in the
[evaluation report](eval/report.md), [lab report](lab-report.md) and
[load report](load-report.md)).

| Metric | Target |
|---|---|
| Missed payloads in in-scope bypass classes | 0 |
| False-positive rate on the benign corpus | < 0.1% |
| p99 added latency | < 5 ms |
| Engine line coverage floor | >= 90% |
| Metric | Target | Result at v0.1.0 |
|---|---|---|
| Missed payloads in in-scope bypass classes | 0 | 0 of 165 corpus entries; 107 of 107 real Log4j JNDI attempts flagged in the lab |
| False-positive rate on the benign corpus | < 0.1% | 0 of 100,111 (95% upper bound below 0.0038%) |
| p99 added latency | < 5 ms | 0.7 ms, one client |
| Coverage floors (total / engine) | 97% / 98% | 97.5% / 99.5% |

## Step 1: inspection engine

Expand Down Expand Up @@ -43,7 +45,19 @@ These are goals to be confirmed by measurement, not claims.

## Step 5

- [ ] Short technical write-up of methodology and results
- [ ] Technical write-up of methodology and results. Drafted as a four-part series (what and why, how it works, how it was tested, how to use it); the links go here once published.

## After 0.1

Candidates, roughly in order of value. None is promised.

- **Outside review.** Invite someone who knows Log4j or OT security to try to bypass it; every confirmed bypass becomes a corpus entry.
- **Wider validation.** Run the lab against more Log4j versions (such as 2.15 and 2.16) and more JDKs, and extend the differential to other logging paths.
- **Measurements still missing.** Memory under concurrent load and behaviour on production-shaped traffic ([performance](performance.md)).
- **Control self-test canary** (Step 4).
- **Container health check.** The distroless image has no shell, so a plain `docker run` has no built-in `HEALTHCHECK`; a small `-healthcheck` mode would close that gap.
- **SLSA Build Level 3**, by moving the release build into an isolated reusable workflow ([assurance](assurance.md)).
- **A second maintainer**, which also raises the Code-Review and OpenSSF scores.

## Working agreement

Expand Down
Loading