# Security update management via .sec files # Each function returns a bash command string that app.py executes via ssh_run() SEC_DIR = "/var/www/updates.seteclabs.io/sec" def os_detect_cmd(): """Return bash cmd to detect OS distro and version.""" return ( "echo '=== OS Detection ===' && " "if [ -f /etc/os-release ]; then " " . /etc/os-release && " " echo \"DISTRO_ID=$ID\" && " " echo \"DISTRO_VERSION=$VERSION_ID\" && " " echo \"DISTRO_CODENAME=$VERSION_CODENAME\" && " " echo \"DISTRO_NAME=$PRETTY_NAME\"; " "else " " echo 'ERROR: /etc/os-release not found'; " "fi && " "echo \"KERNEL=$(uname -r)\" && " "echo \"ARCH=$(dpkg --print-architecture 2>/dev/null || uname -m)\"" ) def list_available_cmd(): """Return bash cmd to list all available .sec files on the update server.""" return ( f"echo '=== Available Security Updates ===' && " f"ls -1 {SEC_DIR}/*.sec 2>/dev/null | while read f; do " f" name=$(basename \"$f\"); " f" desc=$(head -5 \"$f\" | grep '^# Setec Labs' | sed 's/^# //'); " f" target=$(head -5 \"$f\" | grep '^# Target:' | sed 's/^# Target: //'); " f" echo \"$name | $target | $desc\"; " f"done || echo 'No .sec files found in {SEC_DIR}'" ) def check_updates_cmd(distro_id, version_id): """Return bash cmd to find .sec files matching a distro+version prefix.""" # Construct prefix: e.g. ubuntu + 22.04 -> ubuntu2204_ ver_clean = version_id.replace(".", "") prefix = f"{distro_id}{ver_clean}_" return ( f"echo '=== Updates for {distro_id} {version_id} ===' && " f"ls -1 {SEC_DIR}/{prefix}*.sec 2>/dev/null | while read f; do " f" name=$(basename \"$f\"); " f" echo \"$name\"; " f"done || echo 'No updates found for prefix: {prefix}'" ) def download_update_cmd(filename): """Return bash cmd to show the contents of a .sec file (it's already local on VPS).""" if ".." in filename or "/" in filename: return "echo 'ERROR: invalid filename'" return f"cat {SEC_DIR}/{filename} 2>&1" def preview_update_cmd(filename): """Return bash cmd to preview what a .sec file will do without applying it.""" if ".." in filename or "/" in filename: return "echo 'ERROR: invalid filename'" path = f"{SEC_DIR}/{filename}" return ( f"echo '=== Preview: {filename} ===' && " f"if [ ! -f '{path}' ]; then echo 'File not found: {filename}'; exit 1; fi && " f"echo '' && echo '--- [packages] ---' && " f"sed -n '/^\\[packages\\]/,/^\\[/{{ /^\\[/!p }}' '{path}' | grep -v '^#' | grep -v '^$' && " f"echo '' && echo '--- [sysctl] ---' && " f"sed -n '/^\\[sysctl\\]/,/^\\[/{{ /^\\[/!p }}' '{path}' | grep -v '^#' | grep -v '^$' && " f"echo '' && echo '--- [services] ---' && " f"sed -n '/^\\[services\\]/,/^\\[/{{ /^\\[/!p }}' '{path}' | grep -v '^#' | grep -v '^$' && " f"echo '' && echo '--- [files] ---' && " f"sed -n '/^\\[files\\]/,/^\\[/{{ /^\\[/!p }}' '{path}' | grep -v '^#' | grep -v '^$' && " f"echo '' && echo '--- [custom] ---' && " f"sed -n '/^\\[custom\\]/,/^\\[/{{ /^\\[/!p }}' '{path}' | grep -v '^#' | grep -v '^$' && " f"echo '' && echo '=== End Preview ==='" ) def parse_and_apply_cmd(filename): """Return bash cmd to parse a .sec file and apply all sections.""" if ".." in filename or "/" in filename: return "echo 'ERROR: invalid filename'" path = f"{SEC_DIR}/{filename}" return ( f"if [ ! -f '{path}' ]; then echo 'File not found: {filename}'; exit 1; fi && " f"echo '=== Applying: {filename} ===' && " f"echo \"Started: $(date)\" && " f"echo '' && " # Log the apply f"echo \"$(date '+%Y-%m-%d %H:%M:%S') APPLY {filename}\" >> /var/log/setec-updates.log && " # [packages] section — run each line as a command f"echo '>>> [packages]' && " f"sed -n '/^\\[packages\\]/,/^\\[/{{ /^\\[/!p }}' '{path}' | grep -v '^#' | grep -v '^$' | " f"while IFS= read -r cmd; do " f" echo \"+ $cmd\" && " f" eval \"DEBIAN_FRONTEND=noninteractive $cmd\" 2>&1; " f"done && " # [sysctl] section — write to conf file, then apply f"echo '' && echo '>>> [sysctl]' && " f"sed -n '/^\\[sysctl\\]/,/^\\[/{{ /^\\[/!p }}' '{path}' | grep -v '^#' | grep -v '^$' > /etc/sysctl.d/99-setec-update.conf && " f"sysctl -p /etc/sysctl.d/99-setec-update.conf 2>&1 && " # [services] section — run each line f"echo '' && echo '>>> [services]' && " f"sed -n '/^\\[services\\]/,/^\\[/{{ /^\\[/!p }}' '{path}' | grep -v '^#' | grep -v '^$' | " f"while IFS= read -r cmd; do " f" echo \"+ $cmd\" && " f" eval \"$cmd\" 2>&1; " f"done && " # [files] section — run each line f"echo '' && echo '>>> [files]' && " f"sed -n '/^\\[files\\]/,/^\\[/{{ /^\\[/!p }}' '{path}' | grep -v '^#' | grep -v '^$' | " f"while IFS= read -r cmd; do " f" echo \"+ $cmd\" && " f" eval \"$cmd\" 2>&1; " f"done && " # [custom] section — strip bash:-: prefix, run each line f"echo '' && echo '>>> [custom]' && " f"sed -n '/^\\[custom\\]/,/^\\[/{{ /^\\[/!p }}' '{path}' | grep -v '^#' | grep -v '^$' | " f"sed 's/^bash:-://' | " f"while IFS= read -r cmd; do " f" echo \"+ $cmd\" && " f" eval \"$cmd\" 2>&1; " f"done && " f"echo '' && echo \"Completed: $(date)\" && " f"echo \"$(date '+%Y-%m-%d %H:%M:%S') DONE {filename}\" >> /var/log/setec-updates.log && " f"echo '=== Update Applied Successfully ==='" ) def update_history_cmd(): """Return bash cmd to show the update application history.""" return ( "echo '=== Setec Update History ===' && " "if [ -f /var/log/setec-updates.log ]; then " " cat /var/log/setec-updates.log; " "else " " echo 'No update history found'; " "fi" )