#!/bin/sh # RPCD backend for Configuration Vault . /lib/functions.sh . /usr/share/libubox/jshn.sh VAULT_CTL="/usr/sbin/configvaultctl" handle_status() { local enabled vault_path auto_commit auto_push gitea_url gitea_repo json_init config_load config-vault config_get enabled global enabled "0" config_get vault_path global vault_path "/srv/config-vault" config_get auto_commit global auto_commit "1" config_get auto_push global auto_push "1" config_get gitea_url gitea url "" config_get gitea_repo gitea repo "" json_add_boolean enabled "$enabled" json_add_string vault_path "$vault_path" json_add_boolean auto_commit "$auto_commit" json_add_boolean auto_push "$auto_push" json_add_string gitea_url "$gitea_url" json_add_string gitea_repo "$gitea_repo" if [ -d "$vault_path/.git" ]; then cd "$vault_path" json_add_boolean initialized 1 json_add_string branch "$(git branch --show-current 2>/dev/null)" json_add_string last_commit "$(git log -1 --format='%h' 2>/dev/null)" json_add_string last_commit_date "$(git log -1 --format='%ci' 2>/dev/null)" json_add_string last_commit_msg "$(git log -1 --format='%s' 2>/dev/null)" json_add_int uncommitted "$(git status --porcelain 2>/dev/null | wc -l)" json_add_int total_commits "$(git rev-list --count HEAD 2>/dev/null || echo 0)" else json_add_boolean initialized 0 fi json_dump } add_config_json() { local cfg="$1" json_add_object json_add_string name "$cfg" [ -f "/etc/config/$cfg" ] && json_add_boolean exists 1 || json_add_boolean exists 0 json_close_object } list_module_json() { local section="$1" local enabled description files last_backup config_get enabled "$section" enabled "1" config_get description "$section" description "" json_add_object json_add_string name "$section" json_add_string description "$description" json_add_boolean enabled "$enabled" files=0 [ -d "$VAULT_PATH/$section" ] && files=$(find "$VAULT_PATH/$section" -type f 2>/dev/null | wc -l) json_add_int files "$files" last_backup="" [ -f "$VAULT_PATH/$section/manifest.json" ] && { last_backup=$(jsonfilter -i "$VAULT_PATH/$section/manifest.json" -e '@.backed_up' 2>/dev/null) } json_add_string last_backup "$last_backup" json_add_array configs config_list_foreach "$section" config add_config_json json_close_array json_close_object } handle_modules() { json_init json_add_array modules config_load config-vault config_get VAULT_PATH global vault_path "/srv/config-vault" export VAULT_PATH config_foreach list_module_json module json_close_array json_dump } handle_history() { local count vault_path read -r input json_load "$input" json_get_var count count [ -z "$count" ] && count=20 json_init json_add_array commits config_load config-vault config_get vault_path global vault_path "/srv/config-vault" if [ -d "$vault_path/.git" ]; then cd "$vault_path" git log --format='%H|%h|%ci|%s' -n "$count" 2>/dev/null | while IFS='|' read hash short date msg; do json_add_object json_add_string hash "$hash" json_add_string short "$short" json_add_string date "$date" json_add_string message "$msg" json_close_object done fi json_close_array json_dump } handle_diff() { local vault_path diff_output config_load config-vault config_get vault_path global vault_path "/srv/config-vault" json_init if [ -d "$vault_path/.git" ]; then cd "$vault_path" diff_output=$(git diff 2>/dev/null | head -200) json_add_string diff "$diff_output" json_add_int changed_files "$(git status --porcelain 2>/dev/null | wc -l)" else json_add_string diff "" json_add_int changed_files 0 fi json_dump } handle_backup() { local module output rc read -r input json_load "$input" json_get_var module module json_init if [ -n "$module" ]; then output=$($VAULT_CTL backup "$module" 2>&1) else output=$($VAULT_CTL backup 2>&1) fi rc=$? [ $rc -eq 0 ] && json_add_boolean success 1 || json_add_boolean success 0 json_add_string output "$output" json_dump } handle_restore() { local module output rc read -r input json_load "$input" json_get_var module module json_init if [ -z "$module" ]; then json_add_boolean success 0 json_add_string error "Module name required" else output=$($VAULT_CTL restore "$module" 2>&1) rc=$? [ $rc -eq 0 ] && json_add_boolean success 1 || json_add_boolean success 0 json_add_string output "$output" fi json_dump } handle_push() { local output rc json_init output=$($VAULT_CTL push 2>&1) rc=$? [ $rc -eq 0 ] && json_add_boolean success 1 || json_add_boolean success 0 json_add_string output "$output" json_dump } handle_pull() { local output rc json_init output=$($VAULT_CTL pull 2>&1) rc=$? [ $rc -eq 0 ] && json_add_boolean success 1 || json_add_boolean success 0 json_add_string output "$output" json_dump } handle_init() { local output rc json_init output=$($VAULT_CTL init 2>&1) rc=$? [ $rc -eq 0 ] && json_add_boolean success 1 || json_add_boolean success 0 json_add_string output "$output" json_dump } handle_export_clone() { local path output rc read -r input json_load "$input" json_get_var path path [ -z "$path" ] && path="/tmp/secubox-clone-$(date +%Y%m%d).tar.gz" json_init output=$($VAULT_CTL export-clone "$path" 2>&1) rc=$? [ $rc -eq 0 ] && json_add_boolean success 1 || json_add_boolean success 0 json_add_string output "$output" json_add_string path "$path" if [ -f "$path" ]; then json_add_int size "$(stat -c%s "$path" 2>/dev/null || echo 0)" fi json_dump } #------------------------------------------------------------------------------ # Provisioning Methods #------------------------------------------------------------------------------ # Import and auto-apply clone (for remote provisioning) handle_import_apply() { local archive output rc read -r input json_load "$input" json_get_var archive archive json_init if [ -z "$archive" ]; then json_add_boolean success 0 json_add_string error "Archive path required" elif [ ! -f "$archive" ]; then json_add_boolean success 0 json_add_string error "Archive not found: $archive" else output=$($VAULT_CTL import-clone "$archive" --apply 2>&1) rc=$? [ $rc -eq 0 ] && json_add_boolean success 1 || json_add_boolean success 0 json_add_string output "$output" fi json_dump } # Provision remote node handle_provision() { local target clone_file output rc read -r input json_load "$input" json_get_var target target json_get_var clone_file clone_file json_init if [ -z "$target" ]; then json_add_boolean success 0 json_add_string error "Target node required" else output=$($VAULT_CTL provision "$target" "$clone_file" 2>&1) rc=$? [ $rc -eq 0 ] && json_add_boolean success 1 || json_add_boolean success 0 json_add_string output "$output" fi json_dump } # Pull config from master (first-boot) handle_pull_config() { local master apply output rc read -r input json_load "$input" json_get_var master master json_get_var apply apply json_init if [ -z "$master" ]; then json_add_boolean success 0 json_add_string error "Master URL required" else [ "$apply" = "false" ] && apply="--no-apply" || apply="--apply" output=$($VAULT_CTL pull-config "$master" "$apply" 2>&1) rc=$? [ $rc -eq 0 ] && json_add_boolean success 1 || json_add_boolean success 0 json_add_string output "$output" fi json_dump } # Restore all modules handle_restore_all() { local output rc json_init output=$($VAULT_CTL restore-all 2>&1) rc=$? [ $rc -eq 0 ] && json_add_boolean success 1 || json_add_boolean success 0 json_add_string output "$output" json_dump } # Export clone as base64 (for RPC transfer) handle_export_clone_b64() { local path path="/tmp/secubox-clone-rpc-$$.tar.gz" $VAULT_CTL export-clone "$path" >/dev/null 2>&1 json_init if [ -f "$path" ]; then json_add_boolean success 1 json_add_int size "$(stat -c%s "$path" 2>/dev/null || echo 0)" # Stream base64 to avoid memory issues local b64_data=$(base64 -w 0 "$path") json_add_string data "$b64_data" rm -f "$path" else json_add_boolean success 0 json_add_string error "Failed to create clone" fi json_dump } # Serve clone via HTTP (update /www/config-vault/) handle_serve_clone() { local output_dir output rc read -r input json_load "$input" json_get_var output_dir output_dir [ -z "$output_dir" ] && output_dir="/www/config-vault" json_init output=$($VAULT_CTL serve-clone "$output_dir" 2>&1) rc=$? [ $rc -eq 0 ] && json_add_boolean success 1 || json_add_boolean success 0 json_add_string output "$output" json_add_string url "/config-vault/clone.tar.gz" json_dump } case "$1" in list) cat << 'EOF' { "status": {}, "modules": {}, "history": {"count": 20}, "diff": {}, "backup": {"module": "str"}, "restore": {"module": "str"}, "restore_all": {}, "push": {}, "pull": {}, "init": {}, "export_clone": {"path": "str"}, "export_clone_b64": {}, "import_apply": {"archive": "str"}, "provision": {"target": "str", "clone_file": "str"}, "pull_config": {"master": "str", "apply": true}, "serve_clone": {"output_dir": "str"} } EOF ;; call) case "$2" in status) handle_status ;; modules) handle_modules ;; history) handle_history ;; diff) handle_diff ;; backup) handle_backup ;; restore) handle_restore ;; restore_all) handle_restore_all ;; push) handle_push ;; pull) handle_pull ;; init) handle_init ;; export_clone) handle_export_clone ;; export_clone_b64) handle_export_clone_b64 ;; import_apply) handle_import_apply ;; provision) handle_provision ;; pull_config) handle_pull_config ;; serve_clone) handle_serve_clone ;; *) echo '{"error":"Unknown method"}' ;; esac ;; esac