BorgBackup

repository·master·Indexed 11 days ago

https://github.com/borgbackup/borg

A deduplicating backup program providing efficient, secure, and compressed data storage. It features authenticated encryption for untrusted remote targets, support for multiple compression algorithms (lz4, zlib, lzma), and advanced repository management including lock handling and migration from Attic.

Tokens
119.8K
Snippets
407
Records
547
Agent score
95%

What's inside BorgBackup

  1. Manage encryption keys via keyfile or repokey modes

    master

    Borg supports two primary methods for storing encryption keys and secrets:

    1. keyfile mode: The key is stored in a file within the keys/ subdirectory of the Borg configuration directory on the client.
    2. repokey mode: The key is stored within the repository itself under the keys/ namespace, named using the SHA256 of the Borg key content.

    Commonalities:

    • Both modes use the same internal data format.
    • Keys are generated from random data and then encrypted using a key derived from your passphrase.
    • Borg identifies the correct key by attempting to decrypt it with the provided passphrase.
    • The passphrase can be provided via the BORG_PASSPHRASE environment variable or through interactive prompts.
  2. How repository object IDs and keys are generated

    master

    Repository object IDs (used as keys in the key-value store) are 256-bit (32-byte) strings. They are computed by applying an id_hash function to the plaintext data (before encryption, compression, or obfuscation).

    The id_hash function is selected during repository creation using the --id-hash flag. For encrypted repositories, this is a keyed MAC over the plaintext using the id_key:

    • sha256: Uses HMAC-SHA256.
    • blake3: Uses a keyed BLAKE3.
    • none: Uses a plain SHA256 (only for unencrypted repositories).

    Because these IDs are used for deduplication, the selected hash must be cryptographically strong.

    borg repo-create --id-hash sha256 ...
  3. How Pack Index Entries work

    master

    To locate a chunk within a pack, Borg uses a ChunkIndex. The index maps a chunk_id to a specific location within a pack file.

    Mapping: chunk_id $\rightarrow$ (flags, size, pack_id, obj_offset, obj_size)

    • obj_offset: The byte offset of the blob from the start of the pack file.
    • obj_size: The total blob length (header + encrypted_meta + encrypted_data).
    • size: The plaintext chunk size.
    • F_PENDING flag: Indicates a chunk is currently being buffered in the pack writer but its pack location is not yet resolved.

    Retrieval: A reader fetches a chunk using a single range request: read packs/<hex(pack_id)> at [obj_offset, obj_offset + obj_size).

  4. Structure of a Repository Object

    master

    Each repository object is stored separately under its ID in the data/ directory. An object consists of:

    1. 32-bit meta size
    2. 32-bit data size
    3. meta: Metadata (MessagePack-encoded and separately encrypted/authenticated).
    4. data: The actual payload (encrypted and compressed).

    Metadata is stored separately to allow efficient querying (reading/decrypting only the small metadata part) without needing to process the much larger data payload.

  5. Manage files cache memory and TTL

    master

    The files cache uses an age value for lifecycle management.

    • When a file is seen during a backup run, its age is reset to 0.
    • If a file is not seen, its age is incremented by one.
    • Once a file's age reaches the value defined by BORG_FILES_CACHE_TTL, its entry is removed from the cache.

    To minimize memory overhead, Borg stores the files cache in memory as a compressed Python dictionary where chunk lists are stored as 32-bit indexes into the chunks index rather than full 256-bit IDs.

  6. Understand Borg's Message IDs and Exit Codes

    master

    Borg uses unambiguous Message IDs to identify log messages and operations. This allows developers to programmatically handle specific errors or events without parsing variable text strings. Each Message ID is associated with a specific Return Code (rc), which serves as the process exit code.

    When developing frontends or automation scripts, you should rely on these IDs and exit codes for robust error handling.

    Key categories include:

    • Errors (rc 2-92): Critical failures (e.g., Repository.DoesNotExist rc 13, LockTimeout rc 73).
    • Warnings (rc 1, 100-107): Non-fatal issues (e.g., IncludePatternNeverMatchedWarning rc 101).
    • Operations: Specific task names like archive.delete or repository.check used in internal RPC/logic.
  7. Use common options across all Borg commands

    master

    All Borg commands support a set of common options. While the specific list of options is defined in the common options documentation, a key behavior to note is how the --help flag interacts with subcommands:

    • For subcommands (e.g., borg help compact), the --help flag works as expected.
    • For sub-sub-commands (e.g., borg help key export), the --help flag may not work when used with the help command.
    • Workaround: Use the help command as a flag directly on the sub-sub-command (e.g., borg key export --help).
  8. Understand Archive JSON formats

    master

    Borg uses different archive object structures depending on the command used. All archive objects include name, id, and start (timestamp).

    borg list (Simple Format)

    Used for fast retrieval. Archives are returned in an archives array.

    borg info and borg create (Extended Format)

    Used for detailed information. These include additional keys:

    • end: End timestamp.
    • duration: Float representing seconds between start and end.
    • stats: Object containing original_size, compressed_size, deduplicated_size, and nfiles.
    • command_line: Array of strings representing the creation command.
    • chunker_params: Chunker parameters.
    • hostname / username / comment: (Only in borg info) Metadata about the creation.

    Note: To include the comment in borg list JSON output, you must explicitly request it using the --format flag: borg list repo --format "{name}{comment}" --json

    # Example: Simple archive listing
    {
        "archives": [
            {
                "id": "80cd07219ad725b3c5f665c1dcf119435c4dee1647a560ecac30f8d40221a46a",
                "name": "host-system-backup-2017-02-27",
                "start": "2017-08-07T12:27:20.789123"
            }
        ],
        "encryption": { "encryption": "aes256-ocb", "id_hash": "sha256" },
        "repository": { "id": "...", "last_modified": "...", "location": "..." }
    }
  9. Manage Borg versions in remote environments

    master

    When working with remote Borg servers, you can control which Borg version is used by setting the BORG_VERSION environment variable before executing the borg serve command via SSH.

    Additionally, Borg supports placeholders in the --remote-path option, such as {borgversion}, which can be used to facilitate version-specific remote path configurations.

  10. Mitigate size-based fingerprinting in Borg repositories

    master

    An attacker with access to a repository might attempt to identify files by analyzing the sizes of stored chunks. Borg provides several mechanisms to make this fingerprinting attack difficult:

    1. Chunking Algorithms:
      • buzhash and buzhash64: These use secret key material (a chunk_seed or bh64_key derived from the ID key) to determine chunk boundaries. This ensures an attacker cannot predict chunk lengths without the secret key.
      • fixed: Yields fixed-sized chunks.
    2. Obfuscation: Borg offers an optional obfuscate pseudo-compressor. This step adds padding (0x00 bytes) to the output of the compression step to mask the true compressed size. Note that this increases repository size.
    3. Keyed Chunk IDs: Borg uses a keyed hash (MAC, e.g., HMAC-SHA256) with a secret id_key to generate chunk IDs. This prevents an attacker from computing the same chunk IDs for known files to verify their presence in the repository.
    4. Compression/Encryption: While combining them is generally safe in Borg's model, users concerned about specific side channels can choose not to use compression.
  11. What the chunks index is and how it is used

    master

    The chunks index is a key-value mapping persisted in the repository as index fragments. It is used to determine if a specific chunk already exists in the repository to avoid redundant transfers.

    Index Entry Structure

    Each entry in the index (a ChunkIndexEntry) consists of:

    • Key (32 bytes): The id_hash of the chunk.
    • Value (48 bytes):
      • flags (32-bit): Indicates if the chunk is used (F_USED), needs re-compression (F_COMPRESS), or is pending (F_PENDING).
      • size (32-bit): The plaintext chunk size. Note: When building an index from fragments, this may be 0. Code consuming the index must handle size == 0 and not assume it is the real size.
      • pack_id (32 bytes): The ID of the pack file containing the chunk.
      • obj_offset (32-bit): The byte offset within the pack file.
      • obj_size (32-bit): The total length of the stored blob (header + encrypted metadata + encrypted data).

    To read a chunk, perform a ranged read of [obj_offset, obj_offset + obj_size) from packs/<hex(pack_id)>.