Skip to content

Commit f490e5a

Browse files
mjudeikis-botMangirdas Judeikis
andauthored
fix(security): close auth bypass and nil-config holes in site proxy (#20, #27) (#39)
Issue #20 — authorization bypass for non-SA tokens: The previous code only called authorize() when parseServiceAccountToken() succeeded. Any other token (opaque, arbitrary JWT, malformed) fell through the if-block without rejection and was silently proxied to the site cluster. Fix: unrecognized token types now get an explicit 403 Forbidden. Issue #27 — site proxy unauthenticated when kcpConfig is nil: The entire auth block was guarded by 'if p.kcpConfig != nil', so a nil kcpConfig (no kcp backend) allowed any non-empty bearer token through. Fix: nil kcpConfig now returns 503 Service Unavailable immediately — the site proxy cannot safely authorize requests without a kcp backend. Also updated the NewVirtualWorkspaces doc comment which incorrectly stated that nil kcpConfig is acceptable in dev mode. Co-authored-by: Mangirdas Judeikis <mjudeikis@gmail.com>
1 parent 2cfdebd commit f490e5a

2 files changed

Lines changed: 28 additions & 10 deletions

File tree

pkg/virtual/builder/proxy.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ type VirtualWorkspaceHandlers struct {
6565
}
6666

6767
// NewVirtualWorkspaces creates a new VirtualWorkspaceHandlers.
68-
// kcpConfig is used for SA token verification against kcp. If nil, token
69-
// verification is skipped (dev mode only).
68+
// kcpConfig is required for SA token authorization against kcp. A nil
69+
// kcpConfig causes the site-proxy handler to reject all requests with 503.
7070
func NewVirtualWorkspaces(cm *connman.ConnectionManager, kcpConfig *rest.Config, siteRoutes *SiteRouteMap, logger klog.Logger) *VirtualWorkspaceHandlers {
7171
return &VirtualWorkspaceHandlers{
7272
vws: &virtualWorkspaces{

pkg/virtual/builder/site_proxy_handler.go

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,20 +63,38 @@ func (p *virtualWorkspaces) buildSiteProxyHandler() http.Handler {
6363

6464
logger.V(4).Info("Site proxy request", "cluster", clusterName, "site", siteName, "apiPath", apiPath)
6565

66-
// Auth: trust kcp admin bearer token (mount proxy), or validate SA token.
66+
// Auth: kcp backend config is required for authorization.
67+
// Refuse to serve any request when kcpConfig is nil — there is no way
68+
// to validate tokens without a kcp backend (fix for #27).
69+
if p.kcpConfig == nil {
70+
logger.Error(nil, "site proxy has no kcp backend config; rejecting request")
71+
http.Error(w, "Service Unavailable: site proxy not configured", http.StatusServiceUnavailable)
72+
return
73+
}
74+
75+
// Extract and validate the bearer token.
6776
token := extractBearerToken(r)
6877
if token == "" {
6978
http.Error(w, "Unauthorized", http.StatusUnauthorized)
7079
return
7180
}
7281

73-
if p.kcpConfig != nil && token != p.kcpConfig.BearerToken {
74-
if claims, ok := parseServiceAccountToken(token); ok {
75-
if err := authorize(r.Context(), p.kcpConfig, token, claims.ClusterName, "proxy", "sites", siteName); err != nil {
76-
logger.Error(err, "site proxy authorization failed", "site", siteName)
77-
http.Error(w, "Forbidden", http.StatusForbidden)
78-
return
79-
}
82+
// Trust the kcp admin bearer token (used by the mount proxy internally).
83+
// For all other tokens, require a valid ServiceAccount token that passes
84+
// kcp RBAC authorization. Unknown/opaque tokens are explicitly rejected
85+
// rather than silently passed through (fix for #20).
86+
if token != p.kcpConfig.BearerToken {
87+
claims, ok := parseServiceAccountToken(token)
88+
if !ok {
89+
// Token is not a recognized ServiceAccount JWT — reject.
90+
logger.Info("site proxy rejected unrecognized token type", "site", siteName)
91+
http.Error(w, "Forbidden", http.StatusForbidden)
92+
return
93+
}
94+
if err := authorize(r.Context(), p.kcpConfig, token, claims.ClusterName, "proxy", "sites", siteName); err != nil {
95+
logger.Error(err, "site proxy authorization failed", "site", siteName)
96+
http.Error(w, "Forbidden", http.StatusForbidden)
97+
return
8098
}
8199
}
82100

0 commit comments

Comments
 (0)