Merge refactor/config-restructure: JSON config, data/ layout
This commit is contained in:
commit
74644e547c
5 changed files with 435 additions and 124 deletions
|
|
@ -7,6 +7,8 @@
|
||||||
function cmd::config::on_load() {
|
function cmd::config::on_load() {
|
||||||
flag::register --name
|
flag::register --name
|
||||||
flag::register --type
|
flag::register --type
|
||||||
|
flag::register --force
|
||||||
|
flag::register --dry-run
|
||||||
}
|
}
|
||||||
|
|
||||||
# ============================================
|
# ============================================
|
||||||
|
|
@ -33,27 +35,43 @@ EOF
|
||||||
# ============================================
|
# ============================================
|
||||||
|
|
||||||
function cmd::config::run() {
|
function cmd::config::run() {
|
||||||
local name=""
|
local subcmd="${1:-show}"
|
||||||
local type=""
|
|
||||||
|
|
||||||
|
# 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
|
while [[ $# -gt 0 ]]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
--name) name="$2"; shift 2 ;;
|
--name) name="$2"; shift 2 ;;
|
||||||
--type) type="$2"; shift 2 ;;
|
--type) type="$2"; shift 2 ;;
|
||||||
--help) cmd::config::help; return ;;
|
--help) cmd::config::help; return ;;
|
||||||
*)
|
*) log::error "Unknown flag: $1"; return 1 ;;
|
||||||
log::error "Unknown flag: $1"
|
|
||||||
cmd::config::help
|
|
||||||
return 1
|
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
if [[ -z "$name" ]]; then
|
[[ -z "$name" ]] && log::error "Missing required flag: --name" && return 1
|
||||||
log::error "Missing required flag: --name"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
name=$(peers::resolve_and_require "$name" "$type") || return 1
|
name=$(peers::resolve_and_require "$name" "$type") || return 1
|
||||||
|
|
||||||
local conf
|
local conf
|
||||||
|
|
@ -62,3 +80,202 @@ function cmd::config::run() {
|
||||||
log::section "Client Config: ${name}"
|
log::section "Client Config: ${name}"
|
||||||
cat "$conf"
|
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
|
||||||
|
}
|
||||||
106
core/context.sh
106
core/context.sh
|
|
@ -10,80 +10,88 @@ _CTX_CORE="${_CTX_ROOT}/core"
|
||||||
_CTX_MODULES="${_CTX_ROOT}/modules"
|
_CTX_MODULES="${_CTX_ROOT}/modules"
|
||||||
_CTX_COMMANDS="${_CTX_ROOT}/commands"
|
_CTX_COMMANDS="${_CTX_ROOT}/commands"
|
||||||
_CTX_CLIENTS="${_CTX_WG}/clients"
|
_CTX_CLIENTS="${_CTX_WG}/clients"
|
||||||
_CTX_DATA="${_CTX_WG}/.wgctl"
|
|
||||||
|
|
||||||
# ============================================
|
# ── Directory layout ──────────────────────────────────
|
||||||
# Artifacts
|
# .wgctl/
|
||||||
# ============================================
|
# config/ ← wgctl.json, display.json
|
||||||
|
# data/ ← all persistent data (rules, identities, etc.)
|
||||||
|
# daemon/ ← runtime files (logs, caches)
|
||||||
|
|
||||||
|
_CTX_WGCTL="${_CTX_WG}/.wgctl"
|
||||||
|
_CTX_CONFIG="${_CTX_WGCTL}/config"
|
||||||
|
_CTX_DATA="${_CTX_WGCTL}/data"
|
||||||
|
_CTX_DAEMON="${_CTX_WGCTL}/daemon"
|
||||||
|
|
||||||
|
# ── Data subdirs ──────────────────────────────────────
|
||||||
_CTX_RULES="${_CTX_DATA}/rules"
|
_CTX_RULES="${_CTX_DATA}/rules"
|
||||||
_CTX_RULES_BASE="${_CTX_RULES}/base"
|
_CTX_RULES_BASE="${_CTX_RULES}/base"
|
||||||
_CTX_GROUPS="${_CTX_DATA}/groups"
|
_CTX_GROUPS="${_CTX_DATA}/groups"
|
||||||
_CTX_BLOCKS="${_CTX_DATA}/blocks"
|
_CTX_BLOCKS="${_CTX_DATA}/blocks"
|
||||||
_CTX_META="${_CTX_DATA}/meta"
|
_CTX_META="${_CTX_DATA}/meta"
|
||||||
_CTX_IDENTITY="${_CTX_DATA}/identities"
|
_CTX_IDENTITY="${_CTX_DATA}/identities"
|
||||||
_CTX_DAEMON="${_CTX_DATA}/daemon"
|
_CTX_PEER_HISTORY="${_CTX_DATA}/peer-history"
|
||||||
_CTX_NET="${_CTX_DATA}/services.json"
|
|
||||||
|
|
||||||
|
# ── Data files ────────────────────────────────────────
|
||||||
|
_CTX_NET="${_CTX_DATA}/services.json"
|
||||||
|
_CTX_HOSTS="${_CTX_DATA}/hosts.json"
|
||||||
|
_CTX_SUBNETS="${_CTX_DATA}/subnets.json"
|
||||||
|
_CTX_POLICIES="${_CTX_DATA}/policies.json"
|
||||||
|
|
||||||
|
# ── Config files ──────────────────────────────────────
|
||||||
|
_CTX_CONFIG_FILE="${_CTX_CONFIG}/wgctl.json"
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Accessors
|
||||||
# ============================================
|
# ============================================
|
||||||
|
|
||||||
function ctx::root() { echo "$_CTX_ROOT"; }
|
function ctx::root() { echo "$_CTX_ROOT"; }
|
||||||
function ctx::core() { echo "$_CTX_CORE"; }
|
function ctx::core() { echo "$_CTX_CORE"; }
|
||||||
function ctx::modules() { echo "$_CTX_MODULES"; }
|
function ctx::modules() { echo "$_CTX_MODULES"; }
|
||||||
function ctx::commands() { echo "$_CTX_COMMANDS"; }
|
function ctx::commands() { echo "$_CTX_COMMANDS"; }
|
||||||
function ctx::blocks() { echo "$_CTX_BLOCKS"; }
|
function ctx::wg() { echo "$_CTX_WG"; }
|
||||||
function ctx::groups() { echo "$_CTX_GROUPS"; }
|
function ctx::clients() { echo "$_CTX_CLIENTS"; }
|
||||||
|
|
||||||
|
# Top-level dirs
|
||||||
|
function ctx::wgctl() { echo "$_CTX_WGCTL"; }
|
||||||
|
function ctx::config() { echo "$_CTX_CONFIG"; }
|
||||||
|
function ctx::data() { echo "$_CTX_DATA"; }
|
||||||
|
function ctx::daemon() { echo "$_CTX_DAEMON"; }
|
||||||
|
|
||||||
|
# Data subdirs
|
||||||
function ctx::rules() { echo "$_CTX_RULES"; }
|
function ctx::rules() { echo "$_CTX_RULES"; }
|
||||||
function ctx::rules::base() { echo "$_CTX_RULES_BASE"; }
|
function ctx::rules::base() { echo "$_CTX_RULES_BASE"; }
|
||||||
function ctx::clients() { echo "$_CTX_CLIENTS"; }
|
|
||||||
function ctx::wg() { echo "$_CTX_WG"; }
|
|
||||||
function ctx::data() { echo "$_CTX_DATA"; }
|
|
||||||
function ctx::rules() { echo "$_CTX_RULES"; }
|
|
||||||
function ctx::groups() { echo "$_CTX_GROUPS"; }
|
function ctx::groups() { echo "$_CTX_GROUPS"; }
|
||||||
function ctx::blocks() { echo "$_CTX_BLOCKS"; }
|
function ctx::blocks() { echo "$_CTX_BLOCKS"; }
|
||||||
function ctx::meta() { echo "$_CTX_META"; }
|
function ctx::meta() { echo "$_CTX_META"; }
|
||||||
function ctx::daemon() { echo "$_CTX_DAEMON"; }
|
function ctx::identities() { echo "$_CTX_IDENTITY"; }
|
||||||
|
function ctx::peer_history() { echo "$_CTX_PEER_HISTORY"; }
|
||||||
|
|
||||||
|
# Data files
|
||||||
function ctx::net() { echo "$_CTX_NET"; }
|
function ctx::net() { echo "$_CTX_NET"; }
|
||||||
function ctx::identities() { echo "${_CTX_IDENTITY}"; }
|
function ctx::hosts() { echo "$_CTX_HOSTS"; }
|
||||||
function ctx::policies() { echo "${_CTX_DATA}/policies.json"; }
|
function ctx::subnets() { echo "$_CTX_SUBNETS"; }
|
||||||
function ctx::subnets() { echo "${_CTX_DATA}/subnets.json"; }
|
function ctx::policies() { echo "$_CTX_POLICIES"; }
|
||||||
function ctx::hosts() { echo "${_CTX_DATA}/hosts.json"; }
|
|
||||||
function ctx::events_log() { echo "$(ctx::daemon)/events.log"; }
|
# Config files
|
||||||
function ctx::fw_events_log() { echo "$(ctx::daemon)/fw_events.log"; }
|
function ctx::config_file() { echo "$_CTX_CONFIG_FILE"; }
|
||||||
|
|
||||||
|
# Daemon files
|
||||||
|
function ctx::events_log() { echo "${_CTX_DAEMON}/events.log"; }
|
||||||
|
function ctx::fw_events_log() { echo "${_CTX_DAEMON}/fw_events.log"; }
|
||||||
|
function ctx::endpoint_cache() { echo "${_CTX_DAEMON}/endpoint_cache.json"; }
|
||||||
|
|
||||||
|
# Tool paths
|
||||||
function ctx::json_helper() { echo "${_CTX_CORE}/json_helper.py"; }
|
function ctx::json_helper() { echo "${_CTX_CORE}/json_helper.py"; }
|
||||||
function ctx::monitor_script() { echo "${_CTX_ROOT}/daemon/wgctl-monitor.py"; }
|
function ctx::monitor_script() { echo "${_CTX_ROOT}/daemon/wgctl-monitor.py"; }
|
||||||
function ctx::endpoint_cache() { echo "${_CTX_DAEMON}/endpoint_cache.json"; }
|
function ctx::lib() { echo "${_CTX_CORE}/lib"; }
|
||||||
|
|
||||||
# ============================================
|
# ============================================
|
||||||
# Path Helpers
|
# Path Helpers
|
||||||
# ============================================
|
# ============================================
|
||||||
|
|
||||||
function ctx::client::path() {
|
function ctx::client::path() { local IFS="/"; echo "$_CTX_CLIENTS/$*"; }
|
||||||
local IFS="/"
|
function ctx::meta::path() { local IFS="/"; echo "$_CTX_META/$*"; }
|
||||||
echo "$_CTX_CLIENTS/$*"
|
function ctx::identity::path() { local IFS="/"; echo "$_CTX_IDENTITY/$*"; }
|
||||||
}
|
function ctx::block::path() { local IFS="/"; echo "$_CTX_BLOCKS/$*"; }
|
||||||
|
function ctx::group::path() { local IFS="/"; echo "$_CTX_GROUPS/$*"; }
|
||||||
function ctx::meta::path() {
|
function ctx::rule::path() { local IFS="/"; echo "$_CTX_RULES/$*"; }
|
||||||
local IFS="/"
|
|
||||||
echo "$_CTX_META/$*"
|
|
||||||
}
|
|
||||||
|
|
||||||
function ctx::identity::path() {
|
|
||||||
local IFS="/"
|
|
||||||
echo "$_CTX_IDENTITY/$*"
|
|
||||||
}
|
|
||||||
|
|
||||||
function ctx::block::path() {
|
|
||||||
local IFS="/"
|
|
||||||
echo "$_CTX_BLOCKS/$*"
|
|
||||||
}
|
|
||||||
|
|
||||||
function ctx::group::path() {
|
|
||||||
local IFS="/"
|
|
||||||
echo "$_CTX_GROUPS/$*"
|
|
||||||
}
|
|
||||||
|
|
||||||
function ctx::rule::path() {
|
|
||||||
local IFS="/"
|
|
||||||
echo "$_CTX_RULES/$*"
|
|
||||||
}
|
|
||||||
|
|
@ -144,6 +144,10 @@ function json::error_envelope() {
|
||||||
"$command" "$error" "$ts"
|
"$command" "$error" "$ts"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Config
|
||||||
|
|
||||||
|
function json::config_load() { python3 "$JSON_HELPER" config_load "$1" </dev/null; }
|
||||||
|
|
||||||
function json::peer_transfer() {
|
function json::peer_transfer() {
|
||||||
ACTIVITY_TOTAL_LOW="$(config::activity_total_low)" \
|
ACTIVITY_TOTAL_LOW="$(config::activity_total_low)" \
|
||||||
ACTIVITY_TOTAL_MED="$(config::activity_total_med)" \
|
ACTIVITY_TOTAL_MED="$(config::activity_total_med)" \
|
||||||
|
|
|
||||||
|
|
@ -1564,6 +1564,53 @@ def peer_history_lookup(history_dir, ip):
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def config_load(file):
|
||||||
|
"""
|
||||||
|
Load wgctl.json and output KEY=value pairs for all config fields.
|
||||||
|
Automatically handles nested sections — add fields to JSON, they appear here.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
if not os.path.exists(file):
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
with open(file) as f:
|
||||||
|
d = json.load(f)
|
||||||
|
|
||||||
|
def emit(key, val):
|
||||||
|
if val is not None and val != '':
|
||||||
|
print(f"{key}={val}")
|
||||||
|
|
||||||
|
wg = d.get('wireguard', {})
|
||||||
|
dns = d.get('dns', {})
|
||||||
|
hs = d.get('handshake', {})
|
||||||
|
act = d.get('activity', {})
|
||||||
|
dis = d.get('display', {})
|
||||||
|
|
||||||
|
emit('WG_INTERFACE', wg.get('interface'))
|
||||||
|
emit('WG_ENDPOINT', wg.get('endpoint'))
|
||||||
|
emit('WG_PORT', wg.get('port'))
|
||||||
|
emit('WG_SUBNET', wg.get('subnet'))
|
||||||
|
emit('WG_LAN', wg.get('lan'))
|
||||||
|
emit('WG_DNS', dns.get('primary'))
|
||||||
|
# fallback: join array to comma-separated string
|
||||||
|
fb = dns.get('fallback', [])
|
||||||
|
if fb: emit('WG_DNS_FALLBACK', ', '.join(str(x) for x in fb))
|
||||||
|
emit('WG_HANDSHAKE_CHECK_TIME_SEC', hs.get('check_interval_sec'))
|
||||||
|
emit('DATE_FORMAT', dis.get('date_format'))
|
||||||
|
|
||||||
|
atot = act.get('total', {})
|
||||||
|
acur = act.get('current', {})
|
||||||
|
emit('ACTIVITY_TOTAL_LOW_BYTES', atot.get('low'))
|
||||||
|
emit('ACTIVITY_TOTAL_MED_BYTES', atot.get('medium'))
|
||||||
|
emit('ACTIVITY_TOTAL_HIGH_BYTES', atot.get('high'))
|
||||||
|
emit('ACTIVITY_CURRENT_LOW_BYTES', acur.get('low'))
|
||||||
|
emit('ACTIVITY_CURRENT_MED_BYTES', acur.get('medium'))
|
||||||
|
emit('ACTIVITY_CURRENT_HIGH_BYTES', acur.get('high'))
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error: {e}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
# ======================================================
|
# ======================================================
|
||||||
|
|
||||||
def _net_read(file):
|
def _net_read(file):
|
||||||
|
|
@ -1933,6 +1980,7 @@ commands = {
|
||||||
'clean_handshakes': lambda args: clean_handshakes(args[0], args[1] if len(args) > 1 else '300'),
|
'clean_handshakes': lambda args: clean_handshakes(args[0], args[1] if len(args) > 1 else '300'),
|
||||||
'batch_resolve': lambda args: batch_resolve(args[0], args[1], *args[2:]),
|
'batch_resolve': lambda args: batch_resolve(args[0], args[1], *args[2:]),
|
||||||
'peer_history_lookup': lambda args: peer_history_lookup(args[0], args[1]),
|
'peer_history_lookup': lambda args: peer_history_lookup(args[0], args[1]),
|
||||||
|
'config_load': lambda args: config_load(args[0]),
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Main ─────────────────────────────────────────────────────────────────────
|
# ── Main ─────────────────────────────────────────────────────────────────────
|
||||||
|
|
|
||||||
|
|
@ -8,32 +8,30 @@ function config::on_load() {
|
||||||
config::_init_defaults
|
config::_init_defaults
|
||||||
config::load
|
config::load
|
||||||
config::validate
|
config::validate
|
||||||
fmt::set_date_format "${_FMT_DATE_FORMAT:-iso}"
|
fmt::set_date_format "${_FMT_DATE_FORMAT:-eu}"
|
||||||
}
|
}
|
||||||
|
|
||||||
# ============================================
|
# ============================================
|
||||||
# Defaults
|
# Defaults
|
||||||
# ============================================
|
# ============================================
|
||||||
|
|
||||||
# Activity thresholds
|
|
||||||
declare -g _ACTIVITY_TOTAL_LOW_BYTES="${ACTIVITY_TOTAL_LOW_BYTES:-1000000}"
|
declare -g _ACTIVITY_TOTAL_LOW_BYTES="${ACTIVITY_TOTAL_LOW_BYTES:-1000000}"
|
||||||
declare -g _ACTIVITY_TOTAL_MED_BYTES="${ACTIVITY_TOTAL_MED_BYTES:-10000000}"
|
declare -g _ACTIVITY_TOTAL_MED_BYTES="${ACTIVITY_TOTAL_MED_BYTES:-10000000}"
|
||||||
declare -g _ACTIVITY_TOTAL_HIGH_BYTES="${ACTIVITY_TOTAL_HIGH_BYTES:-100000000}"
|
declare -g _ACTIVITY_TOTAL_HIGH_BYTES="${ACTIVITY_TOTAL_HIGH_BYTES:-100000000}"
|
||||||
|
|
||||||
declare -g _ACTIVITY_CURRENT_LOW_BYTES="${ACTIVITY_CURRENT_LOW_BYTES:-1000000}"
|
declare -g _ACTIVITY_CURRENT_LOW_BYTES="${ACTIVITY_CURRENT_LOW_BYTES:-1000000}"
|
||||||
declare -g _ACTIVITY_CURRENT_MED_BYTES="${ACTIVITY_CURRENT_MED_BYTES:-10000000}"
|
declare -g _ACTIVITY_CURRENT_MED_BYTES="${ACTIVITY_CURRENT_MED_BYTES:-10000000}"
|
||||||
declare -g _ACTIVITY_CURRENT_HIGH_BYTES="${ACTIVITY_CURRENT_HIGH_BYTES:-100000000}"
|
declare -g _ACTIVITY_CURRENT_HIGH_BYTES="${ACTIVITY_CURRENT_HIGH_BYTES:-100000000}"
|
||||||
|
|
||||||
|
|
||||||
function config::_init_defaults() {
|
function config::_init_defaults() {
|
||||||
_WG_INTERFACE="${WG_INTERFACE:-wg0}"
|
_WG_INTERFACE="wg0"
|
||||||
_WG_DNS="${WG_DNS:-10.0.0.103}"
|
_WG_DNS="10.0.0.103"
|
||||||
_WG_DNS_FALLBACK="${WG_DNS_FALLBACK:-}"
|
_WG_DNS_FALLBACK=""
|
||||||
_WG_LAN="${WG_LAN:-10.0.0.0/24}"
|
_WG_LAN="10.0.0.0/24"
|
||||||
_WG_SUBNET="${WG_SUBNET:-10.1.0.0/16}"
|
_WG_SUBNET="10.1.0.0/16"
|
||||||
_WG_PORT="${WG_PORT:-51820}"
|
_WG_PORT="51820"
|
||||||
_WG_ENDPOINT="${WG_ENDPOINT:-}"
|
_WG_ENDPOINT=""
|
||||||
_WG_HANDSHAKE_CHECK_TIME_SEC="${WG_HANDSHAKE_CHECK_TIME_SEC:-180}"
|
_WG_HANDSHAKE_CHECK_TIME_SEC="300"
|
||||||
|
_FMT_DATE_FORMAT="eu"
|
||||||
|
|
||||||
# Derived
|
# Derived
|
||||||
_WG_CONFIG="$(ctx::wg)/${_WG_INTERFACE}.conf"
|
_WG_CONFIG="$(ctx::wg)/${_WG_INTERFACE}.conf"
|
||||||
|
|
@ -44,13 +42,89 @@ function config::_init_defaults() {
|
||||||
}
|
}
|
||||||
|
|
||||||
# ============================================
|
# ============================================
|
||||||
# Validation
|
# Load from wgctl.json
|
||||||
|
# Falls back to wgctl.conf for migration period
|
||||||
|
# ============================================
|
||||||
|
|
||||||
|
function config::load() {
|
||||||
|
local json_conf
|
||||||
|
json_conf="$(ctx::config_file)"
|
||||||
|
|
||||||
|
if [[ -f "$json_conf" ]]; then
|
||||||
|
config::_load_json "$json_conf"
|
||||||
|
else
|
||||||
|
# Fallback: legacy wgctl.conf
|
||||||
|
local legacy_conf
|
||||||
|
legacy_conf="$(ctx::wgctl)/wgctl.conf"
|
||||||
|
[[ -f "$legacy_conf" ]] && config::_load_legacy "$legacy_conf"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Recompute derived values after overrides
|
||||||
|
_WG_CONFIG="$(ctx::wg)/${_WG_INTERFACE}.conf"
|
||||||
|
_WG_TUNNEL_SPLIT="${_WG_SUBNET}, ${_WG_LAN}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function config::_load_json() {
|
||||||
|
local file="$1"
|
||||||
|
[[ ! -f "$file" ]] && return 0
|
||||||
|
|
||||||
|
while IFS='=' read -r key value; do
|
||||||
|
[[ -z "$key" ]] && continue
|
||||||
|
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_HANDSHAKE_CHECK_TIME_SEC="$value" ;;
|
||||||
|
DATE_FORMAT)
|
||||||
|
_FMT_DATE_FORMAT="$value"
|
||||||
|
fmt::set_date_format "$value"
|
||||||
|
;;
|
||||||
|
ACTIVITY_TOTAL_LOW_BYTES) _ACTIVITY_TOTAL_LOW_BYTES="$value" ;;
|
||||||
|
ACTIVITY_TOTAL_MED_BYTES) _ACTIVITY_TOTAL_MED_BYTES="$value" ;;
|
||||||
|
ACTIVITY_TOTAL_HIGH_BYTES) _ACTIVITY_TOTAL_HIGH_BYTES="$value" ;;
|
||||||
|
ACTIVITY_CURRENT_LOW_BYTES) _ACTIVITY_CURRENT_LOW_BYTES="$value" ;;
|
||||||
|
ACTIVITY_CURRENT_MED_BYTES) _ACTIVITY_CURRENT_MED_BYTES="$value" ;;
|
||||||
|
ACTIVITY_CURRENT_HIGH_BYTES) _ACTIVITY_CURRENT_HIGH_BYTES="$value" ;;
|
||||||
|
esac
|
||||||
|
done < <(json::config_load "$file" 2>/dev/null)
|
||||||
|
}
|
||||||
|
|
||||||
|
function config::_load_legacy() {
|
||||||
|
local conf_file="$1"
|
||||||
|
log::wg_warning "Using legacy wgctl.conf — run 'wgctl config migrate' to upgrade"
|
||||||
|
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_HANDSHAKE_CHECK_TIME_SEC="$value" ;;
|
||||||
|
DATE_FORMAT)
|
||||||
|
_FMT_DATE_FORMAT="$value"
|
||||||
|
fmt::set_date_format "$value"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done < "$conf_file"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Validation (unchanged)
|
||||||
# ============================================
|
# ============================================
|
||||||
|
|
||||||
function config::validate() {
|
function config::validate() {
|
||||||
local errors=()
|
local errors=()
|
||||||
|
|
||||||
# Server key and config files
|
|
||||||
if [[ ! -f "$_WG_SERVER_PUBLIC_KEY_FILE" ]]; then
|
if [[ ! -f "$_WG_SERVER_PUBLIC_KEY_FILE" ]]; then
|
||||||
errors+=("Server public key not found: ${_WG_SERVER_PUBLIC_KEY_FILE}")
|
errors+=("Server public key not found: ${_WG_SERVER_PUBLIC_KEY_FILE}")
|
||||||
fi
|
fi
|
||||||
|
|
@ -61,7 +135,6 @@ function config::validate() {
|
||||||
errors+=("WireGuard config not found: ${_WG_CONFIG}")
|
errors+=("WireGuard config not found: ${_WG_CONFIG}")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Required config values
|
|
||||||
local endpoint
|
local endpoint
|
||||||
endpoint=$(config::endpoint)
|
endpoint=$(config::endpoint)
|
||||||
if [[ -z "$endpoint" ]]; then
|
if [[ -z "$endpoint" ]]; then
|
||||||
|
|
@ -92,7 +165,6 @@ function config::validate() {
|
||||||
errors+=("WG_SUBNET is not set — required for IP allocation")
|
errors+=("WG_SUBNET is not set — required for IP allocation")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Warn-only
|
|
||||||
local lan
|
local lan
|
||||||
lan=$(config::lan)
|
lan=$(config::lan)
|
||||||
if [[ -z "$lan" ]]; then
|
if [[ -z "$lan" ]]; then
|
||||||
|
|
@ -104,7 +176,7 @@ function config::validate() {
|
||||||
for err in "${errors[@]}"; do
|
for err in "${errors[@]}"; do
|
||||||
printf " ✗ %s\n" "$err" >&2
|
printf " ✗ %s\n" "$err" >&2
|
||||||
done
|
done
|
||||||
printf "\n Edit /etc/wireguard/.wgctl/wgctl.conf to fix these issues.\n\n" >&2
|
printf "\n Edit %s to fix these issues.\n\n" "$(ctx::config_file)" >&2
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
@ -112,44 +184,7 @@ function config::validate() {
|
||||||
}
|
}
|
||||||
|
|
||||||
# ============================================
|
# ============================================
|
||||||
# Load overrides from .wgctl/wgctl.conf
|
# Accessors (unchanged)
|
||||||
# ============================================
|
|
||||||
|
|
||||||
function config::load() {
|
|
||||||
local conf_file
|
|
||||||
conf_file="$(ctx::data)/wgctl.conf"
|
|
||||||
[[ ! -f "$conf_file" ]] && return 0
|
|
||||||
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_HANDSHAKE_CHECK_TIME_SEC="$value" ;;
|
|
||||||
ACTIVITY_LOW_BYTES) _ACTIVITY_LOW_BYTES="$value" ;;
|
|
||||||
ACTIVITY_MED_BYTES) _ACTIVITY_MED_BYTES="$value" ;;
|
|
||||||
ACTIVITY_HIGH_BYTES) _ACTIVITY_HIGH_BYTES="$value" ;;
|
|
||||||
DATE_FORMAT)
|
|
||||||
_FMT_DATE_FORMAT="$value"
|
|
||||||
fmt::set_date_format "$value"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done < "$conf_file"
|
|
||||||
|
|
||||||
# Recompute derived values after overrides
|
|
||||||
_WG_CONFIG="$(ctx::wg)/${_WG_INTERFACE}.conf"
|
|
||||||
_WG_TUNNEL_SPLIT="${_WG_SUBNET}, ${_WG_LAN}"
|
|
||||||
}
|
|
||||||
|
|
||||||
# ============================================
|
|
||||||
# Accessors
|
|
||||||
# ============================================
|
# ============================================
|
||||||
|
|
||||||
function config::interface() { echo "$_WG_INTERFACE"; }
|
function config::interface() { echo "$_WG_INTERFACE"; }
|
||||||
|
|
@ -166,9 +201,9 @@ function config::handshake_time_sec() { echo "$_WG_HANDSHAKE_CHECK_TIME_SEC"
|
||||||
function config::activity_total_low() { echo "$_ACTIVITY_TOTAL_LOW_BYTES"; }
|
function config::activity_total_low() { echo "$_ACTIVITY_TOTAL_LOW_BYTES"; }
|
||||||
function config::activity_total_med() { echo "$_ACTIVITY_TOTAL_MED_BYTES"; }
|
function config::activity_total_med() { echo "$_ACTIVITY_TOTAL_MED_BYTES"; }
|
||||||
function config::activity_total_high() { echo "$_ACTIVITY_TOTAL_HIGH_BYTES"; }
|
function config::activity_total_high() { echo "$_ACTIVITY_TOTAL_HIGH_BYTES"; }
|
||||||
function config::activity_current_low() { echo "$_ACTIVITY_TOTAL_LOW_BYTES"; }
|
function config::activity_current_low() { echo "$_ACTIVITY_CURRENT_LOW_BYTES"; }
|
||||||
function config::activity_current_med() { echo "$_ACTIVITY_TOTAL_MED_BYTES"; }
|
function config::activity_current_med() { echo "$_ACTIVITY_CURRENT_MED_BYTES"; }
|
||||||
function config::activity_current_high() { echo "$_ACTIVITY_TOTAL_HIGH_BYTES"; }
|
function config::activity_current_high() { echo "$_ACTIVITY_CURRENT_HIGH_BYTES"; }
|
||||||
function config::server_public_key() { cat "$_WG_SERVER_PUBLIC_KEY_FILE"; }
|
function config::server_public_key() { cat "$_WG_SERVER_PUBLIC_KEY_FILE"; }
|
||||||
|
|
||||||
function config::allowed_ips_for() {
|
function config::allowed_ips_for() {
|
||||||
|
|
@ -192,4 +227,3 @@ function config::dns_string() {
|
||||||
echo "$(config::dns)"
|
echo "$(config::dns)"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
Loading…
Add table
Reference in a new issue