# Bacula Jobs for Etminan disaster recovery. # # EXAMPLE / REFERENCE ONLY — not installed by the etminan package, not part of # the deliverable. Copy onto your Bacula Director and adapt to your deployment. # # This is the companion to etminan-filesets.conf — that file defines *what* to # back up (Etminan-Verifier-FileSet, Etminan-Agent-FileSet, and the optional # Etminan-Operator-Keys-FileSet); this file defines *when* and *how* (Schedules, # Pools, JobDefs, and the Backup/Restore Jobs that reference those FileSets). # Without this file the FileSets are never exercised. # # Include both from your Director config, FileSets first: # # @/etc/bacula/etminan/etminan-filesets.conf # example path — put them # @/etc/bacula/etminan/etminan-jobs.conf # wherever your Director reads # # The Director, Catalog, Messages, Client (FileDaemon), and Storage resources # are site-specific and NOT defined here — supply them from your environment. # The names referenced below and marked "<-- site resource" must match yours: # # Client = etminan-verifier-fd , etminan-agent-fd , etminan-operator-fd # Storage = File # Messages = Standard # Catalog = MyCatalog # # etminan-operator-fd is the OPERATOR WORKSTATION that holds the signing keys — # deliberately a different client from any verifier (see the security note on # Etminan-Operator-Keys-FileSet in etminan-filesets.conf). # # See Bacula-Forgejo/docs/bacula-director.conf.example for a complete Director/ # Catalog/Storage/Messages scaffold to adapt. # ── Schedules ───────────────────────────────────────────────────────────────── # Weekly Full + daily Incremental. The verifier snapshot pre-job regenerates # baseline.db.snapshot on every run, so Incrementals reliably capture DB changes. Schedule { Name = "EtminanVerifierSchedule" Run = Full 1st sun at 01:00 Run = Incremental mon-sat at 01:00 } Schedule { Name = "EtminanAgentSchedule" # Agent state is small and mostly re-derivable — a nightly Full is cheap and # keeps restore dead-simple (no chain to replay). Run = Full daily at 02:00 } Schedule { Name = "EtminanOperatorKeysSchedule" # Signing keys change rarely (only on rotation). A nightly Full is trivially # cheap and means a fresh key is captured within a day of being generated. Run = Full daily at 04:00 } # ── Pools ───────────────────────────────────────────────────────────────────── # The verifier trust store is the only catastrophic-loss data AND contains # plaintext secrets (verifier.env) plus mTLS/signing material. It gets its own # restricted pool so restore access can be locked down via Console ACLs below. Pool { Name = EtminanVerifierPool Pool Type = Backup Recycle = yes AutoPrune = yes Volume Retention = 365 days Maximum Volume Bytes = 20G Label Format = "EtminanVerifierVol-" } Pool { Name = EtminanAgentPool Pool Type = Backup Recycle = yes AutoPrune = yes Volume Retention = 180 days Maximum Volume Bytes = 20G Label Format = "EtminanAgentVol-" } # The operator-keys pool holds the private signing keys and is kept in its OWN # pool so restore can be locked to a key custodian (see Console ACLs below) and # NEVER mixed with the verifier trust store. Long retention: these keys are # long-lived, so keep history for the working life of the key, not just weeks. Pool { Name = EtminanOperatorKeysPool Pool Type = Backup Recycle = yes AutoPrune = yes Volume Retention = 3650 days Maximum Volume Bytes = 2G Label Format = "EtminanOperatorKeysVol-" } # ── JobDefs ─────────────────────────────────────────────────────────────────── JobDefs { Name = "EtminanVerifierDefs" Type = Backup Level = Incremental Client = etminan-verifier-fd # <-- site resource FileSet = "Etminan-Verifier-FileSet" # from etminan-filesets.conf Storage = File # <-- site resource Messages = Standard # <-- site resource Pool = EtminanVerifierPool Schedule = "EtminanVerifierSchedule" Priority = 10 # CRITICAL: take the WAL-safe snapshot BEFORE the FileSet is read, so the # backup captures baseline.db.snapshot (+ .audit-head) instead of the live # SQLite DB. The FileSet excludes the live DB; this produces the copy it wants. # Place etminan-verifier-snapshot.sh anywhere on the verifier host and point # this at it (example path shown — it runs on the client/FileDaemon). Client Run Before Job = "/usr/local/lib/etminan/etminan-verifier-snapshot.sh" } JobDefs { Name = "EtminanAgentDefs" Type = Backup Level = Full Client = etminan-agent-fd # <-- site resource FileSet = "Etminan-Agent-FileSet" # from etminan-filesets.conf Storage = File # <-- site resource Messages = Standard # <-- site resource Pool = EtminanAgentPool Schedule = "EtminanAgentSchedule" Priority = 15 # No pre-job: agent state (tls/, assigned_profile, /etc/etminan-agent) is # written atomically (temp+fsync+rename), so a live copy is always consistent. } JobDefs { Name = "EtminanOperatorKeysDefs" Type = Backup Level = Full Client = etminan-operator-fd # <-- OPERATOR WORKSTATION, never a verifier FileSet = "Etminan-Operator-Keys-FileSet" # from etminan-filesets.conf Storage = File # <-- site resource Messages = Standard # <-- site resource Pool = EtminanOperatorKeysPool Schedule = "EtminanOperatorKeysSchedule" Priority = 25 # No pre-job: signing keys are static files written 0600 O_EXCL at keygen. } # ── Backup Jobs ─────────────────────────────────────────────────────────────── Job { Use = "EtminanVerifierDefs" Name = "Etminan-Verifier" } # Define one agent Job per monitored host (each with its own Client). This is # the template; clone it per host, overriding Name + Client. Job { Use = "EtminanAgentDefs" Name = "Etminan-Agent" Client = etminan-agent-fd # <-- site resource (one per host) } # Optional — enable only if you back up the operator workstation that holds the # signing keys. Read the security note on Etminan-Operator-Keys-FileSet first. Job { Use = "EtminanOperatorKeysDefs" Name = "Etminan-Operator-Keys" } # ── Restore Jobs ────────────────────────────────────────────────────────────── # NOTE on verifier restore: Bacula writes baseline.db.snapshot and # baseline.db.snapshot.audit-head back to /var/backups/etminan. To bring the # verifier live, copy them into place (see the Verifier FileSet's restore note): # cp /var/backups/etminan/baseline.db.snapshot \ # /var/lib/etminan-verifier/baseline.db # cp /var/backups/etminan/baseline.db.snapshot.audit-head \ # /var/lib/etminan-verifier/baseline.db.audit-head # Never restore the live DB directly — the FileSet never captured it. Job { Name = "Etminan-Verifier-Restore" Type = Restore Client = etminan-verifier-fd # <-- site resource FileSet = "Etminan-Verifier-FileSet" Storage = File # <-- site resource Messages = Standard # <-- site resource Pool = EtminanVerifierPool Where = / # files carry absolute paths; restore in place } Job { Name = "Etminan-Agent-Restore" Type = Restore Client = etminan-agent-fd # <-- site resource FileSet = "Etminan-Agent-FileSet" Storage = File # <-- site resource Messages = Standard # <-- site resource Pool = EtminanAgentPool Where = / } # Restores the signing keys to a STAGING dir, not in place — so a restore can't # silently clobber a live key. The custodian inspects them, then moves each into # its real location by hand (keys are 0600; preserve that on the move). Job { Name = "Etminan-Operator-Keys-Restore" Type = Restore Client = etminan-operator-fd # <-- OPERATOR WORKSTATION FileSet = "Etminan-Operator-Keys-FileSet" Storage = File # <-- site resource Messages = Standard # <-- site resource Pool = EtminanOperatorKeysPool Where = /var/tmp/etminan-key-restore # staging; move into place by hand } # ── Console ACLs (restrict restores by blast radius) ────────────────────────── # The verifier pool holds secrets + trust-store material; the operator-keys pool # holds the signing keys. These are split across three roles so no single console # can restore both the baselines and the keys that authorize them: # * standard-operator — agent pool only. # * dr-operator — verifier + agent pools (the trust store), but NOT the # operator-keys pool. # * key-custodian — operator-keys pool ONLY. Deliberately cannot touch the # verifier pool, preserving the separation of duties. Console { Name = etminan-standard-operator Password = "standard-console-password" # <-- CHANGE CommandACL = status, messages, show, run JobACL = Etminan-Agent, Etminan-Agent-Restore PoolACL = EtminanAgentPool } Console { Name = etminan-dr-operator Password = "dr-console-password" # <-- CHANGE CommandACL = restore, run, status, messages, show, cancel JobACL = Etminan-Verifier, Etminan-Verifier-Restore, Etminan-Agent, Etminan-Agent-Restore PoolACL = EtminanVerifierPool, EtminanAgentPool } Console { Name = etminan-key-custodian Password = "key-custodian-console-password" # <-- CHANGE CommandACL = restore, run, status, messages, show, cancel JobACL = Etminan-Operator-Keys, Etminan-Operator-Keys-Restore PoolACL = EtminanOperatorKeysPool }