Files
tailscale/kube/kubetypes/types.go
T
Tom Proctor 95a135ead1 cmd/{containerboot,k8s-operator}: reissue auth keys for broken proxies (#16450)
Adds logic for containerboot to signal that it can't auth, so the
operator can reissue a new auth key. This only applies when running with
a config file and with a kube state store.

If the operator sees reissue_authkey in a state Secret, it will create a
new auth key iff the config has no auth key or its auth key matches the
value of reissue_authkey from the state Secret. This is to ensure we
don't reissue auth keys in a tight loop if the proxy is slow to start or
failing for some other reason. The reissue logic also uses a burstable
rate limiter to ensure there's no way a terminally misconfigured
or buggy operator can automatically generate new auth keys in a tight loop.

Additional implementation details (ChaosInTheCRD):

- Added `ipn.NotifyInitialHealthState` to ipn watcher, to ensure that
  `n.Health` is populated when notify's are returned.
- on auth failure, containerboot:
  - Disconnects from control server
  - Sets reissue_authkey marker in state Secret with the failing key
  - Polls config file for new auth key (10 minute timeout)
  - Restarts after receiving new key to apply it

- modified operator's reissue logic slightly:
  - Deletes old device from tailnet before creating new key
  - Rate limiting: 1 key per 30s with initial burst equal to replica count
  - In-flight tracking (authKeyReissuing map) prevents duplicate API calls
    across reconcile loops

Updates #14080

Change-Id: I6982f8e741932a6891f2f48a2936f7f6a455317f


(cherry picked from commit 969927c47c3d4de05e90f5b26a6d8d931c5ceed4)

Signed-off-by: Tom Proctor <tomhjp@users.noreply.github.com>
Co-authored-by: chaosinthecrd <tom@tmlabs.co.uk>
2026-03-11 10:25:57 +00:00

86 lines
4.1 KiB
Go

// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause
package kubetypes
import "fmt"
const (
// Hostinfo App values for the Tailscale Kubernetes Operator components.
AppOperator = "k8s-operator"
AppInProcessAPIServerProxy = "k8s-operator-proxy"
AppIngressProxy = "k8s-operator-ingress-proxy"
AppIngressResource = "k8s-operator-ingress-resource"
AppEgressProxy = "k8s-operator-egress-proxy"
AppConnector = "k8s-operator-connector-resource"
AppProxyGroupEgress = "k8s-operator-proxygroup-egress"
AppProxyGroupIngress = "k8s-operator-proxygroup-ingress"
AppProxyGroupKubeAPIServer = "k8s-operator-proxygroup-kube-apiserver"
// Clientmetrics for Tailscale Kubernetes Operator components
MetricIngressProxyCount = "k8s_ingress_proxies" // L3
MetricIngressResourceCount = "k8s_ingress_resources" // L7
MetricIngressPGResourceCount = "k8s_ingress_pg_resources" // L7 on ProxyGroup
MetricServicePGResourceCount = "k8s_service_pg_resources" // L3 on ProxyGroup
MetricEgressProxyCount = "k8s_egress_proxies"
MetricConnectorResourceCount = "k8s_connector_resources"
MetricConnectorWithSubnetRouterCount = "k8s_connector_subnetrouter_resources"
MetricConnectorWithExitNodeCount = "k8s_connector_exitnode_resources"
MetricConnectorWithAppConnectorCount = "k8s_connector_appconnector_resources"
MetricNameserverCount = "k8s_nameserver_resources"
MetricRecorderCount = "k8s_recorder_resources"
MetricEgressServiceCount = "k8s_egress_service_resources"
MetricProxyGroupEgressCount = "k8s_proxygroup_egress_resources"
MetricProxyGroupIngressCount = "k8s_proxygroup_ingress_resources"
MetricProxyGroupAPIServerCount = "k8s_proxygroup_kube_apiserver_resources"
MetricTailnetCount = "k8s_tailnet_resources"
// Keys that containerboot writes to state file that can be used to determine its state.
// fields set in Tailscale state Secret. These are mostly used by the Tailscale Kubernetes operator to determine
// the state of this tailscale device.
KeyDeviceID = "device_id" // node stable ID of the device
KeyDeviceFQDN = "device_fqdn" // device's tailnet hostname
KeyDeviceIPs = "device_ips" // device's tailnet IPs
KeyPodUID = "pod_uid" // Pod UID
KeyCapVer = "tailscale_capver" // tailcfg.CurrentCapabilityVersion of this proxy instance.
KeyReissueAuthkey = "reissue_authkey" // Proxies will set this to the authkey that failed, or "no-authkey", if they can't log in.
// KeyHTTPSEndpoint is a name of a field that can be set to the value of any HTTPS endpoint currently exposed by
// this device to the tailnet. This is used by the Kubernetes operator Ingress proxy to communicate to the operator
// that cluster workloads behind the Ingress can now be accessed via the given DNS name over HTTPS.
KeyHTTPSEndpoint = "https_endpoint"
ValueNoHTTPS = "no-https"
// Pod's IPv4 address header key as returned by containerboot health check endpoint.
PodIPv4Header string = "Pod-IPv4"
EgessServicesPreshutdownEP = "/internal-egress-services-preshutdown"
LabelManaged = "tailscale.com/managed"
LabelSecretType = "tailscale.com/secret-type" // "config", "state" "certs"
LabelSecretTypeConfig = "config"
LabelSecretTypeState = "state"
LabelSecretTypeCerts = "certs"
KubeAPIServerConfigFile = "config.hujson"
APIServerProxyModeAuth APIServerProxyMode = "auth"
APIServerProxyModeNoAuth APIServerProxyMode = "noauth"
)
// APIServerProxyMode specifies whether the API server proxy will add
// impersonation headers to requests based on the caller's Tailscale identity.
// May be "auth" or "noauth".
type APIServerProxyMode string
func (a *APIServerProxyMode) UnmarshalJSON(data []byte) error {
switch string(data) {
case `"auth"`:
*a = APIServerProxyModeAuth
case `"noauth"`:
*a = APIServerProxyModeNoAuth
default:
return fmt.Errorf("unknown APIServerProxyMode %q", data)
}
return nil
}