# 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 }