200 lines
5.6 KiB
Bash
200 lines
5.6 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
|
|
|
|
# ======================================================
|
|
# Hardcoded Fallbacks
|
|
# Mirror of policies.json built-in policies.
|
|
# Used when policies.json lookup fails.
|
|
# ======================================================
|
|
|
|
declare -gA _POLICY_TUNNEL_MODE=(
|
|
[default]="split"
|
|
[guest]="split"
|
|
[trusted]="split"
|
|
[server]="split"
|
|
[iot]="split"
|
|
)
|
|
|
|
declare -gA _POLICY_DEFAULT_RULE=(
|
|
[default]=""
|
|
[guest]="guest"
|
|
[trusted]=""
|
|
[server]=""
|
|
[iot]=""
|
|
)
|
|
|
|
declare -gA _POLICY_STRICT_RULE=(
|
|
[default]="false"
|
|
[guest]="true"
|
|
[trusted]="false"
|
|
[server]="false"
|
|
[iot]="false"
|
|
)
|
|
|
|
declare -gA _POLICY_AUTO_APPLY=(
|
|
[default]="true"
|
|
[guest]="true"
|
|
[trusted]="true"
|
|
[server]="true"
|
|
[iot]="true"
|
|
)
|
|
|
|
function policy::_hardcoded_field() {
|
|
local name="${1:-}" field="${2:-}"
|
|
case "$field" in
|
|
tunnel_mode) echo "${_POLICY_TUNNEL_MODE[$name]:-split}" ;;
|
|
default_rule) echo "${_POLICY_DEFAULT_RULE[$name]:-}" ;;
|
|
strict_rule) echo "${_POLICY_STRICT_RULE[$name]:-false}" ;;
|
|
auto_apply) echo "${_POLICY_AUTO_APPLY[$name]:-true}" ;;
|
|
*) echo "" ;;
|
|
esac
|
|
}
|
|
|
|
# ======================================================
|
|
# Core Accessors
|
|
# ======================================================
|
|
|
|
function ctx::policies() { echo "${_CTX_DATA}/policies.json"; }
|
|
|
|
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"
|
|
}
|