OAuth Provider + Tailscale Kubernetes operator compatibility #422

Closed
opened 2025-12-29 01:28:45 +01:00 by adam · 12 comments
Owner

Originally created by @almereyda on GitHub (Feb 3, 2023).

Feature request

Tailscale have recently released a Kubernetes operator for Tailscale clients.

This issue is a about potential compatibility with its feature set. From a video published alongside the announcement, we learn that this makes use of Tailscale's OAuth provider at their upstream login server implementation.

grafik

Here we could elaborate, how Headscale could make the jump to compatibility by providing an OAuth provider implementation, and how to raise the need upstream (in the operator code base) for support of custom login servers in tailscale/k8s-operator, and passing those to the tailscale/tailscale containers.

0e1403ec39/cmd/k8s-operator/operator.go (L89-L93)

Originally created by @almereyda on GitHub (Feb 3, 2023). **Feature request** - https://github.com/tailscale/tailscale/issues/502#issuecomment-1414530284 Tailscale have recently released a Kubernetes operator for Tailscale clients. - https://tailscale.com/kb/1236/kubernetes-operator/ - https://www.youtube.com/watch?v=PGfJ18cQzho This issue is a about potential compatibility with its feature set. From [a video](https://www.youtube.com/watch?v=PGfJ18cQzho) published alongside the announcement, we learn that this makes use of Tailscale's OAuth provider at their upstream login server implementation. ![grafik](https://user-images.githubusercontent.com/1645308/216716611-9b2e3a0d-cc88-4924-bec9-07e1a0ded4ce.png) Here we could elaborate, how Headscale could make the jump to compatibility by providing an OAuth provider implementation, and how to raise the need upstream (in the operator code base) for support of custom login servers in `tailscale/k8s-operator`, and passing those to the `tailscale/tailscale` containers. https://github.com/tailscale/tailscale/blob/0e1403ec392bdc181a7de3de4f26ccb313e76323/cmd/k8s-operator/operator.go#L89-L93
adam added the enhancementwontfix labels 2025-12-29 01:28:45 +01:00
adam closed this issue 2025-12-29 01:28:45 +01:00
Author
Owner

@almereyda commented on GitHub (Feb 4, 2023):

Increased support for an OAuth provider, bolstered with the already existing support for an OIDC client into an OIDC Relaying Party, could also help in providing short-lived access tokens for third-party Headscale API clients, such as the example of a Web UI #234, which in return would allow to refrain from creating static API tokens.

@almereyda commented on GitHub (Feb 4, 2023): Increased support for an OAuth provider, bolstered with the already existing support for an OIDC client into an OIDC Relaying Party, could also help in providing short-lived access tokens for third-party Headscale API clients, such as the example of a Web UI #234, which in return would allow to refrain from creating static API tokens.
Author
Owner

@madjam002 commented on GitHub (Feb 14, 2023):

The operator uses the Tailscale API which appears to be unrelated to the control plane API that Headscale mimics.

I think the only way to support Headscale with the operator is a complete fork or ground up implementation that uses the Headscale gRPC API instead, or for Headscale to also mimic the Tailscale REST API. https://tailscale.com/kb/1101/api/

@madjam002 commented on GitHub (Feb 14, 2023): The operator uses the Tailscale API which appears to be unrelated to the control plane API that Headscale mimics. I think the only way to support Headscale with the operator is a complete fork or ground up implementation that uses the Headscale gRPC API instead, or for Headscale to also mimic the Tailscale REST API. https://tailscale.com/kb/1101/api/
Author
Owner

@kradalby commented on GitHub (May 10, 2023):

This is out of scope for this project.

@kradalby commented on GitHub (May 10, 2023): This is out of scope for this project.
Author
Owner

@samcday commented on GitHub (Apr 10, 2024):

This is out of scope for this project.

It seems to me that if such an API were to be contributed to the project, it would potentially eat into the moat that Tailscale is attempting to create with their proprietary control plane.

That is, if enough of the Tailscale.com Control Plane API and OAuth provider flow was implemented to support use-cases such as the Kubernetes operator, or the Terraform provider, it might pose a risk to Tailscale.com's bottom line.

I find it quite problematic (ethically, morally) that a salaried Tailscale employee has made the executive decision that this project cannot have such an API implementation.

It seems to me that Tailscale is attempting something of a "embrace, extend, extinguish" approach here. I'd like to know to what extent @juanfont still maintains and oversees this project?

I'm not trying to start a flame war here, I'm just observing what appears to be somewhat bad-faith behaviour from a commercial entity...

More pointedly, why can there not be an open issue in headscale that keeps the door open to folks contributing the bits necessary to implement the Tailscale API that the k8s-operator needs (all two methods)?

I get that Tailscale.com is unlikely to be eager to contribute such a thing, given the points I made earlier. But my understanding is stuff like the OIDC integration in headscale was contributed by kind strangers, who's to say such a thing couldn't happen here?

Just in case this comment gets deleted, if anyone is out there reading this and observes any further suspicious behavior, drop me a line at me@samcday.com - perhaps it's time to fork this project further from Tailscale.com control and conflict of interest.

@samcday commented on GitHub (Apr 10, 2024): > This is out of scope for this project. It seems to me that if such an API were to be contributed to the project, it would potentially eat into the moat that Tailscale is attempting to create with their proprietary control plane. That is, if enough of the Tailscale.com Control Plane API and OAuth provider flow was implemented to support use-cases such as the Kubernetes operator, or the [Terraform provider](https://github.com/juanfont/headscale/issues/222#issuecomment-1398053665), it might pose a risk to Tailscale.com's bottom line. I find it quite problematic (ethically, morally) that a salaried Tailscale employee has made the executive decision that this project *cannot* have such an API implementation. It seems to me that Tailscale is attempting something of a "embrace, extend, extinguish" approach here. I'd like to know to what extent @juanfont still maintains and oversees this project? I'm not trying to start a flame war here, I'm just observing what appears to be somewhat bad-faith behaviour from a commercial entity... More pointedly, why can there not be an open issue in headscale that keeps the door open to folks contributing the bits necessary to implement the Tailscale API that the k8s-operator needs [(all two methods)](https://github.com/tailscale/tailscale/blob/8d83adde07f53d247d8b861bc9559ac44d799dd1/cmd/k8s-operator/operator.go#L330)? I get that Tailscale.com is unlikely to be eager to contribute such a thing, given the points I made earlier. But my understanding is stuff like the OIDC integration in headscale was contributed by kind strangers, who's to say such a thing couldn't happen here? Just in case this comment gets deleted, if anyone is out there reading this and observes any further suspicious behavior, drop me a line at `me@samcday.com` - perhaps it's time to fork this project further from Tailscale.com control and conflict of interest.
Author
Owner

@almereyda commented on GitHub (Apr 10, 2024):

Yes and no. While I can follow the concern to double the API surface, as highlighted by me in https://github.com/juanfont/headscale/issues/582#issuecomment-2021516764 and someone else in https://github.com/tailscale/tailscale/pull/11627#discussion_r1556230490, I am very confident in and grateful for the resources that Tailscale provide to the Headscale project.

I'm historically not involved enough to understand why the approach of a gRPC API was chosen; maybe just because someone wanted to learn something, but it's hard to argue for more features of a (not-only) voluntary project that is already compatible with the Tailscale FLOSS client to a certain degree.

I'm left wondering under which circumstances and conditions community contributions would likely be able to be accepted. The Vaultwarden SSO PR + temporary fork shows that a long time can go into a soft-fork, before it is of considerable quality for merging to a non-hesitant upstream. Maybe this is a good way to move forward with such feature-ladden work proposals anyway?

Let's not do the soft-fork before 0.23 is released, please ;) Else there's so much backporting work. Watching LXD and Incus dance around each other feature-wise is already rather enticing enough!

@almereyda commented on GitHub (Apr 10, 2024): Yes and no. While I can follow the concern to double the API surface, as highlighted by me in https://github.com/juanfont/headscale/issues/582#issuecomment-2021516764 and someone else in https://github.com/tailscale/tailscale/pull/11627#discussion_r1556230490, I am very confident in and grateful for the resources that Tailscale provide to the Headscale project. I'm historically not involved enough to understand why the approach of a gRPC API was chosen; maybe just because someone wanted to learn something, but it's hard to argue for more features of a (not-only) voluntary project that is already compatible with the Tailscale FLOSS client to a certain degree. I'm left wondering under which circumstances and conditions community contributions would likely be able to be accepted. The Vaultwarden SSO PR + temporary fork shows that a long time can go into a soft-fork, before it is of considerable quality for merging to a non-hesitant upstream. Maybe this is a good way to move forward with such feature-ladden work proposals anyway? Let's not do the soft-fork before 0.23 is released, please ;) Else there's so much backporting work. Watching LXD and Incus dance around each other feature-wise is already rather enticing enough!
Author
Owner

@majst01 commented on GitHub (May 15, 2024):

Another possibility would be to write a small api which serves the endpoints the tailscale operator expects, and talks to the headscale grpc api. Still a PR to the tailscale operator is required to make the tokenURL configurable. But as this would be a minimal change without adding extra dependencies, the chances to get this merged are there.

@majst01 commented on GitHub (May 15, 2024): Another possibility would be to write a small api which serves the endpoints the tailscale operator expects, and talks to the headscale grpc api. Still a PR to the tailscale operator is required to make the tokenURL configurable. But as this would be a minimal change without adding extra dependencies, the chances to get this merged are there.
Author
Owner

@Lite5h4dow commented on GitHub (Mar 5, 2025):

or we could fork the operator and modify how it auths itself to align with headscale's capabilities, i may be wrong, but i think its just using the oauth provider to authenticate itself and those using it correct? if so, we could just add the auth checks to headscale in some fashion, and re-impliment the self authentication no?

@Lite5h4dow commented on GitHub (Mar 5, 2025): or we could fork the operator and modify how it auths itself to align with headscale's capabilities, i may be wrong, but i think its just using the oauth provider to authenticate itself and those using it correct? if so, we could just add the auth checks to headscale in some fashion, and re-impliment the self authentication no?
Author
Owner

@almereyda commented on GitHub (Mar 8, 2025):

Good analysis. Eventually they would even allow a PR against the operator to support for both operation modes, given that Headscale is an officially sancitonned project by Tailscale Inc.

@almereyda commented on GitHub (Mar 8, 2025): Good analysis. Eventually they would even allow a PR against the operator to support for both operation modes, given that Headscale is an officially sancitonned project by Tailscale Inc.
Author
Owner

@Lite5h4dow commented on GitHub (Mar 8, 2025):

given that Headscale is an officially sancitonned project by Tailscale Inc.

While it's sanctioned, they said they won't provide any resources to maintenance, so I doubt they would merge it in, especially since the source project is a mono repo of clients.

@Lite5h4dow commented on GitHub (Mar 8, 2025): > given that Headscale is an officially sancitonned project by Tailscale Inc. While it's sanctioned, they said they won't provide any resources to maintenance, so I doubt they would merge it in, especially since the source project is a mono repo of clients.
Author
Owner

@Julien-Delavisse commented on GitHub (Mar 28, 2025):

Hello,

I'd be interested to see this feature in headscale.

If I understand correctly, the tailscale project doesn't want to integrate headscale related code (which makes sense) but would accept to add a parameter to have another authentication server in the kubernetes operator. https://github.com/tailscale/tailscale/pull/11627#issuecomment-2043378813

So headscale would probably have to provide the endpoints expected by the tailscale operator, as proposed by @majst01.

@kradalby why do you say it's out of the scope of the project? Do you want to limit headscale support to tailscale and not to other tailscale-related projects like the operator?

@majst01 @kradalby can you estimate the complexity of the task?

Tailscale and Headcale are good projects, and I can donate 200/300 dollars to fund the work. It's not much but I'm willing to pay 😉

@Julien-Delavisse commented on GitHub (Mar 28, 2025): Hello, I'd be interested to see this feature in headscale. If I understand correctly, the tailscale project doesn't want to integrate headscale related code (which makes sense) but would accept to add a parameter to have another authentication server in the kubernetes operator. https://github.com/tailscale/tailscale/pull/11627#issuecomment-2043378813 So headscale would probably have to provide the endpoints expected by the tailscale operator, as proposed by @majst01. @kradalby why do you say it's out of the scope of the project? Do you want to limit headscale support to tailscale and not to other tailscale-related projects like the operator? @majst01 @kradalby can you estimate the complexity of the task? Tailscale and Headcale are good projects, and I can donate 200/300 dollars to fund the work. It's not much but I'm willing to pay 😉
Author
Owner

@plsnotracking commented on GitHub (Jun 10, 2025):

I'm kind of interested in a headscale operator, I'm trying to work around it, and see if it's possible.

But seems like the missing components to get to a headscale operator is:

  • OAuth support with Headscale - As long as OAuth implementation is standard there should be no reason why this would not be possible?
  • Headscale API server support - Tailscale API has an open source spec, the outcome here is definitive(?)
  • The operator that then consumes OAuth + Headscale API server to do operator things.

It's a long shot, and maybe I'm missing some other components, but could someone educate me on what else could be missing?

More importantly, if a patch of that magnitude

  • Will be accepted? (Even if it's broken down by logical chunks)
  • Is interesting to a section of the community at all?
@plsnotracking commented on GitHub (Jun 10, 2025): I'm kind of interested in a `headscale` operator, I'm trying to work around it, and see if it's possible. But seems like the missing components to get to a headscale operator is: - OAuth support with Headscale - As long as OAuth implementation is standard there should be no reason why this would not be possible? - Headscale API server support - Tailscale API has an open source spec, the outcome here is definitive(?) - The operator that then consumes OAuth + Headscale API server to do operator things. It's a long shot, and maybe I'm missing some other components, but could someone educate me on what else could be missing? More importantly, if a patch of that magnitude - Will be accepted? (Even if it's broken down by logical chunks) - Is interesting to a section of the community at all?
Author
Owner

@antoniolago commented on GitHub (Jun 11, 2025):

Also interested in this, tried to use tailscale sidecars but wasn't productive (if anyone got real world working examples I would appreciate), then I tried blindly building the PR mentioned https://github.com/tailscale/tailscale/pull/11627 but it shows empty logs on pod and don't do anything, so 80% of our infra is currently not supported by headscale.
I understand the lack of resources, but just closing this issue shuts the door for people implementing a solution in-tree...

More importantly, if a patch of that magnitude

  • Will be accepted? (Even if it's broken down by logical chunks)
  • Is interesting to a section of the community at all?

Well, I would use any working headscale instance/operator even if not by official repositories if they integrate our k8s clusters properly.

Edit:
I was able to make sidecar work, it's what I'll be doing now on, but operator would be much better
This allow my service-ms to access subnet routers via IP, but DNS doesn't work properly:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: service-ms-deployment
  namespace: service-ms
spec:
  replicas: 1
  selector:
    matchLabels:
      app: service-ms
  template:
    metadata:
      name: service-ms
      labels:
        app: service-ms
    spec:
      serviceAccountName: tailscale-relay
      nodeSelector:
        kubernetes.io/arch: arm64
        kubernetes.io/os: linux
      imagePullSecrets:
        - name: regcred-harbor
      containers:
        - name: service-ms-container
          imagePullPolicy: Always
          image: placeholder
          ports:
            - containerPort: 80
          resources:
            limits:
              cpu: "0.5"
              memory: "512Mi"
            requests:
              cpu: "20m"
              memory: "60Mi"
        - name: tailscale
          image: ghcr.io/tailscale/tailscale:latest
          securityContext:
            privileged: true
          env:
            - name: TS_USERSPACE
              value: "false"
            - name: TS_KUBE_SECRET
              value: tailscale-auth
            - name: TS_DEBUG_FIREWALL_MODE
              value: nftables
            - name: TS_AUTH_KEY
              valueFrom:
                secretKeyRef:
                  name: vpn-auth
                  key: auth-key
            # - name: TS_HOSTNAME
            #   value: "service-ms-$(POD_NAME)"
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: POD_UID
              valueFrom:
                fieldRef:
                  fieldPath: metadata.uid
            - name: TS_EXTRA_ARGS
              value: "--login-server https://vpn.example.com --accept-routes --advertise-tags=tag:service-ms" # --accept-dns"
          resources:
            limits:
              cpu: "1"
              memory: "512Mi"
            requests:
              cpu: "20m"
              memory: "60Mi"
          volumeMounts:
            - name: tailscale-state
              mountPath: /var/lib/tailscale
      volumes:
        - name: tailscale-state
          persistentVolumeClaim:
            claimName: tailscale-pvc
      shareProcessNamespace: true
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: tailscale-relay
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: tailscale-relay
rules:
- apiGroups:
  - ""
  resources:
  - secrets
  verbs:
  - create
- apiGroups:
  - ""
  resourceNames:
  - tailscale-auth
  resources:
  - secrets
  verbs:
  - get
  - update
  - patch
- apiGroups:
  - ""
  resources:
  - events
  verbs:
  - get
  - create
  - patch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: tailscale-relay
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: tailscale-relay
subjects:
- kind: ServiceAccount
  name: tailscale-relay
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: tailscale-pvc
  annotations:
    kustomize.toolkit.fluxcd.io/prune: disabled
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
@antoniolago commented on GitHub (Jun 11, 2025): Also interested in this, tried to use tailscale sidecars but wasn't productive (if anyone got real world working examples I would appreciate), then I tried blindly building the PR mentioned https://github.com/tailscale/tailscale/pull/11627 but it shows empty logs on pod and don't do anything, so 80% of our infra is currently not supported by headscale. I understand the lack of resources, but just closing this issue shuts the door for people implementing a solution in-tree... > More importantly, if a patch of that magnitude > > * Will be accepted? (Even if it's broken down by logical chunks) > * Is interesting to a section of the community at all? Well, I would use any working headscale instance/operator even if not by official repositories if they integrate our k8s clusters properly. Edit: I was able to make sidecar work, it's what I'll be doing now on, but operator would be much better This allow my service-ms to access subnet routers via IP, but DNS doesn't work properly: <details> ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: service-ms-deployment namespace: service-ms spec: replicas: 1 selector: matchLabels: app: service-ms template: metadata: name: service-ms labels: app: service-ms spec: serviceAccountName: tailscale-relay nodeSelector: kubernetes.io/arch: arm64 kubernetes.io/os: linux imagePullSecrets: - name: regcred-harbor containers: - name: service-ms-container imagePullPolicy: Always image: placeholder ports: - containerPort: 80 resources: limits: cpu: "0.5" memory: "512Mi" requests: cpu: "20m" memory: "60Mi" - name: tailscale image: ghcr.io/tailscale/tailscale:latest securityContext: privileged: true env: - name: TS_USERSPACE value: "false" - name: TS_KUBE_SECRET value: tailscale-auth - name: TS_DEBUG_FIREWALL_MODE value: nftables - name: TS_AUTH_KEY valueFrom: secretKeyRef: name: vpn-auth key: auth-key # - name: TS_HOSTNAME # value: "service-ms-$(POD_NAME)" - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: POD_UID valueFrom: fieldRef: fieldPath: metadata.uid - name: TS_EXTRA_ARGS value: "--login-server https://vpn.example.com --accept-routes --advertise-tags=tag:service-ms" # --accept-dns" resources: limits: cpu: "1" memory: "512Mi" requests: cpu: "20m" memory: "60Mi" volumeMounts: - name: tailscale-state mountPath: /var/lib/tailscale volumes: - name: tailscale-state persistentVolumeClaim: claimName: tailscale-pvc shareProcessNamespace: true --- apiVersion: v1 kind: ServiceAccount metadata: name: tailscale-relay --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: tailscale-relay rules: - apiGroups: - "" resources: - secrets verbs: - create - apiGroups: - "" resourceNames: - tailscale-auth resources: - secrets verbs: - get - update - patch - apiGroups: - "" resources: - events verbs: - get - create - patch --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: tailscale-relay roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: tailscale-relay subjects: - kind: ServiceAccount name: tailscale-relay --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: tailscale-pvc annotations: kustomize.toolkit.fluxcd.io/prune: disabled spec: accessModes: - ReadWriteOnce resources: requests: storage: 1Gi ``` </details>
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/headscale#422