- Bumped the version of the `lol.mleku.dev` dependency from v1.0.3 to v1.0.4. - Removed redundant checks for disabled policy in event handling. - Introduced a `default_policy` field in the policy configuration, allowing for more flexible event handling based on default behaviors. - Enhanced documentation to clarify the default policy logic and its implications on event processing. - Updated tests to validate the new default policy behavior and ensure correct fallback mechanisms are in place.
9.3 KiB
ORLY Policy System
The ORLY relay includes a comprehensive policy system that allows fine-grained control over event storage and retrieval based on various criteria including event kinds, pubkeys, content, and custom script logic.
Configuration
Enable the policy system by setting the environment variable:
export ORLY_POLICY_ENABLED=true
Policy Configuration File
The policy configuration is loaded from $HOME/.config/ORLY/policy.json. See docs/example-policy.json for a complete example with global rules and age validation.
Structure
{
"default_policy": "allow",
"kind": {
"whitelist": [1, 3, 5, 7, 9735],
"blacklist": []
},
"global": {
"description": "Global rules applied to all events",
"write_allow": [],
"write_deny": [],
"read_allow": [],
"read_deny": [],
"size_limit": 100000,
"content_limit": 50000,
"max_age_of_event": 86400,
"max_age_event_in_future": 300
},
"rules": {
"1": {
"description": "Text notes - allow all authenticated users",
"write_allow": [],
"write_deny": [],
"read_allow": [],
"read_deny": [],
"size_limit": 32000,
"content_limit": 10000,
"max_age_of_event": 3600,
"max_age_event_in_future": 60
}
}
}
Default Policy
The default_policy field determines the default behavior when no specific rules deny an event:
"allow"(default): Events are allowed unless explicitly denied by rules"deny": Events are denied unless explicitly allowed by rules
This applies to:
- Events of whitelisted kinds that have no specific rules
- Events that pass all other policy checks but have no explicit allow/deny decision
Policy Evaluation Order
The policy system evaluates events in the following order:
- Global Rules - Applied to all events first
- Kinds Filtering - Whitelist/blacklist by event kind
- Kind-specific Rules - Rules for specific event kinds
- Script Rules - Custom script logic (if enabled)
- Default Policy - Applied when no rules make a decision
Global Rules
The global section defines rules that apply to all events regardless of their kind. These rules are evaluated first and take precedence over kind-specific rules.
Global rules support all the same fields as kind-specific rules, allowing you to:
- Set site-wide size limits
- Block specific pubkeys globally
- Enforce age restrictions on all events
- Apply content limits across all event types
Kinds Filtering
whitelist: If present, only these event kinds are allowed. All others are denied.blacklist: If present, these event kinds are denied. All others are allowed.- If both are empty, all kinds are allowed.
Rule Fields
description: Human-readable description of the rulescript: Path to a script for custom logic (overrides other criteria)write_allow: List of pubkeys allowed to write this kindwrite_deny: List of pubkeys denied from writing this kindread_allow: List of pubkeys allowed to read this kindread_deny: List of pubkeys denied from reading this kindmax_expiry: Maximum expiry time in seconds for eventsmust_have_tags: List of tag keys that must be presentsize_limit: Maximum total event size in bytescontent_limit: Maximum content field size in bytesprivileged: If true, event must be authored by authenticated user or contain authenticated user in p tagsrate_limit: Rate limit in bytes per second (not yet implemented)max_age_of_event: Maximum age of event in seconds (prevents replay attacks)max_age_event_in_future: Maximum time event can be in the future in seconds (prevents clock skew attacks)
Age Validation
The policy system includes built-in timestamp validation to prevent common attacks:
MaxAgeOfEvent
- Purpose: Prevents replay attacks by rejecting events that are too old
- Behavior: Events with
created_atolder thancurrent_time - max_age_of_eventare rejected - Example: Setting
max_age_of_event: 3600rejects events older than 1 hour - Use Cases:
- Prevent replay of old events
- Ensure events are recent and relevant
- Reduce storage of stale data
MaxAgeEventInFuture
- Purpose: Prevents clock skew attacks by rejecting events too far in the future
- Behavior: Events with
created_atnewer thancurrent_time + max_age_event_in_futureare rejected - Example: Setting
max_age_event_in_future: 300rejects events more than 5 minutes in the future - Use Cases:
- Prevent clock manipulation attacks
- Ensure reasonable timestamp accuracy
- Block events with impossible future timestamps
Age Validation Examples
{
"global": {
"max_age_of_event": 86400, // Reject events older than 24 hours
"max_age_event_in_future": 300 // Reject events more than 5 minutes in future
},
"rules": {
"1": {
"max_age_of_event": 3600, // Text notes: reject older than 1 hour
"max_age_event_in_future": 60 // Text notes: reject more than 1 minute in future
},
"4": {
"max_age_of_event": 604800 // Direct messages: reject older than 7 days
}
}
}
Policy Scripts
For advanced policy logic, you can use custom scripts. The script should be placed at $HOME/.config/ORLY/policy.sh and made executable.
Script Interface
The script receives JSON events via stdin and outputs JSON responses via stdout. Each event includes:
- All original event fields
logged_in_pubkey: Hex-encoded authenticated user's pubkey (if any)ip_address: Client's IP address
Response Format
{"id": "event_id", "action": "accept|reject|shadowReject", "msg": "optional message"}
Example Script
See docs/example-policy.sh for a complete example showing:
- IP address blocking
- Content filtering
- Authentication requirements
- User-specific permissions
Integration Points
EVENT Processing
When policy is enabled, every EVENT envelope is checked using CheckPolicy("write", event, loggedInPubkey, ipAddress) before being stored. The policy evaluation follows this order:
- Global Rules - Applied first to all events
- Kinds Filtering - Whitelist/blacklist check
- Kind-specific Rules - Rules for the event's kind
- Script Rules - Custom script logic (if enabled)
REQ Processing
When policy is enabled, every event returned in REQ responses is filtered using CheckPolicy("read", event, loggedInPubkey, ipAddress) before being sent to the client. The same evaluation order applies for read access.
Script Resilience
The policy system is designed to be resilient to script failures:
Automatic Recovery
- Policy scripts are automatically restarted if they crash or fail to load
- The system continuously monitors script health and attempts recovery every 60 seconds (1 minute)
- Script failures don't disable the entire policy system
Fallback Behavior
When a policy script fails or is not running:
- Events that would have been processed by the script fall back to the
default_policy - The system logs which policy rule is inactive and the fallback behavior
- Other policy rules (global, kinds, non-script rules) continue to function normally
Error Handling
- If policy script fails or times out, events fall back to
default_policysetting - If policy configuration is invalid, default policy (allow all) is used
- Policy script failures are logged with specific rule information but don't block relay operation
Monitoring
Policy decisions and script health are logged:
Policy Decisions
policy allowed event <id>policy rejected event <id>
Script Health
policy rule for kind <N> is inactive (script not running), falling back to default policy (<policy>)policy rule for kind <N> failed (script processing error: <error>), falling back to default policy (<policy>)policy rule for kind <N> returned unknown action '<action>', falling back to default policy (<policy>)policy script not found at <path>, will retry periodicallypolicy script crashed - events will fall back to default policy until restartpolicy filtered out event <id> for read access
Best Practices
Global Rules
- Use global rules for site-wide security policies (size limits, age restrictions)
- Keep global rules simple and broad to avoid unintended side effects
- Test global rules thoroughly as they affect all events
Age Validation
- Set reasonable age limits based on your use case:
- Text notes (kind 1): 1-24 hours max age, 1-5 minutes future tolerance
- Direct messages (kind 4): 7-30 days max age, 1-5 minutes future tolerance
- Replaceable events (kind 0, 3): Longer max age, shorter future tolerance
- Consider network latency when setting future tolerance
- Monitor rejected events to tune age limits appropriately
Policy Hierarchy
- Global rules should be broader than kind-specific rules
- Use global rules for security, kind-specific rules for functionality
- Avoid conflicting rules between global and kind-specific policies
Security Considerations
- Policy scripts run with the same privileges as the relay process
- Scripts should be carefully reviewed and tested
- Consider using read-only filesystems for policy scripts in production
- Monitor script execution time to prevent DoS attacks
- Age validation helps prevent replay and clock skew attacks
- Global rules provide defense-in-depth security