Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions cmd/prefilter-manual/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package main

import (
"context"
"flag"
"os"
"path/filepath"
"strings"

"github.com/tis24dev/proxsave/internal/backup"
"github.com/tis24dev/proxsave/internal/logging"
"github.com/tis24dev/proxsave/internal/types"
)

func parseLogLevel(raw string) types.LogLevel {
switch strings.ToLower(strings.TrimSpace(raw)) {
case "debug":
return types.LogLevelDebug
case "info", "":
return types.LogLevelInfo
case "warning", "warn":
return types.LogLevelWarning
case "error":
return types.LogLevelError
default:
return types.LogLevelInfo
}
}

func main() {
var (
root string
maxSize int64
levelLabel string
)

flag.StringVar(&root, "root", "/tmp/test_prefilter", "Root directory to run prefilter on")
flag.Int64Var(&maxSize, "max-size", 8*1024*1024, "Max file size (bytes) to prefilter")
flag.StringVar(&levelLabel, "log-level", "info", "Log level: debug|info|warn|error")
flag.Parse()

root = filepath.Clean(strings.TrimSpace(root))
if root == "" || root == "." {
root = string(os.PathSeparator)
}

logger := logging.New(parseLogLevel(levelLabel), false)
logger.SetOutput(os.Stdout)

cfg := backup.OptimizationConfig{
EnablePrefilter: true,
PrefilterMaxFileSizeBytes: maxSize,
}

if err := backup.ApplyOptimizations(context.Background(), logger, root, cfg); err != nil {
logger.Error("Prefilter failed: %v", err)
os.Exit(1)
}
}
3 changes: 3 additions & 0 deletions docs/BACKUP_ENV_MAPPING.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,15 @@ WEBHOOK_TIMEOUT = SAME
## Go-only variables (new)

SYSTEM_ROOT_PREFIX = NEW (Go-only) → Override system root for collection (testing/chroot). Empty or "/" uses the real root.
NOTE: PBS restore behavior is selected interactively during `--restore` and is intentionally not configured via `backup.env`.
BACKUP_PBS_S3_ENDPOINTS = NEW (Go-only) → Collect `s3.cfg` and S3 endpoint snapshots (PBS).
BACKUP_PBS_NODE_CONFIG = NEW (Go-only) → Collect `node.cfg` and node snapshots (PBS).
BACKUP_PBS_ACME_ACCOUNTS = NEW (Go-only) → Collect `acme/accounts.cfg` and ACME account snapshots (PBS).
BACKUP_PBS_ACME_PLUGINS = NEW (Go-only) → Collect `acme/plugins.cfg` and ACME plugin snapshots (PBS).
BACKUP_PBS_METRIC_SERVERS = NEW (Go-only) → Collect `metricserver.cfg` (PBS).
BACKUP_PBS_TRAFFIC_CONTROL = NEW (Go-only) → Collect `traffic-control.cfg` and traffic-control snapshots (PBS).
BACKUP_PBS_NOTIFICATIONS = NEW (Go-only) → Collect `notifications.cfg` and notification snapshots (PBS).
BACKUP_PBS_NOTIFICATIONS_PRIV = NEW (Go-only) → Collect `notifications-priv.cfg` (PBS notification secrets/credentials).
BACKUP_PBS_NETWORK_CONFIG = NEW (Go-only) → Collect `network.cfg` and network snapshots (PBS), independent from BACKUP_NETWORK_CONFIGS (system).

## Renamed variables / Supported aliases in Go
Expand Down
3 changes: 3 additions & 0 deletions docs/CLI_REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,9 @@ CONFIG_FILE=/etc/pbs/prod.env ./build/proxsave
# Force dry-run mode
DRY_RUN=true ./build/proxsave

# PBS restore behavior
# Selected interactively during `--restore` on PBS hosts (Merge vs Clean 1:1).

# Set debug level
DEBUG_LEVEL=extreme ./build/proxsave --log-level debug

Expand Down
65 changes: 64 additions & 1 deletion docs/CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Complete reference for all 200+ configuration variables in `configs/backup.env`.

- [Configuration File Location](#configuration-file-location)
- [General Settings](#general-settings)
- [Restore (PBS)](#restore-pbs)
- [Security Settings](#security-settings)
- [Disk Space](#disk-space)
- [Storage Paths](#storage-paths)
Expand Down Expand Up @@ -74,6 +75,25 @@ PROFILING_ENABLED=true # true | false (profiles written under LOG_PA

---

## Restore (PBS)

PBS restore behavior is chosen **interactively at restore time** on PBS hosts (not via `backup.env`).

You will be asked to choose a behavior:
- **Merge (existing PBS)**: intended for restoring onto an already operational PBS; ProxSave applies supported PBS categories via `proxmox-backup-manager` without deleting existing objects that are not in the backup.
- **Clean 1:1 (fresh PBS install)**: intended for restoring onto a new, clean PBS; ProxSave attempts to make supported PBS objects match the backup (may remove objects that exist on the system but are not in the backup).

ProxSave applies supported PBS staged categories via API automatically (and may fall back to file-based staged apply only in **Clean 1:1** mode).

**Current API coverage**:
- Node + traffic control (`pbs_host`)
- Datastores + S3 endpoints (`datastore_pbs`)
- Remotes (`pbs_remotes`)
- Jobs (sync/verify/prune) (`pbs_jobs`)
- Notifications endpoints/matchers (`pbs_notifications`)

---

## Security Settings

```bash
Expand Down Expand Up @@ -321,7 +341,29 @@ PREFILTER_MAX_FILE_SIZE_MB=8 # Skip prefilter for files >8MB

- **Smart chunking**: Splits large files for parallel processing
- **Deduplication**: Detects duplicate data blocks (reduces storage)
- **Prefilter**: Analyzes small files before compression (optimizes algorithm selection)
- **Prefilter**: Applies safe, semantic-preserving normalization to small text/JSON files to improve compression (e.g. removes CR from CRLF line endings and minifies JSON). It does **not** reorder, de-indent, or strip structured configuration files, and it avoids touching Proxmox/PBS structured config paths (e.g. `etc/pve/**`, `etc/proxmox-backup/**`).

### Prefilter (`ENABLE_PREFILTER`) — details and risks

**What it does** (on the *staged* backup tree, before compression):
- Removes `\r` from CRLF text files (`.txt`, `.log`, `.md`, `.conf`, `.cfg`, `.ini`) to normalize line endings
- Minifies JSON (`.json`) while keeping valid JSON semantics

**What it does not do**:
- It does **not** reorder lines, remove indentation, or otherwise rewrite whitespace/ordering-sensitive structured configs.
- It does **not** follow symlinks (symlinks are skipped).
- It skips Proxmox/PBS structured configuration paths where formatting/order matters, such as:
- `etc/pve/**`
- `etc/proxmox-backup/**`
- `etc/systemd/system/**`
- `etc/ssh/**`
- `etc/pam.d/**`

**Why you might disable it** (even though it's safe):
- If you need maximum fidelity (bit-for-bit) of text/JSON formatting as originally collected (CRLF preservation, JSON pretty-printing, etc.)
- If you prefer the most conservative pipeline possible (forensics/compliance)

**Important**: Prefilter never edits files on the host system — it only operates on the temporary staging directory that will be archived.

---

Expand Down Expand Up @@ -938,6 +980,24 @@ BACKUP_VM_CONFIGS=true # VM/CT config files
# PBS datastore configs
BACKUP_DATASTORE_CONFIGS=true # Datastore definitions

# S3 endpoints (used by S3 datastores)
BACKUP_PBS_S3_ENDPOINTS=true # s3.cfg (S3 endpoints, used by S3 datastores)

# Node/global config
BACKUP_PBS_NODE_CONFIG=true # node.cfg (global PBS settings)

# ACME
BACKUP_PBS_ACME_ACCOUNTS=true # acme/accounts.cfg
BACKUP_PBS_ACME_PLUGINS=true # acme/plugins.cfg

# Integrations
BACKUP_PBS_METRIC_SERVERS=true # metricserver.cfg
BACKUP_PBS_TRAFFIC_CONTROL=true # traffic-control.cfg

# Notifications
BACKUP_PBS_NOTIFICATIONS=true # notifications.cfg (targets/matchers/endpoints)
BACKUP_PBS_NOTIFICATIONS_PRIV=true # notifications-priv.cfg (secrets/credentials for endpoints)

# User and permissions
BACKUP_USER_CONFIGS=true # PBS users and tokens

Expand All @@ -953,6 +1013,9 @@ BACKUP_VERIFICATION_JOBS=true # Backup verification schedules
# Tape backup
BACKUP_TAPE_CONFIGS=true # Tape library configuration

# Network configuration (PBS)
BACKUP_PBS_NETWORK_CONFIG=true # network.cfg (PBS), independent from BACKUP_NETWORK_CONFIGS (system)

# Prune schedules
BACKUP_PRUNE_SCHEDULES=true # Retention prune schedules

Expand Down
2 changes: 2 additions & 0 deletions docs/RESTORE_DIAGRAMS.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ flowchart TD
style CheckboxMenu fill:#87CEEB
```

**Note (PBS)**: ProxSave applies supported PBS staged categories via `proxmox-backup-manager` by default. In **Clean 1:1** mode it may fall back to writing staged `*.cfg` files back to `/etc/proxmox-backup` when API apply is unavailable or fails.

---

## Service Management Flow
Expand Down
52 changes: 40 additions & 12 deletions docs/RESTORE_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ Restore operations are organized into **20–22 categories** (PBS = 20, PVE = 22
Each category is handled in one of three ways:

- **Normal**: extracted directly to `/` (system paths) after safety backup
- **Staged**: extracted to `/tmp/proxsave/restore-stage-*` and then applied in a controlled way (file copy/validation or `pvesh`); when staged files are written to system paths, ProxSave applies them **atomically** and enforces the final permissions/ownership (including for any created parent directories; not left to `umask`)
- **Staged**: extracted to `/tmp/proxsave/restore-stage-*` and then applied in a controlled way (file copy/validation or API apply: `pvesh`/`pveum` on PVE, `proxmox-backup-manager` on PBS); when staged files are written to system paths, ProxSave applies them **atomically** and enforces the final permissions/ownership (including for any created parent directories; not left to `umask`)
- **Export-only**: extracted to an export directory for manual review (never written to system paths)

### PVE-Specific Categories (11 categories)
Expand All @@ -107,17 +107,23 @@ Each category is handled in one of three ways:

### PBS-Specific Categories (9 categories)

**PBS staged apply behavior**: During restore on PBS, ProxSave prompts you to choose how to reconcile PBS objects:
- **Merge (existing PBS)**: intended for restoring onto an already operational PBS; applies supported PBS categories via `proxmox-backup-manager` without deleting existing objects that are not in the backup.
- **Clean 1:1 (fresh PBS install)**: intended for restoring onto a new, clean PBS; attempts to make supported PBS objects match the backup (may remove objects not in the backup).

API apply is automatic for supported PBS staged categories; ProxSave may fall back to file-based staged apply only in **Clean 1:1** mode.

| Category | Name | Description | Paths |
|----------|------|-------------|-------|
| `pbs_config` | PBS Config Export | **Export-only** copy of /etc/proxmox-backup (never written to system) | `./etc/proxmox-backup/` |
| `pbs_host` | PBS Host & Integrations | **Staged** node settings, ACME, proxy, metric servers and traffic control | `./etc/proxmox-backup/node.cfg`<br>`./etc/proxmox-backup/proxy.cfg`<br>`./etc/proxmox-backup/acme/accounts.cfg`<br>`./etc/proxmox-backup/acme/plugins.cfg`<br>`./etc/proxmox-backup/metricserver.cfg`<br>`./etc/proxmox-backup/traffic-control.cfg` |
| `datastore_pbs` | PBS Datastore Configuration | **Staged** datastore definitions (incl. S3 endpoints) | `./etc/proxmox-backup/datastore.cfg`<br>`./etc/proxmox-backup/s3.cfg` |
| `pbs_host` | PBS Host & Integrations | **Staged** node settings, ACME, proxy, metric servers and traffic control (API/file apply) | `./etc/proxmox-backup/node.cfg`<br>`./etc/proxmox-backup/proxy.cfg`<br>`./etc/proxmox-backup/acme/accounts.cfg`<br>`./etc/proxmox-backup/acme/plugins.cfg`<br>`./etc/proxmox-backup/metricserver.cfg`<br>`./etc/proxmox-backup/traffic-control.cfg`<br>`./var/lib/proxsave-info/commands/pbs/node_config.json`<br>`./var/lib/proxsave-info/commands/pbs/acme_accounts.json`<br>`./var/lib/proxsave-info/commands/pbs/acme_plugins.json`<br>`./var/lib/proxsave-info/commands/pbs/acme_account_*_info.json`<br>`./var/lib/proxsave-info/commands/pbs/acme_plugin_*_config.json`<br>`./var/lib/proxsave-info/commands/pbs/traffic_control.json` |
| `datastore_pbs` | PBS Datastore Configuration | **Staged** datastore definitions (incl. S3 endpoints) (API/file apply) | `./etc/proxmox-backup/datastore.cfg`<br>`./etc/proxmox-backup/s3.cfg`<br>`./var/lib/proxsave-info/commands/pbs/datastore_list.json`<br>`./var/lib/proxsave-info/commands/pbs/datastore_*_status.json`<br>`./var/lib/proxsave-info/commands/pbs/s3_endpoints.json`<br>`./var/lib/proxsave-info/commands/pbs/s3_endpoint_*_buckets.json`<br>`./var/lib/proxsave-info/commands/pbs/pbs_datastore_inventory.json` |
| `maintenance_pbs` | PBS Maintenance | Maintenance settings | `./etc/proxmox-backup/maintenance.cfg` |
| `pbs_jobs` | PBS Jobs | **Staged** sync/verify/prune jobs | `./etc/proxmox-backup/sync.cfg`<br>`./etc/proxmox-backup/verification.cfg`<br>`./etc/proxmox-backup/prune.cfg` |
| `pbs_remotes` | PBS Remotes | **Staged** remotes for sync/verify (may include credentials) | `./etc/proxmox-backup/remote.cfg` |
| `pbs_notifications` | PBS Notifications | **Staged** notification targets and matchers | `./etc/proxmox-backup/notifications.cfg`<br>`./etc/proxmox-backup/notifications-priv.cfg` |
| `pbs_access_control` | PBS Access Control | **Staged** access control + secrets restored 1:1 (root@pam safety rail) | `./etc/proxmox-backup/user.cfg`<br>`./etc/proxmox-backup/domains.cfg`<br>`./etc/proxmox-backup/acl.cfg`<br>`./etc/proxmox-backup/token.cfg`<br>`./etc/proxmox-backup/shadow.json`<br>`./etc/proxmox-backup/token.shadow`<br>`./etc/proxmox-backup/tfa.json` |
| `pbs_tape` | PBS Tape Backup | **Staged** tape config, jobs and encryption keys | `./etc/proxmox-backup/tape.cfg`<br>`./etc/proxmox-backup/tape-job.cfg`<br>`./etc/proxmox-backup/media-pool.cfg`<br>`./etc/proxmox-backup/tape-encryption-keys.json` |
| `pbs_jobs` | PBS Jobs | **Staged** sync/verify/prune jobs (API/file apply) | `./etc/proxmox-backup/sync.cfg`<br>`./etc/proxmox-backup/verification.cfg`<br>`./etc/proxmox-backup/prune.cfg`<br>`./var/lib/proxsave-info/commands/pbs/sync_jobs.json`<br>`./var/lib/proxsave-info/commands/pbs/verification_jobs.json`<br>`./var/lib/proxsave-info/commands/pbs/prune_jobs.json`<br>`./var/lib/proxsave-info/commands/pbs/gc_jobs.json` |
| `pbs_remotes` | PBS Remotes | **Staged** remotes for sync/verify (may include credentials) (API/file apply) | `./etc/proxmox-backup/remote.cfg`<br>`./var/lib/proxsave-info/commands/pbs/remote_list.json` |
| `pbs_notifications` | PBS Notifications | **Staged** notification targets and matchers (API/file apply) | `./etc/proxmox-backup/notifications.cfg`<br>`./etc/proxmox-backup/notifications-priv.cfg`<br>`./var/lib/proxsave-info/commands/pbs/notification_targets.json`<br>`./var/lib/proxsave-info/commands/pbs/notification_matchers.json`<br>`./var/lib/proxsave-info/commands/pbs/notification_endpoints_*.json` |
| `pbs_access_control` | PBS Access Control | **Staged** access control + secrets restored 1:1 (root@pam safety rail) | `./etc/proxmox-backup/user.cfg`<br>`./etc/proxmox-backup/domains.cfg`<br>`./etc/proxmox-backup/acl.cfg`<br>`./etc/proxmox-backup/token.cfg`<br>`./etc/proxmox-backup/shadow.json`<br>`./etc/proxmox-backup/token.shadow`<br>`./etc/proxmox-backup/tfa.json`<br>`./var/lib/proxsave-info/commands/pbs/user_list.json`<br>`./var/lib/proxsave-info/commands/pbs/realms_ldap.json`<br>`./var/lib/proxsave-info/commands/pbs/realms_ad.json`<br>`./var/lib/proxsave-info/commands/pbs/realms_openid.json`<br>`./var/lib/proxsave-info/commands/pbs/acl_list.json` |
| `pbs_tape` | PBS Tape Backup | **Staged** tape config, jobs and encryption keys | `./etc/proxmox-backup/tape.cfg`<br>`./etc/proxmox-backup/tape-job.cfg`<br>`./etc/proxmox-backup/media-pool.cfg`<br>`./etc/proxmox-backup/tape-encryption-keys.json`<br>`./var/lib/proxsave-info/commands/pbs/tape_drives.json`<br>`./var/lib/proxsave-info/commands/pbs/tape_changers.json`<br>`./var/lib/proxsave-info/commands/pbs/tape_pools.json` |

### Common Categories (11 categories)

Expand Down Expand Up @@ -225,10 +231,10 @@ Select restore mode:
- `zfs` - ZFS configuration

**PBS Categories**:
- `datastore_pbs` - Datastore definitions (staged apply)
- `datastore_pbs` - Datastore definitions (staged apply; API preferred, file fallback in Clean 1:1)
- `maintenance_pbs` - Maintenance settings
- `pbs_jobs` - Sync/verify/prune jobs (staged apply)
- `pbs_remotes` - Remotes for sync jobs (staged apply)
- `pbs_jobs` - Sync/verify/prune jobs (staged apply; API preferred, file fallback in Clean 1:1)
- `pbs_remotes` - Remotes for sync jobs (staged apply; API preferred, file fallback in Clean 1:1)
- `filesystem` - /etc/fstab
- `storage_stack` - Storage stack config (mount prerequisites)
- `zfs` - ZFS configuration
Expand Down Expand Up @@ -2371,6 +2377,28 @@ systemctl restart proxmox-backup proxmox-backup-proxy

---

**Issue: "Bad Request (400) parsing /etc/proxmox-backup/datastore.cfg ... duplicate property 'gc-schedule'"**

**Cause**: `datastore.cfg` is malformed (multiple datastore definitions merged into a single block). This typically happens if the file lost its structure (header/order/indentation), leading PBS to interpret keys like `gc-schedule`, `notification-mode`, or `path` as duplicated **within the same datastore**.

**Restore behavior**:
- ProxSave detects this condition during staged apply.
- If `var/lib/proxsave-info/commands/pbs/pbs_datastore_inventory.json` is available in the backup, ProxSave will use its embedded snapshot of the original `datastore.cfg` to recover a valid configuration.
- If recovery is not possible, ProxSave will **leave the existing** `/etc/proxmox-backup/datastore.cfg` unchanged to avoid breaking PBS.

**Manual diagnosis**:
```bash
nl -ba /etc/proxmox-backup/datastore.cfg | sed -n '1,120p'

# Look for duplicate keys inside the same datastore block:
awk '
/^datastore: /{ds=$2; delete seen}
/^[[:space:]]*[A-Za-z0-9-]+[[:space:]]+/{key=$1; if(seen[key]++) printf "DUP datastore=%s key=%s line=%d: %s\n", ds, key, NR, $0}
' /etc/proxmox-backup/datastore.cfg
```

---

**Issue: "unable to read prune/verification job config ... syntax error (expected header)"**

**Cause**: PBS job config files (`/etc/proxmox-backup/prune.cfg`, `/etc/proxmox-backup/verification.cfg`) are empty or malformed. PBS expects a section header at the first non-comment line; an empty file can trigger parse errors.
Expand Down Expand Up @@ -2671,7 +2699,7 @@ tar -xzf /path/to/decrypted.tar.gz ./specific/file/path

A: Yes:
- **Extraction**: ProxSave preserves UID/GID, mode bits and timestamps (mtime/atime) for extracted entries.
- **Staged categories**: files are extracted under `/tmp/proxsave/restore-stage-*` and then applied to system paths using atomic replace; ProxSave explicitly applies mode bits (not left to `umask`) and preserves/derives ownership/group to match expected system defaults (important on PBS, where `proxmox-backup-proxy` runs as `backup`; ProxSave also repairs common `root:root` group regressions by inheriting the destination parent directory's group).
- **Staged categories**: files are extracted under `/tmp/proxsave/restore-stage-*` and then applied to system paths using atomic replace; ProxSave explicitly applies mode bits (not left to `umask`) and preserves/derives ownership/group to match expected system defaults (important on PBS, where `proxmox-backup-proxy` runs as `backup`; ProxSave also repairs common `root:root` group regressions by inheriting the destination parent directory's group). On supported filesystems, staged writes also `fsync()` the temporary file and the destination directory to reduce the risk of incomplete writes after a crash/power loss.
- **ctime**: Cannot be set (kernel-managed).

---
Expand Down
Loading
Loading