tailscale/logtail: redact public ipv6 and ipv4 ip addresses within tailscaled. (#10531)
Updates #15664 Signed-off-by: Anishka Singh <anishkasingh66@gmail.com>
This commit is contained in:
parent
3a635db06e
commit
3fb6ee7fdb
|
@ -15,7 +15,9 @@ import (
|
|||
"log"
|
||||
mrand "math/rand"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
@ -24,6 +26,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 +728,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 +754,10 @@ func (l *Logger) Write(buf []byte) (int, error) {
|
|||
}
|
||||
}
|
||||
|
||||
if obscureIPs() {
|
||||
buf = redactIPs(buf)
|
||||
}
|
||||
|
||||
l.writeLock.Lock()
|
||||
defer l.writeLock.Unlock()
|
||||
|
||||
|
@ -757,6 +766,40 @@ func (l *Logger) Write(buf []byte) (int, error) {
|
|||
return inLen, err
|
||||
}
|
||||
|
||||
var (
|
||||
regexMatchesIPv6 = regexp.MustCompile(`([0-9a-fA-F]{1,4}):([0-9a-fA-F]{1,4}):([0-9a-fA-F:]{1,4})*`)
|
||||
regexMatchesIPv4 = regexp.MustCompile(`(\d{1,3})\.(\d{1,3})\.\d{1,3}\.\d{1,3}`)
|
||||
)
|
||||
|
||||
// redactIPs is a helper function used in Write() to redact IPs (other than tailscale IPs).
|
||||
// This function takes a log line as a byte slice and
|
||||
// uses regex matching to parse and find IP addresses. Based on if the IP address is IPv4 or
|
||||
// IPv6, it parses and replaces the end of the addresses with an "x". This function returns the
|
||||
// log line with the IPs redacted.
|
||||
func redactIPs(buf []byte) []byte {
|
||||
out := regexMatchesIPv6.ReplaceAllFunc(buf, func(b []byte) []byte {
|
||||
ip, err := netip.ParseAddr(string(b))
|
||||
if err != nil || tsaddr.IsTailscaleIP(ip) {
|
||||
return b // don't change this one
|
||||
}
|
||||
|
||||
prefix := bytes.Split(b, []byte(":"))
|
||||
return bytes.Join(append(prefix[:2], []byte("x")), []byte(":"))
|
||||
})
|
||||
|
||||
out = regexMatchesIPv4.ReplaceAllFunc(out, func(b []byte) []byte {
|
||||
ip, err := netip.ParseAddr(string(b))
|
||||
if err != nil || tsaddr.IsTailscaleIP(ip) {
|
||||
return b // don't change this one
|
||||
}
|
||||
|
||||
prefix := bytes.Split(b, []byte("."))
|
||||
return bytes.Join(append(prefix[:2], []byte("x.x")), []byte("."))
|
||||
})
|
||||
|
||||
return []byte(out)
|
||||
}
|
||||
|
||||
var (
|
||||
openBracketV = []byte("[v")
|
||||
v1 = []byte("[v1] ")
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"tailscale.com/envknob"
|
||||
"tailscale.com/tstest"
|
||||
"tailscale.com/tstime"
|
||||
)
|
||||
|
@ -406,3 +407,82 @@ 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/65",
|
||||
"192.167.x.x/65",
|
||||
},
|
||||
{
|
||||
"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",
|
||||
},
|
||||
{
|
||||
"2601:645:8200:edf0::c9de/64",
|
||||
"2601:645:x/64",
|
||||
},
|
||||
{
|
||||
"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",
|
||||
},
|
||||
{
|
||||
"2601:645:8200:edf0::c9de/64 2601:645:8200:edf0:1ce9:b17d:71f5:f6a3/64",
|
||||
"2601:645:x/64 2601:645:x/64",
|
||||
},
|
||||
//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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue