Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFD0185: Kubernetes Access without Certificate-based Routing #47436

Merged

Conversation

strideynet
Copy link
Contributor

@strideynet strideynet commented Oct 10, 2024

Part of #40405

Proposal to replace Certificate-attribute based routing in Kubernetes Access for performance and usability improvements. There's a very rough patch of what this change would look like at 47b55a8 courtesy of @tigrato


## Security Considerations

### Per-session MFA
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My outstanding concern here relates to whether or not Per-Session MFA is meant to be locked to a specific target.

Today, the KubernetesCluster attribute seemingly has the side-effect of locking Per-Session MFA certificates to a specific target cluster. Looking back to the original Per-Session MFA RFD, the design does state that that should be the case, but says that a "TargetName" attribute should be used to enforce this - which I can't see any evidence of within the codebase today.

If locking Per-Session MFA certs to a specific cluster is intended/required, then we can retain the KubernetesCluster attribute, but make this intention more explicit by moving the code that enforces this into the authorizer rather than as part of the router/forwarder.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To close the loop here with #50567 now merged, after some discussion we decided to keep this guarantee in place with:

  1. returning an error if URL routing parameters attempt to override certificate parameters, to ensure session MFA certs remain bound to the cluster for which they were issued
  2. explicitly requiring non-empty identity routing parameters to use the path routing endpoint if an MFAVerified value is present on the cert. Path routing is functionally no-op due in this case to (1), but this won't force e.g. tsh to stop using the path route in kubeconfig.yaml

@strideynet strideynet requested a review from tigrato October 11, 2024 09:40
Comment on lines +58 to +59
encode attributes of an identity - information about which cluster you wish to
connect to is not an attribute of identity!
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will approve the RFD just because of this comment

```yaml
apiVersion: v1
clusters:
- cluster:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's important to note that this setup requires a separate cluster entry for each Kubernetes cluster. Currently, our implementation assigns one cluster per Teleport cluster rather than per Kubernetes cluster. This approach has a significant drawback: when two different Kubernetes clusters are part of the same Teleport cluster, kubectl caches objects in the same directory .kube/cache/discovery/teleport_proxy_port. This can lead to major issues due to version mismatches or incompatible CRDs.

The proposed solution addresses all these problems because kubectl will start caching data in .kube/cache/discovery/teleport_proxy_port/v1/teleport/base64_teleport_cluster_name/base64_kube_cluster_name

Check #12731

Kubernetes clients:

- `kubectl`
- Python Kubernetes SDK
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's worth checking javascript client as we have some that also use it for automation.

It would no longer be possible to track usage of Kubernetes Access for analytics
purposes via the certificate generation event. However, we have other events
that indicate Kubernetes Access usage (e.g an event per request), that are
better suited for this purpose - so this seems acceptable.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that when kubectl performs discovery, it requests a single certificate but retrieves all API resource definitions. In theory, this could generate duplicate events, but Teleport disregards discovery requests for usage purposes, so nothing will change neither the metrics will be duplicated.

As for billing, since we count Kubernetes requests, this behavior won’t affect anything.

# are provided, then the output will contain the union of the results of the
# selectors.
selectors:
- name: my-cluster
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this generate a separate kubeconfig for each cluster, or a single configuration that includes all clusters?

I prefer the latter, but it might be worth mentioning or offering both options. We should also pay attention to context and file naming. By default, tsh creates context entries using the format {{TeleportCluster}}-{{KubeCluster}}, but this can be customized by providing a template with the context override flag. It would be useful to allow users to adjust these settings to customize their outputs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah - following discussion with [CUSTOMER] - I figure it's easier to give an option between a single kubeconfig and a kubeconfig per cluster. I'm sure one you get into the thousands of clusters range, certain implementations of the Kube SDK will probably dislike the size of the config file.

By default, tsh creates context entries using the format {{TeleportCluster}}-{{KubeCluster}}, but this can be customized by providing a template with the context override flag. It would be useful to allow users to adjust these settings to customize their outputs.

This seems reasonable - we use the same context structure in tbot as well atm fwiw.


This has the following limitations:

- We've already witnessed limited support for specifying SNIs in Kubernetes
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main challenge is that by using SNI for routing and supporting kubectl, Teleport would need to generate certificates for *.rootCluster.kube.teleportproxy.com, as well as for leaf clusters *.leafCluster.kube.teleportproxy.com. Over time, this approach could introduce significant issues as the system evolves. Since we must support kubectl, the only viable solution is to send the identification via tls-server-name.

@strideynet strideynet added the no-changelog Indicates that a PR does not require a changelog entry label Oct 11, 2024
@strideynet strideynet marked this pull request as ready for review October 14, 2024 11:00
@github-actions github-actions bot added rfd Request for Discussion size/md labels Oct 14, 2024
Comment on lines +108 to +111
Teleport clients would no longer need to call `GenerateUserCerts` with a
`KubernetesCluster` to produce a certificate explicitly for access to
Kubernetes. When producing a kubeconfig, the client should use the user's
standard identity.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Beyond just routing info (which this will be superseding) we also have a certificate usage restriction for Kubernetes, which the auth middleware currently requires for anything that touches the forwarder. Do you see the usage:kube restriction going away with this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The usage was only configured when using KubeCSR, which was deprecated several versions ago.

Furthermore, if the identity does not have a usage specified, it will bypass any usage validation checks.

// If there is any restriction on the certificate usage
// reject the API server request. This is done so some classes
// of certificates issued for kubernetes usage by proxy, can not be used
// against auth server. Later on we can extend more
// advanced cert usage, but for now this is the safest option.
if len(identity.Usage) != 0 && !slices.Equal(a.AcceptedUsage, identity.Usage) {
log.Warningf("Restricted certificate of user %q with usage %v rejected while accessing the auth endpoint with acceptable usage %v.",
identity.Username, identity.Usage, a.AcceptedUsage)
return nil, trace.AccessDenied("access denied: invalid client certificate")
}

timothyb89 added a commit that referenced this pull request Dec 24, 2024
This implements path-based routing for Kubernetes clusters as
described by [RFD0185]. A new prefixed path handler is added that
accepts base64-encoded Teleport and Kubernetes cluster names. The
request is routed to the destination Teleport cluster using these
parameters instead of those embedded in the session TLS identity, and
then the preexisting handlers check authorization and complete the
request as usual.

This removes the need for certificates to be issued per Kubernetes
cluster: so long as the incoming identity is granted access to the
cluster via its roles, access can succeed, and no `KubernetesCluster`
attribute or cert usage restrictions are needed.

[RFD0185]: #47436
@rosstimothy rosstimothy requested a review from creack January 13, 2025 22:01
github-merge-queue bot pushed a commit that referenced this pull request Jan 27, 2025
* Support routing to Kubernetes clusters by request path

This implements path-based routing for Kubernetes clusters as
described by [RFD0185]. A new prefixed path handler is added that
accepts base64-encoded Teleport and Kubernetes cluster names. The
request is routed to the destination Teleport cluster using these
parameters instead of those embedded in the session TLS identity, and
then the preexisting handlers check authorization and complete the
request as usual.

This removes the need for certificates to be issued per Kubernetes
cluster: so long as the incoming identity is granted access to the
cluster via its roles, access can succeed, and no `KubernetesCluster`
attribute or cert usage restrictions are needed.

[RFD0185]: #47436

* Remove routeSourcer in favor of identity values

Also: fixes various other review comments, adds comment explaining
auth strategy.

* Revert more dead code

* Revert more unnecessary changes; validate utf8 parameters

* Build fixes after merge with slog transition

* Add basic tests for Kubernetes path routing

* Fix imports

* Check identity parameters when per session MFA is enabled

Also, don't allow path parameters to override the identity, if the
contains nonempty routing params.

* Fix failing TestSingleCertRouting due to improper reuse of rest config

* Session MFA includes role based enforcement; added more test cases

* Don't allow route params to be overwritten if MFAVerified is set

* Code review feedback

Deep clone the request and explicitly clear RawPath.
timothyb89 added a commit that referenced this pull request Jan 28, 2025
* Support routing to Kubernetes clusters by request path

This implements path-based routing for Kubernetes clusters as
described by [RFD0185]. A new prefixed path handler is added that
accepts base64-encoded Teleport and Kubernetes cluster names. The
request is routed to the destination Teleport cluster using these
parameters instead of those embedded in the session TLS identity, and
then the preexisting handlers check authorization and complete the
request as usual.

This removes the need for certificates to be issued per Kubernetes
cluster: so long as the incoming identity is granted access to the
cluster via its roles, access can succeed, and no `KubernetesCluster`
attribute or cert usage restrictions are needed.

[RFD0185]: #47436

* Remove routeSourcer in favor of identity values

Also: fixes various other review comments, adds comment explaining
auth strategy.

* Revert more dead code

* Revert more unnecessary changes; validate utf8 parameters

* Build fixes after merge with slog transition

* Add basic tests for Kubernetes path routing

* Fix imports

* Check identity parameters when per session MFA is enabled

Also, don't allow path parameters to override the identity, if the
contains nonempty routing params.

* Fix failing TestSingleCertRouting due to improper reuse of rest config

* Session MFA includes role based enforcement; added more test cases

* Don't allow route params to be overwritten if MFAVerified is set

* Code review feedback

Deep clone the request and explicitly clear RawPath.
@ravicious
Copy link
Member

Just read this following Tim's status update and it all sounds really nice. Being able to weave this into kube APIs is *chef's kiss*. If only this was possible for TCP apps. 🥲

@tigrato
Copy link
Contributor

tigrato commented Jan 28, 2025

Just read this following Tim's status update and it all sounds really nice. Being able to weave this into kube APIs is *chef's kiss*. If only this was possible for TCP apps. 🥲

This is possible to expand to tcp apps using SNI

github-merge-queue bot pushed a commit that referenced this pull request Jan 29, 2025
…#51534)

* Support routing to Kubernetes clusters by request path (#50567)

* Support routing to Kubernetes clusters by request path

This implements path-based routing for Kubernetes clusters as
described by [RFD0185]. A new prefixed path handler is added that
accepts base64-encoded Teleport and Kubernetes cluster names. The
request is routed to the destination Teleport cluster using these
parameters instead of those embedded in the session TLS identity, and
then the preexisting handlers check authorization and complete the
request as usual.

This removes the need for certificates to be issued per Kubernetes
cluster: so long as the incoming identity is granted access to the
cluster via its roles, access can succeed, and no `KubernetesCluster`
attribute or cert usage restrictions are needed.

[RFD0185]: #47436

* Remove routeSourcer in favor of identity values

Also: fixes various other review comments, adds comment explaining
auth strategy.

* Revert more dead code

* Revert more unnecessary changes; validate utf8 parameters

* Build fixes after merge with slog transition

* Add basic tests for Kubernetes path routing

* Fix imports

* Check identity parameters when per session MFA is enabled

Also, don't allow path parameters to override the identity, if the
contains nonempty routing params.

* Fix failing TestSingleCertRouting due to improper reuse of rest config

* Session MFA includes role based enforcement; added more test cases

* Don't allow route params to be overwritten if MFAVerified is set

* Code review feedback

Deep clone the request and explicitly clear RawPath.

* Undo slog for v17 compatibility
Copy link
Contributor

@tigrato tigrato left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@strideynet can we merge this?

@strideynet
Copy link
Contributor Author

@strideynet can we merge this?

Needs one more review by the looks of it, then I'll merge.

@public-teleport-github-review-bot public-teleport-github-review-bot bot removed the request for review from creack February 6, 2025 10:37
@strideynet strideynet added this pull request to the merge queue Feb 6, 2025
Merged via the queue into master with commit b7afa6a Feb 6, 2025
43 of 44 checks passed
@strideynet strideynet deleted the rfd/185-kubernetes-access-cert-attribute-less-routing branch February 6, 2025 10:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
no-changelog Indicates that a PR does not require a changelog entry rfd Request for Discussion size/md
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants