Scope: This report maps the Arkhivio Backup Framework's technical controls to the NIST Special Publication 800-53 Revision 5 security control catalogue and the NIST Cybersecurity Framework 2.0 (CSF) core functions. Only controls directly evidenced in the codebase and deployment configuration are assessed. Organisational controls (policies, risk assessments, workforce training, incident response plans, supply-chain management) remain the responsibility of the operating organisation. This document does not constitute a formal NIST assessment or FedRAMP authorisation.
Two distinct account types — "admin" (full CRUD) and "operator" (read-only) — are provisioned via environment variables or Active Directory group membership (auth.py). AD backend delegates account lifecycle management (creation, disabling, removal) to the directory service. Startup validation in _validate_config() rejects incomplete account configurations before the server accepts connections.
All API write endpoints are gated by the require_admin() FastAPI dependency (main.py). Operator accounts receive HTTP 403 on any attempt to invoke write operations. Read-only endpoints use require_auth(). Unauthenticated requests return HTTP 401 or redirect to the login page. Bucket credentials (access_key, secret_key) are stripped from all API responses by _serialize_bucket(), preventing privilege escalation through information disclosure.
SessionMiddleware configured with max_age=SESSION_TIMEOUT_SECONDS (default 3,600 s). Set SESSION_TIMEOUT_SECONDS=900 in .env for a 15-minute lock consistent with NIST moderate-baseline guidance. The server rejects replayed cookies past max_age; explicit logout clears the session immediately.
Local backend enforces unique usernames per role with timing-safe comparison via secrets.compare_digest() (auth.py:121–130), preventing timing-based credential inference. AD backend binds each user individually via LDAP UPN, providing per-user unique identification backed by the enterprise directory.
Session cookies are signed with HMAC-SHA256 via itsdangerous using SESSION_SECRET; the application fails fast at startup if this secret is absent. The Fernet encryption key (BACKUP_SECRET_KEY) is validated at module load time. Key storage hardening is a configuration task: the framework supports 6 secrets-manager backends (AWS Secrets Manager, Azure Key Vault, GCP, IBM, HashiCorp Vault, OpenBao) via the SECRETS_PROVIDER env var.
SECRETS_PROVIDER=aws (or azure, gcp, ibm, vault, openbao) in .env. The framework's secrets_manager.py will retrieve BACKUP_SECRET_KEY and MongoDB credentials from the chosen provider at startup, keeping secrets entirely off disk.BACKUP_SECRET_KEY, SESSION_SECRET, and MongoDB credentials on a documented schedule (NIST recommends periodic rotation; 90 days is documented in USER_MANUAL.md). No code change required — update the secret in the provider and restart the service.ADMIN_PASSWORD and OPERATOR_PASSWORD.The framework authenticates with username + password only. NIST SP 800-63B and the SP 800-53 moderate baseline require MFA for privileged (admin) accounts. The framework does not implement MFA natively, but the deployment architecture allows this to be satisfied externally.
AD_ADMIN_GROUP. The LDAP bind in auth.py will only succeed for users who have satisfied the MFA challenge at the directory level.Every significant event emits a structured JSON log record via log_event() (logger.py:70–86) containing ts (ISO-8601 UTC), event name, and contextual fields. All 6 privileged write endpoints emit named audit_* events with the acting user identity. Sample:
Events cover the full operation lifecycle: configuration changes, authentication, upload, scan, reconcile, delete, restore, and error conditions. No credentials or key material are ever written to logs.
Logs are written to stdout and optionally to a rotating file via RotatingFileHandler (logger.py:46–53, default 10 × 50 MB = 500 MB on disk). NIST SP 800-53 AU-11 requires retention aligned with organisational policy; for federal systems this is typically 3 years. The rotation scheme alone does not guarantee this.
LOG_FILE=/var/log/backup/backup.log in .env, then configure a log shipper (Filebeat, Fluent Bit, CloudWatch agent) to forward to an immutable log store. Step-by-step guides for ELK, Loki, Splunk, and CloudWatch are in LOG_INTEGRATION.md.In transit: All S3 operations use HTTPS (TLS 1.2+) via boto3. Web dashboard traffic is TLS-terminated by nginx with TLS 1.2+ enforced, ECDHE cipher suites, HSTS (2-year, preload), and HTTP→HTTPS hard redirect. MongoDB TLS is configurable via MONGO_OPTIONS and warned on at startup when absent. At rest: S3 credentials stored in MongoDB are encrypted with Fernet (AES-128-CBC + HMAC-SHA256) via crypto_utils.py. Backup data encryption at rest requires SSE-KMS on the S3 bucket — a cloud-console configuration, not a code change.
aws s3api put-bucket-encryption --bucket <name> --server-side-encryption-configuration '{"Rules":[{"ApplyServerSideEncryptionByDefault":{"SSEAlgorithm":"aws:kms","KMSMasterKeyID":"<arn>"},"BucketKeyEnabled":true}]}'"s3:x-amz-server-side-encryption": "aws:kms" to deny unencrypted PutObject requests.tls=true&tlsCAFile=/path/to/ca.pem to MONGO_OPTIONS in .env. Atlas enforces TLS by default.The Docker deployment isolates the application on an internal bridge network (docker-compose.yml). The uvicorn process listens on port 8000 but is not exposed to the host; only the nginx container on the same internal network can reach it. External traffic enters only on ports 80 (immediate HTTP→HTTPS redirect) and 443 (HTTPS). Nginx sets server_tokens off, X-Frame-Options: SAMEORIGIN, X-Content-Type-Options: nosniff, and Strict-Transport-Security headers. The container runs as non-root user appuser.
Administrative functions (job CRUD, bucket CRUD, auth configuration page) are guarded by require_admin(). Operator-accessible pages (dashboard, history, monitor) use require_auth(). The CLI pipeline (cli.py, scanner.py, uploader.py) is fully decoupled from the web dashboard and does not share session state, reducing the attack surface of the management interface.
API request bodies are validated via Pydantic BaseModel schemas (JobCreateRequest, BucketCreateRequest, etc.) in main.py. job_manager.py validates directory existence, threshold ordering (warning < critical, all > 0), and exclude-dir containment before writing to MongoDB. Invalid inputs return HTTP 422 with structured error detail.
CRC32 checksums are computed for every file at scan time (scanner.py), stored in MongoDB, and written to S3 object metadata on upload (uploader.py). audit.py provides three independent integrity checks on demand: (1) latest snapshot existence, (2) cross-validation of MongoDB CRC32 vs S3 x-amz-meta-crc32 metadata without downloading, (3) random file download with full CRC re-computation. reconciler.py detects MongoDB/S3 key-set drift and optionally corrects it.
All production dependencies are pinned to minimum versions in requirements.txt (e.g. cryptography>=40.0.0, pymongo>=4.0.0). The Dockerfile uses a multi-stage build with python:3.12-slim to minimise the installed surface. Automated dependency scanning is a configuration task — no scanner runs in CI by default.
.github/dependabot.yml with package-ecosystem: pip to receive automated PRs for vulnerable dependencies.pip-audit -r requirements.txt in the CI pipeline to block builds with known CVEs.pip freeze > requirements.lock and build from the lockfile — reduces supply-chain drift between builds.Core purpose of the framework. Multi-job, multi-host backup pipeline stores every file as a plain S3 object at {host}/{absolute/path}. CRC32 metadata is stored alongside each object. The files.json.gz inventory is published to S3 after each run. S3 versioning and cross-region replication can be independently enabled on the bucket for additional redundancy.
restore.py provides multi-threaded restore with conflict handling, dry-run preview, and original path preservation. restore_onlys3.py provides an independent recovery path requiring only S3 credentials — no framework binary or MongoDB needed. reconciler.py detects and fixes consistency drift between MongoDB and S3 before or after a recovery event.
audit.py automates three-layer backup verification: snapshot catalogue check, S3 metadata CRC cross-validation, and random file download with CRC re-computation. Output is structured JSON suitable as evidence in a contingency plan test report. Documented as a weekly operational practice in USER_MANUAL.md.
No automated vulnerability scanner is included in the project. The codebase does ship with a full test suite (tests/) covering authentication, pipeline, compression, MongoDB, and restore+audit flows. Periodic dependency and container vulnerability scanning is a configuration task.
pip-audit or safety check as part of the CI pipeline on every merge to main.bandit -r . for Python security anti-patterns as a CI quality gate.The framework emits rich structured events for all operations (uploads, errors, reconciler drift, auth failures, config warnings). ALERTING.md defines alert rules and runbooks. The gap is that no monitoring pipeline is activated by default — the log stream must be connected to a SIEM or metrics platform to fulfil CA-7.
LOG_FILE in .env and ship logs to ELK, Loki, Splunk, or CloudWatch per LOG_INTEGRATION.md.ALERTING.md into your chosen platform. Key events to alert on: upload_error, reconciler_mismatch, mongo_tls_disabled, secret_fetch_failed, repeated auth failures.DeleteObject or GetObject calls outside approved IAM principals./login endpoint is health-checked every 30 s in the Docker Compose configuration — expose this to your monitoring platform.Risk assessments and System Security Plans (SSPs) are organisational artefacts outside the scope of the framework. The codebase supports the evidence-gathering process: audit.py --json produces machine-readable integrity evidence, all configuration warnings are structured log events, and the deployment documentation (SETUP.md, USER_MANUAL.md) provides an accurate description of controls implemented.
The framework surfaces incident-relevant signals (anomalous CRC mismatches, reconciler drift, auth failures, S3 SlowDown events) as distinct structured log events. Translating those signals into an incident response workflow — classification, escalation, notification, and post-incident review — is an organisational procedure. ALERTING.md provides the starting-point alert rules to feed into an incident management system.
Summary of how the framework maps to the six CSF 2.0 core functions.
| CSF Function | Coverage in this framework | Status |
|---|---|---|
| GV — Govern | Organisational policies, risk tolerance, and supply-chain governance are outside scope. Startup fail-fast validation and startup configuration warnings support governance by preventing misconfigured deployments. | Organisational |
| ID — Identify | Asset inventory via files.json.gz published to S3 after every run. Job and bucket registry in MongoDB provides a managed asset catalogue. Dependency list in requirements.txt enables software component identification. |
Satisfied |
| PR — Protect | RBAC (AC-3/AC-6), session timeout (AC-11), Fernet encryption (SC-28), TLS in transit (SC-8), HSTS + security headers (SC-7), non-root container execution, input validation (SI-10), secrets manager integration (IA-5). | Satisfied |
| DE — Detect | Structured JSON event log covers all operations; CRC mismatch events; reconciler drift detection; startup TLS and localhost warnings; ALERTING.md defines threshold alert rules. Monitoring pipeline must be connected operationally. |
Config required |
| RS — Respond | Framework surfaces incident signals; response workflow (classification, notification, containment) is organisational. delete.py / deleter.py supports containment by removing compromised objects. |
Organisational |
| RC — Recover | restore.py (full restore with integrity check), restore_onlys3.py (S3-only emergency path), reconciler.py (drift correction), audit.py (verification). Full recovery without framework dependencies is possible. |
Satisfied |
Actions required before a production deployment assessed against NIST SP 800-53 Rev. 5.
| # | Action | How resolved | Where | Control |
|---|---|---|---|---|
| 1 | Enable SSE-KMS with a customer-managed key on the S3 bucket | EXT | AWS / Cloudflare console | SC-28 |
| 2 | Enable TLS on MongoDB connection (MONGO_OPTIONS=tls=true&tlsCAFile=…) |
OPS | .env |
SC-8 |
| 3 | Store BACKUP_SECRET_KEY and MongoDB credentials in a secrets manager. Set SECRETS_PROVIDER in .env to activate one of the 6 built-in integrations. |
OPS | .env / secrets provider |
IA-5 |
| 4 | Set SESSION_TIMEOUT_SECONDS=900 in .env for a 15-minute inactivity lock (NIST moderate baseline) |
OPS | .env |
AC-11 |
| 5 | Enable S3 Object Lock (WORM) on the bucket for the required data-retention period | EXT | AWS / Cloudflare console | SI-7 / AU-9 |
| 6 | Forward log output to an immutable log store. Set LOG_FILE in .env and configure a log shipper per LOG_INTEGRATION.md. Set retention ≥ 1,095 days (3 years). |
OPS | .env / log shipper |
AU-9 / AU-11 |
| 7 | Enforce MFA for admin accounts at the IdP (AD FS / Azure AD) or via a VPN / identity-aware proxy | EXT | AD / proxy / VPN | IA-2(1) |
| 8 | Connect log stream to a SIEM and activate alert rules per ALERTING.md. Enable CloudTrail data events and S3 Access Logs. |
OPS | SIEM / AWS console | CA-7 |
| 9 | Add pip-audit or Dependabot to CI; scan container image with Trivy |
OPS | CI pipeline | RA-5 / SI-3 |
| 10 | Document a System Security Plan (SSP) referencing this report as technical control evidence | EXT | Organisational | PL-2 |
| Capability | How it supports NIST controls |
|---|---|
| Fernet encryption of S3 credentials | Satisfies SC-28 for credential data at rest. A MongoDB compromise alone cannot expose S3 access. Key loadable from 6 external secrets managers, keeping secrets off disk (IA-5). |
| Timing-safe authentication | secrets.compare_digest() in auth.py prevents timing-based credential enumeration — directly addresses IA-2 and IA-6 requirements. |
| Startup fail-fast validation | _validate_config() raises RuntimeError if SESSION_SECRET, credentials, or AD variables are absent. Prevents operating in a degraded security posture — supports CA-7 and CM-6. |
| Structured JSON audit log | Every privileged action records actor, resource, and UTC timestamp. Machine-parseable — directly ingestible by SIEM. Satisfies AU-2, AU-3, AU-12. |
| 3-check integrity audit | On-demand verifiable evidence that backup data is intact (SI-7). JSON output can be attached to CP-4 contingency plan test reports. |
| Non-root container execution | Container runs as appuser (Dockerfile); systemd service runs as backup:backup. Satisfies CM-7 least functionality and reduces privilege escalation risk. |
| Network isolation | Internal Docker bridge network; uvicorn unexposed to host; nginx enforces TLS with HSTS and security headers. Directly addresses SC-7 boundary protection. |
| S3-only emergency recovery | Files are plain objects at human-readable paths. No framework binary needed during recovery. Satisfies CP-10 reconstitution and CP-9 backup accessibility. |