diff --git a/cmd/k8s-operator/proxy.go b/cmd/k8s-operator/proxy.go index 276502a32..2d18e3567 100644 --- a/cmd/k8s-operator/proxy.go +++ b/cmd/k8s-operator/proxy.go @@ -207,8 +207,11 @@ func runAPIServerProxy(s *tsnet.Server, rt http.RoundTripper, log *zap.SugaredLo } const ( - capabilityName = "tailscale.com/cap/kubernetes" - oldCapabilityName = "https://" + capabilityName + // oldCapabilityName is a legacy form of + // tailfcg.PeerCapabilityKubernetes capability. The only capability rule + // that is respected for this form is group impersonation - for + // backwards compatibility reasons. + oldCapabilityName = "https://" + tailcfg.PeerCapabilityKubernetes ) type capRule struct { @@ -229,7 +232,7 @@ type impersonateRule struct { func addImpersonationHeaders(r *http.Request, log *zap.SugaredLogger) error { log = log.With("remote", r.RemoteAddr) who := whoIsKey.Value(r.Context()) - rules, err := tailcfg.UnmarshalCapJSON[capRule](who.CapMap, capabilityName) + rules, err := tailcfg.UnmarshalCapJSON[capRule](who.CapMap, tailcfg.PeerCapabilityKubernetes) if len(rules) == 0 && err == nil { // Try the old capability name for backwards compatibility. rules, err = tailcfg.UnmarshalCapJSON[capRule](who.CapMap, oldCapabilityName) diff --git a/cmd/k8s-operator/proxy_test.go b/cmd/k8s-operator/proxy_test.go index 7f0a7237a..9e9634ac5 100644 --- a/cmd/k8s-operator/proxy_test.go +++ b/cmd/k8s-operator/proxy_test.go @@ -49,7 +49,7 @@ func TestImpersonationHeaders(t *testing.T) { name: "user-with-cap", emailish: "foo@example.com", capMap: tailcfg.PeerCapMap{ - capabilityName: { + tailcfg.PeerCapabilityKubernetes: { tailcfg.RawMessage(`{"impersonate":{"groups":["group1","group2"]}}`), tailcfg.RawMessage(`{"impersonate":{"groups":["group1","group3"]}}`), // One group is duplicated. tailcfg.RawMessage(`{"impersonate":{"groups":["group4"]}}`), @@ -71,7 +71,7 @@ func TestImpersonationHeaders(t *testing.T) { emailish: "tagged-device", tags: []string{"tag:foo", "tag:bar"}, capMap: tailcfg.PeerCapMap{ - capabilityName: { + tailcfg.PeerCapabilityKubernetes: { tailcfg.RawMessage(`{"impersonate":{"groups":["group1"]}}`), }, }, @@ -85,7 +85,7 @@ func TestImpersonationHeaders(t *testing.T) { emailish: "tagged-device", tags: []string{"tag:foo", "tag:bar"}, capMap: tailcfg.PeerCapMap{ - capabilityName: { + tailcfg.PeerCapabilityKubernetes: { tailcfg.RawMessage(`[]`), }, }, diff --git a/tailcfg/tailcfg.go b/tailcfg/tailcfg.go index 7b235c91a..f9cf9f8fc 100644 --- a/tailcfg/tailcfg.go +++ b/tailcfg/tailcfg.go @@ -1375,6 +1375,12 @@ type CapGrant struct { // PeerCapabilityTaildriveSharer indicates that a peer has the ability to // share folders with us. PeerCapabilityTaildriveSharer PeerCapability = "tailscale.com/cap/drive-sharer" + + // PeerCapabilityKubernetes grants a peer Kubernetes-specific + // capabilities, such as the ability to impersonate specific Tailscale + // user groups as Kubernetes user groups. This capability is read by + // peers that are Tailscale Kubernetes operator instances. + PeerCapabilityKubernetes PeerCapability = "tailscale.com/cap/kubernetes" ) // NodeCapMap is a map of capabilities to their optional values. It is valid for