diff --git a/cmd/containerboot/main.go b/cmd/containerboot/main.go index 8d97df4e2..e5984a5b5 100644 --- a/cmd/containerboot/main.go +++ b/cmd/containerboot/main.go @@ -19,8 +19,7 @@ // - TS_TAILNET_TARGET_IP: proxy all incoming non-Tailscale traffic to the given // destination. // - TS_TAILSCALED_EXTRA_ARGS: extra arguments to 'tailscaled'. -// - TS_EXTRA_ARGS: extra arguments to 'tailscale login', these are not -// reset on restart. +// - TS_EXTRA_ARGS: extra arguments to 'tailscale up'. // - TS_USERSPACE: run with userspace networking (the default) // instead of kernel networking. // - TS_STATE_DIR: the directory in which to store tailscaled @@ -36,15 +35,9 @@ // - TS_SOCKET: the path where the tailscaled LocalAPI socket should // be created. // - TS_AUTH_ONCE: if true, only attempt to log in if not already -// logged in. If false, forcibly log in every time the container starts. -// The default until 1.50.0 was false, but that was misleading: until -// 1.50, containerboot used `tailscale up` which would ignore an authkey -// argument if there was already a node key. Effectively, this behaved -// as though TS_AUTH_ONCE were always true. -// In 1.50.0 the change was made to use `tailscale login` instead of `up`, -// and login will reauthenticate every time it is given an authkey. -// In 1.50.1 we set the TS_AUTH_ONCE to true, to match the previously -// observed behavior. +// logged in. If false (the default, for backwards +// compatibility), forcibly log in every time the +// container starts. // - TS_SERVE_CONFIG: if specified, is the file path where the ipn.ServeConfig is located. // It will be applied once tailscaled is up and running. If the file contains // ${TS_CERT_DOMAIN}, it will be replaced with the value of the available FQDN. @@ -118,7 +111,7 @@ func main() { SOCKSProxyAddr: defaultEnv("TS_SOCKS5_SERVER", ""), HTTPProxyAddr: defaultEnv("TS_OUTBOUND_HTTP_PROXY_LISTEN", ""), Socket: defaultEnv("TS_SOCKET", "/tmp/tailscaled.sock"), - AuthOnce: defaultBool("TS_AUTH_ONCE", true), + AuthOnce: defaultBool("TS_AUTH_ONCE", false), Root: defaultEnv("TS_TEST_ONLY_ROOT", "/"), } @@ -212,7 +205,7 @@ func main() { } didLogin = true w.Close() - if err := tailscaleLogin(bootCtx, cfg); err != nil { + if err := tailscaleUp(bootCtx, cfg); err != nil { return fmt.Errorf("failed to auth tailscale: %v", err) } w, err = client.WatchIPNBus(bootCtx, ipn.NotifyInitialNetMap|ipn.NotifyInitialState) @@ -262,10 +255,12 @@ func main() { ctx, cancel := context.WithCancel(context.Background()) // no deadline now that we're in steady state defer cancel() - // Now that we are authenticated, we can set/reset any of the - // settings that we need to. - if err := tailscaleSet(ctx, cfg); err != nil { - log.Fatalf("failed to auth tailscale: %v", err) + if cfg.AuthOnce { + // Now that we are authenticated, we can set/reset any of the + // settings that we need to. + if err := tailscaleSet(ctx, cfg); err != nil { + log.Fatalf("failed to auth tailscale: %v", err) + } } if cfg.ServeConfigPath != "" { @@ -545,29 +540,40 @@ func tailscaledArgs(cfg *settings) []string { return args } -// tailscaleLogin uses cfg to run 'tailscale login' everytime containerboot -// starts, or if TS_AUTH_ONCE is set, only the first time containerboot starts. -func tailscaleLogin(ctx context.Context, cfg *settings) error { - args := []string{"--socket=" + cfg.Socket, "login"} +// tailscaleUp uses cfg to run 'tailscale up' everytime containerboot starts, or +// if TS_AUTH_ONCE is set, only the first time containerboot starts. +func tailscaleUp(ctx context.Context, cfg *settings) error { + args := []string{"--socket=" + cfg.Socket, "up"} + if cfg.AcceptDNS { + args = append(args, "--accept-dns=true") + } else { + args = append(args, "--accept-dns=false") + } if cfg.AuthKey != "" { args = append(args, "--authkey="+cfg.AuthKey) } + if cfg.Routes != "" { + args = append(args, "--advertise-routes="+cfg.Routes) + } + if cfg.Hostname != "" { + args = append(args, "--hostname="+cfg.Hostname) + } if cfg.ExtraArgs != "" { args = append(args, strings.Fields(cfg.ExtraArgs)...) } - log.Printf("Running 'tailscale login'") + log.Printf("Running 'tailscale up'") cmd := exec.CommandContext(ctx, "tailscale", args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { - return fmt.Errorf("tailscale login failed: %v", err) + return fmt.Errorf("tailscale up failed: %v", err) } return nil } // tailscaleSet uses cfg to run 'tailscale set' to set any known configuration // options that are passed in via environment variables. This is run after the -// node is in Running state. +// node is in Running state and only if TS_AUTH_ONCE is set. func tailscaleSet(ctx context.Context, cfg *settings) error { args := []string{"--socket=" + cfg.Socket, "set"} if cfg.AcceptDNS { diff --git a/cmd/containerboot/main_test.go b/cmd/containerboot/main_test.go index 7a5280b6e..88d70b025 100644 --- a/cmd/containerboot/main_test.go +++ b/cmd/containerboot/main_test.go @@ -129,22 +129,16 @@ type phase struct { { // Out of the box default: runs in userspace mode, ephemeral storage, interactive login. Name: "no_args", - Env: map[string]string{ - "TS_AUTH_ONCE": "false", - }, - + Env: nil, Phases: []phase{ { WantCmds: []string{ "/usr/bin/tailscaled --socket=/tmp/tailscaled.sock --state=mem: --statedir=/tmp --tun=userspace-networking", - "/usr/bin/tailscale --socket=/tmp/tailscaled.sock login", + "/usr/bin/tailscale --socket=/tmp/tailscaled.sock up --accept-dns=false", }, }, { Notify: runningNotify, - WantCmds: []string{ - "/usr/bin/tailscale --socket=/tmp/tailscaled.sock set --accept-dns=false", - }, }, }, }, @@ -152,21 +146,17 @@ type phase struct { // Userspace mode, ephemeral storage, authkey provided on every run. Name: "authkey", Env: map[string]string{ - "TS_AUTHKEY": "tskey-key", - "TS_AUTH_ONCE": "false", + "TS_AUTHKEY": "tskey-key", }, Phases: []phase{ { WantCmds: []string{ "/usr/bin/tailscaled --socket=/tmp/tailscaled.sock --state=mem: --statedir=/tmp --tun=userspace-networking", - "/usr/bin/tailscale --socket=/tmp/tailscaled.sock login --authkey=tskey-key", + "/usr/bin/tailscale --socket=/tmp/tailscaled.sock up --accept-dns=false --authkey=tskey-key", }, }, { Notify: runningNotify, - WantCmds: []string{ - "/usr/bin/tailscale --socket=/tmp/tailscaled.sock set --accept-dns=false", - }, }, }, }, @@ -174,21 +164,17 @@ type phase struct { // Userspace mode, ephemeral storage, authkey provided on every run. Name: "authkey-old-flag", Env: map[string]string{ - "TS_AUTH_KEY": "tskey-key", - "TS_AUTH_ONCE": "false", + "TS_AUTH_KEY": "tskey-key", }, Phases: []phase{ { WantCmds: []string{ "/usr/bin/tailscaled --socket=/tmp/tailscaled.sock --state=mem: --statedir=/tmp --tun=userspace-networking", - "/usr/bin/tailscale --socket=/tmp/tailscaled.sock login --authkey=tskey-key", + "/usr/bin/tailscale --socket=/tmp/tailscaled.sock up --accept-dns=false --authkey=tskey-key", }, }, { Notify: runningNotify, - WantCmds: []string{ - "/usr/bin/tailscale --socket=/tmp/tailscaled.sock set --accept-dns=false", - }, }, }, }, @@ -197,35 +183,30 @@ type phase struct { Env: map[string]string{ "TS_AUTHKEY": "tskey-key", "TS_STATE_DIR": filepath.Join(d, "tmp"), - "TS_AUTH_ONCE": "false", }, Phases: []phase{ { WantCmds: []string{ "/usr/bin/tailscaled --socket=/tmp/tailscaled.sock --statedir=/tmp --tun=userspace-networking", - "/usr/bin/tailscale --socket=/tmp/tailscaled.sock login --authkey=tskey-key", + "/usr/bin/tailscale --socket=/tmp/tailscaled.sock up --accept-dns=false --authkey=tskey-key", }, }, { Notify: runningNotify, - WantCmds: []string{ - "/usr/bin/tailscale --socket=/tmp/tailscaled.sock set --accept-dns=false", - }, }, }, }, { Name: "routes", Env: map[string]string{ - "TS_AUTHKEY": "tskey-key", - "TS_ROUTES": "1.2.3.0/24,10.20.30.0/24", - "TS_AUTH_ONCE": "false", + "TS_AUTHKEY": "tskey-key", + "TS_ROUTES": "1.2.3.0/24,10.20.30.0/24", }, Phases: []phase{ { WantCmds: []string{ "/usr/bin/tailscaled --socket=/tmp/tailscaled.sock --state=mem: --statedir=/tmp --tun=userspace-networking", - "/usr/bin/tailscale --socket=/tmp/tailscaled.sock login --authkey=tskey-key", + "/usr/bin/tailscale --socket=/tmp/tailscaled.sock up --accept-dns=false --authkey=tskey-key --advertise-routes=1.2.3.0/24,10.20.30.0/24", }, }, { @@ -234,9 +215,6 @@ type phase struct { "proc/sys/net/ipv4/ip_forward": "0", "proc/sys/net/ipv6/conf/all/forwarding": "0", }, - WantCmds: []string{ - "/usr/bin/tailscale --socket=/tmp/tailscaled.sock set --accept-dns=false --advertise-routes=1.2.3.0/24,10.20.30.0/24", - }, }, }, }, @@ -246,13 +224,12 @@ type phase struct { "TS_AUTHKEY": "tskey-key", "TS_ROUTES": "1.2.3.0/24,10.20.30.0/24", "TS_USERSPACE": "false", - "TS_AUTH_ONCE": "false", }, Phases: []phase{ { WantCmds: []string{ "/usr/bin/tailscaled --socket=/tmp/tailscaled.sock --state=mem: --statedir=/tmp", - "/usr/bin/tailscale --socket=/tmp/tailscaled.sock login --authkey=tskey-key", + "/usr/bin/tailscale --socket=/tmp/tailscaled.sock up --accept-dns=false --authkey=tskey-key --advertise-routes=1.2.3.0/24,10.20.30.0/24", }, }, { @@ -261,9 +238,6 @@ type phase struct { "proc/sys/net/ipv4/ip_forward": "1", "proc/sys/net/ipv6/conf/all/forwarding": "0", }, - WantCmds: []string{ - "/usr/bin/tailscale --socket=/tmp/tailscaled.sock set --accept-dns=false --advertise-routes=1.2.3.0/24,10.20.30.0/24", - }, }, }, }, @@ -273,13 +247,12 @@ type phase struct { "TS_AUTHKEY": "tskey-key", "TS_ROUTES": "::/64,1::/64", "TS_USERSPACE": "false", - "TS_AUTH_ONCE": "false", }, Phases: []phase{ { WantCmds: []string{ "/usr/bin/tailscaled --socket=/tmp/tailscaled.sock --state=mem: --statedir=/tmp", - "/usr/bin/tailscale --socket=/tmp/tailscaled.sock login --authkey=tskey-key", + "/usr/bin/tailscale --socket=/tmp/tailscaled.sock up --accept-dns=false --authkey=tskey-key --advertise-routes=::/64,1::/64", }, }, { @@ -288,9 +261,6 @@ type phase struct { "proc/sys/net/ipv4/ip_forward": "0", "proc/sys/net/ipv6/conf/all/forwarding": "1", }, - WantCmds: []string{ - "/usr/bin/tailscale --socket=/tmp/tailscaled.sock set --accept-dns=false --advertise-routes=::/64,1::/64", - }, }, }, }, @@ -300,13 +270,12 @@ type phase struct { "TS_AUTHKEY": "tskey-key", "TS_ROUTES": "::/64,1.2.3.0/24", "TS_USERSPACE": "false", - "TS_AUTH_ONCE": "false", }, Phases: []phase{ { WantCmds: []string{ "/usr/bin/tailscaled --socket=/tmp/tailscaled.sock --state=mem: --statedir=/tmp", - "/usr/bin/tailscale --socket=/tmp/tailscaled.sock login --authkey=tskey-key", + "/usr/bin/tailscale --socket=/tmp/tailscaled.sock up --accept-dns=false --authkey=tskey-key --advertise-routes=::/64,1.2.3.0/24", }, }, { @@ -315,9 +284,6 @@ type phase struct { "proc/sys/net/ipv4/ip_forward": "1", "proc/sys/net/ipv6/conf/all/forwarding": "1", }, - WantCmds: []string{ - "/usr/bin/tailscale --socket=/tmp/tailscaled.sock set --accept-dns=false --advertise-routes=::/64,1.2.3.0/24", - }, }, }, }, @@ -327,20 +293,16 @@ type phase struct { "TS_AUTHKEY": "tskey-key", "TS_DEST_IP": "1.2.3.4", "TS_USERSPACE": "false", - "TS_AUTH_ONCE": "false", }, Phases: []phase{ { WantCmds: []string{ "/usr/bin/tailscaled --socket=/tmp/tailscaled.sock --state=mem: --statedir=/tmp", - "/usr/bin/tailscale --socket=/tmp/tailscaled.sock login --authkey=tskey-key", + "/usr/bin/tailscale --socket=/tmp/tailscaled.sock up --accept-dns=false --authkey=tskey-key", }, }, { Notify: runningNotify, - WantCmds: []string{ - "/usr/bin/tailscale --socket=/tmp/tailscaled.sock set --accept-dns=false", - }, }, }, }, @@ -350,20 +312,16 @@ type phase struct { "TS_AUTHKEY": "tskey-key", "TS_TAILNET_TARGET_IP": "100.99.99.99", "TS_USERSPACE": "false", - "TS_AUTH_ONCE": "false", }, Phases: []phase{ { WantCmds: []string{ "/usr/bin/tailscaled --socket=/tmp/tailscaled.sock --state=mem: --statedir=/tmp", - "/usr/bin/tailscale --socket=/tmp/tailscaled.sock login --authkey=tskey-key", + "/usr/bin/tailscale --socket=/tmp/tailscaled.sock up --accept-dns=false --authkey=tskey-key", }, }, { Notify: runningNotify, - WantCmds: []string{ - "/usr/bin/tailscale --socket=/tmp/tailscaled.sock set --accept-dns=false", - }, }, }, }, @@ -384,7 +342,7 @@ type phase struct { State: ptr.To(ipn.NeedsLogin), }, WantCmds: []string{ - "/usr/bin/tailscale --socket=/tmp/tailscaled.sock login --authkey=tskey-key", + "/usr/bin/tailscale --socket=/tmp/tailscaled.sock up --accept-dns=false --authkey=tskey-key", }, }, { @@ -400,7 +358,6 @@ type phase struct { Env: map[string]string{ "KUBERNETES_SERVICE_HOST": kube.Host, "KUBERNETES_SERVICE_PORT_HTTPS": kube.Port, - "TS_AUTH_ONCE": "false", }, KubeSecret: map[string]string{ "authkey": "tskey-key", @@ -409,7 +366,7 @@ type phase struct { { WantCmds: []string{ "/usr/bin/tailscaled --socket=/tmp/tailscaled.sock --state=kube:tailscale --statedir=/tmp --tun=userspace-networking", - "/usr/bin/tailscale --socket=/tmp/tailscaled.sock login --authkey=tskey-key", + "/usr/bin/tailscale --socket=/tmp/tailscaled.sock up --accept-dns=false --authkey=tskey-key", }, WantKubeSecret: map[string]string{ "authkey": "tskey-key", @@ -417,9 +374,6 @@ type phase struct { }, { Notify: runningNotify, - WantCmds: []string{ - "/usr/bin/tailscale --socket=/tmp/tailscaled.sock set --accept-dns=false", - }, WantKubeSecret: map[string]string{ "authkey": "tskey-key", "device_fqdn": "test-node.test.ts.net", @@ -438,22 +392,18 @@ type phase struct { "TS_KUBE_SECRET": "", "TS_STATE_DIR": filepath.Join(d, "tmp"), "TS_AUTHKEY": "tskey-key", - "TS_AUTH_ONCE": "false", }, KubeSecret: map[string]string{}, Phases: []phase{ { WantCmds: []string{ "/usr/bin/tailscaled --socket=/tmp/tailscaled.sock --statedir=/tmp --tun=userspace-networking", - "/usr/bin/tailscale --socket=/tmp/tailscaled.sock login --authkey=tskey-key", + "/usr/bin/tailscale --socket=/tmp/tailscaled.sock up --accept-dns=false --authkey=tskey-key", }, WantKubeSecret: map[string]string{}, }, { - Notify: runningNotify, - WantCmds: []string{ - "/usr/bin/tailscale --socket=/tmp/tailscaled.sock set --accept-dns=false", - }, + Notify: runningNotify, WantKubeSecret: map[string]string{}, }, }, @@ -464,7 +414,6 @@ type phase struct { "KUBERNETES_SERVICE_HOST": kube.Host, "KUBERNETES_SERVICE_PORT_HTTPS": kube.Port, "TS_AUTHKEY": "tskey-key", - "TS_AUTH_ONCE": "false", }, KubeSecret: map[string]string{}, KubeDenyPatch: true, @@ -472,15 +421,12 @@ type phase struct { { WantCmds: []string{ "/usr/bin/tailscaled --socket=/tmp/tailscaled.sock --state=kube:tailscale --statedir=/tmp --tun=userspace-networking", - "/usr/bin/tailscale --socket=/tmp/tailscaled.sock login --authkey=tskey-key", + "/usr/bin/tailscale --socket=/tmp/tailscaled.sock up --accept-dns=false --authkey=tskey-key", }, WantKubeSecret: map[string]string{}, }, { - Notify: runningNotify, - WantCmds: []string{ - "/usr/bin/tailscale --socket=/tmp/tailscaled.sock set --accept-dns=false", - }, + Notify: runningNotify, WantKubeSecret: map[string]string{}, }, }, @@ -510,7 +456,7 @@ type phase struct { State: ptr.To(ipn.NeedsLogin), }, WantCmds: []string{ - "/usr/bin/tailscale --socket=/tmp/tailscaled.sock login --authkey=tskey-key", + "/usr/bin/tailscale --socket=/tmp/tailscaled.sock up --accept-dns=false --authkey=tskey-key", }, WantKubeSecret: map[string]string{ "authkey": "tskey-key", @@ -534,7 +480,6 @@ type phase struct { Env: map[string]string{ "KUBERNETES_SERVICE_HOST": kube.Host, "KUBERNETES_SERVICE_PORT_HTTPS": kube.Port, - "TS_AUTH_ONCE": "false", }, KubeSecret: map[string]string{ "authkey": "tskey-key", @@ -543,7 +488,7 @@ type phase struct { { WantCmds: []string{ "/usr/bin/tailscaled --socket=/tmp/tailscaled.sock --state=kube:tailscale --statedir=/tmp --tun=userspace-networking", - "/usr/bin/tailscale --socket=/tmp/tailscaled.sock login --authkey=tskey-key", + "/usr/bin/tailscale --socket=/tmp/tailscaled.sock up --accept-dns=false --authkey=tskey-key", }, WantKubeSecret: map[string]string{ "authkey": "tskey-key", @@ -551,9 +496,6 @@ type phase struct { }, { Notify: runningNotify, - WantCmds: []string{ - "/usr/bin/tailscale --socket=/tmp/tailscaled.sock set --accept-dns=false", - }, WantKubeSecret: map[string]string{ "authkey": "tskey-key", "device_fqdn": "test-node.test.ts.net", @@ -586,20 +528,16 @@ type phase struct { Env: map[string]string{ "TS_SOCKS5_SERVER": "localhost:1080", "TS_OUTBOUND_HTTP_PROXY_LISTEN": "localhost:8080", - "TS_AUTH_ONCE": "false", }, Phases: []phase{ { WantCmds: []string{ "/usr/bin/tailscaled --socket=/tmp/tailscaled.sock --state=mem: --statedir=/tmp --tun=userspace-networking --socks5-server=localhost:1080 --outbound-http-proxy-listen=localhost:8080", - "/usr/bin/tailscale --socket=/tmp/tailscaled.sock login", + "/usr/bin/tailscale --socket=/tmp/tailscaled.sock up --accept-dns=false", }, }, { Notify: runningNotify, - WantCmds: []string{ - "/usr/bin/tailscale --socket=/tmp/tailscaled.sock set --accept-dns=false", - }, }, }, }, @@ -607,20 +545,16 @@ type phase struct { Name: "dns", Env: map[string]string{ "TS_ACCEPT_DNS": "true", - "TS_AUTH_ONCE": "false", }, Phases: []phase{ { WantCmds: []string{ "/usr/bin/tailscaled --socket=/tmp/tailscaled.sock --state=mem: --statedir=/tmp --tun=userspace-networking", - "/usr/bin/tailscale --socket=/tmp/tailscaled.sock login", + "/usr/bin/tailscale --socket=/tmp/tailscaled.sock up --accept-dns=true", }, }, { Notify: runningNotify, - WantCmds: []string{ - "/usr/bin/tailscale --socket=/tmp/tailscaled.sock set --accept-dns=true", - }, }, }, }, @@ -629,41 +563,31 @@ type phase struct { Env: map[string]string{ "TS_EXTRA_ARGS": "--widget=rotated", "TS_TAILSCALED_EXTRA_ARGS": "--experiments=widgets", - "TS_AUTH_ONCE": "false", }, Phases: []phase{ { WantCmds: []string{ "/usr/bin/tailscaled --socket=/tmp/tailscaled.sock --state=mem: --statedir=/tmp --tun=userspace-networking --experiments=widgets", - "/usr/bin/tailscale --socket=/tmp/tailscaled.sock login --widget=rotated", + "/usr/bin/tailscale --socket=/tmp/tailscaled.sock up --accept-dns=false --widget=rotated", }, - }, - { + }, { Notify: runningNotify, - WantCmds: []string{ - "/usr/bin/tailscale --socket=/tmp/tailscaled.sock set --accept-dns=false", - }, }, }, }, { Name: "hostname", Env: map[string]string{ - "TS_HOSTNAME": "my-server", - "TS_AUTH_ONCE": "false", + "TS_HOSTNAME": "my-server", }, Phases: []phase{ { WantCmds: []string{ "/usr/bin/tailscaled --socket=/tmp/tailscaled.sock --state=mem: --statedir=/tmp --tun=userspace-networking", - "/usr/bin/tailscale --socket=/tmp/tailscaled.sock login", + "/usr/bin/tailscale --socket=/tmp/tailscaled.sock up --accept-dns=false --hostname=my-server", }, - }, - { + }, { Notify: runningNotify, - WantCmds: []string{ - "/usr/bin/tailscale --socket=/tmp/tailscaled.sock set --accept-dns=false --hostname=my-server", - }, }, }, },