nuno hat die Gist bearbeitet 1 month ago. Zu Änderung gehen
1 file changed, 296 insertions
policy.command.sh(Datei erstellt)
| @@ -0,0 +1,296 @@ | |||
| 1 | + | #!/usr/bin/env bash | |
| 2 | + | # policy.command.sh — manage policies | |
| 3 | + | # | |
| 4 | + | # Subcommands: | |
| 5 | + | # wgctl policy list | |
| 6 | + | # wgctl policy show --name <name> | |
| 7 | + | # wgctl policy add --name <name> [--tunnel-mode split|full] | |
| 8 | + | # [--default-rule <rule>] [--strict-rule] [--no-auto-apply] | |
| 9 | + | # [--desc <desc>] | |
| 10 | + | # wgctl policy rm --name <name> | |
| 11 | + | # wgctl policy set --name <name> --field <field> --value <value> | |
| 12 | + | ||
| 13 | + | # ============================================ | |
| 14 | + | # Lifecycle | |
| 15 | + | # ============================================ | |
| 16 | + | ||
| 17 | + | function cmd::policy::on_load() { | |
| 18 | + | load_module policy | |
| 19 | + | ||
| 20 | + | flag::register --name | |
| 21 | + | flag::register --tunnel-mode | |
| 22 | + | flag::register --default-rule | |
| 23 | + | flag::register --strict-rule | |
| 24 | + | flag::register --no-strict-rule | |
| 25 | + | flag::register --auto-apply | |
| 26 | + | flag::register --no-auto-apply | |
| 27 | + | flag::register --desc | |
| 28 | + | flag::register --field | |
| 29 | + | flag::register --value | |
| 30 | + | ||
| 31 | + | command::mixin json_output | |
| 32 | + | } | |
| 33 | + | ||
| 34 | + | # ============================================ | |
| 35 | + | # Help | |
| 36 | + | # ============================================ | |
| 37 | + | ||
| 38 | + | function cmd::policy::help() { | |
| 39 | + | cat <<EOF | |
| 40 | + | Usage: wgctl policy <subcommand> [options] | |
| 41 | + | ||
| 42 | + | Manage policies. Policies define behavioral flags for subnets and identities. | |
| 43 | + | ||
| 44 | + | Subcommands: | |
| 45 | + | list List all policies | |
| 46 | + | show --name <name> Show policy details | |
| 47 | + | add --name <name> Add a new policy | |
| 48 | + | [--tunnel-mode split|full] | |
| 49 | + | [--default-rule <rule>] | |
| 50 | + | [--strict-rule] | |
| 51 | + | [--no-auto-apply] | |
| 52 | + | [--desc <desc>] | |
| 53 | + | rm --name <name> Remove a policy (built-ins cannot be removed) | |
| 54 | + | set --name <name> Set a single field on a policy | |
| 55 | + | --field <field> | |
| 56 | + | --value <value> | |
| 57 | + | ||
| 58 | + | Fields: | |
| 59 | + | tunnel_mode split|full | |
| 60 | + | default_rule rule name (or empty to clear) | |
| 61 | + | strict_rule true|false | |
| 62 | + | auto_apply true|false | |
| 63 | + | desc description string | |
| 64 | + | ||
| 65 | + | Built-in policies (cannot be removed): default, guest, trusted, server, iot | |
| 66 | + | ||
| 67 | + | Examples: | |
| 68 | + | wgctl policy list | |
| 69 | + | wgctl policy show --name guest | |
| 70 | + | wgctl policy add --name contractor --default-rule contractor --strict-rule | |
| 71 | + | wgctl policy set --name contractor --field tunnel-mode --value full | |
| 72 | + | wgctl policy rm --name contractor | |
| 73 | + | EOF | |
| 74 | + | } | |
| 75 | + | ||
| 76 | + | # ============================================ | |
| 77 | + | # Run | |
| 78 | + | # ============================================ | |
| 79 | + | ||
| 80 | + | function cmd::policy::run() { | |
| 81 | + | local subcmd="${1:-list}" | |
| 82 | + | shift || true | |
| 83 | + | ||
| 84 | + | if command::json; then | |
| 85 | + | cmd::policy::_output_json | |
| 86 | + | return 0 | |
| 87 | + | fi | |
| 88 | + | ||
| 89 | + | case "$subcmd" in | |
| 90 | + | list) cmd::policy::_list "$@" ;; | |
| 91 | + | show) cmd::policy::_show "$@" ;; | |
| 92 | + | add) cmd::policy::_add "$@" ;; | |
| 93 | + | rm) cmd::policy::_rm "$@" ;; | |
| 94 | + | set) cmd::policy::_set "$@" ;; | |
| 95 | + | --help) cmd::policy::help ;; | |
| 96 | + | *) | |
| 97 | + | log::error "Unknown subcommand '${subcmd}'. Available: list, show, add, rm, set" | |
| 98 | + | return 1 | |
| 99 | + | ;; | |
| 100 | + | esac | |
| 101 | + | } | |
| 102 | + | ||
| 103 | + | # ============================================ | |
| 104 | + | # Subcommands | |
| 105 | + | # ============================================ | |
| 106 | + | ||
| 107 | + | function cmd::policy::_list() { | |
| 108 | + | local data | |
| 109 | + | data=$(policy::list_data | ui::sort_rows 1) | |
| 110 | + | ||
| 111 | + | if [[ -z "$data" ]]; then | |
| 112 | + | log::info "No policies defined." | |
| 113 | + | return 0 | |
| 114 | + | fi | |
| 115 | + | ||
| 116 | + | if display::is_table "policy_list"; then | |
| 117 | + | cmd::policy::_render_table "$data" | |
| 118 | + | return 0 | |
| 119 | + | fi | |
| 120 | + | ||
| 121 | + | echo "" | |
| 122 | + | while IFS='|' read -r name tunnel default_rule strict auto desc; do | |
| 123 | + | ui::policy::list_row "$name" "$default_rule" "$strict" "$auto" | |
| 124 | + | done <<< "$data" | |
| 125 | + | echo "" | |
| 126 | + | } | |
| 127 | + | ||
| 128 | + | function cmd::policy::_render_table() { | |
| 129 | + | local data="${1:-}" | |
| 130 | + | [[ -z "$data" ]] && return 0 | |
| 131 | + | ||
| 132 | + | ui::policy::list_header_table | |
| 133 | + | while IFS='|' read -r name tunnel default_rule strict auto desc; do | |
| 134 | + | [[ -z "$name" ]] && continue | |
| 135 | + | ui::policy::list_row_table "$name" "$tunnel" "$default_rule" "$strict" "$auto" | |
| 136 | + | done <<< "$data" | |
| 137 | + | printf "\n" | |
| 138 | + | } | |
| 139 | + | ||
| 140 | + | ||
| 141 | + | function cmd::policy::_show() { | |
| 142 | + | local name="" | |
| 143 | + | while [[ $# -gt 0 ]]; do | |
| 144 | + | case "$1" in | |
| 145 | + | --name) name="$2"; shift 2 ;; | |
| 146 | + | --help) cmd::policy::help; return ;; | |
| 147 | + | *) log::error "Unknown flag: $1"; return 1 ;; | |
| 148 | + | esac | |
| 149 | + | done | |
| 150 | + | ||
| 151 | + | [[ -z "$name" ]] && { log::error "Missing required flag: --name"; return 1; } | |
| 152 | + | policy::require_exists "$name" || return 1 | |
| 153 | + | ||
| 154 | + | local dr tunnel strict auto | |
| 155 | + | dr=$(policy::default_rule "$name") | |
| 156 | + | tunnel=$(policy::tunnel_mode "$name") | |
| 157 | + | strict=$(policy::strict_rule "$name" && echo "yes" || echo "no") | |
| 158 | + | auto=$(policy::auto_apply "$name" && echo "yes" || echo "no") | |
| 159 | + | ||
| 160 | + | local rule_val="-" | |
| 161 | + | [[ -n "$dr" ]] && rule_val="$dr" | |
| 162 | + | ||
| 163 | + | local strict_padded | |
| 164 | + | strict_padded=$(printf "%-4s" "$strict") | |
| 165 | + | ||
| 166 | + | # First line — mirrors list format | |
| 167 | + | echo "" | |
| 168 | + | printf " \033[1m%-14s\033[0m \033[2mrule:\033[0m %-16s \033[2mstrict:\033[0m %s\n" \ | |
| 169 | + | "$name" "$rule_val" "$strict_padded" | |
| 170 | + | echo "" | |
| 171 | + | ||
| 172 | + | # Detail section | |
| 173 | + | local desc | |
| 174 | + | desc=$(policy::get "$name" "desc") | |
| 175 | + | [[ -n "$desc" ]] && printf " \033[2mDescription:\033[0m %s\n" "$desc" | |
| 176 | + | printf " \033[2mTunnel:\033[0m %s\n" "$tunnel" | |
| 177 | + | printf " \033[2mAuto apply:\033[0m %s\n" "$auto" | |
| 178 | + | echo "" | |
| 179 | + | } | |
| 180 | + | ||
| 181 | + | function cmd::policy::_add() { | |
| 182 | + | local name="" tunnel_mode="split" default_rule="" \ | |
| 183 | + | strict_rule="false" auto_apply="true" desc="" | |
| 184 | + | ||
| 185 | + | while [[ $# -gt 0 ]]; do | |
| 186 | + | case "$1" in | |
| 187 | + | --name) name="$2"; shift 2 ;; | |
| 188 | + | --tunnel-mode) tunnel_mode="$2"; shift 2 ;; | |
| 189 | + | --default-rule) default_rule="$2"; shift 2 ;; | |
| 190 | + | --strict-rule) strict_rule="true"; shift ;; | |
| 191 | + | --no-strict-rule) strict_rule="false"; shift ;; | |
| 192 | + | --no-auto-apply) auto_apply="false"; shift ;; | |
| 193 | + | --auto-apply) auto_apply="true"; shift ;; | |
| 194 | + | --desc) desc="$2"; shift 2 ;; | |
| 195 | + | --help) cmd::policy::help; return ;; | |
| 196 | + | *) log::error "Unknown flag: $1"; return 1 ;; | |
| 197 | + | esac | |
| 198 | + | done | |
| 199 | + | ||
| 200 | + | [[ -z "$name" ]] && { log::error "Missing required flag: --name"; return 1; } | |
| 201 | + | ||
| 202 | + | case "$tunnel_mode" in | |
| 203 | + | split|full) ;; | |
| 204 | + | *) log::error "Invalid --tunnel-mode '${tunnel_mode}'. Use: split, full"; return 1 ;; | |
| 205 | + | esac | |
| 206 | + | ||
| 207 | + | json::policy_add "$(ctx::policies)" "$name" "$tunnel_mode" \ | |
| 208 | + | "$default_rule" "$strict_rule" "$auto_apply" "$desc" | |
| 209 | + | log::ok "Policy '${name}' added" | |
| 210 | + | } | |
| 211 | + | ||
| 212 | + | function cmd::policy::_rm() { | |
| 213 | + | local name="" | |
| 214 | + | while [[ $# -gt 0 ]]; do | |
| 215 | + | case "$1" in | |
| 216 | + | --name) name="$2"; shift 2 ;; | |
| 217 | + | --help) cmd::policy::help; return ;; | |
| 218 | + | *) log::error "Unknown flag: $1"; return 1 ;; | |
| 219 | + | esac | |
| 220 | + | done | |
| 221 | + | ||
| 222 | + | [[ -z "$name" ]] && { log::error "Missing required flag: --name"; return 1; } | |
| 223 | + | policy::require_exists "$name" || return 1 | |
| 224 | + | ||
| 225 | + | json::policy_remove "$(ctx::policies)" "$name" | |
| 226 | + | log::ok "Policy '${name}' removed" | |
| 227 | + | } | |
| 228 | + | ||
| 229 | + | function cmd::policy::_set() { | |
| 230 | + | local name="" field="" value="" | |
| 231 | + | while [[ $# -gt 0 ]]; do | |
| 232 | + | case "$1" in | |
| 233 | + | --name) name="$2"; shift 2 ;; | |
| 234 | + | --field) field="$2"; shift 2 ;; | |
| 235 | + | --value) value="$2"; shift 2 ;; | |
| 236 | + | --help) cmd::policy::help; return ;; | |
| 237 | + | *) log::error "Unknown flag: $1"; return 1 ;; | |
| 238 | + | esac | |
| 239 | + | done | |
| 240 | + | ||
| 241 | + | [[ -z "$name" ]] && { log::error "Missing required flag: --name"; return 1; } | |
| 242 | + | [[ -z "$field" ]] && { log::error "Missing required flag: --field"; return 1; } | |
| 243 | + | [[ -z "$value" ]] && { log::error "Missing required flag: --value"; return 1; } | |
| 244 | + | ||
| 245 | + | policy::require_exists "$name" || return 1 | |
| 246 | + | ||
| 247 | + | # Normalise field name (allow tunnel-mode as well as tunnel_mode) | |
| 248 | + | field="${field//-/_}" | |
| 249 | + | ||
| 250 | + | case "$field" in | |
| 251 | + | tunnel_mode) | |
| 252 | + | case "$value" in | |
| 253 | + | split|full) ;; | |
| 254 | + | *) log::error "Invalid value '${value}' for tunnel_mode. Use: split, full"; return 1 ;; | |
| 255 | + | esac | |
| 256 | + | ;; | |
| 257 | + | strict_rule|auto_apply) | |
| 258 | + | case "$value" in | |
| 259 | + | true|false) ;; | |
| 260 | + | *) log::error "Invalid value '${value}' for ${field}. Use: true, false"; return 1 ;; | |
| 261 | + | esac | |
| 262 | + | ;; | |
| 263 | + | default_rule|desc) ;; | |
| 264 | + | *) | |
| 265 | + | log::error "Unknown field '${field}'. Valid: tunnel_mode, default_rule, strict_rule, auto_apply, desc" | |
| 266 | + | return 1 | |
| 267 | + | ;; | |
| 268 | + | esac | |
| 269 | + | ||
| 270 | + | json::policy_set_field "$(ctx::policies)" "$name" "$field" "$value" | |
| 271 | + | log::ok "Policy '${name}': ${field} = ${value}" | |
| 272 | + | } | |
| 273 | + | ||
| 274 | + | function cmd::policy::_output_json() { | |
| 275 | + | local data | |
| 276 | + | data=$(policy::list_data 2>/dev/null) | |
| 277 | + | ||
| 278 | + | local -a policies=() | |
| 279 | + | while IFS='|' read -r name tunnel_mode default_rule strict_rule auto_apply desc; do | |
| 280 | + | [[ -z "$name" ]] && continue | |
| 281 | + | ||
| 282 | + | local strict_json="false" | |
| 283 | + | [[ "$strict_rule" == "true" ]] && strict_json="true" | |
| 284 | + | local auto_json="true" | |
| 285 | + | [[ "$auto_apply" == "false" ]] && auto_json="false" | |
| 286 | + | ||
| 287 | + | policies+=("$(printf '{"name":"%s","tunnel_mode":"%s","default_rule":"%s","strict_rule":%s,"auto_apply":%s,"desc":"%s"}' \ | |
| 288 | + | "$name" "$tunnel_mode" "${default_rule:-}" \ | |
| 289 | + | "$strict_json" "$auto_json" "$desc")") | |
| 290 | + | done <<< "$data" | |
| 291 | + | ||
| 292 | + | local count=${#policies[@]} | |
| 293 | + | local array | |
| 294 | + | array=$(printf '%s\n' "${policies[@]:-}" | paste -sd ',' -) | |
| 295 | + | printf '{"policies":[%s]}' "${array:-}" | json::envelope "policy list" "$count" | |
| 296 | + | } | |
Neuer
Älter