Files
netbird/client/internal/certproof/collect.go
T

80 lines
2.4 KiB
Go

package certproof
import (
"context"
"crypto/sha256"
"time"
log "github.com/sirupsen/logrus"
"github.com/netbirdio/netbird/shared/management/certposture"
"github.com/netbirdio/netbird/shared/management/proto"
)
// 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 Collect(ctx context.Context, store Store, checks []*proto.Checks, peerKey []byte) []certposture.Proof {
challenges := certificateChallenges(checks)
if len(challenges) == 0 {
return nil
}
candidates, err := store.Candidates(ctx)
if err != nil {
log.Warnf("failed loading certificates for posture checks: %v", err)
return nil
}
now := time.Now()
proven := make(map[[sha256.Size]byte]struct{})
var proofs []certposture.Proof
for _, challenge := range challenges {
roots, err := certposture.ParseCAs(challenge.GetCaCertificates())
if err != nil {
log.Warnf("skipping certificate challenge with invalid CA certificates: %v", err)
continue
}
for _, candidate := range candidates {
if certposture.VerifyChain(candidate.Chain, roots, now) != nil {
continue
}
fingerprint := sha256.Sum256(candidate.Chain[0].Raw)
if _, done := proven[fingerprint]; done {
break
}
proof, err := prove(candidate, challenge.GetNonce(), peerKey)
if err != nil {
log.Warnf("failed signing certificate proof for %s: %v", candidate.Chain[0].Subject, err)
continue
}
proven[fingerprint] = struct{}{}
proofs = append(proofs, proof)
break
}
}
return proofs
}
func certificateChallenges(checks []*proto.Checks) []*proto.CertificateChallenge {
var challenges []*proto.CertificateChallenge
for _, check := range checks {
if challenge := check.GetCertificateChallenge(); challenge != nil && len(challenge.GetNonce()) > 0 {
challenges = append(challenges, challenge)
}
}
return challenges
}
func prove(candidate Candidate, nonce, peerKey []byte) (certposture.Proof, error) {
sigAlg, sig, err := certposture.Sign(candidate.Signer, nonce, peerKey)
if err != nil {
return certposture.Proof{}, err
}
chain := make([][]byte, 0, len(candidate.Chain))
for _, cert := range candidate.Chain {
chain = append(chain, cert.Raw)
}
return certposture.Proof{Nonce: nonce, Chain: chain, SigAlg: sigAlg, Signature: sig}, nil
}