diff --git a/cmd/k8s-operator/deploy/crds/tailscale.com_proxyclasses.yaml b/cmd/k8s-operator/deploy/crds/tailscale.com_proxyclasses.yaml index 154123475..24caf5d16 100644 --- a/cmd/k8s-operator/deploy/crds/tailscale.com_proxyclasses.yaml +++ b/cmd/k8s-operator/deploy/crds/tailscale.com_proxyclasses.yaml @@ -2203,6 +2203,25 @@ spec: won't make it *more* imbalanced. It's a required field. type: string + tailnetListener: + type: object + required: + - type + properties: + nodePortConfig: + type: object + properties: + portRanges: + type: array + items: + type: string + selector: + type: object + additionalProperties: + type: string + type: + description: Cannot change after creation + type: string tailscale: description: |- TailscaleConfig contains options to configure the tailscale-specific diff --git a/cmd/k8s-operator/deploy/manifests/operator.yaml b/cmd/k8s-operator/deploy/manifests/operator.yaml index 9bfbd533f..5c0e0761e 100644 --- a/cmd/k8s-operator/deploy/manifests/operator.yaml +++ b/cmd/k8s-operator/deploy/manifests/operator.yaml @@ -2679,6 +2679,25 @@ spec: type: array type: object type: object + tailnetListener: + properties: + nodePortConfig: + properties: + portRanges: + items: + type: string + type: array + selector: + additionalProperties: + type: string + type: object + type: object + type: + description: Cannot change after creation + type: string + required: + - type + type: object tailscale: description: |- TailscaleConfig contains options to configure the tailscale-specific diff --git a/cmd/k8s-operator/proxygroup.go b/cmd/k8s-operator/proxygroup.go index f263829d7..936a29325 100644 --- a/cmd/k8s-operator/proxygroup.go +++ b/cmd/k8s-operator/proxygroup.go @@ -206,6 +206,20 @@ func (r *ProxyGroupReconciler) Reconcile(ctx context.Context, req reconcile.Requ return setStatusReady(pg, metav1.ConditionTrue, reasonProxyGroupReady, reasonProxyGroupReady) } +func (r *ProxyGroupReconciler) maybeExposeViaNodePort(ctx context.Context, pc *tsapi.ProxyClass, pg *tsapi.ProxyGroup) error { + // TODO: make NodePort a const + if pc == nil || pc.Spec.TailnetListenerConfig == nil || pc.Spec.TailnetListenerConfig.Type != "NodePort" { + return nil + } + // 1. Create a NodePort Service per each replica + // TODO: support setting NodePort range + for i := range *(pg.Spec.Replicas) { + + } + + return nil +} + // validateProxyClassForPG applies custom validation logic for ProxyClass applied to ProxyGroup. func validateProxyClassForPG(logger *zap.SugaredLogger, pg *tsapi.ProxyGroup, pc *tsapi.ProxyClass) { if pg.Spec.Type == tsapi.ProxyGroupTypeIngress { diff --git a/k8s-operator/api.md b/k8s-operator/api.md index 190f99d24..0daf6aad5 100644 --- a/k8s-operator/api.md +++ b/k8s-operator/api.md @@ -425,6 +425,23 @@ _Appears in:_ | `ip` _string_ | IP is the ClusterIP of the Service fronting the deployed ts.net nameserver.
Currently you must manually update your cluster DNS config to add
this address as a stub nameserver for ts.net for cluster workloads to be
able to resolve MagicDNS names associated with egress or Ingress
proxies.
The IP address will change if you delete and recreate the DNSConfig. | | | +#### NodePort + + + + + + + +_Appears in:_ +- [TailnetListenerConfig](#tailnetlistenerconfig) + +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `portRanges` _string array_ | | | | +| `selector` _object (keys:string, values:string)_ | | | | + + #### Pod @@ -518,6 +535,7 @@ _Appears in:_ | `metrics` _[Metrics](#metrics)_ | Configuration for proxy metrics. Metrics are currently not supported
for egress proxies and for Ingress proxies that have been configured
with tailscale.com/experimental-forward-cluster-traffic-via-ingress
annotation. Note that the metrics are currently considered unstable
and will likely change in breaking ways in the future - we only
recommend that you use those for debugging purposes. | | | | `tailscale` _[TailscaleConfig](#tailscaleconfig)_ | TailscaleConfig contains options to configure the tailscale-specific
parameters of proxies. | | | | `useLetsEncryptStagingEnvironment` _boolean_ | Set UseLetsEncryptStagingEnvironment to true to issue TLS
certificates for any HTTPS endpoints exposed to the tailnet from
LetsEncrypt's staging environment.
https://letsencrypt.org/docs/staging-environment/
This setting only affects Tailscale Ingress resources.
By default Ingress TLS certificates are issued from LetsEncrypt's
production environment.
Changing this setting true -> false, will result in any
existing certs being re-issued from the production environment.
Changing this setting false (default) -> true, when certs have already
been provisioned from production environment will NOT result in certs
being re-issued from the staging environment before they need to be
renewed. | | | +| `tailnetListener` _[TailnetListenerConfig](#tailnetlistenerconfig)_ | | | | #### ProxyClassStatus @@ -999,6 +1017,36 @@ _Appears in:_ | `tailnetIPs` _string array_ | TailnetIPs is the set of tailnet IP addresses (both IPv4 and IPv6)
assigned to the device. | | | +#### TailnetListenerConfig + + + + + + + +_Appears in:_ +- [ProxyClassSpec](#proxyclassspec) + +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `type` _[TailnetListenerConfigMode](#tailnetlistenerconfigmode)_ | Cannot change after creation | | | +| `nodePortConfig` _[NodePort](#nodeport)_ | | | | + + +#### TailnetListenerConfigMode + +_Underlying type:_ _string_ + + + + + +_Appears in:_ +- [TailnetListenerConfig](#tailnetlistenerconfig) + + + #### TailscaleConfig diff --git a/k8s-operator/apis/v1alpha1/types_proxyclass.go b/k8s-operator/apis/v1alpha1/types_proxyclass.go index 899abf096..3b6bf8376 100644 --- a/k8s-operator/apis/v1alpha1/types_proxyclass.go +++ b/k8s-operator/apis/v1alpha1/types_proxyclass.go @@ -81,7 +81,20 @@ type ProxyClassSpec struct { // being re-issued from the staging environment before they need to be // renewed. // +optional - UseLetsEncryptStagingEnvironment bool `json:"useLetsEncryptStagingEnvironment,omitempty"` + UseLetsEncryptStagingEnvironment bool `json:"useLetsEncryptStagingEnvironment,omitempty"` + TailnetListenerConfig *TailnetListenerConfig `json:"tailnetListener,omitempty"` +} + +type TailnetListenerConfig struct { + // Cannot change after creation + Type TailnetListenerConfigMode `json:"type"` + NodePortConfig *NodePort `json:"nodePortConfig,omitempty"` +} + +type TailnetListenerConfigMode string +type NodePort struct { + PortRanges []string `json:"portRanges,omitempty"` + Selector map[string]string `json:"selector,omitempty"` } type TailscaleConfig struct { diff --git a/k8s-operator/apis/v1alpha1/zz_generated.deepcopy.go b/k8s-operator/apis/v1alpha1/zz_generated.deepcopy.go index 5e7e7455c..c7633d874 100644 --- a/k8s-operator/apis/v1alpha1/zz_generated.deepcopy.go +++ b/k8s-operator/apis/v1alpha1/zz_generated.deepcopy.go @@ -407,6 +407,33 @@ func (in *NameserverStatus) DeepCopy() *NameserverStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NodePort) DeepCopyInto(out *NodePort) { + *out = *in + if in.PortRanges != nil { + in, out := &in.PortRanges, &out.PortRanges + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Selector != nil { + in, out := &in.Selector, &out.Selector + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodePort. +func (in *NodePort) DeepCopy() *NodePort { + if in == nil { + return nil + } + out := new(NodePort) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Pod) DeepCopyInto(out *Pod) { *out = *in @@ -559,6 +586,11 @@ func (in *ProxyClassSpec) DeepCopyInto(out *ProxyClassSpec) { *out = new(TailscaleConfig) **out = **in } + if in.TailnetListenerConfig != nil { + in, out := &in.TailnetListenerConfig, &out.TailnetListenerConfig + *out = new(TailnetListenerConfig) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyClassSpec. @@ -1152,6 +1184,26 @@ func (in *TailnetDevice) DeepCopy() *TailnetDevice { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TailnetListenerConfig) DeepCopyInto(out *TailnetListenerConfig) { + *out = *in + if in.NodePortConfig != nil { + in, out := &in.NodePortConfig, &out.NodePortConfig + *out = new(NodePort) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TailnetListenerConfig. +func (in *TailnetListenerConfig) DeepCopy() *TailnetListenerConfig { + if in == nil { + return nil + } + out := new(TailnetListenerConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TailscaleConfig) DeepCopyInto(out *TailscaleConfig) { *out = *in