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