惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Security Archives - TechRepublic
Security Archives - TechRepublic
T
The Exploit Database - CXSecurity.com
P
Proofpoint News Feed
Scott Helme
Scott Helme
NISL@THU
NISL@THU
Cisco Talos Blog
Cisco Talos Blog
C
Cybersecurity and Infrastructure Security Agency CISA
AWS News Blog
AWS News Blog
V
Vulnerabilities – Threatpost
J
Java Code Geeks
U
Unit 42
The GitHub Blog
The GitHub Blog
H
Help Net Security
T
Tenable Blog
aimingoo的专栏
aimingoo的专栏
Jina AI
Jina AI
Spread Privacy
Spread Privacy
Apple Machine Learning Research
Apple Machine Learning Research
人人都是产品经理
人人都是产品经理
L
Lohrmann on Cybersecurity
T
Threatpost
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Engineering at Meta
Engineering at Meta
A
About on SuperTechFans
I
InfoQ
Microsoft Azure Blog
Microsoft Azure Blog
B
Blog
L
LINUX DO - 最新话题
K
Kaspersky official blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
T
Threat Research - Cisco Blogs
C
Check Point Blog
T
The Blog of Author Tim Ferriss
有赞技术团队
有赞技术团队
宝玉的分享
宝玉的分享
Help Net Security
Help Net Security
Google DeepMind News
Google DeepMind News
A
Arctic Wolf
Y
Y Combinator Blog
N
News | PayPal Newsroom
M
MIT News - Artificial intelligence
Latest news
Latest news
H
Hacker News: Front Page
Blog — PlanetScale
Blog — PlanetScale
腾讯CDC
I
Intezer
爱范儿
爱范儿
F
Fortinet All Blogs
P
Palo Alto Networks Blog
C
CERT Recently Published Vulnerability Notes

Blog on Tailscale

Audit AI agent requests, logs, and access with Aperture A no-nonsense explainer to Agentic AI Database, Kubernetes, and SSH access without passwords | Border0 + Tailscale Tailscale adds log streaming for Azure Blob Storage Build a flexible AI stack with Aperture More Tailscale tricks for your jailbroken Kindle Redundancy only matters if you can reach it Fixing my ridiculous fridge with a tiny Funnel site Canada’s Bill C-22 and the security cost of collecting more data Introducing: Aperture CLI Five TailscaleUp sessions I’d attend if I didn’t work here Bambuddy: self-hosted 3D printing beyond the vendor cloud How Cleric uses tsnet to securely automate software operations Tailscale + Paperless-ngx: scan everything, expose nothing Aperture, now in beta, adds the controls teams need for AI agents This month at Tailscale for April 2026 Meet tailscale-rs, our new Rust library preview Pricing v4: more value, more simply Being the adult in the room The hidden costs of “good enough” network access This month at Tailscale for March 2026 Escaping the notch: Tailscale's new macOS home
Fixing Headlamp OIDC login with Tailscale and tsidp
Alex Kretzschmar · 2026-05-05 · via Blog on Tailscale

Headlamp is a Kubernetes dashboard I use to get a quick view of what is happening in my clusters. It is lightweight, extensible, and supports OIDC. And configuring it to play nicely with OIDC and RBAC is a real, shall we say, education in Kubernetes.

I ran Headlamp as a cluster app and plugged it into Tailscale's tsidp, a tailnet-aware OIDC server. It promised a very clean setup: Tailscale would be the path to the app, the DNS, the HTTPS layer, and the identity provider. Everything looked good until I actually tried to log in.

Alas, dear reader, as is often the case in Kubernetes-land, it was time for a small side quest.

Headlamp was up. Tailscale ingress was up. I could access Headlamp via https://headlamp-clustername.funny-name.ts.net. Everything was automatically configured using Tailscale's Kubernetes Operator. I could even load the login page. But when I authenticated, I kept getting bounced straight back out again during the login flow.

What follows is a tale of woe learning and discovery about how some of the pieces of the Kubernetes jigsaw fit together.

Because of the OIDC configuration I had supplied, Headlamp knew how to trust my Tailscale identity, but the Kubernetes API server did not. Headlamp and Kubernetes were disagreeing about who I was.

And once I understood that, the rest of the fix was fairly straightforward.

This flowchart, titled "Before: Login fails," depicts a sequence where a User accesses Headlamp, which obtains an OIDC Token from tsidp and presents it to the Kubernetes API server. The process fails at the end, indicated by a red "X" leading to a final box labeled "Rejected issuer not trusted."
Before the fix, Headlamp could complete the OIDC flow, but the Kubernetes API server rejected the token because it did not trust the issuer.

Why login was broken

The problem in this chain is that "logging in" to Kubernetes is not a single step.

There are several components that must all agree with each other to let you in. If one of these components can't match up your identity somehow, the whole login flow falls apart. That's what was happening here.

In my case, Headlamp was configured to use Tailscale as its OIDC provider, and that part was working. But when I tried to log in, Headlamp failed opaquely, without a useful error. Headlamp sent me to my tsidp identity server, and returned a token successfully.

So far, so good.

But Headlamp is only the front end. It is not the thing that decides whether I am allowed to interact with cluster APIs to read Pods, inspect Deployments, and so on. In this case, that job is entrusted to the Kubernetes API server.

Side note: I did look at using the Tailscale Kubernetes API proxy specifically for Headlamp, but it was ultimately more awkward than it was helpful. We are still using the API proxy for cluster access elsewhere; for Headlamp, that approach requires pulling in a separate kubeconfig, internal DNS, and egress proxying. In the end, I opted to stick with the tsidp-based OIDC route:

  • Headlamp remains a normal cluster app
  • tsidp handles logins
  • Kubernetes API server is configured to trust the same issuer

This ultimately meant the cluster’s role-based access control (RBAC) could work the normal Kubernetes way. Once the API server trusted the same OIDC issuer as Headlamp, my Tailscale identity flowed cleanly through to Kubernetes permissions, and Headlamp worked again. Understanding why that fixed the problem, however, was perhaps harder than applying the fix itself.

Trust me, bro

There are really five trust relationships at work here.

First, the browser trusts the Tailscale-served HTTPS endpoint for Headlamp. We've all seen the "this website is dangerous" HTTPS warnings on self-signed or expired certs, right? Tailscale serve does away with them.

Second, Headlamp trusts tsidp as its OIDC provider. That is what allows it to redirect the user for login, and later accept the returned token as meaningful.

Third, tsidp trusts Headlamp as an OIDC client. That means the configured client ID, secret, and callback URL must all be valid from the identity provider’s point of view.

Fourth, the Kubernetes API server trusts tsidp as the issuer of the token. This is the critical part that was missing in my setup. Until that was configured, Kubernetes had no reason to accept the token Headlamp was presenting.

Fifth, Kubernetes RBAC trusts the token claims enough to map them into a subject. In my case, that meant using email as the username and tags as groups, then applying the appropriate bindings.

This sequence diagram illustrates a ten-step authentication and authorization flow across five vertical columns: User Browser, Headlamp, tsidp, Kubernetes API Server, and Kubernetes RBAC. The process begins with a user opening Headlamp and authenticating via tsidp (Tailscale identity); once logged in, Headlamp uses the received token to request data from the Kubernetes API Server. The API server maps claims to identity, the RBAC system authorizes the request, and finally, the cluster data is returned to Headlamp to render the dashboard for the user.
The full login flow after the fix: Headlamp gets an OIDC token from tsidp, presents it to the Kubernetes API server, and Kubernetes RBAC maps the token claims to permissions.

Configuring kube-apiserver

As noted, the kube-apiserver is the critical missing link in our chain of trust. Therefore we need to provide it the information it needs to trust our tsidp OIDC issuer.

Setting What it does
oidc-issuer-url Which OIDC issuer to trust
oidc-client-id Which audience the token must be for
oidc-username-claim Treats the email claim as the Kubernetes username
oidc-groups-claim Treats the tags claim as Kubernetes groups

And with this token configured, Headlamp could now successfully hand the API server a token issued by tsidp, but that was not enough on its own. The API server needed to know which issuer to trust, which client ID the token was meant for, and which claims inside that token represented the username and groups. Until that was configured, the token was just an opaque blob of JSON and cryptography that Kubernetes had no reason to accept.

Once I taught the API server to trust tsidp and map the email and tags claims correctly, the token stopped being an untrusted bearer token and started becoming a real Kubernetes identity.

I was now able to login using nothing but my Tailscale identity. In other words, no kubeconfigs, no usernames, no passwords. My Tailscale identity flows all the way through to Kubernetes RBAC. If you don't think that's cool, then, well, what are you doing reading this post?

Cluster level identity mapping

OK, we can log in but we're not done yet. Now Kubernetes knows who I am, but it still doesn't know what I'm allowed to do. Authentication tells Kubernetes who I am, RBAC tells Kubernetes what I am allowed to do.

Because I had configured oidc-username-claim to use email, Kubernetes saw my Tailscale identity as email@example.com. From there, the fix required creating a ClusterRoleBinding for that user and mapping it to a role.

In my case, I used cluster-admin. Given this is only a homelab scenario, I was happy enough with that trade-off. In a shared cluster, I would almost certainly be more restrictive.

This was the final piece of the puzzle. Headlamp could now log me in, the Kubernetes API server could verify who I was, and RBAC could map that identity to permissions that actually let me use the dashboard.

Titled "After: Login works," this diagram on a purple background illustrates a login flow starting with a "User" connecting to "Headlamp." Headlamp obtains an "OIDC Token" from "tsidp" and presents it to the "Kubernetes API server," which then passes "Email, tags claims" to "Kubernetes RBAC." The process concludes with a green checkmark leading to a green box labeled "Authorized dashboard access."
After the fix, the Kubernetes API server accepts the token, maps email and tags claims to an identity, and lets RBAC authorize dashboard access.

This is so nice

What I like about this setup is that the same identity flows all the way through it.

  • I use Tailscale to reach Headlamp
  • I use Tailscale to sign into Headlamp
  • Headlamp presents that Tailscale-issued token to Kubernetes
  • Kubernetes trusts that token
  • RBAC turns that identity into permissions

Tailscale is not just acting as a VPN here. It is the network path, the DNS, the HTTPS implementation, and the identity provider. Once the Kubernetes API server was configured to trust the same issuer as Headlamp, the whole thing clicked into place.

That is what made this feel so clean in the end. I was not juggling kubeconfigs, service accounts, static tokens, or another auth stack. I was just using my Tailscale identity all the way through to Kubernetes RBAC. With my side quest complete, and many dangerous foes vanquished along the way, it was finally time to get back to the main quest and see what fresh nonsense Kubernetes had waiting for me.