mirror of
https://github.com/netbirdio/netbird.git
synced 2026-09-12 17:59:06 +02:00
80 lines
2.4 KiB
Go
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
|
|
}
|