Repository navigation
Stop trusting loopback alone; security dependencies - #1
Merged
Merged
Conversation
setup-go with go-version-file: go.mod installs exactly the go directive (1.26.0), so releases were built without the stdlib security fixes shipped since. Ask for the latest 1.26 patch instead.
Common v0.4.26 requires go 1.26.8, so the go directive moves from 1.26.0 to 1.26.8. It replaces mholt/archiver/v3 with mholt/archives, which drops archiver/v3, nwaples/rardecode v1 and xi2/xz from the build (archiver and rardecode have unfixed advisories) and brings ulikunitz/xz to v0.5.15. It also brings golang-jwt/jwt/v4 v4.5.2 and gorilla/websocket v1.5.3. Nothing here used the JWT middleware or AddFile that Common removed.
v0.115.0 is affected by several openapi3filter advisories, two of them reachable from the request validator: a nil-pointer panic on a content parameter without a schema (GO-2026-6112, fixed in v0.144.0) and unbounded decompression of request bodies (GHSA-wq9g-9vfc-cfq9, fixed in v0.131.0). The deepmap/oapi-codegen v1.12.4 echo middleware and the generated code still build against it, so codegen/ is not regenerated.
…ternal secret The JWT skipper let a request through when its real IP was loopback or its Host header was "unix". Loopback is any local process or container, or a same-host reverse proxy that does not set X-Forwarded-For. The Host header is set by the client, so any TCP client, including one on the LAN going through the gateway, could send Host: unix and reach the whole bus API without a token. A request now skips the JWT only when it comes from loopback with the per-boot internal secret (external.IsInternalRequest, as the other services do since 2026-09-13), or when it really arrived on the unix socket listener, which is read from the connection's local address, not from a header. App-management publishes there without the secret. The socket was only root-reachable because of the systemd umask (0755, and connect needs write permission); it is now chmod 0600 explicitly. The websocket GET skip stays: browsers cannot set headers on a WebSocket and the dashboard subscribes without a token. The access-log filter keeps its loopback test, it only decides logging. The new test drives the whole router: loopback without or with a wrong secret, a LAN address with the secret, a forwarded LAN request and Host: unix over TCP all get 401; loopback (v4 and v6) with the secret, the unix socket and a websocket GET get through.
The JWT skipper let through every GET that carried "Upgrade: websocket", on any path. A client without a token could read /event_type, /action_type, /ysk and every other GET route just by adding that header.
The exemption now also needs the matched route to be one of the four subscribe routes: GET /event/{source_id}, GET /action/{source_id}, GET /socket.io and GET /socket.io/. Echo routes before it runs middleware, so the skipper reads the registered route pattern from c.Path() instead of parsing the URL. The header is compared case-insensitively.
Socket.IO polling (GET without the upgrade, and POST) stays behind the token, as before. The dashboard's socket.io-client 2.x opens the websocket transport directly and only falls back to polling in a browser with no WebSocket. user-service's local-storage listener dials GET /event/local-storage with x/net/websocket, which sends the header, so it still matches.
The subscriptions themselves remain open without a token: the dashboard subscribes without one.
Tests: the upgrade header on /event_type, /action_type and /ysk gets 401, as does a subscribe route without the header and a socket.io POST; the four subscribe routes get past the auth layer; a user JWT, validated against a JWKS served from a stand-in user-service, gets 200 from the LAN and a tampered one gets 401. The README no longer says loopback peers skip the token.
package.json and tsconfig.json generated a typescript-axios client from the OpenAPI spec with openapi-generator. No workflow, goreleaser config or Go code runs them and nothing is published from them; the dashboard has no generated message bus client, it talks to socket.io and the websocket routes directly. Their pinned generator and TypeScript versions only drew dependency alerts.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Host: unixheader, which any client sets and the gateway forwards: anyone who reached the gateway had the whole bus API without a token. Now a request is internal when it arrives on the unix socket (by its local address, not a header; the socket is 0600) or from loopback with the boot's secret, as in every other service.Needs the services built from their security-deps branches, which send the secret. Known residual: the subscription routes still need no token.