-
Notifications
You must be signed in to change notification settings - Fork 0
Security Model
Vulnerability reports go through SECURITY.md — please do not open a public issue for a security problem.
There are two distinct trust boundaries: the wire (bundles connecting to the bus, port 3030) and the HTTP surface (GUI and REST API, port 3000). They authenticate independently.
Hello.authToken: String → HandshakeHandler → TokenValidator.validate(token). Two built-in
implementations:
| Implementation | Behaviour |
|---|---|
acceptAll() |
Default. Any bundle that can reach the port may register |
sharedSecret(token) |
Constant-time comparison against a configured secret |
Configured by:
evento.server.bus.auth-token=${EVENTO_SERVER_BUS_AUTH_TOKEN:}Blank means accept-all. A failed check yields Reject(CODE_AUTH_FAILED), and the bundle's
BundleClient.start() future fails with that reason — the client does not retry into a rejection,
and it does not call System.exit; the caller decides.
Set
EVENTO_SERVER_BUS_AUTH_TOKENfor any deployment that isn't a laptop. An unauthenticated bus port means anyone who can reach it can register handlers for your command and query types — and the broker routes by payload type string.
NettyTransportConfig.sslContext is nullable. When set, the pipeline prepends Netty's SslHandler.
The same config record serves both ends — SslContextBuilder.forClient() versus forServer().
TLS is not on by default. The shared-secret token crosses the wire in the Hello frame, so
without TLS it is exposed to anyone who can observe the network path. Treat TLS as required wherever
the bus traverses an untrusted network.
CBOR decoding is bounded by an explicit type whitelist. MessageTypeRegistry maps byte tags to
specific classes under the sealed Message hierarchy: allowIfSubType(Message.class) plus explicit
per-type registration. There is no "accept everything under this package" mode.
This is the defence against gadget-chain deserialization attacks, and it works because the hierarchy is sealed — the compiler enumerates the permitted set.
What is not a security control:
FAIL_ON_UNKNOWN_PROPERTIES. All four codecs disable it deliberately for version tolerance. Skipping an unrecognised property on an already-whitelisted type instantiates nothing; the type bound is what matters. See Wire Protocol § 8 for the outage that strictness caused.
The server never deserializes business payloads at all. It routes on payloadType: String and
carries payload: byte[] opaquely, so a malicious payload has no server-side class to instantiate —
it can only reach a bundle that already declares a handler for that type.
Authentication is HTTP Basic against a static Spring Boot in-memory user:
spring.security.user.name=${SPRING_SECURITY_USER_NAME:evento}
spring.security.user.password=${SPRING_SECURITY_USER_PASSWORD:secret}
spring.security.user.roles=WEB,ADMINWebConfig requires authentication on /api/** and the actuator endpoints, except health and info.
It answers a plain 401 without a WWW-Authenticate header, so the browser does not raise its
native credential dialog and the GUI's own login page handles the failure.
Per-endpoint enforcement is @Secured("ROLE_WEB") / @Secured("ROLE_ADMIN") on the controllers.
The default password is
secret. Override bothSPRING_SECURITY_USER_NAMEandSPRING_SECURITY_USER_PASSWORDbefore the server is reachable by anyone else.
The former JWT stack — AuthFilter, AuthService, TokenRole, AuthController — was removed along
with the explorative read-only pivot. There is no token endpoint any more.
| Surface | Status |
|---|---|
Deploy-by-upload / /spawn / /kill
|
Removed in 2.0. The server no longer accepts JAR uploads or spawns processes |
| Business class loading server-side | None by design |
| SQL injection in the command lock |
PgDistributedLock is hardened |
| Multipart upload | Still present for artifacts — spring.servlet.multipart.max-file-size=200MB
|
-
EVENTO_SERVER_BUS_AUTH_TOKENset to a real secret - TLS configured if the bus crosses an untrusted network
-
SPRING_SECURITY_USER_PASSWORDchanged fromsecret - Port 3030 reachable only from your bundles, not the internet
- Port 3000 behind whatever fronts your other internal tooling
- Database credentials supplied by environment/secret manager, not baked into an image
- Actuator not publicly exposed (
show-details=when_authorizedis only part of the story)
The repository runs CI, CodeQL and publishes an OpenSSF Scorecard. The Docker image pins its base images by digest, not just tag.
Evento Framework — Copyright 2020–2026 © Gabor Galazzo. Dual-licensed under AGPL-3.0 and a commercial licence.
This wiki documents the implementation; the repository is authoritative where the two disagree. Found something out of date? Open an issue.
Getting oriented
Internals
Operations
- Server Configuration
- Throughput and Capacity
- Observability
- Security Model
- Server REST API
- Troubleshooting
Project