mirror of
https://github.com/tailscale/tailscale.git
synced 2025-07-30 15:53:45 +00:00

* Modifies the k8s-proxy to expose health check and metrics endpoints on the Pod's IP. * Moves cmd/containerboot/healthz.go and cmd/containerboot/metrics.go to /kube to be shared with /k8s-proxy. Updates #13358 Signed-off-by: David Bond <davidsbond93@gmail.com>
129 lines
4.8 KiB
Go
129 lines
4.8 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"
|
|
"errors"
|
|
"fmt"
|
|
"net/netip"
|
|
|
|
"github.com/tailscale/hujson"
|
|
"tailscale.com/kube/kubetypes"
|
|
"tailscale.com/tailcfg"
|
|
"tailscale.com/types/opt"
|
|
)
|
|
|
|
const v1Alpha1 = "v1alpha1"
|
|
|
|
// Config describes a config file.
|
|
type Config struct {
|
|
Raw []byte // raw bytes, in HuJSON form
|
|
Std []byte // standardized JSON form
|
|
Version string // "v1alpha1"
|
|
|
|
// Parsed is the parsed config, converted from its raw bytes 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"` // Tailscale auth key to use.
|
|
State *string `json:",omitempty"` // Path to the Tailscale state.
|
|
LogLevel *string `json:",omitempty"` // "debug", "info". Defaults to "info".
|
|
App *string `json:",omitempty"` // e.g. kubetypes.AppProxyGroupKubeAPIServer
|
|
ServerURL *string `json:",omitempty"` // URL of the Tailscale coordination server.
|
|
LocalAddr *string `json:",omitempty"` // The address to use for serving HTTP health checks and metrics (defaults to all interfaces).
|
|
LocalPort *uint16 `json:",omitempty"` // The port to use for serving HTTP health checks and metrics (defaults to 9002).
|
|
MetricsEnabled opt.Bool `json:",omitempty"` // Serve metrics on <LocalAddr>:<LocalPort>/metrics.
|
|
HealthCheckEnabled opt.Bool `json:",omitempty"` // Serve health check on <LocalAddr>:<LocalPort>/metrics.
|
|
|
|
// TODO(tomhjp): The remaining fields should all be reloadable during
|
|
// runtime, but currently missing most of the APIServerProxy fields.
|
|
Hostname *string `json:",omitempty"` // Tailscale device hostname.
|
|
AcceptRoutes opt.Bool `json:",omitempty"` // Accepts routes advertised by other Tailscale nodes.
|
|
AdvertiseServices []string `json:",omitempty"` // Tailscale Services to advertise.
|
|
APIServerProxy *APIServerProxyConfig `json:",omitempty"` // Config specific to the API Server proxy.
|
|
StaticEndpoints []netip.AddrPort `json:",omitempty"` // StaticEndpoints are additional, user-defined endpoints that this node should advertise amongst its wireguard endpoints.
|
|
}
|
|
|
|
type APIServerProxyConfig struct {
|
|
Enabled opt.Bool `json:",omitempty"` // Whether to enable the API Server proxy.
|
|
Mode *kubetypes.APIServerProxyMode `json:",omitempty"` // "auth" or "noauth" mode.
|
|
ServiceName *tailcfg.ServiceName `json:",omitempty"` // Name of the Tailscale Service to advertise.
|
|
IssueCerts opt.Bool `json:",omitempty"` // Whether this replica should issue TLS certs for the Tailscale Service.
|
|
}
|
|
|
|
// Load reads and parses the config file at the provided path on disk.
|
|
func Load(raw []byte) (c Config, err error) {
|
|
c.Raw = raw
|
|
c.Std, err = hujson.Standardize(c.Raw)
|
|
if err != nil {
|
|
return c, fmt.Errorf("error parsing config as HuJSON/JSON: %w", err)
|
|
}
|
|
var ver VersionedConfig
|
|
if err := json.Unmarshal(c.Std, &ver); err != nil {
|
|
return c, fmt.Errorf("error parsing config: %w", err)
|
|
}
|
|
rootV1Alpha1 := (ver.Version == v1Alpha1)
|
|
backCompatV1Alpha1 := (ver.V1Alpha1 != nil)
|
|
switch {
|
|
case ver.Version == "":
|
|
return c, errors.New("error parsing config: no \"version\" field provided")
|
|
case rootV1Alpha1 && backCompatV1Alpha1:
|
|
// Exactly one of these should be set.
|
|
return c, errors.New("error parsing config: both root and v1alpha1 config provided")
|
|
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: unsupported \"version\" value %q; want \"%s\"", ver.Version, v1Alpha1)
|
|
}
|
|
|
|
return c, nil
|
|
}
|
|
|
|
func (c *Config) GetLocalAddr() string {
|
|
if c.Parsed.LocalAddr == nil {
|
|
return "[::]"
|
|
}
|
|
|
|
return *c.Parsed.LocalAddr
|
|
}
|
|
|
|
func (c *Config) GetLocalPort() uint16 {
|
|
if c.Parsed.LocalPort == nil {
|
|
return uint16(9002)
|
|
}
|
|
|
|
return *c.Parsed.LocalPort
|
|
}
|