wgctl/commands/config.command.sh
Nuno Duque Nunes 2a6648735e refactor: config restructure, wgctl.json, data/ directory layout
- context.sh: .wgctl/{config,data,daemon} directory structure
- ctx::config_file: points to .wgctl/config/wgctl.json
- ctx::data: points to .wgctl/data/ (rules, identities, groups, etc.)
- ctx::peer_history: .wgctl/data/peer-history/
- config.module.sh: loads from wgctl.json via json::config_load
- config::_load_legacy: fallback for old wgctl.conf with migration warning
- json_helper.py: config_load() outputs KEY=value pairs from wgctl.json
- cmd::config::migrate: converts wgctl.conf → wgctl.json, moves data files
- cmd::config::_show: renamed from run body
- daemon/wgctl-monitor.py: updated PEER_HISTORY_DIR path
2026-05-27 01:37:28 +00:00

281 lines
No EOL
7.4 KiB
Bash

#!/usr/bin/env bash
# ============================================
# Lifecycle
# ============================================
function cmd::config::on_load() {
flag::register --name
flag::register --type
flag::register --force
flag::register --dry-run
}
# ============================================
# Help
# ============================================
function cmd::config::help() {
cat <<EOF
Usage: wgctl config --name <name>
Show the WireGuard config file for a client.
Options:
--name <name> Client name (e.g. phone-nuno)
Examples:
wgctl config --name phone-nuno
wgctl config --name laptop-nuno
EOF
}
# ============================================
# Run
# ============================================
function cmd::config::run() {
local subcmd="${1:-show}"
# If first arg is a flag, treat as 'show' subcommand
if [[ "$subcmd" == --* ]]; then
subcmd="show"
else
shift || true
fi
case "$subcmd" in
show) cmd::config::_show "$@" ;;
migrate) cmd::config::migrate "$@" ;;
help) cmd::config::help ;;
*)
log::error "Unknown subcommand: '${subcmd}'"
cmd::config::help
return 1
;;
esac
}
# ============================================
# Show
# ============================================
function cmd::config::_show() {
local name="" type=""
while [[ $# -gt 0 ]]; do
case "$1" in
--name) name="$2"; shift 2 ;;
--type) type="$2"; shift 2 ;;
--help) cmd::config::help; return ;;
*) log::error "Unknown flag: $1"; return 1 ;;
esac
done
[[ -z "$name" ]] && log::error "Missing required flag: --name" && return 1
name=$(peers::resolve_and_require "$name" "$type") || return 1
local conf
conf="$(ctx::clients)/${name}.conf"
log::section "Client Config: ${name}"
cat "$conf"
}
# ============================================
# Migrate
# ============================================
function cmd::config::migrate() {
local force=false dry_run=false
while [[ $# -gt 0 ]]; do
case "$1" in
--force) force=true; shift ;;
--dry-run) dry_run=true; shift ;;
--help) cmd::config::help; return ;;
*) log::error "Unknown flag: $1"; return 1 ;;
esac
done
local wgctl_dir
wgctl_dir="$(ctx::wgctl)"
local config_dir="${wgctl_dir}/config"
local data_dir="${wgctl_dir}/data"
local legacy_conf="${wgctl_dir}/wgctl.conf"
local json_conf="${config_dir}/wgctl.json"
# Check if already migrated
if [[ -f "$json_conf" && ! -f "$legacy_conf" ]]; then
log::wg_warning "Already migrated to new config structure"
return 0
fi
log::section "wgctl Config Migration"
printf "\n"
printf " This will:\n"
printf " 1. Create %s/config/ and %s/data/\n" "$wgctl_dir" "$wgctl_dir"
printf " 2. Convert wgctl.conf → wgctl.json\n"
printf " 3. Move data files to data/\n\n"
if ! $force && ! $dry_run; then
read -r -p " Proceed? [y/N] " confirm
case "$confirm" in [yY]*) ;; *) log::info "Aborted"; return 0 ;; esac
fi
local do=""
$dry_run && do="echo [dry-run]"
# 1. Create directories
$dry_run || mkdir -p "$config_dir" "$data_dir"
$dry_run && printf " Would create: %s/config/\n" "$wgctl_dir"
$dry_run && printf " Would create: %s/data/\n" "$wgctl_dir"
# 2. Convert wgctl.conf → wgctl.json
if [[ -f "$legacy_conf" ]]; then
if ! $dry_run; then
config::_convert_to_json "$legacy_conf" "$json_conf"
fi
printf " %s wgctl.conf → config/wgctl.json\n" "$($dry_run && echo '[dry-run]' || echo '✓')"
else
log::wg_warning "No wgctl.conf found — creating default wgctl.json"
$dry_run || config::_write_default_json "$json_conf"
fi
# 3. Move data files
local -a data_files=(
"hosts.json"
"services.json"
"subnets.json"
"policies.json"
)
local -a data_dirs=(
"rules"
"identities"
"groups"
"blocks"
"meta"
"peer-history"
)
for f in "${data_files[@]}"; do
if [[ -f "${wgctl_dir}/${f}" ]]; then
$dry_run || mv "${wgctl_dir}/${f}" "${data_dir}/${f}"
printf " %s %s → data/%s\n" \
"$($dry_run && echo '[dry-run]' || echo '✓')" "$f" "$f"
fi
done
for d in "${data_dirs[@]}"; do
if [[ -d "${wgctl_dir}/${d}" ]]; then
$dry_run || mv "${wgctl_dir}/${d}" "${data_dir}/${d}"
printf " %s %s/ → data/%s/\n" \
"$($dry_run && echo '[dry-run]' || echo '✓')" "$d" "$d"
fi
done
# 4. Remove legacy conf after successful migration
if ! $dry_run && [[ -f "$legacy_conf" ]]; then
mv "$legacy_conf" "${legacy_conf}.bak"
printf " ✓ wgctl.conf → wgctl.conf.bak (backup)\n"
fi
printf "\n"
$dry_run && log::wg_warning "Dry run — no changes made" \
|| log::wg_success "Migration complete"
}
function config::_convert_to_json() {
local legacy_file="$1" output_file="$2"
# Read legacy conf into variables
local wg_interface="wg0" wg_endpoint="" wg_dns="10.0.0.103"
local wg_dns_fallback="" wg_port="51820" wg_subnet="10.1.0.0/16"
local wg_lan="10.0.0.0/24" wg_hs_check="300" date_format="eu"
while IFS='=' read -r key value || [[ -n "$key" ]]; do
[[ "$key" =~ ^[[:space:]]*# ]] && continue
[[ -z "${key// }" ]] && continue
key="${key// /}"
value="${value// /}"
case "$key" in
WG_INTERFACE) wg_interface="$value" ;;
WG_ENDPOINT) wg_endpoint="$value" ;;
WG_DNS) wg_dns="$value" ;;
WG_DNS_FALLBACK) wg_dns_fallback="$value" ;;
WG_PORT) wg_port="$value" ;;
WG_SUBNET) wg_subnet="$value" ;;
WG_LAN) wg_lan="$value" ;;
WG_HANDSHAKE_CHECK_TIME_SEC) wg_hs_check="$value" ;;
DATE_FORMAT) date_format="$value" ;;
esac
done < "$legacy_file"
# Build fallback DNS array
local dns_fallback_json="[]"
if [[ -n "$wg_dns_fallback" ]]; then
local fallback_array
fallback_array=$(echo "$wg_dns_fallback" | tr ',' '\n' | \
while IFS= read -r s; do
s="${s// /}"
[[ -n "$s" ]] && printf '"%s",' "$s"
done | sed 's/,$//')
dns_fallback_json="[${fallback_array}]"
fi
mkdir -p "$(dirname "$output_file")"
cat > "$output_file" << JSON
{
"wireguard": {
"interface": "${wg_interface}",
"endpoint": "${wg_endpoint}",
"port": ${wg_port},
"subnet": "${wg_subnet}",
"lan": "${wg_lan}"
},
"dns": {
"primary": "${wg_dns}",
"fallback": ${dns_fallback_json}
},
"handshake": {
"check_interval_sec": ${wg_hs_check}
},
"activity": {
"total": {"low": 1000000, "medium": 10000000, "high": 100000000},
"current": {"low": 1000000, "medium": 10000000, "high": 100000000}
},
"display": {
"date_format": "${date_format}"
}
}
JSON
}
function config::_write_default_json() {
local output_file="$1"
mkdir -p "$(dirname "$output_file")"
cat > "$output_file" << 'JSON'
{
"wireguard": {
"interface": "wg0",
"endpoint": "",
"port": 51820,
"subnet": "10.1.0.0/16",
"lan": "10.0.0.0/24"
},
"dns": {
"primary": "10.0.0.103",
"fallback": []
},
"handshake": {
"check_interval_sec": 300
},
"activity": {
"total": {"low": 1000000, "medium": 10000000, "high": 100000000},
"current": {"low": 1000000, "medium": 10000000, "high": 100000000}
},
"display": {
"date_format": "eu"
}
}
JSON
}