mirror of
https://github.com/tailscale/tailscale.git
synced 2026-02-15 10:22:05 -05:00
This commit contains the implementation of multi-tailnet support within the Kubernetes Operator
Each of our custom resources now expose the `spec.tailnet` field. This field is a string that must match the name of an existing `Tailnet` resource. A `Tailnet` resource looks like this:
```yaml
apiVersion: tailscale.com/v1alpha1
kind: Tailnet
metadata:
name: example # This is the name that must be referenced by other resources
spec:
credentials:
secretName: example-oauth
```
Each `Tailnet` references a `Secret` resource that contains a set of oauth credentials. This secret must be created in the same namespace as the operator:
```yaml
apiVersion: v1
kind: Secret
metadata:
name: example-oauth # This is the name that's referenced by the Tailnet resource.
namespace: tailscale
stringData:
client_id: "client-id"
client_secret: "client-secret"
```
When created, the operator performs a basic check that the oauth client has access to all required scopes. This is done using read actions on devices, keys & services. While this doesn't capture a missing "write" permission, it catches completely missing permissions. Once this check passes, the `Tailnet` moves into a ready state and can be referenced. Attempting to use a `Tailnet` in a non-ready state will stall the deployment of `Connector`s, `ProxyGroup`s and `Recorder`s until the `Tailnet` becomes ready.
The `spec.tailnet` field informs the operator that a `Connector`, `ProxyGroup`, or `Recorder` must be given an auth key generated using the specified oauth client. For backwards compatibility, the set of credentials the operator is configured with are considered the default. That is, where `spec.tailnet` is not set, the resource will be deployed in the same tailnet as the operator.
Updates https://github.com/tailscale/corp/issues/34561
317 lines
17 KiB
YAML
317 lines
17 KiB
YAML
apiVersion: apiextensions.k8s.io/v1
|
|
kind: CustomResourceDefinition
|
|
metadata:
|
|
annotations:
|
|
controller-gen.kubebuilder.io/version: v0.17.0
|
|
name: connectors.tailscale.com
|
|
spec:
|
|
group: tailscale.com
|
|
names:
|
|
kind: Connector
|
|
listKind: ConnectorList
|
|
plural: connectors
|
|
shortNames:
|
|
- cn
|
|
singular: connector
|
|
scope: Cluster
|
|
versions:
|
|
- additionalPrinterColumns:
|
|
- description: CIDR ranges exposed to tailnet by a subnet router defined via this Connector instance.
|
|
jsonPath: .status.subnetRoutes
|
|
name: SubnetRoutes
|
|
type: string
|
|
- description: Whether this Connector instance defines an exit node.
|
|
jsonPath: .status.isExitNode
|
|
name: IsExitNode
|
|
type: string
|
|
- description: Whether this Connector instance is an app connector.
|
|
jsonPath: .status.isAppConnector
|
|
name: IsAppConnector
|
|
type: string
|
|
- description: Status of the deployed Connector resources.
|
|
jsonPath: .status.conditions[?(@.type == "ConnectorReady")].reason
|
|
name: Status
|
|
type: string
|
|
- jsonPath: .metadata.creationTimestamp
|
|
name: Age
|
|
type: date
|
|
name: v1alpha1
|
|
schema:
|
|
openAPIV3Schema:
|
|
description: |-
|
|
Connector defines a Tailscale node that will be deployed in the cluster. The
|
|
node can be configured to act as a Tailscale subnet router and/or a Tailscale
|
|
exit node.
|
|
Connector is a cluster-scoped resource.
|
|
More info:
|
|
https://tailscale.com/kb/1441/kubernetes-operator-connector
|
|
type: object
|
|
required:
|
|
- spec
|
|
properties:
|
|
apiVersion:
|
|
description: |-
|
|
APIVersion defines the versioned schema of this representation of an object.
|
|
Servers should convert recognized schemas to the latest internal value, and
|
|
may reject unrecognized values.
|
|
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
|
type: string
|
|
kind:
|
|
description: |-
|
|
Kind is a string value representing the REST resource this object represents.
|
|
Servers may infer this from the endpoint the client submits requests to.
|
|
Cannot be updated.
|
|
In CamelCase.
|
|
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
|
type: string
|
|
metadata:
|
|
type: object
|
|
spec:
|
|
description: |-
|
|
ConnectorSpec describes the desired Tailscale component.
|
|
More info:
|
|
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
|
|
type: object
|
|
properties:
|
|
appConnector:
|
|
description: |-
|
|
AppConnector defines whether the Connector device should act as a Tailscale app connector. A Connector that is
|
|
configured as an app connector cannot be a subnet router or an exit node. If this field is unset, the
|
|
Connector does not act as an app connector.
|
|
Note that you will need to manually configure the permissions and the domains for the app connector via the
|
|
Admin panel.
|
|
Note also that the main tested and supported use case of this config option is to deploy an app connector on
|
|
Kubernetes to access SaaS applications available on the public internet. Using the app connector to expose
|
|
cluster workloads or other internal workloads to tailnet might work, but this is not a use case that we have
|
|
tested or optimised for.
|
|
If you are using the app connector to access SaaS applications because you need a predictable egress IP that
|
|
can be whitelisted, it is also your responsibility to ensure that cluster traffic from the connector flows
|
|
via that predictable IP, for example by enforcing that cluster egress traffic is routed via an egress NAT
|
|
device with a static IP address.
|
|
https://tailscale.com/kb/1281/app-connectors
|
|
type: object
|
|
properties:
|
|
routes:
|
|
description: |-
|
|
Routes are optional preconfigured routes for the domains routed via the app connector.
|
|
If not set, routes for the domains will be discovered dynamically.
|
|
If set, the app connector will immediately be able to route traffic using the preconfigured routes, but may
|
|
also dynamically discover other routes.
|
|
https://tailscale.com/kb/1332/apps-best-practices#preconfiguration
|
|
type: array
|
|
minItems: 1
|
|
items:
|
|
type: string
|
|
format: cidr
|
|
exitNode:
|
|
description: |-
|
|
ExitNode defines whether the Connector device should act as a Tailscale exit node. Defaults to false.
|
|
This field is mutually exclusive with the appConnector field.
|
|
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 defaults 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. This field should only be used when creating a connector
|
|
with an unspecified number of replicas, or a single replica.
|
|
type: string
|
|
pattern: ^[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$
|
|
hostnamePrefix:
|
|
description: |-
|
|
HostnamePrefix specifies the hostname prefix for each
|
|
replica. Each device will have the integer number
|
|
from its StatefulSet pod appended to this prefix to form the full hostname.
|
|
HostnamePrefix can contain lower case letters, numbers and dashes, it
|
|
must not start with a dash and must be between 1 and 62 characters long.
|
|
type: string
|
|
pattern: ^[a-z0-9][a-z0-9-]{0,61}$
|
|
proxyClass:
|
|
description: |-
|
|
ProxyClass is the name of the ProxyClass custom resource that
|
|
contains configuration options that should be applied to the
|
|
resources created for this Connector. If unset, the operator will
|
|
create resources with the default configuration.
|
|
type: string
|
|
replicas:
|
|
description: |-
|
|
Replicas specifies how many devices to create. Set this to enable
|
|
high availability for app connectors, subnet routers, or exit nodes.
|
|
https://tailscale.com/kb/1115/high-availability. Defaults to 1.
|
|
type: integer
|
|
format: int32
|
|
minimum: 0
|
|
subnetRouter:
|
|
description: |-
|
|
SubnetRouter defines subnet routes that the Connector device should
|
|
expose to tailnet as a Tailscale subnet router.
|
|
https://tailscale.com/kb/1019/subnets/
|
|
If this field is unset, the device does not get configured as a Tailscale subnet router.
|
|
This field is mutually exclusive with the appConnector field.
|
|
type: object
|
|
required:
|
|
- advertiseRoutes
|
|
properties:
|
|
advertiseRoutes:
|
|
description: |-
|
|
AdvertiseRoutes refer to 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
|
|
minItems: 1
|
|
items:
|
|
type: string
|
|
format: cidr
|
|
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/1337/acl-syntax#autoapprovers.
|
|
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-]*$.
|
|
type: array
|
|
items:
|
|
type: string
|
|
pattern: ^tag:[a-zA-Z][a-zA-Z0-9-]*$
|
|
tailnet:
|
|
description: |-
|
|
Tailnet specifies the tailnet this Connector should join. If blank, the default tailnet is used. When set, this
|
|
name must match that of a valid Tailnet resource. This field is immutable and cannot be changed once set.
|
|
type: string
|
|
x-kubernetes-validations:
|
|
- rule: self == oldSelf
|
|
message: Connector tailnet is immutable
|
|
x-kubernetes-validations:
|
|
- rule: has(self.subnetRouter) || (has(self.exitNode) && self.exitNode == true) || has(self.appConnector)
|
|
message: A Connector needs to have at least one of exit node, subnet router or app connector configured.
|
|
- rule: '!((has(self.subnetRouter) || (has(self.exitNode) && self.exitNode == true)) && has(self.appConnector))'
|
|
message: The appConnector field is mutually exclusive with exitNode and subnetRouter fields.
|
|
- rule: '!(has(self.hostname) && has(self.replicas) && self.replicas > 1)'
|
|
message: The hostname field cannot be specified when replicas is greater than 1.
|
|
- rule: '!(has(self.hostname) && has(self.hostnamePrefix))'
|
|
message: The hostname and hostnamePrefix fields are mutually exclusive.
|
|
status:
|
|
description: |-
|
|
ConnectorStatus describes the status of the Connector. This is set
|
|
and managed by the Tailscale operator.
|
|
type: object
|
|
properties:
|
|
conditions:
|
|
description: |-
|
|
List of status conditions to indicate the status of the Connector.
|
|
Known condition types are `ConnectorReady`.
|
|
type: array
|
|
items:
|
|
description: Condition contains details for one aspect of the current state of this API Resource.
|
|
type: object
|
|
required:
|
|
- lastTransitionTime
|
|
- message
|
|
- reason
|
|
- status
|
|
- type
|
|
properties:
|
|
lastTransitionTime:
|
|
description: |-
|
|
lastTransitionTime is the last time the condition transitioned from one status to another.
|
|
This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
|
|
type: string
|
|
format: date-time
|
|
message:
|
|
description: |-
|
|
message is a human readable message indicating details about the transition.
|
|
This may be an empty string.
|
|
type: string
|
|
maxLength: 32768
|
|
observedGeneration:
|
|
description: |-
|
|
observedGeneration represents the .metadata.generation that the condition was set based upon.
|
|
For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
|
|
with respect to the current state of the instance.
|
|
type: integer
|
|
format: int64
|
|
minimum: 0
|
|
reason:
|
|
description: |-
|
|
reason contains a programmatic identifier indicating the reason for the condition's last transition.
|
|
Producers of specific condition types may define expected values and meanings for this field,
|
|
and whether the values are considered a guaranteed API.
|
|
The value should be a CamelCase string.
|
|
This field may not be empty.
|
|
type: string
|
|
maxLength: 1024
|
|
minLength: 1
|
|
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
|
|
status:
|
|
description: status of the condition, one of True, False, Unknown.
|
|
type: string
|
|
enum:
|
|
- "True"
|
|
- "False"
|
|
- Unknown
|
|
type:
|
|
description: type of condition in CamelCase or in foo.example.com/CamelCase.
|
|
type: string
|
|
maxLength: 316
|
|
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
|
|
x-kubernetes-list-map-keys:
|
|
- type
|
|
x-kubernetes-list-type: map
|
|
devices:
|
|
description: Devices contains information on each device managed by the Connector resource.
|
|
type: array
|
|
items:
|
|
type: object
|
|
properties:
|
|
hostname:
|
|
description: |-
|
|
Hostname is the fully qualified domain name of the Connector replica.
|
|
If MagicDNS is enabled in your tailnet, it is the MagicDNS name of the
|
|
node.
|
|
type: string
|
|
tailnetIPs:
|
|
description: |-
|
|
TailnetIPs is the set of tailnet IP addresses (both IPv4 and IPv6)
|
|
assigned to the Connector replica.
|
|
type: array
|
|
items:
|
|
type: string
|
|
hostname:
|
|
description: |-
|
|
Hostname is the fully qualified domain name of the Connector node.
|
|
If MagicDNS is enabled in your tailnet, it is the MagicDNS name of the
|
|
node. When using multiple replicas, this field will be populated with the
|
|
first replica's hostname. Use the Hostnames field for the full list
|
|
of hostnames.
|
|
type: string
|
|
isAppConnector:
|
|
description: IsAppConnector is set to true if the Connector acts as an app connector.
|
|
type: boolean
|
|
isExitNode:
|
|
description: IsExitNode is set to true if the Connector acts as an exit node.
|
|
type: boolean
|
|
subnetRoutes:
|
|
description: |-
|
|
SubnetRoutes are the routes currently exposed to tailnet via this
|
|
Connector instance.
|
|
type: string
|
|
tailnetIPs:
|
|
description: |-
|
|
TailnetIPs is the set of tailnet IP addresses (both IPv4 and IPv6)
|
|
assigned to the Connector node.
|
|
type: array
|
|
items:
|
|
type: string
|
|
served: true
|
|
storage: true
|
|
subresources:
|
|
status: {}
|