-
Notifications
You must be signed in to change notification settings - Fork 881
Workspace applications CORS handling prevents external requests to apps #15096
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
Comments
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. |
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. |
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. |
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. |
@spikecurtis yes. I was thinking either per workspace or per template.
"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. |
@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? |
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. |
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 |
CoderVPN allows access to the Coder workspace app at the DNS name 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. |
Yup this. I am wondering if we can just detect an existing CORS header, and if present, do not strip or append our own. This would require us to pass the preflight request to the app, then modify the response if it omits the headers. |
I'm in favor of solution 3 if we can do it at the template level. One disadvantage of the app level is it does not allow shared ports or any ports to be used. If we did it deployment-wide, it would probably break many use cases. |
@bpmct I wonder if we can Solution 3 |
We would still need to rewrite the header if they set |
If we decide not to, then the app can never have a cross origin request because credentials will never be included. But we could let it be their mistake, although the solution is a bit nuanced because it requires knowing you need the Coder cookie for access. Would be amazing if we could return some error message in the preflight response. But that requires us to basically anticipate a CORS error based on the Solution 3 is the most basic, but it is the most transparent and hard to debug. I don't think we can even add any headers to attach meta data on the behavior, because CORS has to whitelist headers (if not using |
Chat with @dannykopping @johnstcn summary. Add the CORS configurable behavior on this cookie jwt: coder/coderd/workspaceapps/request.go Lines 84 to 104 in dae9216
CORS behavior should be configurable on the app section in terraform. An idea: the CORS behavior could be configurable by some enum in terraform on the app: CORS Enum:
|
To be clear, we're opting for Solution 3 :: Disable at the app level. We're going to allow template authors to optionally enable passthru of all CORS requests to the "upstream" app, which will be solely responsible for responding to those requests; coderd will only be responsible for authentication based on the app token provided in the cookie and proxying the requests/responses on. |
Will this also work with shared (or non-shared) ports that are NOT |
@bpmct I do not think the current solution does. We'd have to also add it as a column on the shared ports table, and make some ui element for the toggle? |
@bpmct how do you see this being defined at a template level? As a toggle in the UI only, or via some terraform as well? Or do we instead allow the user to configure CORS handling at the last moment of responsibility when they actually share a port? -- As an aside, and please forgive my ignorance, did we ever consider adding shared ports as a top-level terraform resource, or is it expected that folks created a |
@bpmct that's the workspace view; did you perhaps share the wrong image? I assume you want this set as a policy for all workspaces using a template. |
I like that! Legit perfect.
Ah my image was just showing the ports dropdown and how it is different from |
@dannykopping that is on the template level, however I thought it was discussed to implement it per-app. Meaning it would have to be on the port sharing drop down somehow. If we make this template level, then all apps share the same CORs behavior. In the use cases described, I think it's preferred to have a singular app be exempt, but other apps keep the nice default behavior. |
Ah. It just feels a bit strange to assert at the template level for all ports imo. Per app makes sense, but to apply at the template level means users are going to have to send us their template CORS setting when submitting issues. It just feels disconnected from what it is actually affecting. |
The only requirement from the product side is that we also support this for both I don't have opinions on whether we need to support both template level AND per- |
Thanks for the additional context, that helps. @Emyrk @bpmct could we side-step this whole problem and insist that customers create a If not, I will add this add the port-share level. My only hesitation here is the UI will need to be updated, and honestly the UX of this component feels a bit awkward right now. Users just have to know to click the lock icon to access the port-share, even though there's no UI indication that it's a button. We'll have to make the whole panel a bit wider to accommodate the CORS behavior, and/or use some icons to indicate the behavior. |
Uh yes, that's not intuitive at all We can definitely give the whole port menu a UI/UX polish when looking into this. Let me know once you have agreed on where the CORS settings should live and I can work on some designs. |
Nope we can't. Template admins and developers are totally different personas. Template admins in most cases will not understand what ports developers will be using since thousands of developers across different teams will be writing different apps. |
@bpmct an argument for just the templates (to play devils advocate) is CORS is a security measure. And template admins should be able to control the security surface for the given template. 🤷♂ Saying that, I think the behavior should live in both. Unfortunate the UX will have to fit even more stuff. |
Got it; I thought that CORS behavior changes would only be for well-known apps but I guess it could be for anything.
Can we start in the template (which is the simpler option), and expand to port-shares later if we get enough interest? |
I suppose, I just can't imagine a scenario where a coder_app from one workspace needs to CORS to a coder_app on another users workspace. I figured this was for user<>user collaboration on their projects. Such as a frontend developer using a backend's developer's port. I doubt the template admin will know/hardcode such ports as |
Just reviewed the 2 open tickets we have for this and they both refer to shared ports. Edit: One does, not sure if the other does. |
Ok from the chat:
|
Moving this into the next sprint instead of this one. |
I've pushed up my changes to change |
Removing this from the sprint, we'll be able to pick it up in the new year at some point. |
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. |
Problem
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
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*
.🚫 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:
server.proxy
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.
Disable at the app level
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.
https://8002--<agent>--<workspace>--<user_A>--apps.coder.com/
https://8002--<agent>--<workspace>--<user_B>--apps.coder.com/
with cookies for the app BRelated Links:
The text was updated successfully, but these errors were encountered: