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
This commit is contained in:
parent
38a7470da0
commit
3c3ef47f7f
34 changed files with 5942 additions and 13 deletions
143
taskservs/nushell/default/config.nu.j2
Normal file
143
taskservs/nushell/default/config.nu.j2
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
# Secure Nushell Configuration for Infrastructure Servers
|
||||
# Auto-generated by provisioning system
|
||||
|
||||
# Security-first configuration
|
||||
$env.config = {
|
||||
show_banner: false
|
||||
use_ansi_coloring: true
|
||||
edit_mode: emacs
|
||||
|
||||
# Security settings
|
||||
shell_integration: false
|
||||
cd_with_abbreviations: false
|
||||
filesize_metric: true
|
||||
table_mode: rounded
|
||||
|
||||
# History settings (limited for security)
|
||||
history: {
|
||||
max_size: 1000
|
||||
sync_on_enter: true
|
||||
file_format: "plaintext"
|
||||
isolation: true
|
||||
}
|
||||
|
||||
# Completion settings
|
||||
completions: {
|
||||
case_sensitive: false
|
||||
quick: true
|
||||
partial: true
|
||||
algorithm: "prefix"
|
||||
external: {
|
||||
enable: {% if taskserv.nushell_external_completions | default(false) %}true{% else %}false{% endif %}
|
||||
max_results: 100
|
||||
completer: null
|
||||
}
|
||||
}
|
||||
|
||||
# Performance limits
|
||||
table: {
|
||||
mode: rounded
|
||||
index_mode: always
|
||||
trim: {
|
||||
methodology: wrapping
|
||||
wrapping_try_keep_words: true
|
||||
truncating_suffix: "..."
|
||||
}
|
||||
}
|
||||
|
||||
# Error handling
|
||||
error_style: "fancy"
|
||||
|
||||
# Hooks for security and audit
|
||||
hooks: {
|
||||
pre_prompt: [{
|
||||
condition: {|| true }
|
||||
code: {||
|
||||
# Audit logging
|
||||
if ($env.NUSHELL_AUDIT_ENABLED? | default false) {
|
||||
$"(date now | format date '%Y-%m-%d %H:%M:%S') - Session active" | save -a $env.NUSHELL_AUDIT_FILE
|
||||
}
|
||||
}
|
||||
}]
|
||||
|
||||
pre_execution: [{
|
||||
condition: {|| true }
|
||||
code: {|| |cmd|
|
||||
# Command validation and audit
|
||||
if ($env.NUSHELL_AUDIT_ENABLED? | default false) {
|
||||
$"(date now | format date '%Y-%m-%d %H:%M:%S') - Command: ($cmd)" | save -a $env.NUSHELL_AUDIT_FILE
|
||||
}
|
||||
|
||||
# Security check for blocked commands
|
||||
let blocked = ($env.NUSHELL_BLOCKED_COMMANDS? | default "" | split row ",")
|
||||
let cmd_name = ($cmd | split row " " | first)
|
||||
if $cmd_name in $blocked {
|
||||
error make {msg: $"Command '($cmd_name)' is blocked for security reasons"}
|
||||
}
|
||||
}
|
||||
}]
|
||||
|
||||
command_not_found: [{
|
||||
condition: {|| true }
|
||||
code: {|| |cmd_name|
|
||||
$"Command '($cmd_name)' not found. Available commands are restricted for security."
|
||||
}
|
||||
}]
|
||||
}
|
||||
|
||||
# Menus disabled for security
|
||||
menus: []
|
||||
|
||||
# Keybindings (minimal for security)
|
||||
keybindings: [
|
||||
{
|
||||
name: completion_menu
|
||||
modifier: none
|
||||
keycode: tab
|
||||
mode: [emacs vi_normal vi_insert]
|
||||
event: {
|
||||
until: [
|
||||
{ send: menu name: completion_menu }
|
||||
{ send: menunext }
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
# Security aliases (read-only operations)
|
||||
alias ll = ls -la
|
||||
alias df = df -h
|
||||
alias free = free -h
|
||||
alias pstree = ps aux --forest
|
||||
|
||||
# Restricted environment setup
|
||||
{% if taskserv.nushell_readonly | default(true) %}
|
||||
# Read-only mode - disable write operations
|
||||
def rm [] { error make {msg: "rm command disabled in read-only mode"} }
|
||||
def mv [] { error make {msg: "mv command disabled in read-only mode"} }
|
||||
def cp [] { error make {msg: "cp command disabled in read-only mode"} }
|
||||
def chmod [] { error make {msg: "chmod command disabled in read-only mode"} }
|
||||
def chown [] { error make {msg: "chown command disabled in read-only mode"} }
|
||||
{% endif %}
|
||||
|
||||
# Load observability modules if enabled
|
||||
{% if taskserv.nushell_metrics | default(true) %}
|
||||
source $"($env.NUSHELL_HOME)/observability/collect.nu"
|
||||
{% endif %}
|
||||
|
||||
# Session timeout warning
|
||||
def session-check [] {
|
||||
let start_time = (date now)
|
||||
let timeout = ($env.NUSHELL_SESSION_TIMEOUT? | default 900 | into int)
|
||||
if ((date now) - $start_time) > ($timeout * 1sec) {
|
||||
print "⚠️ Session timeout approaching. Please complete your tasks."
|
||||
}
|
||||
}
|
||||
|
||||
# Initialize secure environment
|
||||
print $"🛡️ Nushell secure mode active - execution mode: ($env.NUSHELL_EXECUTION_MODE? | default 'restricted')"
|
||||
if ($env.NUSHELL_READONLY_MODE? | default true) {
|
||||
print "📖 Read-only mode enabled"
|
||||
}
|
||||
print $"⏱️ Session timeout: ($env.NUSHELL_SESSION_TIMEOUT? | default 900) seconds"
|
||||
44
taskservs/nushell/default/env-nushell.j2
Normal file
44
taskservs/nushell/default/env-nushell.j2
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
# Nushell Runtime Environment Configuration
|
||||
# Security: All paths are sandboxed and validated
|
||||
|
||||
# Core Nushell paths
|
||||
NUSHELL_HOME={{taskserv.admin_user_home}}/nushell
|
||||
NUSHELL_CONFIG_DIR={{taskserv.admin_user_home}}/.config/nushell
|
||||
NUSHELL_DATA_DIR={{taskserv.admin_user_home}}/.local/share/nushell
|
||||
NUSHELL_SCRIPTS_DIR={{taskserv.admin_user_home}}/nushell/scripts
|
||||
NUSHELL_LIB_DIR={{taskserv.admin_user_home}}/nushell/lib
|
||||
|
||||
# Security settings
|
||||
NUSHELL_EXECUTION_MODE={{taskserv.nushell_execution_mode | default("restricted")}}
|
||||
NUSHELL_READONLY_MODE={{taskserv.nushell_readonly | default("true")}}
|
||||
NUSHELL_NETWORK_ENABLED={{taskserv.nushell_network | default("false")}}
|
||||
NUSHELL_MAX_MEMORY={{taskserv.nushell_max_memory | default("256MB")}}
|
||||
NUSHELL_MAX_CPU_TIME={{taskserv.nushell_max_cpu_time | default("30s")}}
|
||||
|
||||
# Plugin configuration
|
||||
NUSHELL_PLUGINS_ENABLED={{taskserv.nushell_plugins | default("false")}}
|
||||
NUSHELL_PLUGIN_ALLOWLIST="{{taskserv.nushell_plugin_allowlist | default('nu_plugin_kcl,nu_plugin_tera,nu_plugin_polars')}}"
|
||||
|
||||
# Remote execution settings
|
||||
NUSHELL_REMOTE_USER={{taskserv.admin_user}}
|
||||
NUSHELL_REMOTE_TIMEOUT={{taskserv.nushell_remote_timeout | default("300")}}
|
||||
NUSHELL_SESSION_TIMEOUT={{taskserv.nushell_session_timeout | default("900")}}
|
||||
|
||||
# Logging and audit
|
||||
NUSHELL_LOG_LEVEL={{taskserv.nushell_log_level | default("info")}}
|
||||
NUSHELL_AUDIT_ENABLED={{taskserv.nushell_audit | default("true")}}
|
||||
NUSHELL_AUDIT_FILE={{taskserv.admin_user_home}}/nushell/audit.log
|
||||
|
||||
# KCL integration (optional)
|
||||
KCL_ENABLED={{taskserv.kcl_enabled | default("false")}}
|
||||
KCL_BINARY_PATH={{taskserv.kcl_binary_path | default("/usr/local/bin/kcl")}}
|
||||
|
||||
# Observability settings
|
||||
NUSHELL_METRICS_ENABLED={{taskserv.nushell_metrics | default("true")}}
|
||||
NUSHELL_TELEMETRY_ENDPOINT={{taskserv.nushell_telemetry_endpoint | default("")}}
|
||||
NUSHELL_LOG_COLLECTION={{taskserv.nushell_log_collection | default("false")}}
|
||||
|
||||
# Environment restrictions
|
||||
NUSHELL_ALLOWED_COMMANDS="{{taskserv.nushell_allowed_commands | default('ls,cat,grep,ps,df,free,uptime,systemctl,kubectl')}}"
|
||||
NUSHELL_BLOCKED_COMMANDS="{{taskserv.nushell_blocked_commands | default('rm,mv,cp,chmod,chown,sudo,su')}}"
|
||||
NUSHELL_ALLOWED_PATHS="{{taskserv.nushell_allowed_paths | default('/tmp,/var/log,/proc,/sys')}}"
|
||||
93
taskservs/nushell/default/env.nu.j2
Normal file
93
taskservs/nushell/default/env.nu.j2
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
# Nushell Environment Variables for Infrastructure Servers
|
||||
# Security-focused environment setup
|
||||
|
||||
# Core environment paths
|
||||
$env.NUSHELL_HOME = "{{taskserv.admin_user_home}}/nushell"
|
||||
$env.NUSHELL_CONFIG_DIR = "{{taskserv.admin_user_home}}/.config/nushell"
|
||||
$env.NUSHELL_DATA_DIR = "{{taskserv.admin_user_home}}/.local/share/nushell"
|
||||
|
||||
# Security environment variables
|
||||
$env.NUSHELL_EXECUTION_MODE = "{{taskserv.nushell_execution_mode | default('restricted')}}"
|
||||
$env.NUSHELL_READONLY_MODE = {% if taskserv.nushell_readonly | default(true) %}true{% else %}false{% endif %}
|
||||
$env.NUSHELL_AUDIT_ENABLED = {% if taskserv.nushell_audit | default(true) %}true{% else %}false{% endif %}
|
||||
$env.NUSHELL_AUDIT_FILE = "{{taskserv.admin_user_home}}/nushell/audit.log"
|
||||
|
||||
# Resource limits
|
||||
$env.NUSHELL_MAX_MEMORY = "{{taskserv.nushell_max_memory | default('256MB')}}"
|
||||
$env.NUSHELL_SESSION_TIMEOUT = {{taskserv.nushell_session_timeout | default(900)}}
|
||||
|
||||
# Command restrictions
|
||||
$env.NUSHELL_ALLOWED_COMMANDS = "{{taskserv.nushell_allowed_commands | default('ls,cat,grep,ps,df,free,uptime,systemctl,kubectl')}}"
|
||||
$env.NUSHELL_BLOCKED_COMMANDS = "{{taskserv.nushell_blocked_commands | default('rm,mv,cp,chmod,chown,sudo,su')}}"
|
||||
$env.NUSHELL_ALLOWED_PATHS = "{{taskserv.nushell_allowed_paths | default('/tmp,/var/log,/proc,/sys')}}"
|
||||
|
||||
# Plugin configuration
|
||||
$env.NUSHELL_PLUGINS_ENABLED = {% if taskserv.nushell_plugins | default(false) %}true{% else %}false{% endif %}
|
||||
{% if taskserv.nushell_plugins | default(false) %}
|
||||
$env.NUSHELL_PLUGIN_ALLOWLIST = "{{taskserv.nushell_plugin_allowlist | default('nu_plugin_kcl,nu_plugin_tera,nu_plugin_polars')}}"
|
||||
{% endif %}
|
||||
|
||||
# KCL integration
|
||||
$env.KCL_ENABLED = {% if taskserv.kcl_enabled | default(false) %}true{% else %}false{% endif %}
|
||||
{% if taskserv.kcl_enabled | default(false) %}
|
||||
$env.KCL_BINARY_PATH = "{{taskserv.kcl_binary_path | default('/usr/local/bin/kcl')}}"
|
||||
{% endif %}
|
||||
|
||||
# Observability settings
|
||||
$env.NUSHELL_METRICS_ENABLED = {% if taskserv.nushell_metrics | default(true) %}true{% else %}false{% endif %}
|
||||
$env.NUSHELL_LOG_COLLECTION = {% if taskserv.nushell_log_collection | default(false) %}true{% else %}false{% endif %}
|
||||
{% if taskserv.nushell_telemetry_endpoint | default("") != "" %}
|
||||
$env.NUSHELL_TELEMETRY_ENDPOINT = "{{taskserv.nushell_telemetry_endpoint}}"
|
||||
{% endif %}
|
||||
|
||||
# Provisioning integration
|
||||
$env.PROVISIONING_NUSHELL_VERSION = "1.0.0"
|
||||
$env.PROVISIONING_NUSHELL_MODE = "infrastructure"
|
||||
|
||||
# Security: Sanitize PATH to prevent privilege escalation
|
||||
$env.PATH = ($env.PATH | split row (char esep) | where $it =~ "^/(usr/)?(local/)?bin$|^/(usr/)?sbin$" | str join (char esep))
|
||||
|
||||
# Add Nushell tools to PATH if they exist
|
||||
if ("{{taskserv.admin_user_home}}/.local/bin" | path exists) {
|
||||
$env.PATH = ($env.PATH | split row (char esep) | prepend "{{taskserv.admin_user_home}}/.local/bin" | str join (char esep))
|
||||
}
|
||||
|
||||
# Default editor for security (read-only contexts)
|
||||
{% if taskserv.nushell_readonly | default(true) %}
|
||||
$env.EDITOR = "cat"
|
||||
$env.VISUAL = "cat"
|
||||
{% else %}
|
||||
$env.EDITOR = "{{taskserv.editor | default('nano')}}"
|
||||
$env.VISUAL = "{{taskserv.visual_editor | default('nano')}}"
|
||||
{% endif %}
|
||||
|
||||
# Logging configuration
|
||||
$env.NU_LOG_LEVEL = "{{taskserv.nushell_log_level | default('info')}}"
|
||||
$env.NU_LOG_FORMAT = "json"
|
||||
$env.NU_LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
|
||||
|
||||
# Network restrictions
|
||||
{% if taskserv.nushell_network | default(false) %}
|
||||
$env.NUSHELL_NETWORK_ENABLED = true
|
||||
{% else %}
|
||||
$env.NUSHELL_NETWORK_ENABLED = false
|
||||
# Disable network access for security
|
||||
$env.http_proxy = "127.0.0.1:9999"
|
||||
$env.https_proxy = "127.0.0.1:9999"
|
||||
{% endif %}
|
||||
|
||||
# Session information
|
||||
$env.NUSHELL_SESSION_ID = (random uuid)
|
||||
$env.NUSHELL_SESSION_START = (date now | format date "%Y-%m-%d %H:%M:%S")
|
||||
$env.NUSHELL_SERVER_ROLE = "{{server.role | default('worker')}}"
|
||||
$env.NUSHELL_SERVER_HOSTNAME = "{{server.hostname | default('unknown')}}"
|
||||
|
||||
# Startup message
|
||||
if not ($env.NUSHELL_QUIET? | default false) {
|
||||
print $"🔧 Nushell Infrastructure Runtime v($env.PROVISIONING_NUSHELL_VERSION)"
|
||||
print $"🏷️ Server: ($env.NUSHELL_SERVER_HOSTNAME) | Role: ($env.NUSHELL_SERVER_ROLE)"
|
||||
print $"🛡️ Security: ($env.NUSHELL_EXECUTION_MODE) mode | Readonly: ($env.NUSHELL_READONLY_MODE)"
|
||||
if $env.NUSHELL_AUDIT_ENABLED {
|
||||
print $"📝 Audit logging enabled: ($env.NUSHELL_AUDIT_FILE)"
|
||||
}
|
||||
}
|
||||
437
taskservs/nushell/default/install-nushell.sh
Executable file
437
taskservs/nushell/default/install-nushell.sh
Executable file
|
|
@ -0,0 +1,437 @@
|
|||
#!/bin/bash
|
||||
# Nushell Infrastructure Runtime Installation Script
|
||||
# Secure installation with version management and safety checks
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Configuration
|
||||
NUSHELL_VERSION="${NUSHELL_VERSION:-0.107.1}"
|
||||
INSTALL_DIR="${INSTALL_DIR:-/usr/local/bin}"
|
||||
CONFIG_DIR="${CONFIG_DIR:-/etc/nushell}"
|
||||
USER_HOME="${USER_HOME:-$HOME}"
|
||||
ADMIN_USER="${ADMIN_USER:-$(whoami)}"
|
||||
|
||||
# Security settings
|
||||
NUSHELL_READONLY="${NUSHELL_READONLY:-true}"
|
||||
NUSHELL_PLUGINS="${NUSHELL_PLUGINS:-false}"
|
||||
NUSHELL_NETWORK="${NUSHELL_NETWORK:-false}"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
log() {
|
||||
echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1"
|
||||
}
|
||||
|
||||
error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1" >&2
|
||||
}
|
||||
|
||||
warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
# Check prerequisites
|
||||
check_prerequisites() {
|
||||
log "Checking prerequisites..."
|
||||
|
||||
# Check if running as root or with sudo privileges
|
||||
if [[ $EUID -eq 0 ]]; then
|
||||
warn "Running as root. Consider using a dedicated user for Nushell operations."
|
||||
fi
|
||||
|
||||
# Check system architecture
|
||||
local arch=$(uname -m)
|
||||
case $arch in
|
||||
x86_64|amd64)
|
||||
NUSHELL_ARCH="x86_64"
|
||||
;;
|
||||
aarch64|arm64)
|
||||
NUSHELL_ARCH="aarch64"
|
||||
;;
|
||||
*)
|
||||
error "Unsupported architecture: $arch"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Check OS
|
||||
local os=$(uname -s)
|
||||
case $os in
|
||||
Linux)
|
||||
NUSHELL_OS="unknown-linux-gnu"
|
||||
;;
|
||||
Darwin)
|
||||
NUSHELL_OS="apple-darwin"
|
||||
;;
|
||||
*)
|
||||
error "Unsupported operating system: $os"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Check available disk space (minimum 100MB)
|
||||
local available_space=$(df "$INSTALL_DIR" | awk 'NR==2 {print $4}')
|
||||
if [[ $available_space -lt 102400 ]]; then
|
||||
error "Insufficient disk space. Need at least 100MB in $INSTALL_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
success "Prerequisites check completed"
|
||||
}
|
||||
|
||||
# Download and verify Nushell
|
||||
download_nushell() {
|
||||
log "Downloading Nushell v${NUSHELL_VERSION} for ${NUSHELL_ARCH}-${NUSHELL_OS}..."
|
||||
|
||||
local download_url="https://github.com/nushell/nushell/releases/download/${NUSHELL_VERSION}/nu-${NUSHELL_VERSION}-${NUSHELL_ARCH}-${NUSHELL_OS}.tar.gz"
|
||||
local temp_dir=$(mktemp -d)
|
||||
local archive_file="${temp_dir}/nushell.tar.gz"
|
||||
|
||||
# Download with retry logic
|
||||
local max_retries=3
|
||||
local retry_count=0
|
||||
|
||||
while [[ $retry_count -lt $max_retries ]]; do
|
||||
if curl -L --fail --silent --show-error "$download_url" -o "$archive_file"; then
|
||||
break
|
||||
else
|
||||
retry_count=$((retry_count + 1))
|
||||
warn "Download attempt $retry_count failed. Retrying..."
|
||||
sleep 2
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $retry_count -eq $max_retries ]]; then
|
||||
error "Failed to download Nushell after $max_retries attempts"
|
||||
rm -rf "$temp_dir"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify download
|
||||
if [[ ! -f "$archive_file" ]] || [[ ! -s "$archive_file" ]]; then
|
||||
error "Downloaded file is empty or missing"
|
||||
rm -rf "$temp_dir"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "Extracting Nushell..."
|
||||
tar -xzf "$archive_file" -C "$temp_dir"
|
||||
|
||||
# Find the nu binary
|
||||
local nu_binary=$(find "$temp_dir" -name "nu" -type f -executable | head -1)
|
||||
if [[ -z "$nu_binary" ]]; then
|
||||
error "Could not find nu binary in downloaded archive"
|
||||
rm -rf "$temp_dir"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Install binary
|
||||
sudo mkdir -p "$INSTALL_DIR"
|
||||
sudo cp "$nu_binary" "$INSTALL_DIR/nu"
|
||||
sudo chmod +x "$INSTALL_DIR/nu"
|
||||
|
||||
# Copy additional tools if they exist
|
||||
for tool in nu_plugin_kcl nu_plugin_tera nu_plugin_polars; do
|
||||
local tool_binary=$(find "$temp_dir" -name "$tool" -type f -executable | head -1)
|
||||
if [[ -n "$tool_binary" ]] && [[ "$NUSHELL_PLUGINS" == "true" ]]; then
|
||||
sudo cp "$tool_binary" "$INSTALL_DIR/$tool"
|
||||
sudo chmod +x "$INSTALL_DIR/$tool"
|
||||
log "Installed plugin: $tool"
|
||||
fi
|
||||
done
|
||||
|
||||
# Cleanup
|
||||
rm -rf "$temp_dir"
|
||||
|
||||
success "Nushell installation completed"
|
||||
}
|
||||
|
||||
# Create secure configuration
|
||||
create_configuration() {
|
||||
log "Creating secure Nushell configuration..."
|
||||
|
||||
# Create system-wide config directory
|
||||
sudo mkdir -p "$CONFIG_DIR"
|
||||
sudo mkdir -p "$CONFIG_DIR/scripts"
|
||||
sudo mkdir -p "$CONFIG_DIR/observability"
|
||||
|
||||
# Create user-specific directories
|
||||
mkdir -p "$USER_HOME/.config/nushell"
|
||||
mkdir -p "$USER_HOME/.local/share/nushell"
|
||||
mkdir -p "$USER_HOME/nushell/scripts"
|
||||
mkdir -p "$USER_HOME/nushell/observability"
|
||||
mkdir -p "$USER_HOME/nushell/lib"
|
||||
|
||||
# Set secure permissions
|
||||
chmod 750 "$USER_HOME/nushell"
|
||||
chmod 700 "$USER_HOME/nushell/scripts"
|
||||
|
||||
# Create basic configuration files
|
||||
cat > "$USER_HOME/.config/nushell/env.nu" << 'EOF'
|
||||
# Nushell Infrastructure Environment
|
||||
# Security-focused configuration for infrastructure servers
|
||||
|
||||
$env.NUSHELL_HOME = $"($env.HOME)/nushell"
|
||||
$env.NUSHELL_CONFIG_DIR = $"($env.HOME)/.config/nushell"
|
||||
$env.NUSHELL_EXECUTION_MODE = "restricted"
|
||||
$env.NUSHELL_READONLY_MODE = true
|
||||
$env.NUSHELL_AUDIT_ENABLED = true
|
||||
$env.NUSHELL_AUDIT_FILE = $"($env.HOME)/nushell/audit.log"
|
||||
|
||||
# Security: Sanitize PATH
|
||||
$env.PATH = ($env.PATH | split row (char esep) | where $it =~ "^/(usr/)?(local/)?bin$|^/(usr/)?sbin$" | str join (char esep))
|
||||
|
||||
# Session information
|
||||
$env.NUSHELL_SESSION_ID = (random uuid)
|
||||
$env.NUSHELL_SESSION_START = (date now | format date "%Y-%m-%d %H:%M:%S")
|
||||
|
||||
print "🔧 Nushell Infrastructure Runtime initialized"
|
||||
print $"🛡️ Security mode: ($env.NUSHELL_EXECUTION_MODE) | Readonly: ($env.NUSHELL_READONLY_MODE)"
|
||||
EOF
|
||||
|
||||
cat > "$USER_HOME/.config/nushell/config.nu" << 'EOF'
|
||||
# Secure Nushell Configuration for Infrastructure Servers
|
||||
|
||||
$env.config = {
|
||||
show_banner: false
|
||||
use_ansi_coloring: true
|
||||
edit_mode: emacs
|
||||
shell_integration: false
|
||||
cd_with_abbreviations: false
|
||||
filesize_metric: true
|
||||
table_mode: rounded
|
||||
|
||||
history: {
|
||||
max_size: 1000
|
||||
sync_on_enter: true
|
||||
file_format: "plaintext"
|
||||
isolation: true
|
||||
}
|
||||
|
||||
completions: {
|
||||
case_sensitive: false
|
||||
quick: true
|
||||
partial: true
|
||||
algorithm: "prefix"
|
||||
external: {
|
||||
enable: false
|
||||
max_results: 100
|
||||
completer: null
|
||||
}
|
||||
}
|
||||
|
||||
error_style: "fancy"
|
||||
|
||||
hooks: {
|
||||
pre_execution: [{
|
||||
condition: {|| true }
|
||||
code: {|| |cmd|
|
||||
if ($env.NUSHELL_AUDIT_ENABLED? | default false) {
|
||||
$"(date now | format date '%Y-%m-%d %H:%M:%S') - Command: ($cmd)" | save -a $env.NUSHELL_AUDIT_FILE
|
||||
}
|
||||
|
||||
let blocked = ["rm", "mv", "cp", "chmod", "chown", "sudo", "su"]
|
||||
let cmd_name = ($cmd | split row " " | first)
|
||||
if $cmd_name in $blocked and ($env.NUSHELL_READONLY_MODE? | default true) {
|
||||
error make {msg: $"Command '($cmd_name)' is blocked in read-only mode"}
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
|
||||
menus: []
|
||||
keybindings: []
|
||||
}
|
||||
|
||||
# Security aliases
|
||||
alias ll = ls -la
|
||||
alias df = df -h
|
||||
alias free = free -h
|
||||
|
||||
print "🛡️ Nushell secure mode active"
|
||||
EOF
|
||||
|
||||
# Set proper ownership
|
||||
chown -R "$ADMIN_USER:$ADMIN_USER" "$USER_HOME/.config/nushell"
|
||||
chown -R "$ADMIN_USER:$ADMIN_USER" "$USER_HOME/nushell"
|
||||
|
||||
success "Configuration created successfully"
|
||||
}
|
||||
|
||||
# Install plugins (if enabled)
|
||||
install_plugins() {
|
||||
if [[ "$NUSHELL_PLUGINS" != "true" ]]; then
|
||||
log "Plugin installation skipped (disabled)"
|
||||
return
|
||||
fi
|
||||
|
||||
log "Installing Nushell plugins..."
|
||||
|
||||
# KCL plugin (for configuration language support)
|
||||
if command -v kcl &> /dev/null; then
|
||||
log "KCL binary found, plugin support available"
|
||||
else
|
||||
warn "KCL binary not found. Install KCL for full configuration support."
|
||||
fi
|
||||
|
||||
# Create plugin registration script
|
||||
cat > "$USER_HOME/nushell/scripts/register-plugins.nu" << 'EOF'
|
||||
# Plugin registration script
|
||||
# Run this to register available plugins
|
||||
|
||||
print "🔌 Registering Nushell plugins..."
|
||||
|
||||
try {
|
||||
if (which nu_plugin_kcl | is-not-empty) {
|
||||
plugin add nu_plugin_kcl
|
||||
print "✅ Registered nu_plugin_kcl"
|
||||
}
|
||||
} catch {
|
||||
print "⚠️ Failed to register nu_plugin_kcl"
|
||||
}
|
||||
|
||||
try {
|
||||
if (which nu_plugin_tera | is-not-empty) {
|
||||
plugin add nu_plugin_tera
|
||||
print "✅ Registered nu_plugin_tera"
|
||||
}
|
||||
} catch {
|
||||
print "⚠️ Failed to register nu_plugin_tera"
|
||||
}
|
||||
|
||||
try {
|
||||
if (which nu_plugin_polars | is-not-empty) {
|
||||
plugin add nu_plugin_polars
|
||||
print "✅ Registered nu_plugin_polars"
|
||||
}
|
||||
} catch {
|
||||
print "⚠️ Failed to register nu_plugin_polars"
|
||||
}
|
||||
|
||||
print "🔌 Plugin registration completed"
|
||||
EOF
|
||||
|
||||
chmod +x "$USER_HOME/nushell/scripts/register-plugins.nu"
|
||||
success "Plugin installation completed"
|
||||
}
|
||||
|
||||
# Create systemd service (optional)
|
||||
create_service() {
|
||||
if [[ ! -d "/etc/systemd/system" ]]; then
|
||||
log "Systemd not available, skipping service creation"
|
||||
return
|
||||
fi
|
||||
|
||||
log "Creating Nushell monitoring service..."
|
||||
|
||||
sudo tee "/etc/systemd/system/nushell-monitoring.service" > /dev/null << EOF
|
||||
[Unit]
|
||||
Description=Nushell Infrastructure Monitoring
|
||||
After=network.target
|
||||
Wants=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=$ADMIN_USER
|
||||
Group=$ADMIN_USER
|
||||
WorkingDirectory=$USER_HOME/nushell
|
||||
Environment=NUSHELL_EXECUTION_MODE=restricted
|
||||
Environment=NUSHELL_READONLY_MODE=true
|
||||
Environment=NUSHELL_AUDIT_ENABLED=true
|
||||
ExecStart=$INSTALL_DIR/nu --config $USER_HOME/.config/nushell/config.nu --env-config $USER_HOME/.config/nushell/env.nu
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
# Security settings
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=read-only
|
||||
ReadOnlyPaths=/
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
sudo systemctl daemon-reload
|
||||
success "Systemd service created (disabled by default)"
|
||||
}
|
||||
|
||||
# Verify installation
|
||||
verify_installation() {
|
||||
log "Verifying Nushell installation..."
|
||||
|
||||
# Check binary
|
||||
if ! command -v nu &> /dev/null; then
|
||||
error "Nushell binary not found in PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check version
|
||||
local installed_version=$(nu --version | awk '{print $2}')
|
||||
if [[ "$installed_version" != "$NUSHELL_VERSION" ]]; then
|
||||
warn "Version mismatch: expected $NUSHELL_VERSION, got $installed_version"
|
||||
else
|
||||
success "Nushell version $installed_version verified"
|
||||
fi
|
||||
|
||||
# Test basic functionality
|
||||
if echo 'print "test"' | nu --config /dev/null &> /dev/null; then
|
||||
success "Basic functionality test passed"
|
||||
else
|
||||
error "Basic functionality test failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check configuration files
|
||||
if [[ -f "$USER_HOME/.config/nushell/config.nu" ]] && [[ -f "$USER_HOME/.config/nushell/env.nu" ]]; then
|
||||
success "Configuration files created successfully"
|
||||
else
|
||||
error "Configuration files missing"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
success "Installation verification completed"
|
||||
}
|
||||
|
||||
# Main installation process
|
||||
main() {
|
||||
log "Starting Nushell Infrastructure Runtime installation..."
|
||||
log "Version: $NUSHELL_VERSION"
|
||||
log "Architecture: $NUSHELL_ARCH-$NUSHELL_OS"
|
||||
log "Install directory: $INSTALL_DIR"
|
||||
log "Security mode: readonly=$NUSHELL_READONLY, plugins=$NUSHELL_PLUGINS"
|
||||
|
||||
check_prerequisites
|
||||
download_nushell
|
||||
create_configuration
|
||||
install_plugins
|
||||
create_service
|
||||
verify_installation
|
||||
|
||||
success "🎉 Nushell Infrastructure Runtime installation completed successfully!"
|
||||
log ""
|
||||
log "Next steps:"
|
||||
log "1. Add $INSTALL_DIR to your PATH if not already present"
|
||||
log "2. Run 'nu' to start Nushell with secure configuration"
|
||||
log "3. Use 'nu $USER_HOME/nushell/scripts/register-plugins.nu' to register plugins (if enabled)"
|
||||
log "4. Enable monitoring service: sudo systemctl enable nushell-monitoring.service (optional)"
|
||||
log ""
|
||||
log "Configuration files:"
|
||||
log "- Config: $USER_HOME/.config/nushell/config.nu"
|
||||
log "- Environment: $USER_HOME/.config/nushell/env.nu"
|
||||
log "- Scripts: $USER_HOME/nushell/scripts/"
|
||||
log "- Audit log: $USER_HOME/nushell/audit.log"
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
57
taskservs/nushell/default/prepare
Executable file
57
taskservs/nushell/default/prepare
Executable file
|
|
@ -0,0 +1,57 @@
|
|||
#!/usr/bin/env nu
|
||||
# Info: Prepare for nushell installation on infrastructure servers
|
||||
# Author: Generated by Claude Code
|
||||
# Release: 1.0.0
|
||||
# Date: 2025-09-23
|
||||
|
||||
use lib_provisioning/cmd/env.nu *
|
||||
use lib_provisioning/cmd/lib.nu *
|
||||
use lib_provisioning/utils/ui.nu *
|
||||
|
||||
print $"(_ansi green_bold)Nushell Runtime(_ansi reset) with ($env.PROVISIONING_VARS)"
|
||||
|
||||
let defs = load_defs
|
||||
|
||||
# Ensure target environment path exists
|
||||
let target_path = $env.PROVISIONING_WK_ENV_PATH
|
||||
^mkdir -p $"($target_path)/.config/nushell"
|
||||
^mkdir -p $"($target_path)/.local/bin"
|
||||
^mkdir -p $"($target_path)/.local/share/nushell"
|
||||
|
||||
# Create secure directory for Nushell scripts
|
||||
^mkdir -p $"($target_path)/nushell/scripts"
|
||||
^mkdir -p $"($target_path)/nushell/observability"
|
||||
^mkdir -p $"($target_path)/nushell/lib"
|
||||
|
||||
# Set secure permissions for Nushell directories
|
||||
^chmod 750 $"($target_path)/nushell"
|
||||
^chmod 700 $"($target_path)/nushell/scripts"
|
||||
|
||||
# Create plugin directory if plugins are enabled
|
||||
if ($defs.taskserv.nushell_plugins? | default false) {
|
||||
^mkdir -p $"($target_path)/.local/share/nushell/plugins"
|
||||
log_debug "Created Nushell plugins directory"
|
||||
}
|
||||
|
||||
# Copy SSH keys if specified for remote operations
|
||||
let ssh_keys = ($defs.taskserv.ssh_keys? | default "" | str replace "~" $env.HOME | str trim)
|
||||
if $ssh_keys != "" {
|
||||
^mkdir -p $"($target_path)/.ssh"
|
||||
for key in ($ssh_keys | split row " ") {
|
||||
log_debug $"Setting up SSH key: ($key)"
|
||||
if ($key | path exists) {
|
||||
cp $key $"($target_path)/.ssh"
|
||||
^chmod 600 $"($target_path)/.ssh/($key | path basename)"
|
||||
}
|
||||
if ($"($key).pub" | path exists) {
|
||||
cp $"($key).pub" $"($target_path)/.ssh"
|
||||
^chmod 644 $"($target_path)/.ssh/($key | path basename).pub"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Ensure proper ownership for security
|
||||
let admin_user = ($defs.taskserv.admin_user? | default "root")
|
||||
^chown -R $"($admin_user):($admin_user)" $"($target_path)/nushell"
|
||||
|
||||
log_info "Nushell environment prepared successfully"
|
||||
262
taskservs/nushell/default/remote-exec.nu.j2
Normal file
262
taskservs/nushell/default/remote-exec.nu.j2
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
# 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
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue