Cleanup
This commit is contained in:
parent
804426dd24
commit
43dd7aacc8
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 69 KiB |
504
docs/CHANGELOG_MISC.md
Normal file
504
docs/CHANGELOG_MISC.md
Normal file
@ -0,0 +1,504 @@
|
||||
# Changelog: /misc Directory Refactoring
|
||||
|
||||
> **Last Updated**: November 28, 2025
|
||||
> **Status**: Major Refactoring Complete
|
||||
|
||||
## Overview
|
||||
|
||||
The `/misc` directory has undergone significant refactoring to improve maintainability, security, and functionality. This document tracks all changes, removed files, and new patterns.
|
||||
|
||||
---
|
||||
|
||||
## File Status Summary
|
||||
|
||||
| File | Status | Notes |
|
||||
|------|--------|-------|
|
||||
| `api.func` | ✅ Active | API integration & reporting |
|
||||
| `build.func` | ✅ Refactored | Core build orchestration (Major changes) |
|
||||
| `cloud-init.sh` | ✅ Active | Cloud-Init VM configuration |
|
||||
| `core.func` | ✅ Active | Core utilities & functions |
|
||||
| `error_handler.func` | ✅ Active | Centralized error handling |
|
||||
| `install.func` | ✅ Active | Container installation orchestration |
|
||||
| `passthrough.func` | ✅ Active | Hardware passthrough utilities |
|
||||
| `tools.func` | ✅ Active | Utility functions & repository setup |
|
||||
| `vm-core.func` | ✅ Active | VM-specific core functions |
|
||||
| `config-file.func` | ❌ **REMOVED** | Replaced by defaults system |
|
||||
| `create_lxc.sh` | ❌ **REMOVED** | Replaced by install.func workflow |
|
||||
|
||||
---
|
||||
|
||||
## Major Changes in build.func
|
||||
|
||||
### 1. **Configuration System Overhaul**
|
||||
|
||||
#### ❌ Removed
|
||||
- **`config-file.func` dependency**: Old configuration file format no longer used
|
||||
- **Static configuration approach**: Replaced with dynamic variable-based system
|
||||
|
||||
#### ✅ New System: Three-Tier Defaults Architecture
|
||||
|
||||
```
|
||||
Priority Hierarchy (Highest to Lowest):
|
||||
1. Environment Variables (var_*) ← Highest Priority
|
||||
2. App-Specific Defaults (.vars files)
|
||||
3. User Defaults (default.vars)
|
||||
4. Built-in Defaults ← Fallback
|
||||
```
|
||||
|
||||
### 2. **Variable Whitelisting System**
|
||||
|
||||
A new security layer has been introduced to control which variables can be persisted:
|
||||
|
||||
```bash
|
||||
# Allowed configurable variables
|
||||
VAR_WHITELIST=(
|
||||
var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse
|
||||
var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu
|
||||
var_net var_nesting var_ns var_protection var_pw var_ram var_tags var_timezone
|
||||
var_tun var_unprivileged var_verbose var_vlan var_ssh var_ssh_authorized_key
|
||||
var_container_storage var_template_storage
|
||||
)
|
||||
```
|
||||
|
||||
**Changes from Previous**:
|
||||
- ❌ Removed: `var_ctid` (unique per container, cannot be shared)
|
||||
- ❌ Removed: `var_ipv6_static` (static IPs are container-specific)
|
||||
|
||||
### 3. **Default Settings Management Functions**
|
||||
|
||||
#### `default_var_settings()`
|
||||
- Creates/updates global user defaults at `/usr/local/community-scripts/default.vars`
|
||||
- Loads existing defaults and merges with current settings
|
||||
- Respects environment variable precedence
|
||||
- Sanitizes values to prevent injection attacks
|
||||
|
||||
#### `get_app_defaults_path()`
|
||||
- Returns app-specific defaults path: `/usr/local/community-scripts/defaults/<appname>.vars`
|
||||
- Example: `/usr/local/community-scripts/defaults/pihole.vars`
|
||||
|
||||
#### `maybe_offer_save_app_defaults()`
|
||||
- Called after advanced installation
|
||||
- Offers to save current settings as app-specific defaults
|
||||
- Provides diff view when updating existing defaults
|
||||
- Validates against whitelist before saving
|
||||
|
||||
### 4. **Load Variables File Function**
|
||||
|
||||
#### `load_vars_file()`
|
||||
- Safely loads variables from `.vars` files
|
||||
- **Key Security Feature**: Does NOT use `source` or `eval`
|
||||
- Manual parsing with whitelist validation
|
||||
- Handles escaping and special characters
|
||||
- Returns 0 on success, 1 on failure
|
||||
|
||||
**Example Usage**:
|
||||
```bash
|
||||
load_vars_file "/usr/local/community-scripts/defaults/pihole.vars"
|
||||
```
|
||||
|
||||
### 5. **Removed Functions**
|
||||
|
||||
- ❌ `create_lxc()` - Replaced by install.func workflow
|
||||
- ❌ `read_config()` - Replaced by load_vars_file()
|
||||
- ❌ `write_config()` - Replaced by direct file generation with sanitization
|
||||
|
||||
---
|
||||
|
||||
## Installation Modes & Workflows
|
||||
|
||||
### Mode 1: **Default Settings**
|
||||
```
|
||||
Quick installation with pre-defined values
|
||||
├── User selects OS/Version
|
||||
├── Uses built-in defaults
|
||||
└── Creates container immediately
|
||||
```
|
||||
|
||||
**Use Case**: First-time users, basic deployments
|
||||
|
||||
### Mode 2: **Advanced Settings**
|
||||
```
|
||||
Full control over all parameters
|
||||
├── User prompted for each setting
|
||||
├── 19-step configuration wizard
|
||||
├── Shows summary before confirmation
|
||||
└── Offers to save as app defaults
|
||||
```
|
||||
|
||||
**Use Case**: Custom configurations, experienced users
|
||||
|
||||
### Mode 3: **User Defaults** (formerly "My Defaults")
|
||||
```
|
||||
Installation using saved user defaults
|
||||
├── Loads: /usr/local/community-scripts/default.vars
|
||||
├── Shows loaded settings summary
|
||||
└── Creates container
|
||||
```
|
||||
|
||||
**Use Case**: Consistent deployments across multiple containers
|
||||
|
||||
### Mode 4: **App Defaults**
|
||||
```
|
||||
Installation using app-specific defaults (if available)
|
||||
├── Loads: /usr/local/community-scripts/defaults/<app>.vars
|
||||
├── Shows loaded settings summary
|
||||
└── Creates container
|
||||
```
|
||||
|
||||
**Use Case**: Repeat installations with saved configurations
|
||||
|
||||
### Mode 5: **Settings Menu**
|
||||
```
|
||||
Manage configuration files
|
||||
├── View current settings
|
||||
├── Edit storage selections
|
||||
├── Manage defaults location
|
||||
└── Reset to built-ins
|
||||
```
|
||||
|
||||
**Use Case**: Configuration management
|
||||
|
||||
---
|
||||
|
||||
## Configurable Variables Reference
|
||||
|
||||
### Resource Allocation
|
||||
|
||||
| Variable | Type | Default | Example |
|
||||
|----------|------|---------|---------|
|
||||
| `var_cpu` | Integer | App-dependent | `4` |
|
||||
| `var_ram` | Integer (MB) | App-dependent | `2048` |
|
||||
| `var_disk` | Integer (GB) | App-dependent | `20` |
|
||||
| `var_unprivileged` | Boolean (0/1) | `1` | `1` |
|
||||
|
||||
### Network Configuration
|
||||
|
||||
| Variable | Type | Default | Example |
|
||||
|----------|------|---------|---------|
|
||||
| `var_net` | String | Auto | `veth` |
|
||||
| `var_brg` | String | `vmbr0` | `vmbr100` |
|
||||
| `var_gateway` | IP Address | Auto-detected | `192.168.1.1` |
|
||||
| `var_mtu` | Integer | `1500` | `9000` |
|
||||
| `var_vlan` | Integer | None | `100` |
|
||||
|
||||
### Identity & Access
|
||||
|
||||
| Variable | Type | Default | Example |
|
||||
|----------|------|---------|---------|
|
||||
| `var_hostname` | String | App name | `mypihole` |
|
||||
| `var_pw` | String | Random | `MySecurePass123!` |
|
||||
| `var_ssh` | Boolean (yes/no) | `no` | `yes` |
|
||||
| `var_ssh_authorized_key` | String | None | `ssh-rsa AAAA...` |
|
||||
|
||||
### Container Features
|
||||
|
||||
| Variable | Type | Default | Example |
|
||||
|----------|------|---------|---------|
|
||||
| `var_fuse` | Boolean (0/1) | `0` | `1` |
|
||||
| `var_tun` | Boolean (0/1) | `0` | `1` |
|
||||
| `var_nesting` | Boolean (0/1) | `0` | `1` |
|
||||
| `var_keyctl` | Boolean (0/1) | `0` | `1` |
|
||||
| `var_mknod` | Boolean (0/1) | `0` | `1` |
|
||||
| `var_mount_fs` | String | None | `ext4` |
|
||||
| `var_protection` | Boolean (0/1) | `0` | `1` |
|
||||
|
||||
### System Configuration
|
||||
|
||||
| Variable | Type | Default | Example |
|
||||
|----------|------|---------|---------|
|
||||
| `var_timezone` | String | System | `Europe/Berlin` |
|
||||
| `var_searchdomain` | String | None | `example.com` |
|
||||
| `var_apt_cacher` | String | None | `apt-cacher-ng` |
|
||||
| `var_apt_cacher_ip` | IP Address | None | `192.168.1.100` |
|
||||
| `var_tags` | String | App name | `docker,production` |
|
||||
| `var_verbose` | Boolean (yes/no) | `no` | `yes` |
|
||||
|
||||
### Storage Configuration
|
||||
|
||||
| Variable | Type | Default | Example |
|
||||
|----------|------|---------|---------|
|
||||
| `var_container_storage` | String | Auto-detected | `local` |
|
||||
| `var_template_storage` | String | Auto-detected | `local` |
|
||||
|
||||
---
|
||||
|
||||
## File Formats
|
||||
|
||||
### User Defaults: `/usr/local/community-scripts/default.vars`
|
||||
|
||||
```bash
|
||||
# User Global Defaults
|
||||
# Generated by ProxmoxVED Scripts
|
||||
# Date: 2024-11-28
|
||||
|
||||
var_cpu=4
|
||||
var_ram=2048
|
||||
var_disk=20
|
||||
var_unprivileged=1
|
||||
var_brg=vmbr0
|
||||
var_gateway=192.168.1.1
|
||||
var_vlan=100
|
||||
var_mtu=1500
|
||||
var_hostname=mydefaults
|
||||
var_timezone=Europe/Berlin
|
||||
var_ssh=yes
|
||||
var_ssh_authorized_key=ssh-rsa AAAAB3NzaC1...
|
||||
var_container_storage=local
|
||||
var_template_storage=local
|
||||
```
|
||||
|
||||
### App Defaults: `/usr/local/community-scripts/defaults/<app>.vars`
|
||||
|
||||
```bash
|
||||
# App-specific defaults for PiHole (pihole)
|
||||
# Generated on 2024-11-28T15:32:00Z
|
||||
|
||||
var_unprivileged=1
|
||||
var_cpu=2
|
||||
var_ram=1024
|
||||
var_disk=10
|
||||
var_brg=vmbr0
|
||||
var_net=veth
|
||||
var_gateway=192.168.1.1
|
||||
var_mtu=1500
|
||||
var_vlan=100
|
||||
var_hostname=pihole
|
||||
var_timezone=Europe/Berlin
|
||||
var_container_storage=local
|
||||
var_template_storage=local
|
||||
var_tags=dns,pihole
|
||||
var_verbose=no
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Example 1: Set Global User Defaults
|
||||
|
||||
1. Run any app installation script
|
||||
2. Select **Advanced Settings**
|
||||
3. Configure all parameters
|
||||
4. When prompted: **"Save as User Defaults?"** → Select **Yes**
|
||||
5. File saved to: `/usr/local/community-scripts/default.vars`
|
||||
|
||||
**Future Installations**: Select **User Defaults** mode to reuse settings
|
||||
|
||||
### Example 2: Create & Use App Defaults
|
||||
|
||||
1. Run app installation (e.g., `pihole-install.sh`)
|
||||
2. Select **Advanced Settings**
|
||||
3. Fine-tune all parameters for PiHole
|
||||
4. When prompted: **"Save as App Defaults for PiHole?"** → Select **Yes**
|
||||
5. File saved to: `/usr/local/community-scripts/defaults/pihole.vars`
|
||||
|
||||
**Next Time**:
|
||||
- Run `pihole-install.sh` again
|
||||
- Select **App Defaults**
|
||||
- Same settings automatically applied
|
||||
|
||||
### Example 3: Override via Environment Variables
|
||||
|
||||
```bash
|
||||
# Set custom values before running script
|
||||
export var_cpu=8
|
||||
export var_ram=4096
|
||||
export var_hostname=custom-pihole
|
||||
|
||||
bash pihole-install.sh
|
||||
```
|
||||
|
||||
**Priority**: Environment variables override all defaults
|
||||
|
||||
### Example 4: Manual File Editing
|
||||
|
||||
```bash
|
||||
# Edit User Defaults
|
||||
sudo nano /usr/local/community-scripts/default.vars
|
||||
|
||||
# Edit App-Specific Defaults
|
||||
sudo nano /usr/local/community-scripts/defaults/pihole.vars
|
||||
|
||||
# Verify syntax (no source/eval, safe to read)
|
||||
cat /usr/local/community-scripts/default.vars
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Security Improvements
|
||||
|
||||
### 1. **No `source` or `eval` Used**
|
||||
- ❌ OLD: `source config_file` (Dangerous - executes arbitrary code)
|
||||
- ✅ NEW: `load_vars_file()` (Safe - manual parsing with validation)
|
||||
|
||||
### 2. **Variable Whitelisting**
|
||||
- Only explicitly allowed variables can be persisted
|
||||
- Prevents accidental storage of sensitive values
|
||||
- Protects against injection attacks
|
||||
|
||||
### 3. **Value Sanitization**
|
||||
```bash
|
||||
# Prevents command injection
|
||||
_sanitize_value() {
|
||||
case "$1" in
|
||||
*'$('* | *'`'* | *';'* | *'&'* | *'<('*)
|
||||
return 1 # Reject dangerous values
|
||||
;;
|
||||
esac
|
||||
echo "$1"
|
||||
}
|
||||
```
|
||||
|
||||
### 4. **File Permissions**
|
||||
```bash
|
||||
# Default vars accessible only to root
|
||||
-rw-r--r-- root root /usr/local/community-scripts/default.vars
|
||||
-rw-r--r-- root root /usr/local/community-scripts/defaults/pihole.vars
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Migration Guide
|
||||
|
||||
### For Users
|
||||
|
||||
**OLD Workflow**: Manual config file editing
|
||||
**NEW Workflow**:
|
||||
1. Run installation script
|
||||
2. Select "Advanced Settings"
|
||||
3. Answer prompts
|
||||
4. Save as defaults when offered
|
||||
|
||||
### For Script Developers
|
||||
|
||||
**OLD Pattern**:
|
||||
```bash
|
||||
source /path/to/config-file.conf
|
||||
```
|
||||
|
||||
**NEW Pattern**:
|
||||
```bash
|
||||
# User defaults are automatically loaded in build.func
|
||||
# No manual intervention needed
|
||||
# Just use the variables directly
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Removed Components
|
||||
|
||||
### `config-file.func` (Deprecated)
|
||||
|
||||
**Reason**: Replaced by three-tier defaults system
|
||||
- Static configuration was inflexible
|
||||
- Manual editing error-prone
|
||||
- No validation or sanitization
|
||||
|
||||
**Migration Path**: Use app/user defaults system
|
||||
|
||||
### `create_lxc.sh` (Deprecated)
|
||||
|
||||
**Reason**: Workflow integrated into install.func
|
||||
- Centralized container creation logic
|
||||
- Better error handling
|
||||
- Unified with VM creation
|
||||
|
||||
**Migration Path**: Use install.func directly
|
||||
|
||||
---
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
### Planned Features
|
||||
|
||||
1. **Configuration UI**: Web-based settings editor
|
||||
2. **Configuration Sync**: Push defaults to multiple nodes
|
||||
3. **Configuration History**: Track changes and diffs
|
||||
4. **Batch Operations**: Apply defaults to multiple containers
|
||||
5. **Configuration Templates**: Pre-built setting templates per app
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Issue: Defaults not loading
|
||||
|
||||
**Solution**:
|
||||
```bash
|
||||
# Check if defaults file exists
|
||||
ls -la /usr/local/community-scripts/default.vars
|
||||
|
||||
# Verify syntax
|
||||
cat /usr/local/community-scripts/default.vars
|
||||
|
||||
# Check file permissions
|
||||
sudo chown root:root /usr/local/community-scripts/default.vars
|
||||
sudo chmod 644 /usr/local/community-scripts/default.vars
|
||||
```
|
||||
|
||||
### Issue: Variable not being applied
|
||||
|
||||
**Solution**:
|
||||
1. Check if variable is in `VAR_WHITELIST`
|
||||
2. Verify variable name starts with `var_`
|
||||
3. Check syntax in .vars file (no spaces around `=`)
|
||||
4. Use `cat` not `source` to read files
|
||||
|
||||
### Issue: "Invalid option" in defaults menu
|
||||
|
||||
**Solution**:
|
||||
- Ensure defaults directory exists: `/usr/local/community-scripts/defaults/`
|
||||
- Create if missing: `sudo mkdir -p /usr/local/community-scripts/defaults/`
|
||||
|
||||
---
|
||||
|
||||
## Technical Reference
|
||||
|
||||
### Variable Loading Precedence
|
||||
|
||||
```
|
||||
1. parse ARGV
|
||||
2. capture ENV variables (hard environment)
|
||||
3. load defaults file if exists
|
||||
4. load app-specific defaults if exists
|
||||
5. parse command line flags (lowest priority for overrides)
|
||||
|
||||
Precedence (Highest to Lowest):
|
||||
ENV var_* > AppDefaults.vars > UserDefaults.vars > Built-ins
|
||||
```
|
||||
|
||||
### State Machine: Installation Modes
|
||||
|
||||
```
|
||||
┌─────────────────┐
|
||||
│ Start Script │
|
||||
└────────┬────────┘
|
||||
│
|
||||
┌────v────────────────┐
|
||||
│ Display Mode Menu │
|
||||
└────┬─────────────────┘
|
||||
│
|
||||
┌────────────────────────────────────┐
|
||||
│ User Selects Mode │
|
||||
├──────────┬──────────┬──────────┬──────────┐
|
||||
│ │ │ │ │
|
||||
v v v v v
|
||||
┌─────┐ ┌────────┐ ┌──────────┐ ┌─────────┐ ┌───────┐
|
||||
│Def. │ │Adv. │ │User │ │App │ │Setting│
|
||||
│Set. │ │Set. │ │Default │ │Default │ │Menu │
|
||||
└─────┘ └────────┘ └──────────┘ └─────────┘ └───────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Document Versions
|
||||
|
||||
| Version | Date | Changes |
|
||||
|---------|------|---------|
|
||||
| 1.0 | 2024-11-28 | Initial comprehensive documentation |
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: November 28, 2025
|
||||
**Maintainers**: community-scripts Team
|
||||
**License**: MIT
|
||||
748
docs/DEFAULTS_SYSTEM_GUIDE.md
Normal file
748
docs/DEFAULTS_SYSTEM_GUIDE.md
Normal file
@ -0,0 +1,748 @@
|
||||
# Configuration & Defaults System - User Guide
|
||||
|
||||
> **Complete Guide to App Defaults and User Defaults**
|
||||
>
|
||||
> *Learn how to configure, save, and reuse your installation settings*
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Quick Start](#quick-start)
|
||||
2. [Understanding the Defaults System](#understanding-the-defaults-system)
|
||||
3. [Installation Modes](#installation-modes)
|
||||
4. [How to Save Defaults](#how-to-save-defaults)
|
||||
5. [How to Use Saved Defaults](#how-to-use-saved-defaults)
|
||||
6. [Managing Your Defaults](#managing-your-defaults)
|
||||
7. [Advanced Configuration](#advanced-configuration)
|
||||
8. [Troubleshooting](#troubleshooting)
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 30-Second Setup
|
||||
|
||||
```bash
|
||||
# 1. Run any container installation script
|
||||
bash pihole-install.sh
|
||||
|
||||
# 2. When prompted, select: "Advanced Settings"
|
||||
# (This allows you to customize everything)
|
||||
|
||||
# 3. Answer all configuration questions
|
||||
|
||||
# 4. At the end, when asked "Save as App Defaults?"
|
||||
# Select: YES
|
||||
|
||||
# 5. Done! Your settings are now saved
|
||||
```
|
||||
|
||||
**Next Time**: Run the same script again, select **"App Defaults"** and your settings will be applied automatically!
|
||||
|
||||
---
|
||||
|
||||
## Understanding the Defaults System
|
||||
|
||||
### The Three-Tier System
|
||||
|
||||
Your installation settings are managed through three layers:
|
||||
|
||||
#### 🔷 **Tier 1: Built-in Defaults** (Fallback)
|
||||
```
|
||||
These are hardcoded in the scripts
|
||||
Provide sensible defaults for each application
|
||||
Example: PiHole uses 2 CPU cores by default
|
||||
```
|
||||
|
||||
#### 🔶 **Tier 2: User Defaults** (Global)
|
||||
```
|
||||
Your personal global defaults
|
||||
Applied to ALL container installations
|
||||
Location: /usr/local/community-scripts/default.vars
|
||||
Example: "I always want 4 CPU cores and 2GB RAM"
|
||||
```
|
||||
|
||||
#### 🔴 **Tier 3: App Defaults** (Specific)
|
||||
```
|
||||
Application-specific saved settings
|
||||
Only applied when installing that specific app
|
||||
Location: /usr/local/community-scripts/defaults/<appname>.vars
|
||||
Example: "Whenever I install PiHole, use these exact settings"
|
||||
```
|
||||
|
||||
### Priority System
|
||||
|
||||
When installing a container, settings are applied in this order:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ 1. Environment Variables (HIGHEST) │ Set in shell: export var_cpu=8
|
||||
│ (these override everything) │
|
||||
├─────────────────────────────────────┤
|
||||
│ 2. App Defaults │ From: defaults/pihole.vars
|
||||
│ (app-specific saved settings) │
|
||||
├─────────────────────────────────────┤
|
||||
│ 3. User Defaults │ From: default.vars
|
||||
│ (your global defaults) │
|
||||
├─────────────────────────────────────┤
|
||||
│ 4. Built-in Defaults (LOWEST) │ Hardcoded in script
|
||||
│ (failsafe, always available) │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**In Plain English**:
|
||||
- If you set an environment variable → it wins
|
||||
- Otherwise, if you have app-specific defaults → use those
|
||||
- Otherwise, if you have user defaults → use those
|
||||
- Otherwise, use the hardcoded defaults
|
||||
|
||||
---
|
||||
|
||||
## Installation Modes
|
||||
|
||||
When you run any installation script, you'll be presented with a menu:
|
||||
|
||||
### Option 1️⃣ : **Default Settings**
|
||||
|
||||
```
|
||||
Quick installation with standard settings
|
||||
├─ Best for: First-time users, quick deployments
|
||||
├─ What happens:
|
||||
│ 1. Script uses built-in defaults
|
||||
│ 2. Container created immediately
|
||||
│ 3. No questions asked
|
||||
└─ Time: ~2 minutes
|
||||
```
|
||||
|
||||
**When to use**: You want a standard installation, don't need customization
|
||||
|
||||
---
|
||||
|
||||
### Option 2️⃣ : **Advanced Settings**
|
||||
|
||||
```
|
||||
Full customization with 19 configuration steps
|
||||
├─ Best for: Power users, custom requirements
|
||||
├─ What happens:
|
||||
│ 1. Script asks for EVERY setting
|
||||
│ 2. You control: CPU, RAM, Disk, Network, SSH, etc.
|
||||
│ 3. Shows summary before creating
|
||||
│ 4. Offers to save as App Defaults
|
||||
└─ Time: ~5-10 minutes
|
||||
```
|
||||
|
||||
**When to use**: You want full control over the configuration
|
||||
|
||||
**Available Settings**:
|
||||
- CPU cores, RAM amount, Disk size
|
||||
- Container name, network settings
|
||||
- SSH access, API access, Features
|
||||
- Password, SSH keys, Tags
|
||||
|
||||
---
|
||||
|
||||
### Option 3️⃣ : **User Defaults**
|
||||
|
||||
```
|
||||
Use your saved global defaults
|
||||
├─ Best for: Consistent deployments across many containers
|
||||
├─ Requires: You've previously saved User Defaults
|
||||
├─ What happens:
|
||||
│ 1. Loads settings from: /usr/local/community-scripts/default.vars
|
||||
│ 2. Shows you the loaded settings
|
||||
│ 3. Creates container immediately
|
||||
└─ Time: ~2 minutes
|
||||
```
|
||||
|
||||
**When to use**: You have preferred defaults you want to use for every app
|
||||
|
||||
---
|
||||
|
||||
### Option 4️⃣ : **App Defaults** (if available)
|
||||
|
||||
```
|
||||
Use previously saved app-specific defaults
|
||||
├─ Best for: Repeating the same configuration multiple times
|
||||
├─ Requires: You've previously saved App Defaults for this app
|
||||
├─ What happens:
|
||||
│ 1. Loads settings from: /usr/local/community-scripts/defaults/<app>.vars
|
||||
│ 2. Shows you the loaded settings
|
||||
│ 3. Creates container immediately
|
||||
└─ Time: ~2 minutes
|
||||
```
|
||||
|
||||
**When to use**: You've installed this app before and want identical settings
|
||||
|
||||
---
|
||||
|
||||
### Option 5️⃣ : **Settings Menu**
|
||||
|
||||
```
|
||||
Manage your saved configurations
|
||||
├─ Functions:
|
||||
│ • View current settings
|
||||
│ • Edit storage selections
|
||||
│ • Manage defaults location
|
||||
│ • See what's currently configured
|
||||
└─ Time: ~1 minute
|
||||
```
|
||||
|
||||
**When to use**: You want to review or modify saved settings
|
||||
|
||||
---
|
||||
|
||||
## How to Save Defaults
|
||||
|
||||
### Method 1: Save While Installing
|
||||
|
||||
This is the easiest way:
|
||||
|
||||
#### Step-by-Step: Create App Defaults
|
||||
|
||||
```bash
|
||||
# 1. Run the installation script
|
||||
bash pihole-install.sh
|
||||
|
||||
# 2. Choose installation mode
|
||||
# ┌─────────────────────────┐
|
||||
# │ Select installation mode:│
|
||||
# │ 1) Default Settings │
|
||||
# │ 2) Advanced Settings │
|
||||
# │ 3) User Defaults │
|
||||
# │ 4) App Defaults │
|
||||
# │ 5) Settings Menu │
|
||||
# └─────────────────────────┘
|
||||
#
|
||||
# Enter: 2 (Advanced Settings)
|
||||
|
||||
# 3. Answer all configuration questions
|
||||
# • Container name? → my-pihole
|
||||
# • CPU cores? → 4
|
||||
# • RAM amount? → 2048
|
||||
# • Disk size? → 20
|
||||
# • SSH access? → yes
|
||||
# ... (more options)
|
||||
|
||||
# 4. Review summary (shown before creation)
|
||||
# ✓ Confirm to proceed
|
||||
|
||||
# 5. After creation completes, you'll see:
|
||||
# ┌──────────────────────────────────┐
|
||||
# │ Save as App Defaults for PiHole? │
|
||||
# │ (Yes/No) │
|
||||
# └──────────────────────────────────┘
|
||||
#
|
||||
# Select: Yes
|
||||
|
||||
# 6. Done! Settings saved to:
|
||||
# /usr/local/community-scripts/defaults/pihole.vars
|
||||
```
|
||||
|
||||
#### Step-by-Step: Create User Defaults
|
||||
|
||||
```bash
|
||||
# Same as App Defaults, but:
|
||||
# When you select "Advanced Settings"
|
||||
# FIRST app you run with this selection will offer
|
||||
# to save as "User Defaults" additionally
|
||||
|
||||
# This saves to: /usr/local/community-scripts/default.vars
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Method 2: Manual File Creation
|
||||
|
||||
For advanced users who want to create defaults without running installation:
|
||||
|
||||
```bash
|
||||
# Create User Defaults manually
|
||||
sudo tee /usr/local/community-scripts/default.vars > /dev/null << 'EOF'
|
||||
# Global User Defaults
|
||||
var_cpu=4
|
||||
var_ram=2048
|
||||
var_disk=20
|
||||
var_unprivileged=1
|
||||
var_brg=vmbr0
|
||||
var_gateway=192.168.1.1
|
||||
var_timezone=Europe/Berlin
|
||||
var_ssh=yes
|
||||
var_container_storage=local
|
||||
var_template_storage=local
|
||||
EOF
|
||||
|
||||
# Create App Defaults manually
|
||||
sudo tee /usr/local/community-scripts/defaults/pihole.vars > /dev/null << 'EOF'
|
||||
# App-specific defaults for PiHole
|
||||
var_unprivileged=1
|
||||
var_cpu=2
|
||||
var_ram=1024
|
||||
var_disk=10
|
||||
var_brg=vmbr0
|
||||
var_gateway=192.168.1.1
|
||||
var_hostname=pihole
|
||||
var_container_storage=local
|
||||
var_template_storage=local
|
||||
EOF
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Method 3: Using Environment Variables
|
||||
|
||||
Set defaults via environment before running:
|
||||
|
||||
```bash
|
||||
# Set as environment variables
|
||||
export var_cpu=4
|
||||
export var_ram=2048
|
||||
export var_disk=20
|
||||
export var_hostname=my-container
|
||||
|
||||
# Run installation
|
||||
bash pihole-install.sh
|
||||
|
||||
# These settings will be used
|
||||
# (Can still be overridden by saved defaults)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## How to Use Saved Defaults
|
||||
|
||||
### Using User Defaults
|
||||
|
||||
```bash
|
||||
# 1. Run any installation script
|
||||
bash pihole-install.sh
|
||||
|
||||
# 2. When asked for mode, select:
|
||||
# Option: 3 (User Defaults)
|
||||
|
||||
# 3. Your settings from default.vars are applied
|
||||
# 4. Container created with your saved settings
|
||||
```
|
||||
|
||||
### Using App Defaults
|
||||
|
||||
```bash
|
||||
# 1. Run the app you configured before
|
||||
bash pihole-install.sh
|
||||
|
||||
# 2. When asked for mode, select:
|
||||
# Option: 4 (App Defaults)
|
||||
|
||||
# 3. Your settings from defaults/pihole.vars are applied
|
||||
# 4. Container created with exact same settings
|
||||
```
|
||||
|
||||
### Overriding Saved Defaults
|
||||
|
||||
```bash
|
||||
# Even if you have defaults saved,
|
||||
# you can override them with environment variables
|
||||
|
||||
export var_cpu=8 # Override saved defaults
|
||||
export var_hostname=custom-name
|
||||
|
||||
bash pihole-install.sh
|
||||
# Installation will use these values instead of saved defaults
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Managing Your Defaults
|
||||
|
||||
### View Your Settings
|
||||
|
||||
#### View User Defaults
|
||||
```bash
|
||||
cat /usr/local/community-scripts/default.vars
|
||||
```
|
||||
|
||||
#### View App Defaults
|
||||
```bash
|
||||
cat /usr/local/community-scripts/defaults/pihole.vars
|
||||
```
|
||||
|
||||
#### List All Saved App Defaults
|
||||
```bash
|
||||
ls -la /usr/local/community-scripts/defaults/
|
||||
```
|
||||
|
||||
### Edit Your Settings
|
||||
|
||||
#### Edit User Defaults
|
||||
```bash
|
||||
sudo nano /usr/local/community-scripts/default.vars
|
||||
```
|
||||
|
||||
#### Edit App Defaults
|
||||
```bash
|
||||
sudo nano /usr/local/community-scripts/defaults/pihole.vars
|
||||
```
|
||||
|
||||
### Update Existing Defaults
|
||||
|
||||
```bash
|
||||
# Run installation again with your app
|
||||
bash pihole-install.sh
|
||||
|
||||
# Select: Advanced Settings
|
||||
# Make desired changes
|
||||
# At end, when asked to save:
|
||||
# "Defaults already exist, Update?"
|
||||
# Select: Yes
|
||||
|
||||
# Your saved defaults are updated
|
||||
```
|
||||
|
||||
### Delete Defaults
|
||||
|
||||
#### Delete User Defaults
|
||||
```bash
|
||||
sudo rm /usr/local/community-scripts/default.vars
|
||||
```
|
||||
|
||||
#### Delete App Defaults
|
||||
```bash
|
||||
sudo rm /usr/local/community-scripts/defaults/pihole.vars
|
||||
```
|
||||
|
||||
#### Delete All App Defaults
|
||||
```bash
|
||||
sudo rm /usr/local/community-scripts/defaults/*
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Advanced Configuration
|
||||
|
||||
### Available Variables
|
||||
|
||||
All configurable variables start with `var_`:
|
||||
|
||||
#### Resource Allocation
|
||||
```bash
|
||||
var_cpu=4 # CPU cores
|
||||
var_ram=2048 # RAM in MB
|
||||
var_disk=20 # Disk in GB
|
||||
var_unprivileged=1 # 0=privileged, 1=unprivileged
|
||||
```
|
||||
|
||||
#### Network
|
||||
```bash
|
||||
var_brg=vmbr0 # Bridge interface
|
||||
var_net=veth # Network driver
|
||||
var_gateway=192.168.1.1 # Default gateway
|
||||
var_mtu=1500 # MTU size
|
||||
var_vlan=100 # VLAN ID
|
||||
```
|
||||
|
||||
#### System
|
||||
```bash
|
||||
var_hostname=pihole # Container name
|
||||
var_timezone=Europe/Berlin # Timezone
|
||||
var_pw=SecurePass123 # Root password
|
||||
var_tags=dns,pihole # Tags for organization
|
||||
var_verbose=yes # Enable verbose output
|
||||
```
|
||||
|
||||
#### Security & Access
|
||||
```bash
|
||||
var_ssh=yes # Enable SSH
|
||||
var_ssh_authorized_key="ssh-rsa AA..." # SSH public key
|
||||
var_protection=1 # Enable protection flag
|
||||
```
|
||||
|
||||
#### Features
|
||||
```bash
|
||||
var_fuse=1 # FUSE filesystem support
|
||||
var_tun=1 # TUN device support
|
||||
var_nesting=1 # Nesting (Docker in LXC)
|
||||
var_keyctl=1 # Keyctl syscall
|
||||
var_mknod=1 # Device node creation
|
||||
```
|
||||
|
||||
#### Storage
|
||||
```bash
|
||||
var_container_storage=local # Where to store container
|
||||
var_template_storage=local # Where to store templates
|
||||
```
|
||||
|
||||
### Example Configuration Files
|
||||
|
||||
#### Gaming Server Defaults
|
||||
```bash
|
||||
# High performance for gaming containers
|
||||
var_cpu=8
|
||||
var_ram=4096
|
||||
var_disk=50
|
||||
var_unprivileged=0
|
||||
var_fuse=1
|
||||
var_nesting=1
|
||||
var_tags=gaming
|
||||
```
|
||||
|
||||
#### Development Server
|
||||
```bash
|
||||
# Development with Docker support
|
||||
var_cpu=4
|
||||
var_ram=2048
|
||||
var_disk=30
|
||||
var_unprivileged=1
|
||||
var_nesting=1
|
||||
var_ssh=yes
|
||||
var_tags=development
|
||||
```
|
||||
|
||||
#### IoT/Monitoring
|
||||
```bash
|
||||
# Low-resource, always-on containers
|
||||
var_cpu=2
|
||||
var_ram=512
|
||||
var_disk=10
|
||||
var_unprivileged=1
|
||||
var_nesting=0
|
||||
var_fuse=0
|
||||
var_tun=0
|
||||
var_tags=iot,monitoring
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "App Defaults not available" Message
|
||||
|
||||
**Problem**: You want to use App Defaults, but option says they're not available
|
||||
|
||||
**Solution**:
|
||||
1. You haven't created App Defaults yet for this app
|
||||
2. Run the app with "Advanced Settings"
|
||||
3. When finished, save as App Defaults
|
||||
4. Next time, App Defaults will be available
|
||||
|
||||
---
|
||||
|
||||
### "Settings not being applied"
|
||||
|
||||
**Problem**: You saved defaults, but they're not being used
|
||||
|
||||
**Checklist**:
|
||||
```bash
|
||||
# 1. Verify files exist
|
||||
ls -la /usr/local/community-scripts/default.vars
|
||||
ls -la /usr/local/community-scripts/defaults/<app>.vars
|
||||
|
||||
# 2. Check file permissions (should be readable)
|
||||
stat /usr/local/community-scripts/default.vars
|
||||
|
||||
# 3. Verify correct mode selected
|
||||
# (Make sure you selected "User Defaults" or "App Defaults")
|
||||
|
||||
# 4. Check for environment variable override
|
||||
env | grep var_
|
||||
# If you have var_* set in environment,
|
||||
# those override your saved defaults
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### "Cannot write to defaults location"
|
||||
|
||||
**Problem**: Permission denied when saving defaults
|
||||
|
||||
**Solution**:
|
||||
```bash
|
||||
# Create the defaults directory if missing
|
||||
sudo mkdir -p /usr/local/community-scripts/defaults
|
||||
|
||||
# Fix permissions
|
||||
sudo chmod 755 /usr/local/community-scripts
|
||||
sudo chmod 755 /usr/local/community-scripts/defaults
|
||||
|
||||
# Make sure you're running as root
|
||||
sudo bash pihole-install.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### "Defaults directory doesn't exist"
|
||||
|
||||
**Problem**: Script can't find where to save defaults
|
||||
|
||||
**Solution**:
|
||||
```bash
|
||||
# Create the directory
|
||||
sudo mkdir -p /usr/local/community-scripts/defaults
|
||||
|
||||
# Verify
|
||||
ls -la /usr/local/community-scripts/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Settings seem random or wrong
|
||||
|
||||
**Problem**: Container gets different settings than expected
|
||||
|
||||
**Possible Causes & Solutions**:
|
||||
|
||||
```bash
|
||||
# 1. Check if environment variables are set
|
||||
env | grep var_
|
||||
# If you see var_* entries, those override your defaults
|
||||
# Clear them: unset var_cpu var_ram (etc)
|
||||
|
||||
# 2. Verify correct defaults are in files
|
||||
cat /usr/local/community-scripts/default.vars
|
||||
cat /usr/local/community-scripts/defaults/pihole.vars
|
||||
|
||||
# 3. Check which mode you actually selected
|
||||
# (Script output shows which defaults were applied)
|
||||
|
||||
# 4. Check Proxmox logs for errors
|
||||
sudo journalctl -u pve-daemon -n 50
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### "Variable not recognized"
|
||||
|
||||
**Problem**: You set a variable that doesn't work
|
||||
|
||||
**Solution**:
|
||||
Only certain variables are allowed (security whitelist):
|
||||
|
||||
```
|
||||
Allowed variables (starting with var_):
|
||||
✓ var_cpu, var_ram, var_disk, var_unprivileged
|
||||
✓ var_brg, var_gateway, var_mtu, var_vlan, var_net
|
||||
✓ var_hostname, var_pw, var_timezone
|
||||
✓ var_ssh, var_ssh_authorized_key
|
||||
✓ var_fuse, var_tun, var_nesting, var_keyctl
|
||||
✓ var_container_storage, var_template_storage
|
||||
✓ var_tags, var_verbose
|
||||
✓ var_apt_cacher, var_apt_cacher_ip
|
||||
✓ var_protection, var_mount_fs
|
||||
|
||||
✗ Other variables are NOT supported
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
### ✅ Do's
|
||||
|
||||
✓ Use **App Defaults** when you want app-specific settings
|
||||
✓ Use **User Defaults** for your global preferences
|
||||
✓ Edit defaults files directly with `nano` (safe)
|
||||
✓ Keep separate App Defaults for each app
|
||||
✓ Back up your defaults regularly
|
||||
✓ Use environment variables for temporary overrides
|
||||
|
||||
### ❌ Don'ts
|
||||
|
||||
✗ Don't use `source` on defaults files (security risk)
|
||||
✗ Don't put sensitive passwords in defaults (use SSH keys)
|
||||
✗ Don't modify defaults while installation is running
|
||||
✗ Don't delete defaults.d while containers are being created
|
||||
✗ Don't use special characters without escaping
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Defaults Locations
|
||||
|
||||
| Type | Location | Example |
|
||||
|------|----------|---------|
|
||||
| User Defaults | `/usr/local/community-scripts/default.vars` | Global settings |
|
||||
| App Defaults | `/usr/local/community-scripts/defaults/<app>.vars` | PiHole-specific |
|
||||
| Backup Dir | `/usr/local/community-scripts/defaults/` | All app defaults |
|
||||
|
||||
### File Format
|
||||
|
||||
```bash
|
||||
# Comments start with #
|
||||
var_name=value
|
||||
|
||||
# No spaces around =
|
||||
✓ var_cpu=4
|
||||
✗ var_cpu = 4
|
||||
|
||||
# String values don't need quotes
|
||||
✓ var_hostname=mycontainer
|
||||
✓ var_hostname='mycontainer'
|
||||
|
||||
# Values with spaces need quotes
|
||||
✓ var_tags="docker,production,testing"
|
||||
✗ var_tags=docker,production,testing
|
||||
```
|
||||
|
||||
### Command Reference
|
||||
|
||||
```bash
|
||||
# View defaults
|
||||
cat /usr/local/community-scripts/default.vars
|
||||
|
||||
# Edit defaults
|
||||
sudo nano /usr/local/community-scripts/default.vars
|
||||
|
||||
# List all app defaults
|
||||
ls /usr/local/community-scripts/defaults/
|
||||
|
||||
# Backup your defaults
|
||||
cp -r /usr/local/community-scripts/defaults/ ~/defaults-backup/
|
||||
|
||||
# Set temporary override
|
||||
export var_cpu=8
|
||||
bash pihole-install.sh
|
||||
|
||||
# Create custom defaults
|
||||
sudo tee /usr/local/community-scripts/defaults/custom.vars << 'EOF'
|
||||
var_cpu=4
|
||||
var_ram=2048
|
||||
EOF
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Getting Help
|
||||
|
||||
### Need More Information?
|
||||
|
||||
- 📖 [Main Documentation](../../docs/)
|
||||
- 🐛 [Report Issues](https://github.com/community-scripts/ProxmoxVED/issues)
|
||||
- 💬 [Discussions](https://github.com/community-scripts/ProxmoxVED/discussions)
|
||||
|
||||
### Useful Commands
|
||||
|
||||
```bash
|
||||
# Check what variables are available
|
||||
grep "var_" /path/to/app-install.sh | head -20
|
||||
|
||||
# Verify defaults syntax
|
||||
cat /usr/local/community-scripts/default.vars
|
||||
|
||||
# Monitor installation with defaults
|
||||
bash pihole-install.sh 2>&1 | tee installation.log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Document Information
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| Version | 1.0 |
|
||||
| Last Updated | November 28, 2025 |
|
||||
| Status | Current |
|
||||
| License | MIT |
|
||||
|
||||
---
|
||||
|
||||
**Happy configuring! 🚀**
|
||||
881
docs/TECHNICAL_REFERENCE.md
Normal file
881
docs/TECHNICAL_REFERENCE.md
Normal file
@ -0,0 +1,881 @@
|
||||
# Technical Reference: Configuration System Architecture
|
||||
|
||||
> **For Developers and Advanced Users**
|
||||
>
|
||||
> *Deep dive into how the defaults and configuration system works*
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [System Architecture](#system-architecture)
|
||||
2. [File Format Specifications](#file-format-specifications)
|
||||
3. [Function Reference](#function-reference)
|
||||
4. [Variable Precedence](#variable-precedence)
|
||||
5. [Data Flow Diagrams](#data-flow-diagrams)
|
||||
6. [Security Model](#security-model)
|
||||
7. [Implementation Details](#implementation-details)
|
||||
|
||||
---
|
||||
|
||||
## System Architecture
|
||||
|
||||
### Component Overview
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Installation Script │
|
||||
│ (pihole-install.sh, docker-install.sh, etc.) │
|
||||
└────────────────────┬────────────────────────────────────────┘
|
||||
│
|
||||
v
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ build.func Library │
|
||||
│ ┌──────────────────────────────────────────────────────┐ │
|
||||
│ │ variables() │ │
|
||||
│ │ - Initialize NSAPP, var_install, etc. │ │
|
||||
│ └──────────────────────────────────────────────────────┘ │
|
||||
│ ┌──────────────────────────────────────────────────────┐ │
|
||||
│ │ install_script() │ │
|
||||
│ │ - Display mode menu │ │
|
||||
│ │ - Route to appropriate workflow │ │
|
||||
│ └──────────────────────────────────────────────────────┘ │
|
||||
│ ┌──────────────────────────────────────────────────────┐ │
|
||||
│ │ base_settings() │ │
|
||||
│ │ - Apply built-in defaults │ │
|
||||
│ │ - Read environment variables (var_*) │ │
|
||||
│ └──────────────────────────────────────────────────────┘ │
|
||||
│ ┌──────────────────────────────────────────────────────┐ │
|
||||
│ │ load_vars_file() │ │
|
||||
│ │ - Safe file parsing (NO source/eval) │ │
|
||||
│ │ - Whitelist validation │ │
|
||||
│ │ - Value sanitization │ │
|
||||
│ └──────────────────────────────────────────────────────┘ │
|
||||
│ ┌──────────────────────────────────────────────────────┐ │
|
||||
│ │ default_var_settings() │ │
|
||||
│ │ - Load user defaults │ │
|
||||
│ │ - Display summary │ │
|
||||
│ └──────────────────────────────────────────────────────┘ │
|
||||
│ ┌──────────────────────────────────────────────────────┐ │
|
||||
│ │ maybe_offer_save_app_defaults() │ │
|
||||
│ │ - Offer to save current settings │ │
|
||||
│ │ - Handle updates vs. new saves │ │
|
||||
│ └──────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
v
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Configuration Files (on Disk) │
|
||||
│ ┌──────────────────────────────────────────────────────┐ │
|
||||
│ │ /usr/local/community-scripts/default.vars │ │
|
||||
│ │ (User global defaults) │ │
|
||||
│ └──────────────────────────────────────────────────────┘ │
|
||||
│ ┌──────────────────────────────────────────────────────┐ │
|
||||
│ │ /usr/local/community-scripts/defaults/*.vars │ │
|
||||
│ │ (App-specific defaults) │ │
|
||||
│ └──────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## File Format Specifications
|
||||
|
||||
### User Defaults: `default.vars`
|
||||
|
||||
**Location**: `/usr/local/community-scripts/default.vars`
|
||||
|
||||
**MIME Type**: `text/plain`
|
||||
|
||||
**Encoding**: UTF-8 (no BOM)
|
||||
|
||||
**Format Specification**:
|
||||
|
||||
```
|
||||
# File Format: Simple key=value pairs
|
||||
# Purpose: Store global user defaults
|
||||
# Security: Sanitized values, whitelist validation
|
||||
|
||||
# Comments and blank lines are ignored
|
||||
# Line format: var_name=value
|
||||
# No spaces around the equals sign
|
||||
# String values do not need quoting (but may be quoted)
|
||||
|
||||
[CONTENT]
|
||||
var_cpu=4
|
||||
var_ram=2048
|
||||
var_disk=20
|
||||
var_hostname=mydefault
|
||||
var_brg=vmbr0
|
||||
var_gateway=192.168.1.1
|
||||
```
|
||||
|
||||
**Formal Grammar**:
|
||||
|
||||
```
|
||||
FILE := (BLANK_LINE | COMMENT_LINE | VAR_LINE)*
|
||||
BLANK_LINE := \n
|
||||
COMMENT_LINE := '#' [^\n]* \n
|
||||
VAR_LINE := VAR_NAME '=' VAR_VALUE \n
|
||||
VAR_NAME := 'var_' [a-z_]+
|
||||
VAR_VALUE := [^\n]* # Any printable characters except newline
|
||||
```
|
||||
|
||||
**Constraints**:
|
||||
|
||||
| Constraint | Value |
|
||||
|-----------|-------|
|
||||
| Max file size | 64 KB |
|
||||
| Max line length | 1024 bytes |
|
||||
| Max variables | 100 |
|
||||
| Allowed var names | `var_[a-z_]+` |
|
||||
| Value validation | Whitelist + Sanitization |
|
||||
|
||||
**Example Valid File**:
|
||||
|
||||
```bash
|
||||
# Global User Defaults
|
||||
# Created: 2024-11-28
|
||||
|
||||
# Resource defaults
|
||||
var_cpu=4
|
||||
var_ram=2048
|
||||
var_disk=20
|
||||
|
||||
# Network defaults
|
||||
var_brg=vmbr0
|
||||
var_gateway=192.168.1.1
|
||||
var_mtu=1500
|
||||
var_vlan=100
|
||||
|
||||
# System defaults
|
||||
var_timezone=Europe/Berlin
|
||||
var_hostname=default-container
|
||||
|
||||
# Storage
|
||||
var_container_storage=local
|
||||
var_template_storage=local
|
||||
|
||||
# Security
|
||||
var_ssh=yes
|
||||
var_protection=0
|
||||
var_unprivileged=1
|
||||
```
|
||||
|
||||
### App Defaults: `<app>.vars`
|
||||
|
||||
**Location**: `/usr/local/community-scripts/defaults/<appname>.vars`
|
||||
|
||||
**Format**: Identical to `default.vars`
|
||||
|
||||
**Naming Convention**: `<nsapp>.vars`
|
||||
|
||||
- `nsapp` = lowercase app name with spaces removed
|
||||
- Examples:
|
||||
- `pihole` → `pihole.vars`
|
||||
- `opnsense` → `opnsense.vars`
|
||||
- `docker compose` → `dockercompose.vars`
|
||||
|
||||
**Example App Defaults**:
|
||||
|
||||
```bash
|
||||
# App-specific defaults for PiHole (pihole)
|
||||
# Generated on 2024-11-28T15:32:00Z
|
||||
# These override user defaults when installing pihole
|
||||
|
||||
var_unprivileged=1
|
||||
var_cpu=2
|
||||
var_ram=1024
|
||||
var_disk=10
|
||||
var_brg=vmbr0
|
||||
var_net=veth
|
||||
var_gateway=192.168.1.1
|
||||
var_hostname=pihole
|
||||
var_timezone=Europe/Berlin
|
||||
var_container_storage=local
|
||||
var_template_storage=local
|
||||
var_tags=dns,pihole
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Function Reference
|
||||
|
||||
### `load_vars_file()`
|
||||
|
||||
**Purpose**: Safely load variables from .vars files without using `source` or `eval`
|
||||
|
||||
**Signature**:
|
||||
```bash
|
||||
load_vars_file(filepath)
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
|
||||
| Param | Type | Required | Example |
|
||||
|-------|------|----------|---------|
|
||||
| filepath | String | Yes | `/usr/local/community-scripts/default.vars` |
|
||||
|
||||
**Returns**:
|
||||
- `0` on success
|
||||
- `1` on error (file missing, parse error, etc.)
|
||||
|
||||
**Environment Side Effects**:
|
||||
- Sets all parsed `var_*` variables as shell variables
|
||||
- Does NOT unset variables if file missing (safe)
|
||||
- Does NOT affect other variables
|
||||
|
||||
**Implementation Pattern**:
|
||||
|
||||
```bash
|
||||
load_vars_file() {
|
||||
local file="$1"
|
||||
|
||||
# File must exist
|
||||
[ -f "$file" ] || return 0
|
||||
|
||||
# Parse line by line (not with source/eval)
|
||||
local line key val
|
||||
while IFS='=' read -r key val || [ -n "$key" ]; do
|
||||
# Skip comments and empty lines
|
||||
[[ "$key" =~ ^[[:space:]]*# ]] && continue
|
||||
[[ -z "$key" ]] && continue
|
||||
|
||||
# Validate key is in whitelist
|
||||
_is_whitelisted_key "$key" || continue
|
||||
|
||||
# Sanitize and export value
|
||||
val="$(_sanitize_value "$val")"
|
||||
[ $? -eq 0 ] && export "$key=$val"
|
||||
done < "$file"
|
||||
|
||||
return 0
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Examples**:
|
||||
|
||||
```bash
|
||||
# Load user defaults
|
||||
load_vars_file "/usr/local/community-scripts/default.vars"
|
||||
|
||||
# Load app-specific defaults
|
||||
load_vars_file "$(get_app_defaults_path)"
|
||||
|
||||
# Check if successful
|
||||
if load_vars_file "$vars_path"; then
|
||||
echo "Settings loaded successfully"
|
||||
else
|
||||
echo "Failed to load settings"
|
||||
fi
|
||||
|
||||
# Values are now available as variables
|
||||
echo "Using $var_cpu cores"
|
||||
echo "Allocating ${var_ram} MB RAM"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `get_app_defaults_path()`
|
||||
|
||||
**Purpose**: Get the full path for app-specific defaults file
|
||||
|
||||
**Signature**:
|
||||
```bash
|
||||
get_app_defaults_path()
|
||||
```
|
||||
|
||||
**Parameters**: None
|
||||
|
||||
**Returns**:
|
||||
- String: Full path to app defaults file
|
||||
|
||||
**Implementation**:
|
||||
|
||||
```bash
|
||||
get_app_defaults_path() {
|
||||
local n="${NSAPP:-${APP,,}}"
|
||||
echo "/usr/local/community-scripts/defaults/${n}.vars"
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Examples**:
|
||||
|
||||
```bash
|
||||
# Get app defaults path
|
||||
app_defaults="$(get_app_defaults_path)"
|
||||
echo "App defaults at: $app_defaults"
|
||||
|
||||
# Check if app defaults exist
|
||||
if [ -f "$(get_app_defaults_path)" ]; then
|
||||
echo "App defaults available"
|
||||
fi
|
||||
|
||||
# Load app defaults
|
||||
load_vars_file "$(get_app_defaults_path)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `default_var_settings()`
|
||||
|
||||
**Purpose**: Load and display user global defaults
|
||||
|
||||
**Signature**:
|
||||
```bash
|
||||
default_var_settings()
|
||||
```
|
||||
|
||||
**Parameters**: None
|
||||
|
||||
**Returns**:
|
||||
- `0` on success
|
||||
- `1` on error
|
||||
|
||||
**Workflow**:
|
||||
|
||||
```
|
||||
1. Find default.vars location
|
||||
(usually /usr/local/community-scripts/default.vars)
|
||||
|
||||
2. Create if missing
|
||||
|
||||
3. Load variables from file
|
||||
|
||||
4. Map var_verbose → VERBOSE variable
|
||||
|
||||
5. Call base_settings (apply to container config)
|
||||
|
||||
6. Call echo_default (display summary)
|
||||
```
|
||||
|
||||
**Implementation Pattern**:
|
||||
|
||||
```bash
|
||||
default_var_settings() {
|
||||
local VAR_WHITELIST=(
|
||||
var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse
|
||||
var_gateway var_hostname var_ipv6_method var_mac var_mtu
|
||||
var_net var_ns var_pw var_ram var_tags var_tun var_unprivileged
|
||||
var_verbose var_vlan var_ssh var_ssh_authorized_key
|
||||
var_container_storage var_template_storage
|
||||
)
|
||||
|
||||
# Ensure file exists
|
||||
_ensure_default_vars
|
||||
|
||||
# Find and load
|
||||
local dv="$(_find_default_vars)"
|
||||
load_vars_file "$dv"
|
||||
|
||||
# Map verbose flag
|
||||
if [[ -n "${var_verbose:-}" ]]; then
|
||||
case "${var_verbose,,}" in
|
||||
1 | yes | true | on) VERBOSE="yes" ;;
|
||||
*) VERBOSE="${var_verbose}" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Apply and display
|
||||
base_settings "$VERBOSE"
|
||||
echo_default
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `maybe_offer_save_app_defaults()`
|
||||
|
||||
**Purpose**: Offer to save current settings as app-specific defaults
|
||||
|
||||
**Signature**:
|
||||
```bash
|
||||
maybe_offer_save_app_defaults()
|
||||
```
|
||||
|
||||
**Parameters**: None
|
||||
|
||||
**Returns**: None (side effects only)
|
||||
|
||||
**Behavior**:
|
||||
|
||||
1. After advanced installation completes
|
||||
2. Offers user: "Save as App Defaults for <APP>?"
|
||||
3. If yes:
|
||||
- Saves to `/usr/local/community-scripts/defaults/<app>.vars`
|
||||
- Only whitelisted variables included
|
||||
- Previous defaults backed up (if exists)
|
||||
4. If no:
|
||||
- No action taken
|
||||
|
||||
**Flow**:
|
||||
|
||||
```bash
|
||||
maybe_offer_save_app_defaults() {
|
||||
local app_vars_path="$(get_app_defaults_path)"
|
||||
|
||||
# Build current settings from memory
|
||||
local new_tmp="$(_build_current_app_vars_tmp)"
|
||||
|
||||
# Check if already exists
|
||||
if [ -f "$app_vars_path" ]; then
|
||||
# Show diff and ask: Update? Keep? View Diff?
|
||||
_show_app_defaults_diff_menu "$new_tmp" "$app_vars_path"
|
||||
else
|
||||
# New defaults - just save
|
||||
if whiptail --yesno "Save as App Defaults for $APP?" 10 60; then
|
||||
mv "$new_tmp" "$app_vars_path"
|
||||
chmod 644 "$app_vars_path"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `_sanitize_value()`
|
||||
|
||||
**Purpose**: Remove dangerous characters/patterns from configuration values
|
||||
|
||||
**Signature**:
|
||||
```bash
|
||||
_sanitize_value(value)
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
|
||||
| Param | Type | Required |
|
||||
|-------|------|----------|
|
||||
| value | String | Yes |
|
||||
|
||||
**Returns**:
|
||||
- `0` (success) + sanitized value on stdout
|
||||
- `1` (failure) + nothing if dangerous
|
||||
|
||||
**Dangerous Patterns**:
|
||||
|
||||
| Pattern | Threat | Example |
|
||||
|---------|--------|---------|
|
||||
| `$(...)` | Command substitution | `$(rm -rf /)` |
|
||||
| `` ` ` `` | Command substitution | `` `whoami` `` |
|
||||
| `;` | Command separator | `value; rm -rf /` |
|
||||
| `&` | Background execution | `value & malicious` |
|
||||
| `<(` | Process substitution | `<(cat /etc/passwd)` |
|
||||
|
||||
**Implementation**:
|
||||
|
||||
```bash
|
||||
_sanitize_value() {
|
||||
case "$1" in
|
||||
*'$('* | *'`'* | *';'* | *'&'* | *'<('*)
|
||||
echo ""
|
||||
return 1 # Reject dangerous value
|
||||
;;
|
||||
esac
|
||||
echo "$1"
|
||||
return 0
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Examples**:
|
||||
|
||||
```bash
|
||||
# Safe value
|
||||
_sanitize_value "192.168.1.1" # Returns: 192.168.1.1 (status: 0)
|
||||
|
||||
# Dangerous value
|
||||
_sanitize_value "$(whoami)" # Returns: (empty) (status: 1)
|
||||
|
||||
# Usage in code
|
||||
if val="$(_sanitize_value "$user_input")"; then
|
||||
export var_hostname="$val"
|
||||
else
|
||||
msg_error "Invalid value: contains dangerous characters"
|
||||
fi
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `_is_whitelisted_key()`
|
||||
|
||||
**Purpose**: Check if variable name is in allowed whitelist
|
||||
|
||||
**Signature**:
|
||||
```bash
|
||||
_is_whitelisted_key(key)
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
|
||||
| Param | Type | Required | Example |
|
||||
|-------|------|----------|---------|
|
||||
| key | String | Yes | `var_cpu` |
|
||||
|
||||
**Returns**:
|
||||
- `0` if key is whitelisted
|
||||
- `1` if key is NOT whitelisted
|
||||
|
||||
**Implementation**:
|
||||
|
||||
```bash
|
||||
_is_whitelisted_key() {
|
||||
local k="$1"
|
||||
local w
|
||||
for w in "${VAR_WHITELIST[@]}"; do
|
||||
[ "$k" = "$w" ] && return 0
|
||||
done
|
||||
return 1
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Examples**:
|
||||
|
||||
```bash
|
||||
# Check if variable can be saved
|
||||
if _is_whitelisted_key "var_cpu"; then
|
||||
echo "var_cpu can be saved"
|
||||
fi
|
||||
|
||||
# Reject unknown variables
|
||||
if ! _is_whitelisted_key "var_custom"; then
|
||||
msg_error "var_custom is not supported"
|
||||
fi
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Variable Precedence
|
||||
|
||||
### Loading Order
|
||||
|
||||
When a container is being created, variables are resolved in this order:
|
||||
|
||||
```
|
||||
Step 1: Read ENVIRONMENT VARIABLES
|
||||
├─ Check if var_cpu is already set in shell environment
|
||||
├─ Check if var_ram is already set
|
||||
└─ ...all var_* variables
|
||||
|
||||
Step 2: Load APP-SPECIFIC DEFAULTS
|
||||
├─ Check if /usr/local/community-scripts/defaults/pihole.vars exists
|
||||
├─ Load all var_* from that file
|
||||
└─ These override built-ins but NOT environment variables
|
||||
|
||||
Step 3: Load USER GLOBAL DEFAULTS
|
||||
├─ Check if /usr/local/community-scripts/default.vars exists
|
||||
├─ Load all var_* from that file
|
||||
└─ These override built-ins but NOT app-specific
|
||||
|
||||
Step 4: Use BUILT-IN DEFAULTS
|
||||
└─ Hardcoded in script (lowest priority)
|
||||
```
|
||||
|
||||
### Precedence Examples
|
||||
|
||||
**Example 1: Environment Variable Wins**
|
||||
```bash
|
||||
# Shell environment has highest priority
|
||||
$ export var_cpu=16
|
||||
$ bash pihole-install.sh
|
||||
|
||||
# Result: Container gets 16 cores
|
||||
# (ignores app defaults, user defaults, built-ins)
|
||||
```
|
||||
|
||||
**Example 2: App Defaults Override User Defaults**
|
||||
```bash
|
||||
# User Defaults: var_cpu=4
|
||||
# App Defaults: var_cpu=2
|
||||
$ bash pihole-install.sh
|
||||
|
||||
# Result: Container gets 2 cores
|
||||
# (app-specific setting takes precedence)
|
||||
```
|
||||
|
||||
**Example 3: All Defaults Missing (Built-ins Used)**
|
||||
```bash
|
||||
# No environment variables set
|
||||
# No app defaults file
|
||||
# No user defaults file
|
||||
$ bash pihole-install.sh
|
||||
|
||||
# Result: Uses built-in defaults
|
||||
# (var_cpu might be 2 by default)
|
||||
```
|
||||
|
||||
### Implementation in Code
|
||||
|
||||
```bash
|
||||
# Typical pattern in build.func
|
||||
|
||||
base_settings() {
|
||||
# Priority 1: Environment variables (already set if export used)
|
||||
CT_TYPE=${var_unprivileged:-"1"} # Use existing or default
|
||||
|
||||
# Priority 2: Load app defaults (may override above)
|
||||
if [ -f "$(get_app_defaults_path)" ]; then
|
||||
load_vars_file "$(get_app_defaults_path)"
|
||||
fi
|
||||
|
||||
# Priority 3: Load user defaults
|
||||
if [ -f "/usr/local/community-scripts/default.vars" ]; then
|
||||
load_vars_file "/usr/local/community-scripts/default.vars"
|
||||
fi
|
||||
|
||||
# Priority 4: Apply built-in defaults (lowest)
|
||||
CORE_COUNT=${var_cpu:-"${APP_CPU_DEFAULT:-2}"}
|
||||
RAM_SIZE=${var_ram:-"${APP_RAM_DEFAULT:-1024}"}
|
||||
|
||||
# Result: var_cpu has been set through precedence chain
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Data Flow Diagrams
|
||||
|
||||
### Installation Flow: Advanced Settings
|
||||
|
||||
```
|
||||
┌──────────────┐
|
||||
│ Start Script│
|
||||
└──────┬───────┘
|
||||
│
|
||||
v
|
||||
┌──────────────────────────────┐
|
||||
│ Display Installation Mode │
|
||||
│ Menu (5 options) │
|
||||
└──────┬───────────────────────┘
|
||||
│ User selects "Advanced Settings"
|
||||
v
|
||||
┌──────────────────────────────────┐
|
||||
│ Call: base_settings() │
|
||||
│ (Apply built-in defaults) │
|
||||
└──────┬───────────────────────────┘
|
||||
│
|
||||
v
|
||||
┌──────────────────────────────────┐
|
||||
│ Call: advanced_settings() │
|
||||
│ (Show 19-step wizard) │
|
||||
│ - Ask CPU, RAM, Disk, Network... │
|
||||
└──────┬───────────────────────────┘
|
||||
│
|
||||
v
|
||||
┌──────────────────────────────────┐
|
||||
│ Show Summary │
|
||||
│ Review all chosen values │
|
||||
└──────┬───────────────────────────┘
|
||||
│ User confirms
|
||||
v
|
||||
┌──────────────────────────────────┐
|
||||
│ Create Container │
|
||||
│ Using current variable values │
|
||||
└──────┬───────────────────────────┘
|
||||
│
|
||||
v
|
||||
┌──────────────────────────────────┐
|
||||
│ Installation Complete │
|
||||
└──────┬───────────────────────────┘
|
||||
│
|
||||
v
|
||||
┌──────────────────────────────────────┐
|
||||
│ Offer: Save as App Defaults? │
|
||||
│ (Save current settings) │
|
||||
└──────┬───────────────────────────────┘
|
||||
│
|
||||
├─ YES → Save to defaults/<app>.vars
|
||||
│
|
||||
└─ NO → Exit
|
||||
```
|
||||
|
||||
### Variable Resolution Flow
|
||||
|
||||
```
|
||||
CONTAINER CREATION STARTED
|
||||
│
|
||||
v
|
||||
┌─────────────────────┐
|
||||
│ Check ENVIRONMENT │
|
||||
│ for var_cpu, var_..│
|
||||
└──────┬──────────────┘
|
||||
│ Found? Use them (Priority 1)
|
||||
│ Not found? Continue...
|
||||
v
|
||||
┌──────────────────────────┐
|
||||
│ Load App Defaults │
|
||||
│ /defaults/<app>.vars │
|
||||
└──────┬───────────────────┘
|
||||
│ File exists? Parse & load (Priority 2)
|
||||
│ Not found? Continue...
|
||||
v
|
||||
┌──────────────────────────┐
|
||||
│ Load User Defaults │
|
||||
│ /default.vars │
|
||||
└──────┬───────────────────┘
|
||||
│ File exists? Parse & load (Priority 3)
|
||||
│ Not found? Continue...
|
||||
v
|
||||
┌──────────────────────────┐
|
||||
│ Use Built-in Defaults │
|
||||
│ (Hardcoded values) │
|
||||
└──────┬───────────────────┘
|
||||
│
|
||||
v
|
||||
┌──────────────────────────┐
|
||||
│ All Variables Resolved │
|
||||
│ Ready for container │
|
||||
│ creation │
|
||||
└──────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Security Model
|
||||
|
||||
### Threat Model
|
||||
|
||||
| Threat | Mitigation |
|
||||
|--------|-----------|
|
||||
| **Arbitrary Code Execution** | No `source` or `eval`; manual parsing only |
|
||||
| **Variable Injection** | Whitelist of allowed variable names |
|
||||
| **Command Substitution** | `_sanitize_value()` blocks `$()`, backticks, etc. |
|
||||
| **Path Traversal** | Files locked to `/usr/local/community-scripts/` |
|
||||
| **Permission Escalation** | Files created with restricted permissions |
|
||||
| **Information Disclosure** | Sensitive variables not logged |
|
||||
|
||||
### Security Controls
|
||||
|
||||
#### 1. Input Validation
|
||||
|
||||
```bash
|
||||
# Only specific variables allowed
|
||||
if ! _is_whitelisted_key "$key"; then
|
||||
skip_this_variable
|
||||
fi
|
||||
|
||||
# Values sanitized
|
||||
if ! val="$(_sanitize_value "$value")"; then
|
||||
reject_entire_line
|
||||
fi
|
||||
```
|
||||
|
||||
#### 2. Safe File Parsing
|
||||
|
||||
```bash
|
||||
# ❌ DANGEROUS (OLD)
|
||||
source /path/to/config.conf
|
||||
# Could execute: rm -rf / or any code
|
||||
|
||||
# ✅ SAFE (NEW)
|
||||
load_vars_file "/path/to/config.conf"
|
||||
# Only reads var_name=value pairs, no execution
|
||||
```
|
||||
|
||||
#### 3. Whitelisting
|
||||
|
||||
```bash
|
||||
# Only these variables can be configured
|
||||
var_cpu, var_ram, var_disk, var_brg, ...
|
||||
var_hostname, var_pw, var_ssh, ...
|
||||
|
||||
# NOT allowed:
|
||||
var_malicious, var_hack, custom_var, ...
|
||||
```
|
||||
|
||||
#### 4. Value Constraints
|
||||
|
||||
```bash
|
||||
# No command injection patterns
|
||||
if [[ "$value" =~ ($|`|;|&|<\() ]]; then
|
||||
reject_value
|
||||
fi
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### Module: `build.func`
|
||||
|
||||
**Load Order** (in actual scripts):
|
||||
1. `#!/usr/bin/env bash` - Shebang
|
||||
2. `source /dev/stdin <<<$(curl ... api.func)` - API functions
|
||||
3. `source /dev/stdin <<<$(curl ... build.func)` - Build functions
|
||||
4. `variables()` - Initialize variables
|
||||
5. `check_root()` - Security check
|
||||
6. `install_script()` - Main flow
|
||||
|
||||
**Key Sections**:
|
||||
|
||||
```bash
|
||||
# Section 1: Initialization & Variables
|
||||
- variables()
|
||||
- NSAPP, var_install, INTEGER pattern, etc.
|
||||
|
||||
# Section 2: Storage Management
|
||||
- storage_selector()
|
||||
- ensure_storage_selection_for_vars_file()
|
||||
|
||||
# Section 3: Base Settings
|
||||
- base_settings() # Apply defaults to all var_*
|
||||
- echo_default() # Display current settings
|
||||
|
||||
# Section 4: Variable Loading
|
||||
- load_vars_file() # Safe parsing
|
||||
- _is_whitelisted_key() # Validation
|
||||
- _sanitize_value() # Threat mitigation
|
||||
|
||||
# Section 5: Defaults Management
|
||||
- default_var_settings() # Load user defaults
|
||||
- get_app_defaults_path() # Get app defaults path
|
||||
- maybe_offer_save_app_defaults() # Save option
|
||||
|
||||
# Section 6: Installation Flow
|
||||
- install_script() # Main entry point
|
||||
- advanced_settings() # 19-step wizard
|
||||
```
|
||||
|
||||
### Regex Patterns Used
|
||||
|
||||
| Pattern | Purpose | Example Match |
|
||||
|---------|---------|---|
|
||||
| `^[0-9]+([.][0-9]+)?$` | Integer validation | `4`, `192.168` |
|
||||
| `^var_[a-z_]+$` | Variable name | `var_cpu`, `var_ssh` |
|
||||
| `*'$('*` | Command substitution | `$(whoami)` |
|
||||
| `*\`*` | Backtick substitution | `` `cat /etc/passwd` `` |
|
||||
|
||||
---
|
||||
|
||||
## Appendix: Migration Reference
|
||||
|
||||
### Old Pattern (Deprecated)
|
||||
|
||||
```bash
|
||||
# ❌ OLD: config-file.func
|
||||
source config-file.conf # Executes arbitrary code
|
||||
if [ "$USE_DEFAULTS" = "yes" ]; then
|
||||
apply_settings_directly
|
||||
fi
|
||||
```
|
||||
|
||||
### New Pattern (Current)
|
||||
|
||||
```bash
|
||||
# ✅ NEW: load_vars_file()
|
||||
if load_vars_file "$(get_app_defaults_path)"; then
|
||||
echo "Settings loaded securely"
|
||||
fi
|
||||
```
|
||||
|
||||
### Function Mapping
|
||||
|
||||
| Old | New | Location |
|
||||
|-----|-----|----------|
|
||||
| `read_config()` | `load_vars_file()` | build.func |
|
||||
| `write_config()` | `_build_current_app_vars_tmp()` | build.func |
|
||||
| None | `maybe_offer_save_app_defaults()` | build.func |
|
||||
| None | `get_app_defaults_path()` | build.func |
|
||||
|
||||
---
|
||||
|
||||
**End of Technical Reference**
|
||||
172
misc/old_misc/alpine-install.func
Normal file
172
misc/old_misc/alpine-install.func
Normal file
@ -0,0 +1,172 @@
|
||||
# Copyright (c) 2021-2025 community-scripts ORG
|
||||
# Author: tteck (tteckster)
|
||||
# Co-Author: MickLesk
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
|
||||
if ! command -v curl >/dev/null 2>&1; then
|
||||
apk update && apk add curl >/dev/null 2>&1
|
||||
fi
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func)
|
||||
load_functions
|
||||
|
||||
# This function enables IPv6 if it's not disabled and sets verbose mode
|
||||
verb_ip6() {
|
||||
set_std_mode # Set STD mode based on VERBOSE
|
||||
|
||||
if [ "$IPV6_METHOD" == "disable" ]; then
|
||||
msg_info "Disabling IPv6 (this may affect some services)"
|
||||
$STD sysctl -w net.ipv6.conf.all.disable_ipv6=1
|
||||
$STD sysctl -w net.ipv6.conf.default.disable_ipv6=1
|
||||
$STD sysctl -w net.ipv6.conf.lo.disable_ipv6=1
|
||||
mkdir -p /etc/sysctl.d
|
||||
$STD tee /etc/sysctl.d/99-disable-ipv6.conf >/dev/null <<EOF
|
||||
net.ipv6.conf.all.disable_ipv6 = 1
|
||||
net.ipv6.conf.default.disable_ipv6 = 1
|
||||
net.ipv6.conf.lo.disable_ipv6 = 1
|
||||
EOF
|
||||
$STD rc-update add sysctl default
|
||||
msg_ok "Disabled IPv6"
|
||||
fi
|
||||
}
|
||||
|
||||
# This function catches errors and handles them with the error handler function
|
||||
catch_errors() {
|
||||
set -Eeuo pipefail
|
||||
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
|
||||
}
|
||||
|
||||
# This function handles errors
|
||||
error_handler() {
|
||||
local exit_code="$?"
|
||||
local line_number="$1"
|
||||
local command="$2"
|
||||
local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}"
|
||||
echo -e "\n$error_message\n"
|
||||
}
|
||||
|
||||
# This function sets up the Container OS by generating the locale, setting the timezone, and checking the network connection
|
||||
setting_up_container() {
|
||||
msg_info "Setting up Container OS"
|
||||
while [ $i -gt 0 ]; do
|
||||
if [ "$(ip addr show | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}' | cut -d'/' -f1)" != "" ]; then
|
||||
break
|
||||
fi
|
||||
echo 1>&2 -en "${CROSS}${RD} No Network! "
|
||||
sleep $RETRY_EVERY
|
||||
i=$((i - 1))
|
||||
done
|
||||
|
||||
if [ "$(ip addr show | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}' | cut -d'/' -f1)" = "" ]; then
|
||||
echo 1>&2 -e "\n${CROSS}${RD} No Network After $RETRY_NUM Tries${CL}"
|
||||
echo -e "${NETWORK}Check Network Settings"
|
||||
exit 1
|
||||
fi
|
||||
msg_ok "Set up Container OS"
|
||||
msg_ok "Network Connected: ${BL}$(ip addr show | grep 'inet ' | awk '{print $2}' | cut -d'/' -f1 | tail -n1)${CL}"
|
||||
}
|
||||
|
||||
# This function checks the network connection by pinging a known IP address and prompts the user to continue if the internet is not connected
|
||||
network_check() {
|
||||
set +e
|
||||
trap - ERR
|
||||
if ping -c 1 -W 1 1.1.1.1 &>/dev/null || ping -c 1 -W 1 8.8.8.8 &>/dev/null || ping -c 1 -W 1 9.9.9.9 &>/dev/null; then
|
||||
msg_ok "Internet Connected"
|
||||
else
|
||||
msg_error "Internet NOT Connected"
|
||||
read -r -p "Would you like to continue anyway? <y/N> " prompt
|
||||
if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then
|
||||
echo -e "${INFO}${RD}Expect Issues Without Internet${CL}"
|
||||
else
|
||||
echo -e "${NETWORK}Check Network Settings"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
RESOLVEDIP=$(getent hosts github.com | awk '{ print $1 }')
|
||||
if [[ -z "$RESOLVEDIP" ]]; then msg_error "DNS Lookup Failure"; else msg_ok "DNS Resolved github.com to ${BL}$RESOLVEDIP${CL}"; fi
|
||||
set -e
|
||||
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
|
||||
}
|
||||
|
||||
# This function updates the Container OS by running apt-get update and upgrade
|
||||
update_os() {
|
||||
msg_info "Updating Container OS"
|
||||
$STD apk -U upgrade
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func)
|
||||
msg_ok "Updated Container OS"
|
||||
}
|
||||
|
||||
# This function modifies the message of the day (motd) and SSH settings
|
||||
motd_ssh() {
|
||||
echo "export TERM='xterm-256color'" >>/root/.bashrc
|
||||
IP=$(ip -4 addr show eth0 | awk '/inet / {print $2}' | cut -d/ -f1 | head -n 1)
|
||||
|
||||
if [ -f "/etc/os-release" ]; then
|
||||
OS_NAME=$(grep ^NAME /etc/os-release | cut -d= -f2 | tr -d '"')
|
||||
OS_VERSION=$(grep ^VERSION_ID /etc/os-release | cut -d= -f2 | tr -d '"')
|
||||
else
|
||||
OS_NAME="Alpine Linux"
|
||||
OS_VERSION="Unknown"
|
||||
fi
|
||||
|
||||
PROFILE_FILE="/etc/profile.d/00_lxc-details.sh"
|
||||
echo "echo -e \"\"" >"$PROFILE_FILE"
|
||||
echo -e "echo -e \"${BOLD}${APPLICATION} LXC Container${CL}"\" >>"$PROFILE_FILE"
|
||||
echo -e "echo -e \"${TAB}${GATEWAY}${YW} Provided by: ${GN}community-scripts ORG ${YW}| GitHub: ${GN}https://github.com/community-scripts/ProxmoxVE${CL}\"" >>"$PROFILE_FILE"
|
||||
echo "echo \"\"" >>"$PROFILE_FILE"
|
||||
echo -e "echo -e \"${TAB}${OS}${YW} OS: ${GN}${OS_NAME} - Version: ${OS_VERSION}${CL}\"" >>"$PROFILE_FILE"
|
||||
echo -e "echo -e \"${TAB}${HOSTNAME}${YW} Hostname: ${GN}\$(hostname)${CL}\"" >>"$PROFILE_FILE"
|
||||
echo -e "echo -e \"${TAB}${INFO}${YW} IP Address: ${GN}\$(ip -4 addr show eth0 | awk '/inet / {print \$2}' | cut -d/ -f1 | head -n 1)${CL}\"" >>"$PROFILE_FILE"
|
||||
|
||||
# Configure SSH if enabled
|
||||
if [[ "${SSH_ROOT}" == "yes" ]]; then
|
||||
# Enable sshd service
|
||||
$STD rc-update add sshd
|
||||
# Allow root login via SSH
|
||||
sed -i "s/#PermitRootLogin prohibit-password/PermitRootLogin yes/g" /etc/ssh/sshd_config
|
||||
# Start the sshd service
|
||||
$STD /etc/init.d/sshd start
|
||||
fi
|
||||
}
|
||||
|
||||
# Validate Timezone for some LXC's
|
||||
validate_tz() {
|
||||
[[ -f "/usr/share/zoneinfo/$1" ]]
|
||||
}
|
||||
|
||||
# This function customizes the container and enables passwordless login for the root user
|
||||
customize() {
|
||||
if [[ "$PASSWORD" == "" ]]; then
|
||||
msg_info "Customizing Container"
|
||||
passwd -d root >/dev/null 2>&1
|
||||
|
||||
# Ensure agetty is available
|
||||
apk add --no-cache --force-broken-world util-linux >/dev/null 2>&1
|
||||
|
||||
# Create persistent autologin boot script
|
||||
mkdir -p /etc/local.d
|
||||
cat <<'EOF' >/etc/local.d/autologin.start
|
||||
#!/bin/sh
|
||||
sed -i 's|^tty1::respawn:.*|tty1::respawn:/sbin/agetty --autologin root --noclear tty1 38400 linux|' /etc/inittab
|
||||
kill -HUP 1
|
||||
EOF
|
||||
touch /root/.hushlogin
|
||||
|
||||
chmod +x /etc/local.d/autologin.start
|
||||
rc-update add local >/dev/null 2>&1
|
||||
|
||||
# Apply autologin immediately for current session
|
||||
/etc/local.d/autologin.start
|
||||
|
||||
msg_ok "Customized Container"
|
||||
fi
|
||||
|
||||
echo "bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/${app}.sh)\"" >/usr/bin/update
|
||||
chmod +x /usr/bin/update
|
||||
|
||||
if [[ -n "${SSH_AUTHORIZED_KEY}" ]]; then
|
||||
mkdir -p /root/.ssh
|
||||
echo "${SSH_AUTHORIZED_KEY}" >/root/.ssh/authorized_keys
|
||||
chmod 700 /root/.ssh
|
||||
chmod 600 /root/.ssh/authorized_keys
|
||||
fi
|
||||
}
|
||||
130
misc/old_misc/api.func
Normal file
130
misc/old_misc/api.func
Normal file
@ -0,0 +1,130 @@
|
||||
# Copyright (c) 2021-2025 community-scripts ORG
|
||||
# Author: michelroegl-brunner
|
||||
# License: MIT | https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/LICENSE
|
||||
|
||||
post_to_api() {
|
||||
|
||||
if ! command -v curl &>/dev/null; then
|
||||
return
|
||||
fi
|
||||
|
||||
if [ "$DIAGNOSTICS" = "no" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
if [ -z "$RANDOM_UUID" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
local API_URL="http://api.community-scripts.org/upload"
|
||||
local pve_version="not found"
|
||||
pve_version=$(pveversion | awk -F'[/ ]' '{print $2}')
|
||||
|
||||
JSON_PAYLOAD=$(
|
||||
cat <<EOF
|
||||
{
|
||||
"ct_type": $CT_TYPE,
|
||||
"type":"lxc",
|
||||
"disk_size": $DISK_SIZE,
|
||||
"core_count": $CORE_COUNT,
|
||||
"ram_size": $RAM_SIZE,
|
||||
"os_type": "$var_os",
|
||||
"os_version": "$var_version",
|
||||
"nsapp": "$NSAPP",
|
||||
"method": "$METHOD",
|
||||
"pve_version": "$pve_version",
|
||||
"status": "installing",
|
||||
"random_id": "$RANDOM_UUID"
|
||||
}
|
||||
EOF
|
||||
)
|
||||
|
||||
if [[ "$DIAGNOSTICS" == "yes" ]]; then
|
||||
RESPONSE=$(curl -s -w "%{http_code}" -L -X POST "$API_URL" --post301 --post302 \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$JSON_PAYLOAD") || true
|
||||
fi
|
||||
}
|
||||
|
||||
post_to_api_vm() {
|
||||
|
||||
if [[ ! -f /usr/local/community-scripts/diagnostics ]]; then
|
||||
return
|
||||
fi
|
||||
DIAGNOSTICS=$(grep -i "^DIAGNOSTICS=" /usr/local/community-scripts/diagnostics | awk -F'=' '{print $2}')
|
||||
if ! command -v curl &>/dev/null; then
|
||||
return
|
||||
fi
|
||||
|
||||
if [ "$DIAGNOSTICS" = "no" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
if [ -z "$RANDOM_UUID" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
local API_URL="http://api.community-scripts.org/upload"
|
||||
local pve_version="not found"
|
||||
pve_version=$(pveversion | awk -F'[/ ]' '{print $2}')
|
||||
|
||||
DISK_SIZE_API=${DISK_SIZE%G}
|
||||
|
||||
JSON_PAYLOAD=$(
|
||||
cat <<EOF
|
||||
{
|
||||
"ct_type": 2,
|
||||
"type":"vm",
|
||||
"disk_size": $DISK_SIZE_API,
|
||||
"core_count": $CORE_COUNT,
|
||||
"ram_size": $RAM_SIZE,
|
||||
"os_type": "$var_os",
|
||||
"os_version": "$var_version",
|
||||
"nsapp": "$NSAPP",
|
||||
"method": "$METHOD",
|
||||
"pve_version": "$pve_version",
|
||||
"status": "installing",
|
||||
"random_id": "$RANDOM_UUID"
|
||||
}
|
||||
EOF
|
||||
)
|
||||
|
||||
if [[ "$DIAGNOSTICS" == "yes" ]]; then
|
||||
RESPONSE=$(curl -s -w "%{http_code}" -L -X POST "$API_URL" --post301 --post302 \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$JSON_PAYLOAD") || true
|
||||
fi
|
||||
}
|
||||
|
||||
POST_UPDATE_DONE=false
|
||||
post_update_to_api() {
|
||||
|
||||
if ! command -v curl &>/dev/null; then
|
||||
return
|
||||
fi
|
||||
|
||||
if [ "$POST_UPDATE_DONE" = true ]; then
|
||||
return 0
|
||||
fi
|
||||
local API_URL="http://api.community-scripts.org/upload/updatestatus"
|
||||
local status="${1:-failed}"
|
||||
local error="${2:-No error message}"
|
||||
|
||||
JSON_PAYLOAD=$(
|
||||
cat <<EOF
|
||||
{
|
||||
"status": "$status",
|
||||
"error": "$error",
|
||||
"random_id": "$RANDOM_UUID"
|
||||
}
|
||||
EOF
|
||||
)
|
||||
|
||||
if [[ "$DIAGNOSTICS" == "yes" ]]; then
|
||||
RESPONSE=$(curl -s -w "%{http_code}" -L -X POST "$API_URL" --post301 --post302 \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$JSON_PAYLOAD") || true
|
||||
fi
|
||||
|
||||
POST_UPDATE_DONE=true
|
||||
}
|
||||
1439
misc/old_misc/build.func
Normal file
1439
misc/old_misc/build.func
Normal file
File diff suppressed because it is too large
Load Diff
699
misc/old_misc/config-file.func
Normal file
699
misc/old_misc/config-file.func
Normal file
@ -0,0 +1,699 @@
|
||||
config_file() {
|
||||
CONFIG_FILE="/opt/community-scripts/.settings"
|
||||
|
||||
if [[ -f "/opt/community-scripts/${NSAPP}.conf" ]]; then
|
||||
CONFIG_FILE="/opt/community-scripts/${NSAPP}.conf"
|
||||
fi
|
||||
|
||||
if CONFIG_FILE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set absolute path to config file" 8 58 "$CONFIG_FILE" --title "CONFIG FILE" 3>&1 1>&2 2>&3); then
|
||||
if [[ ! -f "$CONFIG_FILE" ]]; then
|
||||
echo -e "${CROSS}${RD}Config file not found, exiting script!.${CL}"
|
||||
exit
|
||||
else
|
||||
echo -e "${INFO}${BOLD}${DGN}Using config File: ${BGN}$CONFIG_FILE${CL}"
|
||||
source "$CONFIG_FILE"
|
||||
fi
|
||||
fi
|
||||
if [[ -n "${CT_ID-}" ]]; then
|
||||
if [[ "$CT_ID" =~ ^([0-9]{3,4})-([0-9]{3,4})$ ]]; then
|
||||
MIN_ID=${BASH_REMATCH[1]}
|
||||
MAX_ID=${BASH_REMATCH[2]}
|
||||
if ((MIN_ID >= MAX_ID)); then
|
||||
msg_error "Invalid Container ID range. The first number must be smaller than the second number, was ${CT_ID}"
|
||||
exit
|
||||
fi
|
||||
|
||||
LIST_OF_IDS=$(pvesh get /cluster/resources --type vm --output-format json 2>/dev/null | grep -oP '"vmid":\s*\K\d+') || true
|
||||
if [[ -n "$LIST_OF_IDS" ]]; then
|
||||
for ((ID = MIN_ID; ID <= MAX_ID; ID++)); do
|
||||
if ! grep -q "^$ID$" <<<"$LIST_OF_IDS"; then
|
||||
CT_ID=$ID
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}"
|
||||
|
||||
elif [[ "$CT_ID" =~ ^[0-9]+$ ]]; then
|
||||
LIST_OF_IDS=$(pvesh get /cluster/resources --type vm --output-format json 2>/dev/null | grep -oP '"vmid":\s*\K\d+') || true
|
||||
if [[ -n "$LIST_OF_IDS" ]]; then
|
||||
|
||||
if ! grep -q "^$CT_ID$" <<<"$LIST_OF_IDS"; then
|
||||
echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}"
|
||||
else
|
||||
msg_error "Container ID $CT_ID already exists"
|
||||
exit
|
||||
fi
|
||||
else
|
||||
echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}"
|
||||
fi
|
||||
else
|
||||
msg_error "Invalid Container ID format. Needs to be 0000-9999 or 0-9999, was ${CT_ID}"
|
||||
exit
|
||||
fi
|
||||
else
|
||||
if CT_ID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Container ID" 8 58 "$NEXTID" --title "CONTAINER ID" 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$CT_ID" ]; then
|
||||
CT_ID="$NEXTID"
|
||||
echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}"
|
||||
else
|
||||
echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}"
|
||||
fi
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
|
||||
fi
|
||||
if [[ -n "${CT_TYPE-}" ]]; then
|
||||
if [[ "$CT_TYPE" -eq 0 ]]; then
|
||||
CT_TYPE_DESC="Privileged"
|
||||
elif [[ "$CT_TYPE" -eq 1 ]]; then
|
||||
CT_TYPE_DESC="Unprivileged"
|
||||
else
|
||||
msg_error "Unknown setting for CT_TYPE, should be 1 or 0, was ${CT_TYPE}"
|
||||
exit
|
||||
fi
|
||||
echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}"
|
||||
else
|
||||
if CT_TYPE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \
|
||||
"1" "Unprivileged" ON \
|
||||
"0" "Privileged" OFF \
|
||||
3>&1 1>&2 2>&3); then
|
||||
if [ -n "$CT_TYPE" ]; then
|
||||
CT_TYPE_DESC="Unprivileged"
|
||||
if [ "$CT_TYPE" -eq 0 ]; then
|
||||
CT_TYPE_DESC="Privileged"
|
||||
fi
|
||||
echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}"
|
||||
fi
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "${PW-}" ]]; then
|
||||
if [[ "$PW" == "none" ]]; then
|
||||
PW=""
|
||||
else
|
||||
if [[ "$PW" == *" "* ]]; then
|
||||
msg_error "Password cannot be empty"
|
||||
exit
|
||||
elif [[ ${#PW} -lt 5 ]]; then
|
||||
msg_error "Password must be at least 5 characters long"
|
||||
exit
|
||||
else
|
||||
echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}********${CL}"
|
||||
fi
|
||||
PW="-password $PW"
|
||||
fi
|
||||
else
|
||||
while true; do
|
||||
if PW1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --passwordbox "\nSet Root Password (needed for root ssh access)" 9 58 --title "PASSWORD (leave blank for automatic login)" 3>&1 1>&2 2>&3); then
|
||||
if [[ -n "$PW1" ]]; then
|
||||
if [[ "$PW1" == *" "* ]]; then
|
||||
whiptail --msgbox "Password cannot contain spaces. Please try again." 8 58
|
||||
elif [ ${#PW1} -lt 5 ]; then
|
||||
whiptail --msgbox "Password must be at least 5 characters long. Please try again." 8 58
|
||||
else
|
||||
if PW2=$(whiptail --backtitle "Proxmox VE Helper Scripts" --passwordbox "\nVerify Root Password" 9 58 --title "PASSWORD VERIFICATION" 3>&1 1>&2 2>&3); then
|
||||
if [[ "$PW1" == "$PW2" ]]; then
|
||||
PW="-password $PW1"
|
||||
echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}********${CL}"
|
||||
break
|
||||
else
|
||||
whiptail --msgbox "Passwords do not match. Please try again." 8 58
|
||||
fi
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
fi
|
||||
else
|
||||
PW1="Automatic Login"
|
||||
PW=""
|
||||
echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}$PW1${CL}"
|
||||
break
|
||||
fi
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if [[ -n "${HN-}" ]]; then
|
||||
echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}"
|
||||
else
|
||||
if CT_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 "$NSAPP" --title "HOSTNAME" 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$CT_NAME" ]; then
|
||||
HN="$NSAPP"
|
||||
else
|
||||
HN=$(echo "${CT_NAME,,}" | tr -d ' ')
|
||||
fi
|
||||
echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}"
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "${DISK_SIZE-}" ]]; then
|
||||
if [[ "$DISK_SIZE" =~ ^-?[0-9]+$ ]]; then
|
||||
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}"
|
||||
else
|
||||
msg_error "DISK_SIZE must be an integer, was ${DISK_SIZE}"
|
||||
exit
|
||||
fi
|
||||
else
|
||||
if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GB" 8 58 "$var_disk" --title "DISK SIZE" 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$DISK_SIZE" ]; then
|
||||
DISK_SIZE="$var_disk"
|
||||
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}"
|
||||
else
|
||||
if ! [[ $DISK_SIZE =~ $INTEGER ]]; then
|
||||
echo -e "{INFO}${HOLD}${RD} DISK SIZE MUST BE AN INTEGER NUMBER!${CL}"
|
||||
advanced_settings
|
||||
fi
|
||||
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}"
|
||||
fi
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "${CORE_COUNT-}" ]]; then
|
||||
if [[ "$CORE_COUNT" =~ ^-?[0-9]+$ ]]; then
|
||||
echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}"
|
||||
else
|
||||
msg_error "CORE_COUNT must be an integer, was ${CORE_COUNT}"
|
||||
exit
|
||||
fi
|
||||
else
|
||||
if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 "$var_cpu" --title "CORE COUNT" 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$CORE_COUNT" ]; then
|
||||
CORE_COUNT="$var_cpu"
|
||||
echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}"
|
||||
else
|
||||
echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}"
|
||||
fi
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "${RAM_SIZE-}" ]]; then
|
||||
if [[ "$RAM_SIZE" =~ ^-?[0-9]+$ ]]; then
|
||||
echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}"
|
||||
else
|
||||
msg_error "RAM_SIZE must be an integer, was ${RAM_SIZE}"
|
||||
exit
|
||||
fi
|
||||
else
|
||||
if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 "$var_ram" --title "RAM" 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$RAM_SIZE" ]; then
|
||||
RAM_SIZE="$var_ram"
|
||||
echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}"
|
||||
else
|
||||
echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}"
|
||||
fi
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
fi
|
||||
|
||||
IFACE_FILEPATH_LIST="/etc/network/interfaces"$'\n'$(find "/etc/network/interfaces.d/" -type f)
|
||||
BRIDGES=""
|
||||
OLD_IFS=$IFS
|
||||
IFS=$'\n'
|
||||
|
||||
for iface_filepath in ${IFACE_FILEPATH_LIST}; do
|
||||
|
||||
iface_indexes_tmpfile=$(mktemp -q -u '.iface-XXXX')
|
||||
(grep -Pn '^\s*iface' "${iface_filepath}" | cut -d':' -f1 && wc -l "${iface_filepath}" | cut -d' ' -f1) | awk 'FNR==1 {line=$0; next} {print line":"$0-1; line=$0}' >"${iface_indexes_tmpfile}" || true
|
||||
|
||||
if [ -f "${iface_indexes_tmpfile}" ]; then
|
||||
|
||||
while read -r pair; do
|
||||
start=$(echo "${pair}" | cut -d':' -f1)
|
||||
end=$(echo "${pair}" | cut -d':' -f2)
|
||||
if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge)\b'; then
|
||||
iface_name=$(sed "${start}q;d" "${iface_filepath}" | awk '{print $2}')
|
||||
BRIDGES="${iface_name}"$'\n'"${BRIDGES}"
|
||||
fi
|
||||
|
||||
done <"${iface_indexes_tmpfile}"
|
||||
rm -f "${iface_indexes_tmpfile}"
|
||||
fi
|
||||
|
||||
done
|
||||
IFS=$OLD_IFS
|
||||
BRIDGES=$(echo "$BRIDGES" | grep -v '^\s*$' | sort | uniq)
|
||||
|
||||
if [[ -n "${BRG-}" ]]; then
|
||||
if echo "$BRIDGES" | grep -q "${BRG}"; then
|
||||
echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}"
|
||||
else
|
||||
msg_error "Bridge '${BRG}' does not exist in /etc/network/interfaces or /etc/network/interfaces.d/sdn"
|
||||
exit
|
||||
fi
|
||||
else
|
||||
BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --menu "Select network bridge:" 15 40 6 $(echo "$BRIDGES" | awk '{print $0, "Bridge"}') 3>&1 1>&2 2>&3)
|
||||
if [ -z "$BRG" ]; then
|
||||
exit_script
|
||||
else
|
||||
echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}"
|
||||
fi
|
||||
fi
|
||||
|
||||
local ip_cidr_regex='^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/([0-9]{1,2})$'
|
||||
local ip_regex='^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$'
|
||||
|
||||
if [[ -n ${NET-} ]]; then
|
||||
if [ "$NET" == "dhcp" ]; then
|
||||
echo -e "${NETWORK}${BOLD}${DGN}IP Address: ${BGN}DHCP${CL}"
|
||||
echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}Default${CL}"
|
||||
GATE=""
|
||||
elif [[ "$NET" =~ $ip_cidr_regex ]]; then
|
||||
echo -e "${NETWORK}${BOLD}${DGN}IP Address: ${BGN}$NET${CL}"
|
||||
if [[ -n "$GATE" ]]; then
|
||||
[[ "$GATE" =~ ",gw=" ]] && GATE="${GATE##,gw=}"
|
||||
if [[ "$GATE" =~ $ip_regex ]]; then
|
||||
echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}$GATE${CL}"
|
||||
GATE=",gw=$GATE"
|
||||
else
|
||||
msg_error "Invalid IP Address format for Gateway. Needs to be 0.0.0.0, was ${GATE}"
|
||||
exit
|
||||
fi
|
||||
|
||||
else
|
||||
while true; do
|
||||
GATE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Enter gateway IP address" 8 58 --title "Gateway IP" 3>&1 1>&2 2>&3)
|
||||
if [ -z "$GATE1" ]; then
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "Gateway IP address cannot be empty" 8 58
|
||||
elif [[ ! "$GATE1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "Invalid IP address format" 8 58
|
||||
else
|
||||
GATE=",gw=$GATE1"
|
||||
echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}$GATE1${CL}"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
elif [[ "$NET" == *-* ]]; then
|
||||
IFS="-" read -r ip_start ip_end <<<"$NET"
|
||||
|
||||
if [[ ! "$ip_start" =~ $ip_cidr_regex ]] || [[ ! "$ip_end" =~ $ip_cidr_regex ]]; then
|
||||
msg_error "Invalid IP range format, was $NET should be 0.0.0.0/0-0.0.0.0/0"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ip1="${ip_start%%/*}"
|
||||
ip2="${ip_end%%/*}"
|
||||
cidr="${ip_start##*/}"
|
||||
|
||||
ip_to_int() {
|
||||
local IFS=.
|
||||
read -r i1 i2 i3 i4 <<<"$1"
|
||||
echo $(((i1 << 24) + (i2 << 16) + (i3 << 8) + i4))
|
||||
}
|
||||
|
||||
int_to_ip() {
|
||||
local ip=$1
|
||||
echo "$(((ip >> 24) & 0xFF)).$(((ip >> 16) & 0xFF)).$(((ip >> 8) & 0xFF)).$((ip & 0xFF))"
|
||||
}
|
||||
|
||||
start_int=$(ip_to_int "$ip1")
|
||||
end_int=$(ip_to_int "$ip2")
|
||||
|
||||
for ((ip_int = start_int; ip_int <= end_int; ip_int++)); do
|
||||
ip=$(int_to_ip $ip_int)
|
||||
msg_info "Checking IP: $ip"
|
||||
if ! ping -c 2 -W 1 "$ip" >/dev/null 2>&1; then
|
||||
NET="$ip/$cidr"
|
||||
msg_ok "Using free IP Address: ${BGN}$NET${CL}"
|
||||
sleep 3
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [[ "$NET" == *-* ]]; then
|
||||
msg_error "No free IP found in range"
|
||||
exit 1
|
||||
fi
|
||||
if [ -n "$GATE" ]; then
|
||||
if [[ "$GATE" =~ $ip_regex ]]; then
|
||||
echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}$GATE${CL}"
|
||||
GATE=",gw=$GATE"
|
||||
else
|
||||
msg_error "Invalid IP Address format for Gateway. Needs to be 0.0.0.0, was ${GATE}"
|
||||
exit
|
||||
fi
|
||||
else
|
||||
while true; do
|
||||
GATE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Enter gateway IP address" 8 58 --title "Gateway IP" 3>&1 1>&2 2>&3)
|
||||
if [ -z "$GATE1" ]; then
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "Gateway IP address cannot be empty" 8 58
|
||||
elif [[ ! "$GATE1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "Invalid IP address format" 8 58
|
||||
else
|
||||
GATE=",gw=$GATE1"
|
||||
echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}$GATE1${CL}"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
else
|
||||
msg_error "Invalid IP Address format. Needs to be 0.0.0.0/0 or a range like 10.0.0.1/24-10.0.0.10/24, was ${NET}"
|
||||
exit
|
||||
fi
|
||||
else
|
||||
while true; do
|
||||
NET=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Static IPv4 CIDR Address (/24)" 8 58 dhcp --title "IP ADDRESS" 3>&1 1>&2 2>&3)
|
||||
exit_status=$?
|
||||
if [ $exit_status -eq 0 ]; then
|
||||
if [ "$NET" = "dhcp" ]; then
|
||||
echo -e "${NETWORK}${BOLD}${DGN}IP Address: ${BGN}$NET${CL}"
|
||||
break
|
||||
else
|
||||
if [[ "$NET" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then
|
||||
echo -e "${NETWORK}${BOLD}${DGN}IP Address: ${BGN}$NET${CL}"
|
||||
break
|
||||
else
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "$NET is an invalid IPv4 CIDR address. Please enter a valid IPv4 CIDR address or 'dhcp'" 8 58
|
||||
fi
|
||||
fi
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
done
|
||||
if [ "$NET" != "dhcp" ]; then
|
||||
while true; do
|
||||
GATE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Enter gateway IP address" 8 58 --title "Gateway IP" 3>&1 1>&2 2>&3)
|
||||
if [ -z "$GATE1" ]; then
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "Gateway IP address cannot be empty" 8 58
|
||||
elif [[ ! "$GATE1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "Invalid IP address format" 8 58
|
||||
else
|
||||
GATE=",gw=$GATE1"
|
||||
echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}$GATE1${CL}"
|
||||
break
|
||||
fi
|
||||
done
|
||||
else
|
||||
GATE=""
|
||||
echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}Default${CL}"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$var_os" == "alpine" ]; then
|
||||
APT_CACHER=""
|
||||
APT_CACHER_IP=""
|
||||
else
|
||||
if [[ -n "${APT_CACHER_IP-}" ]]; then
|
||||
if [[ ! $APT_CACHER_IP == "none" ]]; then
|
||||
APT_CACHER="yes"
|
||||
echo -e "${NETWORK}${BOLD}${DGN}APT-CACHER IP Address: ${BGN}$APT_CACHER_IP${CL}"
|
||||
else
|
||||
APT_CACHER=""
|
||||
echo -e "${NETWORK}${BOLD}${DGN}APT-Cacher IP Address: ${BGN}No${CL}"
|
||||
fi
|
||||
else
|
||||
if APT_CACHER_IP=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set APT-Cacher IP (leave blank for none)" 8 58 --title "APT-Cacher IP" 3>&1 1>&2 2>&3); then
|
||||
APT_CACHER="${APT_CACHER_IP:+yes}"
|
||||
echo -e "${NETWORK}${BOLD}${DGN}APT-Cacher IP Address: ${BGN}${APT_CACHER_IP:-Default}${CL}"
|
||||
if [[ -n $APT_CACHER_IP ]]; then
|
||||
APT_CACHER_IP="none"
|
||||
fi
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "${MTU-}" ]]; then
|
||||
if [[ "$MTU" =~ ^-?[0-9]+$ ]]; then
|
||||
echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU${CL}"
|
||||
MTU=",mtu=$MTU"
|
||||
else
|
||||
msg_error "MTU must be an integer, was ${MTU}"
|
||||
exit
|
||||
fi
|
||||
else
|
||||
if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default [The MTU of your selected vmbr, default is 1500])" 8 58 --title "MTU SIZE" 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$MTU1" ]; then
|
||||
MTU1="Default"
|
||||
MTU=""
|
||||
else
|
||||
MTU=",mtu=$MTU1"
|
||||
fi
|
||||
echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}"
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$IPV6_METHOD" == "static" ]]; then
|
||||
if [[ -n "$IPV6STATIC" ]]; then
|
||||
IP6=",ip6=${IPV6STATIC}"
|
||||
echo -e "${NETWORK}${BOLD}${DGN}IPv6 Address: ${BGN}${IPV6STATIC}${CL}"
|
||||
else
|
||||
msg_error "IPV6_METHOD is set to static but IPV6STATIC is empty"
|
||||
exit
|
||||
fi
|
||||
elif [[ "$IPV6_METHOD" == "auto" ]]; then
|
||||
IP6=",ip6=auto"
|
||||
echo -e "${NETWORK}${BOLD}${DGN}IPv6 Address: ${BGN}auto${CL}"
|
||||
else
|
||||
IP6=""
|
||||
echo -e "${NETWORK}${BOLD}${DGN}IPv6 Address: ${BGN}none${CL}"
|
||||
fi
|
||||
|
||||
if [[ -n "${SD-}" ]]; then
|
||||
if [[ "$SD" == "none" ]]; then
|
||||
SD=""
|
||||
echo -e "${SEARCH}${BOLD}${DGN}DNS Search Domain: ${BGN}Host${CL}"
|
||||
else
|
||||
# Strip prefix if present for config file storage
|
||||
local SD_VALUE="$SD"
|
||||
[[ "$SD" =~ ^-searchdomain= ]] && SD_VALUE="${SD#-searchdomain=}"
|
||||
echo -e "${SEARCH}${BOLD}${DGN}DNS Search Domain: ${BGN}$SD_VALUE${CL}"
|
||||
SD="-searchdomain=$SD_VALUE"
|
||||
fi
|
||||
else
|
||||
if SD=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a DNS Search Domain (leave blank for HOST)" 8 58 --title "DNS Search Domain" 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$SD" ]; then
|
||||
SX=Host
|
||||
SD=""
|
||||
else
|
||||
SX=$SD
|
||||
SD="-searchdomain=$SD"
|
||||
fi
|
||||
echo -e "${SEARCH}${BOLD}${DGN}DNS Search Domain: ${BGN}$SX${CL}"
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "${NS-}" ]]; then
|
||||
if [[ $NS == "none" ]]; then
|
||||
NS=""
|
||||
echo -e "${NETWORK}${BOLD}${DGN}DNS Server IP Address: ${BGN}Host${CL}"
|
||||
else
|
||||
# Strip prefix if present for config file storage
|
||||
local NS_VALUE="$NS"
|
||||
[[ "$NS" =~ ^-nameserver= ]] && NS_VALUE="${NS#-nameserver=}"
|
||||
if [[ "$NS_VALUE" =~ $ip_regex ]]; then
|
||||
echo -e "${NETWORK}${BOLD}${DGN}DNS Server IP Address: ${BGN}$NS_VALUE${CL}"
|
||||
NS="-nameserver=$NS_VALUE"
|
||||
else
|
||||
msg_error "Invalid IP Address format for DNS Server. Needs to be 0.0.0.0, was ${NS_VALUE}"
|
||||
exit
|
||||
fi
|
||||
fi
|
||||
else
|
||||
if NX=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a DNS Server IP (leave blank for HOST)" 8 58 --title "DNS SERVER IP" 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$NX" ]; then
|
||||
NX=Host
|
||||
NS=""
|
||||
else
|
||||
NS="-nameserver=$NX"
|
||||
fi
|
||||
echo -e "${NETWORK}${BOLD}${DGN}DNS Server IP Address: ${BGN}$NX${CL}"
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "${MAC-}" ]]; then
|
||||
if [[ "$MAC" == "none" ]]; then
|
||||
MAC=""
|
||||
echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}Host${CL}"
|
||||
else
|
||||
# Strip prefix if present for config file storage
|
||||
local MAC_VALUE="$MAC"
|
||||
[[ "$MAC" =~ ^,hwaddr= ]] && MAC_VALUE="${MAC#,hwaddr=}"
|
||||
if [[ "$MAC_VALUE" =~ ^([A-Fa-f0-9]{2}:){5}[A-Fa-f0-9]{2}$ ]]; then
|
||||
echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC_VALUE${CL}"
|
||||
MAC=",hwaddr=$MAC_VALUE"
|
||||
else
|
||||
msg_error "MAC Address must be in the format xx:xx:xx:xx:xx:xx, was ${MAC_VALUE}"
|
||||
exit
|
||||
fi
|
||||
fi
|
||||
else
|
||||
if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address(leave blank for generated MAC)" 8 58 --title "MAC ADDRESS" 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$MAC1" ]; then
|
||||
MAC1="Default"
|
||||
MAC=""
|
||||
else
|
||||
MAC=",hwaddr=$MAC1"
|
||||
echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}"
|
||||
fi
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "${VLAN-}" ]]; then
|
||||
if [[ "$VLAN" == "none" ]]; then
|
||||
VLAN=""
|
||||
echo -e "${VLANTAG}${BOLD}${DGN}Vlan: ${BGN}Host${CL}"
|
||||
else
|
||||
# Strip prefix if present for config file storage
|
||||
local VLAN_VALUE="$VLAN"
|
||||
[[ "$VLAN" =~ ^,tag= ]] && VLAN_VALUE="${VLAN#,tag=}"
|
||||
if [[ "$VLAN_VALUE" =~ ^-?[0-9]+$ ]]; then
|
||||
echo -e "${VLANTAG}${BOLD}${DGN}Vlan: ${BGN}$VLAN_VALUE${CL}"
|
||||
VLAN=",tag=$VLAN_VALUE"
|
||||
else
|
||||
msg_error "VLAN must be an integer, was ${VLAN_VALUE}"
|
||||
exit
|
||||
fi
|
||||
fi
|
||||
else
|
||||
if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for no VLAN)" 8 58 --title "VLAN" 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$VLAN1" ]; then
|
||||
VLAN1="Default"
|
||||
VLAN=""
|
||||
else
|
||||
VLAN=",tag=$VLAN1"
|
||||
fi
|
||||
echo -e "${VLANTAG}${BOLD}${DGN}Vlan: ${BGN}$VLAN1${CL}"
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "${TAGS-}" ]]; then
|
||||
if [[ "$TAGS" == *"DEFAULT"* ]]; then
|
||||
TAGS="${TAGS//DEFAULT/}"
|
||||
TAGS="${TAGS//;/}"
|
||||
TAGS="$TAGS;${var_tags:-}"
|
||||
echo -e "${NETWORK}${BOLD}${DGN}Tags: ${BGN}$TAGS${CL}"
|
||||
fi
|
||||
else
|
||||
TAGS="community-scripts;"
|
||||
if ADV_TAGS=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Custom Tags?[If you remove all, there will be no tags!]" 8 58 "${TAGS}" --title "Advanced Tags" 3>&1 1>&2 2>&3); then
|
||||
if [ -n "${ADV_TAGS}" ]; then
|
||||
ADV_TAGS=$(echo "$ADV_TAGS" | tr -d '[:space:]')
|
||||
TAGS="${ADV_TAGS}"
|
||||
else
|
||||
TAGS=";"
|
||||
fi
|
||||
echo -e "${NETWORK}${BOLD}${DGN}Tags: ${BGN}$TAGS${CL}"
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "${SSH-}" ]]; then
|
||||
if [[ "$SSH" == "yes" ]]; then
|
||||
echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}"
|
||||
if [[ ! -z "$SSH_AUTHORIZED_KEY" ]]; then
|
||||
echo -e "${ROOTSSH}${BOLD}${DGN}SSH Authorized Key: ${BGN}********************${CL}"
|
||||
else
|
||||
echo -e "${ROOTSSH}${BOLD}${DGN}SSH Authorized Key: ${BGN}None${CL}"
|
||||
fi
|
||||
elif [[ "$SSH" == "no" ]]; then
|
||||
echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}"
|
||||
else
|
||||
msg_error "SSH needs to be 'yes' or 'no', was ${SSH}"
|
||||
exit
|
||||
fi
|
||||
else
|
||||
SSH_AUTHORIZED_KEY="$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "SSH Authorized key for root (leave empty for none)" 8 58 --title "SSH Key" 3>&1 1>&2 2>&3)"
|
||||
if [[ -z "${SSH_AUTHORIZED_KEY}" ]]; then
|
||||
SSH_AUTHORIZED_KEY=""
|
||||
fi
|
||||
if [[ "$PW" == -password* || -n "$SSH_AUTHORIZED_KEY" ]]; then
|
||||
if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH ACCESS" --yesno "Enable Root SSH Access?" 10 58); then
|
||||
SSH="yes"
|
||||
else
|
||||
SSH="no"
|
||||
fi
|
||||
echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}"
|
||||
else
|
||||
SSH="no"
|
||||
echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "$ENABLE_FUSE" ]]; then
|
||||
if [[ "$ENABLE_FUSE" == "yes" ]]; then
|
||||
echo -e "${FUSE}${BOLD}${DGN}Enable FUSE: ${BGN}Yes${CL}"
|
||||
elif [[ "$ENABLE_FUSE" == "no" ]]; then
|
||||
echo -e "${FUSE}${BOLD}${DGN}Enable FUSE: ${BGN}No${CL}"
|
||||
else
|
||||
msg_error "Enable FUSE needs to be 'yes' or 'no', was ${ENABLE_FUSE}"
|
||||
exit
|
||||
fi
|
||||
else
|
||||
if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "FUSE" --yesno "Enable FUSE?" 10 58); then
|
||||
ENABLE_FUSE="yes"
|
||||
else
|
||||
ENABLE_FUSE="no"
|
||||
fi
|
||||
echo -e "${FUSE}${BOLD}${DGN}Enable FUSE: ${BGN}$ENABLE_FUSE${CL}"
|
||||
fi
|
||||
|
||||
if [[ -n "$ENABLE_TUN" ]]; then
|
||||
if [[ "$ENABLE_TUN" == "yes" ]]; then
|
||||
echo -e "${FUSE}${BOLD}${DGN}Enable TUN: ${BGN}Yes${CL}"
|
||||
elif [[ "$ENABLE_TUN" == "no" ]]; then
|
||||
echo -e "${FUSE}${BOLD}${DGN}Enable TUN: ${BGN}No${CL}"
|
||||
else
|
||||
msg_error "Enable TUN needs to be 'yes' or 'no', was ${ENABLE_TUN}"
|
||||
exit
|
||||
fi
|
||||
else
|
||||
if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "TUN" --yesno "Enable TUN?" 10 58); then
|
||||
ENABLE_TUN="yes"
|
||||
else
|
||||
ENABLE_TUN="no"
|
||||
fi
|
||||
echo -e "${FUSE}${BOLD}${DGN}Enable TUN: ${BGN}$ENABLE_TUN${CL}"
|
||||
fi
|
||||
|
||||
if [[ -n "${VERBOSE-}" ]]; then
|
||||
if [[ "$VERBOSE" == "yes" ]]; then
|
||||
echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERBOSE${CL}"
|
||||
elif [[ "$VERBOSE" == "no" ]]; then
|
||||
echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}No${CL}"
|
||||
else
|
||||
msg_error "Verbose Mode needs to be 'yes' or 'no', was ${VERBOSE}"
|
||||
exit
|
||||
fi
|
||||
else
|
||||
if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "VERBOSE MODE" --yesno "Enable Verbose Mode?" 10 58); then
|
||||
VERBOSE="yes"
|
||||
else
|
||||
VERBOSE="no"
|
||||
fi
|
||||
echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERBOSE${CL}"
|
||||
fi
|
||||
|
||||
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS WITH CONFIG FILE COMPLETE" --yesno "Ready to create ${APP} LXC?" 10 58); then
|
||||
echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above settings${CL}"
|
||||
else
|
||||
clear
|
||||
header_info
|
||||
echo -e "${INFO}${HOLD} ${GN}Using Config File on node $PVEHOST_NAME${CL}"
|
||||
config_file
|
||||
fi
|
||||
}
|
||||
452
misc/old_misc/core.func
Normal file
452
misc/old_misc/core.func
Normal file
@ -0,0 +1,452 @@
|
||||
# Copyright (c) 2021-2025 community-scripts ORG
|
||||
# License: MIT | https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/LICENSE
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Loads core utility groups once (colors, formatting, icons, defaults).
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
[[ -n "${_CORE_FUNC_LOADED:-}" ]] && return
|
||||
_CORE_FUNC_LOADED=1
|
||||
|
||||
load_functions() {
|
||||
[[ -n "${__FUNCTIONS_LOADED:-}" ]] && return
|
||||
__FUNCTIONS_LOADED=1
|
||||
color
|
||||
formatting
|
||||
icons
|
||||
default_vars
|
||||
set_std_mode
|
||||
# add more
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Error & Signal Handling – robust, universal, subshell-safe
|
||||
# ============================================================================
|
||||
|
||||
_tool_error_hint() {
|
||||
local cmd="$1"
|
||||
local code="$2"
|
||||
case "$cmd" in
|
||||
curl)
|
||||
case "$code" in
|
||||
6) echo "Curl: Could not resolve host (DNS problem)" ;;
|
||||
7) echo "Curl: Failed to connect to host (connection refused)" ;;
|
||||
22) echo "Curl: HTTP error (404/403 etc)" ;;
|
||||
28) echo "Curl: Operation timeout" ;;
|
||||
*) echo "Curl: Unknown error ($code)" ;;
|
||||
esac
|
||||
;;
|
||||
wget)
|
||||
echo "Wget failed – URL unreachable or permission denied"
|
||||
;;
|
||||
systemctl)
|
||||
echo "Systemd unit failure – check service name and permissions"
|
||||
;;
|
||||
jq)
|
||||
echo "jq parse error – malformed JSON or missing key"
|
||||
;;
|
||||
mariadb | mysql)
|
||||
echo "MySQL/MariaDB command failed – check credentials or DB"
|
||||
;;
|
||||
unzip)
|
||||
echo "unzip failed – corrupt file or missing permission"
|
||||
;;
|
||||
tar)
|
||||
echo "tar failed – invalid format or missing binary"
|
||||
;;
|
||||
node | npm | pnpm | yarn)
|
||||
echo "Node tool failed – check version compatibility or package.json"
|
||||
;;
|
||||
*) echo "" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
catch_errors() {
|
||||
set -Eeuo pipefail
|
||||
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Sets ANSI color codes used for styled terminal output.
|
||||
# ------------------------------------------------------------------------------
|
||||
color() {
|
||||
YW=$(echo "\033[33m")
|
||||
YWB=$'\e[93m'
|
||||
BL=$(echo "\033[36m")
|
||||
RD=$(echo "\033[01;31m")
|
||||
BGN=$(echo "\033[4;92m")
|
||||
GN=$(echo "\033[1;92m")
|
||||
DGN=$(echo "\033[32m")
|
||||
CL=$(echo "\033[m")
|
||||
}
|
||||
|
||||
# Special for spinner and colorized output via printf
|
||||
color_spinner() {
|
||||
CS_YW=$'\033[33m'
|
||||
CS_YWB=$'\033[93m'
|
||||
CS_CL=$'\033[m'
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Defines formatting helpers like tab, bold, and line reset sequences.
|
||||
# ------------------------------------------------------------------------------
|
||||
formatting() {
|
||||
BFR="\\r\\033[K"
|
||||
BOLD=$(echo "\033[1m")
|
||||
HOLD=" "
|
||||
TAB=" "
|
||||
TAB3=" "
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Sets symbolic icons used throughout user feedback and prompts.
|
||||
# ------------------------------------------------------------------------------
|
||||
icons() {
|
||||
CM="${TAB}✔️${TAB}"
|
||||
CROSS="${TAB}✖️${TAB}"
|
||||
DNSOK="✔️ "
|
||||
DNSFAIL="${TAB}✖️${TAB}"
|
||||
INFO="${TAB}💡${TAB}${CL}"
|
||||
OS="${TAB}🖥️${TAB}${CL}"
|
||||
OSVERSION="${TAB}🌟${TAB}${CL}"
|
||||
CONTAINERTYPE="${TAB}📦${TAB}${CL}"
|
||||
DISKSIZE="${TAB}💾${TAB}${CL}"
|
||||
CPUCORE="${TAB}🧠${TAB}${CL}"
|
||||
RAMSIZE="${TAB}🛠️${TAB}${CL}"
|
||||
SEARCH="${TAB}🔍${TAB}${CL}"
|
||||
VERBOSE_CROPPED="🔍${TAB}"
|
||||
VERIFYPW="${TAB}🔐${TAB}${CL}"
|
||||
CONTAINERID="${TAB}🆔${TAB}${CL}"
|
||||
HOSTNAME="${TAB}🏠${TAB}${CL}"
|
||||
BRIDGE="${TAB}🌉${TAB}${CL}"
|
||||
NETWORK="${TAB}📡${TAB}${CL}"
|
||||
GATEWAY="${TAB}🌐${TAB}${CL}"
|
||||
DISABLEIPV6="${TAB}🚫${TAB}${CL}"
|
||||
DEFAULT="${TAB}⚙️${TAB}${CL}"
|
||||
MACADDRESS="${TAB}🔗${TAB}${CL}"
|
||||
VLANTAG="${TAB}🏷️${TAB}${CL}"
|
||||
ROOTSSH="${TAB}🔑${TAB}${CL}"
|
||||
CREATING="${TAB}🚀${TAB}${CL}"
|
||||
ADVANCED="${TAB}🧩${TAB}${CL}"
|
||||
FUSE="${TAB}🗂️${TAB}${CL}"
|
||||
HOURGLASS="${TAB}⏳${TAB}"
|
||||
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Sets default retry and wait variables used for system actions.
|
||||
# ------------------------------------------------------------------------------
|
||||
default_vars() {
|
||||
RETRY_NUM=10
|
||||
RETRY_EVERY=3
|
||||
i=$RETRY_NUM
|
||||
#[[ "${VAR_OS:-}" == "unknown" ]]
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Sets default verbose mode for script and os execution.
|
||||
# ------------------------------------------------------------------------------
|
||||
set_std_mode() {
|
||||
if [ "${VERBOSE:-no}" = "yes" ]; then
|
||||
STD=""
|
||||
else
|
||||
STD="silent"
|
||||
fi
|
||||
}
|
||||
|
||||
# Silent execution function
|
||||
silent() {
|
||||
"$@" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# Function to download & save header files
|
||||
get_header() {
|
||||
local app_name=$(echo "${APP,,}" | tr -d ' ')
|
||||
local app_type=${APP_TYPE:-ct}
|
||||
local header_url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/${app_type}/headers/${app_name}"
|
||||
local local_header_path="/usr/local/community-scripts/headers/${app_type}/${app_name}"
|
||||
|
||||
mkdir -p "$(dirname "$local_header_path")"
|
||||
|
||||
if [ ! -s "$local_header_path" ]; then
|
||||
if ! curl -fsSL "$header_url" -o "$local_header_path"; then
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
cat "$local_header_path" 2>/dev/null || true
|
||||
}
|
||||
|
||||
header_info() {
|
||||
local app_name=$(echo "${APP,,}" | tr -d ' ')
|
||||
local header_content
|
||||
|
||||
header_content=$(get_header "$app_name") || header_content=""
|
||||
|
||||
clear
|
||||
local term_width
|
||||
term_width=$(tput cols 2>/dev/null || echo 120)
|
||||
|
||||
if [ -n "$header_content" ]; then
|
||||
echo "$header_content"
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_tput() {
|
||||
if ! command -v tput >/dev/null 2>&1; then
|
||||
if grep -qi 'alpine' /etc/os-release; then
|
||||
apk add --no-cache ncurses >/dev/null 2>&1
|
||||
elif command -v apt-get >/dev/null 2>&1; then
|
||||
apt-get update -qq >/dev/null
|
||||
apt-get install -y -qq ncurses-bin >/dev/null 2>&1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
is_alpine() {
|
||||
local os_id="${var_os:-${PCT_OSTYPE:-}}"
|
||||
|
||||
if [[ -z "$os_id" && -f /etc/os-release ]]; then
|
||||
os_id="$(
|
||||
. /etc/os-release 2>/dev/null
|
||||
echo "${ID:-}"
|
||||
)"
|
||||
fi
|
||||
|
||||
[[ "$os_id" == "alpine" ]]
|
||||
}
|
||||
|
||||
is_verbose_mode() {
|
||||
local verbose="${VERBOSE:-${var_verbose:-no}}"
|
||||
local tty_status
|
||||
if [[ -t 2 ]]; then
|
||||
tty_status="interactive"
|
||||
else
|
||||
tty_status="not-a-tty"
|
||||
fi
|
||||
[[ "$verbose" != "no" || ! -t 2 ]]
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Handles specific curl error codes and displays descriptive messages.
|
||||
# ------------------------------------------------------------------------------
|
||||
__curl_err_handler() {
|
||||
local exit_code="$1"
|
||||
local target="$2"
|
||||
local curl_msg="$3"
|
||||
|
||||
case $exit_code in
|
||||
1) msg_error "Unsupported protocol: $target" ;;
|
||||
2) msg_error "Curl init failed: $target" ;;
|
||||
3) msg_error "Malformed URL: $target" ;;
|
||||
5) msg_error "Proxy resolution failed: $target" ;;
|
||||
6) msg_error "Host resolution failed: $target" ;;
|
||||
7) msg_error "Connection failed: $target" ;;
|
||||
9) msg_error "Access denied: $target" ;;
|
||||
18) msg_error "Partial file transfer: $target" ;;
|
||||
22) msg_error "HTTP error (e.g. 400/404): $target" ;;
|
||||
23) msg_error "Write error on local system: $target" ;;
|
||||
26) msg_error "Read error from local file: $target" ;;
|
||||
28) msg_error "Timeout: $target" ;;
|
||||
35) msg_error "SSL connect error: $target" ;;
|
||||
47) msg_error "Too many redirects: $target" ;;
|
||||
51) msg_error "SSL cert verify failed: $target" ;;
|
||||
52) msg_error "Empty server response: $target" ;;
|
||||
55) msg_error "Send error: $target" ;;
|
||||
56) msg_error "Receive error: $target" ;;
|
||||
60) msg_error "SSL CA not trusted: $target" ;;
|
||||
67) msg_error "Login denied by server: $target" ;;
|
||||
78) msg_error "Remote file not found (404): $target" ;;
|
||||
*) msg_error "Curl failed with code $exit_code: $target" ;;
|
||||
esac
|
||||
|
||||
[[ -n "$curl_msg" ]] && printf "%s\n" "$curl_msg" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
fatal() {
|
||||
msg_error "$1"
|
||||
kill -INT $$
|
||||
}
|
||||
|
||||
spinner() {
|
||||
local chars=(⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏)
|
||||
local i=0
|
||||
while true; do
|
||||
local index=$((i++ % ${#chars[@]}))
|
||||
printf "\r\033[2K%s %b" "${CS_YWB}${chars[$index]}${CS_CL}" "${CS_YWB}${SPINNER_MSG:-}${CS_CL}"
|
||||
sleep 0.1
|
||||
done
|
||||
}
|
||||
|
||||
clear_line() {
|
||||
tput cr 2>/dev/null || echo -en "\r"
|
||||
tput el 2>/dev/null || echo -en "\033[K"
|
||||
}
|
||||
|
||||
stop_spinner() {
|
||||
local pid="${SPINNER_PID:-}"
|
||||
[[ -z "$pid" && -f /tmp/.spinner.pid ]] && pid=$(</tmp/.spinner.pid)
|
||||
|
||||
if [[ -n "$pid" && "$pid" =~ ^[0-9]+$ ]]; then
|
||||
if kill "$pid" 2>/dev/null; then
|
||||
sleep 0.05
|
||||
kill -9 "$pid" 2>/dev/null || true
|
||||
wait "$pid" 2>/dev/null || true
|
||||
fi
|
||||
rm -f /tmp/.spinner.pid
|
||||
fi
|
||||
|
||||
unset SPINNER_PID SPINNER_MSG
|
||||
stty sane 2>/dev/null || true
|
||||
}
|
||||
|
||||
msg_info() {
|
||||
local msg="$1"
|
||||
[[ -z "$msg" ]] && return
|
||||
|
||||
if ! declare -p MSG_INFO_SHOWN &>/dev/null || ! declare -A MSG_INFO_SHOWN &>/dev/null; then
|
||||
declare -gA MSG_INFO_SHOWN=()
|
||||
fi
|
||||
[[ -n "${MSG_INFO_SHOWN["$msg"]+x}" ]] && return
|
||||
MSG_INFO_SHOWN["$msg"]=1
|
||||
|
||||
stop_spinner
|
||||
SPINNER_MSG="$msg"
|
||||
|
||||
if is_verbose_mode || is_alpine; then
|
||||
local HOURGLASS="${TAB}⏳${TAB}"
|
||||
printf "\r\e[2K%s %b" "$HOURGLASS" "${YW}${msg}${CL}" >&2
|
||||
return
|
||||
fi
|
||||
|
||||
color_spinner
|
||||
spinner &
|
||||
SPINNER_PID=$!
|
||||
echo "$SPINNER_PID" >/tmp/.spinner.pid
|
||||
disown "$SPINNER_PID" 2>/dev/null || true
|
||||
}
|
||||
|
||||
msg_ok() {
|
||||
local msg="$1"
|
||||
[[ -z "$msg" ]] && return
|
||||
stop_spinner
|
||||
clear_line
|
||||
printf "%s %b\n" "$CM" "${GN}${msg}${CL}" >&2
|
||||
unset MSG_INFO_SHOWN["$msg"]
|
||||
}
|
||||
|
||||
msg_error() {
|
||||
stop_spinner
|
||||
local msg="$1"
|
||||
echo -e "${BFR:-} ${CROSS:-✖️} ${RD}${msg}${CL}"
|
||||
}
|
||||
|
||||
msg_warn() {
|
||||
stop_spinner
|
||||
local msg="$1"
|
||||
echo -e "${BFR:-} ${INFO:-ℹ️} ${YWB}${msg}${CL}"
|
||||
}
|
||||
|
||||
msg_custom() {
|
||||
local symbol="${1:-"[*]"}"
|
||||
local color="${2:-"\e[36m"}"
|
||||
local msg="${3:-}"
|
||||
[[ -z "$msg" ]] && return
|
||||
stop_spinner
|
||||
echo -e "${BFR:-} ${symbol} ${color}${msg}${CL:-\e[0m}"
|
||||
}
|
||||
|
||||
run_container_safe() {
|
||||
local ct="$1"
|
||||
shift
|
||||
local cmd="$*"
|
||||
|
||||
lxc-attach -n "$ct" -- bash -euo pipefail -c "
|
||||
trap 'echo Aborted in container; exit 130' SIGINT SIGTERM
|
||||
$cmd
|
||||
" || __handle_general_error "lxc-attach to CT $ct"
|
||||
}
|
||||
|
||||
cleanup_lxc() {
|
||||
msg_info "Cleaning up"
|
||||
|
||||
if is_alpine; then
|
||||
$STD apk cache clean || true
|
||||
rm -rf /var/cache/apk/*
|
||||
else
|
||||
$STD apt -y autoremove || true
|
||||
$STD apt -y autoclean || true
|
||||
$STD apt -y clean || true
|
||||
fi
|
||||
|
||||
# Clear temp artifacts (keep sockets/FIFOs; ignore errors)
|
||||
find /tmp /var/tmp -type f -name 'tmp*' -delete 2>/dev/null || true
|
||||
find /tmp /var/tmp -type f -name 'tempfile*' -delete 2>/dev/null || true
|
||||
|
||||
# Truncate writable log files silently (permission errors ignored)
|
||||
if command -v truncate >/dev/null 2>&1; then
|
||||
find /var/log -type f -writable -print0 2>/dev/null |
|
||||
xargs -0 -n1 truncate -s 0 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Node.js npm
|
||||
if command -v npm &>/dev/null; then $STD npm cache clean --force || true; fi
|
||||
# Node.js yarn
|
||||
if command -v yarn &>/dev/null; then $STD yarn cache clean || true; fi
|
||||
# Node.js pnpm
|
||||
if command -v pnpm &>/dev/null; then $STD pnpm store prune || true; fi
|
||||
# Go
|
||||
if command -v go &>/dev/null; then $STD go clean -cache -modcache || true; fi
|
||||
# Rust cargo
|
||||
if command -v cargo &>/dev/null; then $STD cargo clean || true; fi
|
||||
# Ruby gem
|
||||
if command -v gem &>/dev/null; then $STD gem cleanup || true; fi
|
||||
# Composer (PHP)
|
||||
if command -v composer &>/dev/null; then $STD composer clear-cache || true; fi
|
||||
|
||||
if command -v journalctl &>/dev/null; then
|
||||
$STD journalctl --vacuum-time=10m || true
|
||||
fi
|
||||
msg_ok "Cleaned"
|
||||
}
|
||||
|
||||
check_or_create_swap() {
|
||||
msg_info "Checking for active swap"
|
||||
|
||||
if swapon --noheadings --show | grep -q 'swap'; then
|
||||
msg_ok "Swap is active"
|
||||
return 0
|
||||
fi
|
||||
|
||||
msg_error "No active swap detected"
|
||||
|
||||
read -p "Do you want to create a swap file? [y/N]: " create_swap
|
||||
create_swap="${create_swap,,}" # to lowercase
|
||||
|
||||
if [[ "$create_swap" != "y" && "$create_swap" != "yes" ]]; then
|
||||
msg_info "Skipping swap file creation"
|
||||
return 1
|
||||
fi
|
||||
|
||||
read -p "Enter swap size in MB (e.g., 2048 for 2GB): " swap_size_mb
|
||||
if ! [[ "$swap_size_mb" =~ ^[0-9]+$ ]]; then
|
||||
msg_error "Invalid size input. Aborting."
|
||||
return 1
|
||||
fi
|
||||
|
||||
local swap_file="/swapfile"
|
||||
|
||||
msg_info "Creating ${swap_size_mb}MB swap file at $swap_file"
|
||||
if dd if=/dev/zero of="$swap_file" bs=1M count="$swap_size_mb" status=progress &&
|
||||
chmod 600 "$swap_file" &&
|
||||
mkswap "$swap_file" &&
|
||||
swapon "$swap_file"; then
|
||||
msg_ok "Swap file created and activated successfully"
|
||||
else
|
||||
msg_error "Failed to create or activate swap"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
trap 'stop_spinner' EXIT INT TERM
|
||||
385
misc/old_misc/create_lxc.sh
Normal file
385
misc/old_misc/create_lxc.sh
Normal file
@ -0,0 +1,385 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright (c) 2021-2025 tteck
|
||||
# Author: tteck (tteckster)
|
||||
# Co-Author: MickLesk
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
|
||||
# This sets verbose mode if the global variable is set to "yes"
|
||||
# if [ "$VERBOSE" == "yes" ]; then set -x; fi
|
||||
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func)
|
||||
load_functions
|
||||
#echo "(create-lxc.sh) Loaded core.func via curl"
|
||||
elif command -v wget >/dev/null 2>&1; then
|
||||
source <(wget -qO- https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func)
|
||||
load_functions
|
||||
#echo "(create-lxc.sh) Loaded core.func via wget"
|
||||
fi
|
||||
|
||||
# This sets error handling options and defines the error_handler function to handle errors
|
||||
set -Eeuo pipefail
|
||||
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
|
||||
trap on_exit EXIT
|
||||
trap on_interrupt INT
|
||||
trap on_terminate TERM
|
||||
|
||||
function on_exit() {
|
||||
local exit_code="$?"
|
||||
[[ -n "${lockfile:-}" && -e "$lockfile" ]] && rm -f "$lockfile"
|
||||
exit "$exit_code"
|
||||
}
|
||||
|
||||
function error_handler() {
|
||||
local exit_code="$?"
|
||||
local line_number="$1"
|
||||
local command="$2"
|
||||
printf "\e[?25h"
|
||||
echo -e "\n${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}\n"
|
||||
exit "$exit_code"
|
||||
}
|
||||
|
||||
function on_interrupt() {
|
||||
echo -e "\n${RD}Interrupted by user (SIGINT)${CL}"
|
||||
exit 130
|
||||
}
|
||||
|
||||
function on_terminate() {
|
||||
echo -e "\n${RD}Terminated by signal (SIGTERM)${CL}"
|
||||
exit 143
|
||||
}
|
||||
|
||||
function exit_script() {
|
||||
clear
|
||||
printf "\e[?25h"
|
||||
echo -e "\n${CROSS}${RD}User exited script${CL}\n"
|
||||
kill 0
|
||||
exit 1
|
||||
}
|
||||
|
||||
function check_storage_support() {
|
||||
local CONTENT="$1"
|
||||
local -a VALID_STORAGES=()
|
||||
while IFS= read -r line; do
|
||||
local STORAGE_NAME
|
||||
STORAGE_NAME=$(awk '{print $1}' <<<"$line")
|
||||
[[ -z "$STORAGE_NAME" ]] && continue
|
||||
VALID_STORAGES+=("$STORAGE_NAME")
|
||||
done < <(pvesm status -content "$CONTENT" 2>/dev/null | awk 'NR>1')
|
||||
|
||||
[[ ${#VALID_STORAGES[@]} -gt 0 ]]
|
||||
}
|
||||
|
||||
# This function selects a storage pool for a given content type (e.g., rootdir, vztmpl).
|
||||
function select_storage() {
|
||||
local CLASS=$1 CONTENT CONTENT_LABEL
|
||||
|
||||
case $CLASS in
|
||||
container)
|
||||
CONTENT='rootdir'
|
||||
CONTENT_LABEL='Container'
|
||||
;;
|
||||
template)
|
||||
CONTENT='vztmpl'
|
||||
CONTENT_LABEL='Container template'
|
||||
;;
|
||||
iso)
|
||||
CONTENT='iso'
|
||||
CONTENT_LABEL='ISO image'
|
||||
;;
|
||||
images)
|
||||
CONTENT='images'
|
||||
CONTENT_LABEL='VM Disk image'
|
||||
;;
|
||||
backup)
|
||||
CONTENT='backup'
|
||||
CONTENT_LABEL='Backup'
|
||||
;;
|
||||
snippets)
|
||||
CONTENT='snippets'
|
||||
CONTENT_LABEL='Snippets'
|
||||
;;
|
||||
*)
|
||||
msg_error "Invalid storage class '$CLASS'"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Check for preset STORAGE variable
|
||||
if [ "$CONTENT" = "rootdir" ] && [ -n "${STORAGE:-}" ]; then
|
||||
if pvesm status -content "$CONTENT" | awk 'NR>1 {print $1}' | grep -qx "$STORAGE"; then
|
||||
STORAGE_RESULT="$STORAGE"
|
||||
msg_info "Using preset storage: $STORAGE_RESULT for $CONTENT_LABEL"
|
||||
return 0
|
||||
else
|
||||
msg_error "Preset storage '$STORAGE' is not valid for content type '$CONTENT'."
|
||||
return 2
|
||||
fi
|
||||
fi
|
||||
|
||||
local -A STORAGE_MAP
|
||||
local -a MENU
|
||||
local COL_WIDTH=0
|
||||
|
||||
while read -r TAG TYPE _ TOTAL USED FREE _; do
|
||||
[[ -n "$TAG" && -n "$TYPE" ]] || continue
|
||||
local STORAGE_NAME="$TAG"
|
||||
local DISPLAY="${STORAGE_NAME} (${TYPE})"
|
||||
local USED_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$USED")
|
||||
local FREE_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$FREE")
|
||||
local INFO="Free: ${FREE_FMT}B Used: ${USED_FMT}B"
|
||||
STORAGE_MAP["$DISPLAY"]="$STORAGE_NAME"
|
||||
MENU+=("$DISPLAY" "$INFO" "OFF")
|
||||
((${#DISPLAY} > COL_WIDTH)) && COL_WIDTH=${#DISPLAY}
|
||||
done < <(pvesm status -content "$CONTENT" | awk 'NR>1')
|
||||
|
||||
if [ ${#MENU[@]} -eq 0 ]; then
|
||||
msg_error "No storage found for content type '$CONTENT'."
|
||||
return 2
|
||||
fi
|
||||
|
||||
if [ $((${#MENU[@]} / 3)) -eq 1 ]; then
|
||||
STORAGE_RESULT="${STORAGE_MAP[${MENU[0]}]}"
|
||||
STORAGE_INFO="${MENU[1]}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local WIDTH=$((COL_WIDTH + 42))
|
||||
while true; do
|
||||
local DISPLAY_SELECTED
|
||||
DISPLAY_SELECTED=$(whiptail --backtitle "Proxmox VE Helper Scripts" \
|
||||
--title "Storage Pools" \
|
||||
--radiolist "Which storage pool for ${CONTENT_LABEL,,}?\n(Spacebar to select)" \
|
||||
16 "$WIDTH" 6 "${MENU[@]}" 3>&1 1>&2 2>&3)
|
||||
|
||||
# Cancel or ESC
|
||||
[[ $? -ne 0 ]] && exit_script
|
||||
|
||||
# Strip trailing whitespace or newline (important for storages like "storage (dir)")
|
||||
DISPLAY_SELECTED=$(sed 's/[[:space:]]*$//' <<<"$DISPLAY_SELECTED")
|
||||
|
||||
if [[ -z "$DISPLAY_SELECTED" || -z "${STORAGE_MAP[$DISPLAY_SELECTED]+_}" ]]; then
|
||||
whiptail --msgbox "No valid storage selected. Please try again." 8 58
|
||||
continue
|
||||
fi
|
||||
|
||||
STORAGE_RESULT="${STORAGE_MAP[$DISPLAY_SELECTED]}"
|
||||
for ((i = 0; i < ${#MENU[@]}; i += 3)); do
|
||||
if [[ "${MENU[$i]}" == "$DISPLAY_SELECTED" ]]; then
|
||||
STORAGE_INFO="${MENU[$i + 1]}"
|
||||
break
|
||||
fi
|
||||
done
|
||||
return 0
|
||||
done
|
||||
}
|
||||
|
||||
# Test if required variables are set
|
||||
[[ "${CTID:-}" ]] || {
|
||||
msg_error "You need to set 'CTID' variable."
|
||||
exit 203
|
||||
}
|
||||
[[ "${PCT_OSTYPE:-}" ]] || {
|
||||
msg_error "You need to set 'PCT_OSTYPE' variable."
|
||||
exit 204
|
||||
}
|
||||
|
||||
# Test if ID is valid
|
||||
[ "$CTID" -ge "100" ] || {
|
||||
msg_error "ID cannot be less than 100."
|
||||
exit 205
|
||||
}
|
||||
|
||||
# Test if ID is in use
|
||||
if qm status "$CTID" &>/dev/null || pct status "$CTID" &>/dev/null; then
|
||||
echo -e "ID '$CTID' is already in use."
|
||||
unset CTID
|
||||
msg_error "Cannot use ID that is already in use."
|
||||
exit 206
|
||||
fi
|
||||
|
||||
# This checks for the presence of valid Container Storage and Template Storage locations
|
||||
if ! check_storage_support "rootdir"; then
|
||||
msg_error "No valid storage found for 'rootdir' [Container]"
|
||||
exit 1
|
||||
fi
|
||||
if ! check_storage_support "vztmpl"; then
|
||||
msg_error "No valid storage found for 'vztmpl' [Template]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
while true; do
|
||||
if select_storage template; then
|
||||
TEMPLATE_STORAGE="$STORAGE_RESULT"
|
||||
TEMPLATE_STORAGE_INFO="$STORAGE_INFO"
|
||||
msg_ok "Storage ${BL}$TEMPLATE_STORAGE${CL} ($TEMPLATE_STORAGE_INFO) [Template]"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
while true; do
|
||||
if select_storage container; then
|
||||
CONTAINER_STORAGE="$STORAGE_RESULT"
|
||||
CONTAINER_STORAGE_INFO="$STORAGE_INFO"
|
||||
msg_ok "Storage ${BL}$CONTAINER_STORAGE${CL} ($CONTAINER_STORAGE_INFO) [Container]"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# Check free space on selected container storage
|
||||
STORAGE_FREE=$(pvesm status | awk -v s="$CONTAINER_STORAGE" '$1 == s { print $6 }')
|
||||
REQUIRED_KB=$((${PCT_DISK_SIZE:-8} * 1024 * 1024))
|
||||
if [ "$STORAGE_FREE" -lt "$REQUIRED_KB" ]; then
|
||||
msg_error "Not enough space on '$CONTAINER_STORAGE'. Needed: ${PCT_DISK_SIZE:-8}G."
|
||||
exit 214
|
||||
fi
|
||||
|
||||
# Check Cluster Quorum if in Cluster
|
||||
if [ -f /etc/pve/corosync.conf ]; then
|
||||
msg_info "Checking cluster quorum"
|
||||
if ! pvecm status | awk -F':' '/^Quorate/ { exit ($2 ~ /Yes/) ? 0 : 1 }'; then
|
||||
|
||||
msg_error "Cluster is not quorate. Start all nodes or configure quorum device (QDevice)."
|
||||
exit 210
|
||||
fi
|
||||
msg_ok "Cluster is quorate"
|
||||
fi
|
||||
|
||||
# Update LXC template list
|
||||
TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}"
|
||||
case "$PCT_OSTYPE" in
|
||||
debian | ubuntu)
|
||||
TEMPLATE_PATTERN="-standard_"
|
||||
;;
|
||||
alpine | fedora | rocky | centos)
|
||||
TEMPLATE_PATTERN="-default_"
|
||||
;;
|
||||
*)
|
||||
TEMPLATE_PATTERN=""
|
||||
;;
|
||||
esac
|
||||
|
||||
# 1. Check local templates first
|
||||
msg_info "Searching for template '$TEMPLATE_SEARCH'"
|
||||
mapfile -t TEMPLATES < <(
|
||||
pveam list "$TEMPLATE_STORAGE" |
|
||||
awk -v s="$TEMPLATE_SEARCH" -v p="$TEMPLATE_PATTERN" '$1 ~ s && $1 ~ p {print $1}' |
|
||||
sed 's/.*\///' | sort -t - -k 2 -V
|
||||
)
|
||||
|
||||
if [ ${#TEMPLATES[@]} -gt 0 ]; then
|
||||
TEMPLATE_SOURCE="local"
|
||||
else
|
||||
msg_info "No local template found, checking online repository"
|
||||
pveam update >/dev/null 2>&1
|
||||
mapfile -t TEMPLATES < <(
|
||||
pveam update >/dev/null 2>&1 &&
|
||||
pveam available -section system |
|
||||
sed -n "s/.*\($TEMPLATE_SEARCH.*$TEMPLATE_PATTERN.*\)/\1/p" |
|
||||
sort -t - -k 2 -V
|
||||
)
|
||||
TEMPLATE_SOURCE="online"
|
||||
fi
|
||||
|
||||
TEMPLATE="${TEMPLATES[-1]}"
|
||||
TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null ||
|
||||
echo "/var/lib/vz/template/cache/$TEMPLATE")"
|
||||
msg_ok "Template ${BL}$TEMPLATE${CL} [$TEMPLATE_SOURCE]"
|
||||
|
||||
# 4. Validate template (exists & not corrupted)
|
||||
TEMPLATE_VALID=1
|
||||
|
||||
if [ ! -s "$TEMPLATE_PATH" ]; then
|
||||
TEMPLATE_VALID=0
|
||||
elif ! tar --use-compress-program=zstdcat -tf "$TEMPLATE_PATH" >/dev/null 2>&1; then
|
||||
TEMPLATE_VALID=0
|
||||
fi
|
||||
|
||||
if [ "$TEMPLATE_VALID" -eq 0 ]; then
|
||||
msg_warn "Template $TEMPLATE is missing or corrupted. Re-downloading."
|
||||
[[ -f "$TEMPLATE_PATH" ]] && rm -f "$TEMPLATE_PATH"
|
||||
for attempt in {1..3}; do
|
||||
msg_info "Attempt $attempt: Downloading LXC template..."
|
||||
if pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null 2>&1; then
|
||||
msg_ok "Template download successful."
|
||||
break
|
||||
fi
|
||||
if [ $attempt -eq 3 ]; then
|
||||
msg_error "Failed after 3 attempts. Please check network access or manually run:\n pveam download $TEMPLATE_STORAGE $TEMPLATE"
|
||||
exit 208
|
||||
fi
|
||||
sleep $((attempt * 5))
|
||||
done
|
||||
fi
|
||||
|
||||
msg_info "Creating LXC Container"
|
||||
# Check and fix subuid/subgid
|
||||
grep -q "root:100000:65536" /etc/subuid || echo "root:100000:65536" >>/etc/subuid
|
||||
grep -q "root:100000:65536" /etc/subgid || echo "root:100000:65536" >>/etc/subgid
|
||||
|
||||
# Combine all options
|
||||
PCT_OPTIONS=(${PCT_OPTIONS[@]:-${DEFAULT_PCT_OPTIONS[@]}})
|
||||
[[ " ${PCT_OPTIONS[@]} " =~ " -rootfs " ]] || PCT_OPTIONS+=(-rootfs "$CONTAINER_STORAGE:${PCT_DISK_SIZE:-8}")
|
||||
|
||||
# Secure creation of the LXC container with lock and template check
|
||||
lockfile="/tmp/template.${TEMPLATE}.lock"
|
||||
exec 9>"$lockfile" || {
|
||||
msg_error "Failed to create lock file '$lockfile'."
|
||||
exit 200
|
||||
}
|
||||
flock -w 60 9 || {
|
||||
msg_error "Timeout while waiting for template lock"
|
||||
exit 211
|
||||
}
|
||||
|
||||
if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" &>/dev/null; then
|
||||
msg_error "Container creation failed. Checking if template is corrupted or incomplete."
|
||||
|
||||
if [[ ! -s "$TEMPLATE_PATH" || "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then
|
||||
msg_error "Template file too small or missing – re-downloading."
|
||||
rm -f "$TEMPLATE_PATH"
|
||||
elif ! zstdcat "$TEMPLATE_PATH" | tar -tf - &>/dev/null; then
|
||||
msg_error "Template appears to be corrupted – re-downloading."
|
||||
rm -f "$TEMPLATE_PATH"
|
||||
else
|
||||
msg_error "Template is valid, but container creation failed. Update your whole Proxmox System (pve-container) first or check https://github.com/community-scripts/ProxmoxVE/discussions/8126"
|
||||
exit 209
|
||||
fi
|
||||
|
||||
# Retry download
|
||||
for attempt in {1..3}; do
|
||||
msg_info "Attempt $attempt: Re-downloading template..."
|
||||
if timeout 120 pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null; then
|
||||
msg_ok "Template re-download successful."
|
||||
break
|
||||
fi
|
||||
if [ "$attempt" -eq 3 ]; then
|
||||
msg_error "Three failed attempts. Aborting."
|
||||
exit 208
|
||||
fi
|
||||
sleep $((attempt * 5))
|
||||
done
|
||||
|
||||
sleep 1 # I/O-Sync-Delay
|
||||
msg_ok "Re-downloaded LXC Template"
|
||||
fi
|
||||
|
||||
if ! pct list | awk '{print $1}' | grep -qx "$CTID"; then
|
||||
msg_error "Container ID $CTID not listed in 'pct list' – unexpected failure."
|
||||
exit 215
|
||||
fi
|
||||
|
||||
if ! grep -q '^rootfs:' "/etc/pve/lxc/$CTID.conf"; then
|
||||
msg_error "RootFS entry missing in container config – storage not correctly assigned."
|
||||
exit 216
|
||||
fi
|
||||
|
||||
if grep -q '^hostname:' "/etc/pve/lxc/$CTID.conf"; then
|
||||
CT_HOSTNAME=$(grep '^hostname:' "/etc/pve/lxc/$CTID.conf" | awk '{print $2}')
|
||||
if [[ ! "$CT_HOSTNAME" =~ ^[a-z0-9-]+$ ]]; then
|
||||
msg_warn "Hostname '$CT_HOSTNAME' contains invalid characters – may cause issues with networking or DNS."
|
||||
fi
|
||||
fi
|
||||
|
||||
msg_ok "LXC Container ${BL}$CTID${CL} ${GN}was successfully created."
|
||||
217
misc/old_misc/install.func
Normal file
217
misc/old_misc/install.func
Normal file
@ -0,0 +1,217 @@
|
||||
# Copyright (c) 2021-2025 tteck
|
||||
# Author: tteck (tteckster)
|
||||
# Co-Author: MickLesk
|
||||
# License: MIT
|
||||
# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
|
||||
if ! command -v curl >/dev/null 2>&1; then
|
||||
printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2
|
||||
apt-get update >/dev/null 2>&1
|
||||
apt-get install -y curl >/dev/null 2>&1
|
||||
fi
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func)
|
||||
load_functions
|
||||
# This function enables IPv6 if it's not disabled and sets verbose mode
|
||||
verb_ip6() {
|
||||
set_std_mode # Set STD mode based on VERBOSE
|
||||
|
||||
if [ "$IPV6_METHOD" == "disable" ]; then
|
||||
msg_info "Disabling IPv6 (this may affect some services)"
|
||||
mkdir -p /etc/sysctl.d
|
||||
$STD tee /etc/sysctl.d/99-disable-ipv6.conf >/dev/null <<EOF
|
||||
# Disable IPv6 (set by community-scripts)
|
||||
net.ipv6.conf.all.disable_ipv6 = 1
|
||||
net.ipv6.conf.default.disable_ipv6 = 1
|
||||
net.ipv6.conf.lo.disable_ipv6 = 1
|
||||
EOF
|
||||
$STD sysctl -p /etc/sysctl.d/99-disable-ipv6.conf
|
||||
msg_ok "Disabled IPv6"
|
||||
fi
|
||||
}
|
||||
|
||||
# This function sets error handling options and defines the error_handler function to handle errors
|
||||
catch_errors() {
|
||||
set -Eeuo pipefail
|
||||
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
|
||||
}
|
||||
|
||||
# This function handles errors
|
||||
error_handler() {
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func)
|
||||
printf "\e[?25h"
|
||||
local exit_code="$?"
|
||||
local line_number="$1"
|
||||
local command="$2"
|
||||
local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}"
|
||||
echo -e "\n$error_message"
|
||||
if [[ "$line_number" -eq 51 ]]; then
|
||||
echo -e "The silent function has suppressed the error, run the script with verbose mode enabled, which will provide more detailed output.\n"
|
||||
post_update_to_api "failed" "No error message, script ran in silent mode"
|
||||
else
|
||||
post_update_to_api "failed" "${command}"
|
||||
fi
|
||||
}
|
||||
|
||||
# This function sets up the Container OS by generating the locale, setting the timezone, and checking the network connection
|
||||
setting_up_container() {
|
||||
msg_info "Setting up Container OS"
|
||||
for ((i = RETRY_NUM; i > 0; i--)); do
|
||||
if [ "$(hostname -I)" != "" ]; then
|
||||
break
|
||||
fi
|
||||
echo 1>&2 -en "${CROSS}${RD} No Network! "
|
||||
sleep $RETRY_EVERY
|
||||
done
|
||||
if [ "$(hostname -I)" = "" ]; then
|
||||
echo 1>&2 -e "\n${CROSS}${RD} No Network After $RETRY_NUM Tries${CL}"
|
||||
echo -e "${NETWORK}Check Network Settings"
|
||||
exit 1
|
||||
fi
|
||||
rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED
|
||||
systemctl disable -q --now systemd-networkd-wait-online.service
|
||||
msg_ok "Set up Container OS"
|
||||
#msg_custom "${CM}" "${GN}" "Network Connected: ${BL}$(hostname -I)"
|
||||
msg_ok "Network Connected: ${BL}$(hostname -I)"
|
||||
}
|
||||
|
||||
# This function checks the network connection by pinging a known IP address and prompts the user to continue if the internet is not connected
|
||||
# This function checks the network connection by pinging a known IP address and prompts the user to continue if the internet is not connected
|
||||
network_check() {
|
||||
set +e
|
||||
trap - ERR
|
||||
ipv4_connected=false
|
||||
ipv6_connected=false
|
||||
sleep 1
|
||||
|
||||
# Check IPv4 connectivity to Google, Cloudflare & Quad9 DNS servers.
|
||||
if ping -c 1 -W 1 1.1.1.1 &>/dev/null || ping -c 1 -W 1 8.8.8.8 &>/dev/null || ping -c 1 -W 1 9.9.9.9 &>/dev/null; then
|
||||
msg_ok "IPv4 Internet Connected"
|
||||
ipv4_connected=true
|
||||
else
|
||||
msg_error "IPv4 Internet Not Connected"
|
||||
fi
|
||||
|
||||
# Check IPv6 connectivity to Google, Cloudflare & Quad9 DNS servers.
|
||||
if ping6 -c 1 -W 1 2606:4700:4700::1111 &>/dev/null || ping6 -c 1 -W 1 2001:4860:4860::8888 &>/dev/null || ping6 -c 1 -W 1 2620:fe::fe &>/dev/null; then
|
||||
msg_ok "IPv6 Internet Connected"
|
||||
ipv6_connected=true
|
||||
else
|
||||
msg_error "IPv6 Internet Not Connected"
|
||||
fi
|
||||
|
||||
# If both IPv4 and IPv6 checks fail, prompt the user
|
||||
if [[ $ipv4_connected == false && $ipv6_connected == false ]]; then
|
||||
read -r -p "No Internet detected, would you like to continue anyway? <y/N> " prompt
|
||||
if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then
|
||||
echo -e "${INFO}${RD}Expect Issues Without Internet${CL}"
|
||||
else
|
||||
echo -e "${NETWORK}Check Network Settings"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# DNS resolution checks for GitHub-related domains (IPv4 and/or IPv6)
|
||||
GIT_HOSTS=("github.com" "raw.githubusercontent.com" "api.github.com" "git.community-scripts.org")
|
||||
GIT_STATUS="Git DNS:"
|
||||
DNS_FAILED=false
|
||||
|
||||
for HOST in "${GIT_HOSTS[@]}"; do
|
||||
RESOLVEDIP=$(getent hosts "$HOST" | awk '{ print $1 }' | grep -E '(^([0-9]{1,3}\.){3}[0-9]{1,3}$)|(^[a-fA-F0-9:]+$)' | head -n1)
|
||||
if [[ -z "$RESOLVEDIP" ]]; then
|
||||
GIT_STATUS+="$HOST:($DNSFAIL)"
|
||||
DNS_FAILED=true
|
||||
else
|
||||
GIT_STATUS+=" $HOST:($DNSOK)"
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ "$DNS_FAILED" == true ]]; then
|
||||
fatal "$GIT_STATUS"
|
||||
else
|
||||
msg_ok "$GIT_STATUS"
|
||||
fi
|
||||
|
||||
set -e
|
||||
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
|
||||
}
|
||||
|
||||
# This function updates the Container OS by running apt-get update and upgrade
|
||||
update_os() {
|
||||
msg_info "Updating Container OS"
|
||||
if [[ "$CACHER" == "yes" ]]; then
|
||||
echo 'Acquire::http::Proxy-Auto-Detect "/usr/local/bin/apt-proxy-detect.sh";' >/etc/apt/apt.conf.d/00aptproxy
|
||||
cat <<EOF >/usr/local/bin/apt-proxy-detect.sh
|
||||
#!/bin/bash
|
||||
if nc -w1 -z "${CACHER_IP}" 3142; then
|
||||
echo -n "http://${CACHER_IP}:3142"
|
||||
else
|
||||
echo -n "DIRECT"
|
||||
fi
|
||||
EOF
|
||||
chmod +x /usr/local/bin/apt-proxy-detect.sh
|
||||
fi
|
||||
$STD apt-get update
|
||||
$STD apt-get -o Dpkg::Options::="--force-confold" -y dist-upgrade
|
||||
rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED
|
||||
msg_ok "Updated Container OS"
|
||||
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func)
|
||||
}
|
||||
|
||||
# This function modifies the message of the day (motd) and SSH settings
|
||||
motd_ssh() {
|
||||
# Set terminal to 256-color mode
|
||||
grep -qxF "export TERM='xterm-256color'" /root/.bashrc || echo "export TERM='xterm-256color'" >>/root/.bashrc
|
||||
|
||||
# Get OS information (Debian / Ubuntu)
|
||||
if [ -f "/etc/os-release" ]; then
|
||||
OS_NAME=$(grep ^NAME /etc/os-release | cut -d= -f2 | tr -d '"')
|
||||
OS_VERSION=$(grep ^VERSION_ID /etc/os-release | cut -d= -f2 | tr -d '"')
|
||||
elif [ -f "/etc/debian_version" ]; then
|
||||
OS_NAME="Debian"
|
||||
OS_VERSION=$(cat /etc/debian_version)
|
||||
fi
|
||||
|
||||
PROFILE_FILE="/etc/profile.d/00_lxc-details.sh"
|
||||
echo "echo -e \"\"" >"$PROFILE_FILE"
|
||||
echo -e "echo -e \"${BOLD}${APPLICATION} LXC Container${CL}"\" >>"$PROFILE_FILE"
|
||||
echo -e "echo -e \"${TAB}${GATEWAY}${YW} Provided by: ${GN}community-scripts ORG ${YW}| GitHub: ${GN}https://github.com/community-scripts/ProxmoxVE${CL}\"" >>"$PROFILE_FILE"
|
||||
echo "echo \"\"" >>"$PROFILE_FILE"
|
||||
echo -e "echo -e \"${TAB}${OS}${YW} OS: ${GN}${OS_NAME} - Version: ${OS_VERSION}${CL}\"" >>"$PROFILE_FILE"
|
||||
echo -e "echo -e \"${TAB}${HOSTNAME}${YW} Hostname: ${GN}\$(hostname)${CL}\"" >>"$PROFILE_FILE"
|
||||
echo -e "echo -e \"${TAB}${INFO}${YW} IP Address: ${GN}\$(hostname -I | awk '{print \$1}')${CL}\"" >>"$PROFILE_FILE"
|
||||
|
||||
# Disable default MOTD scripts
|
||||
chmod -x /etc/update-motd.d/*
|
||||
|
||||
if [[ "${SSH_ROOT}" == "yes" ]]; then
|
||||
sed -i "s/#PermitRootLogin prohibit-password/PermitRootLogin yes/g" /etc/ssh/sshd_config
|
||||
systemctl restart sshd
|
||||
fi
|
||||
}
|
||||
|
||||
# This function customizes the container by modifying the getty service and enabling auto-login for the root user
|
||||
customize() {
|
||||
if [[ "$PASSWORD" == "" ]]; then
|
||||
msg_info "Customizing Container"
|
||||
GETTY_OVERRIDE="/etc/systemd/system/container-getty@1.service.d/override.conf"
|
||||
mkdir -p $(dirname $GETTY_OVERRIDE)
|
||||
cat <<EOF >$GETTY_OVERRIDE
|
||||
[Service]
|
||||
ExecStart=
|
||||
ExecStart=-/sbin/agetty --autologin root --noclear --keep-baud tty%I 115200,38400,9600 \$TERM
|
||||
EOF
|
||||
systemctl daemon-reload
|
||||
systemctl restart $(basename $(dirname $GETTY_OVERRIDE) | sed 's/\.d//')
|
||||
msg_ok "Customized Container"
|
||||
fi
|
||||
echo "bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/${app}.sh)\"" >/usr/bin/update
|
||||
chmod +x /usr/bin/update
|
||||
|
||||
if [[ -n "${SSH_AUTHORIZED_KEY}" ]]; then
|
||||
mkdir -p /root/.ssh
|
||||
echo "${SSH_AUTHORIZED_KEY}" >/root/.ssh/authorized_keys
|
||||
chmod 700 /root/.ssh
|
||||
chmod 600 /root/.ssh/authorized_keys
|
||||
fi
|
||||
}
|
||||
4818
misc/old_misc/tools.func
Normal file
4818
misc/old_misc/tools.func
Normal file
File diff suppressed because it is too large
Load Diff
1283
misc/tools.func.md
Normal file
1283
misc/tools.func.md
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user