kubernetes-client / csharp

Officially supported dotnet Kubernetes Client library
Apache License 2.0
1.07k stars 291 forks source link

Login with client.authentication.k8s.io/v1beta1 fails due to handling of arguments #1568

Open JonasMH opened 2 weeks ago

JonasMH commented 2 weeks ago

Describe the bug I'm trying to use the library against a cluster protected by Pinniped which uses a custom exec 'client.authentication.k8s.io/v1beta1' to authenticate the user. One of the arguments contains a space which seems to be handled incorrectly by the Kubernetes C# SDK.

The following Kubeconfig works fine using kubectl

apiVersion: v1
clusters:
  - cluster:
      server: https://pinniped-concierge-impersonation-proxy.mycluster-staging.example.com
    name: mycluster-staging-example-com-pinniped
contexts:
  - context:
      cluster: mycluster-staging-example-com-pinniped
      user: mycluster-staging-example-com-pinniped-cluster-reader
    name: mycluster-staging-readonly
kind: Config
preferences: {}
users:
  - name: mycluster-staging-example-com-pinniped-cluster-reader
    user:
      exec:
        apiVersion: client.authentication.k8s.io/v1beta1
        args:
          - login
          - oidc
          - --enable-concierge
          - --concierge-api-group-suffix=pinniped.dev
          - --concierge-authenticator-name=pinniped-concierge-jwt-auth
          - --concierge-authenticator-type=jwt
          - --concierge-endpoint=https://pinniped-concierge-impersonation-proxy.mycluster-staging.example.com
          - --issuer=https://sup-pinniped.infra.example.com
          - --client-id=pinniped-cli
          - --scopes=offline_access,openid,pinniped:request-audience,username,groups
          - --request-audience=mycluster-staging.example.com
          - --upstream-identity-provider-name=Entra ID # <----------- Note the space in Entra ID causing the issue
          - --upstream-identity-provider-type=oidc
          - --upstream-identity-provider-flow=browser_authcode
          - --upstream-identity-provider-flow=browser_authcode
        command: pinniped
        env: []
        installHint:
          The pinniped CLI does not appear to be installed.  See https://get.pinniped.dev/cli
          for more details
        interactiveMode: IfAvailable
        provideClusterInfo: true

Also tried with

- --upstream-identity-provider-name
- Entra ID

But that fails with the same error

But fails when using the C# Kubernetes SDK with:

Error: unknown command "ID" for "pinniped login oidc"
Unhandled exception. k8s.Exceptions.KubeConfigException: external exec failed due to failed deserialization process: System.Text.Json.JsonException: The input does not contain any JSON tokens. Expected the input to start with a valid JSON token, when isFinalBlock is true. Path: $ | LineNumber: 0 | BytePositionInLine: 0.
 ---> System.Text.Json.JsonReaderException: The input does not contain any JSON tokens. Expected the input to start with a valid JSON token, when isFinalBlock is true. LineNumber: 0 | BytePositionInLine: 0.
   at System.Text.Json.ThrowHelper.ThrowJsonReaderException(Utf8JsonReader& json, ExceptionResource resource, Byte nextByte, ReadOnlySpan`1 bytes)
   at System.Text.Json.Utf8JsonReader.Read()
   at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
   --- End of inner exception stack trace ---
   at System.Text.Json.ThrowHelper.ReThrowWithPath(ReadStack& state, JsonReaderException ex)
   at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
   at System.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan`1 utf8Json, JsonTypeInfo`1 jsonTypeInfo, Nullable`1 actualByteCount)
   at System.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan`1 json, JsonTypeInfo`1 jsonTypeInfo)
   at k8s.KubernetesJson.Deserialize[TValue](String json, JsonSerializerOptions jsonSerializerOptions)
   at k8s.KubernetesClientConfiguration.ExecuteExternalCommand(ExternalExecution config)
   at k8s.KubernetesClientConfiguration.ExecuteExternalCommand(ExternalExecution config)
   at k8s.KubernetesClientConfiguration.SetUserDetails(K8SConfiguration k8SConfig, Context activeContext)
   at k8s.KubernetesClientConfiguration.InitializeContext(K8SConfiguration k8SConfig, String currentContext)
   at k8s.KubernetesClientConfiguration.GetKubernetesClientConfiguration(String currentContext, String masterUrl, K8SConfiguration k8SConfig)
   at k8s.KubernetesClientConfiguration.BuildConfigFromConfigFileAsync(FileInfo kubeconfig, String currentContext, String masterUrl, Boolean useRelativePaths)
   at k8s.KubernetesClientConfiguration.BuildConfigFromConfigFile(FileInfo kubeconfig, String currentContext, String masterUrl, Boolean useRelativePaths)
   at k8s.KubernetesClientConfiguration.BuildConfigFromConfigFile(String kubeconfigPath, String currentContext, String masterUrl, Boolean useRelativePaths)
   at FixWorkspacesCommand.ExecuteAsync(CommandContext context) in /home/jmp/git/Hyperplane/FixWorkspaceCommand.cs:line 44

If i instead write Entra ID wrapped in quotes as below, C# Kubernetes SDK Handles it fine

- --upstream-identity-provider-name="Entra ID"

But now kubectl doesn't properly forward the arguments to the pinniped cli, as the quotes which will be seen as a part of the argument value, which makes it invalid

Kubernetes C# SDK Client Version 14.0.2

Server Kubernetes Version v1.28.2

Dotnet Runtime Version .net8

To Reproduce Steps to reproduce the behavior:

NB: Doesn't require a running cluster

  1. Install the pinniped CLI
  2. Copy-paste the kubeconfig from below
  3. Try to load it using
    var config = KubernetesClientConfiguration.BuildConfigFromConfigFile("kubeconfig.yaml", "mycluster-staging-readonly");    

I don't imagine the issue is only against Pinniped login flows

Expected behavior It should handle arguments given to a client.authentication.k8s.io/v1beta1 the same way as kubectl, not matter if there's a space or not

KubeConfig

apiVersion: v1
clusters:
  - cluster:
      server: https://pinniped-concierge-impersonation-proxy.mycluster-staging.example.com
    name: mycluster-staging-example-com-pinniped
contexts:
  - context:
      cluster: mycluster-staging-example-com-pinniped
      user: mycluster-staging-example-com-pinniped-cluster-reader
    name: mycluster-staging-readonly
kind: Config
preferences: {}
users:
  - name: mycluster-staging-example-com-pinniped-cluster-reader
    user:
      exec:
        apiVersion: client.authentication.k8s.io/v1beta1
        args:
          - login
          - oidc
          - --enable-concierge
          - --concierge-api-group-suffix=pinniped.dev
          - --concierge-authenticator-name=pinniped-concierge-jwt-auth
          - --concierge-authenticator-type=jwt
          - --concierge-endpoint=https://pinniped-concierge-impersonation-proxy.mycluster-staging.example.com
          - --issuer=https://sup-pinniped.infra.example.com
          - --client-id=pinniped-cli
          - --scopes=offline_access,openid,pinniped:request-audience,username,groups
          - --request-audience=mycluster-staging.example.com
          - --upstream-identity-provider-name=Entra ID
          - --upstream-identity-provider-type=oidc
          - --upstream-identity-provider-flow=browser_authcode
          - --upstream-identity-provider-flow=browser_authcode
        command: pinniped
        env: []
        installHint:
          The pinniped CLI does not appear to be installed.  See https://get.pinniped.dev/cli
          for more details
        interactiveMode: IfAvailable
        provideClusterInfo: true

Where do you run your app with Kubernetes SDK (please complete the following information):

Additional context Add any other context about the problem here.

tg123 commented 2 weeks ago

you need to put pinniped in PATH

seems sdk did not get cmd correctly, let me take a look