pjfanning commented on code in PR #3478:
URL: https://github.com/apache/pekko/pull/3478#discussion_r3876337029
##########
THREAT_MODEL.md:
##########
@@ -0,0 +1,420 @@
+# Apache Pekko — Threat Model
+
+**Status:** Reviewed by the Apache Pekko PMC, 2026-08-27. The thirteen
questions this document was drafted around are answered in §14 and carry
*(maintainer)* provenance; the residual module in/out split noted in §2 and Q6
is the one open item.
+
+| | |
+| --- | --- |
+| **Project** | Apache Pekko (core toolkit) |
+| **Written against** | commit `90b02d60`, `main` |
+| **Date** | 2026-08-27 |
+| **Authors** | ASF Security team, at the request of the Pekko PMC |
+| **Version binding** | This model is versioned alongside the project. A
report against Pekko version *N* is triaged against the model as it stood at
*N*, not at `main`. |
+| **Reporting** | Findings that violate a §8 property should be reported per
[`SECURITY.md`](SECURITY.md). Findings that fall under §3 or §9 will be closed
citing this document. |
+
+**Provenance legend.** Every non-trivial claim is tagged:
+*(documented)* — stated in Pekko's own docs, cited.
+*(maintainer)* — stated by a Pekko maintainer in review of this document.
+*(inferred)* — reasoned from code or config defaults, **not yet confirmed**;
each has a matching question in §14.
+
+**Provenance mix:** 43 documented / 48 maintainer / 3 inferred. The documented
share is unusually high because Pekko's own remoting and serialization docs
already state much of the trust model explicitly. The §5a default rulings —
previously the largest inferred block — are now answered by the §5b posture
statement and §14 Q1 to Q5. What remains inferred is the residual module in/out
split (§14 Q6).
+
+## §1 Overview
+
+Apache Pekko is a Scala/Java toolkit for building concurrent, distributed and
resilient message-driven applications. Its unit of computation is the actor: an
object with private state that communicates only by asynchronous message
passing. Pekko extends that model across machines — an `ActorSystem` can
address actors on remote nodes as if they were local, and a cluster of such
systems provides membership, sharding, singletons and replicated data. That
transparency is the source of most of this document: **the network is not
incidental to Pekko, it is part of the programming model**, and the security
consequences follow from where the toolkit assumes that network sits.
+
+---
+
+## §2 Scope and intended use
+
+Pekko is an **in-process library** that the application embeds and configures.
It is not a server, not a daemon, and ships no runnable artifact that an
operator deploys on its own. There is consequently no "Pekko instance" to
secure independently of the application hosting it.
+
+Three caller roles matter, and they are not equally trusted:
+
+- **The embedding application** — fully trusted. It constructs the
`ActorSystem`, supplies configuration, and already shares the JVM with Pekko.
+- **The operator/deployer** — trusted for the instance. Chooses transport, TLS
material, and the network the node sits on. Most of §10 lands here.
+- **The remote peer** — another `ActorSystem` that has associated over
remoting. Its trust level is the central question of this model, and §7 answers
it.
+
+### Component families
+
+| Family | Modules | Entry point | Leaves the process? | In model |
+| --- | --- | --- | --- | --- |
+| Actor core | `actor`, `actor-typed`, `slf4j`, `coordination` |
`ActorSystem`, `ActorRef` | no | **yes** |
+| Classic IO | `actor` (`org.apache.pekko.io`) | `IO(Tcp)`, `IO(Udp)`,
`IO(Dns)` | **network — if the application binds** | **yes** |
+| Remoting | `remote` | Artery transport (`tcp` / `tls-tcp` / `aeron-udp`);
classic remoting (netty), deprecated but supported *(maintainer — §14 Q13)* |
**network** | **yes — primary surface** |
+| Cluster | `cluster`, `cluster-typed`, `cluster-tools`, `cluster-sharding*`,
`cluster-metrics` | gossip, membership, sharding | **network** (via remoting) |
**yes** |
+| Replicated data | `distributed-data` | CRDT replication | **network** (via
remoting) | **yes** |
+| Serialization | `serialization-jackson`, `serialization-jackson3` |
`Serializer` SPI | deserializes network bytes | **yes — critical** |
+| Wire encoding | `protobuf-v3` | shaded protobuf runtime | parses remoting
and cluster wire bytes | **yes** |
+| Streams | `stream`, `stream-typed` | `Source`/`Flow`/`Sink`; `Tcp`, `TLS`,
`FileIO`, `Framing` | **network / filesystem — if the application uses those
connectors** | **yes** |
+| Persistence | `persistence`, `persistence-typed`, `persistence-query`,
`persistence-shared` | journal / snapshot plugin SPI | **storage backend** |
**yes**, boundary at the SPI |
+| PKI | `pki` | PEM/keystore parsing | **reads files** | **yes** |
+| Discovery | `discovery` | service-discovery SPI | **network / DNS** |
**yes** |
+| OSGi | `osgi` | bundle activator | classloading | **yes** *(maintainer — §14
Q6)* |
+| Test kits | `*-testkit`, `multi-node-testkit`, `*-tests`, `persistence-tck`,
`stream-tests-tck` | — | — | **no** — §3 |
+| Benchmarks | `bench-jmh` | — | — | **no** — §3 |
+| Build / docs | `docs`, `project`, `scripts`, `legal`, `kubernetes`
(test-cluster provisioning scripts), `plugins`, `bill-of-materials`,
`scala-nightly` | — | — | **no** — §3 |
+
+*(inferred — the in/out split is the ASF Security team's proposal, except
`osgi`, which is confirmed in model; see §14 Q6)*
+
+---
+
+## §3 Out of scope (explicit non-goals)
+
+- **Test kits, benchmarks, build tooling and documentation sources.** These
ship in the repository but are not part of the security-supported surface. A
finding in `bench-jmh` or any `*-tests` module is `OUT-OF-MODEL:
unsupported-component`. *(inferred — §14 Q6)*
+- **Pekko is not a sandbox.** Actors are not an isolation boundary. Any code
running in the JVM can reach any actor's state by ordinary means; the actor
model is a concurrency discipline, not a security control. *(maintainer — §14
Q4)*
+- **Pekko is not an authorization framework.** It carries no notion of a
principal, role, or permission on a message. Application-level authorization is
the embedding application's job. *(maintainer — §14 Q11)*
+- **A Pekko cluster is not a multi-tenancy boundary.** The documentation is
explicit that *"you'll have to trust all cluster nodes the same in a Pekko
cluster anyway"* *(documented — `remote-security.md`)*. Separating
mutually-distrusting tenants across nodes of one cluster is not a supported
deployment.
+- **Attackers who already control the embedding process** are out of scope —
they have already won. *(maintainer — §14 Q11)*
+- **This document describes the `apache/pekko` core toolkit.** Its technical
content — the §2 component families, the §5a defaults, the §8 properties — is
about that codebase. Other Apache Pekko deliverables (Pekko Management, Pekko
HTTP, Pekko gRPC, Pekko Connectors, Pekko Projection, the persistence plugins)
ship from their own repositories, and this model does not enumerate their
surfaces.
+
+ That is a limit on **coverage, not on standing**. Reporting and triage are
identical across all Pekko repositories: the same address (see
[`SECURITY.md`](SECURITY.md)), the same PMC, and the same posture — an
implementation defect is Pekko's to fix, while a request to change a default is
a change request under §5b, not a vulnerability. A finding in another Pekko
repository is **not** `OUT-OF-MODEL: unsupported-component`; that disposition
is for the §3 components above, not for code that simply lives elsewhere.
*(maintainer — §14 Q6)*
+
+---
+
+## §4 Trust boundaries and data flow
+
+**The primary trust boundary is the remoting network boundary**, and Pekko's
documentation places it unusually explicitly:
+
+> "An `ActorSystem` should not be exposed via Pekko Cluster or Pekko Remote
over plain Aeron/UDP or TCP to an untrusted network, such as the Internet. It
should be protected by network security, such as a firewall. If that is not
considered enough protection, TLS with mutual authentication should be enabled."
+> — *(documented — `remote-security.md`)*
+
+and states the consequence of crossing it:
+
+> "As soon as an actor system can connect to another remotely, it may in
principle send any possible message to any actor contained within that remote
system."
+> — *(documented — `remote-security.md`, Untrusted Mode)*
+
+Read together, these define the model: **remoting assumes it runs on a network
the operator has already restricted.** Association is the security decision;
once a peer is associated, it is inside. Pekko offers two mechanisms to tighten
that (TLS mutual authentication, untrusted mode) but neither converts remoting
into a boundary that is safe to face the open internet *(documented — see §9)*.
+
+The application's own boundaries — the HTTP endpoint, the message broker, the
database — sit **outside** Pekko and are the embedding application's
responsibility.
+
+### Reachability preconditions per family
+
+A finding must meet its family's precondition to be in-model:
+
+- **Remoting** — reachable from bytes arriving on the Artery transport, *and*
the report must state whether it assumes the network-isolation assumption above
is intact. A finding that requires an attacker already on the cluster network
is judged under §7, not automatically valid.
+- **Serialization** — reachable from a message payload deserialized by a
**configured, enabled** serializer. Findings reachable only when
`allow-java-serialization = on` are judged under §5a.
+- **Cluster / distributed-data** — reachable from gossip or replication
traffic originating at an **associated peer**. Per §7 such a peer is trusted,
so these are typically out of model unless the finding shows a pre-association
reach.
+- **Actor core / streams** — reachable from data the embedding application
passes in. Trusted by default; a finding must show the data crosses an
application boundary that Pekko itself defines.
+- **Persistence** — reachable from journal or snapshot contents. The store is
trusted *(maintainer — §14 Q5)*, so a finding requiring write access to it is
out of model; a defect in Pekko's replay handling is not.
+- **PKI** — reachable from PEM/keystore material. Operator-supplied and
trusted per §6.
+
+---
+
+## §5 Assumptions about the environment
+
+- **Runtime.** A conformant JVM. Pekko does not defend against a hostile JVM,
a hostile classpath, or an attacker with local code execution in the same
process. *(maintainer — §14 Q11)*
+- **Network adjacency.** *"Best practice is that Pekko remoting nodes should
only be accessible from the adjacent network."* *(documented —
`remote-security.md`)*
+- **PKI scope.** Where TLS is used, every certificate issued by the same
internal PKI tree is equivalent: *"there is still a risk that an attacker can
gain access to a valid certificate by compromising any node with certificates
issued by the same internal PKI tree."* *(documented — `remote-security.md`)*
+- **Clock.** Failure detection and gossip do **not** depend on other nodes'
clocks: the failure detector reads only the local clock, and gossip ordering
uses logical `VectorClock` counters. The exception is `LWWRegister`/`LWWMap`,
which merge by wall-clock timestamp and therefore assume reasonably
synchronised clocks across writers *(documented — `typed/distributed-data.md`,
and the `LWWRegister` scaladoc)*. Pekko does not defend against adversarial
clock manipulation on a cluster node *(maintainer — §14 Q12; see Q9)*.
+- **Entropy.** On Linux with SHA1PRNG, the docs recommend
`-Djava.security.egd=file:/dev/urandom` to avoid blocking, noting it *"is NOT
as secure because it reuses the seed."* *(documented — `remote-security.md`)*
+
+### What Pekko does not do to its host
+
+These are negative claims, rarely written down anywhere. Each is confirmed and
carries the citation, or the exception, behind it *(maintainer — §14 Q7)*:
+
+- Installs no signal handlers and spawns no child processes — no
`sun.misc.Signal`/`SignalHandler`, `Runtime.exec` or `ProcessBuilder` in the
main sources. It does register **one JVM shutdown hook**, via
`CoordinatedShutdown` (`actor/.../actor/CoordinatedShutdown.scala:381`).
+- Opens no listening socket of its own accord. Remoting binds when configured;
`org.apache.pekko.io.Tcp`/`Udp` (in `pekko-actor`) and `stream.scaladsl.Tcp`
(in `pekko-stream`) bind only on an explicit application call. Pekko never
binds a port the application did not ask for.
+- **Reads environment variables during configuration startup, but never
modifies them** *(maintainer — §14 Q7)*. Pekko itself calls neither
`System.getenv` nor `sys.env` anywhere in the main sources; environment values
reach it only through HOCON `${?VAR}` substitution when `ConfigFactory.load`
resolves the configuration (`actor/.../actor/ActorSystem.scala:281`). That path
is deliberate and documented — it is how the docs tell operators to supply
passwords (§10.8).
+- Writes to logging via the configured logger. One exception:
`StandardOutLogger` prints to stdout with `println`
(`actor/.../event/Logging.scala:1024` onward). It carries the very early
startup log, before the configured loggers are running, and is bounded by
`pekko.stdout-loglevel`, which defaults to `WARNING`.
+- Does not mutate process-global state at initialization — no
`System.setProperty`, `Locale.setDefault` or `TimeZone.setDefault` in the main
sources.
+
+---
+
+## §5a Configuration variants that change the security envelope
+
+Pekko's security posture is set almost entirely by configuration. **Every row
below whose default is the weaker value needs a maintainer ruling** — see §14.
+
+| Setting | Default | Effect | Maintainer stance |
+| --- | --- | --- | --- |
+| `pekko.actor.allow-java-serialization` | `off` | On, exposes the JVM
deserialization attack surface to any message payload. Docs: *"highly
discouraged to enable in production"* *(documented — `serialization.md`)* |
Secure default. Enabling it transfers gadget-chain defence to the operator —
§5b, §14 Q2 *(maintainer)* |
+| `pekko.remote.artery.transport` | `tcp` | **Plaintext.** No peer
authentication and no confidentiality on the wire. `tls-tcp` opts into TLS |
Compatibility default — §5b, §14 Q1 *(maintainer)* |
+| `pekko.remote.artery.ssl.config-ssl-engine.require-mutual-authentication` |
`on` | Both ends present certificates *(documented)* | Secure default |
+| `…ssl.config-ssl-engine.hostname-verification` | `off` | Off, a valid cert
from the trusted PKI authenticates regardless of which host presents it. Docs
*recommend* `on` but ship `off` *(documented)* | Compatibility default, warned
at runtime — §5b, §14 Q3 *(maintainer)* |
+| `pekko.remote.artery.untrusted-mode` | `off` | On, blocks inbound system
messages, `PossiblyHarmful` messages, remote deployment, remote DeathWatch, and
actor selections outside `trusted-selection-paths` *(documented)* | Hardening;
adoption is the operator's call — §5b, §14 Q4 *(maintainer)* |
+| `pekko.remote.artery.trusted-selection-paths` | `[]` | Allow-list of actor
paths that may receive selections under untrusted mode *(documented)* | Follows
Q4 |
+| `pekko.remote.deployment.enable-allow-list` | `off` | On, restricts which
actor classes a peer may remote-deploy *(documented — `remoting.md`)* |
Hardening; adoption is the operator's call — §5b, §14 Q4 *(maintainer)* |
+| `pekko.remote.classic.untrusted-mode` | `off` | Classic-remoting equivalent
of the artery flag *(reference.conf:381)* | Follows Q4 *(maintainer — §14 Q13)*
|
+| `pekko.remote.classic.trusted-selection-paths` | `[]` | As above
*(reference.conf:387)* | Follows Q4 |
+| `pekko.remote.classic.netty.tcp.enable-ssl` | `false` | Classic's default
transport is plaintext netty TCP *(reference.conf:582)* | Follows Q1
*(maintainer — §14 Q13)* |
+| `pekko.actor.serialize-messages` / `serialize-creators` | `off` | Testing
aids that force serialization round-trips. Not security controls. Docs: *"this
is only intended for testing"* *(documented —
`actor/src/main/resources/reference.conf`)* | Not a security knob |
+
+---
+
+## §5b Security posture: hardening, not secure-by-default
+
+Pekko is a long-lived toolkit whose deployment base is inherited from Akka.
Its configuration defaults are chosen for compatibility with those deployments,
in which operators have already been tasked with securing the network Pekko
runs on and controlling who may reach and message a deployed system. Changing a
default to a more restrictive value breaks those deployments on upgrade,
sometimes without a clear signal as to why.
+
+Pekko therefore takes the following position *(maintainer)*:
+
+1. **Defaults are compatibility choices, not security claims.** §5a lists
every setting whose default affects the security envelope; §10 lists what the
operator must do as a result. Read together they are the contract: Pekko states
what it does not provide, and states what it expects of the operator instead.
+2. **A report that a default should be more restrictive is not a vulnerability
report.** It is a change request, and is closed as `BY-DESIGN:
default-configuration` (§13).
+3. **Proposals to change a default are welcome, and belong on the development
list.** The PMC will weigh them in good faith on their merits — the
compatibility cost, whether a migration path exists, and whether a major
version is in flight. Defaults can and do change; they change through project
discussion, not as the remediation of a security report.
+4. **If an implementation is wrong, Pekko fixes it.** Where a control does not
do what it is documented to do once enabled, that is a defect, in scope, at the
severity §8 assigns. This posture governs which value ships as the default —
never whether the mechanism works.
+
+---
+
+## §6 Assumptions about inputs
+
+For a toolkit whose surface is a wire protocol, the useful table is keyed by
**message class**, not by function.
+
+| Source | Input | Attacker-controllable? | Who must enforce what |
+| --- | --- | --- | --- |
+| Artery transport | Inbound frame headers / framing | **Yes if the
network-isolation assumption fails** | Operator: network isolation (§10), or
TLS |
+| Artery transport | Serialized user-message payload | **Yes**, from an
associated peer | Pekko: only enabled serializers run. App: validate semantic
content |
+| Artery transport | System messages (`Create`, `Terminate`, `Watch`,
`Supervise`) | **Yes**, from an associated peer unless untrusted mode is on |
Operator: `untrusted-mode` if peers are less than fully trusted |
+| Artery transport | `PossiblyHarmful` messages (`PoisonPill`, `Kill`) |
**Yes**, same condition | Same |
+| Artery transport | Remote-deployment `Props` | **Yes**, same condition |
Operator: `enable-allow-list` |
+| Cluster | Gossip / membership state | From an **associated** peer — trusted
per §7 | — |
+| `distributed-data` | Replicated CRDT deltas | From an associated peer —
trusted per §7 | — |
+| Persistence | Journal / snapshot contents on replay | **No** — the store is
trusted *(maintainer — §14 Q5)* | Operator/DBA: secure the store |
+| `pki` | PEM / keystore files | **No** — operator-supplied, trusted |
Operator: protect key material |
+| `discovery` | Service-discovery responses (DNS, K8s API) | **Potentially** —
depends on the resolver | Operator: trust the discovery mechanism |
+| `io.Tcp` / `io.Udp` | Bytes on an application-bound socket | **Potentially**
— depends where the application exposes it | App: it chose to bind, defines the
protocol, and owns what it exposes |
+| `stream.Tcp` / `stream.TLS` | Bytes on an application-bound stream server |
**Potentially** — as above | App: as above. TLS itself is delegated to the
JDK's JSSE |
+| `Framing` / `JsonFraming` | Delimited or length-prefixed frames |
**Potentially** where fed from a network source | App: supplies
`maximumFrameLength`, which bounds frame size |
+| `stream.FileIO` | File contents at an application-supplied path | Depends on
the path | App |
+| Config | `application.conf`, system properties | **No** — trusted, part of
the deployment | Operator |
+
+**Size and rate.** Artery imposes frame-size limits and the failure detector
bounds how long an unresponsive peer is tolerated. These are tuning parameters,
not security controls *(maintainer — §14 Q8)*.
+
+---
+
+## §7 Adversary model
+
+**In scope:**
+
+- **An unassociated network attacker** who can reach the remoting port — in a
deployment where the operator's isolation assumption has held, this attacker
should not exist; where it does, the relevant question is whether they can
achieve anything **before** association completes. Pre-association reachability
is the sharpest in-model attack surface. *(maintainer — §14 Q1)*
+- **An attacker supplying message content** to an otherwise legitimate peer —
e.g. data that originates at the application's own untrusted edge and is
forwarded into an actor message. *(maintainer — §14 Q11)*
+
+**Explicitly out of scope:**
+
+- **An associated peer behaving arbitrarily.** There is no Byzantine-peer
model. The documentation is direct: *"you'll have to trust all cluster nodes
the same in a Pekko cluster anyway"* *(documented)*, and *"as soon as an actor
system can connect to another remotely, it may in principle send any possible
message to any actor contained within that remote system"* *(documented)*. **A
finding whose precondition is "a cluster member misbehaves" is out of model** —
there is no honest-majority threshold to state, because the model has no notion
of a dishonest member. This generalises to every cluster protocol — cluster
membership, sharding, singleton and `distributed-data` alike: **Pekko makes no
guarantee of being able to recognise a compromised node.** Failure detection is
heartbeat-based (`remote/.../PhiAccrualFailureDetector.scala`), so it
identifies members that stop responding, not members that respond dishonestly;
a compromised node that keeps heartbeating is indistinguishabl
e from a healthy one. *(maintainer — §14 Q9)*
+- **An attacker holding any certificate from the cluster's PKI tree.**
Documented as equivalent to cluster access *(documented)*.
+- **An attacker with code execution in the embedding JVM.** Already inside the
trust boundary.
+- **Side-channel observers.** Pekko makes no timing or memory-access
guarantees. *(maintainer — §14 Q11)*
+
+---
+
+## §8 Security properties Pekko provides
+
+| # | Property & conditions | Violation symptom | Severity | Provenance |
+| --- | --- | --- | --- | --- |
+| P1 | **Java serialization is disabled by default**; Pekko uses it for none
of its own internal messages | A payload deserialized via Java serialization
under default config | **Critical** — RCE class | *(documented —
`serialization.md`)* |
+| P2 | The disabled Java serializer **logs rejected attempts** under the
`SECURITY` marker, and those *"SHOULD be treated as potential attacks which the
serializer prevented"* | Silent acceptance where rejection + log is expected |
High | *(documented — `serialization.md`)* |
+| P3 | **Remote deployment is not remote code loading.** The actor class must
already be present on the target system | A peer causing a class absent from
the target's classpath to execute | **Critical** | *(documented —
`remoting.md`)* |
+| P4 | With `enable-allow-list = on`, only listed actor classes may be
remote-deployed onto this node | An unlisted class deployed | High |
*(documented — `remoting.md`)* |
+| P5 | With `untrusted-mode = on`, inbound system messages, `PossiblyHarmful`
messages, remote deployment, remote DeathWatch and non-allow-listed actor
selections are **dropped and logged** | Any of these taking effect despite the
flag | High | *(documented — `remote-security.md`)* |
+| P6 | With `transport = tls-tcp`, TLS is applied and **mutual authentication
is on by default** — the server side also requests and verifies the client's
certificate | Association completing without peer certificate verification |
**Critical** | *(documented — `remote-security.md`)* |
+| P7 | Certificate rotation is supported for mTLS in Kubernetes without
cluster restart | Rotation causing association failure or silent downgrade |
Medium | *(documented — `remote-security.md`; **2.0+ only**, absent from the
released 1.x docs —
[snapshot](https://pekko.apache.org/docs/pekko/snapshot/remote-security.html#mtls-with-rotated-certificates-in-kubernetes))*
|
+
+**Note the shape of this list:** P4, P5 and P6 are all *conditional on a
non-default setting*. Under stock configuration, the properties Pekko actively
provides at the network boundary are P1, P2 and P3 — the rest of the posture is
delegated to the operator via §10. This is a deliberate design, but it is the
single most important thing for a triager to understand.
+
+---
+
+## §9 Security properties Pekko does **not** provide
+
+- **No peer authentication by default.** With the default `transport = tcp`
there is no shared secret, no certificate, and no handshake credential. Any
host that can reach the port and speak Artery can attempt association.
*(maintainer — §14 Q1; §5b)*
+- **No confidentiality or integrity on the wire by default.** Same cause.
*(maintainer — §14 Q1; §5b)*
+- **No intra-cluster authorization.** Once associated, a peer may address any
actor in the system. There is no per-actor, per-message, or per-peer permission
model. *(documented)*
+- **No Byzantine fault tolerance, and no compromised-node detection.** See §7.
Cluster protocols assume members are honest; there is no threshold below which
arbitrary member behaviour is tolerated *(documented — `remote-security.md`)*,
and Pekko offers no mechanism that would identify a member as compromised in
the first place *(maintainer — §14 Q9)*.
+- **No bound on blast radius from one compromised node.** Documented
explicitly for the PKI case *(documented)*.
+- **No protection once Java serialization is enabled.** Turning it on
re-exposes the full JVM deserialization surface; the docs place this squarely
on the operator. *(documented)*
+
+### False friends
+
+These are the assumptions integrators most often bring with them, and each is
wrong:
+
+- **Untrusted mode is not a security boundary.** *"Untrusted mode does not
give full protection against attacks by itself. It makes it slightly harder to
perform malicious or unintended actions"* *(documented —
`remote-security.md`)*. It is hardening. Treating it as a substitute for
network isolation is a §11 misuse.
+- **`PossiblyHarmful` is a marker, not an authorization mechanism.** It is a
compile-time trait that untrusted mode consults. It confers no protection when
untrusted mode is off.
+- **A service mesh is not a substitute for remoting security.** *"Encryption
and authentication via a service mesh is not a replacement for Pekko Cluster
remoting security"* — Pekko's peer-to-peer addressing has requirements a mesh
does not satisfy *(documented — `remote-security.md`; **2.0+ only**, absent
from the released 1.x docs —
[snapshot](https://pekko.apache.org/docs/pekko/snapshot/general/remoting.html#service-mesh))*.
+- **TLS mutual authentication does not give per-node identity guarantees by
default**, because `hostname-verification` ships `off` — any cert from the
trusted PKI authenticates as any node. *(documented + config default)*
+- **The actor boundary is not a security boundary.** Message-passing isolation
is a concurrency property, not a confidentiality one.
+
+### Well-known attack classes left to the caller
+
+- **JVM deserialization gadget chains** — mitigated by P1 only so long as Java
serialization stays off, and only for payloads Pekko itself deserializes;
application-level serializers are the application's problem. Pekko integrates
**no** serialization filter: `JavaSerializer.fromBinary` performs an unfiltered
`ObjectInputStream.readObject` (`actor/.../serialization/Serializer.scala`). An
operator who enables Java serialization must supply the allow list themselves
through the JVM — `-Djdk.serialFilter` or a process-wide `ObjectInputFilter` —
and owns that entirely *(maintainer — §14 Q2)*.
+- **Resource-exhaustion via message volume or size** — CPU cost is not in
itself a security concern, and volume from an associated peer is out of model.
Only disproportionate cost, where work explodes relative to a single input, is
a defect. See §14 Q8.
+- **DNS / service-discovery spoofing** — `discovery` trusts the resolver it is
configured with.
+- **Storage-layer tampering** on persistence journals and snapshot stores —
the store is trusted, and securing it belongs to whoever administers it
*(maintainer — §14 Q5)*.
+
+---
+
+## §10 Downstream responsibilities
+
+The operator or embedding application must:
+
+1. **Keep remoting off untrusted networks.** Firewall the remoting port to the
adjacent network. This is the assumption the whole model rests on
*(documented)*.
+2. **Enable `tls-tcp` if the network is not sufficiently trusted**, and set
`hostname-verification = on` unless hostnames are genuinely dynamic
*(documented)*.
+3. **Leave `allow-java-serialization = off`.** If it must be enabled for
legacy compatibility, treat the deployment as having no deserialization
protection *(documented)*, and **maintain your own gadget-chain allow list**
via `-Djdk.serialFilter` or a process-wide `ObjectInputFilter`. Pekko supplies
no filter of its own, and findings that require the flag to be on are out of
model *(maintainer — §14 Q2)*.
+4. **Treat `SECURITY`-marked log entries as security signals**, not noise. The
disabled Java serializer marks rejected attempts this way *(documented)*; the
TLS providers use the same marker to report a disabled hostname verification
and the use of default keystore passwords *(`ConfigSSLEngineProvider`)*.
+5. **Scope the PKI tree to the cluster.** Any certificate it issues is cluster
access *(documented)*.
+6. **Consider `untrusted-mode = on` and `enable-allow-list = on`** where peers
are less than fully trusted — understanding both are hardening, not boundaries.
+7. **Never place mutually-distrusting tenants in one cluster** *(documented)*.
+8. **Supply passwords by environment substitution, not literals in config
files** *(documented)*, and **replace the shipped defaults**. Both SSL blocks
ship `key-store-password`, `key-password` and `trust-store-password` as
`"changeme"` (`reference.conf:701`, `:1205`); Pekko warns under the `SECURITY`
marker if they survive into a running system.
+9. **Secure the persistence store.** Pekko trusts journal and snapshot
contents on replay and does not validate them as potentially hostile, so access
control and integrity for the database or store are the administrator's
responsibility *(maintainer — §14 Q5)*.
+10. **Protect the key material itself.** §10.5 scopes what a certificate
grants; this is the file-level counterpart. Keystores, truststores and PEM
material are operator-supplied and trusted by §6 — Pekko validates neither
their provenance nor their permissions.
+11. **Keep message-content logging off in production.**
`log-received-messages` and `log-sent-messages` (`reference.conf:862`, `:866`,
and `:393`, `:397` for classic) and `pekko.actor.debug.receive`
(`actor/.../reference.conf:787`) all write message contents to the log, and
`pekko.log-config-on-start` (`:50`) writes the resolved configuration — which,
per §10.8, is where secrets pulled in by environment substitution end up. All
four default to `off`; turning any on moves payloads or credentials into the
logging system, whose protection is the operator's.
+12. **Understand that transport encryption exists only on `tls-tcp`.** Neither
`tcp` nor `aeron-udp` offers an encryption option *(documented —
`remote-security.md`)*, so on those transports network isolation is not one
control among several, it is the only one.
+13. **Trust, or secure, the discovery mechanism.** `discovery` accepts
whatever its configured resolver returns (§6); Pekko does not authenticate DNS
or service-registry responses.
+
+---
+
+## §11 Known misuse patterns
+
+- **Exposing remoting or cluster ports to the internet**, on the theory that
TLS or untrusted mode makes it safe. Both are explicitly documented as
insufficient alone.
+- **Enabling `allow-java-serialization` for prototyping convenience and
shipping it.** The docs anticipate exactly this path.
+- **Relying on a service mesh** for cluster authentication *(documented as
insufficient)*.
+- **Using cluster membership as a tenancy or privilege boundary.**
+- **Enabling TLS but leaving `hostname-verification = off`** and assuming
per-node identity.
+- **Treating actor encapsulation as a confidentiality guarantee** within a
single JVM.
+
+---
+
+## §11a Known non-findings (recurring false positives)
+
+Feed this section to scanners and AI triage as a suppression list.
+
+- **"Remote deployment permits arbitrary class instantiation."** Remote
deployment is *not* remote code loading — the class must already be on the
target's classpath *(P3, documented)*. Absent a way to introduce a class, this
is not RCE.
+- **"Remoting transport defaults to plaintext."** Correct, and by design under
the §4 network-isolation assumption. In-model only if the finding shows harm
reachable **pre-association**; a request to change the default is `BY-DESIGN:
default-configuration` per §5b.
+- **"`PoisonPill` can be sent remotely to shut down a system."** Documented
behaviour, gated by `untrusted-mode` *(documented — `remote-security.md`)*.
+- **"`hostname-verification` is disabled by default."** Known and documented,
with the trade-off spelled out for dynamic-hostname deployments, and warned at
runtime under `LogMarker.Security`. A request to change the default is
`BY-DESIGN: default-configuration` per §5b.
+- **Findings in `*-tests`, `*-testkit`, `bench-jmh`, `docs`** — `OUT-OF-MODEL:
unsupported-component` per §3.
+- **"SHA1PRNG with `/dev/urandom` reuses the seed."** Documented trade-off,
deliberately recommended to avoid blocking *(documented)*.
Review Comment:
This new doc was generated using AI in the main and it just found stuff in
our own Pekko docs.
https://github.com/search?q=repo%3Aapache%2Fpekko+SHA1PRNG&type=code
No problem reviewing this but this threat model entry basically means we
won't accept someone telling us to change the docs or default configs as a CVE
issue. This line is in the 'Known non-findings' section.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]