- display.module.sh: style toggle per view (compact/table) - display.json: default config with all views set to compact - ctx::display: points to .wgctl/config/display.json - list: _render_table with dynamic widths, colors, shared row_color/status_color - group/identity/net/hosts/activity: _render_table added - rule/subnet/policy: table UI functions + _render_table - ui::peer::status_color: \033[2m for offline (dimmer, more readable) - note: individual table layout refinements pending cleanup pass - note: configurable colors per field deferred to display config v2
296 lines
No EOL
8.6 KiB
Bash
296 lines
No EOL
8.6 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
|
|
|
|
command::mixin json_output
|
|
}
|
|
|
|
# ============================================
|
|
# 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
|
|
|
|
if command::json; then
|
|
cmd::policy::_output_json
|
|
return 0
|
|
fi
|
|
|
|
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 | ui::sort_rows 1)
|
|
|
|
if [[ -z "$data" ]]; then
|
|
log::info "No policies defined."
|
|
return 0
|
|
fi
|
|
|
|
if display::is_table "policy_list"; then
|
|
cmd::policy::_render_table "$data"
|
|
return 0
|
|
fi
|
|
|
|
echo ""
|
|
while IFS='|' read -r name tunnel default_rule strict auto desc; do
|
|
ui::policy::list_row "$name" "$default_rule" "$strict" "$auto"
|
|
done <<< "$data"
|
|
echo ""
|
|
}
|
|
|
|
function cmd::policy::_render_table() {
|
|
local data="${1:-}"
|
|
[[ -z "$data" ]] && return 0
|
|
|
|
ui::policy::list_header_table
|
|
while IFS='|' read -r name tunnel default_rule strict auto desc; do
|
|
[[ -z "$name" ]] && continue
|
|
ui::policy::list_row_table "$name" "$tunnel" "$default_rule" "$strict" "$auto"
|
|
done <<< "$data"
|
|
printf "\n"
|
|
}
|
|
|
|
|
|
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
|
|
|
|
local dr tunnel strict auto
|
|
dr=$(policy::default_rule "$name")
|
|
tunnel=$(policy::tunnel_mode "$name")
|
|
strict=$(policy::strict_rule "$name" && echo "yes" || echo "no")
|
|
auto=$(policy::auto_apply "$name" && echo "yes" || echo "no")
|
|
|
|
local rule_val="-"
|
|
[[ -n "$dr" ]] && rule_val="$dr"
|
|
|
|
local strict_padded
|
|
strict_padded=$(printf "%-4s" "$strict")
|
|
|
|
# First line — mirrors list format
|
|
echo ""
|
|
printf " \033[1m%-14s\033[0m \033[2mrule:\033[0m %-16s \033[2mstrict:\033[0m %s\n" \
|
|
"$name" "$rule_val" "$strict_padded"
|
|
echo ""
|
|
|
|
# Detail section
|
|
local desc
|
|
desc=$(policy::get "$name" "desc")
|
|
[[ -n "$desc" ]] && printf " \033[2mDescription:\033[0m %s\n" "$desc"
|
|
printf " \033[2mTunnel:\033[0m %s\n" "$tunnel"
|
|
printf " \033[2mAuto apply:\033[0m %s\n" "$auto"
|
|
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}"
|
|
}
|
|
|
|
function cmd::policy::_output_json() {
|
|
local data
|
|
data=$(policy::list_data 2>/dev/null)
|
|
|
|
local -a policies=()
|
|
while IFS='|' read -r name tunnel_mode default_rule strict_rule auto_apply desc; do
|
|
[[ -z "$name" ]] && continue
|
|
|
|
local strict_json="false"
|
|
[[ "$strict_rule" == "true" ]] && strict_json="true"
|
|
local auto_json="true"
|
|
[[ "$auto_apply" == "false" ]] && auto_json="false"
|
|
|
|
policies+=("$(printf '{"name":"%s","tunnel_mode":"%s","default_rule":"%s","strict_rule":%s,"auto_apply":%s,"desc":"%s"}' \
|
|
"$name" "$tunnel_mode" "${default_rule:-}" \
|
|
"$strict_json" "$auto_json" "$desc")")
|
|
done <<< "$data"
|
|
|
|
local count=${#policies[@]}
|
|
local array
|
|
array=$(printf '%s\n' "${policies[@]:-}" | paste -sd ',' -)
|
|
printf '{"policies":[%s]}' "${array:-}" | json::envelope "policy list" "$count"
|
|
} |