secubox-openwrt/package/secubox/secubox-master-link/files/usr/lib/secubox/profiles.sh
CyberMind-FR 5fd3ebb17a feat(factory): Add zero-touch auto-provisioning for mesh devices
- Add inventory.sh for hardware inventory collection (MAC, serial, model, CPU, RAM, storage)
- Add profiles.sh for profile management and device matching
- Add default.json profile template for auto-provisioned peers
- Add discovery mode to master-link.sh with pending queue and approval workflow
- Add bulk token generation (up to 100 tokens per batch)
- Enhance 50-secubox-clone-provision with inventory collection and discovery join
- Add 9 new RPCD methods to luci.cloner for factory provisioning
- Fix p2p-mesh.sh to be silent when sourced as library
- Add UCI options: discovery_mode, auto_approve_known, discovery_window, default_profile

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-24 17:58:36 +01:00

223 lines
4.8 KiB
Bash

#!/bin/sh
# SecuBox Profile Management for Factory Provisioning
# Manages configuration profiles for new devices
PROFILE_DIR="/etc/secubox/profiles"
PROFILE_RULES="/etc/secubox/profile-rules.json"
# Initialize profile storage
profiles_init() {
mkdir -p "$PROFILE_DIR"
}
# Match device to profile based on rules
# Priority: MAC prefix > Model > Serial prefix > Default
profile_match() {
local mac="$1"
local model="$2"
local serial="$3"
# If no rules file, return default
[ -f "$PROFILE_RULES" ] || { echo "default"; return; }
local profile
# Check MAC prefix rules (first 3 octets = vendor)
if [ -n "$mac" ]; then
local mac_prefix=$(echo "$mac" | cut -d: -f1-3 | tr '[:lower:]' '[:upper:]')
profile=$(jsonfilter -i "$PROFILE_RULES" -e "@.mac_prefix[\"$mac_prefix\"]" 2>/dev/null)
[ -n "$profile" ] && { echo "$profile"; return; }
fi
# Check model rules
if [ -n "$model" ]; then
profile=$(jsonfilter -i "$PROFILE_RULES" -e "@.model[\"$model\"]" 2>/dev/null)
[ -n "$profile" ] && { echo "$profile"; return; }
fi
# Check serial prefix rules (first 4 chars)
if [ -n "$serial" ]; then
local serial_prefix=$(echo "$serial" | cut -c1-4)
profile=$(jsonfilter -i "$PROFILE_RULES" -e "@.serial_prefix[\"$serial_prefix\"]" 2>/dev/null)
[ -n "$profile" ] && { echo "$profile"; return; }
fi
# Return default profile
jsonfilter -i "$PROFILE_RULES" -e "@.default" 2>/dev/null || echo "default"
}
# Get profile content by name
profile_get() {
local name="$1"
local file="$PROFILE_DIR/${name}.json"
[ -f "$file" ] && cat "$file" || echo "{}"
}
# List all available profiles
profile_list() {
profiles_init
echo "["
local first=1
for f in "$PROFILE_DIR"/*.json; do
[ -f "$f" ] || continue
[ "$first" = "1" ] || echo ","
local name=$(basename "$f" .json)
printf '{"name":"%s","config":' "$name"
cat "$f"
printf '}'
first=0
done
echo "]"
}
# Get profile names only
profile_names() {
profiles_init
for f in "$PROFILE_DIR"/*.json; do
[ -f "$f" ] || continue
basename "$f" .json
done
}
# Create or update a profile
profile_save() {
local name="$1"
local content="$2"
[ -z "$name" ] && return 1
[ -z "$content" ] && return 1
profiles_init
echo "$content" > "$PROFILE_DIR/${name}.json"
logger -t factory "Saved profile: $name"
return 0
}
# Delete a profile
profile_delete() {
local name="$1"
local file="$PROFILE_DIR/${name}.json"
[ -f "$file" ] && rm -f "$file" && return 0
return 1
}
# Apply profile to device (generates UCI commands)
profile_apply() {
local profile_name="$1"
local device_hostname="$2"
local profile
profile=$(profile_get "$profile_name")
[ "$profile" = "{}" ] && { echo "# No profile found: $profile_name"; return 1; }
# Generate UCI commands from profile
echo "# Profile: $profile_name for $device_hostname"
# Apply UCI commands
echo "$profile" | jsonfilter -e '@.uci[*]' 2>/dev/null | while read -r cmd; do
[ -n "$cmd" ] && echo "uci $cmd"
done
echo "uci commit"
}
# Get profile description
profile_description() {
local name="$1"
local file="$PROFILE_DIR/${name}.json"
[ -f "$file" ] || { echo ""; return; }
jsonfilter -i "$file" -e '@.description' 2>/dev/null || echo ""
}
# Validate profile JSON
profile_validate() {
local name="$1"
local file="$PROFILE_DIR/${name}.json"
[ -f "$file" ] || { echo "not_found"; return 1; }
# Check required fields
local has_name=$(jsonfilter -i "$file" -e '@.name' 2>/dev/null)
local has_uci=$(jsonfilter -i "$file" -e '@.uci' 2>/dev/null)
if [ -n "$has_name" ]; then
echo "valid"
return 0
else
echo "missing_name"
return 1
fi
}
# Add rule for profile matching
profile_add_rule() {
local rule_type="$1" # mac_prefix, model, serial_prefix
local key="$2"
local profile="$3"
[ -f "$PROFILE_RULES" ] || echo '{"mac_prefix":{},"model":{},"serial_prefix":{},"default":"default"}' > "$PROFILE_RULES"
# This would need proper JSON manipulation
# Simplified: log what should be added
logger -t factory "Profile rule: $rule_type[$key] = $profile"
return 0
}
# Set default profile
profile_set_default() {
local profile="$1"
[ -f "$PROFILE_RULES" ] || echo '{"mac_prefix":{},"model":{},"serial_prefix":{},"default":"default"}' > "$PROFILE_RULES"
# Update default
local tmp="/tmp/profile_rules_$$.json"
sed "s/\"default\":\"[^\"]*\"/\"default\":\"$profile\"/" "$PROFILE_RULES" > "$tmp"
mv "$tmp" "$PROFILE_RULES"
logger -t factory "Set default profile: $profile"
return 0
}
# CLI interface
case "${1:-}" in
match)
profile_match "$2" "$3" "$4"
;;
get)
profile_get "$2"
;;
list)
profile_list
;;
names)
profile_names
;;
save)
profile_save "$2" "$3"
;;
delete)
profile_delete "$2"
;;
apply)
profile_apply "$2" "$3"
;;
validate)
profile_validate "$2"
;;
set-default)
profile_set_default "$2"
;;
*)
# Sourced as library - do nothing
:
;;
esac