diff --git a/docs/sandbox-link-protocol.md b/docs/sandbox-link-protocol.md index 5a2a3c7b..71d59505 100644 --- a/docs/sandbox-link-protocol.md +++ b/docs/sandbox-link-protocol.md @@ -18,7 +18,7 @@ A stream ends in one of two ways, and the relay keeps them apart end to end. An The Sandbox I/O service runs `sandboxlink.Serve` with a `ServeConfig`: -- `URL`, `Credential` and `Resource` come from the [bootstrap input](sandbox-bootstrap.md). `PeerID` identifies the service. `ServerInstanceID` is a new ID whenever the service starts without its operation and handle registries. +- `URL`, `Credential` and `Resource` come from the [bootstrap input](sandbox-bootstrap.md). The credential identifies the service, so the Hello carries no peer ID. `ServerInstanceID` is a new ID whenever the service starts without its operation and handle registries. - `Services` holds one handler per offered service and version. A handler receives the `Bind`, which carries the authorized binding including a File stream's exports, and the stream. It owns the stream and returns when it is done with it. Its context ends when the attachment closes or `Serve` returns. - `Serve` reconnects with jittered exponential backoff, sending the same `ServerInstanceID`, whenever the link drops. It returns when its context ends or when the relay refuses the Hello with any failure other than `ServiceUnavailable` or `LimitExceeded`, for example `AuthenticationFailed` after the credential is withdrawn or `StaleGeneration` after a newer sandbox took over the resource. Before returning it cancels every handler's context and waits for the handlers. - `OnAttachmentLost` fires when an attachment's last open stream ends while the attachment is still open, such as when the link drops. `OnAttachmentRestored` fires when a stream binds a lost attachment again. `OnAttachmentClosed` fires with the reason when the relay reports `AttachmentClosed`. Losing a socket is not closing an attachment: the service keeps an attachment's state until it is closed. @@ -114,7 +114,6 @@ Hello Version u16 // 1 Role enum // RoleServe = 1, RoleAttach = 2 if RoleServe: - PeerID ID Credential bytes // 1..4096 bytes Resource ResourceRef ServerInstanceID ID @@ -204,7 +203,7 @@ Later control requests continue the Hello's request IDs. The relay ends an attac `HelloAccepted.MaxStreams` bounds the link's concurrent service streams. `MaxFrameBytes` bounds the payload of every frame on the link's service streams. -For a serve peer, the Authority returns the peer ID and the resource, including generation, that the credential serves. Both must equal the Hello's, otherwise the answer is `PermissionDenied`. The relay then applies the [generation rule](#authority-and-staleness) and makes the link the resource's current serve peer. +For a serve peer, the Authority returns the peer's identity and the resource, including generation, that the credential serves. The resource must equal the Hello's, otherwise the answer is `PermissionDenied`. The relay then applies the [generation rule](#authority-and-staleness) and makes the link the resource's current serve peer. The relay and the serve peer bound each handshake step, the WebSocket upgrade, the Hello, reading an `Open`, a `Bind` and its answer and each Authority call, by `sandboxlink.HandshakeTimeout` (10 seconds). The attach peer bounds an Open with its context. diff --git a/internal/sandboxlink/protocol.go b/internal/sandboxlink/protocol.go index d3f14fa5..abcae331 100644 --- a/internal/sandboxlink/protocol.go +++ b/internal/sandboxlink/protocol.go @@ -292,11 +292,11 @@ type ServiceVersion struct { Version uint16 } -// ServeHello introduces the Sandbox I/O service. ServerInstanceID changes -// whenever the service loses its operation or handle registry. +// ServeHello introduces the Sandbox I/O service. The credential identifies +// the peer, so the Hello carries no peer ID. ServerInstanceID changes whenever +// the service loses its operation or handle registry. type ServeHello struct { Version uint16 - PeerID sandboxwire.ID Credential []byte Resource ResourceRef ServerInstanceID sandboxwire.ID @@ -611,7 +611,7 @@ func decodeRequest(r *reader, op Op) Message { return nil } if Role(r.enum(func(v uint16) bool { return Role(v) == RoleServe || Role(v) == RoleAttach })) == RoleServe { - h := ServeHello{Version: Version, PeerID: r.id(), Credential: r.bytes(), Resource: r.resource(), ServerInstanceID: r.id()} + h := ServeHello{Version: Version, Credential: r.bytes(), Resource: r.resource(), ServerInstanceID: r.id()} n := r.count(uint32(ServiceNetwork)) for range n { h.Services = append(h.Services, ServiceVersion{Service: r.service(), Version: r.u16()}) @@ -672,7 +672,6 @@ func decodeSuccess(r *reader, op Op) Message { func (h ServeHello) encode(e *sandboxwire.Encoder) { e.U16(h.Version) e.Enum(uint16(RoleServe)) - e.ID(h.PeerID) e.Bytes(h.Credential) encodeResource(e, h.Resource) e.ID(h.ServerInstanceID) @@ -911,7 +910,7 @@ func (h ServeHello) validate() error { if err := h.Resource.validate(); err != nil { return err } - return checkIDs(h.PeerID, h.ServerInstanceID) + return checkIDs(h.ServerInstanceID) } func (h AttachHello) validate() error { diff --git a/internal/sandboxlink/protocol_test.go b/internal/sandboxlink/protocol_test.go index 271e56eb..f80b415d 100644 --- a/internal/sandboxlink/protocol_test.go +++ b/internal/sandboxlink/protocol_test.go @@ -40,7 +40,7 @@ type golden struct { // goldenFrames are the frames in testdata/link_v1.hex, in order. var goldenFrames = []golden{ - {1, ServeHello{Version: 1, PeerID: testID(0x04), Credential: []byte("serve"), Resource: testResource, ServerInstanceID: testID(0x05), + {1, ServeHello{Version: 1, Credential: []byte("serve"), Resource: testResource, ServerInstanceID: testID(0x05), Services: []ServiceVersion{{ServiceFile, 1}, {ServiceNetwork, 1}}}}, {1, AttachHello{Version: 1, RuntimeID: testID(0x06), Credential: []byte("runtime")}}, {1, HelloAccepted{LinkID: testID(0x0a), MaxStreams: 256, MaxFrameBytes: 1 << 20}}, diff --git a/internal/sandboxlink/relay/relay.go b/internal/sandboxlink/relay/relay.go index 2c678c20..ac8ff4aa 100644 --- a/internal/sandboxlink/relay/relay.go +++ b/internal/sandboxlink/relay/relay.go @@ -396,7 +396,7 @@ func (rl *Relay) admitServe(l *link, id uint64, hello sandboxlink.ServeHello) (* ctx, cancel := rl.authorityContext() peer, err := rl.cfg.Authority.AuthenticateServe(ctx, hello) cancel() - if err == nil && peer != (sandboxlink.ServePeer{PeerID: hello.PeerID, Resource: hello.Resource}) { + if err == nil && peer.Resource != hello.Resource { err = sandboxlink.Fail(sandboxlink.PermissionDenied) } if err != nil { diff --git a/internal/sandboxlink/relay/relay_test.go b/internal/sandboxlink/relay/relay_test.go index 0f24f00e..2972697d 100644 --- a/internal/sandboxlink/relay/relay_test.go +++ b/internal/sandboxlink/relay/relay_test.go @@ -143,8 +143,7 @@ func (f *fixture) serve(generation uint64) *servePeer { func (f *fixture) startServe(generation uint64) *servePeer { credential := []byte(fmt.Sprintf("serve credential %d", generation)) - peerID := sandboxwire.NewID() - f.auth.AddServe(credential, sandboxlink.ServePeer{PeerID: peerID, Resource: resource(generation)}) + f.auth.AddServe(credential, sandboxlink.ServePeer{PeerID: sandboxwire.NewID(), Resource: resource(generation)}) p := &servePeer{instance: sandboxwire.NewID(), connected: make(chan sandboxlink.HelloAccepted, 16), conns: make(chan net.Conn, 16), lost: make(chan sandboxwire.ID, 16), restored: make(chan sandboxwire.ID, 16), closed: make(chan sandboxlink.CloseReason, 128), binds: make(chan sandboxlink.Bind, 16), echoed: make(chan error, 16), done: make(chan struct{})} @@ -168,7 +167,7 @@ func (f *fixture) startServe(generation uint64) *servePeer { } return c, err }, - PeerID: peerID, Credential: credential, Resource: resource(generation), ServerInstanceID: p.instance, + Credential: credential, Resource: resource(generation), ServerInstanceID: p.instance, Services: []sandboxlink.ServiceHandler{ {Service: sandboxlink.ServiceFile, Version: 1, Serve: echo}, {Service: sandboxlink.ServiceProcess, Version: 1, Serve: resetAfterOne}, diff --git a/internal/sandboxlink/serve.go b/internal/sandboxlink/serve.go index 33f3634d..65df23f2 100644 --- a/internal/sandboxlink/serve.go +++ b/internal/sandboxlink/serve.go @@ -28,7 +28,6 @@ type ServeConfig struct { URL string TLS *tls.Config Dial Dialer - PeerID sandboxwire.ID Credential []byte Resource ResourceRef ServerInstanceID sandboxwire.ID @@ -59,7 +58,7 @@ type ServeConfig struct { // LimitExceeded; it then returns that *Error. Before returning it cancels // every handler's context and waits for the handlers. func Serve(parent context.Context, cfg ServeConfig) error { - hello := ServeHello{Version: Version, PeerID: cfg.PeerID, Credential: cfg.Credential, Resource: cfg.Resource, ServerInstanceID: cfg.ServerInstanceID} + hello := ServeHello{Version: Version, Credential: cfg.Credential, Resource: cfg.Resource, ServerInstanceID: cfg.ServerInstanceID} for _, h := range cfg.Services { if h.Serve == nil { return errors.New("sandbox link: service handler without Serve") diff --git a/internal/sandboxlink/testdata/link_v1.hex b/internal/sandboxlink/testdata/link_v1.hex index 75aabf27..537cbc79 100644 --- a/internal/sandboxlink/testdata/link_v1.hex +++ b/internal/sandboxlink/testdata/link_v1.hex @@ -1,11 +1,10 @@ # Link version 1 frames from goldenFrames in protocol_test.go, in order. Hex bytes; text after # is a comment. -# IDs repeat one byte: 01 tenant, 02 environment, 03 resource, 04 peer, 05 server instance, 06 Runtime, 07 attachment, 08 Session, 09 assignment, 0a link. +# IDs repeat one byte: 01 tenant, 02 environment, 03 resource, 05 server instance, 06 Runtime, 07 attachment, 08 Session, 09 assignment, 0a link. # ServeHello -00000073 0001 0000 0000000000000001 # header: length 115, OpHello, flags, request 1 +00000063 0001 0000 0000000000000001 # header: length 99, OpHello, flags, request 1 0001 # Version 1 0001 # Role RoleServe -04040404040404040404040404040404 # PeerID 00000005 7365727665 # Credential "serve" 01010101010101010101010101010101 # Resource.TenantID 02020202020202020202020202020202 # Resource.EnvironmentID