Make it possible to define an exit node to be deployed to a Kubernetes cluster
via Connector Custom resource.
Also changes to Connector API so that one Connector corresponds
to one Tailnet node that can be either a subnet router or an exit
node or both.
Updates tailscale/tailscale#10708
Signed-off-by: Irbe Krumina <irbe@tailscale.com>
description:Desired state of the Connector resource.
description:ConnectorSpec describes the desired Tailscale component.
type:object
required:
- subnetRouter
properties:
exitNode:
description:ExitNode defines whether the Connector node should act as a Tailscale exit node. Defaults to false. https://tailscale.com/kb/1103/exit-nodes
type:boolean
hostname:
description:Hostname is the tailnet hostname that should be assigned to the Connector node. If unset, hostname is defaulted to <connector name>-connector. Hostname can contain lower case letters, numbers and dashes, it must not start or end with a dash and must be between 2 and 63 characters long.
type:string
pattern:^[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$
subnetRouter:
description:SubnetRouter configures a Tailscale subnet router to be deployed in the cluster. If unset no subnet router will be deployed. https://tailscale.com/kb/1019/subnets/
description:SubnetRouter defines subnet routes that the Connector node should expose to tailnet. If unset, none are exposed. https://tailscale.com/kb/1019/subnets/
type:object
required:
- routes
properties:
hostname:
description:Hostname is the tailnet hostname that should be assigned to the subnet router node. If unset hostname is defaulted to <connector name>-subnetrouter. Hostname can contain lower case letters, numbers and dashes, it must not start or end with a dash and must be between 2 and 63 characters long.
type:string
pattern:^[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$
routes:
description:Routes refer to in-cluster CIDRs that the subnet router should make available. Route values must be strings that represent a valid IPv4 or IPv6 CIDR range. Values can be Tailscale 4via6 subnet routes. https://tailscale.com/kb/1201/4via6-subnets/
type:array
items:
type:string
format:cidr
tags:
description:Tags that the Tailscale node will be tagged with. If you want the subnet router to be autoapproved, you can configure Tailscale ACLs to autoapprove the subnetrouter's CIDRs for these tags. See https://tailscale.com/kb/1018/acls/#auto-approvers-for-routes-and-exit-nodes Defaults to tag:k8s. If you specify custom tags here, you must also make tag:k8s-operator owner of the custom tag. See https://tailscale.com/kb/1236/kubernetes-operator/#setting-up-the-kubernetes-operator. Tags cannot be changed once a Connector has been created. Tag values must be in form ^tag:[a-zA-Z][a-zA-Z0-9-]*$.
type:array
items:
type:string
pattern:^tag:[a-zA-Z][a-zA-Z0-9-]*$
x-kubernetes-validations:
- rule:has(self.tags) == has(oldSelf.tags)
message:Subnetrouter tags cannot be changed. Delete and redeploy the Connector if you need to change it.
tags:
description:Tags that the Tailscale node will be tagged with. Defaults to [tag:k8s]. To autoapprove the subnet routes or exit node defined by a Connector, you can configure Tailscale ACLs to give these tags the necessary permissions. See https://tailscale.com/kb/1018/acls/#auto-approvers-for-routes-and-exit-nodes If you specify custom tags here, you must also make the operator an owner of these tags. See https://tailscale.com/kb/1236/kubernetes-operator/#setting-up-the-kubernetes-operator. Tags cannot be changed once a Connector node has been created. Tag values must be in form ^tag:[a-zA-Z][a-zA-Z0-9-]*$.
// +kubebuilder:printcolumn:name="SubnetRoutes",type="string",JSONPath=`.status.subnetRouter.routes`,description="Cluster CIDR ranges exposed to tailnet via subnet router"
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=`.status.conditions[?(@.type == "ConnectorReady")].reason`,description="Status of the components deployed by the connector"
// +kubebuilder:printcolumn:name="SubnetRoutes",type="string",JSONPath=`.status.subnetRoutes`,description="CIDR ranges exposed to tailnet by a subnet router defined via this Connector instance."
// +kubebuilder:printcolumn:name="IsExitNode",type="string",JSONPath=`.status.isExitNode`,description="Whether this Connector instance defines an exit node."
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=`.status.conditions[?(@.type == "ConnectorReady")].reason`,description="Status of the deployed Connector resources."
typeConnectorstruct{
metav1.TypeMeta`json:",inline"`
metav1.ObjectMeta`json:"metadata,omitempty"`
// Desired state of the Connector resource.
// ConnectorSpec describes the desired Tailscale component.
SpecConnectorSpec`json:"spec"`
// Status of the Connector. This is set and managed by the Tailscale operator.
// ConnectorStatus describes the status of the Connector. This is set
// and managed by the Tailscale operator.
// +optional
StatusConnectorStatus`json:"status"`
}
@ -41,40 +46,69 @@ type ConnectorList struct {
Items[]Connector`json:"items"`
}
// ConnectorSpec defines the desired state of a ConnectorSpec.
// ConnectorSpec describes a Tailscale node to be deployed in the cluster.
// +kubebuilder:validation:XValidation:rule="has(self.subnetRouter) || self.exitNode == true",message="A Connector needs to be either an exit node or a subnet router, or both."
typeConnectorSpecstruct{
// SubnetRouter configures a Tailscale subnet router to be deployed in
// the cluster. If unset no subnet router will be deployed.
// Tags that the Tailscale node will be tagged with.
// Defaults to [tag:k8s].
// To autoapprove the subnet routes or exit node defined by a Connector,
// you can configure Tailscale ACLs to give these tags the necessary
// permissions.
// See https://tailscale.com/kb/1018/acls/#auto-approvers-for-routes-and-exit-nodes
// If you specify custom tags here, you must also make the operator an owner of these tags.
// See https://tailscale.com/kb/1236/kubernetes-operator/#setting-up-the-kubernetes-operator.
// Tags cannot be changed once a Connector node has been created.
// Tag values must be in form ^tag:[a-zA-Z][a-zA-Z0-9-]*$.
// +optional
TagsTags`json:"tags,omitempty"`
// Hostname is the tailnet hostname that should be assigned to the
// Connector node. If unset, hostname is defaulted to <connector
// name>-connector. Hostname can contain lower case letters, numbers and
// dashes, it must not start or end with a dash and must be between 2
// and 63 characters long.
// +optional
HostnameHostname`json:"hostname,omitempty"`
// SubnetRouter defines subnet routes that the Connector node should
// expose to tailnet. If unset, none are exposed.
// https://tailscale.com/kb/1019/subnets/
// +optional
SubnetRouter*SubnetRouter`json:"subnetRouter"`
// ExitNode defines whether the Connector node should act as a
// Tailscale exit node. Defaults to false.
// https://tailscale.com/kb/1103/exit-nodes
// +optional
ExitNodebool`json:"exitNode"`
}
// SubnetRouter describes a subnet router.
// +kubebuilder:validation:XValidation:rule="has(self.tags) == has(oldSelf.tags)",message="Subnetrouter tags cannot be changed. Delete and redeploy the Connector if you need to change it."
// SubnetRouter defines subnet routes that should be exposed to tailnet via a
// Connector node.
typeSubnetRouterstruct{
// Routes refer to in-cluster CIDRs that the subnet router should make
// available. Route values must be strings that represent a valid IPv4
// or IPv6 CIDR range. Values can be Tailscale 4via6 subnet routes.
// https://tailscale.com/kb/1201/4via6-subnets/
Routes[]Route`json:"routes"`
// Tags that the Tailscale node will be tagged with. If you want the
// subnet router to be autoapproved, you can configure Tailscale ACLs to
// autoapprove the subnetrouter's CIDRs for these tags.
// See https://tailscale.com/kb/1018/acls/#auto-approvers-for-routes-and-exit-nodes
// Defaults to tag:k8s.
// If you specify custom tags here, you must also make tag:k8s-operator owner of the custom tag.
// See https://tailscale.com/kb/1236/kubernetes-operator/#setting-up-the-kubernetes-operator.
// Tags cannot be changed once a Connector has been created.
// Tag values must be in form ^tag:[a-zA-Z][a-zA-Z0-9-]*$.
// +optional
Tags[]Tag`json:"tags,omitempty"`
// Hostname is the tailnet hostname that should be assigned to the
// subnet router node. If unset hostname is defaulted to <connector
// name>-subnetrouter. Hostname can contain lower case letters, numbers
// and dashes, it must not start or end with a dash and must be between
// 2 and 63 characters long.
// +optional
HostnameHostname`json:"hostname,omitempty"`
RoutesRoutes`json:"routes"`
}
typeTags[]Tag
func(tagsTags)Stringify()[]string{
stringTags:=make([]string,len(tags))
fori,t:=rangetags{
stringTags[i]=string(t)
}
returnstringTags
}
typeRoutes[]Route
func(routesRoutes)Stringify()string{
varsbstrings.Builder
sb.WriteString(string(routes[0]))
for_,r:=rangeroutes[1:]{
sb.WriteString(fmt.Sprintf(",%s",r))
}
returnsb.String()
}
// +kubebuilder:validation:Type=string
@ -91,28 +125,19 @@ type Hostname string
// ConnectorStatus defines the observed state of the Connector.
typeConnectorStatusstruct{
// List of status conditions to indicate the status of the Connector.
// Known condition types are `ConnectorReady`.
// +listType=map
// +listMapKey=type
// +optional
Conditions[]ConnectorCondition`json:"conditions"`
// SubnetRouter status is the current status of a subnet router
// SubnetRoutes are the routes currently exposed to tailnet via this