Skip to content

fix(net): answer TLS for the names a plugin exports - #4044

Merged
dr-bonez merged 2 commits into
masterfrom
fix/vhost-plugin-hostnames
Sep 22, 2026
Merged

dr-bonez merged 2 commits into
masterfrom
fix/vhost-plugin-hostnames

Conversation

@dr-bonez

@dr-bonez dr-bonez commented Sep 21, 2026 •

Copy link
Copy Markdown
Member

Summary

On master, an SSL .onion address on an OS-terminated binding fails its TLS handshake with unrecognized name. Every plugin-exported SSL hostname is affected; plaintext ones are not.

0ff264313 made the vhost lookup exact — "a name is served only if it has an entry of its own" — and registered mDNS names as entries to match. The loop in net_controller.rs that decides which names a host answers to still admitted only PublicDomain | PrivateDomain | Mdns, so a hostname a URL plugin exported (HostnameMetadata::Plugin) never got an entry, and the fallback that used to cover it was gone. Released 0.4.0.1 is unaffected: its lookup still falls back to the port's * entry.

  • named_vhost_leg decides the (port, leg) a name is served on. A plugin's name is served on the binding's assigned SSL port, because its provider forwards to the binding's bridge address whatever port the name itself advertises, and on the private leg whatever public the export carries — that flag describes the URL, not how the connection arrives.
  • Every named entry accepts the binding's loopback and bridge SSL addresses (ssl_vhost_internal_ips), whatever row creates it; gateway merges add to that. A plugin name has no gateways, so for it this is the whole accept set, and it holds when the name shares an entry with a private domain on the same port. Other services on the box reach any of the binding's names over the bridge the same way.
  • The named-vhost assembly lives in add_named_vhosts, so it can be tested directly.
  • The entry is otherwise built like any other name, so a terminated binding gets a certificate for the name and a passthrough binding routes it by SNI.
  • Changelog: the regression never shipped, so this amends the unreleased "Your server answers to its own addresses and no others" entry instead of adding a Fixed line.

Verification

  • a_plugin_name_is_served_on_the_bindings_ssl_port_over_internal_ips_alone covers the helpers; every_named_vhost_accepts_the_bridge_even_when_names_share_an_entry runs the assembly for a plugin name, a private domain, a public domain, and a private domain and plugin name sharing one key. All 407 net:: tests pass; pinned rustfmt clean.
  • The defect was reproduced on a StartOS 0.4.0.2 alpha VM (x86_64) with the Tor service, over the real Tor network against the start-os/admin onion on port 443: with the .onion name as SNI the handshake ends in TLS alert, unrecognized name; the same onion and port with no SNI return HTTP 200. So the path Tor → 10.0.3.1:443 → the TLS terminator → the UI is sound, and only the named entry was missing.
  • Not exercised on a box with this build.

Found while testing Start9Labs/tor-startos#39.

0ff2643 made the vhost lookup exact: a name is served only if it has an
entry of its own. It registered mDNS names as entries to match, but the
loop that decides which names a host answers to still admitted only
PublicDomain, PrivateDomain and Mdns. A hostname a URL plugin exported
never got an entry, and the fallback to the port's `*` entry that used to
cover it was gone, so every SSL .onion on an OS-terminated binding ended
its handshake in `unrecognized name`. 0.4.0.1 still has the fallback and
is unaffected.

A plugin's name is served on the binding's assigned SSL port, since its
provider forwards to the binding's bridge address whatever port the name
advertises, and on the private leg whatever `public` the export carries.
Its accept set is the binding's loopback and bridge SSL addresses alone:
a plugin hostname has no gateways, and its provider connects from its own
container.

The regression never shipped, so the changelog amends the unreleased
entry for the exact lookup rather than adding a Fixed line.

@helix-a helix-a left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

One correctness issue in merging a plugin name with an existing private-domain entry; the standalone onion case looks correct.

Verification at 1250103a: all 12 net::net_controller::tests and all 44 net::vhost:: tests pass; the changed Rust file passes the pinned rustfmt check and git diff --check passes. An isolated Rust harness executing the unchanged named-vhost assembly and helpers reproduces the shared-name failure: plugin alone admits 10.0.3.1, but adding the private-domain row leaves only 192.0.2.10. Extending the accept set after entry lookup makes both cases pass. Native tests needed the generated build/env files staged at the legacy include path under shared-libs/build; that temporary directory was removed. I did not exercise this build on a StartOS VM.

Comment thread shared-libs/crates/start-core/src/net/net_controller.rs
…rows share

A plugin name that is also a private domain on the same port shares one
vhost entry with it. The private-domain row sorts first and made the
entry, so the plugin-only bridge accept set was never applied and the
provider's connection was rejected. Every named entry now starts from the
binding's loopback and bridge SSL addresses, whatever row makes it.

The named-vhost assembly moves into add_named_vhosts so the shared-name
case has a regression test, and the ssl_vhost_private_ips doc comment the
previous commit separated from its function goes back.

@helix-a helix-a left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Re-reviewed 832b816. The shared-name accept-set defect is fixed and covered through the production assembly helper. All 407 network tests and scoped formatting checks pass locally. No further correctness findings; VM verification remains unperformed.

@dr-bonez
dr-bonez merged commit 8306dcf into master Sep 22, 2026
40 of 41 checks passed
@dr-bonez
dr-bonez deleted the fix/vhost-plugin-hostnames branch September 22, 2026 20:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants