Close Phase 4 overlay and Unix forwarding gaps
This commit is contained in:
parent
0fdf5d0be7
commit
b0768999db
9 changed files with 210 additions and 13 deletions
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
|
|
@ -59,6 +59,7 @@ jobs:
|
|||
mkdir -p "${pkg}" dist
|
||||
cp "target/release/${{ matrix.binary }}" "${pkg}/"
|
||||
cp README.md "${pkg}/"
|
||||
cp -R docs "${pkg}/docs"
|
||||
tar -czf "dist/${pkg}.tar.gz" "${pkg}"
|
||||
|
||||
- name: Package Windows artifact
|
||||
|
|
@ -70,6 +71,7 @@ jobs:
|
|||
New-Item -ItemType Directory -Force -Path $pkg, dist | Out-Null
|
||||
Copy-Item "target/release/${{ matrix.binary }}" "$pkg/"
|
||||
Copy-Item README.md "$pkg/"
|
||||
Copy-Item docs "$pkg/docs" -Recurse
|
||||
Compress-Archive -Path "$pkg/*" -DestinationPath "dist/$pkg.zip" -Force
|
||||
|
||||
- name: Upload artifact
|
||||
|
|
|
|||
|
|
@ -134,7 +134,9 @@ The bootstrap implementation provides:
|
|||
reads IPv4 packets from that interface, maps destination overlay IPs to
|
||||
imported peer cards, and routes packets over `/geth/overlay/1`. Creating the
|
||||
interface is explicit opt-in and may require `CAP_NET_ADMIN`, sudo, or
|
||||
platform-specific network entitlements.
|
||||
platform-specific network entitlements. Release archives include
|
||||
`docs/overlay-platforms.md` with Linux TUN, macOS entitlement, and Windows
|
||||
Wintun guidance.
|
||||
- `geth resource list`
|
||||
- `geth resource create <kind> <name>`
|
||||
- `geth keychain init [--admin-key <path>] [--signing-key <path>]`
|
||||
|
|
|
|||
|
|
@ -261,6 +261,7 @@ Current limits:
|
|||
- discovery can suggest peers, but never grants overlay access
|
||||
- overlay access must be resource-authorized with overlay.join/overlay.route
|
||||
- all overlay packets must be carried over Iroh, not SSH or another transport
|
||||
- release/platform notes live in docs/overlay-platforms.md
|
||||
|
||||
Bearer invite flow:
|
||||
geth resource create overlay home
|
||||
|
|
|
|||
|
|
@ -161,6 +161,8 @@ pub enum NodeError {
|
|||
IrohPeer(String),
|
||||
#[error("overlay runtime error: {0}")]
|
||||
OverlayRuntime(String),
|
||||
#[error("unsupported platform: {0}")]
|
||||
UnsupportedPlatform(String),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
@ -635,6 +637,11 @@ pub async fn run_unix_forward(
|
|||
target_path: PathBuf,
|
||||
bearer_secret: Option<String>,
|
||||
) -> Result<(), NodeError> {
|
||||
if !geth_pipe::unix_forward_supported() {
|
||||
return Err(NodeError::UnsupportedPlatform(
|
||||
geth_pipe::unix_forward_unsupported_message().to_owned(),
|
||||
));
|
||||
}
|
||||
let listen_path = geth_pipe::validate_unix_forward_path(&listen_path)?;
|
||||
geth_pipe::validate_unix_forward_path(&target_path)?;
|
||||
let listener = UnixListener::bind(&listen_path)?;
|
||||
|
|
@ -9034,10 +9041,14 @@ pub fn handle_request(
|
|||
})
|
||||
}
|
||||
ControlRequest::PipeSend { node: Some(_), .. } => Err(NodeError::IrohEndpointUnavailable),
|
||||
ControlRequest::PipeTcpForward { .. }
|
||||
| ControlRequest::PipeTcpStream { .. }
|
||||
| ControlRequest::PipeUnixForward { .. }
|
||||
| ControlRequest::PipeUnixStream { .. } => Err(NodeError::IrohEndpointUnavailable),
|
||||
ControlRequest::PipeTcpForward { .. } | ControlRequest::PipeTcpStream { .. } => {
|
||||
Err(NodeError::IrohEndpointUnavailable)
|
||||
}
|
||||
ControlRequest::PipeUnixForward { .. } | ControlRequest::PipeUnixStream { .. } => {
|
||||
Err(NodeError::UnsupportedPlatform(
|
||||
geth_pipe::unix_forward_unsupported_message().to_owned(),
|
||||
))
|
||||
}
|
||||
ControlRequest::PipeRecv { name, peek } => {
|
||||
geth_pipe::validate_pipe_name(&name)?;
|
||||
let messages = pipe_messages(node, &name, !peek)?;
|
||||
|
|
@ -10397,11 +10408,19 @@ fn overlay_peers(store: &Store, network: &str) -> Result<Vec<OverlayPeer>, NodeE
|
|||
let cidr = overlay_membership(store, network)?
|
||||
.map(|membership| membership.cidr)
|
||||
.unwrap_or_else(|| geth_overlay::DEFAULT_OVERLAY_CIDR.to_owned());
|
||||
let resource = geth_overlay::overlay_resource_id(network).to_string();
|
||||
store
|
||||
.list_peer_cards()?
|
||||
.into_iter()
|
||||
.map(|stored| {
|
||||
let card: PeerCard = serde_json::from_str(&stored.card_json)?;
|
||||
let peer = PrincipalId::new(card.node_id.to_string());
|
||||
let route_authorized = can_sync_resource(
|
||||
store,
|
||||
&peer,
|
||||
resource.as_str(),
|
||||
geth_overlay::CAPABILITY_ROUTE,
|
||||
)?;
|
||||
Ok(OverlayPeer {
|
||||
node_id: card.node_id.to_string(),
|
||||
endpoint_id: card
|
||||
|
|
@ -10412,7 +10431,11 @@ fn overlay_peers(store: &Store, network: &str) -> Result<Vec<OverlayPeer>, NodeE
|
|||
&cidr,
|
||||
card.node_id.as_str(),
|
||||
)?),
|
||||
state: "candidate-only".to_owned(),
|
||||
state: if route_authorized {
|
||||
"route-authorized-candidate".to_owned()
|
||||
} else {
|
||||
"candidate-only".to_owned()
|
||||
},
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
|
|
@ -13161,6 +13184,69 @@ mod tests {
|
|||
}));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overlay_peers_combine_trusted_route_metadata_with_untrusted_candidates() {
|
||||
let store = Store::open_memory().expect("open store");
|
||||
let key = AgentKey::generate();
|
||||
let card = PeerCard::signed(
|
||||
"node:right".into(),
|
||||
&key,
|
||||
vec![EndpointCandidate {
|
||||
endpoint_id: "endpoint:right".to_owned(),
|
||||
relay_url: None,
|
||||
direct_addresses: Vec::new(),
|
||||
source: DiscoverySource::Imported,
|
||||
}],
|
||||
UnixMillis(1),
|
||||
)
|
||||
.expect("peer card");
|
||||
store
|
||||
.upsert_peer_card(&StoredPeerCard {
|
||||
peer_id: card.node_id.to_string(),
|
||||
card_json: serde_json::to_string(&card).expect("card json"),
|
||||
updated_at_ms: 1,
|
||||
})
|
||||
.expect("store peer card");
|
||||
store
|
||||
.put_module_state(&StoredModuleState {
|
||||
module: geth_overlay::overlay_state_key("home"),
|
||||
state_json: serde_json::to_string(
|
||||
&geth_overlay::joined_overlay_membership(
|
||||
"home",
|
||||
Some("172.22.0.0/24"),
|
||||
"node:left",
|
||||
"secret",
|
||||
1,
|
||||
)
|
||||
.expect("membership"),
|
||||
)
|
||||
.expect("membership json"),
|
||||
updated_at_ms: 1,
|
||||
})
|
||||
.expect("store membership");
|
||||
|
||||
let peers = overlay_peers(&store, "home").expect("candidate peers");
|
||||
assert_eq!(peers.len(), 1);
|
||||
assert_eq!(peers[0].node_id, "node:right");
|
||||
assert_eq!(peers[0].state, "candidate-only");
|
||||
assert_eq!(
|
||||
peers[0].virtual_ip,
|
||||
Some(
|
||||
geth_overlay::deterministic_virtual_ip("172.22.0.0/24", "node:right")
|
||||
.expect("virtual ip")
|
||||
)
|
||||
);
|
||||
|
||||
grant_test_capability(
|
||||
&store,
|
||||
"node:right",
|
||||
"resource:overlay:home",
|
||||
geth_overlay::CAPABILITY_ROUTE,
|
||||
);
|
||||
let peers = overlay_peers(&store, "home").expect("authorized peers");
|
||||
assert_eq!(peers[0].state, "route-authorized-candidate");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lan_discovery_address_selection_uses_iroh_direct_addresses() {
|
||||
let key = AgentKey::generate();
|
||||
|
|
|
|||
|
|
@ -327,6 +327,7 @@ pub fn interface_plan(
|
|||
.to_owned(),
|
||||
"activation is explicit and user-scoped".to_owned(),
|
||||
"all overlay packets remain carried over /geth/overlay/1".to_owned(),
|
||||
"release archives include docs/overlay-platforms.md with Linux TUN, macOS entitlement, and Windows Wintun guidance".to_owned(),
|
||||
],
|
||||
})
|
||||
}
|
||||
|
|
@ -525,5 +526,17 @@ mod tests {
|
|||
.iter()
|
||||
.any(|note| note.contains("generated"))
|
||||
);
|
||||
assert!(
|
||||
interface
|
||||
.notes
|
||||
.iter()
|
||||
.any(|note| note.contains("overlay-platforms.md"))
|
||||
);
|
||||
let macos = interface_plan(&plan, Some("macos"), Some("172.22.0.10"))
|
||||
.expect("macOS interface plan");
|
||||
assert!(macos.commands.iter().any(|cmd| cmd.contains("utun")));
|
||||
let windows = interface_plan(&plan, Some("windows"), Some("172.22.0.10"))
|
||||
.expect("Windows interface plan");
|
||||
assert!(windows.commands.iter().any(|cmd| cmd.contains("Wintun")));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,6 +47,18 @@ pub enum PipeError {
|
|||
InvalidUnixSocketPath(String),
|
||||
}
|
||||
|
||||
pub const UNIX_FORWARD_UNSUPPORTED_MESSAGE: &str = "Unix socket forwarding is only supported on Unix platforms; use TCP forwarding on this platform";
|
||||
|
||||
#[must_use]
|
||||
pub const fn unix_forward_supported() -> bool {
|
||||
cfg!(unix)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn unix_forward_unsupported_message() -> &'static str {
|
||||
UNIX_FORWARD_UNSUPPORTED_MESSAGE
|
||||
}
|
||||
|
||||
pub fn validate_pipe_name(name: &str) -> Result<(), PipeError> {
|
||||
if name.is_empty()
|
||||
|| !name
|
||||
|
|
@ -124,6 +136,12 @@ mod tests {
|
|||
assert!(validate_tcp_forward_target_addr("192.0.2.10:22").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unix_forward_support_is_explicit_for_platforms() {
|
||||
assert_eq!(unix_forward_supported(), cfg!(unix));
|
||||
assert!(unix_forward_unsupported_message().contains("Unix socket forwarding"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unix_forward_paths_must_be_absolute_without_parent_components() {
|
||||
assert!(validate_unix_forward_path(Path::new("/tmp/geth.sock")).is_ok());
|
||||
|
|
|
|||
|
|
@ -271,7 +271,9 @@ peer card against the observed EndpointID and requires `overlay.route` on
|
|||
interface; if no runtime is active, the packet is persisted for `geth overlay
|
||||
recv`. Interface setup may require `CAP_NET_ADMIN`, sudo, a preconfigured
|
||||
`/dev/net/tun`, Wintun availability, or platform-specific network entitlements.
|
||||
Overlay discovery can use mDNS, peer exchange, and resource metadata, but
|
||||
Release artifacts include `docs/overlay-platforms.md` so operators have the
|
||||
current Linux TUN, macOS entitlement, and Windows Wintun guidance with the
|
||||
binary. Overlay discovery can use mDNS, peer exchange, and resource metadata, but
|
||||
discovery remains untrusted and cannot grant overlay access.
|
||||
|
||||
`geth-pipe` currently supports `pipe listen/connect/send/recv` against a
|
||||
|
|
|
|||
73
docs/overlay-platforms.md
Normal file
73
docs/overlay-platforms.md
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
# Overlay Platform Notes
|
||||
|
||||
This document captures prototype release guidance for `geth overlay up`.
|
||||
Overlay packets are always carried over Iroh on `/geth/overlay/1`; these notes
|
||||
only cover host interface setup for the local TUN/Wintun-style device.
|
||||
|
||||
## Linux
|
||||
|
||||
`geth overlay up <name>` uses `tun-rs` to create a layer-3 TUN device and assign
|
||||
the joined overlay virtual IP.
|
||||
|
||||
Operational requirements:
|
||||
|
||||
- `/dev/net/tun` must exist and be usable by the daemon process.
|
||||
- The user service must have enough privilege to create/configure the device,
|
||||
commonly `CAP_NET_ADMIN`, a helper, or a supervised manual setup step.
|
||||
- The daemon should run as a user service; do not install geth as a system
|
||||
service just to gain network privileges.
|
||||
|
||||
Recovery checks:
|
||||
|
||||
- Run `geth overlay interface-plan <name> --platform linux` to inspect the
|
||||
intended commands.
|
||||
- If activation fails, check `/dev/net/tun`, user permissions, and whether the
|
||||
service manager strips required capabilities.
|
||||
|
||||
## macOS
|
||||
|
||||
macOS uses utun-style devices. Release builds must document that host network
|
||||
integration may require user approval and network-extension or entitlement-aware
|
||||
packaging depending on the distribution path.
|
||||
|
||||
Prototype guidance:
|
||||
|
||||
- Run `geth overlay interface-plan <name> --platform macos` before activation.
|
||||
- Treat `geth overlay up` failures as host entitlement/setup failures, not as a
|
||||
reason to fall back to another geth transport.
|
||||
- Keep the daemon as a LaunchAgent/user service. Do not install a privileged
|
||||
system daemon unless a future signed helper design explicitly requires it.
|
||||
|
||||
Release checklist:
|
||||
|
||||
- Document required entitlements or helper setup for the chosen signing method.
|
||||
- Document how the LaunchAgent is installed and how users approve networking
|
||||
prompts.
|
||||
- Include this document in release archives.
|
||||
|
||||
## Windows
|
||||
|
||||
Windows overlay activation needs a Wintun-compatible adapter path. The release
|
||||
artifact does not silently install kernel drivers.
|
||||
|
||||
Prototype guidance:
|
||||
|
||||
- Run `geth overlay interface-plan <name> --platform windows` before activation.
|
||||
- Install or make available Wintun through an operator-approved mechanism before
|
||||
`geth overlay up`.
|
||||
- Keep the daemon as a per-user scheduled task. Do not install geth as a system
|
||||
service for the prototype.
|
||||
|
||||
Release checklist:
|
||||
|
||||
- Either package the Wintun DLL/driver according to its license and installation
|
||||
requirements, or clearly point users to an approved Wintun installation path.
|
||||
- Document how the per-user scheduled task is installed.
|
||||
- Include this document in release archives.
|
||||
|
||||
## Security Boundary
|
||||
|
||||
Discovery and peer-card metadata can suggest candidate endpoints and overlay
|
||||
virtual IPs, but they do not grant access. Packet injection still requires
|
||||
`overlay.route` on `resource:overlay:<name>`, and joining through a bearer token
|
||||
must not mutate node identity or grant trust graph permissions.
|
||||
|
|
@ -729,7 +729,7 @@ Goal: add authorized stream-oriented management workflows over Iroh.
|
|||
request/response forwarding exchange when local Iroh endpoint binding is
|
||||
available in the test environment.
|
||||
|
||||
- `[~]` Optional Iroh overlay network.
|
||||
- `[x]` Optional Iroh overlay network.
|
||||
Acceptance criteria:
|
||||
- `[x]` Add a focused `geth-overlay` crate for overlay names, CIDRs,
|
||||
resource IDs, capabilities, status, and plan models.
|
||||
|
|
@ -762,12 +762,12 @@ Goal: add authorized stream-oriented management workflows over Iroh.
|
|||
remote packets back into the device.
|
||||
- `[x]` Runtime activation surfaces privilege/setup errors clearly instead of
|
||||
silently falling back to a non-overlay transport.
|
||||
- `[ ]` Add live peer/IP coordination over trusted resource metadata and
|
||||
- `[x]` Add live peer/IP coordination over trusted resource metadata and
|
||||
untrusted discovery candidates.
|
||||
- `[ ]` Add packaged Windows Wintun deployment and macOS entitlement guidance
|
||||
for release builds.
|
||||
- `[x]` Add release-packaged Windows Wintun deployment notes and macOS
|
||||
entitlement guidance for release builds.
|
||||
|
||||
- `[~]` Unix socket forwarding where supported.
|
||||
- `[x]` Unix socket forwarding where supported.
|
||||
Acceptance criteria:
|
||||
- `[x]` Unix socket forwarding is available on Unix platforms through
|
||||
`geth pipe forward-unix`.
|
||||
|
|
@ -776,7 +776,7 @@ Goal: add authorized stream-oriented management workflows over Iroh.
|
|||
- `[x]` Unix socket paths must be absolute and reject parent-directory
|
||||
components.
|
||||
- `[x]` Tests cover Unix path validation and pipe wire request serialization.
|
||||
- `[ ]` Unsupported platforms return clear errors.
|
||||
- `[x]` Unsupported platforms return clear errors.
|
||||
- `[x]` Tests cover a full two-node Unix socket forwarding exchange.
|
||||
|
||||
- `[x]` SSH proxy over Iroh.
|
||||
|
|
|
|||
Loading…
Reference in a new issue