feat: tailscale-operator Workload Identity(beta) - Helm upgrade + docs#514
feat: tailscale-operator Workload Identity(beta) - Helm upgrade + docs#514
Conversation
ArgoCD Diff ResultAuth path: tailscale アプリケーション: tailscale-operator の差分パス: argoproj/tailscale-operator ===== /ServiceAccount tailscale-operator/operator ======
1,19d0
< apiVersion: v1
< kind: ServiceAccount
< metadata:
< annotations:
< argocd.argoproj.io/tracking-id: tailscale-operator:/ServiceAccount:tailscale-operator/operator
< managedFields:
< - apiVersion: v1
< fieldsType: FieldsV1
< fieldsV1:
< f:metadata:
< f:annotations:
< f:argocd.argoproj.io/tracking-id: {}
< manager: argocd-controller
< operation: Apply
< time: "2026-02-28T10:28:08Z"
< name: operator
< namespace: tailscale-operator
< resourceVersion: "543551503"
< uid: b56e0a3e-4f99-4fb9-ab31-349c40f498f2
===== /ServiceAccount tailscale-operator/proxies ======
1,19d0
< apiVersion: v1
< kind: ServiceAccount
< metadata:
< annotations:
< argocd.argoproj.io/tracking-id: tailscale-operator:/ServiceAccount:tailscale-operator/proxies
< managedFields:
< - apiVersion: v1
< fieldsType: FieldsV1
< fieldsV1:
< f:metadata:
< f:annotations:
< f:argocd.argoproj.io/tracking-id: {}
< manager: argocd-controller
< operation: Apply
< time: "2026-02-28T10:28:08Z"
< name: proxies
< namespace: tailscale-operator
< resourceVersion: "543551501"
< uid: 458ca0b2-6ed9-41f0-ad78-44863020b6af
===== apiextensions.k8s.io/CustomResourceDefinition /connectors.tailscale.com ======
1,324d0
< apiVersion: apiextensions.k8s.io/v1
< kind: CustomResourceDefinition
< metadata:
< annotations:
< controller-gen.kubebuilder.io/version: v0.17.0
< generation: 1
< managedFields:
< - apiVersion: apiextensions.k8s.io/v1
< fieldsType: FieldsV1
< fieldsV1:
< f:metadata:
< f:annotations:
< f:controller-gen.kubebuilder.io/version: {}
< f:spec:
< f:group: {}
< f:names:
< f:kind: {}
< f:listKind: {}
< f:plural: {}
< f:shortNames: {}
< f:singular: {}
< f:scope: {}
< f:versions: {}
< manager: argocd-controller
< operation: Apply
< time: "2026-02-28T10:28:08Z"
< - apiVersion: apiextensions.k8s.io/v1
< fieldsType: FieldsV1
< fieldsV1:
< f:status:
< f:acceptedNames:
< f:kind: {}
< f:listKind: {}
< f:plural: {}
< f:shortNames: {}
< f:singular: {}
< f:conditions:
< k:{"type":"Established"}:
< .: {}
< f:lastTransitionTime: {}
< f:message: {}
< f:reason: {}
< f:status: {}
< f:type: {}
< k:{"type":"NamesAccepted"}:
< .: {}
< f:lastTransitionTime: {}
< f:message: {}
< f:reason: {}
< f:status: {}
< f:type: {}
< manager: kube-apiserver
< operation: Update
< subresource: status
< time: "2026-02-28T10:28:09Z"
< name: connectors.tailscale.com
< resourceVersion: "543551527"
< uid: 9466db02-1fa4-425c-aded-284d08ffbb24
< spec:
< conversion:
< strategy: None
< 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
< 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
< 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
< 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
< 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
< items:
< format: cidr
< type: string
< minItems: 1
< type: array
< type: object
< 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.
< pattern: ^[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$
< type: string
< 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
< 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.
< 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/
< items:
< format: cidr
< type: string
< minItems: 1
< type: array
< required:
< - advertiseRoutes
< type: object
< 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-]*$.
< items:
< pattern: ^tag:[a-zA-Z][a-zA-Z0-9-]*$
< type: string
< type: array
< type: object
< x-kubernetes-validations:
< - 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.subnetRouter) || (has(self.exitNode) && self.exitNode
< == true)) && has(self.appConnector))'
< status:
< description: |-
< ConnectorStatus describes the status of the Connector. This is set
< and managed by the Tailscale operator.
< properties:
< conditions:
< description: |-
< List of status conditions to indicate the status of the Connector.
< Known condition types are `ConnectorReady`.
< items:
< description: Condition contains details for one aspect of the current
< state of this API Resource.
< 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.
< format: date-time
< type: string
< message:
< description: |-
< message is a human readable message indicating details about the transition.
< This may be an empty string.
< maxLength: 32768
< type: string
< 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.
< format: int64
< minimum: 0
< type: integer
< 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.
< maxLength: 1024
< minLength: 1
< pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
< type: string
< status:
< description: status of the condition, one of True, False, Unknown.
< enum:
< - "True"
< - "False"
< - Unknown
< type: string
< type:
< description: type of condition in CamelCase or in foo.example.com/CamelCase.
< 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])$
< type: string
< required:
< - lastTransitionTime
< - message
< - reason
< - status
< - type
< type: object
< type: array
< x-kubernetes-list-map-keys:
< - type
< x-kubernetes-list-type: map
< 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.
< 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.
< items:
< type: string
< type: array
< type: object
< required:
< - spec
< type: object
< served: true
< storage: true
< subresources:
< status: {}
===== apiextensions.k8s.io/CustomResourceDefinition /dnsconfigs.tailscale.com ======
1,237d0
< apiVersion: apiextensions.k8s.io/v1
< kind: CustomResourceDefinition
< metadata:
< annotations:
< controller-gen.kubebuilder.io/version: v0.17.0
< generation: 1
< managedFields:
< - apiVersion: apiextensions.k8s.io/v1
< fieldsType: FieldsV1
< fieldsV1:
< f:metadata:
< f:annotations:
< f:controller-gen.kubebuilder.io/version: {}
< f:spec:
< f:group: {}
< f:names:
< f:kind: {}
< f:listKind: {}
< f:plural: {}
< f:shortNames: {}
< f:singular: {}
< f:scope: {}
< f:versions: {}
< manager: argocd-controller
< operation: Apply
< time: "2026-02-28T10:28:08Z"
< - apiVersion: apiextensions.k8s.io/v1
< fieldsType: FieldsV1
< fieldsV1:
< f:status:
< f:acceptedNames:
< f:kind: {}
< f:listKind: {}
< f:plural: {}
< f:shortNames: {}
< f:singular: {}
< f:conditions:
< k:{"type":"Established"}:
< .: {}
< f:lastTransitionTime: {}
< f:message: {}
< f:reason: {}
< f:status: {}
< f:type: {}
< k:{"type":"NamesAccepted"}:
< .: {}
< f:lastTransitionTime: {}
< f:message: {}
< f:reason: {}
< f:status: {}
< f:type: {}
< manager: kube-apiserver
< operation: Update
< subresource: status
< time: "2026-02-28T10:28:09Z"
< name: dnsconfigs.tailscale.com
< resourceVersion: "543551520"
< uid: c525e4ac-e08e-4ab3-99ee-6a02b4bc34e1
< spec:
< conversion:
< strategy: None
< group: tailscale.com
< names:
< kind: DNSConfig
< listKind: DNSConfigList
< plural: dnsconfigs
< shortNames:
< - dc
< singular: dnsconfig
< scope: Cluster
< versions:
< - additionalPrinterColumns:
< - description: Service IP address of the nameserver
< jsonPath: .status.nameserver.ip
< name: NameserverIP
< type: string
< name: v1alpha1
< schema:
< openAPIV3Schema:
< description: |-
< DNSConfig can be deployed to cluster to make a subset of Tailscale MagicDNS
< names resolvable by cluster workloads. Use this if: A) you need to refer to
< tailnet services, exposed to cluster via Tailscale Kubernetes operator egress
< proxies by the MagicDNS names of those tailnet services (usually because the
< services run over HTTPS)
< B) you have exposed a cluster workload to the tailnet using Tailscale Ingress
< and you also want to refer to the workload from within the cluster over the
< Ingress's MagicDNS name (usually because you have some callback component
< that needs to use the same URL as that used by a non-cluster client on
< tailnet).
< When a DNSConfig is applied to a cluster, Tailscale Kubernetes operator will
< deploy a nameserver for ts.net DNS names and automatically populate it with records
< for any Tailscale egress or Ingress proxies deployed to that cluster.
< Currently you must manually update your cluster DNS configuration to add the
< IP address of the deployed nameserver as a ts.net stub nameserver.
< Instructions for how to do it:
< https://kubernetes.io/docs/tasks/administer-cluster/dns-custom-nameservers/#configuration-of-stub-domain-and-upstream-nameserver-using-coredns (for CoreDNS),
< https://cloud.google.com/kubernetes-engine/docs/how-to/kube-dns (for kube-dns).
< Tailscale Kubernetes operator will write the address of a Service fronting
< the nameserver to dsnconfig.status.nameserver.ip.
< DNSConfig is a singleton - you must not create more than one.
< NB: if you want cluster workloads to be able to refer to Tailscale Ingress
< using its MagicDNS name, you must also annotate the Ingress resource with
< tailscale.com/experimental-forward-cluster-traffic-via-ingress annotation to
< ensure that the proxy created for the Ingress listens on its Pod IP address.
< NB: Clusters where Pods get assigned IPv6 addresses only are currently not supported.
< 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: |-
< Spec describes the desired DNS configuration.
< More info:
< https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
< properties:
< nameserver:
< description: |-
< Configuration for a nameserver that can resolve ts.net DNS names
< associated with in-cluster proxies for Tailscale egress Services and
< Tailscale Ingresses. The operator will always deploy this nameserver
< when a DNSConfig is applied.
< properties:
< image:
< description: Nameserver image. Defaults to tailscale/k8s-nameserver:unstable.
< properties:
< repo:
< description: Repo defaults to tailscale/k8s-nameserver.
< type: string
< tag:
< description: Tag defaults to unstable.
< type: string
< type: object
< type: object
< required:
< - nameserver
< type: object
< status:
< description: |-
< Status describes the status of the DNSConfig. This is set
< and managed by the Tailscale operator.
< properties:
< conditions:
< items:
< description: Condition contains details for one aspect of the current
< state of this API Resource.
< 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.
< format: date-time
< type: string
< message:
< description: |-
< message is a human readable message indicating details about the transition.
< This may be an empty string.
< maxLength: 32768
< type: string
< 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.
< format: int64
< minimum: 0
< type: integer
< 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 emp
---
⚠️ **diff出力が長すぎるため、一部内容を省略しました**
📝 **省略された内容**: 685,369文字
table prefix for written object names.
< type: string
< credentials:
< description: |-
< Configure environment variable credentials for managing objects in the
< configured bucket. If not set, tsrecorder will try to acquire credentials
< first from the file system and then the STS API.
< properties:
< secret:
< description: |-
< Use a Kubernetes Secret from the operator's namespace as the source of
< credentials.
< properties:
< name:
< description: |-
< The name of a Kubernetes Secret in the operator's namespace that contains
< credentials for writing to the configured bucket. Each key-value pair
< from the secret's data will be mounted as an environment variable. It
< should include keys for AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY if
< using a static access key.
< type: string
< type: object
< type: object
< endpoint:
< description: S3-compatible endpoint, e.g. s3.us-east-1.amazonaws.com.
< type: string
< type: object
< type: object
< tags:
< description: |-
< Tags that the Tailscale device will be tagged with. Defaults to [tag:k8s].
< If you specify custom tags here, make sure you 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 Recorder node has been created.
< Tag values must be in form ^tag:[a-zA-Z][a-zA-Z0-9-]*$.
< items:
< pattern: ^tag:[a-zA-Z][a-zA-Z0-9-]*$
< type: string
< type: array
< type: object
< status:
< description: |-
< RecorderStatus describes the status of the recorder. This is set
< and managed by the Tailscale operator.
< properties:
< conditions:
< description: |-
< List of status conditions to indicate the status of the Recorder.
< Known condition types are `RecorderReady`.
< items:
< description: Condition contains details for one aspect of the current
< state of this API Resource.
< 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.
< format: date-time
< type: string
< message:
< description: |-
< message is a human readable message indicating details about the transition.
< This may be an empty string.
< maxLength: 32768
< type: string
< 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.
< format: int64
< minimum: 0
< type: integer
< 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.
< maxLength: 1024
< minLength: 1
< pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
< type: string
< status:
< description: status of the condition, one of True, False, Unknown.
< enum:
< - "True"
< - "False"
< - Unknown
< type: string
< type:
< description: type of condition in CamelCase or in foo.example.com/CamelCase.
< 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])$
< type: string
< required:
< - lastTransitionTime
< - message
< - reason
< - status
< - type
< type: object
< type: array
< x-kubernetes-list-map-keys:
< - type
< x-kubernetes-list-type: map
< devices:
< description: List of tailnet devices associated with the Recorder
< StatefulSet.
< items:
< properties:
< hostname:
< description: |-
< Hostname is the fully qualified domain name of the device.
< 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 device.
< items:
< type: string
< type: array
< url:
< description: |-
< URL where the UI is available if enabled for replaying recordings. This
< will be an HTTPS MagicDNS URL. You must be connected to the same tailnet
< as the recorder to access it.
< type: string
< required:
< - hostname
< type: object
< type: array
< x-kubernetes-list-map-keys:
< - hostname
< x-kubernetes-list-type: map
< type: object
< required:
< - spec
< type: object
< served: true
< storage: true
< subresources:
< status: {}
===== apps/Deployment tailscale-operator/operator ======
1,239d0
< apiVersion: apps/v1
< kind: Deployment
< metadata:
< annotations:
< argocd.argoproj.io/tracking-id: tailscale-operator:apps/Deployment:tailscale-operator/operator
< deployment.kubernetes.io/revision: "4"
< generation: 4
< managedFields:
< - apiVersion: apps/v1
< fieldsType: FieldsV1
< fieldsV1:
< f:metadata:
< f:annotations:
< f:argocd.argoproj.io/tracking-id: {}
< f:spec:
< f:replicas: {}
< f:selector: {}
< f:strategy:
< f:type: {}
< f:template:
< f:metadata:
< f:labels:
< f:app: {}
< f:spec:
< f:containers:
< k:{"name":"operator"}:
< .: {}
< f:env:
< k:{"name":"APISERVER_PROXY"}:
< .: {}
< f:name: {}
< f:value: {}
< k:{"name":"CLIENT_ID_FILE"}:
< .: {}
< f:name: {}
< f:value: {}
< k:{"name":"CLIENT_SECRET_FILE"}:
< .: {}
< f:name: {}
< f:value: {}
< k:{"name":"OPERATOR_HOSTNAME"}:
< .: {}
< f:name: {}
< f:value: {}
< k:{"name":"OPERATOR_INITIAL_TAGS"}:
< .: {}
< f:name: {}
< f:value: {}
< k:{"name":"OPERATOR_LOGGING"}:
< .: {}
< f:name: {}
< f:value: {}
< k:{"name":"OPERATOR_NAMESPACE"}:
< .: {}
< f:name: {}
< f:valueFrom:
< f:fieldRef: {}
< k:{"name":"OPERATOR_SECRET"}:
< .: {}
< f:name: {}
< f:value: {}
< k:{"name":"POD_NAME"}:
< .: {}
< f:name: {}
< f:valueFrom:
< f:fieldRef: {}
< k:{"name":"POD_UID"}:
< .: {}
< f:name: {}
< f:valueFrom:
< f:fieldRef: {}
< k:{"name":"PROXY_FIREWALL_MODE"}:
< .: {}
< f:name: {}
< f:value: {}
< k:{"name":"PROXY_IMAGE"}:
< .: {}
< f:name: {}
< f:value: {}
< k:{"name":"PROXY_TAGS"}:
< .: {}
< f:name: {}
< f:value: {}
< f:image: {}
< f:imagePullPolicy: {}
< f:name: {}
< f:volumeMounts:
< k:{"mountPath":"/oauth"}:
< .: {}
< f:mountPath: {}
< f:name: {}
< f:readOnly: {}
< f:nodeSelector: {}
< f:serviceAccountName: {}
< f:volumes:
< k:{"name":"oauth"}:
< .: {}
< f:name: {}
< f:secret:
< f:secretName: {}
< manager: argocd-controller
< operation: Apply
< time: "2026-03-01T01:19:58Z"
< - apiVersion: apps/v1
< fieldsType: FieldsV1
< fieldsV1:
< f:spec:
< f:template:
< f:metadata:
< f:annotations: {}
< manager: argocd-server
< operation: Update
< time: "2026-03-02T15:13:50Z"
< - apiVersion: apps/v1
< fieldsType: FieldsV1
< fieldsV1:
< f:spec:
< f:template:
< f:metadata:
< f:annotations:
< f:kubectl.kubernetes.io/restartedAt: {}
< manager: kubectl-rollout
< operation: Update
< time: "2026-03-02T16:22:02Z"
< - apiVersion: apps/v1
< fieldsType: FieldsV1
< fieldsV1:
< f:metadata:
< f:annotations:
< f:deployment.kubernetes.io/revision: {}
< f:status:
< f:availableReplicas: {}
< f:conditions:
< .: {}
< k:{"type":"Available"}:
< .: {}
< f:lastTransitionTime: {}
< f:lastUpdateTime: {}
< f:message: {}
< f:reason: {}
< f:status: {}
< f:type: {}
< k:{"type":"Progressing"}:
< .: {}
< f:lastTransitionTime: {}
< f:lastUpdateTime: {}
< f:message: {}
< f:reason: {}
< f:status: {}
< f:type: {}
< f:observedGeneration: {}
< f:readyReplicas: {}
< f:replicas: {}
< f:updatedReplicas: {}
< manager: kube-controller-manager
< operation: Update
< subresource: status
< time: "2026-03-02T16:22:24Z"
< name: operator
< namespace: tailscale-operator
< resourceVersion: "545903484"
< uid: 1ec088cf-cddb-45b9-b6e1-85054ecf634a
< spec:
< progressDeadlineSeconds: 600
< replicas: 1
< revisionHistoryLimit: 10
< selector:
< matchLabels:
< app: operator
< strategy:
< type: Recreate
< template:
< metadata:
< annotations:
< kubectl.kubernetes.io/restartedAt: "2026-03-03T01:22:02+09:00"
< labels:
< app: operator
< spec:
< containers:
< - env:
< - name: OPERATOR_INITIAL_TAGS
< value: tag:k8s-operator
< - name: OPERATOR_HOSTNAME
< value: tailscale-operator
< - name: OPERATOR_SECRET
< value: operator
< - name: OPERATOR_LOGGING
< value: info
< - name: OPERATOR_NAMESPACE
< valueFrom:
< fieldRef:
< apiVersion: v1
< fieldPath: metadata.namespace
< - name: CLIENT_ID_FILE
< value: /oauth/client_id
< - name: CLIENT_SECRET_FILE
< value: /oauth/client_secret
< - name: PROXY_IMAGE
< value: tailscale/tailscale:v1.80.3
< - name: PROXY_TAGS
< value: tag:k8s-operator
< - name: APISERVER_PROXY
< value: "false"
< - name: PROXY_FIREWALL_MODE
< value: auto
< - name: POD_NAME
< valueFrom:
< fieldRef:
< apiVersion: v1
< fieldPath: metadata.name
< - name: POD_UID
< valueFrom:
< fieldRef:
< apiVersion: v1
< fieldPath: metadata.uid
< image: tailscale/k8s-operator:v1.80.3
< imagePullPolicy: Always
< name: operator
< resources: {}
< terminationMessagePath: /dev/termination-log
< terminationMessagePolicy: File
< volumeMounts:
< - mountPath: /oauth
< name: oauth
< readOnly: true
< dnsPolicy: ClusterFirst
< nodeSelector:
< kubernetes.io/os: linux
< restartPolicy: Always
< schedulerName: default-scheduler
< securityContext: {}
< serviceAccount: operator
< serviceAccountName: operator
< terminationGracePeriodSeconds: 30
< volumes:
< - name: oauth
< secret:
< defaultMode: 420
< secretName: tailscale-operator-oauth
===== networking.k8s.io/IngressClass /tailscale ======
1,23d0
< apiVersion: networking.k8s.io/v1
< kind: IngressClass
< metadata:
< annotations:
< argocd.argoproj.io/tracking-id: tailscale-operator:networking.k8s.io/IngressClass:tailscale-operator/tailscale
< generation: 1
< managedFields:
< - apiVersion: networking.k8s.io/v1
< fieldsType: FieldsV1
< fieldsV1:
< f:metadata:
< f:annotations:
< f:argocd.argoproj.io/tracking-id: {}
< f:spec:
< f:controller: {}
< manager: argocd-controller
< operation: Apply
< time: "2026-02-28T10:28:17Z"
< name: tailscale
< resourceVersion: "543551606"
< uid: ea912cb2-6bd8-4b0f-94b8-68b1eb2e4001
< spec:
< controller: tailscale.com/ts-ingress
===== rbac.authorization.k8s.io/ClusterRole /tailscale-operator ======
1,111d0
< apiVersion: rbac.authorization.k8s.io/v1
< kind: ClusterRole
< metadata:
< annotations:
< argocd.argoproj.io/tracking-id: tailscale-operator:rbac.authorization.k8s.io/ClusterRole:tailscale-operator/tailscale-operator
< managedFields:
< - apiVersion: rbac.authorization.k8s.io/v1
< fieldsType: FieldsV1
< fieldsV1:
< f:metadata:
< f:annotations:
< f:argocd.argoproj.io/tracking-id: {}
< f:rules: {}
< manager: argocd-controller
< operation: Apply
< - apiVersion: rbac.authorization.k8s.io/v1
< fieldsType: FieldsV1
< fieldsV1:
< f:metadata:
< f:annotations:
< .: {}
< f:argocd.argoproj.io/tracking-id: {}
< f:rules: {}
< manager: argocd-application-controller
< operation: Update
< time: "2026-02-28T10:28:12Z"
< name: tailscale-operator
< resourceVersion: "543551554"
< uid: 1cdbaf79-748a-4a87-b5ca-0937e0f6d372
< rules:
< - apiGroups:
< - ""
< resources:
< - events
< - services
< - services/status
< verbs:
< - create
< - delete
< - deletecollection
< - get
< - list
< - patch
< - update
< - watch
< - apiGroups:
< - networking.k8s.io
< resources:
< - ingresses
< - ingresses/status
< verbs:
< - create
< - delete
< - deletecollection
< - get
< - list
< - patch
< - update
< - watch
< - apiGroups:
< - networking.k8s.io
< resources:
< - ingressclasses
< verbs:
< - get
< - list
< - watch
< - apiGroups:
< - tailscale.com
< resources:
< - connectors
< - connectors/status
< - proxyclasses
< - proxyclasses/status
< - proxygroups
< - proxygroups/status
< verbs:
< - get
< - list
< - watch
< - update
< - apiGroups:
< - tailscale.com
< resources:
< - dnsconfigs
< - dnsconfigs/status
< verbs:
< - get
< - list
< - watch
< - update
< - apiGroups:
< - tailscale.com
< resources:
< - recorders
< - recorders/status
< verbs:
< - get
< - list
< - watch
< - update
< - apiGroups:
< - apiextensions.k8s.io
< resourceNames:
< - servicemonitors.monitoring.coreos.com
< resources:
< - customresourcedefinitions
< verbs:
< - get
< - list
< - watch
===== rbac.authorization.k8s.io/ClusterRoleBinding /tailscale-operator ======
1,39d0
< apiVersion: rbac.authorization.k8s.io/v1
< kind: ClusterRoleBinding
< metadata:
< annotations:
< argocd.argoproj.io/tracking-id: tailscale-operator:rbac.authorization.k8s.io/ClusterRoleBinding:tailscale-operator/tailscale-operator
< managedFields:
< - apiVersion: rbac.authorization.k8s.io/v1
< fieldsType: FieldsV1
< fieldsV1:
< f:metadata:
< f:annotations:
< f:argocd.argoproj.io/tracking-id: {}
< f:roleRef: {}
< f:subjects: {}
< manager: argocd-controller
< operation: Apply
< - apiVersion: rbac.authorization.k8s.io/v1
< fieldsType: FieldsV1
< fieldsV1:
< f:metadata:
< f:annotations:
< .: {}
< f:argocd.argoproj.io/tracking-id: {}
< f:roleRef: {}
< f:subjects: {}
< manager: argocd-application-controller
< operation: Update
< time: "2026-02-28T10:28:13Z"
< name: tailscale-operator
< resourceVersion: "543551567"
< uid: a6dee65a-360a-4b01-9d9a-bfa5e835a49f
< roleRef:
< apiGroup: rbac.authorization.k8s.io
< kind: ClusterRole
< name: tailscale-operator
< subjects:
< - kind: ServiceAccount
< name: operator
< namespace: tailscale-operator
===== rbac.authorization.k8s.io/Role tailscale-operator/operator ======
1,108d0
< apiVersion: rbac.authorization.k8s.io/v1
< kind: Role
< metadata:
< annotations:
< argocd.argoproj.io/tracking-id: tailscale-operator:rbac.authorization.k8s.io/Role:tailscale-operator/operator
< managedFields:
< - apiVersion: rbac.authorization.k8s.io/v1
< fieldsType: FieldsV1
< fieldsV1:
< f:metadata:
< f:annotations:
< f:argocd.argoproj.io/tracking-id: {}
< f:rules: {}
< manager: argocd-controller
< operation: Apply
< - apiVersion: rbac.authorization.k8s.io/v1
< fieldsType: FieldsV1
< fieldsV1:
< f:metadata:
< f:annotations:
< .: {}
< f:argocd.argoproj.io/tracking-id: {}
< f:rules: {}
< manager: argocd-application-controller
< operation: Update
< time: "2026-02-28T10:28:14Z"
< name: operator
< namespace: tailscale-operator
< resourceVersion: "543551578"
< uid: 50211e60-9e2a-404d-bfcd-4630557948dd
< rules:
< - apiGroups:
< - ""
< resources:
< - secrets
< - serviceaccounts
< - configmaps
< verbs:
< - create
< - delete
< - deletecollection
< - get
< - list
< - patch
< - update
< - watch
< - apiGroups:
< - ""
< resources:
< - pods
< verbs:
< - get
< - list
< - watch
< - update
< - apiGroups:
< - ""
< resources:
< - pods/status
< verbs:
< - update
< - apiGroups:
< - apps
< resources:
< - statefulsets
< - deployments
< verbs:
< - create
< - delete
< - deletecollection
< - get
< - list
< - patch
< - update
< - watch
< - apiGroups:
< - discovery.k8s.io
< resources:
< - endpointslices
< verbs:
< - get
< - list
< - watch
< - create
< - update
< - deletecollection
< - apiGroups:
< - rbac.authorization.k8s.io
< resources:
< - roles
< - rolebindings
< verbs:
< - get
< - create
< - patch
< - update
< - list
< - watch
< - apiGroups:
< - monitoring.coreos.com
< resources:
< - servicemonitors
< verbs:
< - get
< - list
< - update
< - create
< - delete
===== rbac.authorization.k8s.io/Role tailscale-operator/proxies ======
1,52d0
< apiVersion: rbac.authorization.k8s.io/v1
< kind: Role
< metadata:
< annotations:
< argocd.argoproj.io/tracking-id: tailscale-operator:rbac.authorization.k8s.io/Role:tailscale-operator/proxies
< managedFields:
< - apiVersion: rbac.authorization.k8s.io/v1
< fieldsType: FieldsV1
< fieldsV1:
< f:metadata:
< f:annotations:
< f:argocd.argoproj.io/tracking-id: {}
< f:rules: {}
< manager: argocd-controller
< operation: Apply
< - apiVersion: rbac.authorization.k8s.io/v1
< fieldsType: FieldsV1
< fieldsV1:
< f:metadata:
< f:annotations:
< .: {}
< f:argocd.argoproj.io/tracking-id: {}
< f:rules: {}
< manager: argocd-application-controller
< operation: Update
< time: "2026-02-28T10:28:14Z"
< name: proxies
< namespace: tailscale-operator
< resourceVersion: "543551577"
< uid: 5c1659cc-deb5-4e57-840a-0a3918c2ae35
< rules:
< - apiGroups:
< - ""
< resources:
< - secrets
< verbs:
< - create
< - delete
< - deletecollection
< - get
< - list
< - patch
< - update
< - watch
< - apiGroups:
< - ""
< resources:
< - events
< verbs:
< - create
< - patch
< - get
===== rbac.authorization.k8s.io/RoleBinding tailscale-operator/operator ======
1,40d0
< apiVersion: rbac.authorization.k8s.io/v1
< kind: RoleBinding
< metadata:
< annotations:
< argocd.argoproj.io/tracking-id: tailscale-operator:rbac.authorization.k8s.io/RoleBinding:tailscale-operator/operator
< managedFields:
< - apiVersion: rbac.authorization.k8s.io/v1
< fieldsType: FieldsV1
< fieldsV1:
< f:metadata:
< f:annotations:
< f:argocd.argoproj.io/tracking-id: {}
< f:roleRef: {}
< f:subjects: {}
< manager: argocd-controller
< operation: Apply
< - apiVersion: rbac.authorization.k8s.io/v1
< fieldsType: FieldsV1
< fieldsV1:
< f:metadata:
< f:annotations:
< .: {}
< f:argocd.argoproj.io/tracking-id: {}
< f:roleRef: {}
< f:subjects: {}
< manager: argocd-application-controller
< operation: Update
< time: "2026-02-28T10:28:15Z"
< name: operator
< namespace: tailscale-operator
< resourceVersion: "543551592"
< uid: f4d86ad3-adff-42ee-b214-780aa14dd53f
< roleRef:
< apiGroup: rbac.authorization.k8s.io
< kind: Role
< name: operator
< subjects:
< - kind: ServiceAccount
< name: operator
< namespace: tailscale-operator
===== rbac.authorization.k8s.io/RoleBinding tailscale-operator/proxies ======
1,40d0
< apiVersion: rbac.authorization.k8s.io/v1
< kind: RoleBinding
< metadata:
< annotations:
< argocd.argoproj.io/tracking-id: tailscale-operator:rbac.authorization.k8s.io/RoleBinding:tailscale-operator/proxies
< managedFields:
< - apiVersion: rbac.authorization.k8s.io/v1
< fieldsType: FieldsV1
< fieldsV1:
< f:metadata:
< f:annotations:
< f:argocd.argoproj.io/tracking-id: {}
< f:roleRef: {}
< f:subjects: {}
< manager: argocd-controller
< operation: Apply
< - apiVersion: rbac.authorization.k8s.io/v1
< fieldsType: FieldsV1
< fieldsV1:
< f:metadata:
< f:annotations:
< .: {}
< f:argocd.argoproj.io/tracking-id: {}
< f:roleRef: {}
< f:subjects: {}
< manager: argocd-application-controller
< operation: Update
< time: "2026-02-28T10:28:15Z"
< name: proxies
< namespace: tailscale-operator
< resourceVersion: "543551590"
< uid: f6b28e25-6308-4899-bb11-b00c2822f80c
< roleRef:
< apiGroup: rbac.authorization.k8s.io
< kind: Role
< name: proxies
< subjects:
< - kind: ServiceAccount
< name: proxies
< namespace: tailscale-operator
===== rbac.authorization.k8s.io/ClusterRoleBinding /oidc-discovery ======
0a1,14
> apiVersion: rbac.authorization.k8s.io/v1
> kind: ClusterRoleBinding
> metadata:
> annotations:
> argocd.argoproj.io/tracking-id: tailscale-operator:rbac.authorization.k8s.io/ClusterRoleBinding:tailscale-operator/oidc-discovery
> name: oidc-discovery
> roleRef:
> apiGroup: rbac.authorization.k8s.io
> kind: ClusterRole
> name: system:service-account-issuer-discovery
> subjects:
> - apiGroup: rbac.authorization.k8s.io
> kind: Group
> name: system:unauthenticated
ℹ️ 上記の差分が見つかりました💡 完全なdiffを確認するには、ローカルで |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a2c5551f4b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| ### Step 4: lolice values.yaml のプレースホルダを更新 | ||
|
|
||
| `argoproj/tailscale-operator/helm/values.yaml` の以下を更新: | ||
|
|
||
| ```yaml |
There was a problem hiding this comment.
WIF切替手順にoauthSecretVolume削除を含める
この手順では oauth.clientId と oauth.audience の設定だけを案内しており、同コミットの argoproj/tailscale-operator/helm/values.yaml で必須としている oauthSecretVolume の削除が明示されていません。手順どおりに実施すると OAuth Secret のマウント設定が残ったままになり、WIF へ完全に切り替わらず(CLIENT_SECRET_FILE が残る)、移行検証やロールアウト判断を誤る原因になります。
Useful? React with 👍 / 👎.
See PR commits for changes. Related: boxp/arch#7333