#!/usr/bin/env bash # policy.command.sh — manage policies # # Subcommands: # wgctl policy list # wgctl policy show --name # wgctl policy add --name [--tunnel-mode split|full] # [--default-rule ] [--strict-rule] [--no-auto-apply] # [--desc ] # wgctl policy rm --name # wgctl policy set --name --field --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 < [options] Manage policies. Policies define behavioral flags for subnets and identities. Subcommands: list List all policies show --name Show policy details add --name Add a new policy [--tunnel-mode split|full] [--default-rule ] [--strict-rule] [--no-auto-apply] [--desc ] rm --name Remove a policy (built-ins cannot be removed) set --name Set a single field on a policy --field --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}" }