mirror of
https://github.com/tailscale/tailscale.git
synced 2025-06-29 11:38:39 +00:00

Adds a new k8s-proxy command to convert operator's in-process proxy to a separately deployable type of ProxyGroup: kube-apiserver. k8s-proxy reads in a new config file written by the operator, modelled on tailscaled's conffile but with some modifications to ensure multiple versions of the config can co-exist within a file. This should make it much easier to support reading that config file from a Kube Secret with a stable file name. The operator's RBAC has had some updates to ensure it can delegate the impersonation permissions that k8s-proxy requires to run its API Server proxy in auth mode where it can impersonate users and groups. Proxies deployed by kube-apiserver ProxyGroups currently work the same as the operator's in-process proxy. They do not yet leverage Tailscale Services for presenting a single HA DNS name. Updates #13358 Change-Id: Ib6ead69b2173c5e1929f3c13fb48a9a5362195d8 Signed-off-by: Tom Proctor <tomhjp@users.noreply.github.com>
102 lines
3.2 KiB
Go
102 lines
3.2 KiB
Go
// Copyright (c) Tailscale Inc & AUTHORS
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
//go:build !plan9
|
|
|
|
// Package conf contains code to load, manipulate, and access config file
|
|
// settings for k8s-proxy.
|
|
package conf
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
|
|
"github.com/tailscale/hujson"
|
|
"tailscale.com/types/opt"
|
|
)
|
|
|
|
const v1Alpha1 = "v1alpha1"
|
|
|
|
// Config describes a config file.
|
|
type Config struct {
|
|
Path string // disk path of HuJSON
|
|
Raw []byte // raw bytes from disk, in HuJSON form
|
|
Std []byte // standardized JSON form
|
|
Version string // "v1alpha1"
|
|
|
|
// Parsed is the parsed config, converted from its on-disk version to the
|
|
// latest known format.
|
|
Parsed ConfigV1Alpha1
|
|
}
|
|
|
|
// VersionedConfig allows specifying config at the root of the object, or in
|
|
// a versioned sub-object.
|
|
// e.g. {"version": "v1alpha1", "authKey": "abc123"}
|
|
// or {"version": "v1beta1", "a-beta-config": "a-beta-value", "v1alpha1": {"authKey": "abc123"}}
|
|
type VersionedConfig struct {
|
|
Version string `json:",omitempty"` // "v1alpha1"
|
|
|
|
// Latest version of the config.
|
|
*ConfigV1Alpha1
|
|
|
|
// Backwards compatibility version(s) of the config. Fields and sub-fields
|
|
// from here should only be added to, never changed in place.
|
|
V1Alpha1 *ConfigV1Alpha1 `json:",omitempty"`
|
|
// V1Beta1 *ConfigV1Beta1 `json:",omitempty"` // Not yet used.
|
|
}
|
|
|
|
type ConfigV1Alpha1 struct {
|
|
AuthKey *string `json:",omitempty"`
|
|
Hostname *string `json:",omitempty"`
|
|
State *string `json:",omitempty"` // Path to the Tailscale state.
|
|
LogLevel *string `json:",omitempty"` // "debug", "info", "warn", "error"
|
|
App *string `json:",omitempty"` // e.g. kubetypes.AppProxyGroupKubeAPIServer
|
|
KubeAPIServer *KubeAPIServer `json:",omitempty"` // Config specific to the API Server proxy.
|
|
}
|
|
|
|
type KubeAPIServer struct {
|
|
AuthMode opt.Bool `json:",omitempty"`
|
|
}
|
|
|
|
// Load reads and parses the config file at the provided path on disk.
|
|
func Load(path string) (c Config, err error) {
|
|
c.Path = path
|
|
|
|
c.Raw, err = os.ReadFile(path)
|
|
if err != nil {
|
|
return c, fmt.Errorf("error reading config file: %w", err)
|
|
}
|
|
c.Std, err = hujson.Standardize(c.Raw)
|
|
if err != nil {
|
|
return c, fmt.Errorf("error parsing config file %s HuJSON/JSON: %w", path, err)
|
|
}
|
|
var ver VersionedConfig
|
|
if err := json.Unmarshal(c.Std, &ver); err != nil {
|
|
return c, fmt.Errorf("error parsing config file %s: %w", path, err)
|
|
}
|
|
rootV1Alpha1 := (ver.Version == v1Alpha1)
|
|
backCompatV1Alpha1 := (ver.V1Alpha1 != nil)
|
|
switch {
|
|
case ver.Version == "":
|
|
return c, fmt.Errorf("error parsing config file %s: no \"version\" field provided", path)
|
|
case rootV1Alpha1 && backCompatV1Alpha1:
|
|
// Exactly one of these should be set.
|
|
return c, fmt.Errorf("error parsing config file %s: both root and v1alpha1 config provided", path)
|
|
case rootV1Alpha1 != backCompatV1Alpha1:
|
|
c.Version = v1Alpha1
|
|
switch {
|
|
case rootV1Alpha1 && ver.ConfigV1Alpha1 != nil:
|
|
c.Parsed = *ver.ConfigV1Alpha1
|
|
case backCompatV1Alpha1:
|
|
c.Parsed = *ver.V1Alpha1
|
|
default:
|
|
c.Parsed = ConfigV1Alpha1{}
|
|
}
|
|
default:
|
|
return c, fmt.Errorf("error parsing config file %q: unsupported \"version\" value %q; want \"%s\"", path, ver.Version, v1Alpha1)
|
|
}
|
|
|
|
return c, nil
|
|
}
|