mirror of
https://github.com/tailscale/tailscale.git
synced 2025-12-22 05:27:08 +00:00
wgengine/netlog: include node OS in logged attributes (#17755)
Include the node's OS with network flow log information. Refactor the JSON-length computation to be a bit more precise. Updates tailscale/corp#33352 Fixes tailscale/corp#34030 Signed-off-by: Joe Tsai <joetsai@digital-static.net>
This commit is contained in:
@@ -825,7 +825,7 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/
|
|||||||
tailscale.com/tsweb from tailscale.com/util/eventbus
|
tailscale.com/tsweb from tailscale.com/util/eventbus
|
||||||
tailscale.com/tsweb/varz from tailscale.com/util/usermetric+
|
tailscale.com/tsweb/varz from tailscale.com/util/usermetric+
|
||||||
tailscale.com/types/appctype from tailscale.com/ipn/ipnlocal+
|
tailscale.com/types/appctype from tailscale.com/ipn/ipnlocal+
|
||||||
tailscale.com/types/bools from tailscale.com/tsnet
|
tailscale.com/types/bools from tailscale.com/tsnet+
|
||||||
tailscale.com/types/dnstype from tailscale.com/ipn/ipnlocal+
|
tailscale.com/types/dnstype from tailscale.com/ipn/ipnlocal+
|
||||||
tailscale.com/types/empty from tailscale.com/ipn+
|
tailscale.com/types/empty from tailscale.com/ipn+
|
||||||
tailscale.com/types/ipproto from tailscale.com/net/flowtrack+
|
tailscale.com/types/ipproto from tailscale.com/net/flowtrack+
|
||||||
|
|||||||
@@ -392,6 +392,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
|||||||
tailscale.com/tsweb from tailscale.com/util/eventbus
|
tailscale.com/tsweb from tailscale.com/util/eventbus
|
||||||
tailscale.com/tsweb/varz from tailscale.com/cmd/tailscaled+
|
tailscale.com/tsweb/varz from tailscale.com/cmd/tailscaled+
|
||||||
tailscale.com/types/appctype from tailscale.com/ipn/ipnlocal+
|
tailscale.com/types/appctype from tailscale.com/ipn/ipnlocal+
|
||||||
|
tailscale.com/types/bools from tailscale.com/wgengine/netlog
|
||||||
tailscale.com/types/dnstype from tailscale.com/ipn/ipnlocal+
|
tailscale.com/types/dnstype from tailscale.com/ipn/ipnlocal+
|
||||||
tailscale.com/types/empty from tailscale.com/ipn+
|
tailscale.com/types/empty from tailscale.com/ipn+
|
||||||
tailscale.com/types/flagtype from tailscale.com/cmd/tailscaled
|
tailscale.com/types/flagtype from tailscale.com/cmd/tailscaled
|
||||||
|
|||||||
@@ -230,7 +230,7 @@ tailscale.com/cmd/tsidp dependencies: (generated by github.com/tailscale/depawar
|
|||||||
tailscale.com/tsweb from tailscale.com/util/eventbus
|
tailscale.com/tsweb from tailscale.com/util/eventbus
|
||||||
tailscale.com/tsweb/varz from tailscale.com/tsweb+
|
tailscale.com/tsweb/varz from tailscale.com/tsweb+
|
||||||
tailscale.com/types/appctype from tailscale.com/ipn/ipnlocal+
|
tailscale.com/types/appctype from tailscale.com/ipn/ipnlocal+
|
||||||
tailscale.com/types/bools from tailscale.com/tsnet
|
tailscale.com/types/bools from tailscale.com/tsnet+
|
||||||
tailscale.com/types/dnstype from tailscale.com/client/local+
|
tailscale.com/types/dnstype from tailscale.com/client/local+
|
||||||
tailscale.com/types/empty from tailscale.com/ipn+
|
tailscale.com/types/empty from tailscale.com/ipn+
|
||||||
tailscale.com/types/ipproto from tailscale.com/ipn+
|
tailscale.com/types/ipproto from tailscale.com/ipn+
|
||||||
|
|||||||
@@ -225,7 +225,7 @@ tailscale.com/tsnet dependencies: (generated by github.com/tailscale/depaware)
|
|||||||
LDW tailscale.com/tsweb from tailscale.com/util/eventbus
|
LDW tailscale.com/tsweb from tailscale.com/util/eventbus
|
||||||
tailscale.com/tsweb/varz from tailscale.com/tsweb+
|
tailscale.com/tsweb/varz from tailscale.com/tsweb+
|
||||||
tailscale.com/types/appctype from tailscale.com/ipn/ipnlocal+
|
tailscale.com/types/appctype from tailscale.com/ipn/ipnlocal+
|
||||||
tailscale.com/types/bools from tailscale.com/tsnet
|
tailscale.com/types/bools from tailscale.com/tsnet+
|
||||||
tailscale.com/types/dnstype from tailscale.com/client/local+
|
tailscale.com/types/dnstype from tailscale.com/client/local+
|
||||||
tailscale.com/types/empty from tailscale.com/ipn+
|
tailscale.com/types/empty from tailscale.com/ipn+
|
||||||
tailscale.com/types/ipproto from tailscale.com/ipn+
|
tailscale.com/types/ipproto from tailscale.com/ipn+
|
||||||
|
|||||||
@@ -44,18 +44,6 @@ const (
|
|||||||
// Each [ConnectionCounts] occupies at most [MaxConnectionCountsJSONSize].
|
// Each [ConnectionCounts] occupies at most [MaxConnectionCountsJSONSize].
|
||||||
MinMessageJSONSize = len(messageJSON)
|
MinMessageJSONSize = len(messageJSON)
|
||||||
|
|
||||||
nodeJSON = `{"nodeId":` + maxJSONStableID + `,"name":"","addresses":` + maxJSONAddrs + `,"user":"","tags":[]}`
|
|
||||||
maxJSONAddrV4 = `"255.255.255.255"`
|
|
||||||
maxJSONAddrV6 = `"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"`
|
|
||||||
maxJSONAddrs = `[` + maxJSONAddrV4 + `,` + maxJSONAddrV6 + `]`
|
|
||||||
|
|
||||||
// MinNodeJSONSize is the overhead size of Node when it is
|
|
||||||
// serialized as JSON assuming that each field is minimally populated.
|
|
||||||
// It does not account for bytes occupied by
|
|
||||||
// [Node.Name], [Node.User], or [Node.Tags]. The [Node.Addresses]
|
|
||||||
// is assumed to contain a pair of IPv4 and IPv6 address.
|
|
||||||
MinNodeJSONSize = len(nodeJSON)
|
|
||||||
|
|
||||||
maxJSONConnCounts = `{` + maxJSONConn + `,` + maxJSONCounts + `}`
|
maxJSONConnCounts = `{` + maxJSONConn + `,` + maxJSONCounts + `}`
|
||||||
maxJSONConn = `"proto":` + maxJSONProto + `,"src":` + maxJSONAddrPort + `,"dst":` + maxJSONAddrPort
|
maxJSONConn = `"proto":` + maxJSONProto + `,"src":` + maxJSONAddrPort + `,"dst":` + maxJSONAddrPort
|
||||||
maxJSONProto = `255`
|
maxJSONProto = `255`
|
||||||
@@ -82,6 +70,9 @@ type Node struct {
|
|||||||
// Addresses are the Tailscale IP addresses of the node.
|
// Addresses are the Tailscale IP addresses of the node.
|
||||||
Addresses []netip.Addr `json:"addresses,omitempty"`
|
Addresses []netip.Addr `json:"addresses,omitempty"`
|
||||||
|
|
||||||
|
// OS is the operating system of the node.
|
||||||
|
OS string `json:"os,omitzero"` // e.g., "linux"
|
||||||
|
|
||||||
// User is the user that owns the node.
|
// User is the user that owns the node.
|
||||||
// It is not populated if the node is tagged.
|
// It is not populated if the node is tagged.
|
||||||
User string `json:"user,omitzero"` // e.g., "johndoe@example.com"
|
User string `json:"user,omitzero"` // e.g., "johndoe@example.com"
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
|
"tailscale.com/types/bools"
|
||||||
"tailscale.com/types/netlogtype"
|
"tailscale.com/types/netlogtype"
|
||||||
"tailscale.com/util/set"
|
"tailscale.com/util/set"
|
||||||
)
|
)
|
||||||
@@ -134,17 +135,31 @@ func compareConnCnts(x, y netlogtype.ConnectionCounts) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// jsonLen computes an upper-bound on the size of the JSON representation.
|
// jsonLen computes an upper-bound on the size of the JSON representation.
|
||||||
func (nu nodeUser) jsonLen() int {
|
func (nu nodeUser) jsonLen() (n int) {
|
||||||
if !nu.Valid() {
|
if !nu.Valid() {
|
||||||
return len(`{"nodeId":""}`)
|
return len(`{"nodeId":""}`)
|
||||||
}
|
}
|
||||||
n := netlogtype.MinNodeJSONSize + jsonQuotedLen(nu.Name())
|
n += len(`{}`)
|
||||||
|
n += len(`"nodeId":`) + jsonQuotedLen(string(nu.StableID())) + len(`,`)
|
||||||
|
if len(nu.Name()) > 0 {
|
||||||
|
n += len(`"name":`) + jsonQuotedLen(nu.Name()) + len(`,`)
|
||||||
|
}
|
||||||
|
if nu.Addresses().Len() > 0 {
|
||||||
|
n += len(`"addresses":[]`)
|
||||||
|
for _, addr := range nu.Addresses().All() {
|
||||||
|
n += bools.IfElse(addr.Addr().Is4(), len(`"255.255.255.255"`), len(`"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"`)) + len(",")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if nu.Hostinfo().Valid() && len(nu.Hostinfo().OS()) > 0 {
|
||||||
|
n += len(`"os":`) + jsonQuotedLen(nu.Hostinfo().OS()) + len(`,`)
|
||||||
|
}
|
||||||
if nu.Tags().Len() > 0 {
|
if nu.Tags().Len() > 0 {
|
||||||
|
n += len(`"tags":[]`)
|
||||||
for _, tag := range nu.Tags().All() {
|
for _, tag := range nu.Tags().All() {
|
||||||
n += jsonQuotedLen(tag) + len(",")
|
n += jsonQuotedLen(tag) + len(",")
|
||||||
}
|
}
|
||||||
} else if nu.user.Valid() && nu.user.ID() == nu.User() {
|
} else if nu.user.Valid() && nu.user.ID() == nu.User() && len(nu.user.LoginName()) > 0 {
|
||||||
n += jsonQuotedLen(nu.user.LoginName())
|
n += len(`"user":`) + jsonQuotedLen(nu.user.LoginName()) + len(",")
|
||||||
}
|
}
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
@@ -166,6 +181,9 @@ func (nu nodeUser) toNode() netlogtype.Node {
|
|||||||
}
|
}
|
||||||
n.Addresses = []netip.Addr{ipv4, ipv6}
|
n.Addresses = []netip.Addr{ipv4, ipv6}
|
||||||
n.Addresses = slices.DeleteFunc(n.Addresses, func(a netip.Addr) bool { return !a.IsValid() })
|
n.Addresses = slices.DeleteFunc(n.Addresses, func(a netip.Addr) bool { return !a.IsValid() })
|
||||||
|
if nu.Hostinfo().Valid() {
|
||||||
|
n.OS = nu.Hostinfo().OS()
|
||||||
|
}
|
||||||
if nu.Tags().Len() > 0 {
|
if nu.Tags().Len() > 0 {
|
||||||
n.Tags = nu.Tags().AsSlice()
|
n.Tags = nu.Tags().AsSlice()
|
||||||
slices.Sort(n.Tags)
|
slices.Sort(n.Tags)
|
||||||
|
|||||||
@@ -190,6 +190,7 @@ func TestToNode(t *testing.T) {
|
|||||||
node: &tailcfg.Node{
|
node: &tailcfg.Node{
|
||||||
StableID: "n123456CNTL",
|
StableID: "n123456CNTL",
|
||||||
Addresses: []netip.Prefix{prefix("100.1.2.3")},
|
Addresses: []netip.Prefix{prefix("100.1.2.3")},
|
||||||
|
Hostinfo: (&tailcfg.Hostinfo{OS: "linux"}).View(),
|
||||||
User: 12345,
|
User: 12345,
|
||||||
},
|
},
|
||||||
user: &tailcfg.UserProfile{
|
user: &tailcfg.UserProfile{
|
||||||
@@ -199,6 +200,7 @@ func TestToNode(t *testing.T) {
|
|||||||
want: netlogtype.Node{
|
want: netlogtype.Node{
|
||||||
NodeID: "n123456CNTL",
|
NodeID: "n123456CNTL",
|
||||||
Addresses: []netip.Addr{addr("100.1.2.3")},
|
Addresses: []netip.Addr{addr("100.1.2.3")},
|
||||||
|
OS: "linux",
|
||||||
User: "user@domain",
|
User: "user@domain",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user