GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Workspace applications CORS handling prevents external requests to apps · Issue #15096 · coder/coder

Workspace applications CORS handling prevents external requests to apps #15096

New issue
Copy link
New issue
Copy link
Closed
Feature
Closed
Workspace applications CORS handling prevents external requests to apps#15096
Feature
Copy link
Assignees
cstyan
Labels
must-doIssues that must be completed by the end of the Sprint. Or else. Only humans may set this.Issues that must be completed by the end of the Sprint. Or else. Only humans may set this.needs decisionNeeds a higher-level decision to be unblocked.Needs a higher-level decision to be unblocked.

Description

@Emyrk
Emyrk
opened on Oct 15, 2024
Issue body actions

Problem

  • External requests to a Coder app will always fail.
  • Workspace apps cannot make requests to each other if they are owned by different users.

Explanation of why

As coder stands today (v2.15.3), we allow cross domain requests between workspace apps of the same user. Meaning you could host a backend webserver on workspace A, and host the frontend on workspace B. As long as the workspaces are owned by the same user, all web requests will function correctly (assuming credentials: "include").

This is all handled magically here

coder/coderd/httpmw/cors.go

Lines 47 to 75 in eb1602b

func WorkspaceAppCors(regex *regexp.Regexp, app appurl.ApplicationURL) func(next http.Handler) http.Handler {
return cors.Handler(cors.Options{
AllowOriginFunc: func(r *http.Request, rawOrigin string) bool {
origin, err := url.Parse(rawOrigin)
if rawOrigin == "" || origin.Host == "" || err != nil {
return false
}
subdomain, ok := appurl.ExecuteHostnamePattern(regex, origin.Host)
if !ok {
return false
}
originApp, err := appurl.ParseSubdomainAppURL(subdomain)
if err != nil {
return false
}
return ok && originApp.Username == app.Username
},
AllowedMethods: []string{
http.MethodHead,
http.MethodGet,
http.MethodPost,
http.MethodPut,
http.MethodPatch,
http.MethodDelete,
},
AllowedHeaders: []string{"*"},
AllowCredentials: true,
})
}

This allows basic intuitive functionality. By handling this for the user's, we can address some of the nuanced CORS behaviors within Coder. For example, the most common answer on solving CORS is Access-Control-Allow-Origin: *. This will not work for an application in Coder. All Coder apps require Coder authentication via a cookie, and credentials are not supported if CORS is set to *.

From Alice Bob
Workspace 1 Workspace 2 Workspace 3
To App A App B App C App D
Alice Workspace 1 App A ✅ ✅* ✅* ❌
App B ✅* ✅ ✅* ❌
Workspace 2 App C ✅* ✅* ✅ ❌
Bob Workspace 3 App D ❌ ❌ ❌ ✅

🚫 What is the status quo?

The status quo is that Coder manages CORS requests between apps. This approach has generally lead to less customer support tickets on how to handle CORS.

We currently do not allow requests between different users. This does not appear to be because of huge security risk. Subdomain apps are given very limited session tokens that only allow workspace app connections. There is a risk that someone could steal this token and go to other apps by the user, but they cannot interact with other Coder resources.

Solution #1 Solve it in Coder (INCOMPLETE SOLUTION)

We can adjust the table above to include cross user requests. This would expand the security surface by default, which although attacks are limited in escalation, providing this by default seems insecure.

It also does not handle requests external to Coder. External requests must be supported by public shared apps to handle the use cases requested.

🚫 Solution #2 Suggest workarounds (Not always possible)

Instead of allowing cross site requests, the user could deploy a local webserver to proxy requests to the second user's app.

Many frontend servers have this as a feature:

  • Vite server.proxy
  • NextJS rewrites

If the web app in question has a backend server, then the backend server can proxy traffic to avoid CORS issues.

❓ Solution #3 Disable CORS handling by Coder

The best option is to disable our custom CORS handling and defer to the workspace app. There is a few ways we could maybe do this:

Disable at the deployment level.

  • Adds another server flag 👎
  • Breaks the QOL behavior for likely 1 use case 👎
  • Easy to switch behavior without any state (db) lookups 👍

Disable at the app level

  • Could configure in terraform per app 👍
  • Have to do a db lookup on requests or maintain a cache 👎

Disable based on response headers.
Given CORS requests are preflight requests, can we do this? Can we pass the preflight to the workspace app, get the headers. If CORS headers are present, return as is. If they are not, add them to the response? Then we just defer to the app if they configure it themselves. No Coder config necessary.

Final thoughts

When workspace sharing becomes a feature, this use case might show up more.

Implementation notes

The implementation cannot be configured at the workspace, template, or user level. To be configured there, an in memory cache would be required.

The requirements to a solution cannot require a database lookup on each request. That would cause too many database calls for web apps with hundreds of static resources.

The only input information on each request you get from making a request from app A to app B.

  • The origin, https://8002--<agent>--<workspace>--<user_A>--apps.coder.com/
  • The request to B, https://8002--<agent>--<workspace>--<user_B>--apps.coder.com/ with cookies for the app B

Related Links:

  • feat: allow cross-origin requests between users' own apps #7688
  • CORS is not permitted between multiple subdomain workspace apps  #5706
  • fix: strip CORS headers from applications #8057
Reacted by Pfau, Sascha

Activity

Next
  1. coder-labeler
    added
    needs decisionNeeds a higher-level decision to be unblocked.Needs a higher-level decision to be unblocked.
    on Oct 15, 2024
  2. dannykopping
    changed the title [-]Cross workspace application requests blocked to CORs across user accounts[/-] [+]Cross workspace application requests blocked to CORS across user accounts[/+] on Oct 15, 2024
  3. MrPeacockNLB commented on Oct 18, 2024

    @MrPeacockNLB
    MrPeacockNLB
    on Oct 18, 2024
    Contributor
    More actions

    Nice to see that this is now further developed. I ask a long time ago Coder support for this!

    In the mean time we are using a simple ingress to bypass coder completely as it was not flexible enough.

  4. MrPeacockNLB commented on Oct 18, 2024

    @MrPeacockNLB
    MrPeacockNLB
    on Oct 18, 2024
    Contributor
    More actions

    Our UseCase was to Develop WebComponents in a Coder Workspace which then gets included into a deployed web portal. This was not possible due CORS issues.

  5. Emyrk commented on Oct 18, 2024

    @Emyrk
    Emyrk
    on Oct 18, 2024
    MemberAuthor
    More actions

    Our UseCase was to Develop WebComponents in a Coder Workspace which then gets included into a deployed web portal. This was not possible due CORS issues.

    That use case would still not work even under the new proposal. The CORs handling is only between workspace apps. It still would block requests that are exiting Coder.

  6. spikecurtis commented on Oct 21, 2024

    @spikecurtis
    spikecurtis
    on Oct 21, 2024
    Contributor
    More actions

    CoderVPN can be helpful here, in that you can directly interact with a web app on your workspace and set whatever CORS policy you'd like on it.

    Reacted by Steven Masley
  7. spikecurtis commented on Oct 21, 2024

    @spikecurtis
    spikecurtis
    on Oct 21, 2024
    Contributor
    More actions

    The implementation cannot be configured at the workspace, template, or user level. To be configured there, an in memory cache would be required.

    The requirements to a solution cannot require a database lookup on each request. That would cause too many database calls for web apps with hundreds of static resources.

    I agree we can't do a DB lookup on each request, but I think a per-workspace policy cache would give acceptable performance. It adds complexity, which needs to be balanced against the utility of configuration at workspace, template, or user scope.

  8. Emyrk commented on Oct 21, 2024

    @Emyrk
    Emyrk
    on Oct 21, 2024
    MemberAuthor
    More actions

    @spikecurtis yes. I was thinking either per workspace or per template.

    I wonder too if request cookie headers come across in CORS pre-flight requests. If so, we have a cookie on all subdomain apps (that are not public), that define the app. It looks like this inside a jwt. I just realized you need to auth to the subdomain first to have the cookie set. So this only works if the user visits the subdomain first. And this breaks even more if an external app from outside a browser context hits the app (assuming it is public)

      "request": {
        "access_method": "subdomain",
        "base_path": "/",
        "app_prefix": "",
        "username_or_id": "emyrk",
        "workspace_name_or_id": "april",
        "agent_name_or_id": "dev",
        "app_slug_or_port": "filebrowser"
        # CORS level here?
      },

    We could maybe define it on the app level, if the app is not public (or we set this cookie regardless of auth?). We sign it with a local key, so forging a cookie is not possible.

  9. Emyrk commented on Oct 21, 2024

    @Emyrk
    Emyrk
    on Oct 21, 2024
    MemberAuthor
    More actions

    @MrPeacockNLB to confirm, is that web portal external to Coder? And is that web portal static?

    What sort of CORS policy is required for your functionality? Access-Control-Allow-Origin: *? Or is it specific domain(s) known in advance? Access-Control-Allow-Origin: https://some.domain.com?

  10. Emyrk commented on Oct 21, 2024

    @Emyrk
    Emyrk
    on Oct 21, 2024
    MemberAuthor
    More actions

    Including some more information from another user. They have a use case to use a Coder app as the API service for some external service.

    When dealing with external services, we can either support some static list, or we will have to defer to the application. I do not see a way to implement a dynamic filter in any sensible way.

    Maybe if we configure this specific to an app, a workspace, or a template, we have an option to not strip the original CORS headers. Just pass through whatever the app does by default.

  11. Emyrk
    self-assigned this
    on Oct 21, 2024
  12. Emyrk commented on Oct 21, 2024

    @Emyrk
    Emyrk
    on Oct 21, 2024
    MemberAuthor
    More actions

    CoderVPN can be helpful here, in that you can directly interact with a web app on your workspace and set whatever CORS policy you'd like on it.

    This might not work for all use cases. One reported use case is to make requests to the Coder app from some external site/server. So the user is not in the equation at all. A VPN would only expose apps to other localhost servers. This is only made possible because share level public is a thing. So no auth is required.

  13. Emyrk
    changed the title [-]Cross workspace application requests blocked to CORS across user accounts[/-] [+]Cross workspace application requests blocked to CORS across user accounts and external requests[/+] on Oct 21, 2024
  14. Emyrk
    added
    customer-requestedDO NOT USE. Instead, add to the project and fill in "Customer".DO NOT USE. Instead, add to the project and fill in "Customer".
    on Oct 21, 2024
  15. spikecurtis commented on Oct 22, 2024

    @spikecurtis
    spikecurtis
    on Oct 22, 2024
    Contributor
    More actions

    One reported use case is to make requests to the Coder app from some external site/server. So the user is not in the equation at all. A VPN would only expose apps to other localhost servers. This is only made possible because share level public is a thing. So no auth is required.

    CoderVPN allows access to the Coder workspace app at the DNS name <workspace>.coder, so it would be accessible from an external site/server if the right CORS policy is configured on the app. Coder is not involved at the HTTP layer at all in this case, so the developer who is working on the app can set their own CORS policy. It's only accessible to the user that owns the workspace tho.

    If the goal is to make a public application via Coder, then you'd need to use the Coder proxy or set up some other way to do ingress into the workspace.

  16. 34 remaining items

  17. bpmct commented on Dec 12, 2024

    @bpmct
    bpmct
    on Dec 12, 2024
    Member
    More actions

    Removing this from the sprint, we'll be able to pick it up in the new year at some point.

    Reacted by Pfau, Sascha and Christoph Landsdorf
  18. mtojek commented on Jan 20, 2025

    @mtojek
    mtojek
    on Jan 20, 2025
    Member
    More actions

    Closing this issue.

    This issue has remained open and assigned for an extended period, but higher-priority tasks have taken precedence. As discussed in our recent Weekly call, we lack the bandwidth to conduct the rigorous testing required for the changes proposed in here.

    Looking ahead, if priorities shift or the need arises to revisit this logic, it would be ideal to address it comprehensively -implementing E2E tests and releasing the improvements. For now, I’m closing this issue as unresolved.

  19. mtojek
    closed this as not plannedon Jan 20, 2025
  20. bpmct
    reopened this on Apr 8, 2025
  21. bpmct
    added
    must-doIssues that must be completed by the end of the Sprint. Or else. Only humans may set this.Issues that must be completed by the end of the Sprint. Or else. Only humans may set this.
    on Apr 8, 2025
  22. Kira-Pilot
    unassigned
    dannykopping
    on May 7, 2025
  23. f0ssel
    assigned
    cstyan
    on May 8, 2025
  24. Jphalan
    added
    Skydio
    on May 8, 2025
  25. matifali
    removed
    Skydio
    on May 16, 2025
  26. deansheather commented on May 22, 2025

    @deansheather
    deansheather
    on May 22, 2025
    Collaborator
    More actions

    This likely needs a mini RFC. One thing I would like to see is the ability to disable the CORS app sharing by user completely at the deployment level due to some recent customer requests.

  27. deansheather commented on May 22, 2025

    @deansheather
    deansheather
    on May 22, 2025
    Collaborator
    More actions

    and likely needs some product direction on how they want this feature to work before writing an RFC too*

  28. matifali
    removed
    customer-requestedDO NOT USE. Instead, add to the project and fill in "Customer".DO NOT USE. Instead, add to the project and fill in "Customer".
    on May 22, 2025
  29. cstyan
    added a commit that references this issue on Jul 30, 2025

    feat: allow bypassing current CORS magic based on template config (#1…

    ffbfaf2
  30. cstyan commented on Aug 14, 2025

    @cstyan
    cstyan
    on Aug 14, 2025
    Contributor
    More actions

    PR was merged, closing this. We can reopen or open a new issue for future related work if necessary.

  31. cstyan
    closed this as completedon Aug 14, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

  • @cstyan
    cstyan

Labels

must-doIssues that must be completed by the end of the Sprint. Or else. Only humans may set this.Issues that must be completed by the end of the Sprint. Or else. Only humans may set this.needs decisionNeeds a higher-level decision to be unblocked.Needs a higher-level decision to be unblocked.

Type

Feature

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    You are visitor 01402503 — GeezerVison does not store the pages you look up.
    Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.