The remote source reader's encrypted client-log path writes the
decrypted output to to_path through decrypt_encrypted_data_blob()
(which uses its own atomic replace_file), but never removes the
encrypted bytes left at <snapshot>/client.log.blob.tmp. The
non-encrypted path consumes the same tmp_path via std::fs::rename().
Bail out (matching the surrounding error handling style) if the
cleanup fails, so the leftover doesn't go unnoticed.
Fixes: e5b191109 ("sync: decrypt client log on pull with matching decryption key")
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
LocalSourceReader::try_fetch_client_log() bubbles up ENOENT from the
underlying std::fs::read() in load_file_into() if the source snapshot
has no client.log.blob, which the caller in pull_snapshot()
propagates via `?`, aborting the snapshot pull. Many snapshots
legitimately lack a log (VM backups, scheduled API jobs, older
clients), so this breaks local sync for them.
Match the remote reader's intentional best-effort behavior ("be
silent if there is no log") by checking up front whether the source
file is present and returning Ok(()) without touching the
destination if not. As a side benefit, this avoids the empty
client.log.blob that load_file_into() would otherwise leave behind
through its O_CREAT|O_TRUNC open.
Fixes: c456d58a4 ("sync: fix client log fetching for local sync job")
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
Client logs are currently fetched as is, not decrypting when pulling
even when there is a matching decryption key.
Fix this by:
- Factoring out the DataBlob decryption helper so it can be reused
- Pass the crypt config as conditional parameter to the fetch_log closure
- Extend the try_fetch_client_log() implementation to decrypt the source
blob on the fly, if the crypt config is given.
Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
Link: https://lore.proxmox.com/20260425140927.928214-4-c.ebner@proxmox.com
For the log messages to be correctly logged and prefixed, extend the
trait method for fetching the client log by the log sender and use
that for logging. Since the local source reader does not yet log
this, store a full pbs_datastore::BackupDir instead of the
pbs_api_types::BackupDir, which is a superset thereof and already
contains a reference to the datastore required by the reader.
Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
Link: https://lore.proxmox.com/20260425140927.928214-3-c.ebner@proxmox.com
Pulling with a local sync job currently does not handle fetching the
client log correctly, completely lacking the implementation.
Fix this by adding the missing implementation on the local source
reader.
Since this does not actually download anything, also rename the
method from try_download_client_log() to a now better fitting
try_fetch_client_log().
Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
Link: https://lore.proxmox.com/20260425140927.928214-2-c.ebner@proxmox.com
Commit 2f94320db ("sync: pull: decrypt blob files on pull if
encryption key is configured") introduced the logic to decrypt
encrypted data blob contents from the source on the fly when pulling
with a matching decryption key.
This however incorrectly performs a double blob decoding, as the
DataBlobReader already checks and strips the header while reading the
raw data from file, the DataBlob::load_from_reader() therefore
choking when trying to check the now missing header.
Fix this by fully relying on the DataBlobReader to check, decode and
decrypt the blob, and add the still missing finish() call to verify
the reader state after decoding. Only then construct the new
decrypted blob and pass individual slices for checksum calculation
and writing to file, as the former consumes the slice.
Reported-by: Markus Frank <m.frank@proxmox.com>
Reported-by: Erik Fastermann <e.fastermann@proxmox.com>
Reported-by: Shan Shaji <s.shaji@proxmox.com>
Reported-by: Dominik Csapak <d.csapak@proxmox.com>
Fixes: 2f94320db ("sync: pull: decrypt blob files on pull if encryption key is configured")
Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
Link: https://lore.proxmox.com/20260425101015.437816-1-c.ebner@proxmox.com
[TL: s/chocking/choking/ typo fix in commit message]
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
This one returns a unique ID that can be used to uniquely identify a PBS
node. This will be used by PDM to match PBS storages in a PVE cluster to
PBS remotes in PDM, mainly for associating backup jobs with the
appropriate PBS remote. Later we can also use this to match PBS sync
jobs to their matching PBS remotes in PDM.
The instance ID is derived from /etc/machine-id as recommended by
systemd [1] by using proxmox_systemd::sd_id128::get_app_specific_id.
[1] https://www.freedesktop.org/software/systemd/man/latest/sd_id128_get_machine_app_specific.html
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Link: https://lore.proxmox.com/20260424082943.89614-4-l.wagner@proxmox.com
[TL: regenerate ID freshly on apply]
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
Move 'datastore move-group' on the manager to 'group move' on the
client, and add a 'move' subcommand to the client's 'namespace' group.
new CLI layout:
client:
group forget
[+] group move
namespace list
namespace create
namespace delete
[+] namespace move
manager:
[-] datastore move-group
[-] datastore move-namespace
Suggested-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
Link: https://lore.proxmox.com/20260424120041.221676-1-h.laimer@proxmox.com
[TL: drop the addition of a namespace group to manager for now,
should be its own commit in any case]
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
make the full sequence of checks:
- is source encrypted? if not -> regular pull
- if a local snapshot exists
-- is it encrypted or signed using the same key -> regular pull
-- is it encrypted using a different key -> abort
-- if it is signed, we either abort later or log + regular pull
- find decryption key
-- if none is found, but a local snapshot exists and the source is full
encrypted -> abort
-- if none is found, and no local snapshot exists -> regular pull
-- only proceed for plain snapshots or signed-with-same key, since those could
be the result of a decrypting pull
- check signature -> abort if it doesn't match
- if a local snapshot exists, check change-detection fingerprint, and abort if
it doesn't match or doesn't exist, otherwise assume we can proceed with a
decrypting sync
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Link: https://lore.proxmox.com/20260424124615.654666-2-f.gruenbichler@proxmox.com
Fixes an if_same_then_else clippy warning and improves the
readability for the check of sync jobs having a key assigned as
associated key.
Both branches have the same push logic, so combine into a common if
statement. Since the if statement would then however be hard to
parse, move the associated key check logic into a closue instead.
Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
Link: https://lore.proxmox.com/20260424103607.531400-4-c.ebner@proxmox.com
In an effort to improve code readability and maintanablity.
By pulling out the logic into a dedicated helper, nested branches can
be switched over to early return statements instead, greatly reducing
the required nesting level.
Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
Link: https://lore.proxmox.com/20260424103607.531400-3-c.ebner@proxmox.com
The key-fingerprint stored on the source manifest might be present if
the snapshot contents are encrypted or signed.
Checking the key fingerprints presence on the source manifest is
therefore not enough to determine whether the snapshot is fully
encrypted, rather each of the registered files chunk crypt mode has
to be checked.
Only decrypt on pull with a matching key if the snapshots contains
only contains encrypted files, warn and fallback to regular pull if
not. Adapt error messages on key mismatch to reflect that snapshot
might be signed but not encrypted.
Reported-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
Link: https://lore.proxmox.com/20260424103607.531400-2-c.ebner@proxmox.com
Let's Encrypt recommends renewing certificates once 2/3 of the
lifetime has passed for typical 90-day certs, and already at 1/2 for
short-lived ones [1]. Pick the scaling factor based on the
certificate's total lifetime: 1/2 when under ten days, 1/3 otherwise.
The 3-day floor still applies so the daily-update service has a
couple of chances to recover from transient renewal failures.
[1] https://letsencrypt.org/docs/integration-guide/#when-to-renew
Originally-by: Manuel Federanko <m.federanko@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
The seconds-per-day factor appears three times in the renewal helpers.
Replace the literals with a file-local constant for clarity.
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
The 'move-namespace' and 'move-group' commands had called their api
handler in-process via the CLI's rpcenv, so the worker task ran inside
the manager process and wrote into the datastore as root. Route them
through the api with connect_to_localhost() instead.
Reported-by: Michael Köppl <m.koeppl@proxmox.com>
Fixes: 4d74e20d ("cli: add move-namespace and move-group commands")
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
Link: https://lore.proxmox.com/20260424112001.206897-1-h.laimer@proxmox.com
Add 'move-namespace' and 'move-group' subcommands to
proxmox-backup-manager datastore. Both call the corresponding API
handler and wait for the worker task to complete.
move-namespace accepts optional --max-depth and --delete-source
flags matching the API parameters.
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
Link: https://lore.proxmox.com/20260422133951.192862-14-h.laimer@proxmox.com
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
Add a dedicated /move-namespace endpoint for moving namespaces to a
new location within the same datastore. Exposes max-depth,
delete-source, and merge-groups as optional parameters.
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
Link: https://lore.proxmox.com/20260422133951.192862-11-h.laimer@proxmox.com
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
Add a dedicated /move-group endpoint for moving backup groups between
namespaces within the same datastore.
The permission model allows users with DATASTORE_PRUNE on the source
and DATASTORE_BACKUP on the target namespace to move groups they own,
without requiring full DATASTORE_MODIFY on both sides.
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
Link: https://lore.proxmox.com/20260422133951.192862-10-h.laimer@proxmox.com
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
Removal previously always descended into child namespaces. Callers
that drive traversal themselves (pull's vanished-namespace loop, and
the upcoming move-namespace cleanup) only need the per-level work.
Factor the single-level work into its own public entry point and
keep the recursive variant as a bottom-up wrapper around it. Also
lift the empty-type-dir cleanup so it runs in both modes. Otherwise
the flat path cannot prune an already-empty namespace whose type
dirs were left behind by prior group moves or deletes. Switch pull's
vanished-NS cleanup to the flat variant.
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
Link: https://lore.proxmox.com/20260422133951.192862-5-h.laimer@proxmox.com
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
Move-namespace needs the same depth check sync (push/pull) already
uses. Move it to pbs-datastore as a free helper so both share one
implementation.
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
Link: https://lore.proxmox.com/20260422133951.192862-3-h.laimer@proxmox.com
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
The active encryption key is consumed only by the push-side of sync,
where it triggers on-the-fly encryption of plain source snapshots. On
a pull job the field has no effect -- the pull side only decrypts via
the associated-key list -- but the config/update APIs previously
accepted it silently, which is user-confusing and risks hiding a
misconfigured job.
Reject the combination at create time and at update time, including
switching an existing push job over to pull while an active key is
still configured (the user has to clear the key in the same request
or up-front).
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
Adapt the api endpoint to not only allow to archive a key, but rather
allow to toggle its archived state by setting or stripping the
optional `archived-at` timestamp in the config.
Expose this in the ui by adapting the corresponding button
accordingly.
Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
Decrypt any backup snapshot during pull which was encrypted with a
matching encryption key. Matching of keys is performed by comparing
the fingerprint of the key as stored in the source manifest and the
key configured for the pull sync jobs.
If matching, pass along the key's crypto config to the index and chunk
readers and write the local files unencrypted instead of simply
downloading them.
If the local snapshot already exists (resync), refuse to sync without
decryption if the target snapshot is unencrypted, the source however
encrypted.
To detect file changes for resync, compare the file change
fingerprint calculated on the decrypted files before push sync with
encryption.
Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
Once the matching decryption key will be provided, use it to decrypt
the chunks on pull and rewrite the index file based on the decrypted
chunk digests and offsets.
Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
During pull, blob files are stored in a temporary file before being
renamed to the actual blob filename as stored in the manifest.
If a decryption key is configured in the pull parameters, use the
decrypted temporary blob file after downloading it from the remote
to decrypt it and re-encode as new compressed but unencrypted blob
file. Rename the decrypted tempfile to be the new tmpfile to be
finally moved in place.
Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
For index files being decrypted during the pull, it is not enough to
keep track of the processes source chunks, but the decrypted digest
has to be known as well in order to rewrite the index file.
Extend the encountered chunks such that this can be tracked as well.
To not introduce clippy warnings and to keep the code readable,
introduce the EncounteredChunksInfo struct as internal type for the
hash map values.
Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
In order to decrypt and encrypted index file during a pull sync job
when a matching decryption key is configured, the manifest and index
has to be rewritten as the chunks has to be decrypted and the new
digests calculated based on the decrypted chunk.
The newly written index file need to be registered in the new manifest
file, both will be moved into place by a final rename, just like for
the regular case.
In order to be able to do so, provide a DecryptedIndexWriter instance
to the chunk pulling logic. The DecryptIndexWriter provides variants
for fix and dynamic index writers, or none if no rewriting should
happen.
This remains disarmed for the time being by never passing the crypt
config and new manifest, until the logic to decrypt the chunk and
re-calculate the digests is in place, done in subsequent code changes.
Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
Introduce closures for client log fetching and cleanup of manifest
tempfile for early return cases. This is in preparation for reusing
the same logic also for cases where the encrypted source matches the
decrypted target manifest, as checked by the change detection
fingerprint.
As a side effect, this fixes the missing client upload for s3 backed
datastores in the skip sync branch, where it was previously forgotten.
Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
[TL: fix extra trailing `;` in fetch_log closure and "were -> where"
typo in commit message]
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
Allows to pass in the crypto config for the source chunk reader,
making it possible to decrypt chunks when fetching.
This will be used by the pull sync job to decrypt snapshot chunks
which have been encrypted with an encryption key matching the
one in the pull job configuration.
Disarmed by not setting the crypt config until the rest of the logic
to correctly decrypt snapshots on pull, including manifest, index
files and chunks is put in place in subsequet code changes.
Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
If configured and passed in on PullParams construction, check access
and load the encryption key. Any snapshots matching this key
fingerprint should be decrypted during pull sync.
Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
If an encryption key id is provided in the push parameters, the key is
loaded at the start of the push sync job and passed along via the
crypt config.
Backup snapshots which are already encrypted or partially encrypted
snapshots are skipped to avoid mixing of contents. Pre-existing
snapshots on the remote are however not checked to match the key.
Special care has to be taken when tracking the already encountered
chunks. For regular push sync jobs chunk upload is optimized to skip
re-upload of chunks from the previous snapshot (if any) and new, but
already encountered chunks for the current group sync. Since the chunks
now have to be re-processes anyways, do not load the chunks from the
previous snapshot into memory if they need re-encryption and keep track
of the unencrypted -> encrypted digest mapping in a hashmap to avoid
re-processing. This might be optimized in the future by e.g. move the
tracking to an LRU cache, which however requrires more carefully
evaluaton of memory consumption.
The chunk handler working task has been pulled out from push_index()
and implemented based on whether chunk encryption must happen or not,
keeping the logic simpler for each dedicated case.
Fixes: https://bugzilla.proxmox.com/show_bug.cgi?id=7251
Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
[TL: fix "re-encypting" typo in log message]
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
Loading of known chunks only makes sense for snapshots which do not
need encryption while pushing. To check this move the known chunk
loading into a common helper method and distinguish dynamic/fixed
index for loading based on archive type.
Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
Encrypt the client log blob with given encryption key during syncs in push
direction, if given. The client log is not part of the manifest and therefore
needs to be handled separately.
Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
Encrypt the data blob with given encryption key during syncs in push
direction, if given.
Introduces a helper to read and decode the data blob from source into
raw data and re-encrypt, so the new blob is compressed and encrypted,
including the correct header when uploading. The same helper will be
reused for client log uploads in subsequent code changes.
Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
Exposes the optional encryption key id to be used for server side
encryption of contents during push sync jobs. Only expose the
parameter for now and load the key if given, the logic to use it will
be implemented in subsequent code changes.
Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
In preparation for being able to encrypt unencypted backup snapshots
during push sync jobs.
Previously the pre-existing manifest file was pushed to the remote
target since it did not require modifications and contained all the
files with the correct metadata. When encrypting, the files must
however be marked as encrypted by individually setting the crypt mode
and the manifest must be signed and the encryption key fingerprint
added to the unprotected part of the manifest.
Therefore, now recreate the manifest and update accordingly. To do
so, pushing of the index must return the full BackupStats, not just
the sync stats for accounting.
Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
Since the SyncJobConfig got extended to include an optional active
encryption key, set the default to none.
Extend the api config update handler to also set, update or delete
the active encryption key based on the provided parameters.
Associated keys will also be updated accordingly, however it is
assured that the previously active key will remain associated, if
changed.
They encryption key will be used to encrypt unencrypted backup
snapshots during push sync. Any of the associated keys will be used
to decrypt snapshots with matching key fingerprint during pull sync.
During updates to active encryption key, associated keys and/or owner
assure access to keys is granted for respective sync job owner.
Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
So a sync job can not be configured with a non existing or non
accessible key for given sync owner/local-user.
Key access is checked by loading the key from the keyfile.
When setting the active encryption key for push sync jobs it is
further assured that the key is not archived yet.
Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
Defines the api endpoints for listing existing keys as defined in the
config, create new keys and archive or remove keys.
New keys are either generated on the server side or uploaded as json
string. Password protected keys are currently not supported and will
be added at a later stage, once a general mechanism for secrets
handling is implemented for PBS.
Keys are archived by setting the `archived-at` timestamp, marking
them as no longer usable for encrypting new content with respective
keys. This is only possible if the key is not in-use as active
encryption key by any sync job.
Removing a key requires for it to be archived first. Further, is only
possible when the key is no longer referenced by a sync job config,
protecting from accidental deletion of an in-use key.
Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
[TL: merge if to else if to avoid that a job might be reported twice
in check_encryption_key_in_use()]
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
Introduces a common helper function to be used when loading an
encryption key in sync job for either push or pull direction.
For given user, access to the provided key by id is checked and the
key config containing the secret loaded from the file by means of the
config.
Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
cert_expires_soon and cert_renew_lead_time each read and parsed
/etc/proxmox-backup/proxy.pem independently, so a single "is a renewal
due?" check did up to three file reads and PEM parses: once in the
expiry check, again in the lead-time computation invoked from there,
and a third time when the caller formatted the user-facing message.
Collapse both into a single check_renewal_needed() helper that parses
the certificate once and returns both the expiry status and the lead
time. The helper returns the lead time in days so callers no longer
have to divide by seconds-per-day themselves.
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
The else branch silently swallowed the not_before_unix/not_after_unix
errors via .ok() and returned the 30-day default. Surface the fallback
via log::warn! so a misparsed certificate is not hidden from operators.
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
After 29bd6b89c scaled the renewal check by certificate lifetime, the
30-day wording on renew_acme_cert and cert_expires_soon no longer
matches the code. Reword both to refer to the renewal lead time in the
abstract.
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
Start renewing a certificate once 2/3 of its total lifetime have passed,
instead of the hardcoded 30 days. This stays consistent with many
certificates, which are valid for 90 days.
The update service runs daily, impose a 3 day minimum remaining lifetime
to still be able to handle transient failures for certificate renewals.
Signed-off-by: Manuel Federanko <m.federanko@proxmox.com>
Fixes: https://bugzilla.proxmox.com/show_bug.cgi?id=6372
Link: https://lore.proxmox.com/20260421144645.275884-1-m.federanko@proxmox.com
The also extraced proxmox-disks crate requires the ParallelHandler
helper, so we need to extract it as well.
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Reviewed-by: Arthur Bied-Charreton <a.bied-charreton@proxmox.com>
Tested-by: Arthur Bied-Charreton <a.bied-charreton@proxmox.com>
Link: https://lore.proxmox.com/20260319094617.169594-13-l.wagner@proxmox.com
If a user opts into gc-on-unmount they expect GC to actually run
before unmount; downgrading a failed start to a warning makes that
silent. Bail on real failures so it is not missed.
Check JobState first to treat a concurrent scheduled GC as a
non-error: the existing run already blocks the unmount in maintenance
mode until done, which is what the user asked for.
This is slightly racy (GC starts between our state check and this API
call), which is not really worth the hassle to close especially as
there is no actual problem, only slightly worsening UX due to an error
message that could be an info one.
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>