certproof

package
v0.81.2 Latest Latest
Warning

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

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

Documentation

Overview

Package certproof answers certificate posture challenges: it finds the certificates a peer holds a private key for and signs the challenge nonce, bound to the peer's WireGuard key, with that key. Management verifies the signature and the chain against the CAs of the check, so only a holder of the key passes and a proof made for one peer cannot be replayed by another. Keys in the OS stores, the TPM and a PKCS#11 token are used through the platform, which signs; a plain PEM key file is the exception, parsed and used in the daemon's memory.

Machine stores are read by the daemon itself: the Windows LocalMachine store, the macOS System keychain, and on Linux a directory of PEM files, TSS2 key files the TPM signs with, and a PKCS#11 token. On Linux the directory and the token URI come from the daemon's environment (NB_CERT_STORE_DIR, NB_CERT_PKCS11_URI), as does the token PIN (NB_TPM_PIN); none of them is read from the profile config.

User stores cannot be read by a privileged daemon, so it starts this binary as "netbird posture cert-proof" inside the user's session and receives only signatures and chains on its stdout. On macOS uid is not what unlocks a login keychain, the session's bootstrap namespace is, which is why the helper enters it through launchctl asuser. On Windows a service opening CURRENT_USER silently reads its own empty hive, so the helper runs with the session's token instead. Its output is untrusted: sizes are capped and only proofs for requested nonces are kept.

A user proof shows that some process in that user's session could use a key whose certificate chains to the CA. It does not show which binary signed, or that the key is hardware-bound; device stores are the stronger signal.

Index

Constants

View Source
const (
	// PINEnv carries the user PIN of the PKCS#11 token. It is read from the daemon's
	// environment only, so the PIN never lands in the profile config or on a command line.
	PINEnv = "NB_TPM_PIN"
	// PKCS11URIEnv carries the RFC 7512 URI of the token. Like the PIN it lives in the
	// daemon's environment rather than the profile config, since it may carry pin-value.
	PKCS11URIEnv = "NB_CERT_PKCS11_URI"
)
View Source
const (
	StoreDirEnv = "NB_CERT_STORE_DIR"
)

Variables

This section is empty.

Functions

func Collect

func Collect(ctx context.Context, store Store, checks []*proto.Checks, peerKey []byte) []certposture.Proof

Collect answers the certificate challenges in checks: for each challenge it picks a stored certificate that chains to the challenge's CAs and signs the nonce with its key. The same certificate is proven once even if several checks accept it.

func CollectChallenges

func CollectChallenges(ctx context.Context, store Store, challenges []*proto.CertificateChallenge, peerKey []byte) []certposture.Proof

CollectChallenges answers challenges already extracted from the posture checks, so a caller that ships them across a process boundary reuses the same matching and signing. Only nonces of the size management issues are signed, for a peer key of the size of ours, so the keys behind the store never sign arbitrary caller-chosen data.

func CollectProofs

func CollectProofs(ctx context.Context, checks []*proto.Checks, peerKey []byte, cfg Config) []certposture.Proof

CollectProofs answers the certificate challenges in checks from the PEM directory cfg names, joined by its PKCS#11 token when it names one. Only macOS and Windows keep per-user certificates out of reach of a privileged daemon, so every other platform reads its store in the daemon itself.

func HasChallenges

func HasChallenges(checks []*proto.Checks) bool

HasChallenges reports whether any of checks asks for a certificate proof.

func RunHelper

func RunHelper(ctx context.Context, in io.Reader, out io.Writer) error

RunHelper answers the challenges on in from the store of the user running this process and writes the proofs to out. It is the child half of the console user lookup: the daemon cannot read a login keychain, so it launches this in the user's session instead. Only the signature crosses back, never the private key.

func StoreDir

func StoreDir() string

func UserContext

func UserContext(Config) string

UserContext identifies the user whose certificates a collection would include. These platforms have no per-user store, so it never changes.

Types

type Candidate

type Candidate struct {
	Chain         []*x509.Certificate
	Signer        crypto.Signer
	Intermediates []*x509.Certificate
}

Candidate is a certificate chain the peer can sign for. Signer never exposes the key. Chain is leaf first. Intermediates holds every other certificate the store has, so a path to a challenge's CAs can be found even where Chain followed a different issuer, such as an expired copy of a renewed intermediate.

type Collector

type Collector struct {
	// contains filtered or unexported fields
}

Collector runs CollectProofs with a deadline and, in the normal case, one collection at a time in the process. Token, TPM and keychain calls cannot be interrupted, and on Linux there is not even a process to kill, so a collection that overruns is abandoned rather than awaited. A single wedged call would then refuse every later collection for the life of the daemon, so once an abandoned collection has been running for lostAfter deadlines another may start beside it, up to maxInFlight. The zero value is ready to use.

func (*Collector) Collect

func (c *Collector) Collect(ctx context.Context, checks []*proto.Checks, peerKey []byte, cfg Config) []certposture.Proof

Collect answers the certificate challenges in checks, returning no proofs when there are no challenges, when previous collections still hold the slots, or when this one does not finish in time. Missing proofs fail the certificate check on management.

func (*Collector) Stuck

func (c *Collector) Stuck() bool

Stuck reports whether lost collections hold every slot. No collection starts again until one of them returns, which for a store that never answers means a restart.

type Config

type Config struct {
	PKCS11       PKCS11Config
	ProfileOwner string
	OwnerUnknown bool
}

Config selects where the daemon looks for certificates. PKCS11 names a token whose keys sign for certificates on the token or in the PEM directory, which NB_CERT_STORE_DIR names on Linux, /etc/netbird/certs by default.

ProfileOwner is the OS account the active profile belongs to. On macOS and Windows only that account's certificate store is consulted for user certificates, so on a machine with several people signed in the result does not depend on who else is logged in. Empty means the profile has no owner, and only the user at the physical console counts. OwnerUnknown means the owner could not be determined, and no user store is consulted: guessing would let whoever sits at the console answer for the profile.

type HelperChallenge

type HelperChallenge struct {
	Nonce          []byte   `json:"nonce"`
	CACertificates []string `json:"caCertificates"`
}

HelperChallenge is one certificate challenge in the form the helper needs.

type HelperRequest

type HelperRequest struct {
	PeerKey    []byte            `json:"peerKey"`
	Challenges []HelperChallenge `json:"challenges"`
}

HelperRequest is the work the daemon hands to a helper running in a user session. The peer key binds every signature to this machine, so a proof cannot be replayed onto another peer.

type HelperResponse

type HelperResponse struct {
	Proofs []certposture.Proof `json:"proofs"`
}

HelperResponse carries the proofs the helper produced from its own keychain.

type PKCS11Config

type PKCS11Config struct {
	URI string
	PIN string
}

PKCS11Config names the token whose certificates the store yields. URI is an RFC 7512 PKCS#11 URI, or empty for the first token the p11-kit proxy exposes. PIN is the user PIN, and takes precedence over a pin-value or pin-source the URI carries.

func PKCS11FromEnv

func PKCS11FromEnv() PKCS11Config

PKCS11FromEnv returns the token named by NB_CERT_PKCS11_URI with the PIN set in NB_TPM_PIN, either empty when unset.

type Store

type Store interface {
	Candidates(ctx context.Context) ([]Candidate, error)
}

Store yields the certificates a peer may prove possession of. fileStore is the PEM directory implementation; OS keystores (CNG, Keychain, PKCS#11) slot in here.

func DefaultStore

func DefaultStore() Store

DefaultStore is the PEM directory named by NB_CERT_STORE_DIR, or /etc/netbird/certs.

type Stores

type Stores []Store

Stores queries several stores and carries on when one fails, so a broken token cannot hide the certificates a directory holds. A failure is logged instead of returned because Collect treats a store error as "no proofs at all".

func (Stores) Candidates

func (s Stores) Candidates(ctx context.Context) ([]Candidate, error)

Jump to

Keyboard shortcuts

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