[added] jwt/issuerkey/nametag/tags to monitoring and event endpoints (#1830)

Also added a trace on jwt authentication

Signed-off-by: Matthias Hanel <mh@synadia.com>
This commit is contained in:
Matthias Hanel
2021-01-21 21:16:34 -05:00
committed by GitHub
parent 9081646109
commit d35cd2996d
7 changed files with 117 additions and 40 deletions

View File

@@ -82,6 +82,8 @@ type Account struct {
eventIds *nuid.NUID
eventIdsMu sync.Mutex
defaultPerms *Permissions
tags jwt.TagList
nameTag string
}
// Account based limits.
@@ -2813,6 +2815,10 @@ func (s *Server) updateAccountClaimsWithRefresh(a *Account, ac *jwt.AccountClaim
// Clone to update, only select certain fields.
old := &Account{Name: a.Name, exports: a.exports, limits: a.limits, signingKeys: a.signingKeys}
// overwrite claim meta data
a.nameTag = ac.Name
a.tags = ac.Tags
// Reset exports and imports here.
// Exports is creating a whole new map.

View File

@@ -627,13 +627,23 @@ func (s *Server) processClientOrLeafAuthentication(c *client, opts *Options) boo
return false
}
// Hold onto the user's public key.
c.mu.Lock()
c.pubKey = juc.Subject
c.tags = juc.Tags
c.nameTag = juc.Name
c.mu.Unlock()
// Generate an event if we have a system account.
s.accountConnectEvent(c)
// Check if we need to set an auth timer if the user jwt expires.
c.setExpiration(juc.Claims(), validFor)
acc.mu.RLock()
c.Debugf("Authenticated JWT: %s %q (claim-name: %q, claim-tags: %q) "+
"signed with %q by Account %q (claim-name: %q, claim-tags: %q) signed with %q",
c.typeString(), juc.Subject, juc.Name, juc.Tags, juc.Issuer, issuer, acc.nameTag, acc.tags, acc.Issuer)
acc.mu.RUnlock()
return true
}

View File

@@ -263,6 +263,9 @@ type client struct {
trace bool
echo bool
tags jwt.TagList
nameTag string
}
type rrTracking struct {
@@ -4662,6 +4665,10 @@ func (c *client) getClientInfo(detailed bool) *ClientInfo {
ci.Lang = c.opts.Lang
ci.Version = c.opts.Version
ci.Server = sn
ci.Jwt = c.opts.JWT
ci.IssuerKey = issuerForClient(c)
ci.NameTag = c.nameTag
ci.Tags = c.tags
}
c.mu.Unlock()
return &ci

View File

@@ -163,17 +163,21 @@ type ServerInfo struct {
// ClientInfo is detailed information about the client forming a connection.
type ClientInfo struct {
Start *time.Time `json:"start,omitempty"`
Host string `json:"host,omitempty"`
ID uint64 `json:"id,omitempty"`
Account string `json:"acc"`
User string `json:"user,omitempty"`
Name string `json:"name,omitempty"`
Lang string `json:"lang,omitempty"`
Version string `json:"ver,omitempty"`
RTT time.Duration `json:"rtt,omitempty"`
Server string `json:"server,omitempty"`
Stop *time.Time `json:"stop,omitempty"`
Start *time.Time `json:"start,omitempty"`
Host string `json:"host,omitempty"`
ID uint64 `json:"id,omitempty"`
Account string `json:"acc"`
User string `json:"user,omitempty"`
Name string `json:"name,omitempty"`
Lang string `json:"lang,omitempty"`
Version string `json:"ver,omitempty"`
RTT time.Duration `json:"rtt,omitempty"`
Server string `json:"server,omitempty"`
Stop *time.Time `json:"stop,omitempty"`
Jwt string `json:"jwt,omitempty"`
IssuerKey string `json:"issuer_key,omitempty"`
NameTag string `json:"name_tag,omitempty"`
Tags jwt.TagList `json:"tags,omitempty"`
}
// ServerStats hold various statistics that we will periodically send out.
@@ -1277,14 +1281,18 @@ func (s *Server) accountConnectEvent(c *client) {
Time: time.Now().UTC(),
},
Client: ClientInfo{
Start: &c.start,
Host: c.host,
ID: c.cid,
Account: accForClient(c),
User: c.getRawAuthUser(),
Name: c.opts.Name,
Lang: c.opts.Lang,
Version: c.opts.Version,
Start: &c.start,
Host: c.host,
ID: c.cid,
Account: accForClient(c),
User: c.getRawAuthUser(),
Name: c.opts.Name,
Lang: c.opts.Lang,
Version: c.opts.Version,
Jwt: c.opts.JWT,
IssuerKey: issuerForClient(c),
Tags: c.tags,
NameTag: c.nameTag,
},
}
c.mu.Unlock()
@@ -1320,16 +1328,20 @@ func (s *Server) accountDisconnectEvent(c *client, now time.Time, reason string)
Time: now.UTC(),
},
Client: ClientInfo{
Start: &c.start,
Stop: &now,
Host: c.host,
ID: c.cid,
Account: accForClient(c),
User: c.getRawAuthUser(),
Name: c.opts.Name,
Lang: c.opts.Lang,
Version: c.opts.Version,
RTT: c.getRTT(),
Start: &c.start,
Stop: &now,
Host: c.host,
ID: c.cid,
Account: accForClient(c),
User: c.getRawAuthUser(),
Name: c.opts.Name,
Lang: c.opts.Lang,
Version: c.opts.Version,
RTT: c.getRTT(),
Jwt: c.opts.JWT,
IssuerKey: issuerForClient(c),
Tags: c.tags,
NameTag: c.nameTag,
},
Sent: DataStats{
Msgs: atomic.LoadInt64(&c.inMsgs),
@@ -1365,16 +1377,20 @@ func (s *Server) sendAuthErrorEvent(c *client) {
Time: now.UTC(),
},
Client: ClientInfo{
Start: &c.start,
Stop: &now,
Host: c.host,
ID: c.cid,
Account: accForClient(c),
User: c.getRawAuthUser(),
Name: c.opts.Name,
Lang: c.opts.Lang,
Version: c.opts.Version,
RTT: c.getRTT(),
Start: &c.start,
Stop: &now,
Host: c.host,
ID: c.cid,
Account: accForClient(c),
User: c.getRawAuthUser(),
Name: c.opts.Name,
Lang: c.opts.Lang,
Version: c.opts.Version,
RTT: c.getRTT(),
Jwt: c.opts.JWT,
IssuerKey: issuerForClient(c),
Tags: c.tags,
NameTag: c.nameTag,
},
Sent: DataStats{
Msgs: c.inMsgs,
@@ -1760,6 +1776,18 @@ func accForClient(c *client) string {
return "N/A"
}
// Helper to grab issuer for a client.
func issuerForClient(c *client) (issuerKey string) {
if c == nil || c.user == nil {
return
}
issuerKey = c.user.SigningKey
if issuerKey == "" && c.user.Account != nil {
issuerKey = c.user.Account.Name
}
return
}
// Helper to clear timers.
func clearTimer(tp **time.Timer) {
if t := *tp; t != nil {

View File

@@ -4431,6 +4431,11 @@ func TestJWTUserRevocation(t *testing.T) {
defer srv.Shutdown()
updateJwt(t, srv.ClientURL(), sysCreds, sysjwt, 1) // update system account jwt
updateJwt(t, srv.ClientURL(), sysCreds, ajwt1, 1) // set account jwt without revocation
ncSys := natsConnect(t, srv.ClientURL(), nats.UserCredentials(sysCreds), nats.Name("conn name"))
defer ncSys.Close()
ncChan := make(chan *nats.Msg, 10)
defer close(ncChan)
ncSys.ChanSubscribe(fmt.Sprintf(disconnectEventSubj, apub), ncChan) // observe disconnect message
// use credentials that will be revoked ans assure that the connection will be disconnected
nc := natsConnect(t, srv.ClientURL(), nats.UserCredentials(aCreds1),
nats.DisconnectErrHandler(func(conn *nats.Conn, err error) {
@@ -4440,7 +4445,7 @@ func TestJWTUserRevocation(t *testing.T) {
}))
defer nc.Close()
// update account jwt to contain revocation
if passCnt := updateJwt(t, srv.ClientURL(), sysCreds, ajwt2, 1); passCnt != 1 {
if updateJwt(t, srv.ClientURL(), sysCreds, ajwt2, 1) != 1 {
t.Fatalf("Expected jwt update to pass")
}
// assure that nc got disconnected due to the revocation
@@ -4449,6 +4454,9 @@ func TestJWTUserRevocation(t *testing.T) {
case <-time.After(time.Second):
t.Fatalf("Expected connection to have failed")
}
m := <-ncChan
require_Len(t, strings.Count(string(m.Data), apub), 2)
require_True(t, strings.Contains(string(m.Data), `"jwt": "eyJ0`))
// try again with old credentials. Expected to fail
if nc1, err := nats.Connect(srv.ClientURL(), nats.UserCredentials(aCreds1)); err == nil {
nc1.Close()

View File

@@ -127,6 +127,10 @@ type ConnInfo struct {
Account string `json:"account,omitempty"`
Subs []string `json:"subscriptions_list,omitempty"`
SubsDetail []SubDetail `json:"subscriptions_list_detail,omitempty"`
JWT string `json:"jwt,omitempty"`
IssuerKey string `json:"issuer_key,omitempty"`
NameTag string `json:"name_tag,omitempty"`
Tags jwt.TagList `json:"tags,omitempty"`
}
// DefaultConnListSize is the default size of the connection list.
@@ -334,6 +338,10 @@ func (s *Server) Connz(opts *ConnzOptions) (*Connz, error) {
if client.acc != nil && (client.acc.Name != globalAccountName) {
ci.Account = client.acc.Name
}
ci.JWT = client.opts.JWT
ci.IssuerKey = issuerForClient(client)
ci.Tags = client.tags
ci.NameTag = client.nameTag
}
client.mu.Unlock()
pconns[i] = ci
@@ -1976,6 +1984,9 @@ type AccountInfo struct {
Exports []ExtExport `json:"exports,omitempty"`
Imports []ExtImport `json:"imports,omitempty"`
Jwt string `json:"jwt,omitempty"`
IssuerKey string `json:"issuer_key,omitempty"`
NameTag string `json:"name_tag,omitempty"`
Tags jwt.TagList `json:"tags,omitempty"`
Claim *jwt.AccountClaims `json:"decoded_jwt,omitempty"`
Vr []ExtVrIssues `json:"validation_result_jwt,omitempty"`
RevokedUser map[string]time.Time `json:"revoked_user,omitempty"`
@@ -2174,6 +2185,9 @@ func (s *Server) accountInfo(accName string) (*AccountInfo, error) {
exports,
imports,
a.claimJWT,
a.Issuer,
a.nameTag,
a.tags,
claim,
vrIssues,
collectRevocations(a.usersRevoked),

View File

@@ -2343,6 +2343,10 @@ func (s *Server) saveClosedClient(c *client, nc net.Conn, reason ClosedState) {
if c.acc != nil && c.acc.Name != globalAccountName {
cc.acc = c.acc.Name
}
cc.JWT = c.opts.JWT
cc.IssuerKey = issuerForClient(c)
cc.Tags = c.tags
cc.NameTag = c.nameTag
c.mu.Unlock()
// Place in the ring buffer