Skip to content

Commit b1702aa

Browse files
authored
Derive the serve peer's identity from its credential (#344)
ServeHello no longer carries a PeerID: the serve side has no source for it and the credential already identifies the peer. The relay checks only the Hello's Resource against the Authority's ServePeer, which still returns the peer's identity. Updates the codec, validation, serve config, relay, tests, golden frames and the Link protocol document.
1 parent 4bd566b commit b1702aa

7 files changed

Lines changed: 14 additions & 19 deletions

File tree

‎docs/sandbox-link-protocol.md‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ A stream ends in one of two ways, and the relay keeps them apart end to end. An
1818

1919
The Sandbox I/O service runs `sandboxlink.Serve` with a `ServeConfig`:
2020

21-
- `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.
21+
- `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.
2222
- `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.
2323
- `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.
2424
- `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
114114
Version u16 // 1
115115
Role enum // RoleServe = 1, RoleAttach = 2
116116
if RoleServe:
117-
PeerID ID
118117
Credential bytes // 1..4096 bytes
119118
Resource ResourceRef
120119
ServerInstanceID ID
@@ -204,7 +203,7 @@ Later control requests continue the Hello's request IDs. The relay ends an attac
204203

205204
`HelloAccepted.MaxStreams` bounds the link's concurrent service streams. `MaxFrameBytes` bounds the payload of every frame on the link's service streams.
206205

207-
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.
206+
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.
208207

209208
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.
210209

‎internal/sandboxlink/protocol.go‎

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -292,11 +292,11 @@ type ServiceVersion struct {
292292
Version uint16
293293
}
294294

295-
// ServeHello introduces the Sandbox I/O service. ServerInstanceID changes
296-
// whenever the service loses its operation or handle registry.
295+
// ServeHello introduces the Sandbox I/O service. The credential identifies
296+
// the peer, so the Hello carries no peer ID. ServerInstanceID changes whenever
297+
// the service loses its operation or handle registry.
297298
type ServeHello struct {
298299
Version uint16
299-
PeerID sandboxwire.ID
300300
Credential []byte
301301
Resource ResourceRef
302302
ServerInstanceID sandboxwire.ID
@@ -611,7 +611,7 @@ func decodeRequest(r *reader, op Op) Message {
611611
return nil
612612
}
613613
if Role(r.enum(func(v uint16) bool { return Role(v) == RoleServe || Role(v) == RoleAttach })) == RoleServe {
614-
h := ServeHello{Version: Version, PeerID: r.id(), Credential: r.bytes(), Resource: r.resource(), ServerInstanceID: r.id()}
614+
h := ServeHello{Version: Version, Credential: r.bytes(), Resource: r.resource(), ServerInstanceID: r.id()}
615615
n := r.count(uint32(ServiceNetwork))
616616
for range n {
617617
h.Services = append(h.Services, ServiceVersion{Service: r.service(), Version: r.u16()})
@@ -672,7 +672,6 @@ func decodeSuccess(r *reader, op Op) Message {
672672
func (h ServeHello) encode(e *sandboxwire.Encoder) {
673673
e.U16(h.Version)
674674
e.Enum(uint16(RoleServe))
675-
e.ID(h.PeerID)
676675
e.Bytes(h.Credential)
677676
encodeResource(e, h.Resource)
678677
e.ID(h.ServerInstanceID)
@@ -911,7 +910,7 @@ func (h ServeHello) validate() error {
911910
if err := h.Resource.validate(); err != nil {
912911
return err
913912
}
914-
return checkIDs(h.PeerID, h.ServerInstanceID)
913+
return checkIDs(h.ServerInstanceID)
915914
}
916915

917916
func (h AttachHello) validate() error {

‎internal/sandboxlink/protocol_test.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ type golden struct {
4040

4141
// goldenFrames are the frames in testdata/link_v1.hex, in order.
4242
var goldenFrames = []golden{
43-
{1, ServeHello{Version: 1, PeerID: testID(0x04), Credential: []byte("serve"), Resource: testResource, ServerInstanceID: testID(0x05),
43+
{1, ServeHello{Version: 1, Credential: []byte("serve"), Resource: testResource, ServerInstanceID: testID(0x05),
4444
Services: []ServiceVersion{{ServiceFile, 1}, {ServiceNetwork, 1}}}},
4545
{1, AttachHello{Version: 1, RuntimeID: testID(0x06), Credential: []byte("runtime")}},
4646
{1, HelloAccepted{LinkID: testID(0x0a), MaxStreams: 256, MaxFrameBytes: 1 << 20}},

‎internal/sandboxlink/relay/relay.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,7 @@ func (rl *Relay) admitServe(l *link, id uint64, hello sandboxlink.ServeHello) (*
396396
ctx, cancel := rl.authorityContext()
397397
peer, err := rl.cfg.Authority.AuthenticateServe(ctx, hello)
398398
cancel()
399-
if err == nil && peer != (sandboxlink.ServePeer{PeerID: hello.PeerID, Resource: hello.Resource}) {
399+
if err == nil && peer.Resource != hello.Resource {
400400
err = sandboxlink.Fail(sandboxlink.PermissionDenied)
401401
}
402402
if err != nil {

‎internal/sandboxlink/relay/relay_test.go‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,7 @@ func (f *fixture) serve(generation uint64) *servePeer {
143143

144144
func (f *fixture) startServe(generation uint64) *servePeer {
145145
credential := []byte(fmt.Sprintf("serve credential %d", generation))
146-
peerID := sandboxwire.NewID()
147-
f.auth.AddServe(credential, sandboxlink.ServePeer{PeerID: peerID, Resource: resource(generation)})
146+
f.auth.AddServe(credential, sandboxlink.ServePeer{PeerID: sandboxwire.NewID(), Resource: resource(generation)})
148147
p := &servePeer{instance: sandboxwire.NewID(), connected: make(chan sandboxlink.HelloAccepted, 16), conns: make(chan net.Conn, 16),
149148
lost: make(chan sandboxwire.ID, 16), restored: make(chan sandboxwire.ID, 16), closed: make(chan sandboxlink.CloseReason, 128),
150149
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 {
168167
}
169168
return c, err
170169
},
171-
PeerID: peerID, Credential: credential, Resource: resource(generation), ServerInstanceID: p.instance,
170+
Credential: credential, Resource: resource(generation), ServerInstanceID: p.instance,
172171
Services: []sandboxlink.ServiceHandler{
173172
{Service: sandboxlink.ServiceFile, Version: 1, Serve: echo},
174173
{Service: sandboxlink.ServiceProcess, Version: 1, Serve: resetAfterOne},

‎internal/sandboxlink/serve.go‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ type ServeConfig struct {
2828
URL string
2929
TLS *tls.Config
3030
Dial Dialer
31-
PeerID sandboxwire.ID
3231
Credential []byte
3332
Resource ResourceRef
3433
ServerInstanceID sandboxwire.ID
@@ -59,7 +58,7 @@ type ServeConfig struct {
5958
// LimitExceeded; it then returns that *Error. Before returning it cancels
6059
// every handler's context and waits for the handlers.
6160
func Serve(parent context.Context, cfg ServeConfig) error {
62-
hello := ServeHello{Version: Version, PeerID: cfg.PeerID, Credential: cfg.Credential, Resource: cfg.Resource, ServerInstanceID: cfg.ServerInstanceID}
61+
hello := ServeHello{Version: Version, Credential: cfg.Credential, Resource: cfg.Resource, ServerInstanceID: cfg.ServerInstanceID}
6362
for _, h := range cfg.Services {
6463
if h.Serve == nil {
6564
return errors.New("sandbox link: service handler without Serve")

‎internal/sandboxlink/testdata/link_v1.hex‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
# Link version 1 frames from goldenFrames in protocol_test.go, in order. Hex bytes; text after # is a comment.
2-
# 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.
2+
# IDs repeat one byte: 01 tenant, 02 environment, 03 resource, 05 server instance, 06 Runtime, 07 attachment, 08 Session, 09 assignment, 0a link.
33

44
# ServeHello
5-
00000073 0001 0000 0000000000000001 # header: length 115, OpHello, flags, request 1
5+
00000063 0001 0000 0000000000000001 # header: length 99, OpHello, flags, request 1
66
0001 # Version 1
77
0001 # Role RoleServe
8-
04040404040404040404040404040404 # PeerID
98
00000005 7365727665 # Credential "serve"
109
01010101010101010101010101010101 # Resource.TenantID
1110
02020202020202020202020202020202 # Resource.EnvironmentID

0 commit comments

Comments
 (0)