mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-10 23:49:09 +02:00
Both platform collectors wrapped the helper launch in a 30 second deadline, but the context they wrapped was already capped at 10 seconds by the collector that calls them, and CollectProofs has no other caller. The inner deadline could never be reached, so it described a budget the code does not have. Leave the collector as the single owner of the deadline rather than picking a smaller inner value: on macOS one budget has to cover the System keychain read and the helper launch that follows it, and how to divide it is a question about the budget as a whole, not about the helper alone.
103 lines
3.5 KiB
Go
103 lines
3.5 KiB
Go
package certproof
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"syscall"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
"golang.org/x/sys/windows"
|
|
|
|
"github.com/netbirdio/netbird/shared/management/certposture"
|
|
"github.com/netbirdio/netbird/shared/management/proto"
|
|
)
|
|
|
|
// CollectProofs answers the certificate challenges in checks from every store this
|
|
// machine can reach. The service reads the local machine store itself, where AD and
|
|
// Intune enrol device certificates, and reaches the signed-in user's store by launching
|
|
// a helper with that session's token. A machine at the sign-in screen therefore proves
|
|
// device certificates alone.
|
|
func CollectProofs(ctx context.Context, checks []*proto.Checks, peerKey []byte, cfg Config) []certposture.Proof {
|
|
challenges := certificateChallenges(checks)
|
|
if len(challenges) == 0 {
|
|
logNoChallenges(checks)
|
|
return nil
|
|
}
|
|
|
|
proofs := CollectChallenges(ctx, DefaultStore(), challenges, peerKey)
|
|
|
|
// The helper already runs as the signed-in user, and an ordinary process has no
|
|
// right to a session token, so only the service goes looking for one.
|
|
if !runningAsLocalSystem() {
|
|
return proofs
|
|
}
|
|
|
|
userProofs, err := collectAsDesktopUser(ctx, cfg.ProfileOwner, challenges, peerKey)
|
|
if err != nil {
|
|
log.Debugf("certificate posture: user certificate store unavailable: %v", err)
|
|
}
|
|
return mergeProofs(proofs, userProofs)
|
|
}
|
|
|
|
// UserContext identifies the session whose store a collection would include: a session
|
|
// of the profile owner, or empty when no user store would be asked. A change means a
|
|
// collection made earlier no longer reflects what this machine can prove.
|
|
func UserContext(cfg Config) string {
|
|
if !runningAsLocalSystem() {
|
|
return ""
|
|
}
|
|
user, ok := CurrentDesktopUser(cfg.ProfileOwner)
|
|
if !ok {
|
|
return ""
|
|
}
|
|
defer user.Close()
|
|
return fmt.Sprintf("%d:%s", user.Session, user.Name)
|
|
}
|
|
|
|
// helperStore is the store the helper reads. It runs as the signed-in user, so it wants
|
|
// that user's store rather than the machine store the service already read.
|
|
func helperStore() Store {
|
|
return NewUserStore()
|
|
}
|
|
|
|
// collectAsDesktopUser runs the helper inside the interactive session of the signed-in
|
|
// user. Unlike a keychain on macOS, a Windows service can assume a user identity
|
|
// directly, so the session token goes straight into the child process.
|
|
func collectAsDesktopUser(ctx context.Context, owner string, challenges []*proto.CertificateChallenge, peerKey []byte) ([]certposture.Proof, error) {
|
|
user, ok := CurrentDesktopUser(owner)
|
|
if !ok {
|
|
return nil, nil
|
|
}
|
|
defer user.Close()
|
|
|
|
binary, err := os.Executable()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("resolve own binary: %w", err)
|
|
}
|
|
|
|
// The user's own environment, not the service's: the service environment may carry
|
|
// secrets such as a setup key that the signed-in user must not be able to read.
|
|
env, err := user.Token.Environ(false)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("build environment of %s: %w", user.Name, err)
|
|
}
|
|
|
|
cmd := exec.CommandContext(ctx, binary, "posture", "cert-proof")
|
|
cmd.Env = env
|
|
cmd.SysProcAttr = &syscall.SysProcAttr{
|
|
Token: syscall.Token(user.Token),
|
|
HideWindow: true,
|
|
CreationFlags: windows.CREATE_NO_WINDOW,
|
|
}
|
|
|
|
log.Debugf("certificate posture: asking session %d to answer %d challenges", user.Session, len(challenges))
|
|
proofs, err := runHelperCmd(cmd, helperRequest(challenges, peerKey))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("run helper in session %d: %w", user.Session, err)
|
|
}
|
|
log.Debugf("certificate posture: session %d returned %d proofs", user.Session, len(proofs))
|
|
return proofs, nil
|
|
}
|