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

Support routing to Kubernetes clusters by request path #50567

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

timothyb89
Copy link
Contributor

@timothyb89 timothyb89 commented 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.

Fixes #40405

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
@timothyb89
Copy link
Contributor Author

timothyb89 commented Dec 24, 2024

Outstanding TODOs:

  • Add manual testing instructions to PR description
  • Unit tests
  • More rigorously validate security impact. Particularly want to ensure per-session MFA guarantees remain intact.
  • PR for tbot changes to test this more easily. Should be a low-impact change, and my current hacked tsh build is not really an acceptable validation environment. PR is available here: Machine ID: Support path-based Kubernetes routing #50898

@@ -342,6 +342,11 @@ func NewForwarder(cfg ForwarderConfig) (*Forwarder, error) {

router.GET("/api/:ver/teleport/join/:session", fwd.withAuthPassthrough(fwd.join))

path := fmt.Sprintf("/v1/teleport/:%s/:%s/*path", paramTeleportCluster, paramKubernetesCluster)
for _, method := range allHTTPMethods() {
router.Handle(method, path, fwd.withAuthPassthrough(fwd.singleCertHandler, withRouteFromPath()))
Copy link
Contributor Author

Choose a reason for hiding this comment

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

As a note, compared to @tigrato's PoC I had to adapt withAuthPassthrough() to support path routing as well, since it calls f.authenticate() which requires the route info. I'm not sure if anything's changed since the PoC or if I missed something else, but that change seemed to be necessary.

Hopefully my route sourcing implementation is sane 😅

Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need to authenticate the request given that we will authenticate it once we rewrite the handler?

@@ -342,6 +342,11 @@ func NewForwarder(cfg ForwarderConfig) (*Forwarder, error) {

router.GET("/api/:ver/teleport/join/:session", fwd.withAuthPassthrough(fwd.join))

path := fmt.Sprintf("/v1/teleport/:%s/:%s/*path", paramTeleportCluster, paramKubernetesCluster)
Copy link
Contributor

Choose a reason for hiding this comment

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

specifying the path ends up being way better in terms of readability instead of using fmt.Sprintf

@@ -342,6 +342,11 @@ func NewForwarder(cfg ForwarderConfig) (*Forwarder, error) {

router.GET("/api/:ver/teleport/join/:session", fwd.withAuthPassthrough(fwd.join))

path := fmt.Sprintf("/v1/teleport/:%s/:%s/*path", paramTeleportCluster, paramKubernetesCluster)
for _, method := range allHTTPMethods() {
router.Handle(method, path, fwd.withAuthPassthrough(fwd.singleCertHandler, withRouteFromPath()))
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need to authenticate the request given that we will authenticate it once we rewrite the handler?

@@ -520,7 +525,7 @@ type handlerWithAuthFuncStd func(ctx *authContext, w http.ResponseWriter, r *htt
const accessDeniedMsg = "[00] access denied"

// authenticate function authenticates request
func (f *Forwarder) authenticate(req *http.Request) (*authContext, error) {
func (f *Forwarder) authenticate(req *http.Request, params httprouter.Params, routeSourcer routeSourcer) (*authContext, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't fully understand why you need to pass the params/route sourcer here.

Given that singleCertHandler already defines the

o.Identity.RouteToCluster = teleportCluster
o.Identity.KubernetesCluster = kubeCluster

Do we ned to have a special case for route sourcer? We can always retrieve it from identity

return "", "", trace.Wrap(err)
}

// TODO: do we care to otherwise validate these results before casting
Copy link
Contributor

Choose a reason for hiding this comment

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

yeah, I think it would be better anw

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support connecting to multiple Kubernetes clusters using a single X509 cert
2 participants