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 #15096DescriptionProblem
- 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
|
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:
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:
Activitychanged 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, 2024MrPeacockNLB commented on Oct 18, 2024 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. MrPeacockNLB commented on Oct 18, 2024 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. Emyrk commented on Oct 18, 2024
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. spikecurtis commented on Oct 21, 2024 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. spikecurtis commented on Oct 21, 2024
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. Emyrk commented on Oct 21, 2024 @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. Emyrk commented on Oct 21, 2024 @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? Emyrk commented on Oct 21, 2024 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. Emyrk commented on Oct 21, 2024
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. 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, 2024spikecurtis commented on Oct 22, 2024
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. bpmct commented on Dec 12, 2024 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 mtojek commented on Jan 20, 2025 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. deansheather commented on May 22, 2025 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. deansheather commented on May 22, 2025 and likely needs some product direction on how they want this feature to work before writing an RFC too* cstyan commented on Aug 14, 2025 PR was merged, closing this. We can reopen or open a new issue for future related work if necessary.
MetadataMetadataDevelopmentNo branches or pull requestsIssue actions
|