From f789d98c16da8383e957ea0c9934ce0fa29438dd Mon Sep 17 00:00:00 2001 From: Anishka Singh Date: Wed, 6 Dec 2023 17:05:23 -0800 Subject: [PATCH] tailscale/logtail: redact public ipv6 and ipv4 ip addresses within tailscaled. Updates #15664 Signed-off-by: Anishka Singh --- logtail/logtail.go | 38 +++++++++++++++++++++ logtail/logtail_test.go | 76 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+) diff --git a/logtail/logtail.go b/logtail/logtail.go index f17fd11d5744bd..cf18c565ced7fb 100644 --- a/logtail/logtail.go +++ b/logtail/logtail.go @@ -15,8 +15,11 @@ import ( "log" mrand "math/rand" "net/http" + "net/netip" "os" + "regexp" "strconv" + "strings" "sync" "sync/atomic" "time" @@ -24,6 +27,7 @@ import ( "tailscale.com/envknob" "tailscale.com/net/netmon" "tailscale.com/net/sockstats" + "tailscale.com/net/tsaddr" "tailscale.com/tstime" tslogger "tailscale.com/types/logger" "tailscale.com/types/logid" @@ -725,6 +729,8 @@ func (l *Logger) Logf(format string, args ...any) { fmt.Fprintf(l, format, args...) } +var obscureIPs = envknob.RegisterBool("TS_OBSCURE_LOGGED_IPS") + // Write logs an encoded JSON blob. // // If the []byte passed to Write is not an encoded JSON blob, @@ -749,6 +755,10 @@ func (l *Logger) Write(buf []byte) (int, error) { } } + if obscureIPs() { + buf = redactIPs(buf) + } + l.writeLock.Lock() defer l.writeLock.Unlock() @@ -757,6 +767,34 @@ func (l *Logger) Write(buf []byte) (int, error) { return inLen, err } +func redactIPs(buf []byte) []byte { + regexMatchesIPv6 := regexp.MustCompile(`([0-9a-fA-F]{1,4}):([0-9a-fA-F]{1,4}):.\S*`) + regexMatchesIPv4 := regexp.MustCompile(`(\d{1,3})\.(\d{1,3})\.\d{1,3}\.\d{1,3}`) + + out := regexMatchesIPv6.ReplaceAllStringFunc(string(buf), func(s string) string { + ip, err := netip.ParseAddr(s) + + if err != nil || tsaddr.IsTailscaleIP(ip) { + return s // don't change this one + } + + prefix := strings.Split(s, ":") + + return strings.Join(append(prefix[:2], "x"), ":") + }) + + out = regexMatchesIPv4.ReplaceAllStringFunc(string(out), func(s string) string { + ip, err := netip.ParseAddr(s) + if err != nil || tsaddr.IsTailscaleIP(ip) { + return s // don't change this one + } + prefix := strings.Split(s, ".") + return strings.Join(append(prefix[:2], "x.x"), ".") + }) + + return []byte(out) +} + var ( openBracketV = []byte("[v") v1 = []byte("[v1] ") diff --git a/logtail/logtail_test.go b/logtail/logtail_test.go index f0d3f36b2f79bf..b9a8cdb8aa9e75 100644 --- a/logtail/logtail_test.go +++ b/logtail/logtail_test.go @@ -14,6 +14,7 @@ import ( "testing" "time" + "tailscale.com/envknob" "tailscale.com/tstest" "tailscale.com/tstime" ) @@ -406,3 +407,78 @@ func TestLoggerWriteResult(t *testing.T) { t.Errorf("mismatch.\n got: %#q\nwant: %#q", back, want) } } +func TestRedact(t *testing.T) { + envknob.Setenv("TS_OBSCURE_LOGGED_IPS", "true") + tests := []struct { + in string + want string + }{ + // tests for ipv4 addresses + { + "120.100.30.47", + "120.100.x.x", + }, + { + "192.167.0.1", + "192.167.x.x", + }, + { + "node [5Btdd] d:e89a3384f526d251 now using 10.0.0.222:41641 mtu=1360 tx=d81a8a35a0ce", + "node [5Btdd] d:e89a3384f526d251 now using 10.0.x.x:41641 mtu=1360 tx=d81a8a35a0ce", + }, + //tests for ipv6 addresses + { + "2001:0db8:85a3:0000:0000:8a2e:0370:7334", + "2001:0db8:x", + }, + { + "2345:0425:2CA1:0000:0000:0567:5673:23b5", + "2345:0425:x", + }, + { + "2041:0000:140F::875B:131B", + "2041:0000:x", + }, + { + "node [5Btdd] d:e89a3384f526d251 now using 2051:0000:140F::875B:131C mtu=1360 tx=d81a8a35a0ce", + "node [5Btdd] d:e89a3384f526d251 now using 2051:0000:x mtu=1360 tx=d81a8a35a0ce", + }, + //tests for tailscale ip addresses + { + "100.64.5.6", + "100.64.5.6", + }, + { + "fd7a:115c:a1e0::/96", + "fd7a:115c:a1e0::/96", + }, + //tests for ipv6 and ipv4 together + { + "192.167.0.1 2001:0db8:85a3:0000:0000:8a2e:0370:7334", + "192.167.x.x 2001:0db8:x", + }, + { + "node [5Btdd] d:e89a3384f526d251 now using 10.0.0.222:41641 mtu=1360 tx=d81a8a35a0ce 2345:0425:2CA1::0567:5673:23b5", + "node [5Btdd] d:e89a3384f526d251 now using 10.0.x.x:41641 mtu=1360 tx=d81a8a35a0ce 2345:0425:x", + }, + { + "100.64.5.6 2091:0db8:85a3:0000:0000:8a2e:0370:7334", + "100.64.5.6 2091:0db8:x", + }, + { + "192.167.0.1 120.100.30.47 2041:0000:140F::875B:131B", + "192.167.x.x 120.100.x.x 2041:0000:x", + }, + { + "fd7a:115c:a1e0::/96 192.167.0.1 2001:0db8:85a3:0000:0000:8a2e:0370:7334", + "fd7a:115c:a1e0::/96 192.167.x.x 2001:0db8:x", + }, + } + + for _, tt := range tests { + gotBuf := redactIPs([]byte(tt.in)) + if string(gotBuf) != tt.want { + t.Errorf("for %q,\n got: %#q\nwant: %#q\n", tt.in, gotBuf, tt.want) + } + } +}