Skip to content

Cedarling Nativity Plan

Michael Schwartz edited this page Nov 20, 2024 · 207 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": "mike@gluu.org", "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_POLICY_STORE_ID : The identifier of the policy store in case there is more then one policy_store_id in the policy store.

  • 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_USER_AUTHZ : When enabled, Cedar engine authorization is queried for a User principal.

  • CEDARLING_WORKLOAD_AUTHZ : When enabled, Cedar engine authorization is queried for a Workload principal.

  • CEDARLING_USER_WORKLOAD_BOOLEAN_OPERATION : AND, OR

The following bootstrap properties are needed to configure JWT and cryptographic behavior:

  • CEDARLING_LOCAL_JWKS : JWKS file with public keys

  • CEDARLING_LOCAL_POLICY_STORE : JSON object with policy store

  • CEDARLING_POLICY_STORE_LOCAL_FN : Local file with JSON object with policy store

  • CEDARLING_JWT_SIG_VALIDATION : Enabled | Disabled -- Whether to check the signature of all JWT tokens. This requires an iss is present.

  • CEDARLING_JWT_STATUS_VALIDATION : Enabled | Disabled -- Whether to check the status of the JWT. On startup, the Cedarling should fetch and retreive the latest Status List JWT from the .well-known/openid-configuration via the status_list_endpoint claim and cache it. See the IETF Draft for more info.

  • CEDARLING_JWT_SIGNATURE_ALGORITHMS_SUPPORTED : Only tokens signed with these algorithms are acceptable to the Cedarling.

  • CEDARLING_AT_ISS_VALIDATION : When enabled, the iss claim must be present in access token and the scheme must be https.

  • CEDARLING_AT_JTI_VALIDATION : When enabled, the jti claim must be present in access token.

  • CEDARLING_AT_NBF_VALIDATION : When enabled, the nbf claim must be present in access token and the Cedarling should verify that the current date is after the nbf.

  • CEDARLING_AT_EXP_VALIDATION : When enabled, the exp claim must be present and not past the date specified.

  • CEDARLING_IDT_ISS_VALIDATION : When enabled, the iss claim must be present in id_token and the scheme must be https.

  • CEDARLING_IDT_SUB_VALIDATION : When enabled, the sub claim must be present in id_token.

  • CEDARLING_IDT_EXP_VALIDATION : When enabled, the exp claim must be present and not past the date specified.

  • CEDARLING_IDT_IAT_VALIDATION : When enabled, the iat claim must be present in id_token.

  • CEDARLING_IDT_AUD_VALIDATION : When enabled, the aud claim must be present in id_token.

  • CEDARLING_USERINFO_ISS_VALIDATION : When enabled, the iss claim must be present and the scheme must be https.

  • CEDARLING_USERINFO_SUB_VALIDATION : When enabled, the sub claim must be present in Userinfo JWT.

  • CEDARLING_USERINFO_AUD_VALIDATION : When enabled, the aud claim must be present in Userinfo JWT.

  • CEDARLING_USERINFO_EXP_VALIDATION : When enabled, the exp claim must be present and not past the date specified.

  • CEDARLING_ID_TOKEN_TRUST_MODE : Strict | None. Varying levels of validations based on the preference of the developer. Strict mode requires (1) id_token aud matches the access_token client_id; (2) if a Userinfo token is present, the sub matches the id_token, and that the aud matches the access token client_id.

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_DYNAMIC_CONFIGURATION : Enabled | Disabled, controls whether Cedarling should listen for SSE config updates.

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

  • 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_LISTEN_SSE : Enabled | Disabled: controls whether Cedarling should listen for updates from the Lock Server.

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",
    "policy_stores": {
            "some_random_id": {
                "name": "",
                "description": "",
                "policies": {...}
                "trusted_issuers": {...},
                "schema": ""
            }
    },
}

Currently cedarling support only one policy_store in policy_stores map.
We have policy_stores map to avoid incompatible change in future.

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",
    ... <- `Token Entity Metadata Schema` values
  },
 "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": "Jans::email_address",  
                         "regex_expression" : "^(?P<UID>[^@]+)@(?P<DOMAIN>.+)$", 
                         "UID": {"attr": "uid", "type":"String"}, 
                         "DOMAIN": {"attr": "domain", "type":"String"} 
                      }, 
                      "profile": {
                         "parser": "regex", 
                         "type": "Jans::Url", 
                         "regex_expression": "(?x) ^(?P<SCHEME>[a-zA-Z][a-zA-Z0-9+.-]*):\\/\\/(?P<HOST>[^\\/:\\#?]+)(?::(?<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"}
                      }
                   }
}

user_id - the field in the JWT payload that should be used to retrieve the id of the User entity.

role_mapping - This field in the JWT payload specifies which field(s) should be used to retrieve the id of the Role entity. It can be either a string or an array. If an array is provided, multiple Role entities will be created, and authorization will be attempted for each one until a permit status is achieved.

claim_mapping - is used for mapping JWT payload values to types defined in the cedar-policy schema. Mapping fields ("attr") should be the same as type in cedar-policy schema. It is very important to use correct namespace and typename to proper mapping.

In regex attribute mapping "UID": {"attr": "uid", "type":"String"}, type field can contain possible variants:

  • String - to string without transformation,
  • Number - parse string to float64 (JSON number) if error returns default value
  • Boolean - if string NOT empty map to true else false

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.

When you use (?x) modifier in regexp, ensure that you escaped character # => \#.

Cedar Schema

namespace Jans {
    // ******  TYPES  ******
    type Url = {
        host: String,
        path: String,
        protocol: String
    };
    type email_address = {
        domain: String,
        uid: String
    };
    type Context = {
        network: String,
        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,
        rp_id?: String,
        spiffe_id?: String
    };
    entity Access_token = {
        aud: String,
        exp: Long,
        iat: Long,
        iss: TrustedIssuer,
        jti: String,
        nbf: Long,
        scope: Set<String>
    };
    entity TrustedIssuer = {
        issuer_entity_id: Url
    };
    entity Role;
    entity User in [Role] = {
        email: email_address,
        phone_number?: String,
        role: Set<String>,
        sub: String,
        "username"?: String
    };
    entity id_token = {
        acr: String,
        amr: Set<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 = {
        accept: Set<String>,
        header: Set<String>,
        url: Url
    };
    entity Application = {
        app_id: String,
        name: String,
        url: Url
    };

    // ******  Actions  ******
    action Compare appliesTo {
      principal: [User, Role, Workload],
      resource: [Application],
      context: Context
    };
    action Execute appliesTo {
      principal: [User, Role, Workload],
      resource: [Application],
      context: Context
    };
    action Monitor appliesTo {
      principal: [User, Role, Workload],
      resource: [Application],
      context: Context
    };
    action Read appliesTo {
      principal: [User, Role, Workload],
      resource: [Application],
      context: Context
    };
    action Search appliesTo {
      principal: [User, Role, Workload],
      resource: [Application],
      context: Context
    };
    action Share appliesTo {
      principal: [User, Role, Workload],
      resource: [Application],
      context: Context
    };
    action Tag appliesTo {
      principal: [User, Role, Workload],
      resource: [Application],
      context: Context
    };
    action Write appliesTo {
      principal: [User, Role, Workload],
      resource: [Application],
      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

Lock Master .well-known metadata

{
  "version": "1.0",
  "issuer": "yurem-evolved-woodcock.gluu.info",
  "audit": {
    "health_endpoint": "https://yurem-evolved-woodcock.gluu.info/jans-lock/v1/audit/health",
    "log_endpoint": "https://yurem-evolved-woodcock.gluu.info/jans-lock/v1/audit/log",
    "telemetry_endpoint": "https://yurem-evolved-woodcock.gluu.info/jans-lock/v1/audit/telemetry"
  },
  "config": {
    "config_endpoint": "https://yurem-evolved-woodcock.gluu.info/jans-lock/v1/config",
    "issuers_endpoint": "https://yurem-evolved-woodcock.gluu.info/jans-lock/v1/config/issuers",
    "policy_endpoint": "https://yurem-evolved-woodcock.gluu.info/jans-lock/v1/config/policy",
    "schema_endpoint": "https://yurem-evolved-woodcock.gluu.info/jans-lock/v1/config/schema",
    "sse_endpoint": "https://yurem-evolved-woodcock.gluu.info/jans-lock/v1/sse"
  }
}

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 - centralize logs by sending to Jans Lock Server

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>;
}

Log schema

(Enter all fields and quick description.

Sample logs

Startup Message

{
  "id": "01929b39-b7d9-7e58-8abb-329e91d2b37f",
  "time": 1729181104,
  "log_kind": "System",
  "pdp_id": "1039a067-88de-4e8b-8d5f-421fc912b94f",
  "msg": "Cedarling Authz initialized successfully",
  "application_id": "TestApp"
}

Decision Log

{
  "id": "0193414e-9676-7d8a-b55b-3f0097355851",
  "time": 1731967489,
  "log_kind": "Decision",
  "pdp_id": "c0ec33ff-9482-4bdc-83f6-2925a41a3280",
  "msg": "Result of authorize.",
  "application_id": "TestApp",
  "action": "Jans::Action::\"Read\"",
  "resource": "Jans::Application::\"some_id\"",
  "context": {
    "user_agent": "Linux",
    "operating_system": "Linux",
    "network_type": "Local",
    "network": "127.0.0.1",
    "geolocation": [
      "America"
    ],
    "fraud_indicators": [
      "Allowed"
    ],
    "device_health": [
      "Healthy"
    ],
    "current_time": 1731967489
  },
  "person_principal": "Jans::User::\"qzxn1Scrb9lWtGxVedMCky-Ql_ILspZaQA6fyuYktw0\"",
  "person_diagnostics": {
    "reason": [
      "840da5d85403f35ea76519ed1a18a33989f855bf1cf8"
    ],
    "errors": []
  },
  "person_decision": "ALLOW",
  "workload_principal": "Jans::Workload::\"d7f71bea-c38d-4caf-a1ba-e43c74a11a62\"",
  "workload_diagnostics": {
    "reason": [
      "444da5d85403f35ea76519ed1a18a33989f855bf1cf8"
    ],
    "errors": []
  },
  "workload_decision": "ALLOW",
  "role_authorize_info": [
    {
      "role_principal": "Jans::Role::\"CasaAdmin\"",
      "role_diagnostics": {
        "reason": [],
        "errors": []
      },
      "role_decision": "DENY"
    }
  ],
  "authorized": true
}

In diagnostic information

"reason": [
      "444da5d85403f35ea76519ed1a18a33989f855bf1cf8"
    ]
  • 444da5d85403f35ea76519ed1a18a33989f855bf1cf8 - shows policy ID (from policy store) that was executed.

Cedarling Startup Sequence

cedarling-startup

Source

Authz Sequence Diagram

Note: no JWT status check

cedarling-authz-sequence-diagram

Source

Topology Diagram

cedarling-lock-topology-concept

Clone this wiki locally