Dernière activité 1 month ago

nuno a révisé ce gist 1 month ago. Aller à la révision

1 file changed, 305 insertions

export.command.sh(fichier créé)

@@ -0,0 +1,305 @@
1 + #!/usr/bin/env bash
2 + # commands/export.command.sh
3 +
4 + function cmd::export::on_load() {
5 + flag::register --peer
6 + flag::register --identity
7 + flag::register --all
8 + flag::register --out
9 + flag::register --conf-only
10 + flag::register --meta-only
11 + flag::register --no-config
12 + flag::register --no-peers
13 + flag::register --force
14 + }
15 +
16 + function cmd::export::help() {
17 + cat <<EOF
18 + Usage: wgctl export [options]
19 +
20 + Export wgctl data as a portable JSON bundle.
21 +
22 + Options:
23 + --peer <name> Export a single peer (conf, meta, groups, identity, blocks)
24 + --identity <name> Export an identity
25 + --all Full backup (all peers, rules, identities, groups, etc.)
26 + --out <file> Write to file instead of stdout
27 + --conf-only Export peer conf only (with --peer)
28 + --meta-only Export peer meta only (with --peer)
29 + --no-config Skip wgctl.json (with --all)
30 + --no-peers Skip peer confs (with --all)
31 + --force Overwrite existing output file
32 +
33 + Examples:
34 + wgctl export --peer phone-nuno
35 + wgctl export --peer phone-nuno --out phone-nuno.json
36 + wgctl export --identity nuno --out nuno.json
37 + wgctl export --all --out backup.json
38 + wgctl export --all --no-config --out data-only.json
39 + EOF
40 + }
41 +
42 + function cmd::export::run() {
43 + local peer="" identity="" all=false out=""
44 + local conf_only=false meta_only=false
45 + local no_config=false no_peers=false force=false
46 +
47 + while [[ $# -gt 0 ]]; do
48 + case "$1" in
49 + --peer) peer="$2"; shift 2 ;;
50 + --identity) identity="$2"; shift 2 ;;
51 + --all) all=true; shift ;;
52 + --out) out="$2"; shift 2 ;;
53 + --conf-only) conf_only=true; shift ;;
54 + --meta-only) meta_only=true; shift ;;
55 + --no-config) no_config=true; shift ;;
56 + --no-peers) no_peers=true; shift ;;
57 + --force) force=true; shift ;;
58 + --help) cmd::export::help; return ;;
59 + *) log::error "Unknown flag: $1"; return 1 ;;
60 + esac
61 + done
62 +
63 + # Validate
64 + local mode_count=0
65 + [[ -n "$peer" ]] && (( mode_count++ )) || true
66 + [[ -n "$identity" ]] && (( mode_count++ )) || true
67 + $all && (( mode_count++ )) || true
68 +
69 + if [[ "$mode_count" -eq 0 ]]; then
70 + log::error "Specify --peer, --identity, or --all"
71 + cmd::export::help
72 + return 1
73 + fi
74 + if [[ "$mode_count" -gt 1 ]]; then
75 + log::error "Only one of --peer, --identity, --all can be used at a time"
76 + return 1
77 + fi
78 +
79 + # Check output file
80 + if [[ -n "$out" && -f "$out" && ! $force ]]; then
81 + log::error "Output file already exists: ${out} (use --force to overwrite)"
82 + return 1
83 + fi
84 +
85 + local json=""
86 + if [[ -n "$peer" ]]; then
87 + json=$(cmd::export::_peer "$peer" "$conf_only" "$meta_only") || return 1
88 + elif [[ -n "$identity" ]]; then
89 + json=$(cmd::export::_identity "$identity") || return 1
90 + elif $all; then
91 + json=$(cmd::export::_full "$no_config" "$no_peers") || return 1
92 + fi
93 +
94 + if [[ -n "$out" ]]; then
95 + echo "$json" > "$out"
96 + log::wg_success "Exported to ${out}"
97 + else
98 + echo "$json"
99 + fi
100 + }
101 +
102 + # ======================================================
103 + # Peer export
104 + # ======================================================
105 +
106 + function cmd::export::_peer() {
107 + local name="${1:-}" conf_only="${2:-false}" meta_only="${3:-false}"
108 +
109 + peers::require_exists "$name" || return 1
110 +
111 + local conf_file
112 + conf_file="$(ctx::clients)/${name}.conf"
113 + [[ ! -f "$conf_file" ]] && log::error "Client conf not found: ${conf_file}" && return 1
114 +
115 + local conf_b64
116 + conf_b64=$(base64 -w 0 < "$conf_file" 2>/dev/null || base64 < "$conf_file")
117 +
118 + if $conf_only; then
119 + cmd::export::_envelope "peer_conf" \
120 + "$(printf '{"name":"%s","conf":"%s"}' "$name" "$conf_b64")"
121 + return 0
122 + fi
123 +
124 + # Meta
125 + local meta_file meta_json="{}"
126 + meta_file="$(ctx::meta)/${name}.meta"
127 + [[ -f "$meta_file" ]] && meta_json=$(cat "$meta_file")
128 +
129 + if $meta_only; then
130 + cmd::export::_envelope "peer_meta" \
131 + "$(printf '{"name":"%s","meta":%s}' "$name" "$meta_json")"
132 + return 0
133 + fi
134 +
135 + # Public key
136 + local public_key=""
137 + local key_file
138 + key_file="$(ctx::clients)/${name}_public.key"
139 + [[ -f "$key_file" ]] && public_key=$(cat "$key_file")
140 +
141 + # IP
142 + local ip
143 + ip=$(peers::get_ip "$name")
144 +
145 + # Type
146 + local peer_type
147 + peer_type=$(peers::get_type "$name" 2>/dev/null || echo "")
148 +
149 + # Direct rule
150 + local direct_rule
151 + direct_rule=$(peers::get_meta "$name" "rule" 2>/dev/null || echo "")
152 +
153 + # Identity
154 + local identity
155 + identity=$(peers::get_identity "$name" 2>/dev/null || echo "")
156 +
157 + # Groups
158 + local -a group_list=()
159 + while IFS= read -r g; do
160 + [[ -n "$g" ]] && group_list+=("\"$g\"")
161 + done < <(json::peer_groups "$(ctx::groups)" "$name" 2>/dev/null)
162 + local groups_json="[]"
163 + [[ ${#group_list[@]} -gt 0 ]] && \
164 + groups_json="[$(printf '%s,' "${group_list[@]}" | sed 's/,$//')]"
165 +
166 + # Blocks
167 + local block_file is_blocked="false" block_json="null"
168 + block_file="$(ctx::blocks)/${name}.block"
169 + if [[ -f "$block_file" ]]; then
170 + is_blocked="true"
171 + block_json=$(base64 -w 0 < "$block_file" 2>/dev/null || base64 < "$block_file")
172 + block_json="\"${block_json}\""
173 + fi
174 +
175 + local peer_data
176 + peer_data=$(printf \
177 + '{"name":"%s","ip":"%s","type":"%s","public_key":"%s","conf":"%s","meta":%s,"identity":"%s","groups":%s,"direct_rule":"%s","blocks":{"is_blocked":%s,"block_file":%s}}' \
178 + "$name" "$ip" "$peer_type" "$public_key" "$conf_b64" \
179 + "$meta_json" "$identity" "$groups_json" "$direct_rule" \
180 + "$is_blocked" "$block_json")
181 +
182 + cmd::export::_envelope "peer" "$peer_data"
183 + }
184 +
185 + # ======================================================
186 + # Identity export
187 + # ======================================================
188 +
189 + function cmd::export::_identity() {
190 + local name="${1:-}"
191 + identity::require_exists "$name" || return 1
192 +
193 + local id_file
194 + id_file="$(ctx::identities)/${name}.identity"
195 + local id_json
196 + id_json=$(cat "$id_file")
197 +
198 + cmd::export::_envelope "identity" \
199 + "$(printf '{"name":"%s","identity":%s}' "$name" "$id_json")"
200 + }
201 +
202 + # ======================================================
203 + # Full backup
204 + # ======================================================
205 +
206 + function cmd::export::_full() {
207 + local no_config="${1:-false}" no_peers="${2:-false}"
208 + local version
209 + version=$(wgctl::version 2>/dev/null || echo "unknown")
210 +
211 + python3 "$(ctx::json_helper)" export_full \
212 + "$(ctx::clients)" \
213 + "$(ctx::meta)" \
214 + "$(ctx::rules)" \
215 + "$(ctx::identities)" \
216 + "$(ctx::groups)" \
217 + "$(ctx::blocks)" \
218 + "$(ctx::block_history)" \
219 + "$(ctx::config_file)" \
220 + "$(ctx::policies)" \
221 + "$(ctx::subnets)" \
222 + "$(ctx::net)" \
223 + "$(ctx::hosts)" \
224 + "$no_config" \
225 + "$no_peers" \
226 + "$version" \
227 + 2>/dev/null
228 + }
229 +
230 + # Helper — peer data without envelope (used by full backup)
231 + function cmd::export::_peer_data() {
232 + local name="${1:-}"
233 + local conf_file
234 + conf_file="$(ctx::clients)/${name}.conf"
235 + [[ ! -f "$conf_file" ]] && return 0
236 +
237 + local conf_b64
238 + conf_b64=$(base64 -w 0 < "$conf_file" 2>/dev/null || base64 < "$conf_file")
239 +
240 + local meta_file meta_json="{}"
241 + meta_file="$(ctx::meta)/${name}.meta"
242 + [[ -f "$meta_file" ]] && meta_json=$(cat "$meta_file")
243 +
244 + local public_key=""
245 + local key_file
246 + key_file="$(ctx::clients)/${name}_public.key"
247 + [[ -f "$key_file" ]] && public_key=$(cat "$key_file")
248 +
249 + local ip
250 + ip=$(peers::get_ip "$name")
251 +
252 + local peer_type
253 + peer_type=$(peers::get_type "$name" 2>/dev/null || echo "")
254 +
255 + local direct_rule
256 + direct_rule=$(peers::get_meta "$name" "rule" 2>/dev/null || echo "")
257 +
258 + local identity
259 + identity=$(peers::get_identity "$name" 2>/dev/null || echo "")
260 +
261 + local -a group_list=()
262 + while IFS= read -r g; do
263 + [[ -n "$g" ]] && group_list+=("\"$g\"")
264 + done < <(json::peer_groups "$(ctx::groups)" "$name" 2>/dev/null)
265 + local groups_json="[]"
266 + [[ ${#group_list[@]} -gt 0 ]] && \
267 + groups_json="[$(printf '%s,' "${group_list[@]}" | sed 's/,$//')]"
268 +
269 + local block_file is_blocked="false" block_json="null"
270 + block_file="$(ctx::blocks)/${name}.block"
271 + if [[ -f "$block_file" ]]; then
272 + is_blocked="true"
273 + block_json="\"$(base64 -w 0 < "$block_file" 2>/dev/null || base64 < "$block_file")\""
274 + fi
275 +
276 + printf \
277 + '{"name":"%s","ip":"%s","type":"%s","public_key":"%s","conf":"%s","meta":%s,"identity":"%s","groups":%s,"direct_rule":"%s","blocks":{"is_blocked":%s,"block_file":%s}}' \
278 + "$name" "$ip" "$peer_type" "$public_key" "$conf_b64" \
279 + "$meta_json" "$identity" "$groups_json" "$direct_rule" \
280 + "$is_blocked" "$block_json"
281 + }
282 +
283 + # ======================================================
284 + # Envelope helper
285 + # ======================================================
286 +
287 + function cmd::export::_envelope() {
288 + local export_type="${1:-}" data="${2:-}"
289 + local version ts
290 + version=$(wgctl::version 2>/dev/null || echo "unknown")
291 + ts=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
292 + printf '{"wgctl_version":"%s","export_type":"%s","exported_at":"%s","data":%s}\n' \
293 + "$version" "$export_type" "$ts" "$data"
294 + }
295 +
296 + function cmd::export::_compact_json() {
297 + local file="$1"
298 + python3 -c "
299 + import json, sys
300 + try:
301 + print(json.dumps(json.load(open('${file}'))))
302 + except Exception as e:
303 + print('{}', file=sys.stderr)
304 + " 2>/dev/null
305 + }
Plus récent Plus ancien