cmd/containerboot: load auth key from file if TS_AUTHKEY_FILE is set

This changes adds a new environment variable `TS_AUTHKEY_FILE`, that
can be used instead of `TS_AUTHKEY` to specify the auth key to use for
login. TS_AUTHKEY takes precedence over TS_AUTHKEY_FILE, if both are
set.

This is useful for cases where the auth key is stored in a docker secret
or similar, and makes it possible to avoid storing the auth key in a
docker compose file.

Fixes <https://github.com/tailscale/tailscale/issues/14210>

Signed-off-by: Josh McKinney <joshka@users.noreply.github.com>
pull/16091/head
Josh McKinney 6 months ago
parent cd49faa123
commit 6d84c2813a
No known key found for this signature in database
GPG Key ID: 722287396A903BC5

@ -12,6 +12,7 @@
// variables. All configuration is optional.
//
// - TS_AUTHKEY: the authkey to use for login.
// - TS_AUTHKEY_FILE: the path to a file containing the authkey to use for login.
// - TS_HOSTNAME: the hostname to request for the node.
// - TS_ROUTES: subnet routes to advertise. Explicitly setting it to an empty
// value will cause containerboot to stop acting as a subnet router for any

@ -262,6 +262,29 @@ func TestContainerBoot(t *testing.T) {
},
}
},
"authkey_file": func(env *testEnv) testCase {
authFile := filepath.Join(env.d, "authkey.txt")
if err := os.WriteFile(authFile, []byte("tskey-key"), 0600); err != nil {
t.Fatalf("Failed to write authkey file: %v", err)
}
return testCase{
// Userspace mode, ephemeral storage, authkey provided via file.
Env: map[string]string{
"TS_AUTHKEY_FILE": authFile,
},
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 up --accept-dns=false --authkey=tskey-key",
},
},
{
Notify: runningNotify,
},
},
}
},
"authkey_disk_state": func(env *testEnv) testCase {
return testCase{
Env: map[string]string{

@ -85,7 +85,7 @@ type settings struct {
func configFromEnv() (*settings, error) {
cfg := &settings{
AuthKey: defaultEnvs([]string{"TS_AUTHKEY", "TS_AUTH_KEY"}, ""),
AuthKey: defaultEnvs([]string{"TS_AUTHKEY", "TS_AUTH_KEY", "TS_AUTHKEY_FILE", "TS_AUTH_KEY_FILE"}, ""),
Hostname: defaultEnv("TS_HOSTNAME", ""),
Routes: defaultEnvStringPointer("TS_ROUTES"),
ServeConfigPath: defaultEnv("TS_SERVE_CONFIG", ""),
@ -365,9 +365,21 @@ func defaultEnvBoolPointer(name string) *bool {
return &ret
}
// defaultEnvs returns the value of the first envvar in names that is set,
// or defVal if none are set. If the envvar ends with "_FILE", it reads the
// value from the file specified by the envvar.
func defaultEnvs(names []string, defVal string) string {
for _, name := range names {
if v, ok := os.LookupEnv(name); ok {
if strings.HasSuffix(name, "_FILE") {
if filepath, ok := os.LookupEnv(name); ok {
data, err := os.ReadFile(filepath)
if err != nil {
log.Printf("error reading env var %s from file %s: %v", name, filepath, err)
continue
}
return strings.TrimSpace(string(data))
}
} else if v, ok := os.LookupEnv(name); ok {
return v
}
}

Loading…
Cancel
Save