diff --git a/scripts/gpu_tpu/switch_gpu_mode.sh b/scripts/gpu_tpu/switch_gpu_mode.sh old mode 100755 new mode 100644 diff --git a/scripts/gpu_tpu/switch_gpu_mode_direct.sh b/scripts/gpu_tpu/switch_gpu_mode_direct.sh index d3e6d3fb..9f9e773c 100644 --- a/scripts/gpu_tpu/switch_gpu_mode_direct.sh +++ b/scripts/gpu_tpu/switch_gpu_mode_direct.sh @@ -1020,13 +1020,7 @@ main() { exit 1 fi - # Show info about selected GPU - local gpu_idx="${SELECTED_GPU_IDX[0]}" - msg_info "$(translate 'GPU selected'): ${ALL_GPU_NAMES[$gpu_idx]} (${ALL_GPU_PCIS[$gpu_idx]})" - msg_info "$(translate 'Current driver'): ${ALL_GPU_DRIVERS[$gpu_idx]}" - msg_info "$(translate 'Target mode'): $TARGET_MODE" - echo - + # Confirm the operation confirm_plan diff --git a/scripts/oci/catalog.json b/scripts/oci/catalog.json new file mode 100644 index 00000000..84a66636 --- /dev/null +++ b/scripts/oci/catalog.json @@ -0,0 +1,254 @@ +{ + "version": "1.0.0", + "last_updated": "2025-01-15T10:00:00Z", + "apps": { + "secure-gateway": { + "id": "secure-gateway", + "name": "Secure Gateway", + "short_name": "VPN Gateway", + "subtitle": "Tailscale VPN Gateway", + "version": "1.0.0", + "category": "security", + "subcategory": "remote_access", + "icon": "shield-check", + "icon_type": "shield", + "color": "#0EA5E9", + + "summary": "Secure remote access without opening ports", + "description": "Deploy a managed VPN gateway using Tailscale for zero-trust access to your Proxmox infrastructure. Access ProxMenux Monitor, Proxmox UI, VMs, and LXC containers from anywhere without exposing ports to the internet.", + "documentation_url": "https://macrimi.github.io/ProxMenux/docs/secure-gateway", + "code_url": "https://github.com/MacRimi/ProxMenux/tree/main/Scripts/oci", + + "features": [ + "Zero-trust network access", + "No port forwarding required", + "End-to-end encryption", + "Easy mobile access", + "MagicDNS for easy hostname access", + "Access control via Tailscale admin" + ], + + "container": { + "type": "lxc", + "template": "alpine", + "install_method": "apk", + "packages": ["tailscale"], + "services": ["tailscale"], + "privileged": false, + "memory": 256, + "cores": 1, + "disk_size": 2, + "requires_ip_forward": true, + "features": ["nesting=1"], + "lxc_config": [ + "lxc.cgroup2.devices.allow: c 10:200 rwm", + "lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file" + ] + }, + + "volumes": { + "state": { + "container_path": "/var/lib/tailscale", + "persistent": true, + "description": "Tailscale state and keys" + } + }, + + "environment": [ + { + "name": "TS_STATE_DIR", + "value": "/var/lib/tailscale" + }, + { + "name": "TS_USERSPACE", + "value": "false" + }, + { + "name": "TS_AUTHKEY", + "value": "$auth_key" + }, + { + "name": "TS_HOSTNAME", + "value": "$hostname" + }, + { + "name": "TS_ROUTES", + "value": "$advertise_routes" + }, + { + "name": "TS_EXTRA_ARGS", + "value": "$extra_args" + } + ], + + "config_schema": { + "auth_key": { + "type": "password", + "label": "Tailscale Auth Key", + "description": "Pre-authentication key from Tailscale admin console. Generate one at the link below.", + "placeholder": "tskey-auth-xxxxx", + "required": true, + "sensitive": true, + "env_var": "TS_AUTHKEY", + "help_url": "https://login.tailscale.com/admin/settings/keys", + "help_text": "Generate Auth Key" + }, + "hostname": { + "type": "text", + "label": "Device Hostname", + "description": "Name shown in Tailscale admin console", + "placeholder": "proxmox-gateway", + "default": "proxmox-gateway", + "required": false, + "env_var": "TS_HOSTNAME", + "validation": { + "pattern": "^[a-zA-Z0-9-]+$", + "max_length": 63, + "message": "Only letters, numbers, and hyphens allowed" + } + }, + "access_mode": { + "type": "select", + "label": "Access Scope", + "description": "What should be accessible through this gateway", + "default": "host_only", + "required": true, + "options": [ + { + "value": "host_only", + "label": "Proxmox Only", + "description": "Access only this Proxmox server (UI and ProxMenux Monitor)" + }, + { + "value": "proxmox_network", + "label": "Full Local Network", + "description": "Access all devices on your local network (NAS, printers, VMs, etc.)" + }, + { + "value": "custom", + "label": "Custom Subnets", + "description": "Select specific subnets to expose" + } + ] + }, + "advertise_routes": { + "type": "networks", + "label": "Advertised Networks", + "description": "Select networks to make accessible through the VPN", + "required": false, + "depends_on": { + "field": "access_mode", + "values": ["custom"] + }, + "env_var": "TS_ROUTES", + "env_format": "csv" + }, + "exit_node": { + "type": "boolean", + "label": "Exit Node", + "description": "Use this gateway as your internet exit point when away from home. All your internet traffic will appear to come from your Proxmox server's IP address.", + "default": false, + "required": false, + "flag": "--advertise-exit-node", + "warning": "Requires approval in Tailscale Admin. When enabled on your device, ALL internet traffic routes through your Proxmox server." + }, + "accept_routes": { + "type": "boolean", + "label": "Accept Routes", + "description": "Allow this gateway to access networks advertised by OTHER Tailscale nodes in your tailnet. Useful if you have multiple Tailscale subnet routers.", + "default": false, + "required": false, + "flag": "--accept-routes" + } + }, + + "healthcheck": { + "command": ["tailscale", "status", "--json"], + "interval_seconds": 30, + "timeout_seconds": 10, + "retries": 3, + "healthy_condition": "BackendState == Running" + }, + + "requirements": { + "min_memory_mb": 64, + "min_disk_mb": 100, + "proxmox_min_version": "9.1", + "checks": [ + { + "type": "proxmox_version", + "min": "9.1", + "message": "OCI containers require Proxmox VE 9.1+" + } + ] + }, + + "security_notes": [ + "Requires NET_ADMIN capability for VPN tunneling", + "Uses /dev/net/tun for network virtualization", + "Auth key is stored encrypted at rest", + "No ports are opened on the host firewall", + "All traffic is end-to-end encrypted" + ], + + "ui": { + "wizard_steps": [ + { + "id": "intro", + "title": "Secure Remote Access", + "description": "Set up secure VPN access to your Proxmox server" + }, + { + "id": "auth", + "title": "Tailscale Authentication", + "description": "Connect to your Tailscale account", + "fields": ["auth_key", "hostname"] + }, + { + "id": "access", + "title": "Access Scope", + "description": "Choose what to make accessible", + "fields": ["access_mode", "advertise_routes"] + }, + { + "id": "options", + "title": "Advanced Options", + "description": "Additional configuration", + "fields": ["exit_node", "accept_routes"] + }, + { + "id": "deploy", + "title": "Deploy Gateway", + "description": "Review and deploy" + } + ], + "show_in_sections": ["security"], + "dashboard_widget": false, + "status_indicators": { + "running": { + "color": "green", + "icon": "check-circle", + "label": "Connected" + }, + "stopped": { + "color": "yellow", + "icon": "pause-circle", + "label": "Stopped" + }, + "error": { + "color": "red", + "icon": "x-circle", + "label": "Error" + } + } + }, + + "metadata": { + "author": "ProxMenux", + "license": "MIT", + "upstream": "https://tailscale.com", + "tags": ["vpn", "remote-access", "tailscale", "zero-trust", "security"] + } + } + } +} diff --git a/scripts/oci/oci_manager.sh b/scripts/oci/oci_manager.sh new file mode 100644 index 00000000..6e9207df --- /dev/null +++ b/scripts/oci/oci_manager.sh @@ -0,0 +1,522 @@ +#!/bin/bash +# ProxMenux - OCI Application Manager +# ============================================ +# Author : MacRimi +# License : MIT +# Version : 1.0 +# ============================================ +# Manages OCI container applications through dialog menus or direct CLI. +# This script wraps the Python oci_manager.py for terminal usage. + +SCRIPT_TITLE="OCI Application Manager" + +LOCAL_SCRIPTS="/usr/local/share/proxmenux/scripts" +BASE_DIR="/usr/local/share/proxmenux" +UTILS_FILE="$BASE_DIR/utils.sh" +OCI_MANAGER="$LOCAL_SCRIPTS/oci_manager.py" + +# OCI paths - persistent data in proxmenux directory +OCI_DIR="$BASE_DIR/oci" +OCI_CATALOG="$OCI_DIR/catalog.json" +OCI_INSTALLED="$OCI_DIR/installed.json" +OCI_INSTANCES="$OCI_DIR/instances" + +# Source catalog bundled with Scripts +SCRIPTS_CATALOG="$LOCAL_SCRIPTS/oci/catalog.json" + +export BASE_DIR + +if [[ -f "$UTILS_FILE" ]]; then + source "$UTILS_FILE" +fi + +load_language 2>/dev/null || true +initialize_cache 2>/dev/null || true + + +# ========================================================== +# OCI Directory Initialization +# ========================================================== +ensure_oci_directories() { + # Create OCI directories if they don't exist + mkdir -p "$OCI_DIR" + mkdir -p "$OCI_INSTANCES" + + # Copy catalog from Scripts if not present + if [[ ! -f "$OCI_CATALOG" && -f "$SCRIPTS_CATALOG" ]]; then + cp "$SCRIPTS_CATALOG" "$OCI_CATALOG" + msg_ok "Initialized OCI catalog" + fi + + # Create empty installed.json if not present + if [[ ! -f "$OCI_INSTALLED" ]]; then + echo '{"version": "1.0.0", "instances": {}}' > "$OCI_INSTALLED" + fi +} + + +# ========================================================== +# Proxmox OCI Support Detection +# ========================================================== +check_proxmox_oci_support() { + PVE_VERSION="" + OCI_SUPPORTED=false + + # Get Proxmox VE version + if command -v pveversion >/dev/null 2>&1; then + PVE_VERSION=$(pveversion | grep -oP 'pve-manager/\K[0-9]+\.[0-9]+' | head -1) + + # Check if version >= 9.1 + local major minor + major=$(echo "$PVE_VERSION" | cut -d. -f1) + minor=$(echo "$PVE_VERSION" | cut -d. -f2) + + if [[ $major -gt 9 ]] || [[ $major -eq 9 && $minor -ge 1 ]]; then + OCI_SUPPORTED=true + fi + fi +} + +check_oci_support() { + check_proxmox_oci_support + + if [[ "$OCI_SUPPORTED" != "true" ]]; then + msg_error "$(translate "OCI containers require Proxmox VE 9.1 or later.")" + msg_info2 "$(translate "Current version: $PVE_VERSION")" + return 1 + fi + return 0 +} + + +# ========================================================== +# Helper Functions +# ========================================================== +run_oci_manager() { + python3 "$OCI_MANAGER" "$@" +} + +get_app_status() { + local app_id="$1" + run_oci_manager status --app-id "$app_id" 2>/dev/null | jq -r '.state // "not_installed"' +} + +is_installed() { + local app_id="$1" + local status=$(get_app_status "$app_id") + [[ "$status" != "not_installed" ]] +} + + +# ========================================================== +# Secure Gateway Functions +# ========================================================== +deploy_secure_gateway() { + show_proxmenux_logo + msg_title "$(translate "Secure Gateway (Tailscale VPN)")" + + if ! check_oci_support; then + return 1 + fi + + # Check if already installed + if is_installed "secure-gateway"; then + local status=$(get_app_status "secure-gateway") + msg_warn "$(translate "Secure Gateway is already installed.")" + msg_info2 "Status: $status" + echo "" + read -p "$(translate "Press Enter to continue...")" _ + return 0 + fi + + msg_info2 "$(translate "This will deploy a Tailscale VPN gateway for secure remote access.")" + msg_info2 "$(translate "You will need a Tailscale auth key from: https://login.tailscale.com/admin/settings/keys")" + echo "" + + # Get auth key + local auth_key + while true; do + read -p "$(translate "Enter Tailscale Auth Key"): " auth_key + if [[ -z "$auth_key" ]]; then + msg_error "$(translate "Auth key is required.")" + continue + fi + if [[ ! "$auth_key" =~ ^tskey- ]]; then + msg_warn "$(translate "Warning: Auth key should start with 'tskey-'")" + fi + break + done + + # Get hostname + local default_hostname="${HOSTNAME:-proxmox}-gateway" + read -p "$(translate "Device hostname") [$default_hostname]: " hostname + hostname="${hostname:-$default_hostname}" + + # Access mode + echo "" + msg_info2 "$(translate "Access Scope:")" + echo " 1) Host Only - ProxMenux Monitor & Proxmox UI only" + echo " 2) Proxmox Network - Include VMs, LXCs, and host services" + echo " 3) Custom - Select specific networks" + echo "" + + local access_mode="host_only" + local routes="" + + read -p "$(translate "Select access mode") [1]: " mode_choice + case "$mode_choice" in + 2) + access_mode="proxmox_network" + # Auto-detect networks + routes=$(detect_networks_for_routing) + ;; + 3) + access_mode="custom" + msg_info2 "$(translate "Enter networks to advertise (comma-separated CIDR):")" + msg_info2 " Example: 10.0.1.0/24,192.168.1.0/24" + read -p "Networks: " routes + ;; + *) + access_mode="host_only" + ;; + esac + + # Exit node option + local exit_node="false" + read -p "$(translate "Offer as exit node?") [y/N]: " exit_choice + [[ "$exit_choice" =~ ^[Yy] ]] && exit_node="true" + + # Accept routes option + local accept_routes="false" + read -p "$(translate "Accept routes from other nodes?") [y/N]: " accept_choice + [[ "$accept_choice" =~ ^[Yy] ]] && accept_routes="true" + + echo "" + msg_info2 "$(translate "Configuration Summary:")" + echo " Hostname: $hostname" + echo " Access Mode: $access_mode" + [[ -n "$routes" ]] && echo " Networks: $routes" + echo " Exit Node: $exit_node" + echo " Accept Routes: $accept_routes" + echo "" + + read -p "$(translate "Deploy with this configuration?") [Y/n]: " confirm + if [[ "$confirm" =~ ^[Nn] ]]; then + msg_warn "$(translate "Deployment cancelled.")" + return 0 + fi + + # Build config JSON + local routes_array="[]" + if [[ -n "$routes" ]]; then + # Convert comma-separated to JSON array + routes_array=$(echo "$routes" | tr ',' '\n' | jq -R . | jq -s .) + fi + + local config_json=$(cat <&1) + + if echo "$result" | jq -e '.success == true' >/dev/null 2>&1; then + msg_ok "$(translate "Secure Gateway deployed successfully!")" + msg_info2 "$(translate "The gateway should appear in your Tailscale admin console shortly.")" + else + local error_msg=$(echo "$result" | jq -r '.message // "Unknown error"') + msg_error "$(translate "Deployment failed"): $error_msg" + return 1 + fi + + echo "" + read -p "$(translate "Press Enter to continue...")" _ +} + +detect_networks_for_routing() { + # Detect bridge interfaces and their subnets + local networks="" + + for iface in $(ip -o link show | awk -F': ' '{print $2}' | grep -E '^vmbr|^bond' | head -5); do + local subnet=$(ip -4 addr show "$iface" 2>/dev/null | grep -oP 'inet \K[\d.]+/\d+' | head -1) + if [[ -n "$subnet" ]]; then + # Convert IP/prefix to network/prefix + local network=$(python3 -c "import ipaddress; print(ipaddress.IPv4Network('$subnet', strict=False))" 2>/dev/null) + if [[ -n "$network" ]]; then + [[ -n "$networks" ]] && networks="$networks," + networks="$networks$network" + fi + fi + done + + echo "$networks" +} + +manage_secure_gateway() { + show_proxmenux_logo + msg_title "$(translate "Manage Secure Gateway")" + + local status=$(get_app_status "secure-gateway") + + msg_info2 "Current status: $status" + echo "" + + case "$status" in + "running") + echo "1) Stop gateway" + echo "2) Restart gateway" + echo "3) View logs" + echo "4) Remove gateway" + echo "5) Back" + ;; + "stopped"|"exited") + echo "1) Start gateway" + echo "2) View logs" + echo "3) Remove gateway" + echo "4) Back" + ;; + *) + msg_error "$(translate "Gateway is not installed.")" + read -p "$(translate "Press Enter to continue...")" _ + return + ;; + esac + + echo "" + read -p "$(translate "Select option"): " choice + + case "$status" in + "running") + case "$choice" in + 1) action_stop_gateway ;; + 2) action_restart_gateway ;; + 3) action_view_logs ;; + 4) action_remove_gateway ;; + esac + ;; + "stopped"|"exited") + case "$choice" in + 1) action_start_gateway ;; + 2) action_view_logs ;; + 3) action_remove_gateway ;; + esac + ;; + esac +} + +action_start_gateway() { + msg_info "$(translate "Starting gateway...")" + local result=$(run_oci_manager start --app-id "secure-gateway" 2>&1) + if echo "$result" | jq -e '.success == true' >/dev/null 2>&1; then + msg_ok "$(translate "Gateway started.")" + else + msg_error "$(translate "Failed to start gateway.")" + fi + read -p "$(translate "Press Enter to continue...")" _ +} + +action_stop_gateway() { + msg_info "$(translate "Stopping gateway...")" + local result=$(run_oci_manager stop --app-id "secure-gateway" 2>&1) + if echo "$result" | jq -e '.success == true' >/dev/null 2>&1; then + msg_ok "$(translate "Gateway stopped.")" + else + msg_error "$(translate "Failed to stop gateway.")" + fi + read -p "$(translate "Press Enter to continue...")" _ +} + +action_restart_gateway() { + msg_info "$(translate "Restarting gateway...")" + local result=$(run_oci_manager restart --app-id "secure-gateway" 2>&1) + if echo "$result" | jq -e '.success == true' >/dev/null 2>&1; then + msg_ok "$(translate "Gateway restarted.")" + else + msg_error "$(translate "Failed to restart gateway.")" + fi + read -p "$(translate "Press Enter to continue...")" _ +} + +action_view_logs() { + echo "" + msg_info2 "$(translate "Recent logs:")" + echo "----------------------------------------" + $RUNTIME logs --tail 50 proxmenux-secure-gateway 2>/dev/null || echo "No logs available" + echo "----------------------------------------" + echo "" + read -p "$(translate "Press Enter to continue...")" _ +} + +action_remove_gateway() { + echo "" + read -p "$(translate "Remove Secure Gateway? State will be preserved.") [y/N]: " confirm + if [[ ! "$confirm" =~ ^[Yy] ]]; then + return + fi + + msg_info "$(translate "Removing gateway...")" + local result=$(run_oci_manager remove --app-id "secure-gateway" 2>&1) + if echo "$result" | jq -e '.success == true' >/dev/null 2>&1; then + msg_ok "$(translate "Gateway removed.")" + else + msg_error "$(translate "Failed to remove gateway.")" + fi + read -p "$(translate "Press Enter to continue...")" _ +} + + +# ========================================================== +# Main Menu +# ========================================================== +show_oci_menu() { + while true; do + show_proxmenux_logo + msg_title "$(translate "$SCRIPT_TITLE")" + + detect_runtime + + if [[ -z "$RUNTIME" ]]; then + msg_warn "$(translate "No container runtime available.")" + msg_info2 "$(translate "Install podman or docker to continue.")" + echo "" + read -p "$(translate "Press Enter to exit...")" _ + return + fi + + msg_info2 "Runtime: $RUNTIME $RUNTIME_VERSION" + echo "" + + # Check gateway status + local gw_status=$(get_app_status "secure-gateway") + local gw_label="Secure Gateway (Tailscale VPN)" + if [[ "$gw_status" != "not_installed" ]]; then + gw_label="$gw_label [$gw_status]" + fi + + echo "1) $gw_label" + echo "" + echo "0) $(translate "Exit")" + echo "" + + read -p "$(translate "Select option"): " choice + + case "$choice" in + 1) + if [[ "$gw_status" == "not_installed" ]]; then + deploy_secure_gateway + else + manage_secure_gateway + fi + ;; + 0|q|Q) + break + ;; + *) + msg_error "$(translate "Invalid option")" + sleep 1 + ;; + esac + done +} + + +# ========================================================== +# CLI Mode +# ========================================================== +cli_mode() { + local command="$1" + shift + + case "$command" in + deploy) + local app_id="" + local config="" + while [[ $# -gt 0 ]]; do + case "$1" in + --app-id) app_id="$2"; shift 2 ;; + --config) config="$2"; shift 2 ;; + *) shift ;; + esac + done + + if [[ -z "$app_id" ]]; then + echo "Error: --app-id required" + exit 1 + fi + + run_oci_manager deploy --app-id "$app_id" --config "${config:-{}}" --source "cli" + ;; + start|stop|restart|remove|status) + local app_id="" + while [[ $# -gt 0 ]]; do + case "$1" in + --app-id) app_id="$2"; shift 2 ;; + *) shift ;; + esac + done + + if [[ -z "$app_id" ]]; then + echo "Error: --app-id required" + exit 1 + fi + + run_oci_manager "$command" --app-id "$app_id" + ;; + list) + run_oci_manager list + ;; + catalog) + run_oci_manager catalog + ;; + networks) + run_oci_manager networks + ;; + runtime) + run_oci_manager runtime + ;; + *) + echo "Usage: $0 [command] [options]" + echo "" + echo "Commands:" + echo " (no args) Interactive menu" + echo " deploy Deploy an app (--app-id, --config)" + echo " start Start an app (--app-id)" + echo " stop Stop an app (--app-id)" + echo " restart Restart an app (--app-id)" + echo " remove Remove an app (--app-id)" + echo " status Get app status (--app-id)" + echo " list List installed apps" + echo " catalog List available apps" + echo " networks Detect available networks" + echo " runtime Show container runtime info" + exit 1 + ;; + esac +} + + +# ========================================================== +# Entry Point +# ========================================================== +main() { + # Initialize OCI directories and catalog + ensure_oci_directories + + if [[ $# -gt 0 ]]; then + cli_mode "$@" + else + show_oci_menu + fi +} + +main "$@" diff --git a/scripts/storage/add_controller_nvme_vm.sh b/scripts/storage/add_controller_nvme_vm.sh old mode 100755 new mode 100644