wgctl/commands/policy.command.sh
2026-05-21 02:16:32 +00:00

238 lines
No EOL
7.2 KiB
Bash

#!/usr/bin/env bash
# policy.command.sh — manage policies
#
# Subcommands:
# wgctl policy list
# wgctl policy show --name <name>
# wgctl policy add --name <name> [--tunnel-mode split|full]
# [--default-rule <rule>] [--strict-rule] [--no-auto-apply]
# [--desc <desc>]
# wgctl policy rm --name <name>
# wgctl policy set --name <name> --field <field> --value <value>
# ============================================
# Lifecycle
# ============================================
function cmd::policy::on_load() {
load_module policy
flag::register --name
flag::register --tunnel-mode
flag::register --default-rule
flag::register --strict-rule
flag::register --no-strict-rule
flag::register --auto-apply
flag::register --no-auto-apply
flag::register --desc
flag::register --field
flag::register --value
}
# ============================================
# Help
# ============================================
function cmd::policy::help() {
cat <<EOF
Usage: wgctl policy <subcommand> [options]
Manage policies. Policies define behavioral flags for subnets and identities.
Subcommands:
list List all policies
show --name <name> Show policy details
add --name <name> Add a new policy
[--tunnel-mode split|full]
[--default-rule <rule>]
[--strict-rule]
[--no-auto-apply]
[--desc <desc>]
rm --name <name> Remove a policy (built-ins cannot be removed)
set --name <name> Set a single field on a policy
--field <field>
--value <value>
Fields:
tunnel_mode split|full
default_rule rule name (or empty to clear)
strict_rule true|false
auto_apply true|false
desc description string
Built-in policies (cannot be removed): default, guest, trusted, server, iot
Examples:
wgctl policy list
wgctl policy show --name guest
wgctl policy add --name contractor --default-rule contractor --strict-rule
wgctl policy set --name contractor --field tunnel-mode --value full
wgctl policy rm --name contractor
EOF
}
# ============================================
# Run
# ============================================
function cmd::policy::run() {
local subcmd="${1:-list}"
shift || true
case "$subcmd" in
list) cmd::policy::_list "$@" ;;
show) cmd::policy::_show "$@" ;;
add) cmd::policy::_add "$@" ;;
rm) cmd::policy::_rm "$@" ;;
set) cmd::policy::_set "$@" ;;
--help) cmd::policy::help ;;
*)
log::error "Unknown subcommand '${subcmd}'. Available: list, show, add, rm, set"
return 1
;;
esac
}
# ============================================
# Subcommands
# ============================================
function cmd::policy::_list() {
local data
data=$(policy::list_data)
if [[ -z "$data" ]]; then
log::info "No policies defined."
return 0
fi
printf " %-14s %-8s %-14s %-12s %-12s %s\n" \
"NAME" "TUNNEL" "DEFAULT RULE" "STRICT RULE" "AUTO APPLY" "DESCRIPTION"
ui::divider 84
while IFS='|' read -r name tunnel default_rule strict auto desc; do
local rule_display="${default_rule:-}"
local strict_display auto_display
[[ "$strict" == "true" ]] && strict_display="$(color::red yes)" || strict_display="no"
[[ "$auto" == "true" ]] && auto_display="yes" || auto_display="no"
printf " %-14s %-8s %-14s %-12s %-12s %s\n" \
"$name" "$tunnel" "$rule_display" "$strict_display" "$auto_display" "$desc"
done <<< "$data"
}
function cmd::policy::_show() {
local name=""
while [[ $# -gt 0 ]]; do
case "$1" in
--name) name="$2"; shift 2 ;;
--help) cmd::policy::help; return ;;
*) log::error "Unknown flag: $1"; return 1 ;;
esac
done
[[ -z "$name" ]] && { log::error "Missing required flag: --name"; return 1; }
policy::require_exists "$name" || return 1
echo ""
ui::row "Name" "$name"
ui::row "Tunnel mode" "$(policy::tunnel_mode "$name")"
local dr
dr=$(policy::default_rule "$name")
ui::row "Default rule" "${dr:-}"
ui::row "Strict rule" "$(policy::strict_rule "$name" && echo "yes" || echo "no")"
ui::row "Auto apply" "$(policy::auto_apply "$name" && echo "yes" || echo "no")"
echo ""
}
function cmd::policy::_add() {
local name="" tunnel_mode="split" default_rule="" \
strict_rule="false" auto_apply="true" desc=""
while [[ $# -gt 0 ]]; do
case "$1" in
--name) name="$2"; shift 2 ;;
--tunnel-mode) tunnel_mode="$2"; shift 2 ;;
--default-rule) default_rule="$2"; shift 2 ;;
--strict-rule) strict_rule="true"; shift ;;
--no-strict-rule) strict_rule="false"; shift ;;
--no-auto-apply) auto_apply="false"; shift ;;
--auto-apply) auto_apply="true"; shift ;;
--desc) desc="$2"; shift 2 ;;
--help) cmd::policy::help; return ;;
*) log::error "Unknown flag: $1"; return 1 ;;
esac
done
[[ -z "$name" ]] && { log::error "Missing required flag: --name"; return 1; }
case "$tunnel_mode" in
split|full) ;;
*) log::error "Invalid --tunnel-mode '${tunnel_mode}'. Use: split, full"; return 1 ;;
esac
json::policy_add "$(ctx::policies)" "$name" "$tunnel_mode" \
"$default_rule" "$strict_rule" "$auto_apply" "$desc"
log::ok "Policy '${name}' added"
}
function cmd::policy::_rm() {
local name=""
while [[ $# -gt 0 ]]; do
case "$1" in
--name) name="$2"; shift 2 ;;
--help) cmd::policy::help; return ;;
*) log::error "Unknown flag: $1"; return 1 ;;
esac
done
[[ -z "$name" ]] && { log::error "Missing required flag: --name"; return 1; }
policy::require_exists "$name" || return 1
json::policy_remove "$(ctx::policies)" "$name"
log::ok "Policy '${name}' removed"
}
function cmd::policy::_set() {
local name="" field="" value=""
while [[ $# -gt 0 ]]; do
case "$1" in
--name) name="$2"; shift 2 ;;
--field) field="$2"; shift 2 ;;
--value) value="$2"; shift 2 ;;
--help) cmd::policy::help; return ;;
*) log::error "Unknown flag: $1"; return 1 ;;
esac
done
[[ -z "$name" ]] && { log::error "Missing required flag: --name"; return 1; }
[[ -z "$field" ]] && { log::error "Missing required flag: --field"; return 1; }
[[ -z "$value" ]] && { log::error "Missing required flag: --value"; return 1; }
policy::require_exists "$name" || return 1
# Normalise field name (allow tunnel-mode as well as tunnel_mode)
field="${field//-/_}"
case "$field" in
tunnel_mode)
case "$value" in
split|full) ;;
*) log::error "Invalid value '${value}' for tunnel_mode. Use: split, full"; return 1 ;;
esac
;;
strict_rule|auto_apply)
case "$value" in
true|false) ;;
*) log::error "Invalid value '${value}' for ${field}. Use: true, false"; return 1 ;;
esac
;;
default_rule|desc) ;;
*)
log::error "Unknown field '${field}'. Valid: tunnel_mode, default_rule, strict_rule, auto_apply, desc"
return 1
;;
esac
json::policy_set_field "$(ctx::policies)" "$name" "$field" "$value"
log::ok "Policy '${name}': ${field} = ${value}"
}