feature/syslog, cmd/tailscaled, logpolicy: add optional --syslog flag

Add a new modular syslog feature providing a tailscaled --syslog flag
that sends the daemon's logs to the system syslog daemon instead of
stderr, which is useful when running as a daemon without a service
manager that captures stderr (e.g. OpenWrt's procd).

The feature package registers two new hooks: one to register its flag
before flag parsing, and one that tailscaled calls early in main to
redirect the standard library's default logger. Because logpolicy later
points the default logger at logtail, whose local console copy writes
to stderr, logpolicy now also consults the hook and sends its console
copy to the same sink (with timestamps disabled, as syslog records its
own).

The feature is linked by default only on Linux, FreeBSD, and OpenBSD,
and can be removed with the ts_omit_syslog build tag. If connecting to
the syslog daemon fails at startup, tailscaled logs a warning and
continues logging to stderr.

Fixes #16270

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: I8f3a92d4c1e6b70a5d29e4f61b3c874250a9de13
This commit is contained in:
Brad Fitzpatrick
2026-07-17 13:55:23 -07:00
committed by Brad Fitzpatrick
parent def265083b
commit 0433cc6929
10 changed files with 137 additions and 2 deletions
+2 -1
View File
@@ -323,6 +323,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/feature/runtimemetrics from tailscale.com/feature/condregister
L tailscale.com/feature/sdnotify from tailscale.com/feature/condregister
LD tailscale.com/feature/ssh from tailscale.com/cmd/tailscaled
L tailscale.com/feature/syslog from tailscale.com/feature/condregister
tailscale.com/feature/syspolicy from tailscale.com/feature/condregister
tailscale.com/feature/taildrop from tailscale.com/feature/condregister
tailscale.com/feature/tailnetlock from tailscale.com/feature/condregister
@@ -745,7 +746,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
iter from maps+
log from expvar+
log/internal from log
LD log/syslog from tailscale.com/ssh/tailssh
LD log/syslog from tailscale.com/ssh/tailssh+
maps from tailscale.com/clientupdate+
math from archive/tar+
math/big from crypto/dsa+
+15
View File
@@ -33,6 +33,21 @@ func TestOmitSSH(t *testing.T) {
}.Check(t)
}
func TestOmitSyslog(t *testing.T) {
const msg = "unexpected syslog usage with ts_omit_syslog"
deptest.DepChecker{
GOOS: "linux",
GOARCH: "amd64",
// Tailscale SSH's incubator also uses log/syslog, so omit
// SSH too to lock down the standard library package.
Tags: "ts_omit_syslog,ts_omit_ssh,ts_include_cli",
BadDeps: map[string]string{
"log/syslog": msg,
"tailscale.com/feature/syslog": msg,
},
}.Check(t)
}
func TestOmitSyspolicy(t *testing.T) {
const msg = "unexpected syspolicy usage with ts_omit_syspolicy"
deptest.DepChecker{
+7
View File
@@ -233,6 +233,9 @@ store state on filesystem.`)
if f, ok := hookRegisterOutboundProxyFlags.GetOk(); ok {
f()
}
if f, ok := feature.HookRegisterLogSinkFlags.GetOk(); ok {
f()
}
if runtime.GOOS == "plan9" && os.Getenv("_NETSHELL_CHILD_") != "" {
os.Args = []string{"tailscaled", "be-child", "plan9-netshell"}
@@ -261,6 +264,10 @@ store state on filesystem.`)
}
}
if f, ok := feature.HookLogSink.GetOk(); ok {
f() // redirects the default logger (e.g. to syslog) if requested by flags
}
if fd, ok := envknob.LookupInt("TS_PARENT_DEATH_FD"); ok && fd > 2 {
go dieOnPipeReadErrorOfFD(fd)
}