Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Multiple Impersonate-Group Not Possible #2474

Copy link
Copy link
Closed
@karl-cardenas-coding

Description

@karl-cardenas-coding
Issue body actions

Describe the bug

The official Kubernetes documentation states that when performing user impersonation, multiple groups can be specified with a request through headers. The guidance is to place each group in an individual header entry, like so:

Impersonate-User: jane.doe@example.com
Impersonate-Group: developers
Impersonate-Group: admins

The challenge with this approach is that Node.js and other JavaScript runtime environments do not support adding identical keys to an object. This breaks the ability to specify multiple user groups through impersonation.

    const kc = new k8s.KubeConfig();
    kc.loadFromDefault();
    const currentUser = kc.getCurrentUser()
    const currentCluster = kc.getCurrentCluster()

    const results: any = {}

    const agent = new https.Agent({
        ca: currentCluster?.caData
            ? Buffer.from(currentCluster.caData, 'base64').toString('utf8')
            : undefined,
        cert: currentUser?.certData
            ? Buffer.from(currentUser.certData, 'base64').toString('utf8')
            : undefined,
        key: currentUser?.keyData
            ? Buffer.from(currentUser.keyData, 'base64').toString('utf8')
            : undefined,
        keepAlive: true,
    })

    const opts = {
        method: 'GET',
        headers: {
            'Impersonate-User': 'acme@example.com',
            'Impersonate-Group': 'developer-write',
            'Impersonate-Group': 'developer-read',
        },
        agent: agent,
    }
    await kc.applyToHTTPSOptions(opts)
    const url = `${currentCluster?.server}/api/v1/pods`

    try {
        const response = await fetch(url, opts as RequestInit)
        const body = await response.text()
        const podList = JSON.parse(body)
        results.pods = {
            success: true,
            items: podList.items?.map((pod: any) => pod.metadata.name) || [],
            rawStatus: response.status,
        }
    } catch (err: any) {
        results.pods = {
            success: false,
            error: err.message,
            stack: err.stack,
        }
    }

This will result in a single Impersonate-Group entry in the request with the value being the last entry of the identical key. In this example, that would be developer-read.

If I print out opts before the request I get this:

opts {
  method: 'GET',
  headers: {
    'Impersonate-User': 'acme@example.com',
    'Impersonate-Group': 'developer-read'
  },
  agent: Agent {
    _events: [Object: null prototype] {
      free: [Function (anonymous)],
      newListener: [Function: maybeEnableKeylog]
    },
    _eventsCount: 2,
    _maxListeners: undefined,
    defaultPort: 443,
    protocol: 'https:',
    options: [Object: null prototype] {
      ca: '-----BEGIN CERTIFICATE-----\n' +

I'm not really sure what can get done from the SDK's perspective to fix this. Part of the reason I brought this issue to light is to help others who may encounter it. But also to see if we can maybe make some traction from a Kubernetes API perspective to pass in groups differently.

Client Version

Client Version: v1.33.1
Kustomize Version: v5.6.0
Server Version: v1.32.2

In package.json I have 1.3.0 of this SDK.

Server Version
e.g. 1.32.2

To Reproduce
You can create two roles with whatever permissions you want, such as list pods, and create a binding to the role and specify the group. Below is an example:

 kubectl create clusterrole developer-read \
            --verb=get,list,watch \
            --resource=pods,services,configmaps \
            --resource=deployments.apps \
            --dry-run=client -o yaml | kubectl apply -f - && \
          kubectl create clusterrolebinding developer-read-binding \
            --clusterrole=developer-read \
            --group=developer-read \
            --dry-run=client -o yaml | kubectl apply -f - && \
          kubectl create clusterrole developer-write \
            --verb=create,update,delete,get,list \
            --resource=pods,services,configmaps \
            --resource=deployments.apps \
            --dry-run=client -o yaml | kubectl apply -f - && \
          kubectl create clusterrolebinding developer-write-binding \
            --clusterrole=developer-write \
            --group=developer-write \

Try to use add multiple groups to the user impersonation header,

Expected behavior

I expect the same behavior I get with kubectl if I were to use the impersonation flags.

kubectl --as=acme@example.com --as-group=developer-read --as-group=developer-read get pods -n demo
NAME                                        READY   STATUS    RESTARTS   AGE
primary-55c477cf8b-5wwxc                    1/1     Running   0          15m
secondary-77cd86bc9-8dhpg                       1/1     Running   0          15m
dev-devspace-f8bb6b678-56jvv         2/2     Running   0          14m
cert-manager-cainjector-5c87f4477-g6spl     1/1     Running   0          15m
cert-manager-fd6dcf8cb-nd8fs                1/1     Running   0          15m
cert-manager-webhook-54cd859596-ghqnl       1/1     Running   0          15m
dex-58fd549d7b-h264k                        1/1     Running   0          15m
helm-controller-69b7c9dbd7-8brdg            1/1     Running   0          15m
star-6d8c59f7c7-54zgw                        1/1     Running   0          15m
ingress-nginx-controller-84cf557d69-bq2cq   1/1     Running   0          15m
kustomize-controller-657f4fdfcd-8z6fs       1/1     Running   0          15m
apple-manager-886567c48-zb8vd              1/1     Running   0          15m
source-controller-5dc6d5d47-xv4b5           1/1     Running   0          15m
zot-568

From an SDK perspective, I expect to be able to achieve the same capability.

Example Code
See above.

Environment (please complete the following information):

  • OS: Apline, MacOS
  • Node.js version 22
  • Cloud runtime N/A - Kind, and other k8s environments

Additional context

There are other SDKs that are experiencing this issue, but no solution has presented itself.

If I try to pass in the values in a single entry comma-separated, I also get an error. The snipped below is from the Kubernetes Audit log

  },
  "impersonatedUser": {
    "username": "acme@example.com",
    "groups": [
      "developer-write,developer-read",
      "system:authenticated"
    ]
  },
  "sourceIPs": [
    "10.244.0.23"
  ],
  "userAgent": "node-fetch",
  "objectRef": {
    "resource": "pods",
    "apiVersion": "v1"
  },
  "responseStatus": {
    "metadata": {},
    "status": "Failure",
    "message": "pods is forbidden: User \"acme@example.com\" cannot list resource \"pods\" in API group \"\" at the cluster scope",
    "reason": "Forbidden",
    "details": {
      "kind": "pods"
    },
    "code": 403
  },

But if I only include one of the groups

 "impersonatedUser": {
    "username": "acme@example.com",
    "groups": [
      "developer-read",
      "system:authenticated"
    ]
  },
  "sourceIPs": [
    "10.244.0.23"
  ],
  "userAgent": "node-fetch",
  "objectRef": {
    "resource": "pods",
    "apiVersion": "v1"
  },
  "responseStatus": {
    "metadata": {},
    "code": 200
  },
  "responseObject": {
    "kind": "PodList",
    "apiVersion": "v1",

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      Morty Proxy This is a proxified and sanitized view of the page, visit original site.