mirror of
https://github.com/netbirdio/netbird.git
synced 2026-09-29 01:59:07 +02:00
implement certificate posture check
This commit is contained in:
@@ -426,6 +426,9 @@ func accountPostureChecks(id string, pc *nmdata.PostureChecks, publicID string)
|
||||
}
|
||||
out.Checks.ProcessCheck = &posture.ProcessCheck{Processes: procs}
|
||||
}
|
||||
if def.CertificateCheck != nil {
|
||||
out.Checks.CertificateCheck = &posture.CertificateCheck{CACertificates: def.CertificateCheck.CACertificates}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ const (
|
||||
GetPeersQuery = `
|
||||
select id, key, ssh_key, dns_label, extra_dns_labels, user_id, ssh_enabled, login_expiration_enabled, last_login, ip, ipv6,
|
||||
peer_status_requires_approval, peer_status_connected, proxy_meta_embedded, proxy_meta_cluster,
|
||||
meta_wt_version, meta_go_os, meta_os_version, meta_kernel_version, meta_network_addresses, meta_files, meta_capabilities, meta_flags, meta_sync_message_version,
|
||||
meta_wt_version, meta_go_os, meta_os_version, meta_kernel_version, meta_network_addresses, meta_files, meta_certificates, meta_capabilities, meta_flags, meta_sync_message_version,
|
||||
location_country_code, location_city_name, location_connection_ip
|
||||
from peers
|
||||
where account_id = $1
|
||||
|
||||
@@ -179,6 +179,7 @@ type Peer struct {
|
||||
MetaKernelVersion sql.NullString `nmap:"skip"`
|
||||
MetaNetworkAddresses []byte `nmap:"skip,json"`
|
||||
MetaFiles []byte `nmap:"skip,json"`
|
||||
MetaCertificates []byte `nmap:"skip,json"`
|
||||
MetaCapabilities []byte `nmap:"skip,json"`
|
||||
MetaFlags []byte `nmap:"skip,json"`
|
||||
MetaSyncMessageVersion sql.NullInt64 `nmap:"skip"`
|
||||
@@ -338,6 +339,12 @@ func ConvertToNmdataPeers(peers []Peer) ([]nmdata.Peer, map[string][]*nmdata.Pee
|
||||
return toret, nil, err
|
||||
}
|
||||
}
|
||||
if p.MetaCertificates != nil {
|
||||
err := json.Unmarshal(p.MetaCertificates, &dp.Meta.Certificates)
|
||||
if err != nil {
|
||||
return toret, nil, err
|
||||
}
|
||||
}
|
||||
if p.MetaCapabilities != nil {
|
||||
err := json.Unmarshal(p.MetaCapabilities, &dp.Meta.Capabilities)
|
||||
if err != nil {
|
||||
|
||||
@@ -11,7 +11,7 @@ const (
|
||||
GetPeersQuery = `
|
||||
select id, key, ssh_key, dns_label, extra_dns_labels, user_id, ssh_enabled, login_expiration_enabled, last_login, ip, ipv6,
|
||||
peer_status_requires_approval, peer_status_connected, proxy_meta_embedded, proxy_meta_cluster,
|
||||
meta_wt_version, meta_go_os, meta_os_version, meta_kernel_version, meta_network_addresses, meta_files, meta_capabilities, meta_flags, meta_sync_message_version,
|
||||
meta_wt_version, meta_go_os, meta_os_version, meta_kernel_version, meta_network_addresses, meta_files, meta_certificates, meta_capabilities, meta_flags, meta_sync_message_version,
|
||||
location_country_code, location_city_name, location_connection_ip
|
||||
from peers
|
||||
where account_id = ?
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
|
||||
"github.com/netbirdio/netbird/shared/management/certposture"
|
||||
"github.com/netbirdio/netbird/shared/management/proto"
|
||||
)
|
||||
|
||||
const certChallengeKeyDomain = "netbird-cert-challenge-key"
|
||||
|
||||
// certChallenger derives the nonce secret from the management WireGuard key so every
|
||||
// instance sharing that key issues and verifies the same nonces without extra state.
|
||||
func certChallenger(serverKey wgtypes.Key) *certposture.Challenger {
|
||||
h := sha256.New()
|
||||
h.Write([]byte(certChallengeKeyDomain))
|
||||
h.Write(serverKey[:])
|
||||
return certposture.NewChallenger(h.Sum(nil))
|
||||
}
|
||||
|
||||
// stampCertificateChallenges fills the per-peer nonce into every certificate challenge
|
||||
// right before the response is encrypted for that peer.
|
||||
func stampCertificateChallenges(checks []*proto.Checks, peerKey, serverKey wgtypes.Key) {
|
||||
var nonce []byte
|
||||
for _, check := range checks {
|
||||
challenge := check.GetCertificateChallenge()
|
||||
if challenge == nil {
|
||||
continue
|
||||
}
|
||||
if nonce == nil {
|
||||
nonce = certChallenger(serverKey).Nonce(peerKey[:], time.Now())
|
||||
}
|
||||
challenge.Nonce = nonce
|
||||
}
|
||||
}
|
||||
|
||||
// verifiedCertificates turns the peer's proofs into PEM chains for its meta. Possession
|
||||
// (nonce + signature) is verified here; trust against a check's CAs is evaluated by the
|
||||
// posture check itself. Any invalid proof rejects the whole set.
|
||||
func (s *Server) verifiedCertificates(ctx context.Context, peerKey wgtypes.Key, proofs []*proto.CertificateProof) []string {
|
||||
if len(proofs) == 0 {
|
||||
return nil
|
||||
}
|
||||
serverKey, err := s.secretsManager.GetWGKey()
|
||||
if err != nil {
|
||||
log.WithContext(ctx).Warnf("skipping certificate proofs of peer %s: %v", peerKey, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
challenger := certChallenger(serverKey)
|
||||
now := time.Now()
|
||||
chains := make([]string, 0, len(proofs))
|
||||
for _, p := range proofs {
|
||||
chain, err := challenger.Verify(certposture.Proof{
|
||||
Nonce: p.GetNonce(),
|
||||
Chain: p.GetChain(),
|
||||
SigAlg: p.GetSigAlg(),
|
||||
Signature: p.GetSignature(),
|
||||
}, peerKey[:], now)
|
||||
if err != nil {
|
||||
log.WithContext(ctx).Warnf("rejecting certificate proofs of peer %s: %v", peerKey, err)
|
||||
return nil
|
||||
}
|
||||
chains = append(chains, certposture.EncodeChainPEM(chain))
|
||||
}
|
||||
return chains
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
|
||||
"github.com/netbirdio/netbird/shared/management/certposture"
|
||||
"github.com/netbirdio/netbird/shared/management/certposture/certtest"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
"github.com/netbirdio/netbird/shared/management/proto"
|
||||
)
|
||||
|
||||
func TestCertificateChallenge_StampAndVerifyRoundTrip(t *testing.T) {
|
||||
serverKey := generateKey(t)
|
||||
peerKey := generateKey(t).PublicKey()
|
||||
ca := certtest.NewCA(t, "corp-root")
|
||||
ctx := context.Background()
|
||||
|
||||
checks := toProtocolChecks(ctx, []*nmdata.PostureChecks{{
|
||||
ID: "cert-check",
|
||||
Checks: nmdata.ChecksDefinition{CertificateCheck: &nmdata.CertificateCheck{CACertificates: []string{ca.PEM}}},
|
||||
}})
|
||||
require.Len(t, checks, 1)
|
||||
require.Equal(t, []string{ca.PEM}, checks[0].GetCertificateChallenge().GetCaCertificates())
|
||||
require.Empty(t, checks[0].GetCertificateChallenge().GetNonce())
|
||||
|
||||
stampCertificateChallenges(checks, peerKey, serverKey)
|
||||
nonce := checks[0].GetCertificateChallenge().GetNonce()
|
||||
require.NotEmpty(t, nonce)
|
||||
|
||||
deviceKey := certtest.ECDSAKey(t)
|
||||
leaf := ca.Issue(t, deviceKey, "device")
|
||||
sigAlg, sig, err := certposture.Sign(deviceKey, nonce, peerKey[:])
|
||||
require.NoError(t, err)
|
||||
proofs := []*proto.CertificateProof{{Nonce: nonce, Chain: [][]byte{leaf.Raw}, SigAlg: sigAlg, Signature: sig}}
|
||||
|
||||
s := &Server{secretsManager: &TimeBasedAuthSecretsManager{wgKey: serverKey}}
|
||||
|
||||
chains := s.verifiedCertificates(ctx, peerKey, proofs)
|
||||
require.Len(t, chains, 1)
|
||||
assert.True(t, certposture.ChainMatchesCAs(chains[0], []string{ca.PEM}, time.Now()))
|
||||
|
||||
t.Run("proof replayed by another peer is rejected", func(t *testing.T) {
|
||||
assert.Nil(t, s.verifiedCertificates(ctx, generateKey(t).PublicKey(), proofs))
|
||||
})
|
||||
t.Run("nonce from another management key is rejected", func(t *testing.T) {
|
||||
other := &Server{secretsManager: &TimeBasedAuthSecretsManager{wgKey: generateKey(t)}}
|
||||
assert.Nil(t, other.verifiedCertificates(ctx, peerKey, proofs))
|
||||
})
|
||||
t.Run("one invalid proof rejects the whole set", func(t *testing.T) {
|
||||
bad := &proto.CertificateProof{Nonce: nonce, Chain: [][]byte{leaf.Raw}, SigAlg: sigAlg, Signature: []byte("junk")}
|
||||
assert.Nil(t, s.verifiedCertificates(ctx, peerKey, append(proofs, bad)))
|
||||
})
|
||||
t.Run("no proofs yields no certificates", func(t *testing.T) {
|
||||
assert.Nil(t, s.verifiedCertificates(ctx, peerKey, nil))
|
||||
})
|
||||
}
|
||||
|
||||
func TestStampCertificateChallenges_SkipsFileOnlyChecks(t *testing.T) {
|
||||
checks := []*proto.Checks{{Files: []string{"/bin/agent"}}}
|
||||
stampCertificateChallenges(checks, generateKey(t).PublicKey(), generateKey(t))
|
||||
assert.Nil(t, checks[0].GetCertificateChallenge())
|
||||
}
|
||||
|
||||
func generateKey(t *testing.T) wgtypes.Key {
|
||||
t.Helper()
|
||||
key, err := wgtypes.GeneratePrivateKey()
|
||||
require.NoError(t, err)
|
||||
return key
|
||||
}
|
||||
@@ -246,6 +246,7 @@ func (s *Server) Sync(req *proto.EncryptedMessage, srv proto.ManagementService_S
|
||||
realIP := getRealIP(ctx)
|
||||
sRealIP := realIP.String()
|
||||
peerMeta := extractPeerMeta(ctx, syncReq.GetMeta())
|
||||
peerMeta.Certificates = s.verifiedCertificates(ctx, peerKey, syncReq.GetMeta().GetCertificateProofs())
|
||||
|
||||
userID, err := s.accountManager.GetUserIDByPeerKey(ctx, peerKey.String())
|
||||
if err != nil {
|
||||
@@ -481,6 +482,7 @@ func (s *Server) sendUpdate(ctx context.Context, accountID string, peerKey wgtyp
|
||||
return status.Errorf(codes.Internal, "failed processing update message")
|
||||
}
|
||||
|
||||
stampCertificateChallenges(update.Update.GetChecks(), peerKey, key)
|
||||
encryptedResp, err := encryption.EncryptMessage(peerKey, key, update.Update)
|
||||
if err != nil {
|
||||
s.cancelPeerRoutines(ctx, accountID, peer, streamStartTime)
|
||||
@@ -735,6 +737,7 @@ func (s *Server) Login(ctx context.Context, req *proto.EncryptedMessage) (*proto
|
||||
}
|
||||
|
||||
peerMeta := extractPeerMeta(ctx, loginReq.GetMeta())
|
||||
peerMeta.Certificates = s.verifiedCertificates(ctx, peerKey, loginReq.GetMeta().GetCertificateProofs())
|
||||
metahashed := metaHash(peerMeta)
|
||||
if !s.loginFilter.allowLogin(peerKey.String(), metahashed) {
|
||||
if s.logBlockedPeers {
|
||||
@@ -811,6 +814,7 @@ func (s *Server) Login(ctx context.Context, req *proto.EncryptedMessage) (*proto
|
||||
return nil, status.Errorf(codes.Internal, "failed logging in peer")
|
||||
}
|
||||
|
||||
stampCertificateChallenges(loginResp.Checks, peerKey, key)
|
||||
encryptedResp, err := encryption.EncryptMessage(peerKey, key, loginResp)
|
||||
if err != nil {
|
||||
log.WithContext(ctx).Warnf("failed encrypting peer %s message", peer.ID)
|
||||
@@ -1062,6 +1066,7 @@ func (s *Server) sendInitialSync(ctx context.Context, peerKey wgtypes.Key, peer
|
||||
return status.Errorf(codes.Internal, "failed getting server key")
|
||||
}
|
||||
|
||||
stampCertificateChallenges(plainResp.Checks, peerKey, key)
|
||||
encryptedResp, err := encryption.EncryptMessage(peerKey, key, plainResp)
|
||||
if err != nil {
|
||||
return status.Errorf(codes.Internal, "error handling request")
|
||||
@@ -1255,7 +1260,9 @@ func (s *Server) SyncMeta(ctx context.Context, req *proto.EncryptedMessage) (*pr
|
||||
return nil, msg
|
||||
}
|
||||
|
||||
err = s.accountManager.SyncPeerMeta(ctx, peerKey.String(), extractPeerMeta(ctx, syncMetaReq.GetMeta()), realIP)
|
||||
peerMeta := extractPeerMeta(ctx, syncMetaReq.GetMeta())
|
||||
peerMeta.Certificates = s.verifiedCertificates(ctx, peerKey, syncMetaReq.GetMeta().GetCertificateProofs())
|
||||
err = s.accountManager.SyncPeerMeta(ctx, peerKey.String(), peerMeta, realIP)
|
||||
if err != nil {
|
||||
return nil, mapError(ctx, err)
|
||||
}
|
||||
@@ -1331,7 +1338,11 @@ func toProtocolCheck(postureCheck *nmdata.PostureChecks) *proto.Checks {
|
||||
}
|
||||
}
|
||||
|
||||
if len(protoCheck.Files) == 0 {
|
||||
if check := postureCheck.Checks.CertificateCheck; check != nil {
|
||||
protoCheck.CertificateChallenge = &proto.CertificateChallenge{CaCertificates: check.CACertificates}
|
||||
}
|
||||
|
||||
if len(protoCheck.Files) == 0 && protoCheck.CertificateChallenge == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -173,6 +173,7 @@ type PeerSystemMeta struct { //nolint:revive
|
||||
Environment Environment `gorm:"serializer:json"`
|
||||
Flags Flags `gorm:"serializer:json"`
|
||||
Files []File `gorm:"serializer:json"`
|
||||
Certificates []string `gorm:"serializer:json"`
|
||||
Capabilities []int32 `gorm:"serializer:json"`
|
||||
SyncMessageVersion int
|
||||
}
|
||||
@@ -198,7 +199,8 @@ func (p PeerSystemMeta) isEmpty() bool {
|
||||
p.SystemManufacturer == "" &&
|
||||
p.Environment.Cloud == "" &&
|
||||
p.Environment.Platform == "" &&
|
||||
len(p.Files) == 0
|
||||
len(p.Files) == 0 &&
|
||||
len(p.Certificates) == 0
|
||||
}
|
||||
|
||||
// AddedWithSSOLogin indicates whether this peer has been added with an SSO login by a user.
|
||||
@@ -417,6 +419,9 @@ func diffMeta(oldMeta, newMeta PeerSystemMeta, oldLocation, newLocation Location
|
||||
if !sameMultiset(oldMeta.Files, newMeta.Files) {
|
||||
add("files", fmt.Sprintf("%v", oldMeta.Files), fmt.Sprintf("%v", newMeta.Files))
|
||||
}
|
||||
if !sameMultiset(oldMeta.Certificates, newMeta.Certificates) {
|
||||
add("certificates", len(oldMeta.Certificates), len(newMeta.Certificates))
|
||||
}
|
||||
if oldMeta.SyncMessageVersion != newMeta.SyncMessageVersion {
|
||||
add("sync_meta_version", fmt.Sprintf("%d", oldMeta.SyncMessageVersion), fmt.Sprintf("%d", newMeta.SyncMessageVersion))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package posture
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
||||
"github.com/netbirdio/netbird/shared/management/certposture"
|
||||
)
|
||||
|
||||
// CertificateCheck passes when the peer holds a certificate, proven at meta ingestion,
|
||||
// that chains to one of the configured PEM encoded CA certificates.
|
||||
type CertificateCheck struct {
|
||||
CACertificates []string
|
||||
}
|
||||
|
||||
var _ Check = (*CertificateCheck)(nil)
|
||||
|
||||
func (c *CertificateCheck) Check(_ context.Context, peer nbpeer.Peer) (bool, error) {
|
||||
return certposture.AnyChainMatchesCAs(peer.Meta.Certificates, c.CACertificates, time.Now()), nil
|
||||
}
|
||||
|
||||
func (c *CertificateCheck) Name() string {
|
||||
return CertificateCheckName
|
||||
}
|
||||
|
||||
func (c *CertificateCheck) Validate() error {
|
||||
if len(c.CACertificates) == 0 {
|
||||
return fmt.Errorf("%s ca certificates shouldn't be empty", c.Name())
|
||||
}
|
||||
if _, err := certposture.ParseCAs(c.CACertificates); err != nil {
|
||||
return fmt.Errorf("%s: %w", c.Name(), err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package posture
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server/peer"
|
||||
"github.com/netbirdio/netbird/shared/management/certposture"
|
||||
"github.com/netbirdio/netbird/shared/management/certposture/certtest"
|
||||
)
|
||||
|
||||
func TestCertificateCheck_Check(t *testing.T) {
|
||||
ca := certtest.NewCA(t, "corp-root")
|
||||
otherCA := certtest.NewCA(t, "other-root")
|
||||
chain := certposture.EncodeChainPEM([]*x509.Certificate{ca.Issue(t, certtest.ECDSAKey(t), "device")})
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
certificates []string
|
||||
cas []string
|
||||
want bool
|
||||
}{
|
||||
{"chains to configured CA", []string{chain}, []string{ca.PEM}, true},
|
||||
{"one of several CAs matches", []string{chain}, []string{otherCA.PEM, ca.PEM}, true},
|
||||
{"unrelated CA", []string{chain}, []string{otherCA.PEM}, false},
|
||||
{"no certificates proven", nil, []string{ca.PEM}, false},
|
||||
{"garbage entry does not hide a valid one", []string{"garbage", chain}, []string{ca.PEM}, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
check := CertificateCheck{CACertificates: tt.cas}
|
||||
got, err := check.Check(context.Background(), peer.Peer{Meta: peer.PeerSystemMeta{Certificates: tt.certificates}})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCertificateCheck_Validate(t *testing.T) {
|
||||
ca := certtest.NewCA(t, "corp-root")
|
||||
|
||||
assert.Error(t, (&CertificateCheck{}).Validate())
|
||||
assert.Error(t, (&CertificateCheck{CACertificates: []string{"not a pem"}}).Validate())
|
||||
assert.NoError(t, (&CertificateCheck{CACertificates: []string{ca.PEM}}).Validate())
|
||||
}
|
||||
|
||||
func TestChecks_CertificateCheckRegistered(t *testing.T) {
|
||||
ca := certtest.NewCA(t, "corp-root")
|
||||
checks := &Checks{Name: "cert", Checks: ChecksDefinition{CertificateCheck: &CertificateCheck{CACertificates: []string{ca.PEM}}}}
|
||||
|
||||
require.NoError(t, checks.Validate())
|
||||
require.Len(t, checks.GetChecks(), 1)
|
||||
assert.Equal(t, CertificateCheckName, checks.GetChecks()[0].Name())
|
||||
|
||||
copied := checks.Copy()
|
||||
checks.Checks.CertificateCheck.CACertificates[0] = "mutated"
|
||||
assert.Equal(t, ca.PEM, copied.Checks.CertificateCheck.CACertificates[0])
|
||||
|
||||
api := checks.ToAPIResponse()
|
||||
require.NotNil(t, api.Checks.CertificateCheck)
|
||||
roundTrip, err := NewChecksFromAPIPostureCheck(*api)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, checks.Checks.CertificateCheck.CACertificates, roundTrip.Checks.CertificateCheck.CACertificates)
|
||||
}
|
||||
@@ -19,6 +19,7 @@ const (
|
||||
GeoLocationCheckName = "GeoLocationCheck"
|
||||
PeerNetworkRangeCheckName = "PeerNetworkRangeCheck"
|
||||
ProcessCheckName = "ProcessCheck"
|
||||
CertificateCheckName = "CertificateCheck"
|
||||
|
||||
CheckActionAllow string = "allow"
|
||||
CheckActionDeny string = "deny"
|
||||
@@ -61,6 +62,7 @@ type ChecksDefinition struct {
|
||||
GeoLocationCheck *GeoLocationCheck `json:",omitempty"`
|
||||
PeerNetworkRangeCheck *PeerNetworkRangeCheck `json:",omitempty"`
|
||||
ProcessCheck *ProcessCheck `json:",omitempty"`
|
||||
CertificateCheck *CertificateCheck `json:",omitempty"`
|
||||
}
|
||||
|
||||
// Copy returns a copy of a checks definition.
|
||||
@@ -113,6 +115,12 @@ func (cd ChecksDefinition) Copy() ChecksDefinition {
|
||||
}
|
||||
copy(cdCopy.ProcessCheck.Processes, processCheck.Processes)
|
||||
}
|
||||
if cd.CertificateCheck != nil {
|
||||
cdCopy.CertificateCheck = &CertificateCheck{
|
||||
CACertificates: make([]string, len(cd.CertificateCheck.CACertificates)),
|
||||
}
|
||||
copy(cdCopy.CertificateCheck.CACertificates, cd.CertificateCheck.CACertificates)
|
||||
}
|
||||
return cdCopy
|
||||
}
|
||||
|
||||
@@ -157,6 +165,9 @@ func (pc *Checks) GetChecks() []Check {
|
||||
if pc.Checks.ProcessCheck != nil {
|
||||
checks = append(checks, pc.Checks.ProcessCheck)
|
||||
}
|
||||
if pc.Checks.CertificateCheck != nil {
|
||||
checks = append(checks, pc.Checks.CertificateCheck)
|
||||
}
|
||||
return checks
|
||||
}
|
||||
|
||||
@@ -212,6 +223,10 @@ func buildPostureCheck(postureChecksID string, name string, description string,
|
||||
postureChecks.Checks.ProcessCheck = toProcessCheck(processCheck)
|
||||
}
|
||||
|
||||
if certificateCheck := checks.CertificateCheck; certificateCheck != nil {
|
||||
postureChecks.Checks.CertificateCheck = &CertificateCheck{CACertificates: certificateCheck.CaCertificates}
|
||||
}
|
||||
|
||||
return &postureChecks, nil
|
||||
}
|
||||
|
||||
@@ -246,6 +261,10 @@ func (pc *Checks) ToAPIResponse() *api.PostureCheck {
|
||||
checks.ProcessCheck = toProcessCheckResponse(pc.Checks.ProcessCheck)
|
||||
}
|
||||
|
||||
if pc.Checks.CertificateCheck != nil {
|
||||
checks.CertificateCheck = &api.CertificateCheck{CaCertificates: pc.Checks.CertificateCheck.CACertificates}
|
||||
}
|
||||
|
||||
return &api.PostureCheck{
|
||||
Id: pc.ID,
|
||||
Name: pc.Name,
|
||||
|
||||
@@ -1896,7 +1896,7 @@ func (s *SqlStore) getPeers(ctx context.Context, accountID string) ([]nbpeer.Pee
|
||||
inactivity_expiration_enabled, last_login, created_at, ephemeral, extra_dns_labels, allow_extra_dns_labels, meta_hostname,
|
||||
meta_go_os, meta_kernel, meta_core, meta_platform, meta_os, meta_os_version, meta_wt_version, meta_ui_version,
|
||||
meta_kernel_version, meta_network_addresses, meta_system_serial_number, meta_system_product_name, meta_system_manufacturer,
|
||||
meta_environment, meta_flags, meta_files, meta_capabilities, peer_status_last_seen, peer_status_session_started_at,
|
||||
meta_environment, meta_flags, meta_files, meta_certificates, meta_capabilities, peer_status_last_seen, peer_status_session_started_at,
|
||||
peer_status_connected, peer_status_login_expired, peer_status_requires_approval, location_connection_ip,
|
||||
location_country_code, location_city_name, location_geo_name_id, proxy_meta_embedded, proxy_meta_cluster, ipv6, meta_sync_message_version
|
||||
FROM peers WHERE account_id = $1`
|
||||
@@ -1914,7 +1914,7 @@ func (s *SqlStore) getPeers(ctx context.Context, accountID string) ([]nbpeer.Pee
|
||||
peerStatusLastSeen sql.NullTime
|
||||
peerStatusSessionStartedAt sql.NullInt64
|
||||
peerStatusConnected, peerStatusLoginExpired, peerStatusRequiresApproval, proxyEmbedded sql.NullBool
|
||||
ip, extraDNS, netAddr, env, flags, files, capabilities, connIP, ipv6 []byte
|
||||
ip, extraDNS, netAddr, env, flags, files, certificates, capabilities, connIP, ipv6 []byte
|
||||
metaHostname, metaGoOS, metaKernel, metaCore, metaPlatform sql.NullString
|
||||
metaOS, metaOSVersion, metaWtVersion, metaUIVersion, metaKernelVersion sql.NullString
|
||||
metaSystemSerialNumber, metaSystemProductName, metaSystemManufacturer sql.NullString
|
||||
@@ -1927,7 +1927,7 @@ func (s *SqlStore) getPeers(ctx context.Context, accountID string) ([]nbpeer.Pee
|
||||
&loginExpirationEnabled, &inactivityExpirationEnabled, &lastLogin, &createdAt, &ephemeral, &extraDNS,
|
||||
&allowExtraDNSLabels, &metaHostname, &metaGoOS, &metaKernel, &metaCore, &metaPlatform,
|
||||
&metaOS, &metaOSVersion, &metaWtVersion, &metaUIVersion, &metaKernelVersion, &netAddr,
|
||||
&metaSystemSerialNumber, &metaSystemProductName, &metaSystemManufacturer, &env, &flags, &files, &capabilities,
|
||||
&metaSystemSerialNumber, &metaSystemProductName, &metaSystemManufacturer, &env, &flags, &files, &certificates, &capabilities,
|
||||
&peerStatusLastSeen, &peerStatusSessionStartedAt, &peerStatusConnected, &peerStatusLoginExpired,
|
||||
&peerStatusRequiresApproval, &connIP, &locationCountryCode, &locationCityName, &locationGeoNameID,
|
||||
&proxyEmbedded, &proxyCluster, &ipv6, &metaSyncMessageVersion)
|
||||
@@ -2044,6 +2044,9 @@ func (s *SqlStore) getPeers(ctx context.Context, accountID string) ([]nbpeer.Pee
|
||||
if files != nil {
|
||||
_ = json.Unmarshal(files, &p.Meta.Files)
|
||||
}
|
||||
if certificates != nil {
|
||||
_ = json.Unmarshal(certificates, &p.Meta.Certificates)
|
||||
}
|
||||
if capabilities != nil {
|
||||
_ = json.Unmarshal(capabilities, &p.Meta.Capabilities)
|
||||
}
|
||||
|
||||
@@ -573,7 +573,7 @@ func TestSqlStore_SavePeer(t *testing.T) {
|
||||
|
||||
numOfFields, err := populateFields.PopulateAll(reflectedMetadata)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 32, numOfFields)
|
||||
assert.Equal(t, 33, numOfFields)
|
||||
|
||||
// save status of non-existing peer
|
||||
peer := &nbpeer.Peer{
|
||||
|
||||
@@ -194,6 +194,7 @@ func twinPeer(p *nbpeer.Peer) *nmdata.Peer {
|
||||
KernelVersion: p.Meta.KernelVersion,
|
||||
NetworkAddresses: networkAddresses,
|
||||
Files: files,
|
||||
Certificates: p.Meta.Certificates,
|
||||
Capabilities: p.Meta.Capabilities,
|
||||
SyncMessageVersion: p.Meta.SyncMessageVersion,
|
||||
Flags: nmdata.Flags{
|
||||
@@ -449,6 +450,9 @@ func TwinPostureChecks(pc *posture.Checks) *nmdata.PostureChecks {
|
||||
}
|
||||
out.Checks.ProcessCheck = &nmdata.ProcessCheck{Processes: procs}
|
||||
}
|
||||
if def.CertificateCheck != nil {
|
||||
out.Checks.CertificateCheck = &nmdata.CertificateCheck{CACertificates: def.CertificateCheck.CACertificates}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user