# Sparkle & Authzd Overview ## Slide 1: Overview ### What We Built Two services that work together to provide authorization at the edge: 1. **Sparkled** - Demo application (Go web service) 2. **Authzd** - Authorization daemon (gRPC service implementing Envoy's `ext_authz` API) ### Key Innovation Authorization decisions happen in Envoy *before* requests reach the application. This is different from Rails where authorization typically happens inside the app. --- ## Slide 2: The Problem We're Solving ### Traditional Rails Approach ``` +-------------+ | Browser | +------+------+ | | HTTP Request v +-------------+ | Rails | <-- Authentication + Authorization happens here +-------------+ ``` ### Challenges - Every app reimplements auth logic - Hard to enforce consistent policies - Difficult to audit authorization decisions - Sensitive tokens handled by application code --- ## Slide 3: The Warsaw Accord Solution ![Warsaw Accord Diagram](./screenshot.png) ### Key Components: 1. **Ingress L7 Firewall** - Handles authentication flows and intercepts all requests 2. **STS TS (PDP)** - Makes authorization decisions based on policies 3. **URT (Unified Request Token)** - Standardized token format for downstream services --- ## Slide 4: Sparling Implementation ``` +--------------+ | User-Agent | +------+-------+ | | HTTP Request v +=========================================+ | DOCKER IMAGE: Sparkle | | | | +-------------------------------------+ | | | ENVOY PROXY (Sparkle) | | | | | | | | 1. OAuth2 Filter (login flow) | | | | 2. JWT Filter (validates tokens) | | | | 3. Ext_Authz Filter (Go authzd) --+ | | | | 4. Router (forwards to app) | | | | +---------------+-------------------+-+ | | | | | | v v | | +-------------+ +----------+ | | | Sparkled | | Authzd | | | | (App) | | (sidecar)| | | +-------------+ +----+-----+ | +==============================|==========+ | | gRPC call | (network) v +==============================+=========+ | DOCKER IMAGE: Authzd | | | v | | +------------------------------------+ | | | ENVOY PROXY (Remote) | | | +------------------+-----------------+ | | | | | v | | +---------------+ | | | Authzd | | | | (remote) | | | +---------------+ | +========================================+ ``` --- ## Slide 5: Request Flow - First Time User ``` User-Agent Envoy(Sparkle) Sparkled GitLab | | | | | GET /dashboard | | | |--------------->| | | | | | | | | No auth cookie | | | 302 Redirect | detected | | |<---------------| | | | | | | | GET gitlab.com/oauth/authorize | | |----------------------------------|------------->| | | | | (User logs in) | | | | | | 302 /callback?code=xyz | | |<---------------------------------|------------->| | | | | | GET /callback | | | |--------------->| | | | | Exchange code | | | |-----------------|------------->| | | | | | | ID token + | | | | Access token | | | |<----------------|--------------| | | | | | Set cookies | | | | 302 /dashboard | | | |<---------------| | | ``` --- ## Slide 6: Request Flow - Authenticated User ``` User-Agent Envoy(Sparkle) Sidecar Authzd Remote Authzd | | | | | GET /dashboard | | | | (with cookies) | | | |--------------->| | | | | | | | | 1. Extract | | | | ID token | | | | | | | | 2. Validate JWT | | | | ✓ Valid | | | | | | | | 3. Check authz | | | |---------------->| | | | | | | | | gRPC call | | | | (network) | | | |------------->| | | | | | | | Evaluate | | | | policies | | | |<-------------| | | | | | | gRPC: OK | | | |<----------------| | | | + URT token in headers | | | | | 200 OK | | | Dashboard HTML | | |<---------------| | ``` --- ## Slide 7: The Dual Authzd Architecture ### Current: Sidecar + Remote ``` +-----------------------------------------------------+ | Docker Container | | | | +---------+ +--------------+ +----------+ | | | Envoy |--->| Authzd | | Sparkled | | | | (local) | | (sidecar) | | (App) | | | +---------+ +------+-------+ +----------+ | | | | +------------------------|----------------------------+ | | gRPC call (fallback) v +---------------+ | ENVOY PROXY | | (remote) | +-------+-------+ | v +---------------+ | Authzd | | (remote) | +---------------+ ``` ### Future: In-Process Library + Remote Fallback ``` +-----------------------------------------------------+ | Docker Container | | | | +---------+ +--------------+ +----------+ | | | Envoy |--->| Authzd | | Sparkled | | | | (local) | | (library) | | (App) | | | +---------+ +------+-------+ +----------+ | | | | +------------------------|----------------------------+ | | HTTP call (fallback) v +---------------+ | Authzd | | (remote) | +---------------+ ``` ### Benefits: 1. **Local authzd** - Fast, no network latency, basic policies 2. **Remote authzd** - Centralized policy management, complex rules 3. **Future**: In-process library reduces gRPC overhead --- ## Slide 8: Envoy Configuration Deep Dive ### **Architecture Evolution Note** The OAuth2 and JWT filters shown below may be removed in future versions: - **Option 1**: Move to Remote authzd's Envoy configuration - **Option 2**: Implement as code inside Remote authzd - **Goal**: Replace JWT with URT (Unified Request Token) via `ext_authz` ### Current Filter Chain (order matters!) ```yaml http_filters: - name: envoy.filters.http.oauth2 # 1. Handle login/logout - name: envoy.filters.http.jwt_authn # 2. Validate tokens - name: envoy.filters.http.ext_authz # 3. Check authorization - name: envoy.filters.http.router # 4. Route to app ``` Each filter processes the request and can: - Allow it to continue to the next filter - Return an immediate response (redirect, error, etc.) - Modify headers before passing along --- ## Slide 9: OAuth2 Filter - Authentication (Current) ### **Future Architecture** This OIDC authentication may move to Remote authzd for centralized token management. ### Configuration ```yaml - name: envoy.filters.http.oauth2 config: authorization_endpoint: "https://gitlab.com/oauth/authorize" token_endpoint: "https://gitlab.com/oauth/token" credentials: client_id: "OAUTH_CLIENT_ID" cookie_names: id_token: id_token bearer_token: bearer_token pass_through_matcher: # Skip auth for these paths - name: ":path" string_match: safe_regex: regex: ^.+\.(css|js|png)$ ``` ### What it does: 1. Intercepts unauthenticated requests 2. Manages OAuth2 flow with GitLab 3. Stores tokens in encrypted cookies 4. Handles token refresh automatically --- ## Slide 10: JWT Filter - Token Validation (Current) ### **Future Architecture** JWT validation may move to authzd, which will: 1. Validate JWT from identity provider 2. **Replace JWT with URT (Unified Request Token)** 3. Inject URT as header via `ext_authz` response ### Configuration ```yaml - name: envoy.filters.http.jwt_authn providers: id_token_provider: issuer: https://gitlab.com audiences: [OAUTH_CLIENT_ID] remote_jwks: uri: https://gitlab.com/oauth/discovery/keys claim_to_headers: # Extract claims to headers - claim_name: sub header_name: x-jwt-claim-sub - claim_name: nickname header_name: x-jwt-claim-username ``` ### Current headers passed to app: ``` x-jwt-claim-sub: 123456 x-jwt-claim-username: john.doe x-jwt-payload: ``` --- ## Slide 11: `ext_Authz` Filter - Authorization ### The gRPC Call ``` +------------+ CheckRequest +------------+ | Envoy | ---------------------> | Authzd | | | | | | | <--------------------- | | +------------+ CheckResponse +------------+ ``` ### CheckRequest includes: ```protobuf message CheckRequest { AttributeContext attributes = 1; } message AttributeContext { Request request = 1; // HTTP method, path, headers // ... context about the request } ``` ### CheckResponse: ```protobuf message CheckResponse { Status status = 1; // OK or Permission Denied // Can add/remove headers } ``` ### **Key Feature: URT Injection** Authzd can inject **URT (Unified Request Token)** headers: ``` x-urt: x-user-id: 123456 ``` --- ## Slide 12: Authzd Implementation ### **Current Cedar Policies (Placeholder)** **Note**: These are hard-coded placeholder policies to test the local <-> remote authzd interaction. Real policies are being developed next. ```cedar // Allow static assets permit(principal, action, resource) when { context has path && context has method && context.method == "GET" && context.path like "*.css" }; // Allow specific Sparkle endpoints permit(principal, action, resource) when { context has host && context.host == "sparkle.runway.gitlab.net" && context.path == "/health" }; ``` **Next**: Replace with real authorization policies based on user roles and permissions. --- ## Slide 13: How Authzd Makes Decisions ``` CheckRequest from Envoy | v +---------------------+ | Extract from request | | - Bearer token | | - Path | | - Method | | - Host | +----------+----------+ | v +---------------------+ | Create Cedar Request | | - Principal: User | | - Action: check | | - Resource: resource | | - Context: {token,..}| +----------+----------+ | v +---------------------+ | Evaluate Policies | | using Cedar engine | +----------+----------+ | +-------+--------+ v v ALLOW (200) DENY (401) ``` --- ## Slide 14: Demo Scenarios ### Scenario 1: Unauthenticated Access ```bash curl http://localhost:10000/sparkles # → 302 Redirect to GitLab login ``` Sparkle Logs ``` {"app":"authzd","authzd":{"state":"IDLE","target":"dns:///0.0.0.0:20000"},"host":"localhost:10000","method":"GET","path":"/sparkles","protocol":"HTTP/1.1","scheme":"http","authorized":true} {"app":"sparkled","host":"","method":"GET","path":"/sparkles"} {"app":"envoy","authority":"localhost:10000","method":"GET","path":"/sparkles","protocol":"HTTP/1.1","response_code":200} ``` Authzd Logs ``` {"fields":{"method":"GET","host":"localhost:10000","path":"/sparkles","decision":"Deny","diagnostics":"Diagnostics { reason: {}, errors: [] }"},"target":"authzd::authorization::cedar_authorizer"} {"method":"POST","path":"/envoy.service.auth.v3.Authorization/Check","protocol":"HTTP/2","response_code":200,"user_agent":"grpc-go/1.73.0"} ``` ### Scenario 2: Static Asset (No Auth Required) ```bash curl http://localhost:10000/style.css # → 200 OK (bypasses auth) ``` ### Scenario 3: Authenticated Access ```bash curl -H "Cookie: id_token=..." http://localhost:10000/dashboard # → 200 OK (if authorized) ``` ### Scenario 4: Invalid Token ```bash curl -H "Cookie: id_token=expired" http://localhost:10000/dashboard # → 401 Unauthorized ``` --- ## Slide 15: Key Takeaways 1. **Authorization at the edge** is more secure and performant 2. **Envoy handles the complex parts** - OAuth flows, token validation 3. **Cedar policies** are easier to audit than code 4. **Separation of concerns** - Apps do business logic, not auth 5. **Gradual migration** is possible - no big bang required --- ## Appendix A: Resources ### Documentation - Envoy `ext_authz:` https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/ext_authz_filter - Cedar Language: https://www.cedarpolicy.com/ - Warsaw Accord Design Doc: https://docs.google.com/document/d/1iDS9qy9iW5KSV5AsoIQlNzFVOkvgYSvCgFvbPSBAaDY/ ### Code Repositories - Sparkled: `/sparkled` - Demo application - Authzd: `/authzd` - Authorization daemon ### Key Files - [`/authzd/etc/authzd/*.cedar`](https://gitlab.com/gitlab-org/software-supply-chain-security/authorization/authzd/-/tree/63c5263087c9e282ced0e549b78c7ebd4353b273/etc/authzd) - Authorization policies - [`/sparkled/etc/envoy/envoy.yaml`](https://gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/-/blob/main/etc/envoy/envoy.yaml) - Sparkle Envoy configuration - [`/sparkled/etc/envoy/envoy.yaml`](https://gitlab.com/gitlab-org/software-supply-chain-security/authorization/authzd/-/blob/63c5263087c9e282ced0e549b78c7ebd4353b273/etc/envoy/envoy.yaml) - Authzd Envoy configuration - [`/sparkled/share/man/ENVOY.md`](https://gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/-/blob/a3b0accde30a92434053bab1d25d8028e24ed866/share/man/ENVOY.md) - Detailed Envoy documentation