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

View File

@ -66,11 +66,12 @@ type ProxyGroupReconciler struct {
tsClient tsClient
// User-specified defaults from the helm installation.
tsNamespace string
proxyImage string
defaultTags []string
tsFirewallMode string
defaultProxyClass string
tsNamespace string
proxyImage string
defaultTags []string
tsFirewallMode string
defaultProxyClass string
proxyUseEphemeralKeys bool
mu sync.Mutex // protects following
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 {
tags = r.defaultTags
}
authKey, err = newAuthKey(ctx, r.tsClient, tags)
authKey, err = newAuthKey(ctx, r.tsClient, tags, r.proxyUseEphemeralKeys)
if err != nil {
return "", err
}

View File

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

View File

@ -57,6 +57,7 @@ type RecorderReconciler struct {
clock tstime.Clock
tsNamespace string
tsClient tsClient
proxyUseEphemeralKeys bool
mu sync.Mutex // protects following
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 {
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 {
return err
}