Skip to content

Automatically authenticate required applications during web app auth#45512

Merged
avatus merged 1 commit intomasterfrom
avatus/redirect
Sep 7, 2024
Merged

Automatically authenticate required applications during web app auth#45512
avatus merged 1 commit intomasterfrom
avatus/redirect

Conversation

@avatus
Copy link
Copy Markdown
Contributor

@avatus avatus commented Aug 14, 2024

part of: #17588
This app fixes the issue where a user would have to manually click the "launch" button for every app that another app may require to work (for instance, a backend API for a client side react app).

The solution is to have admins set a "required apps" field in their app spec, which will allow the app handler and client to automate the authentication redirects when launching the desired app. This value is passed through a query param through the authentication flow and used by the server to determine which app to redirect to next, ultimately landing on the originally requested app.

security considerations

Although this query param can be "user supplied", it does not bypass any of the current authentication flow and goes through the same validation as if it were clicked from the "Launch" button in the Web UI. It is used merely as an indication of which URL to build.
Scenarios checked (please let me know if I've missed any)

  • If this was somehow maliciously set by an admin to a bogus website, the same checks for "does this app really exist in our cluster" are still made. The same thing happens if the query param is tampered with, the validation is still done.
  • If an app requires an app that the user doesn't have access to, the authentication flow will still fail as usual.
  • If an app can't be found during the authentication flow (either via mis-configuration or maliciously set by a user), the failed app is logged and thrown out. This means misconfiguration should not stop authentication of the original requested app

Example breakdown

the app named client requires the app api to serve it static assets

Example old flow:
-> The user would authenticate to Teleport.
-> The user would click "Launch" for client.
-> client would go through the redirect flow
-> client will be authenticated and load. However, assets would not load because api is not authenticated.
-> user goes back to teleport, clicks "Launch" on api, and goes through the redirect flow
-> client now works as expected.
-> repeat above for any other required apps

New flow
-> The user would authentication to Teleport
-> The user would click "Launch" for client
-> client will instead go through the redirect flow for any of the required apps.
-> once all the required apps are done, the last redirect is for client
-> client works as expected.

This essentially boils down to automatically going to all the correct /web/launch/:fqdn URLs in the browser and nothing more.

Example of my app service

app_service:
  enabled: "yes"
  debug_app: true
  apps:
    - name: "api22"
      uri: "http://localhost:4000"
      public_addr: "api.teleport.zarquon.sh"
    - name: "client"
      uri: "http://localhost:3002"
      public_addr: "client.teleport.zarquon.sh"
      required_apps:
        - "api22"

This PR will not solve the preflight CORS requests if client.teleport.zarquon.sh is trying to access api.teleport.zarquon.sh with some sort of custom header (such as Authorization) because currently, we do not accept non-authenticated requests (OPTIONS requests are always unauthenticated). That PR will come after.

You can use this repo for local apps to test with https://github.com/avatus/corstest

changelog: You can now set required app names in your application spec. This will enable the Web UI to automatically authenticate every app required during application authentication.

Comment thread lib/web/app/auth.go
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Sep 4, 2024

🤖 Vercel preview here: https://docs-4ny5njkol-goteleport.vercel.app/docs/ver/preview

@avatus avatus requested a review from kimlisa September 4, 2024 19:25
@avatus
Copy link
Copy Markdown
Contributor Author

avatus commented Sep 6, 2024

@ryanclark could you give this one a look when you get the chance please 🙏 . Security review came back good so this is ready

Copy link
Copy Markdown
Contributor

@flyinghermit flyinghermit left a comment

Choose a reason for hiding this comment

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

LGTM based on the decision that this is the way we want to implement the feature.

Left a few suggestions on renaming and reducing extra request steps.

Comment thread api/types/app.go Outdated
// GetIntegration will return the Integration.
// If present, the Application must use the Integration's credentials instead of ambient credentials to access Cloud APIs.
GetIntegration() string
// GetRequiredApps will return a list of required apps public addrs that should be authenticated during this apps authentication process.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

// GetRequiredApps will return a list of required apps public addrs

Does this return public addrs or app name?

Comment thread lib/web/app/middleware.go Outdated
// This field is used to preserve the original requested path through
// the app access authentication redirections.
path string
// requiredApps is a list of required app names to be used during application
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

// requiredApps is a list of required app names

At this point, the value of requiredApps is fqdn and not app name right?

Suggested change
// requiredApps is a list of required app names to be used during application
// requiredApps is a list of required app fqdn to be used during application

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think it would also be good to update the names so distinction is clear?

  • requiredAppsName in the app configuration spec and
  • requiredAppsFQDN here

Comment thread lib/web/apps.go Outdated
type GetAppDetailsResponse struct {
// FQDN is application FQDN.
FQDN string `json:"fqdn"`
// RequiredApps is a list of required app names
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

fqdn here too?

Suggested change
// RequiredApps is a list of required app names
// RequiredApps is a list of required app FQDN

}

let requiredApps = resolvedApp.requiredApps || [];
if (isRedirectFlow !== null) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit:

Suggested change
if (isRedirectFlow !== null) {
if (!!isRedirectFlow) {

(unless you want to make it clear that we are explicitly checking for null :) )

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Specifically checking for null because redirectFlow is got from queryParams.get which returns null or string. (also, the double negation throws eslint here based on string type so, i'll leave this one)

Comment thread lib/web/apps.go
// GET /v1/webapi/apps/:fqdnHint/:clusterName/:publicAddr
func (h *Handler) getAppFQDN(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *SessionContext) (interface{}, error) {
req := GetAppFQDNRequest{
func (h *Handler) getAppDetails(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *SessionContext) (interface{}, error) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

suggestion: If we can send required apps (fqdn) in the app response from the unified resource api, we can collapse the first two requests we have now to one.

  1. First request /web/launch...?
  2. Second request webapi/apps responds with required apps
  3. Third request /web/launch...?required-apps=<required app from responds received in second request >.

I can see this URL was always called to get the app fqdn which I do not understand why it was done that way. Up to you if you want to reduce number of requests here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

That or looks like we cna also combine the state and required apps response to one, which would reduce few extra requests.

Copy link
Copy Markdown
Contributor Author

@avatus avatus Sep 6, 2024

Choose a reason for hiding this comment

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

Agree, but I will TODO this for now. I'd like to talk to @capnspacehook to make sure this extra call can be merged with the first. Really like this idea a lot

most of the /web/launch/ calls are from a redirect and not a direct api call so sending the data back might be a bit weird but I'll explore it

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Sep 6, 2024

🤖 Vercel preview here: https://docs-faqodnc7t-goteleport.vercel.app/docs/ver/preview

@avatus
Copy link
Copy Markdown
Contributor Author

avatus commented Sep 6, 2024

Im not sure what happened, but a bunch of other changes got scooped up when i did make derive. But its rebased on latest master. I'll inspect before i merge

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Sep 6, 2024

🤖 Vercel preview here: https://docs-bgt920ijl-goteleport.vercel.app/docs/ver/preview

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Sep 6, 2024

🤖 Vercel preview here: https://docs-5oxjb7sll-goteleport.vercel.app/docs/ver/preview

@avatus avatus added this pull request to the merge queue Sep 7, 2024
Merged via the queue into master with commit 8352594 Sep 7, 2024
@avatus avatus deleted the avatus/redirect branch September 7, 2024 01:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants