Skip to content

Cedarling Nativity Plan

Richard Marin edited this page Nov 8, 2024 · 159 revisions

JS App

  • Needs to call the WASM component using a few javascript lines of code (ideally 1-2)
  • Input from JS component is object
input = { 
           "access_token": "...", 
           "id_token": "...", 
           "userinfo_token": "...", 
           "tx_token": "...",
           "resource": {"id": "12345", "type": "Ticket", "creator": "[email protected]", "organization": "gluu"},
           "action": "View",
           "context": {
                       "ip_address": "54.9.21.201",
                       "network_type": "VPN",
                       "user_agent": "Chrome 125.0.6422.77 (Official Build) (arm64)",
                       "time": "1719266610.98636",
                      }
         }

decision_result = authz(input)

Bootstrap properties

  • CEDARLING_APPLICATION_NAME : Human friendly identifier for this application

  • CEDARLING_POLICY_STORE_URI : Location of policy store JSON, used if policy store is not local, or retreived from Lock Master.

  • CEDARLING_JWT_VALIDATION : Enabled | Disabled

  • CEDARLING_JWT_SIGNATURE_ALGORITHMS_SUPPORTED : ....

  • CEDARLING_REQUIRE_AUD_VALIDATION : Enabled | Disabled. Controls if Cedarling will discard id_token without an access token with the corresponding client_id.

  • CEDARLING_ROLE_MAPPING : Default: {"id_token": "role", "userinfo_token": "role"} but the role may be sent as an access token, or with a different identifier. For example, for Ping Identity, you might see {"userinfo_token": "memberOf"}.

  • CEDARLING_LOG_TYPE : off, memory, std_out, lock

  • CEDARLING_LOG_TTL : in case of memory store, TTL (time to live) of log entities in seconds.

  • CEDARLING_LOCAL_JWKS : JWKS file with public keys

The following bootstrap properties are only needed for enterprise deployments.

  • CEDARLING_LOCK : Enabled | Disabled. If Enabled, the Cedarling will connect to the Lock Master for policies, and subscribe for SSE events.

  • CEDARLING_LOCK_MASTER_CONFIGURATION_URI : Required if LOCK == Enabled. URI where Cedarling can get JSON file with all required metadata about Lock Master, i.e. .well-known/lock-master-configuration.

  • CEDARLING_LOCK_SSA_JWT : SSA for DCR in a Lock Master deployment. The Cedarling will validate this SSA JWT prior to DCR.

  • CEDARLING_POLICY_STORE_ID : The identifier of the policy stored needed only for Lock Master deployments.

  • CEDARLING_AUDIT_LOG_INTERVAL : How often to send log messages to Lock Master (0 to turn off trasmission)

  • CEDARLING_AUDIT_HEALTH_INTERVAL : How often to send health messages to Lock Master (0 to turn off transmission)

  • CEDARLING_AUDIT_TELEMETRY_INTERVAL : How often to send telemetry messages to Lock Master (0 to turn off transmission)

  • CEDARLING_DYNAMIC_CONFIGURATION : Enabled | Disabled, controls whether Cedarling should listen for SSE config updates

  • CEDARLING_GET_TOKEN_STATUS_LIST_UPDATES : Enabled | Disabled, controls whether Cedarling should listen for SSE OAuth Status List updates

Cedarling Policy Store

The Cedarling Policy Store is a JSON file that contains all the data the Cedarling needs to verify JWT tokens and evaluate policies:

  1. Cedar Schema - JSON format Schema file
  2. Cedar Policies - JSON format Policy Set file (beware CLI bug cedar-950)
  3. Trusted Issuers - JSON file with below syntax

cedarling_store.json schema

{
    "cedar_version": "v4.0.0",
    "name": "",
    "description": "",
    "policies": {...}
    "identity_source": {...},
    "schema": ""
}

Trusted Issuer Metadata Schema

This record contains the information needed to validate tokens from this issuer:

{
 "name": "Google", 
 "description": "Consumer IDP", 
 "openid_configuration_endpoint": "https://accounts.google.com/.well-known/openid-configuration",
 "access_tokens": {
    "trusted": true,
    "principal_identifier": "jti",
    "role_mapping": ""
  },
 "id_tokens": {...},
 "userinfo_tokens": {...},
 "tx_tokens": {...}
}

Token Entity Metadata Schema

{
  "user_id": "..."},                    <-- OPTIONAL e.g. email, sub, uid
  "role_mapping": "..."},               <-- OPTIONAL e.g. role, memberOf
  "claim_mapping": {                    <-- OPTIONAL
                      "email": {
                         "parser": "regex", 
                         "type": "Acme::Email",  
                         "regex_expression" : "^(?P<UID>[^@]+)@(?P<DOMAIN>.+)$", 
                         "UID": {"attr": "uid", "type":"string"}, 
                         "DOMAIN": {"attr": "domain", "type":"string"} 
                      }, 
                      "profile": {
                         "parser": "regex", 
                         "type": "Acme::URI", 
                         "regex_expression": "(?x) ^(?P<SCHEME>[a-zA-Z][a-zA-Z0-9+.-]*):\/\/ (?P<HOST>[^\/:#?]+) (?::(?P<PORT>\d+))?  (?P<PATH>\/[^?#]*)? (?:\?(?P<QUERY>[^#]*))?  (?:#(?P<FRAGMENT>.*))?$",
                         "SCHEME": {"attr": "scheme", "type":"string"}, 
                         "HOST": {"attr": "host", "type":"string"}, 
                         "PORT": {"attr": "port", "type":"string"}, 
                         "PATH": {"attr": "path", "type":"string"}, 
                         "QUERY": {"attr": "query", "type":"string"}, 
                         "FRAGMENT": {"attr": "fragment", "type":"string"}
                      },
                      "dolphin": {
                          "parser": "json", 
                          "type": "Acme::Dolphin"}
                      }
                   }
}

Note use of regex named capture groups which is more readable by referring to parts of a regex match by descriptive names rather than numbers. For example, (?P<name>...) defines a named capture group where name is the identifier, and ... is the regex pattern for what you want to capture.

Cedar Schema

namespace Jans {
    // ******  TYPES  ******
    type Url = {
        protocol: String,
        host: String,
        path: String,
    };
    type email_address = {
        id: String, 
        domain: String,
    };
    type Context = {
            network: ipaddr,
            network_type: String,
            user_agent: String, 
            operating_system: String,
            device_health: Set<String>,
            current_time: Long,
            geolocation: Set<String>,
            fraud_indicators: Set<String>,
    };

    // ******  Entities  ******
    entity Workload  = {
        client_id: String,
        iss: TrustedIssuer,
        name: String,
        spiffe_id: String,
        rp_id: String
    };
    entity Access_token  = {
        aud: String,
        exp: Long,
        iat: Long,
        iss: TrustedIssuer,
        jti: String,
        nbf: Long,
        scope: String,
    };
    entity TrustedIssuer = {
        issuer_entity_id: Url,
    };
    entity Role;
    entity User in [Role] {
        sub: String,
        username: String,
        email: email_address,
        phone_number: String,
        role: Set<String>,
    };
    entity id_token  = {
        acr: Set<String>,
        amr: String,
        aud: String,
        azp: String,
        birthdate: String,
        email: email_address,
        exp: Long,
        iat: Long,
        iss: TrustedIssuer,
        jti: String,        
        name: String,
        phone_number: String,
        role: Set<String>,
        sub: String,
    };
    entity Userinfo_token  = {
        aud: String,
        birthdate: String,
        email: email_address,
        exp: Long,
        iat: Long,
        iss: TrustedIssuer,
        jti: String,
        name: String,
        phone_number: String,
        role: Set<String>,
        sub: String,
    };
    entity HTTP_Request = {
        "url": Url,
        "header": Set<String>,
        "accept": Set<String>,
    };

    // ******  Actions  ******
    action Compare appliesTo {
        principal: [User, Role],
        resource: Workload,
        context: Context,
    };
    action Execute appliesTo {
        principal: [User, Role],
        resource: Workload,
        context: Context,
    };
    action Monitor appliesTo {
        principal: [User, Role],
        resource: Workload,
        context: Context,
    };
    action Read appliesTo {
        principal: [User, Role],
        resource: Workload,
        context: Context,
    };
    action Search appliesTo {
        principal: [User, Role],
        resource: Workload,
        context: Context,
    };
    action Share appliesTo {
        principal: [User, Role],
        resource: Workload,
        context: Context,
    };
    action Tag appliesTo {
        principal: [User, Role],
        resource: Workload,
        context: Context,
    };
    action Write appliesTo {
        principal: [User, Role],
        resource: Workload,
        context: Context,
    };
    action GET appliesTo {
          principal: Workload,
          resource: HTTP_Request,
          context: Context,
    };
    action PUT appliesTo {
        principal: Workload,
        resource: HTTP_Request,
        context: Context,
    };
    action DELETE appliesTo {
        principal: Workload,
        resource: HTTP_Request,
        context: Context,
    };
    action HEAD appliesTo {
        principal: Workload,
        resource: HTTP_Request,
        context: Context,
    };
    action PATCH appliesTo {
        principal: Workload,
        resource: HTTP_Request,
        context: Context,
    };
}

JSON Format

Entity Mapping

  • TrustedIssuer: Created on startup from Policy Store
  • Workload: Created from access token client_id
  • Application: Created if input supplies an Application name
  • Role: Created for each role claim value in the joined id_token and userinfo token
  • User: Created based on the joined id_token and userinfo token. sub is the entity identifier
  • Access_token: 1:1 mapping from claims in token
  • id_token: 1:1 mapping from claims in token
  • Userinfo_token: 1:1 mapping from claims in token

Cederling Agama Lab / AdminUI Mockups

Here

Lock Master Swagger

Swagger UI

Cedarling token validation

  • Validate signature from Trusted Issuer (if hash not found in cache)
  • If bootstrap property CEDARLING_REQUIRE_AUD_VALIDATION is true... discard id_token if aud does not match access_token client_id
  • Discard any Userinfo token not associated with a sub from the id_token
  • If Cedarling is Locked, check token status
  • Check access token and id_token exp and nbf claims if time sent in Context

Lock Design Goals

Original design goals of Jans Lock and the Cedarling:

  • Move the PDP to the edge of the network--the browser itself.
  • Make the PDP performant and deterministic (i.e. milliseond statup time and always return a PERMIT/DENY response).
  • Empower application developers to author policies appropriate for the resources and actions they need to protect.
  • Centralilze audit and health data collection
  • Send updates to the Cedarlings from the Lock Master to enable realtime attack mitigation

Cedarling logger

Cedarling application has internal logger.

Configuration

Using configuration parameter CEDARLING_LOG_TYPE you can set up:

  • off - disabled
  • memory - store log entry in memory
  • std_out - write log entry data to std output stream
  • lock - the logger sends log data to the server (corporate feature).

Using the memory logger, we can set time to live entry in memory using CEDARLING_LOG_TTL.

Entry types

Each log entry marked with log type. In field log_kind. Possible variants:

  • Decision
  • System
  • Metric

Log Storage interface

pub trait LogStorage {
    /// return logs and remove them from the storage
    fn pop_logs(&self) -> Vec<LogEntry>;

    /// get specific log entry
    fn get_log_by_id(&self, id: &str) -> Option<LogEntry>;

    /// returns a list of all log ids
    fn get_log_ids(&self) -> Vec<String>;
}

Lock'd Cedarling Startup Sequence

Cedarling_Startup_Sequence

Source

Authz Sequence Diagram

Note: no JWT status check

cedarling-authz-sequence-diagram

Source

Topology Diagram

cedarling-lock-topology-concept

Clone this wiki locally