From 758c28fd41173fe0cde7c1020fd52d78b410eb82 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Fri, 3 Jul 2026 08:04:37 +0000 Subject: [PATCH] cmd/tailscaled: allow "optional:" prefix on -config optional:vm:user-data boots unconfigured when the source is absent instead of failing; an invalid config still fails. Updates #1866 Signed-off-by: Kristoffer Dalby --- cmd/tailscaled/tailscaled.go | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/cmd/tailscaled/tailscaled.go b/cmd/tailscaled/tailscaled.go index 325b66182..c1408bc6a 100644 --- a/cmd/tailscaled/tailscaled.go +++ b/cmd/tailscaled/tailscaled.go @@ -224,7 +224,7 @@ func main() { } flag.BoolVar(&printVersion, "version", false, "print version information and exit") flag.BoolVar(&args.disableLogs, "no-logs-no-support", false, "disable log uploads; this also disables any technical support") - flag.StringVar(&args.confFile, "config", "", "path to config file, or 'vm:user-data' to use the VM's user-data (EC2)") + flag.StringVar(&args.confFile, "config", "", "path to config file, or 'vm:user-data' to use the VM's user-data (EC2); prefix with 'optional:' to boot unconfigured when the source is absent instead of failing") if buildfeatures.HasTPM { flag.Var(&args.hardwareAttestation, "hardware-attestation", `use hardware-backed keys to bind node identity to this device when supported by the OS and hardware. Uses TPM 2.0 on Linux and Windows; SecureEnclave on @@ -432,11 +432,24 @@ func run() (err error) { // Parse config, if specified, to fail early if it's invalid. var conf *conffile.Config if args.confFile != "" { - conf, err = conffile.Load(args.confFile) - if err != nil { + // An "optional:" prefix (e.g. "optional:vm:user-data") means it's fine + // for the config source to be absent: boot unconfigured and let the node + // be enrolled interactively instead of failing to start. A config that's + // present but invalid still fails, even when optional. + path := args.confFile + optional := false + if p, ok := strings.CutPrefix(path, "optional:"); ok { + optional, path = true, p + } + conf, err = conffile.Load(path) + switch { + case err == nil: + sys.InitialConfig = conf + case optional && errors.Is(err, conffile.ErrNoConfig): + logf("config: none present at %q; continuing unconfigured", path) + default: return fmt.Errorf("error reading config file: %w", err) } - sys.InitialConfig = conf } var netMon *netmon.Monitor