mirror of
https://github.com/gogrlx/nats-server.git
synced 2026-04-02 03:38:42 -07:00
OCSP Peer Verification (#4258)
New security feature [ADR-38: OCSP Peer Verification](https://github.com/nats-io/nats-architecture-and-design/pull/226/files#diff-575a9545de9d498a48d2889972b0cb57dbadebde3b4328b65ab02bb43f557935) providing fine-grain certificate status check via OCSP verification; for inbound NATS, MQTT, WebSocket, and Leaf client connections (mTLS) as well as outbound Leaf connections to another NATS System.
This commit is contained in:
105
server/events.go
105
server/events.go
@@ -17,6 +17,7 @@ import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -30,7 +31,9 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/klauspost/compress/s2"
|
||||
|
||||
"github.com/nats-io/jwt/v2"
|
||||
"github.com/nats-io/nats-server/v2/server/certidp"
|
||||
"github.com/nats-io/nats-server/v2/server/pse"
|
||||
)
|
||||
|
||||
@@ -84,6 +87,9 @@ const (
|
||||
|
||||
accReqTokens = 5
|
||||
accReqAccIndex = 3
|
||||
|
||||
ocspPeerRejectEventSubj = "$SYS.SERVER.%s.OCSP.PEER.CONN.REJECT"
|
||||
ocspPeerChainlinkInvalidEventSubj = "$SYS.SERVER.%s.OCSP.PEER.LINK.INVALID"
|
||||
)
|
||||
|
||||
// FIXME(dlc) - make configurable.
|
||||
@@ -157,6 +163,34 @@ type DisconnectEventMsg struct {
|
||||
// DisconnectEventMsgType is the schema type for DisconnectEventMsg
|
||||
const DisconnectEventMsgType = "io.nats.server.advisory.v1.client_disconnect"
|
||||
|
||||
// OCSPPeerRejectEventMsg is sent when a peer TLS handshake is ultimately rejected due to OCSP invalidation.
|
||||
// A "peer" can be an inbound client connection or a leaf connection to a remote server. Peer in event payload
|
||||
// is always the peer's (TLS) leaf cert, which may or may be the invalid cert (See also OCSPPeerChainlinkInvalidEventMsg)
|
||||
type OCSPPeerRejectEventMsg struct {
|
||||
TypedEvent
|
||||
Kind string `json:"kind"`
|
||||
Peer certidp.CertInfo `json:"peer"`
|
||||
Server ServerInfo `json:"server"`
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
|
||||
// OCSPPeerRejectEventMsgType is the schema type for OCSPPeerRejectEventMsg
|
||||
const OCSPPeerRejectEventMsgType = "io.nats.server.advisory.v1.ocsp_peer_reject"
|
||||
|
||||
// OCSPPeerChainlinkInvalidEventMsg is sent when a certificate (link) in a valid TLS chain is found to be OCSP invalid
|
||||
// during a peer TLS handshake. A "peer" can be an inbound client connection or a leaf connection to a remote server.
|
||||
// Peer and Link may be the same if the invalid cert was the peer's leaf cert
|
||||
type OCSPPeerChainlinkInvalidEventMsg struct {
|
||||
TypedEvent
|
||||
Link certidp.CertInfo `json:"link"`
|
||||
Peer certidp.CertInfo `json:"peer"`
|
||||
Server ServerInfo `json:"server"`
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
|
||||
// OCSPPeerChainlinkInvalidEventMsgType is the schema type for OCSPPeerChainlinkInvalidEventMsg
|
||||
const OCSPPeerChainlinkInvalidEventMsgType = "io.nats.server.advisory.v1.ocsp_peer_link_invalid"
|
||||
|
||||
// AccountNumConns is an event that will be sent from a server that is tracking
|
||||
// a given account when the number of connections changes. It will also HB
|
||||
// updates in the absence of any changes.
|
||||
@@ -2719,3 +2753,74 @@ func (s *Server) wrapChk(f func()) func() {
|
||||
s.mu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// sendOCSPPeerRejectEvent sends a system level event to system account when a peer connection is
|
||||
// rejected due to OCSP invalid status of its trust chain(s).
|
||||
func (s *Server) sendOCSPPeerRejectEvent(kind string, peer *x509.Certificate, reason string) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if !s.eventsEnabled() {
|
||||
return
|
||||
}
|
||||
if peer == nil {
|
||||
s.Errorf(certidp.ErrPeerEmptyNoEvent)
|
||||
return
|
||||
}
|
||||
eid := s.nextEventID()
|
||||
now := time.Now().UTC()
|
||||
m := OCSPPeerRejectEventMsg{
|
||||
TypedEvent: TypedEvent{
|
||||
Type: OCSPPeerRejectEventMsgType,
|
||||
ID: eid,
|
||||
Time: now,
|
||||
},
|
||||
Kind: kind,
|
||||
Peer: certidp.CertInfo{
|
||||
Subject: certidp.GetSubjectDNForm(peer),
|
||||
Issuer: certidp.GetIssuerDNForm(peer),
|
||||
Fingerprint: certidp.GenerateFingerprint(peer),
|
||||
Raw: peer.Raw,
|
||||
},
|
||||
Reason: reason,
|
||||
}
|
||||
subj := fmt.Sprintf(ocspPeerRejectEventSubj, s.info.ID)
|
||||
s.sendInternalMsg(subj, _EMPTY_, &m.Server, &m)
|
||||
}
|
||||
|
||||
// sendOCSPPeerChainlinkInvalidEvent sends a system level event to system account when a link in a peer's trust chain
|
||||
// is OCSP invalid.
|
||||
func (s *Server) sendOCSPPeerChainlinkInvalidEvent(peer *x509.Certificate, link *x509.Certificate, reason string) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if !s.eventsEnabled() {
|
||||
return
|
||||
}
|
||||
if peer == nil || link == nil {
|
||||
s.Errorf(certidp.ErrPeerEmptyNoEvent)
|
||||
return
|
||||
}
|
||||
eid := s.nextEventID()
|
||||
now := time.Now().UTC()
|
||||
m := OCSPPeerChainlinkInvalidEventMsg{
|
||||
TypedEvent: TypedEvent{
|
||||
Type: OCSPPeerChainlinkInvalidEventMsgType,
|
||||
ID: eid,
|
||||
Time: now,
|
||||
},
|
||||
Link: certidp.CertInfo{
|
||||
Subject: certidp.GetSubjectDNForm(link),
|
||||
Issuer: certidp.GetIssuerDNForm(link),
|
||||
Fingerprint: certidp.GenerateFingerprint(link),
|
||||
Raw: link.Raw,
|
||||
},
|
||||
Peer: certidp.CertInfo{
|
||||
Subject: certidp.GetSubjectDNForm(peer),
|
||||
Issuer: certidp.GetIssuerDNForm(peer),
|
||||
Fingerprint: certidp.GenerateFingerprint(peer),
|
||||
Raw: peer.Raw,
|
||||
},
|
||||
Reason: reason,
|
||||
}
|
||||
subj := fmt.Sprintf(ocspPeerChainlinkInvalidEventSubj, s.info.ID)
|
||||
s.sendInternalMsg(subj, _EMPTY_, &m.Server, &m)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user