mirror of
https://github.com/coder/coder.git
synced 2026-10-16 06:37:58 +08:00
Breaking API Change:
> The presence of the `ip` field on `codersdk.ConnectionLog` cannot be
guaranteed, and so the field has been made optional. It may be omitted
on API responses.
When running a scaletest, I noticed logs of the form:
```
2025-09-12 06:34:10.924 [erro] coderd.workspaceapps: upsert connection log failed trace=0xa17580 span=0xa17620 workspace_id=81b937d7-5777-4df5-b5cb-80241f30326f agent_id=78b2ff6d-b4a6-4a4e-88a7-283e05455a88 app_id=00000000-0000-0000-0000-000000000000 user_id=00000000-0000-0000-0000-000000000000 user_agent="" app_slug_or_port=terminal status_code=404 request_id=67f03cf8-9523-444a-97bc-90de080a54c8 ...
error= 1 error occurred:
* pq: null value in column "ip" of relation "connection_logs" violates not-null constraint
```
to ensure logs are never omitted from the connection log due to a
missing IP again (i.e. I'm not sure if we can always rely on a valid,
parseable, IP from `(http.Request).RemoteAddr`), I've removed the `NOT
NULL` constraint on `ip` on `connection_logs`, and made `ip` on the API
response optional.
The specific cause for these null IPs was the
`/workspaceproxies/me/issue-signed-app-token [post]` endpoint
constructing it's own `http.Request` without a `RemoteAddr` set, and
then passing that to the token issuer.
To solve this, we'll have workspace proxies send the real IP of the
client when calling `/workspaceproxies/me/issue-signed-app-token [post]`
via the header `Coder-Workspace-Proxy-Real-IP`.
63 lines
1.9 KiB
Go
63 lines
1.9 KiB
Go
package wsproxy
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"net/url"
|
|
|
|
"cdr.dev/slog"
|
|
|
|
"github.com/coder/coder/v2/coderd/cryptokeys"
|
|
"github.com/coder/coder/v2/coderd/jwtutils"
|
|
"github.com/coder/coder/v2/coderd/workspaceapps"
|
|
"github.com/coder/coder/v2/enterprise/wsproxy/wsproxysdk"
|
|
)
|
|
|
|
var _ workspaceapps.SignedTokenProvider = (*TokenProvider)(nil)
|
|
|
|
type TokenProvider struct {
|
|
DashboardURL *url.URL
|
|
AccessURL *url.URL
|
|
AppHostname string
|
|
|
|
Client *wsproxysdk.Client
|
|
TokenSigningKeycache cryptokeys.SigningKeycache
|
|
APIKeyEncryptionKeycache cryptokeys.EncryptionKeycache
|
|
Logger slog.Logger
|
|
}
|
|
|
|
func (p *TokenProvider) FromRequest(r *http.Request) (*workspaceapps.SignedToken, bool) {
|
|
return workspaceapps.FromRequest(r, p.TokenSigningKeycache)
|
|
}
|
|
|
|
func (p *TokenProvider) Issue(ctx context.Context, rw http.ResponseWriter, r *http.Request, issueReq workspaceapps.IssueTokenRequest) (*workspaceapps.SignedToken, string, bool) {
|
|
appReq := issueReq.AppRequest.Normalize()
|
|
err := appReq.Check()
|
|
if err != nil {
|
|
workspaceapps.WriteWorkspaceApp500(p.Logger, p.DashboardURL, rw, r, &appReq, err, "invalid app request")
|
|
return nil, "", false
|
|
}
|
|
issueReq.AppRequest = appReq
|
|
|
|
resp, ok := p.Client.IssueSignedAppTokenHTML(ctx, rw, issueReq, r.RemoteAddr)
|
|
if !ok {
|
|
return nil, "", false
|
|
}
|
|
|
|
// Check that it verifies properly and matches the string.
|
|
var token workspaceapps.SignedToken
|
|
err = jwtutils.Verify(ctx, p.TokenSigningKeycache, resp.SignedTokenStr, &token)
|
|
if err != nil {
|
|
workspaceapps.WriteWorkspaceApp500(p.Logger, p.DashboardURL, rw, r, &appReq, err, "failed to verify newly generated signed token")
|
|
return nil, "", false
|
|
}
|
|
|
|
// Check that it matches the request.
|
|
if !token.MatchesRequest(appReq) {
|
|
workspaceapps.WriteWorkspaceApp500(p.Logger, p.DashboardURL, rw, r, &appReq, err, "newly generated signed token does not match request")
|
|
return nil, "", false
|
|
}
|
|
|
|
return &token, resp.SignedTokenStr, true
|
|
}
|