From 489e27f0859a8753ca95c7b36ee2781c0b0fbfd9 Mon Sep 17 00:00:00 2001 From: Maisem Ali Date: Mon, 13 Mar 2023 08:48:09 -0700 Subject: [PATCH] cmd/k8s-operator: make auth proxy pass tags as Impersonate-Group We were not handling tags at all, pass them through as Impersonate-Group headers. And use the FQDN for tagged nodes as Impersonate-User. Updates #5055 Signed-off-by: Maisem Ali --- .../manifests/authproxy-rbac.yaml | 2 +- cmd/k8s-operator/proxy.go | 24 +++++++++++++++---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/cmd/k8s-operator/manifests/authproxy-rbac.yaml b/cmd/k8s-operator/manifests/authproxy-rbac.yaml index ae711a858..ddbdda32e 100644 --- a/cmd/k8s-operator/manifests/authproxy-rbac.yaml +++ b/cmd/k8s-operator/manifests/authproxy-rbac.yaml @@ -7,7 +7,7 @@ metadata: name: tailscale-auth-proxy rules: - apiGroups: [""] - resources: ["users"] + resources: ["users", "groups"] verbs: ["impersonate"] --- apiVersion: rbac.authorization.k8s.io/v1 diff --git a/cmd/k8s-operator/proxy.go b/cmd/k8s-operator/proxy.go index 83a4cfb81..94dde4809 100644 --- a/cmd/k8s-operator/proxy.go +++ b/cmd/k8s-operator/proxy.go @@ -51,13 +51,16 @@ func runAuthProxy(lc *tailscale.LocalClient, ls net.Listener, rt http.RoundTripp lc: lc, rp: &httputil.ReverseProxy{ Director: func(r *http.Request) { - // Replace the request with the user's identity. - who := r.Context().Value(whoIsKey{}).(*apitype.WhoIsResponse) - r.Header.Set("Impersonate-User", who.UserProfile.LoginName) + // We want to proxy to the Kubernetes API, but we want to use + // the caller's identity to do so. We do this by impersonating + // the caller using the Kubernetes User Impersonation feature: + // https://kubernetes.io/docs/reference/access-authn-authz/authentication/#user-impersonation - // Remove all authentication headers. + // Out of paranoia, remove all authentication headers that might + // have been set by the client. r.Header.Del("Authorization") r.Header.Del("Impersonate-Group") + r.Header.Del("Impersonate-User") r.Header.Del("Impersonate-Uid") for k := range r.Header { if strings.HasPrefix(k, "Impersonate-Extra-") { @@ -65,6 +68,19 @@ func runAuthProxy(lc *tailscale.LocalClient, ls net.Listener, rt http.RoundTripp } } + // Now add the impersonation headers that we want. + who := r.Context().Value(whoIsKey{}).(*apitype.WhoIsResponse) + if who.Node.IsTagged() { + // Use the nodes FQDN as the username, and the nodes tags as the groups. + // "Impersonate-Group" requires "Impersonate-User" to be set. + r.Header.Set("Impersonate-User", who.Node.Name) + for _, tag := range who.Node.Tags { + r.Header.Add("Impersonate-Group", tag) + } + } else { + r.Header.Set("Impersonate-User", who.UserProfile.LoginName) + } + // Replace the URL with the Kubernetes APIServer. r.URL.Scheme = u.Scheme r.URL.Host = u.Host