provisioning/taskservs/nushell/default/remote-exec.nu.j2
Jesús Pérez 3c3ef47f7f
feat(taskserv): implement real-time version checking with configurable HTTP client
- Add: GitHub API integration for live version checking in taskserv management
- Add: HTTP client configuration option (http.use_curl) in config.defaults.toml
- Add: Helper function fetch_latest_version with curl/http get support
- Fix: Settings path structure for prov_data_dirpath access pattern
- Remove: Legacy simulation code for version checking
- Update: Core configuration name from "provisioning-system" to "provisioning"
- Clean: Remove obsolete example configs and infrastructure files
2025-09-24 01:55:06 +01:00

262 lines
8.4 KiB
Django/Jinja

# Remote Execution Library for Nushell Infrastructure
# Secure, audited remote script execution capabilities
# Execute a Nushell script remotely with security restrictions
export def nu-remote-exec [
script_path: string # Path to the Nushell script to execute
--readonly(-r) # Force read-only mode
--timeout(-t): int = 300 # Execution timeout in seconds
--audit(-a) # Enable audit logging
] -> record {
# Validate script path
if not ($script_path | path exists) {
return {
success: false
error: $"Script not found: ($script_path)"
output: ""
duration: 0
}
}
# Security checks
let allowed_paths = ($env.NUSHELL_ALLOWED_PATHS? | default "/tmp,/var/log,/proc,/sys" | split row ",")
let script_dir = ($script_path | path dirname)
if not ($allowed_paths | any {|path| $script_dir | str starts-with $path}) {
return {
success: false
error: $"Script path not in allowed directories: ($script_dir)"
output: ""
duration: 0
}
}
# Prepare execution environment
let start_time = (date now)
let session_id = (random uuid)
# Audit logging if enabled
if ($audit or ($env.NUSHELL_AUDIT_ENABLED? | default false)) {
let audit_entry = {
timestamp: ($start_time | format date "%Y-%m-%d %H:%M:%S")
session_id: $session_id
action: "remote-exec"
script: $script_path
readonly: $readonly
user: ($env.USER? | default "unknown")
hostname: ($env.HOSTNAME? | default "unknown")
}
$audit_entry | to json | save -a ($env.NUSHELL_AUDIT_FILE? | default "/tmp/nushell-audit.log")
}
# Build execution command with security flags
mut nu_args = ["--no-config-file"]
if $readonly or ($env.NUSHELL_READONLY_MODE? | default true) {
$nu_args = ($nu_args | append "--no-history")
}
# Set resource limits
let memory_limit = ($env.NUSHELL_MAX_MEMORY? | default "256MB")
let cpu_time = ($env.NUSHELL_MAX_CPU_TIME? | default "30s")
try {
# Execute with timeout and resource limits
let result = (timeout $"($timeout)s" nu ...$nu_args $script_path | complete)
let end_time = (date now)
let duration = (($end_time - $start_time) / 1ms | math round)
# Log completion
if ($audit or ($env.NUSHELL_AUDIT_ENABLED? | default false)) {
let completion_entry = {
timestamp: ($end_time | format date "%Y-%m-%d %H:%M:%S")
session_id: $session_id
action: "remote-exec-complete"
exit_code: $result.exit_code
duration_ms: $duration
output_lines: ($result.stdout | lines | length)
}
$completion_entry | to json | save -a ($env.NUSHELL_AUDIT_FILE? | default "/tmp/nushell-audit.log")
}
return {
success: ($result.exit_code == 0)
error: $result.stderr
output: $result.stdout
duration: $duration
exit_code: $result.exit_code
session_id: $session_id
}
} catch { |err|
let end_time = (date now)
let duration = (($end_time - $start_time) / 1ms | math round)
# Log error
if ($audit or ($env.NUSHELL_AUDIT_ENABLED? | default false)) {
let error_entry = {
timestamp: ($end_time | format date "%Y-%m-%d %H:%M:%S")
session_id: $session_id
action: "remote-exec-error"
error: ($err | get msg)
duration_ms: $duration
}
$error_entry | to json | save -a ($env.NUSHELL_AUDIT_FILE? | default "/tmp/nushell-audit.log")
}
return {
success: false
error: ($err | get msg)
output: ""
duration: $duration
exit_code: 1
session_id: $session_id
}
}
}
# Execute a command pipeline remotely with streaming output
export def nu-remote-stream [
command: string # Command to execute
--filter(-f): string # Optional filter expression
--format: string = "table" # Output format (table, json, yaml)
--lines(-l): int # Limit output lines
] -> any {
# Security validation
let blocked_commands = ($env.NUSHELL_BLOCKED_COMMANDS? | default "" | split row ",")
let cmd_parts = ($command | split row " ")
let cmd_name = ($cmd_parts | first)
if $cmd_name in $blocked_commands {
error make {msg: $"Command '($cmd_name)' is blocked for security reasons"}
}
# Build pipeline
mut pipeline = $command
if ($filter | is-not-empty) {
$pipeline = $"($pipeline) | ($filter)"
}
if ($lines | is-not-empty) {
$pipeline = $"($pipeline) | first ($lines)"
}
# Format output
match $format {
"json" => { $pipeline = $"($pipeline) | to json" }
"yaml" => { $pipeline = $"($pipeline) | to yaml" }
"csv" => { $pipeline = $"($pipeline) | to csv" }
_ => { $pipeline = $"($pipeline) | table" }
}
# Execute with audit
if ($env.NUSHELL_AUDIT_ENABLED? | default false) {
let audit_entry = {
timestamp: (date now | format date "%Y-%m-%d %H:%M:%S")
action: "remote-stream"
command: $command
filter: ($filter | default "")
format: $format
}
$audit_entry | to json | save -a ($env.NUSHELL_AUDIT_FILE? | default "/tmp/nushell-audit.log")
}
# Execute the pipeline
nu -c $pipeline
}
# Validate script security before execution
export def nu-validate-script [
script_path: string
] -> record {
if not ($script_path | path exists) {
return {valid: false, reason: "Script file not found"}
}
let content = (open $script_path)
let blocked_patterns = [
"rm -rf"
"sudo"
"su -"
"chmod 777"
"wget http://"
"curl http://"
"nc -"
"telnet"
"/dev/tcp"
"eval"
"exec"
]
for pattern in $blocked_patterns {
if ($content | str contains $pattern) {
return {
valid: false
reason: $"Script contains blocked pattern: ($pattern)"
}
}
}
# Check for allowed paths only
let allowed_paths = ($env.NUSHELL_ALLOWED_PATHS? | default "/tmp,/var/log,/proc,/sys" | split row ",")
let path_accesses = ($content | find -r "/(etc|root|home|usr/bin)" | length)
if $path_accesses > 0 {
return {
valid: false
reason: "Script accesses restricted system paths"
}
}
return {valid: true, reason: "Script validation passed"}
}
# Health check for remote Nushell environment
export def nu-health-check [] -> record {
let start_time = (date now)
mut health = {
status: "healthy"
checks: {}
timestamp: ($start_time | format date "%Y-%m-%d %H:%M:%S")
}
# Check Nushell version
try {
let version = (version | get version)
$health.checks = ($health.checks | insert nushell_version {status: "ok", value: $version})
} catch {
$health.checks = ($health.checks | insert nushell_version {status: "error", value: "unknown"})
$health.status = "degraded"
}
# Check environment variables
let required_vars = ["NUSHELL_HOME", "NUSHELL_EXECUTION_MODE"]
for var in $required_vars {
if ($env | get -i $var | is-empty) {
$health.checks = ($health.checks | insert $"env_($var)" {status: "error", value: "missing"})
$health.status = "unhealthy"
} else {
$health.checks = ($health.checks | insert $"env_($var)" {status: "ok", value: ($env | get $var)})
}
}
# Check disk space
try {
let disk_usage = (df -h | where Filesystem =~ "/" | first | get "Use%")
$health.checks = ($health.checks | insert disk_usage {status: "ok", value: $disk_usage})
} catch {
$health.checks = ($health.checks | insert disk_usage {status: "error", value: "unknown"})
}
# Check memory usage
try {
let mem_info = (free -m | lines | get 1 | split row " " | where $it != "" | get 2)
$health.checks = ($health.checks | insert memory_mb {status: "ok", value: $mem_info})
} catch {
$health.checks = ($health.checks | insert memory_mb {status: "error", value: "unknown"})
}
return $health
}