wgctl/modules/policy.module.sh
2026-05-25 18:47:48 +00:00

162 lines
4.8 KiB
Bash

#!/usr/bin/env bash
# policy.module.sh — policy system
# Policies define behavioral flags for subnets, identities, and future contexts.
# Chain: Subnet → Policy → Identity → Peer
function policy::_hardcoded_field() {
local name="${1:-}" field="${2:-}"
# Only fallback for 'default' policy if policies.json is unavailable
[[ "$name" != "default" ]] && echo "" && return 0
case "$field" in
tunnel_mode) echo "split" ;;
default_rule) echo "" ;;
strict_rule) echo "false" ;;
auto_apply) echo "true" ;;
*) echo "" ;;
esac
}
# ======================================================
# Core Accessors
# ======================================================
function policy::exists() {
local name="${1:-}"
json::policy_exists "$(ctx::policies)" "$name" 2>/dev/null
}
function policy::require_exists() {
local name="${1:-}"
if ! policy::exists "$name"; then
log::error "Policy '${name}' not found. Use 'wgctl policy list' to see available policies."
return 1
fi
}
function policy::get() {
local name="${1:-}" field="${2:-}"
local result
result=$(json::policy_get "$(ctx::policies)" "$name" "$field" 2>/dev/null) || true
if [[ -n "$result" ]]; then
echo "$result"
return 0
fi
# Fallback to hardcoded
[[ -n "$field" ]] && policy::_hardcoded_field "$name" "$field"
}
function policy::tunnel_mode() {
local name="${1:-default}"
policy::get "$name" "tunnel_mode"
}
function policy::default_rule() {
local name="${1:-default}"
policy::get "$name" "default_rule"
}
function policy::strict_rule() {
local name="${1:-default}"
local val
val=$(policy::get "$name" "strict_rule")
[[ "$val" == "true" ]]
}
function policy::auto_apply() {
local name="${1:-default}"
local val
val=$(policy::get "$name" "auto_apply")
[[ "$val" != "false" ]]
}
function policy::list_data() {
json::policy_list "$(ctx::policies)" 2>/dev/null || true
}
# ======================================================
# Subnet Policy Resolution
# ======================================================
# policy::for_subnet <subnet_name> [type_key]
# Returns the policy name for a subnet entry.
# Falls back to "default" if no policy set on the subnet.
function policy::for_subnet() {
local subnet_name="${1:-}" type_key="${2:-}"
local result
result=$(json::subnet_policy "$(ctx::subnets)" "$subnet_name" "$type_key" 2>/dev/null) || true
echo "${result:-default}"
}
# policy::resolve_for_add <subnet_name> [type_key]
# Returns the fully resolved policy for use during wgctl add.
# Output: policy_name
function policy::resolve_for_add() {
local subnet_name="${1:-}" type_key="${2:-}"
if [[ -z "$subnet_name" ]]; then
echo "default"
return 0
fi
policy::for_subnet "$subnet_name" "$type_key"
}
# ======================================================
# Identity Policy Resolution
# ======================================================
# policy::for_identity <identity_name>
# Returns the policy name stored in an identity file.
# Falls back to "default".
function policy::for_identity() {
local identity_name="${1:-}"
local id_file
id_file=$(ctx::identity::path "${identity_name}.identity")
local result
result=$(json::get "$id_file" "policy" 2>/dev/null) || true
echo "${result:-default}"
}
# policy::effective <subnet_name> <type_key> <identity_name>
# Returns the effective policy name for a peer being added.
# Priority: identity policy > subnet policy > default
function policy::effective() {
local subnet_name="${1:-}" type_key="${2:-}" identity_name="${3:-}"
# Identity policy takes precedence if explicitly set
if [[ -n "$identity_name" ]]; then
local id_policy
id_policy=$(policy::for_identity "$identity_name")
if [[ "$id_policy" != "default" ]]; then
echo "$id_policy"
return 0
fi
fi
# Subnet policy next
if [[ -n "$subnet_name" ]]; then
local subnet_policy
subnet_policy=$(policy::for_subnet "$subnet_name" "$type_key")
echo "$subnet_policy"
return 0
fi
echo "default"
}
# ======================================================
# Warning Helpers (called from add.command.sh)
# ======================================================
function policy::warn_strict_rule() {
local identity_name="${1:-}" policy_name="${2:-}" identity_rule="${3:-}"
log::warn "Identity '${identity_name}' has policy '${policy_name}' with strict rule enabled — peer rule will not be applied, '${identity_rule}' is the only active rule"
}
function policy::warn_additive_rule() {
local identity_name="${1:-}" identity_rule="${1:-}" peer_rule="${2:-}"
log::info "Identity '${identity_name}' has strict rule disabled — '${identity_rule}' and '${peer_rule}' will both apply"
}
function policy::warn_no_rule() {
local peer_name="${1:-}"
log::warn "'${peer_name}' has no rule assigned — peer has unrestricted access"
}