#!/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 "$@"