-
Notifications
You must be signed in to change notification settings - Fork 928
feat: Add serving applications on subdomains and port-based proxying #3753
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
Conversation
I'll defer to @sreya and @kylecarbs on the token scoping. From a product POV, supporting the following as a first iteration seems solid: coder.mydomain.com # host
*.coder.mydomain.com # subdomains for apps We can open another issue "Run Coder apps on a separate domain" as some users may eventually want coder.mydomain.com
*.different-coder-apps-domain.com (We have a few Coder Classic customers who do this today for security reasons) |
Yes, it's more secure to run from another domain. I think we will want to implement some sort of toggle on the BE to block the same domain in the future. Awesome on first iteration though 👍. For now, I think I'll just throw a cookie on both |
For named applications, will this PR make the relative_path property for coder_app function as expected? (e.g. links from to apps from the web UI will go to the subdomain) |
It will not. The deployment still needs to setup their certs with wildcards to support this feature. So The subdomain url will work, but the FE still has the path based link |
@Emyrk since our |
We also strip the session token header on the proxy as well, correct? |
We strip the cookie in the proxy app: Lines 186 to 188 in 0578588
There is something you can do though with a malicious app. You can send authenticated requests to coderd. This is an issue I put in the description. We should not allow sharing apps until we close that |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@deansheather right now, I'm doing this in the FE:
Is that right? |
Yes @BrunoQuaresma, but the subpath should be |
coderd/workspaceapps.go
Outdated
// ParseSubdomainAppURL parses an application from the subdomain of r's Host | ||
// header. If the subdomain is not a valid application URL hostname, returns a | ||
// non-nil error. | ||
// | ||
// Subdomains should be in the form: | ||
// | ||
// {PORT/APP_NAME}--{AGENT_NAME}--{WORKSPACE_NAME}--{USERNAME} | ||
// (eg. http://8080--main--dev--dean.hi.c8s.io) | ||
func ParseSubdomainAppURL(hostname string) (ApplicationURL, error) { | ||
subdomain, rest, err := SplitSubdomain(hostname) | ||
if err != nil { | ||
return ApplicationURL{}, xerrors.Errorf("split host domain %q: %w", hostname, err) | ||
} | ||
|
||
matches := appURL.FindAllStringSubmatch(subdomain, -1) | ||
if len(matches) == 0 { | ||
return ApplicationURL{}, xerrors.Errorf("invalid application url format: %q", subdomain) | ||
} | ||
matchGroup := matches[0] | ||
|
||
appName, port := AppNameOrPort(matchGroup[appURL.SubexpIndex("AppName")]) | ||
return ApplicationURL{ | ||
AppName: appName, | ||
Port: port, | ||
AgentName: matchGroup[appURL.SubexpIndex("AgentName")], | ||
WorkspaceName: matchGroup[appURL.SubexpIndex("WorkspaceName")], | ||
Username: matchGroup[appURL.SubexpIndex("Username")], | ||
BaseHostname: rest, | ||
}, nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think httpapi
might be a better spot for this too, but it's a bit weird, so chef's choice on this one. Regardless, I think it'd be better to remove this from being exposed and use an _internal.go
test for it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how do you do the internal test thing? I've never seen that before
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved to httpapi
// We only support setting this cookie on the access URL subdomains. This is | ||
// to ensure we don't accidentally leak the auth cookie to subdomains on | ||
// another hostname. | ||
appCookie.Domain = "." + api.AccessURL.Hostname() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is interesting... I didn't know we could apply to subdomains without requiring an authentication redirect, this is awesome.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We probably won't be doing this soon as we'll be implementing proper devurl auth similarly to v1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kylecarbs this is a stop gap. It will not work on apps hosted on another domain.
Also currently using the same full auth token. We should used scoped auth tokens.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@deansheather are we going to use [relative_path
}(https://registry.terraform.io/providers/coder/coder/latest/docs/resources/app#relative_path) to determine the client-side routing?
It seems like we'll need to add a server-side option for the wildcard path so we can redirect users on the frontend properly, or we could assume a wildcard above the root, but we'd just have to document that.
@kylecarbs this PR only does wildcards underneath the site In a future PR when we implement proper devurl auth (with their own tokens, similar to v1) we will be able to support separate domains, and it'll be configurable through a separate This PR is the MVP for hostname based routing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems reasonable overall
coderd/users.go
Outdated
devurlCookie := api.applicationCookie(cookie) | ||
if devurlCookie != nil { | ||
http.SetCookie(rw, devurlCookie) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
api.setAuthCookie
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The frontend uses an environment variable which decides whether links in the dashboard should go to a wildcard or path (if the admin didn't set up wildcard DNS, for example).
Line 17 in 38a0f7b
CODER_ENABLE_WILDCARD_APPS: "", |
What do you think about having this also impact the functionality (e.g. whether the app is routed) versus it being purely a cosmetic/dashboard variable?
If it's the latter, perhaps we should rename the var in #3812. I suggested the name, but originally assumed it'd be used across both.
/
on subdomains
@bpmct the env var can be temporary until we do the changes I said in my above comment. I'll add a check for the env var |
Actually @bpmct that's a build time env var, not a proper one. I'm just going to merge this as is and we can figure out what we want to do about it when we solve the devurl auth problem |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My comment mentioning relative_path
indicates that in its current form, there's no way to access a wildcard application from the frontend. We should respect that value, and direct a user to the subdomain if it's specified (maybe it's called wildcard
instead, so we don't break current behavior).
We also need to specify whether the wildcard is enabled or not, and have that information surfaced to the frontend so the "Open Port" button open in a subsequent PR functions.
coderd/workspaceapps_test.go
Outdated
}).String() | ||
} | ||
|
||
t.Run("NoAuthShould401", func(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we redirect to /login
with a redirect
query parameter back to the subdomain URL instead? Seems like this would be a questionable UX.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've made it redirect to login but it's impossible to redirect back to the subdomain because we can't determine whether the accessed scheme was HTTP or HTTPS. If users are using a reverse proxy in front of coder (which they likely will) then coder will think it's receiving HTTP traffic when the actual scheme is HTTPS.
@kylecarbs Bruno is working on this in a separate frontend PR. He will be reading that value to figure out whether to link to wildcard or path-based route. |
@kylecarbs here is the related PR #3812 |
What this does
This PR adds the ability to serve applications at
/
on subdomains. It also allows accessing ports inside a workspace with an anonymous application.Examples:
Conversation Wanted
I hit a wall working on this, and want some discussion before I proceed.
Token Scoping + Security
We need to scope the
session_token
used for application authentication. It should only have access to application endpoints, and nothing else. This is so a malicious application cannot make authenticated requests on a user's behalf.We can do this with RBAC token scopes as defined in the RFC, or by making a new token type like we did for agent authentication. The new token can live in
coder_application_token
.If the application is using the path based routing, then the
session_token
of the primary auth can still be used. We need to either have a security setting to always host applications on another subdomain, or somehow reject api calls from these paths (??).Applications on another domain
Eventually we want to serve applications on another domain for security reasons. To do this, we need to implement a way to insert a cookie on that subdomain, like we did with devurls in V1. I suggest we do this as a follow up PR.
Applications on subpath
We should be checking if authenticated requests come from an app via the
Origin
header. If it is, we should not allow that Origin to make authenticated requests.At present
At present we do not allow sharing these application URLs to other users. So all of the security considerations can be follow ups.
Future Work