cli/configure: respect $KUBECONFIG (#11604)

cmd/tailscale/cli: respect $KUBECONFIG

* `$KUBECONFIG` is a `$PATH`-like: it defines a *list*.
`tailscale config kubeconfig` works like the rest of the
ecosystem so that if $KUBECONFIG is set it will write to the first existant file in the list, if none exist then
the final entry in the list.
* if `$KUBECONFIG` is an empty string, the old logic takes over.

Notes:

* The logic for file detection is inlined based on what `kind` does.
Technically it's a race condition, since the file could be removed/added
in between the processing steps, but the fallout shouldn't be too bad.
https://github.com/kubernetes-sigs/kind/blob/v0.23.0-alpha/pkg/cluster/internal/kubeconfig/internal/kubeconfig/paths.go

* The sandboxed (App Store) variant relies on a specific temporary
entitlement to access the ~/.kube/config file.
The entitlement is only granted to specific files, and so is not
applicable to paths supplied by the user at runtime.
While there may be other ways to achieve this access to arbitrary
kubeconfig files, it's out of scope for now.

Updates #11645

Signed-off-by: Chloé Vulquin <code@toast.bunkerlabs.net>
pull/11667/head
Chloé Vulquin 1 month ago committed by GitHub
parent c71e8db058
commit 0f3a292ebd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -43,7 +43,20 @@ See: https://tailscale.com/s/k8s-auth-proxy
}
// kubeconfigPath returns the path to the kubeconfig file for the current user.
func kubeconfigPath() string {
func kubeconfigPath() (string, error) {
if kubeconfig := os.Getenv("KUBECONFIG"); kubeconfig != "" {
if version.IsSandboxedMacOS() {
return "", errors.New("$KUBECONFIG is incompatible with the App Store version")
}
var out string
for _, out = range filepath.SplitList(kubeconfig) {
if info, err := os.Stat(out); !os.IsNotExist(err) && !info.IsDir() {
break
}
}
return out, nil
}
var dir string
if version.IsSandboxedMacOS() {
// The HOME environment variable in macOS sandboxed apps is set to
@ -55,7 +68,7 @@ func kubeconfigPath() string {
} else {
dir = homedir.HomeDir()
}
return filepath.Join(dir, ".kube", "config")
return filepath.Join(dir, ".kube", "config"), nil
}
func runConfigureKubeconfig(ctx context.Context, args []string) error {
@ -76,7 +89,11 @@ func runConfigureKubeconfig(ctx context.Context, args []string) error {
return fmt.Errorf("no peer found with hostname %q", hostOrFQDN)
}
targetFQDN = strings.TrimSuffix(targetFQDN, ".")
if err := setKubeconfigForPeer(targetFQDN, kubeconfigPath()); err != nil {
var kubeconfig string
if kubeconfig, err = kubeconfigPath(); err != nil {
return err
}
if err = setKubeconfigForPeer(targetFQDN, kubeconfig); err != nil {
return err
}
printf("kubeconfig configured for %q\n", hostOrFQDN)

Loading…
Cancel
Save