Container Escape in Chi with Jwt Tokens
Container Escape in Chi with Jwt Tokens — how this specific combination creates or exposes the vulnerability
A container escape in a service built with the Chi router typically involves an attacker who has compromised an application process inside a container leveraging a server-side request forgery (SSRF) or path traversal to reach the container runtime socket. When the application also uses JWT tokens for authorization, the interaction can amplify impact if token validation is weak or if the runtime exposes administrative endpoints that accept tokens from the container host. For example, a handler that forwards requests to a metadata service or a local Kubernetes API might inadvertently allow an authenticated request with a valid JWT token to trigger a call to the Docker socket (/var/run/docker.sock), enabling an attacker to create privileged containers or mount host paths. This becomes a container escape when the attacker uses the valid JWT token to access an endpoint that the developer assumed was internal and therefore safe, but the runtime exposes it to the container’s network namespace. Chi’s composable middleware makes it straightforward to define routes that chain handlers; if one of those handlers performs unchecked HTTP calls or file operations based on claims in a JWT token, an attacker can chain an input validation flaw with a misconfigured network to achieve escape. Common patterns include insecure use of http.Client to reach http://localhost:15000 or the Docker API, where the service trusts JWT-based client certificates or bearer tokens without revalidating scope or audience. In secure designs, JWT tokens should never implicitly grant host-level operations; the container boundary must be enforced by runtime policies, not by token validity alone. middleBrick’s scans detect endpoints that accept JWT tokens and then interact with host services, highlighting risks where authentication bypasses network isolation. Attack patterns such as path traversal (e.g., ../../../var/run/docker.sock) combined with a trusted JWT can lead to container escapes, exposing sensitive host resources. The interplay of Chi routing, JWT handling, and container networking creates a scenario where an authenticated request can traverse trust boundaries that should have been enforced by the container runtime.
Jwt Tokens-Specific Remediation in Chi — concrete code fixes
To mitigate container escape risks when using JWT tokens in Chi, enforce strict validation, avoid forwarding tokens to internal services, and isolate runtime interactions behind network policies. Below are concrete code examples that demonstrate secure handling of JWT tokens in a Chi application.
1. Validate JWT tokens with strict claims and audience scope
Always verify the issuer, audience, and expiration before using claims. Do not trust tokens for host-level operations.
package main
import (
"context"
"net/http"
"github.com/dgrijalva/jwt-go"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
)
type Claims struct {
Scope string `json:"scope"`
jwt.StandardClaims
}
func validateToken(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
auth := r.Header.Get("Authorization")
if auth == "" {
http.Error(w, "missing authorization", http.StatusUnauthorized)
return
}
tokenStr := auth[len("Bearer "):]
token, err := jwt.ParseWithClaims(tokenStr, &Claims{}, func(token *jwt.Token) (interface{}, error) {
// Use a secure key source in production
return []byte("secret-key"), nil
})
if err != nil || !token.Valid {
http.Error(w, "invalid token", http.StatusUnauthorized)
return
}
claims, ok := token.Claims.(*Claims)
if !ok || claims.Audience != "my-api" || claims.Scope != "container:read" {
http.Error(w, "insufficient scope", http.StatusForbidden)
return
}
ctx := context.WithValue(r.Context(), "claims", claims)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func handler(w http.ResponseWriter, r *http.Request) {
claims := r.Context().Value("claims").(*Claims)
http.Fprintf(w, "Hello %s, scope: %s", claims.Subject, claims.Scope)
}
func main() {
r := chi.NewRouter()
r.Use(middleware.RequestID)
r.Use(middleware.RealIP)
r.Use(validateToken)
r.Get("/hello", handler)
http.ListenAndServe(":8080", r)
}
2. Avoid forwarding requests to host services when authenticated with JWT
Do not construct URLs from user input or JWT claims that point to internal endpoints such as the Docker API. If you must call internal services, use a predefined allowlist and enforce network segmentation.
package main
import (
"context"
"fmt"
"net/http"
"net/url"
"github.com/go-chi/chi/v5"
)
func safeProxy() http.HandlerFunc {
allowedHost := "https://api.example.com"
return func(w http.ResponseWriter, r *http.Request) {
base, err := url.Parse(allowedHost)
if err != nil {
http.Error(w, "internal error", http.StatusInternalServerError)
return
}
reqURL := base.JoinPath(r.URL.Path)
req, err := http.NewRequestWithContext(r.Context(), r.Method, reqURL.String(), r.Body)
if err != nil {
http.Error(w, "bad request", http.StatusBadRequest)
return
}
// Copy only safe headers; do not forward Authorization if not intended
req.Header.Set("X-Forwarded-For", r.Header.Get("X-Forwarded-For"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
http.Error(w, "upstream error", http.StatusBadGateway)
return
}
defer resp.Body.Close()
for k, vv := range resp.Header {
for _, v := range vv {
w.Header().Add(k, v)
}
}
w.WriteHeader(resp.StatusCode)
// copy body omitted for brevity
}
}
func main() {
r := chi.NewRouter()
r.Use(middleware.RequestID)
r.Use(middleware.RealIP)
r.Get("/proxy/*", safeProxy())
http.ListenAndServe(":8080", r)
}
3. Enforce network policies to protect runtime sockets
Even with proper JWT validation, ensure that containers do not have access to the Docker socket unless explicitly required. Use Kubernetes Pod Security Standards or Docker runtime options to restrict socket access. Do not mount /var/run/docker.sock into application containers unless the container is dedicated to orchestration tasks and the JWT scope is tightly limited to that role.
4. Use middleware to reject tokens with excessive claims
Implement a Chi middleware that inspects JWT claims and rejects tokens that grant broad or host-related permissions. This prevents attackers from using a valid token to escalate to container operations.
package main
import (
"net/http"
"github.com/go-chi/chi/v5"
)
func jwtScopeMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
claims, ok := r.Context().Value("claims").(*Claims)
if !ok {
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
// Example: reject tokens that imply host-level operations
if claims.Scope == "" || claims.Scope == "*" {
http.Error(w, "token scope too broad", http.StatusForbidden)
return
}
next.ServeHTTP(w, r)
})
}
func main() {
r := chi.NewRouter()
r.Use(middleware.RequestID)
r.Use(middleware.RealIP)
r.Use(jwtScopeMiddleware)
r.Use(validateToken)
r.Get("/safe", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("ok"))
})
http.ListenAndServe(":8080", r)
}