Skip to content

fix(ocap-kernel): allow plain ws:// connections for relay dialing#855

Merged
sirtimid merged 1 commit intomainfrom
sirtimid/ws-relay-filter
Feb 27, 2026
Merged

fix(ocap-kernel): allow plain ws:// connections for relay dialing#855
sirtimid merged 1 commit intomainfrom
sirtimid/ws-relay-filter

Conversation

@sirtimid
Copy link
Contributor

@sirtimid sirtimid commented Feb 26, 2026

Summary

  • Import @libp2p/websockets/filters and use the all filter for the WebSocket transport
  • The default libp2p WebSocket filter only permits wss:// (TLS) connections, which blocks relays on private networks or behind a reverse proxy that expose plain ws:// endpoints

Test plan

  • Relay dialing over ws:// multiaddrs succeeds (previously rejected)
  • wss:// relay connections continue to work

🤖 Generated with Claude Code


Note

Medium Risk
Changes libp2p transport configuration to permit non-TLS ws:// connections, which can affect connectivity/security expectations when dialing relays. Scope is small but impacts network transport behavior across environments.

Overview
Updates the libp2p WebSocket transport configuration in connection-factory.ts to use @libp2p/websockets/filters.all, allowing relay dialing over plain ws:// multiaddrs in addition to wss://.

This removes the previous implicit restriction to secure WebSockets, improving compatibility with private-network/reverse-proxy relays that only expose ws:// endpoints.

Written by Cursor Bugbot for commit 73f8293. This will update automatically on new commits. Configure here.

The default libp2p WebSocket filter only permits wss:// (TLS)
connections. Relays on private networks or behind a reverse proxy
often expose plain ws:// endpoints. Use the "all" filter so both
ws:// and wss:// multiaddrs are accepted.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@sirtimid sirtimid requested a review from a team as a code owner February 26, 2026 11:47
@sirtimid sirtimid enabled auto-merge February 26, 2026 11:50
@github-actions
Copy link
Contributor

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 76.02%
🟰 ±0%
6567 / 8638
🔵 Statements 75.91%
🟰 ±0%
6672 / 8789
🔵 Functions 73.83%
🟰 ±0%
1642 / 2224
🔵 Branches 75.43%
🟰 ±0%
2426 / 3216
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
packages/ocap-kernel/src/remotes/platform/connection-factory.ts 97.27%
🟰 ±0%
90%
🟰 ±0%
96.29%
🟰 ±0%
97.22%
🟰 ±0%
262, 407, 441
Generated in workflow #3819 for commit 73f8293 by the Vitest Coverage Report Action

Copy link
Member

@rekmarks rekmarks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving, but we shouldn't be able to use ws:// outside of the use cases mentioned in the PR description. We can keep webSockets({ filter: wsFilters.all }) so the transport handles
ws://, but replace denyDialMultiaddr: async () => false with a function that:

  • Allows any wss:// (secure)
  • Allows ws:// to RFC 1918 / loopback addresses
  • Allows ws:// to any hostname in a configurable allowedWsHosts list
  • Denies everything else

@sirtimid sirtimid added this pull request to the merge queue Feb 27, 2026
Merged via the queue into main with commit 17f947e Feb 27, 2026
29 checks passed
@sirtimid sirtimid deleted the sirtimid/ws-relay-filter branch February 27, 2026 01:05
github-merge-queue bot pushed a commit that referenced this pull request Feb 27, 2026
…ed addresses (#857)

PR #855 switched `webSockets()` to use `wsFilters.all` to enable plain
`ws://` connections for private-network and reverse-proxy relay
scenarios. As written, this also allowed `ws://` to any public internet
address, sending unencrypted traffic over the open internet.

This PR keeps the transport-level change (`wsFilters.all` must stay so
the transport handles `ws://` at all) and adds enforcement in
`connectionGater.denyDialMultiaddr`.

## Changes

- **`types.ts`**: Adds `allowedWsHosts?: string[]` to both
`RemoteCommsOptions` and `ConnectionFactoryOptions` — an explicit
per-instance list of hostnames/IPs that are trusted for plain `ws://`
beyond the always-allowed private ranges.
- **`transport.ts`**: Threads `allowedWsHosts` through to
`ConnectionFactory.make()`.
- **`connection-factory.ts`**: Replaces the `async () => false` stub
with real gating logic via two new module-level helpers:
- `isPlainWs(ma)` — detects plain `ws://` using `ma.protoNames()` (true
only when `ws` is present and neither `wss` nor `tls` is).
- `isPrivateAddress(host)` — checks IPv4 loopback (`127.0.0.0/8`), RFC
1918 ranges, IPv6 loopback/ULA/link-local by direct octet comparison (no
bitwise operators).
- `denyDialMultiaddr` allows `wss://`, WebRTC, and circuit-relay
addresses unconditionally; allows `ws://` only to private/loopback IPs
or hosts on `allowedWsHosts`; denies everything else.
- **`connection-factory.test.ts`**: Replaces the single stub test with a
9-case `it.each` suite covering secure transports, all four private IPv4
ranges, public IP denial, allowlisted/non-allowlisted hostnames, and
non-WebSocket multiaddrs.

## Testing

The new `connectionGater.denyDialMultiaddr` describe block exercises the
gating function directly by passing minimal `Multiaddr`-shaped objects
(with `protoNames()` and `toOptions()`) extracted from the
`createLibp2p` mock call args. This avoids any libp2p networking and
keeps the tests fast and deterministic.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes libp2p connection gating for `ws://` dials, which can impact
connectivity if deployments rely on public plaintext WebSocket relays
without configuring `allowedWsHosts`. Logic is security-sensitive
(transport encryption policy) but narrowly scoped and covered by new
table-driven tests.
> 
> **Overview**
> Tightens relay dialing security by adding
`connectionGater.denyDialMultiaddr` logic that **denies plaintext
`ws://` connections to public/unrecognised hosts** while still allowing
`wss://` and non-WebSocket transports.
> 
> Introduces a new `allowedWsHosts` option (plumbed from
`RemoteCommsOptions` through `transport.ts` into `ConnectionFactory`) to
permit specific non-private `ws://` relay hosts when needed, and
replaces the prior “allow all” gater stub with RFC-based
private/loopback detection plus an allowlist.
> 
> Updates `connection-factory.test.ts` to validate the new gating
behavior across private IPv4 ranges, public IP denial, allowlisted
hostnames, and non-WebSocket multiaddrs.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
0b7674f. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants