-
Notifications
You must be signed in to change notification settings - Fork 39
Add UDS peer credentials on Posix.Context #192
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| /* | ||
| * Copyright 2026, gRPC Authors All rights reserved. | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| // Xcode's Archive builds with Xcode's Package support struggle with empty .c files | ||
| // (https://bugs.swift.org/browse/SR-12939). | ||
| void CGRPCNIOTransportLinux_i_do_nothing_just_working_around_a_darwin_toolchain_bug(void) {} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| /* | ||
| * Copyright 2026, gRPC Authors All rights reserved. | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
| #ifndef C_GRPC_NIO_TRANSPORT_LINUX_H_ | ||
| #define C_GRPC_NIO_TRANSPORT_LINUX_H_ | ||
|
|
||
| #ifdef __linux__ | ||
|
|
||
| // glibc 2.36+ only declares `struct ucred` when `_GNU_SOURCE` is defined, and the Swift Glibc | ||
| // module does not reliably set it. This target defines `_GNU_SOURCE` (see Package.swift) so that | ||
| // including <sys/socket.h> here exposes `struct ucred` and `SO_PEERCRED` to Swift. | ||
| #ifndef _GNU_SOURCE | ||
| #error You must define _GNU_SOURCE | ||
| #endif | ||
|
|
||
| #include <sys/socket.h> | ||
|
|
||
| #endif // __linux__ | ||
|
|
||
| #endif // C_GRPC_NIO_TRANSPORT_LINUX_H_ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -273,6 +273,8 @@ extension HTTP2ServerTransport { | |
| } | ||
| } catch {} | ||
|
|
||
| context.udsCredentials = await makeUDSCredentials(channel: channel) | ||
|
|
||
| return context | ||
| } | ||
| } | ||
|
|
@@ -308,10 +310,81 @@ extension HTTP2ServerTransport.Posix { | |
| @available(gRPCSwiftNIOTransport 2.2, *) | ||
| public var peerCertificateChain: X509.ValidatedCertificateChain? | ||
|
|
||
| /// Kernel-validated peer credentials of the connecting process when the | ||
| /// transport is bound to a Unix domain socket. `nil` for non-UDS | ||
| /// channels and on platforms or socket types where peer credentials are | ||
| /// unavailable. | ||
| @available(gRPCSwiftNIOTransport 2.10, *) | ||
| public var udsCredentials: UDSCredentials? | ||
|
|
||
| public init() { | ||
| } | ||
| } | ||
|
|
||
| /// Kernel-validated peer credentials reported by the operating system at | ||
| /// connect time for a Unix domain socket. They reflect the peer process as | ||
| /// recorded by the kernel when the connection was established; they are not | ||
| /// re-read per request. | ||
| @available(gRPCSwiftNIOTransport 2.10, *) | ||
| public struct UDSCredentials: Hashable, Sendable { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. None of these types are specific to server transport so shouldn't be nested within |
||
| /// The process ID of the peer process. | ||
| public var pid: ProcessID | ||
|
|
||
| /// The user ID of the peer process. | ||
| public var uid: UserID | ||
|
|
||
| /// The group ID of the peer process. | ||
| public var gid: GroupID | ||
|
Comment on lines
+331
to
+337
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we spell these out rather than abbreviating them? |
||
|
|
||
| public init(pid: ProcessID, uid: UserID, gid: GroupID) { | ||
| self.pid = pid | ||
| self.uid = uid | ||
| self.gid = gid | ||
| } | ||
| } | ||
|
|
||
| /// The ID of a process. | ||
| @available(gRPCSwiftNIOTransport 2.10, *) | ||
| public struct ProcessID: Hashable, Sendable, RawRepresentable, ExpressibleByIntegerLiteral { | ||
| public var rawValue: Int32 | ||
|
|
||
| public init(rawValue: Int32) { | ||
| self.rawValue = rawValue | ||
| } | ||
|
|
||
| public init(integerLiteral value: Int32) { | ||
| self.init(rawValue: value) | ||
| } | ||
| } | ||
|
|
||
| /// The ID of a user. | ||
| @available(gRPCSwiftNIOTransport 2.10, *) | ||
| public struct UserID: Hashable, Sendable, RawRepresentable, ExpressibleByIntegerLiteral { | ||
| public var rawValue: UInt32 | ||
|
|
||
| public init(rawValue: UInt32) { | ||
| self.rawValue = rawValue | ||
| } | ||
|
|
||
| public init(integerLiteral value: UInt32) { | ||
| self.init(rawValue: value) | ||
| } | ||
| } | ||
|
|
||
| /// The ID of a group. | ||
| @available(gRPCSwiftNIOTransport 2.10, *) | ||
| public struct GroupID: Hashable, Sendable, RawRepresentable, ExpressibleByIntegerLiteral { | ||
| public var rawValue: UInt32 | ||
|
|
||
| public init(rawValue: UInt32) { | ||
| self.rawValue = rawValue | ||
| } | ||
|
|
||
| public init(integerLiteral value: UInt32) { | ||
| self.init(rawValue: value) | ||
| } | ||
| } | ||
|
|
||
| /// Config for the `Posix` transport. | ||
| public struct Config: Sendable { | ||
| /// Compression configuration. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| /* | ||
| * Copyright 2026, gRPC Authors All rights reserved. | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| internal import GRPCNIOTransportCore | ||
| internal import NIOCore | ||
|
|
||
| #if canImport(Darwin) | ||
| internal import Darwin | ||
| #elseif canImport(Glibc) | ||
| internal import Glibc | ||
| // glibc guards `struct ucred` and `SO_PEERCRED` behind `_GNU_SOURCE`, which the Glibc module | ||
| // doesn't reliably set, so they come from this shim instead. | ||
| internal import CGRPCNIOTransportLinux | ||
| #elseif canImport(Musl) | ||
| internal import Musl | ||
| internal import CGRPCNIOTransportLinux | ||
| #endif | ||
|
|
||
| /// Reads kernel-validated peer credentials from `channel`'s underlying socket. | ||
| /// | ||
| /// Returns `nil` when the channel isn't backed by a Unix domain socket, when it isn't a | ||
| /// `SocketOptionProvider`, on platforms without peer-credential support, or on any read failure. | ||
| @available(gRPCSwiftNIOTransport 2.0, *) | ||
| internal func makeUDSCredentials( | ||
| channel: any Channel | ||
| ) async -> HTTP2ServerTransport.Posix.UDSCredentials? { | ||
| // Peer credentials only exist for Unix domain sockets, so avoid the socket option reads | ||
| // altogether for any other transport. | ||
| guard case .some(.unixDomainSocket) = channel.localAddress else { return nil } | ||
| guard let provider = channel as? any SocketOptionProvider else { return nil } | ||
|
|
||
| #if canImport(Darwin) | ||
| do { | ||
| // Darwin reports the peer's effective PID and its credentials through two separate options. | ||
| let pid: pid_t = try await provider.unsafeGetSocketOption( | ||
| level: SocketOptionLevel(SOL_LOCAL), | ||
| name: SocketOptionName(LOCAL_PEEREPID) | ||
| ).get() | ||
| let credentials: xucred = try await provider.unsafeGetSocketOption( | ||
| level: SocketOptionLevel(SOL_LOCAL), | ||
| name: SocketOptionName(LOCAL_PEERCRED) | ||
| ).get() | ||
| // `cr_groups` is imported as a fixed-size tuple; the first element is the primary group. | ||
| return HTTP2ServerTransport.Posix.UDSCredentials( | ||
| pid: .init(rawValue: pid), | ||
| uid: .init(rawValue: credentials.cr_uid), | ||
| gid: .init(rawValue: credentials.cr_groups.0) | ||
| ) | ||
| } catch { | ||
| return nil | ||
| } | ||
| #elseif canImport(Glibc) || canImport(Musl) | ||
| do { | ||
| let credentials: ucred = try await provider.unsafeGetSocketOption( | ||
| level: SocketOptionLevel(SOL_SOCKET), | ||
| name: SocketOptionName(SO_PEERCRED) | ||
| ).get() | ||
| return HTTP2ServerTransport.Posix.UDSCredentials( | ||
| pid: .init(rawValue: credentials.pid), | ||
| uid: .init(rawValue: credentials.uid), | ||
| gid: .init(rawValue: credentials.gid) | ||
| ) | ||
| } catch { | ||
| return nil | ||
| } | ||
| #else | ||
| return nil | ||
| #endif | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apologies, it turns out that we don't need to go as far as this: we can just add the C settings to the posix module, no need for a C module at all.