Merge pull request #1862 from nats-io/varz-jwt

[added] field to varz output containing the operator jwt
This commit is contained in:
Ivan Kozlovic
2021-01-29 11:53:20 -07:00
committed by GitHub
4 changed files with 117 additions and 68 deletions

View File

@@ -32,34 +32,39 @@ const jwtPrefix = "eyJ"
// ReadOperatorJWT will read a jwt file for an operator claim. This can be a decorated file.
func ReadOperatorJWT(jwtfile string) (*jwt.OperatorClaims, error) {
_, claim, err := readOperatorJWT(jwtfile)
return claim, err
}
func readOperatorJWT(jwtfile string) (string, *jwt.OperatorClaims, error) {
contents, err := ioutil.ReadFile(jwtfile)
if err != nil {
// Check to see if the JWT has been inlined.
if !strings.HasPrefix(jwtfile, jwtPrefix) {
return nil, err
return "", nil, err
}
// We may have an inline jwt here.
contents = []byte(jwtfile)
}
defer wipeSlice(contents)
var claim string
var theJWT string
items := nscDecoratedRe.FindAllSubmatch(contents, -1)
if len(items) == 0 {
claim = string(contents)
theJWT = string(contents)
} else {
// First result should be the JWT.
// We copy here so that if the file contained a seed file too we wipe appropriately.
raw := items[0][1]
tmp := make([]byte, len(raw))
copy(tmp, raw)
claim = string(tmp)
theJWT = string(tmp)
}
opc, err := jwt.DecodeOperatorClaims(claim)
opc, err := jwt.DecodeOperatorClaims(theJWT)
if err != nil {
return nil, err
return "", nil, err
}
return opc, nil
return theJWT, opc, nil
}
// Just wipe slice with 'x', for clearing contents of nkey seed file.

View File

@@ -992,59 +992,61 @@ func (s *Server) HandleStacksz(w http.ResponseWriter, r *http.Request) {
// Varz will output server information on the monitoring port at /varz.
type Varz struct {
ID string `json:"server_id"`
Name string `json:"server_name"`
Version string `json:"version"`
Proto int `json:"proto"`
GitCommit string `json:"git_commit,omitempty"`
GoVersion string `json:"go"`
Host string `json:"host"`
Port int `json:"port"`
AuthRequired bool `json:"auth_required,omitempty"`
TLSRequired bool `json:"tls_required,omitempty"`
TLSVerify bool `json:"tls_verify,omitempty"`
IP string `json:"ip,omitempty"`
ClientConnectURLs []string `json:"connect_urls,omitempty"`
WSConnectURLs []string `json:"ws_connect_urls,omitempty"`
MaxConn int `json:"max_connections"`
MaxSubs int `json:"max_subscriptions,omitempty"`
PingInterval time.Duration `json:"ping_interval"`
MaxPingsOut int `json:"ping_max"`
HTTPHost string `json:"http_host"`
HTTPPort int `json:"http_port"`
HTTPBasePath string `json:"http_base_path"`
HTTPSPort int `json:"https_port"`
AuthTimeout float64 `json:"auth_timeout"`
MaxControlLine int32 `json:"max_control_line"`
MaxPayload int `json:"max_payload"`
MaxPending int64 `json:"max_pending"`
Cluster ClusterOptsVarz `json:"cluster,omitempty"`
Gateway GatewayOptsVarz `json:"gateway,omitempty"`
LeafNode LeafNodeOptsVarz `json:"leaf,omitempty"`
JetStream JetStreamVarz `json:"jetstream,omitempty"`
TLSTimeout float64 `json:"tls_timeout"`
WriteDeadline time.Duration `json:"write_deadline"`
Start time.Time `json:"start"`
Now time.Time `json:"now"`
Uptime string `json:"uptime"`
Mem int64 `json:"mem"`
Cores int `json:"cores"`
MaxProcs int `json:"gomaxprocs"`
CPU float64 `json:"cpu"`
Connections int `json:"connections"`
TotalConnections uint64 `json:"total_connections"`
Routes int `json:"routes"`
Remotes int `json:"remotes"`
Leafs int `json:"leafnodes"`
InMsgs int64 `json:"in_msgs"`
OutMsgs int64 `json:"out_msgs"`
InBytes int64 `json:"in_bytes"`
OutBytes int64 `json:"out_bytes"`
SlowConsumers int64 `json:"slow_consumers"`
Subscriptions uint32 `json:"subscriptions"`
HTTPReqStats map[string]uint64 `json:"http_req_stats"`
ConfigLoadTime time.Time `json:"config_load_time"`
Tags jwt.TagList `json:"tags,omitempty"`
ID string `json:"server_id"`
Name string `json:"server_name"`
Version string `json:"version"`
Proto int `json:"proto"`
GitCommit string `json:"git_commit,omitempty"`
GoVersion string `json:"go"`
Host string `json:"host"`
Port int `json:"port"`
AuthRequired bool `json:"auth_required,omitempty"`
TLSRequired bool `json:"tls_required,omitempty"`
TLSVerify bool `json:"tls_verify,omitempty"`
IP string `json:"ip,omitempty"`
ClientConnectURLs []string `json:"connect_urls,omitempty"`
WSConnectURLs []string `json:"ws_connect_urls,omitempty"`
MaxConn int `json:"max_connections"`
MaxSubs int `json:"max_subscriptions,omitempty"`
PingInterval time.Duration `json:"ping_interval"`
MaxPingsOut int `json:"ping_max"`
HTTPHost string `json:"http_host"`
HTTPPort int `json:"http_port"`
HTTPBasePath string `json:"http_base_path"`
HTTPSPort int `json:"https_port"`
AuthTimeout float64 `json:"auth_timeout"`
MaxControlLine int32 `json:"max_control_line"`
MaxPayload int `json:"max_payload"`
MaxPending int64 `json:"max_pending"`
Cluster ClusterOptsVarz `json:"cluster,omitempty"`
Gateway GatewayOptsVarz `json:"gateway,omitempty"`
LeafNode LeafNodeOptsVarz `json:"leaf,omitempty"`
JetStream JetStreamVarz `json:"jetstream,omitempty"`
TLSTimeout float64 `json:"tls_timeout"`
WriteDeadline time.Duration `json:"write_deadline"`
Start time.Time `json:"start"`
Now time.Time `json:"now"`
Uptime string `json:"uptime"`
Mem int64 `json:"mem"`
Cores int `json:"cores"`
MaxProcs int `json:"gomaxprocs"`
CPU float64 `json:"cpu"`
Connections int `json:"connections"`
TotalConnections uint64 `json:"total_connections"`
Routes int `json:"routes"`
Remotes int `json:"remotes"`
Leafs int `json:"leafnodes"`
InMsgs int64 `json:"in_msgs"`
OutMsgs int64 `json:"out_msgs"`
InBytes int64 `json:"in_bytes"`
OutBytes int64 `json:"out_bytes"`
SlowConsumers int64 `json:"slow_consumers"`
Subscriptions uint32 `json:"subscriptions"`
HTTPReqStats map[string]uint64 `json:"http_req_stats"`
ConfigLoadTime time.Time `json:"config_load_time"`
Tags jwt.TagList `json:"tags,omitempty"`
TrustedOperatorsJwt []string `json:"trusted_operators_jwt,omitempty"`
TrustedOperatorsClaim []*jwt.OperatorClaims `json:"trusted_operators_claim,omitempty"`
}
// JetStreamVarz contains basic runtime information about jetstream
@@ -1251,11 +1253,13 @@ func (s *Server) createVarz(pcpu float64, rss int64) *Varz {
TLSVerify: leafTlsVerify,
Remotes: []RemoteLeafOptsVarz{},
},
Start: s.start,
MaxSubs: opts.MaxSubs,
Cores: numCores,
MaxProcs: maxProcs,
Tags: opts.Tags,
Start: s.start,
MaxSubs: opts.MaxSubs,
Cores: numCores,
MaxProcs: maxProcs,
Tags: opts.Tags,
TrustedOperatorsJwt: opts.operatorJWT,
TrustedOperatorsClaim: opts.TrustedOperators,
}
if len(opts.Routes) > 0 {
varz.Cluster.URLs = urlsToStrings(opts.Routes)
@@ -1281,7 +1285,6 @@ func (s *Server) createVarz(pcpu float64, rss int64) *Varz {
}
varz.LeafNode.Remotes = rlna
}
if s.js != nil {
s.js.mu.RLock()
varz.JetStream = JetStreamVarz{

View File

@@ -3495,6 +3495,43 @@ func pollLeafz(t *testing.T, s *Server, mode int, url string, opts *LeafzOptions
return l
}
func TestMonitorOpJWT(t *testing.T) {
content := `
listen: "127.0.0.1:-1"
http: "127.0.0.1:-1"
operator = "../test/configs/nkeys/op.jwt"
resolver = MEMORY
`
conf := createConfFile(t, []byte(content))
defer os.Remove(conf)
sa, _ := RunServerWithConfig(conf)
defer sa.Shutdown()
theJWT, err := ioutil.ReadFile("../test/configs/nkeys/op.jwt")
require_NoError(t, err)
theJWT = []byte(strings.Split(string(theJWT), "\n")[1])
claim, err := jwt.DecodeOperatorClaims(string(theJWT))
require_NoError(t, err)
pollURL := fmt.Sprintf("http://127.0.0.1:%d/varz", sa.MonitorAddr().Port)
for pollMode := 1; pollMode < 2; pollMode++ {
l := pollVarz(t, sa, pollMode, pollURL, nil)
if len(l.TrustedOperatorsJwt) != 1 {
t.Fatalf("Expected one operator jwt")
}
if len(l.TrustedOperatorsClaim) != 1 {
t.Fatalf("Expected one operator claim")
}
if l.TrustedOperatorsJwt[0] != string(theJWT) {
t.Fatalf("Expected operator to be identical to configuration")
}
if !reflect.DeepEqual(l.TrustedOperatorsClaim[0], claim) {
t.Fatal("claims need to be equal")
}
}
}
func TestMonitorLeafz(t *testing.T) {
content := `
listen: "127.0.0.1:-1"

View File

@@ -238,7 +238,6 @@ type Options struct {
TrustedOperators []*jwt.OperatorClaims `json:"-"`
AccountResolver AccountResolver `json:"-"`
AccountResolverTLSConfig *tls.Config `json:"-"`
resolverPreloads map[string]string
CustomClientAuthentication Authentication `json:"-"`
CustomRouterAuthentication Authentication `json:"-"`
@@ -265,6 +264,10 @@ type Options struct {
inConfig map[string]bool
inCmdLine map[string]bool
// private fields for operator mode
operatorJWT []string
resolverPreloads map[string]string
// private fields, used for testing
gatewaysSolicitDelay time.Duration
routeProto int
@@ -862,12 +865,13 @@ func (o *Options) processConfigFileLine(k string, v interface{}, errors *[]error
// Assume for now these are file names, but they can also be the JWT itself inline.
o.TrustedOperators = make([]*jwt.OperatorClaims, 0, len(opFiles))
for _, fname := range opFiles {
opc, err := ReadOperatorJWT(fname)
theJWT, opc, err := readOperatorJWT(fname)
if err != nil {
err := &configErr{tk, fmt.Sprintf("error parsing operator JWT: %v", err)}
*errors = append(*errors, err)
continue
}
o.operatorJWT = append(o.operatorJWT, theJWT)
o.TrustedOperators = append(o.TrustedOperators, opc)
}
if len(o.TrustedOperators) == 1 {