-
Notifications
You must be signed in to change notification settings - Fork 73
Cedarling Nativity Plan
- 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)
-
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 ofmemory
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 ifLOCK
==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
The Cedarling Policy Store is a JSON file that contains all the data the Cedarling needs to verify JWT tokens and evaluate policies:
- Cedar Schema - JSON format Schema file
- Cedar Policies - JSON format Policy Set file (beware CLI bug cedar-950)
- Trusted Issuers - JSON file with below syntax
cedarling_store.json
schema
{
"cedar_version": "v4.0.0",
"name": "",
"description": "",
"policies": {...}
"identity_source": {...},
"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": {...}
}
{
"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.
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,
};
}
- 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
- 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_tokenclient_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
andnbf
claims if time sent in Context
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 application has internal logger.
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
.
Each log entry marked with log type. In field log_kind
.
Possible variants:
Decision
System
Metric
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>;
}
Note: no JWT status check