ipcauth

package
v0.77.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 21, 2026 License: BSD-3-Clause Imports: 16 Imported by: 0

Documentation

Overview

Package ipcauth provides the kernel-authenticated identity of a local IPC (gRPC) caller and the transport credentials that surface it into the gRPC context, so the daemon can authorize individual RPCs by caller identity.

On Unix the identity is read from the kernel via SO_PEERCRED (Linux) or LOCAL_PEERCRED (Darwin/FreeBSD). On Windows it is derived from the named-pipe client token. Platforms without a peer-identity primitive get no credentials, and every consumer must fail closed when no identity is available.

Index

Constants

View Source
const (
	// ErrorReasonPrivilegeRequired identifies the detail.
	ErrorReasonPrivilegeRequired = "PRIVILEGE_REQUIRED"
	// ErrorDomain scopes the reason to the NetBird daemon.
	ErrorDomain = "daemon.netbird.io"
	// ErrorMetaSummary is the one-sentence explanation of what was refused.
	ErrorMetaSummary = "summary"
	// ErrorMetaCommand is the command that performs the same operation with the
	// privileges it needs, ready to copy and run.
	ErrorMetaCommand = "command"
)

Fields of the ErrorInfo detail the daemon attaches to a PermissionDenied it raises for an operation that requires root/administrator. Clients match on Reason and Domain rather than on the message text, and render the summary and command themselves so the user gets guidance instead of a gRPC error dump.

Variables

This section is empty.

Functions

func ElevatedCommand

func ElevatedCommand(command string) string

ElevatedCommand renders a command so that running it grants the privileges the operation needs. Windows has no in-line equivalent of sudo, so the command is returned unchanged and the user is expected to run it from an elevated terminal.

func ForwardIdentityMetadata

func ForwardIdentityMetadata(id Identity, known bool) metadata.MD

ForwardIdentityMetadata encodes an HTTP client's identity for the JSON gateway to forward to the daemon. When known is false only the marker is set, which makes the daemon treat the caller as unidentified rather than as the daemon itself.

func IsDaemonSelf

func IsDaemonSelf(id Identity) bool

IsDaemonSelf reports whether an identity is this very process. The JSON gateway runs inside the daemon and re-dials it locally, so this is what distinguishes the gateway from any other caller, whatever user the daemon runs as.

func IsPrivilegedCaller

func IsPrivilegedCaller(id Identity) bool

IsPrivilegedCaller reports whether an identity may make the changes the daemon restricts to the platform administrator. This is the daemon's own rule and cannot be evaluated by a client, which does not know what the daemon runs as.

Beyond root/administrator it accepts a caller running as the daemon's own identity when the daemon is itself unprivileged. That keeps a rootless container working, where there is no uid 0 at all, and a Windows daemon in netstack mode, which needs no administrator rights. In those setups a caller sharing the daemon's identity can already rewrite the config files it reads and replace the binary it runs, so refusing it a config change would protect nothing; and an unprivileged daemon cannot hand out a root shell in the first place.

func IsReservedForwardKey

func IsReservedForwardKey(key string) bool

IsReservedForwardKey reports whether a gRPC metadata key belongs to the gateway's identity forwarding, and therefore must be dropped when it arrives from outside.

grpc-gateway maps "Grpc-Metadata-<key>" request headers into gRPC metadata and joins them ahead of the values its own annotators add. Without dropping these, an HTTP client could hand the daemon "x-netbird-fwd-uid: 0" and be believed, because the daemon trusts forwarded metadata when the transport peer is the (privileged) gateway.

func NewTransportCredentials

func NewTransportCredentials() credentials.TransportCredentials

NewTransportCredentials returns gRPC transport credentials that expose the caller's kernel-authenticated identity via IdentityFromContext. It returns nil on platforms that have no peer-identity primitive, which the caller must treat as "authorization cannot be enforced".

The handshake exchanges no bytes on the wire, so a client dialing with insecure credentials interoperates with a server using these. That keeps older CLI and UI binaries working against an upgraded daemon.

func OpenOwnedFile added in v0.76.1

func OpenOwnedFile(id Identity, path string) (*os.File, error)

OpenOwnedFile opens path for reading on behalf of the IPC caller identified by id, and fails unless the opened file is a regular file that id owns.

It exists for the paths a local caller hands to the daemon over the IPC. The daemon runs as root, so opening such a path unchecked lets any local user read any file through it. Ownership is the invariant that keeps the daemon from reading, with its own privileges, a file the caller could not read itself: a symlink or hard link planted at the path resolves to a file someone else owns and is refused.

The check is made against the open descriptor rather than the path, so swapping the path between the check and the read cannot change the answer.

A privileged caller is exempt: it can read the file directly, so refusing it here would protect nothing. The regular-file requirement still applies to everyone, since a fifo or device planted at the path is never a log file.

func PrivilegedActor

func PrivilegedActor() string

PrivilegedActor names the principal a privileged operation requires, for use in messages shown to the user.

func UpCommand

func UpCommand(flags string) string

UpCommand renders an elevated `netbird up` with the given flags, preceded by a `down`. The down is what makes the command work on a connected client: `netbird up` prints "Already connected" and returns without applying any config flag, so on its own the command would appear to do nothing. It is a no-op, exit 0, when the client is not connected.

";" rather than "&&" so the line can be pasted into any of the shells a user might have: PowerShell 5.1, still the default on Windows Server, rejects "&&" as a syntax error.

func WithForwardedIdentity

func WithForwardedIdentity(ctx context.Context, id Identity, known bool) context.Context

WithForwardedIdentity stamps id onto a context's outgoing metadata for the JSON gateway's call to the daemon, replacing any forwarding keys already present so values supplied from outside cannot survive alongside it.

This is deliberately not done with runtime.WithMetadata: grpc-gateway skips its annotators entirely when no request header maps to metadata ("if len(pairs) == 0 { return ctx, nil, nil }", runtime/context.go), which an HTTP/1.0 request with no Host header over a unix socket achieves. The daemon would then see an unmarked call whose transport peer is the daemon's own identity, and authorize it as the daemon. A client interceptor runs for every RPC regardless of headers.

Types

type AuthInfo

type AuthInfo struct {
	credentials.CommonAuthInfo
	Identity Identity
}

AuthInfo carries the peer Identity as a gRPC credentials.AuthInfo so handlers can retrieve it from the request context via IdentityFromContext.

func (AuthInfo) AuthType

func (AuthInfo) AuthType() string

AuthType identifies the authentication scheme.

type Identity

type Identity struct {
	// UID and GID are the caller's Unix user ID and primary group ID. Both are
	// zero on Windows, where SID is authoritative instead.
	UID uint32
	GID uint32

	// SID is the caller's Windows security identifier, empty on Unix.
	SID string

	// Groups holds the caller's Windows group SIDs, captured from the client
	// token at handshake time. Only groups that are enabled and not
	// deny-only are captured, so a group listed here is one the caller can
	// actually exercise. Empty on Unix.
	Groups []string

	// Elevated reports whether the Windows client token is elevated (running
	// as administrator, or an administrator with UAC turned off). Always false
	// on Unix, where privilege is uid 0.
	Elevated bool

	// PID is the caller's process ID where the platform reports it (Linux's
	// SO_PEERCRED), and 0 where it does not. It identifies the daemon's own
	// process dialling itself, which is what the JSON gateway does, and is never
	// used to grant anything.
	PID int32
}

Identity is the kernel-authenticated identity of a local IPC caller. The zero value is not a valid identity: consumers must only use one obtained with a true ok/nil error return.

func CallerIdentity

func CallerIdentity(ctx context.Context) (Identity, bool)

CallerIdentity returns the identity to authorize a request against. For a direct connection that is the transport peer's kernel identity. For a request relayed by the local JSON gateway it is the identity the gateway forwarded, since the transport peer is then the daemon itself.

A forwarded identity is only honoured when the transport peer is the daemon's own identity and the metadata carries this process's forwarding proof, so forged forwarding metadata gains a caller nothing. A forwarded request that carries no identity is reported as unidentified, never as the daemon.

The second return value is false when no identity could be established, and callers MUST fail closed in that case.

func ConnIdentity

func ConnIdentity(conn net.Conn) (Identity, error)

ConnIdentity extracts the caller's identity from an accepted local IPC connection. It is shared by the gRPC transport credentials and by the JSON gateway, which reads the identity of its own HTTP clients.

func CurrentProcessIdentity

func CurrentProcessIdentity() (Identity, error)

CurrentProcessIdentity returns this process's identity as the daemon would see it if this process connected to the local IPC. It lets a client (the UI) decide up front whether a privileged operation can succeed, without a round-trip and without duplicating the rules: the answer comes from the same Identity.IsPrivileged the daemon applies.

func IdentityFromContext

func IdentityFromContext(ctx context.Context) (Identity, bool)

IdentityFromContext extracts the caller's kernel-authenticated identity from the gRPC peer context. The second return value is false when no IPC transport credentials were negotiated, which happens on a TCP daemon socket and on platforms without a peer-identity primitive. Callers MUST fail closed in that case.

func PeerIdentity

func PeerIdentity(conn net.Conn) (Identity, error)

PeerIdentity reads the kernel-authenticated identity of the process on the other end of a Unix socket via SO_PEERCRED. The credentials are recorded by the kernel at connect() time and cannot be changed for the life of the connection, so they are not spoofable by the caller.

func SelfDelegatesTo

func SelfDelegatesTo() (Identity, bool)

SelfDelegatesTo returns the identity this process delegates its authority to, and whether it delegates at all. Only an unprivileged daemon does: see IsPrivilegedCaller. It exists so a refusal can name who may actually perform the operation, because on such a host root is neither required nor necessarily available.

func (Identity) IsPrivileged

func (i Identity) IsPrivileged() bool

IsPrivileged reports whether the caller is the platform's administrative principal, which is what the daemon requires for changes that cross the user-to-root boundary.

On Windows the decision comes from the caller's token rather than from account names or group RIDs: an elevated token, one of the service accounts the daemon itself may run as, or a token with BUILTIN\Administrators enabled. A UAC-filtered administrator has that group marked deny-only, and deny-only groups are dropped when the identity is captured, so such a caller is correctly reported as unprivileged. Domain group memberships (Domain Admins and friends) are deliberately not consulted: they say nothing about what this token may do on this machine.

func (Identity) IsWindows

func (i Identity) IsWindows() bool

IsWindows reports whether this identity is a Windows principal (SID-based) rather than a Unix uid/gid principal.

func (Identity) String

func (i Identity) String() string

String renders the identity for audit logs and denial messages.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL