This commit is contained in:
Raj Singh 2025-04-01 23:39:04 -05:00
parent e2f7750125
commit 23fae133f8
6 changed files with 47 additions and 23 deletions

View File

@ -0,0 +1,4 @@
- name: OPERATOR_DEFAULT_LOAD_BALANCER
value: {{ .Values.defaultLoadBalancer | quote }}
- name: PROXY_EPHEMERAL_KEYS
value: {{ .Values.ephemeralKeys | quote }}

View File

@ -111,3 +111,12 @@ apiServerProxyConfig:
mode: "false" # "true", "false", "noauth" mode: "false" # "true", "false", "noauth"
imagePullSecrets: [] imagePullSecrets: []
# If true, the operator will consider LoadBalancer Services with no
# loadBalancerClass field set as Tailscale Services.
defaultLoadBalancer: false
# If true, use ephemeral device authentication keys for all proxies.
# Ephemeral devices are automatically deleted from your tailnet when they
# disconnect, helping keep your tailnet clean.
ephemeralKeys: false

View File

@ -76,6 +76,7 @@ func main() {
tsFirewallMode = defaultEnv("PROXY_FIREWALL_MODE", "") tsFirewallMode = defaultEnv("PROXY_FIREWALL_MODE", "")
defaultProxyClass = defaultEnv("PROXY_DEFAULT_CLASS", "") defaultProxyClass = defaultEnv("PROXY_DEFAULT_CLASS", "")
isDefaultLoadBalancer = defaultBool("OPERATOR_DEFAULT_LOAD_BALANCER", false) isDefaultLoadBalancer = defaultBool("OPERATOR_DEFAULT_LOAD_BALANCER", false)
useEphemeralKeys = defaultBool("PROXY_EPHEMERAL_KEYS", false)
) )
var opts []kzap.Opts var opts []kzap.Opts
@ -109,7 +110,7 @@ func main() {
hostinfo.SetApp(kubetypes.AppAPIServerProxy) hostinfo.SetApp(kubetypes.AppAPIServerProxy)
} }
s, tsc := initTSNet(zlog) s, tsc := initTSNet(zlog, useEphemeralKeys)
defer s.Close() defer s.Close()
restConfig := config.GetConfigOrDie() restConfig := config.GetConfigOrDie()
maybeLaunchAPIServerProxy(zlog, restConfig, s, mode) maybeLaunchAPIServerProxy(zlog, restConfig, s, mode)
@ -125,6 +126,7 @@ func main() {
proxyTags: tags, proxyTags: tags,
proxyFirewallMode: tsFirewallMode, proxyFirewallMode: tsFirewallMode,
defaultProxyClass: defaultProxyClass, defaultProxyClass: defaultProxyClass,
proxyUseEphemeralKeys: useEphemeralKeys,
} }
runReconcilers(rOpts) runReconcilers(rOpts)
} }
@ -132,7 +134,7 @@ func main() {
// initTSNet initializes the tsnet.Server and logs in to Tailscale. It uses the // initTSNet initializes the tsnet.Server and logs in to Tailscale. It uses the
// CLIENT_ID_FILE and CLIENT_SECRET_FILE environment variables to authenticate // CLIENT_ID_FILE and CLIENT_SECRET_FILE environment variables to authenticate
// with Tailscale. // with Tailscale.
func initTSNet(zlog *zap.SugaredLogger) (*tsnet.Server, tsClient) { func initTSNet(zlog *zap.SugaredLogger, useEphemeralKeys bool) (*tsnet.Server, tsClient) {
var ( var (
clientIDPath = defaultEnv("CLIENT_ID_FILE", "") clientIDPath = defaultEnv("CLIENT_ID_FILE", "")
clientSecretPath = defaultEnv("CLIENT_SECRET_FILE", "") clientSecretPath = defaultEnv("CLIENT_SECRET_FILE", "")
@ -196,6 +198,7 @@ waitOnline:
Create: tailscale.KeyDeviceCreateCapabilities{ Create: tailscale.KeyDeviceCreateCapabilities{
Reusable: false, Reusable: false,
Preauthorized: true, Preauthorized: true,
Ephemeral: useEphemeralKeys,
Tags: strings.Split(operatorTags, ","), Tags: strings.Split(operatorTags, ","),
}, },
}, },
@ -288,6 +291,7 @@ func runReconcilers(opts reconcilerOpts) {
proxyImage: opts.proxyImage, proxyImage: opts.proxyImage,
proxyPriorityClassName: opts.proxyPriorityClassName, proxyPriorityClassName: opts.proxyPriorityClassName,
tsFirewallMode: opts.proxyFirewallMode, tsFirewallMode: opts.proxyFirewallMode,
proxyUseEphemeralKeys: opts.proxyUseEphemeralKeys,
} }
err = builder. err = builder.
ControllerManagedBy(mgr). ControllerManagedBy(mgr).
@ -542,12 +546,13 @@ func runReconcilers(opts reconcilerOpts) {
Watches(&rbacv1.Role{}, recorderFilter). Watches(&rbacv1.Role{}, recorderFilter).
Watches(&rbacv1.RoleBinding{}, recorderFilter). Watches(&rbacv1.RoleBinding{}, recorderFilter).
Complete(&RecorderReconciler{ Complete(&RecorderReconciler{
recorder: eventRecorder, recorder: eventRecorder,
tsNamespace: opts.tailscaleNamespace, tsNamespace: opts.tailscaleNamespace,
Client: mgr.GetClient(), Client: mgr.GetClient(),
l: opts.log.Named("recorder-reconciler"), l: opts.log.Named("recorder-reconciler"),
clock: tstime.DefaultClock{}, clock: tstime.DefaultClock{},
tsClient: opts.tsClient, tsClient: opts.tsClient,
proxyUseEphemeralKeys: opts.proxyUseEphemeralKeys,
}) })
if err != nil { if err != nil {
startlog.Fatalf("could not create Recorder reconciler: %v", err) startlog.Fatalf("could not create Recorder reconciler: %v", err)
@ -573,11 +578,12 @@ func runReconcilers(opts reconcilerOpts) {
clock: tstime.DefaultClock{}, clock: tstime.DefaultClock{},
tsClient: opts.tsClient, tsClient: opts.tsClient,
tsNamespace: opts.tailscaleNamespace, tsNamespace: opts.tailscaleNamespace,
proxyImage: opts.proxyImage, proxyImage: opts.proxyImage,
defaultTags: strings.Split(opts.proxyTags, ","), defaultTags: strings.Split(opts.proxyTags, ","),
tsFirewallMode: opts.proxyFirewallMode, tsFirewallMode: opts.proxyFirewallMode,
defaultProxyClass: opts.defaultProxyClass, defaultProxyClass: opts.defaultProxyClass,
proxyUseEphemeralKeys: opts.proxyUseEphemeralKeys,
}) })
if err != nil { if err != nil {
startlog.Fatalf("could not create ProxyGroup reconciler: %v", err) startlog.Fatalf("could not create ProxyGroup reconciler: %v", err)
@ -627,9 +633,10 @@ type reconcilerOpts struct {
// class for proxies that do not have a ProxyClass set. // class for proxies that do not have a ProxyClass set.
// this is defined by an operator env variable. // this is defined by an operator env variable.
defaultProxyClass string defaultProxyClass string
proxyUseEphemeralKeys bool
} }
// enqueueAllIngressEgressProxySvcsinNS returns a reconcile request for each // enqueueAllIngressEgressProxySvcsInNS returns a reconcile request for each
// ingress/egress proxy headless Service found in the provided namespace. // ingress/egress proxy headless Service found in the provided namespace.
func enqueueAllIngressEgressProxySvcsInNS(ns string, cl client.Client, logger *zap.SugaredLogger) handler.MapFunc { func enqueueAllIngressEgressProxySvcsInNS(ns string, cl client.Client, logger *zap.SugaredLogger) handler.MapFunc {
return func(ctx context.Context, _ client.Object) []reconcile.Request { return func(ctx context.Context, _ client.Object) []reconcile.Request {

View File

@ -66,11 +66,12 @@ type ProxyGroupReconciler struct {
tsClient tsClient tsClient tsClient
// User-specified defaults from the helm installation. // User-specified defaults from the helm installation.
tsNamespace string tsNamespace string
proxyImage string proxyImage string
defaultTags []string defaultTags []string
tsFirewallMode string tsFirewallMode string
defaultProxyClass string defaultProxyClass string
proxyUseEphemeralKeys bool
mu sync.Mutex // protects following mu sync.Mutex // protects following
egressProxyGroups set.Slice[types.UID] // for egress proxygroups gauge egressProxyGroups set.Slice[types.UID] // for egress proxygroups gauge
@ -477,7 +478,7 @@ func (r *ProxyGroupReconciler) ensureConfigSecretsCreated(ctx context.Context, p
if len(tags) == 0 { if len(tags) == 0 {
tags = r.defaultTags tags = r.defaultTags
} }
authKey, err = newAuthKey(ctx, r.tsClient, tags) authKey, err = newAuthKey(ctx, r.tsClient, tags, r.proxyUseEphemeralKeys)
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@ -165,6 +165,7 @@ type tailscaleSTSReconciler struct {
proxyImage string proxyImage string
proxyPriorityClassName string proxyPriorityClassName string
tsFirewallMode string tsFirewallMode string
proxyUseEphemeralKeys bool
} }
func (sts tailscaleSTSReconciler) validate() error { func (sts tailscaleSTSReconciler) validate() error {
@ -376,7 +377,7 @@ func (a *tailscaleSTSReconciler) createOrGetSecret(ctx context.Context, logger *
if len(tags) == 0 { if len(tags) == 0 {
tags = a.defaultTags tags = a.defaultTags
} }
authKey, err = newAuthKey(ctx, a.tsClient, tags) authKey, err = newAuthKey(ctx, a.tsClient, tags, a.proxyUseEphemeralKeys)
if err != nil { if err != nil {
return "", "", nil, err return "", "", nil, err
} }
@ -511,12 +512,13 @@ func deviceInfo(sec *corev1.Secret, podUID string, log *zap.SugaredLogger) (dev
return dev, nil return dev, nil
} }
func newAuthKey(ctx context.Context, tsClient tsClient, tags []string) (string, error) { func newAuthKey(ctx context.Context, tsClient tsClient, tags []string, ephemeral bool) (string, error) {
caps := tailscale.KeyCapabilities{ caps := tailscale.KeyCapabilities{
Devices: tailscale.KeyDeviceCapabilities{ Devices: tailscale.KeyDeviceCapabilities{
Create: tailscale.KeyDeviceCreateCapabilities{ Create: tailscale.KeyDeviceCreateCapabilities{
Reusable: false, Reusable: false,
Preauthorized: true, Preauthorized: true,
Ephemeral: ephemeral,
Tags: tags, Tags: tags,
}, },
}, },

View File

@ -57,6 +57,7 @@ type RecorderReconciler struct {
clock tstime.Clock clock tstime.Clock
tsNamespace string tsNamespace string
tsClient tsClient tsClient tsClient
proxyUseEphemeralKeys bool
mu sync.Mutex // protects following mu sync.Mutex // protects following
recorders set.Slice[types.UID] // for recorders gauge recorders set.Slice[types.UID] // for recorders gauge
@ -289,7 +290,7 @@ func (r *RecorderReconciler) ensureAuthSecretCreated(ctx context.Context, tsr *t
if len(tags) == 0 { if len(tags) == 0 {
tags = tsapi.Tags{"tag:k8s"} tags = tsapi.Tags{"tag:k8s"}
} }
authKey, err := newAuthKey(ctx, r.tsClient, tags.Stringify()) authKey, err := newAuthKey(ctx, r.tsClient, tags.Stringify(), r.proxyUseEphemeralKeys)
if err != nil { if err != nil {
return err return err
} }