Skip to content

Add vsock peer credentials on Posix.Context - #193

Open
VictorDebray wants to merge 3 commits into
grpc:mainfrom
VictorDebray:feat/add-vsock-peer-credentials-on-posix-context
Open

Add vsock peer credentials on Posix.Context#193
VictorDebray wants to merge 3 commits into
grpc:mainfrom
VictorDebray:feat/add-vsock-peer-credentials-on-posix-context

Conversation

@VictorDebray

Copy link
Copy Markdown

Surfaces the peer's VSOCK context ID (CID) as Posix.Context.vsockCredentials.

The CID is read through NIOPosix's remoteVsockAddress channel option rather than from channel.remoteAddress: NIOCore.SocketAddress has no vsock representation, so remoteAddress is always nil for vsock channels. The option reads the peer address from getpeername and validates the address family before returning it.

getPeerCID returns nil for any channel that is not a connected vsock channel -- the option is rejected for other address families and is unsupported on non-POSIX channel types -- so vsockCredentials is nil for TCP and UDS connections.

Requires the apple/swift-nio#3689 remoteVsockAddress channel option.

Surfaces the peer's VSOCK context ID (CID) as `Posix.Context.vsockCredentials`.

The CID is read through NIOPosix's `remoteVsockAddress` channel option rather
than from `channel.remoteAddress`: `NIOCore.SocketAddress` has no vsock
representation, so `remoteAddress` is always nil for vsock channels. The option
reads the peer address from `getpeername` and validates the address family
before returning it.

`getPeerCID` returns nil for any channel that is not a connected vsock channel
-- the option is rejected for other address families and is unsupported on
non-POSIX channel types -- so `vsockCredentials` is nil for TCP and UDS
connections.

Requires the apple/swift-nio#3689 `remoteVsockAddress` channel option.
@linux-foundation-easycla

linux-foundation-easycla Bot commented Aug 3, 2026

Copy link
Copy Markdown

CLA Signed
The committers listed above are authorized under a signed CLA.

  • ✅ login: VictorDebray / name: Victor Debray (2da0ae2)

@VictorDebray

Copy link
Copy Markdown
Author

/easycla

@glbrntt glbrntt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks, we'll need a test for this as well.

Comment thread Sources/GRPCNIOTransportHTTP2Posix/HTTP2ServerTransport+Posix.swift Outdated
Comment thread Sources/GRPCNIOTransportHTTP2Posix/HTTP2ServerTransport+Posix.swift Outdated
Comment thread Sources/GRPCNIOTransportHTTP2Posix/HTTP2ServerTransport+Posix.swift Outdated
Comment thread Sources/GRPCNIOTransportHTTP2Posix/HTTP2ServerTransport+Posix.swift Outdated
Comment thread Sources/GRPCNIOTransportHTTP2Posix/HTTP2ServerTransport+Posix.swift Outdated
Comment thread Sources/GRPCNIOTransportHTTP2Posix/HTTP2ServerTransport+Posix.swift Outdated
- Inline `getPeerCID` at its single call site, removing the indirection.
- Use `SocketAddress.VirtualSocket.ContextID` for the CID instead of a raw
  `UInt32`, reusing the existing gRPC type.
- Make `VsockCredentials` `Hashable` with a `var` property.
- Annotate the new API with `@available(gRPCSwiftNIOTransport 2.10, *)` and
  bump `nextMinorVersion` so the 2.10 availability macro is defined.
- Drop the redundant second sentence of the `VsockCredentials` doc comment: the
  CID identifies whichever context is at the other end, not specifically a guest.
Covers both directions of the `vsockCredentials` contract with end-to-end RPCs, asserting on the server's transport-specific context from inside a server interceptor, which is the only place it's reachable.

- Over vsock loopback, the credentials are populated and the CID is the local context. Gated on a new `vsockLoopbackAvailable()` helper: the existing `vsockAvailable()` only reports that the address family exists, which is enough to bind a listener, but connecting to `VMADDR_CID_LOCAL` also needs the `vsock_loopback` transport, so a connect test needs the stricter check.

- Over a Unix domain socket and over IPv4, the credentials are nil. The CID is read with `getpeername`, which succeeds on any connected socket, so without an address-family check a UDS or TCP peer would yield a `sockaddr_un`/`sockaddr_in` reinterpreted as a `sockaddr_vm` and produce a meaningless CID. These two tests are what pin that down.
/// The peer's VSOCK context ID for a virtual-socket connection, taken from the connection's
/// peer address.
@available(gRPCSwiftNIOTransport 2.10, *)
public struct VsockCredentials: Hashable, Sendable {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This can just be in the core module, I think. It's also not specific to the server transport so should be in its own file at the top level.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Given we spell out the VirtualSocket for the address type we should do the same here: VirtualSocketCredentials

Comment on lines +338 to +340
public var cid: GRPCNIOTransportCore.SocketAddress.VirtualSocket.ContextID

public init(cid: GRPCNIOTransportCore.SocketAddress.VirtualSocket.ContextID) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Let's spell this out as contextID like the VirtualSocket address does.

Comment on lines +276 to +280
// `channel.remoteAddress` can't report a vsock peer because NIO's `SocketAddress` has no
// vsock representation, so read the peer address from the channel option instead. It's
// rejected for other address families and unsupported on non-POSIX channels, both of which
// leave `vsockCredentials` nil.
if let vsockAddress = try? await channel.getOption(.remoteVsockAddress).get() {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Let's not do this unconditionally: we shouldn't pay the cost of attempting to look up a vsock address if we're not using vsock.

Comment on lines +68 to +102
return try await withThrowingTaskGroup(of: Void.self) { group in
let server = GRPCServer(
transport: HTTP2ServerTransport.Posix(
address: serverAddress,
transportSecurity: .plaintext
),
services: [ControlService()],
interceptors: [RecordingInterceptor(recorder: recorder)]
)

group.addTask {
try await server.serve()
}

let address = try await server.listeningAddress!

let client = GRPCClient(
transport: try HTTP2ClientTransport.Posix(
target: try makeTarget(address),
transportSecurity: .plaintext
)
)

group.addTask {
try await client.runConnections()
}

let control = ControlClient(wrapping: client)
let input = ControlInput.with { $0.numberOfMessages = 1 }
try await control.unary(request: ClientRequest(message: input)) { response in
_ = try response.message
}

server.beginGracefulShutdown()
client.beginGracefulShutdown()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Just use withGRPCServer and withGRPCClient here, no need to manually manage each set of resources.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

You can also use the HelloWorldService here whose implementation can be hooked on init, so the logic of each test can effectively live inside that and be driven by the client making a request. That removes the need for the recorder altogether.

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