From 14c2246c3c5514713991006aa7c0c6f673773e54 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 31 Jul 2026 08:24:37 +0000 Subject: [PATCH 1/5] docs: add a top-level Security page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The published documentation had no security page. What existed was a four-paragraph "Security" section inside Platform Support, covering per-trust API keys and payload encryption and nothing else — no mention of MFA, RBAC, the cohort-query boundary, the cloud posture, FL transport security, or the supply chain. A partner trust assessing FLIP had nowhere to look. Adds `security.rst` at the end of the Documentation toctree, describing the controls layer by layer: network and perimeter, cloud infrastructure, identity and access, the clinical data boundary, federated learning, data in transit and at rest, disclosure control in error paths, supply chain and change control, and how to report a vulnerability. Removes the old section from Platform Support. Beyond being superseded, it had gone stale: it stated that payloads are encrypted with a shared `AES_KEY_BASE64`, which no longer describes the scheme. Platform Support keeps Networking, Backup/Restore and Access, which are operator-facing rather than assurance-facing. Two things are stated deliberately rather than glossed, because the audience is information governance: - The FL update filter is described as statistical clipping and sparsification, and explicitly **not** as differential privacy — there is no calibrated noise and no privacy budget. Presenting it as DP to an ethics committee would misstate it. - Researcher-supplied training code runs on trust hardware with access to that trust's data. That is inherent to federated learning, so the page says so plainly and then sets out the controls around it. The page carries no file paths, line numbers, or internal finding identifiers, so it is safe to share with external stakeholders. Cross-references the existing on-prem deployment guide for trust firewall rules and the RBAC role matrix for the full permission table. Sphinx builds clean — the eight remaining warnings are pre-existing autoapi issues in generated files, unrelated to this page. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01UY3k5789vkwUEL613TKxjU Signed-off-by: Claude --- docs/source/index.rst | 1 + docs/source/security.rst | 246 ++++++++++++++++++ .../sys-admin/admin-platform-support.rst | 8 - 3 files changed, 247 insertions(+), 8 deletions(-) create mode 100644 docs/source/security.rst diff --git a/docs/source/index.rst b/docs/source/index.rst index 85969609c..816e76267 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -16,6 +16,7 @@ Federated Learning Interoperability Platform sys-admin user-guides working-with-flip-apps + security .. toctree:: :maxdepth: 2 diff --git a/docs/source/security.rst b/docs/source/security.rst new file mode 100644 index 000000000..3bbc41c3a --- /dev/null +++ b/docs/source/security.rst @@ -0,0 +1,246 @@ +.. _security: + +######## +Security +######## + +This page describes how FLIP addresses security at each layer of the platform: the +network, the cloud estate the Central Hub runs on, the identity of every user and +service, the boundary around clinical data, the federated learning process itself, the +storage and transport of everything in between, and the software supply chain the +platform is built from. + +It is written for partner trusts, information governance leads, and anyone assessing +FLIP before deploying it. It deliberately contains no exploitation detail. + +FLIP is built on a single principle: **patient data does not leave the hospital that +holds it.** Models travel to the data, results come back aggregated, and every control +described below exists to keep that boundary intact. + +FLIP is open source under Apache 2.0 — the code, its full change history, and the +automated checks that run against every change are publicly inspectable. + +********************* +Network and perimeter +********************* + +**Trust systems accept no inbound connections.** Each participating trust runs FLIP +services that reach *out* to the Central Hub to collect work and report results. +Nothing on the internet can open a connection to a trust's FLIP services. This is +enforced in the infrastructure definitions themselves — the security groups permit no +inbound traffic at all — rather than depending on configuration discipline. +Operator access is via AWS Systems Manager Session Manager, so port 22 is never +opened. + +**Only the Central Hub is internet-facing.** It sits behind CloudFront with modern TLS, +HSTS, AWS WAF managed rules, and an internal-only Application Load Balancer. Nothing +else in the platform is reachable from the public internet. + +**A site-to-site VPN is available on request.** Trust-to-hub traffic is encrypted in +transit by default, and payloads carry their own authenticated encryption on top of +that. Where a trust's own policy calls for network-layer separation as well, a +site-to-site VPN between the trust's network and the hub VPC can be provisioned, +carrying all outbound polling and FL client traffic. + +See :ref:`deploy-flip-node-on-prem` for the firewall rules required at a trust host. + +******************** +Cloud infrastructure +******************** + +The Central Hub runs on AWS, architected to align with AWS's own reference guidance +for regulated workloads. + +**The estate is being consolidated onto the AWS Landing Zone Accelerator** — AWS's +reference implementation for organisations with elevated compliance requirements, and +the pattern AWS recommends for healthcare and public sector workloads. It provides +account separation, centrally managed guardrails, consistent logging and encryption +baselines, and a controlled path for images and dependencies entering the environment, +applied uniformly across environments rather than configured per service. + +Within that estate: + +- **Least-privilege identity.** Each service has its own IAM role scoped to named + resources rather than wildcards, so compromise of one component grants nothing + beyond that component's own function. +- **Private by default.** Compute runs in private subnets with no public IP addresses. +- **Encryption under managed keys.** S3 storage, RDS, and EBS volumes are encrypted, + under a customer-managed KMS key where the data is FLIP's own. +- **No standing database credential.** The production database is reached through RDS + Proxy using a short-lived IAM authentication token minted per connection, so there is + no long-lived password to leak or rotate. +- **Infrastructure as code.** The environment is defined in Terraform/OpenTofu and + validated automatically in CI, so what is deployed matches what was reviewed. +- **No static cloud credentials in automation.** CI authenticates to AWS with + short-lived federated tokens rather than stored access keys. + +******************* +Identity and access +******************* + +**Authentication is layered.** Sign-in uses AWS Cognito with the SRP password protocol, +which never transmits the password itself. Access tokens are verified on every request: +the signature algorithm is pinned, the issuer and audience are checked, and ID tokens +presented in place of access tokens are rejected. + +**Multi-factor authentication is mandatory.** MFA is enforced at the application +boundary on *every* authenticated request, not only at the moment of login, so a +session cannot outlive the requirement. A user who has not enrolled cannot reach any +protected function, and an administrative MFA reset takes effect immediately. + +**Access is role-based and default-deny.** Users hold one of three defined roles — +**Admin**, **Researcher**, or **Viewer** — each carrying an explicit permission set. +Permission is granted rather than assumed: a request with no matching grant is refused. +Role membership alone is not sufficient for project data, which additionally requires +membership of that specific project, re-checked on every access. Credential comparisons +are constant-time, so response timing cannot be used to guess a secret. Access is +reviewed annually and dormant accounts are removed. For the full permission matrix, see +:ref:`rbac-roles`. + +**Role separation continues inside the trust.** Access control is not only a hub +concern. XNAT enforces its own roles, and the ability to query and retrieve from the +trust PACS is restricted to the FLIP service account and to accounts explicitly granted +the DQR role. An ordinary XNAT account cannot pull imaging from PACS — retrieval happens +as part of an approved project's data import, not on demand by an individual user. A +FLIP role therefore never becomes an implicit route into the trust's wider imaging +estate. + +**Machine-to-machine access is separately controlled.** Each trust authenticates to the +hub with its own ``TRUST_API_KEY``, of which the hub stores only a SHA-256 hash. +Services within a trust authenticate to one another with a per-trust +``TRUST_INTERNAL_SERVICE_KEY`` that never reaches the Central Hub, compared in constant +time. Credentials compromised at one trust cannot be replayed against another. + +************************** +The clinical data boundary +************************** + +Cohort queries execute inside the trust, against the trust's own OMOP database. Several +independent controls would each have to fail before anything unintended could execute: + +- the query runs as a read-only database role with DML and DDL revoked — the database + itself refuses to write, regardless of what the query says; +- the query is parsed with ``sqlglot`` and re-emitted from its parsed form before + execution, which breaks the injection taint chain; +- only read-only statement types are permitted, decided from the parsed AST rather than + by scanning for banned keywords; +- multiple statements bundled into one request are rejected; +- queries are pinned to the ``omop`` schema, with ``LIMIT``/``OFFSET`` restricted to + literal values; +- results below ``COHORT_QUERY_THRESHOLD`` are suppressed, and a genuine zero is + deliberately indistinguishable from a small suppressed count, so a response cannot + reveal that a handful of patients matched; +- cached results are scoped to the requesting project and expire in minutes, so no + project is served another's data and no result outlives a withdrawal of consent or a + correction to a record. + +This is achieved **without restricting researchers to a fixed menu of queries** — +arbitrary analytical SQL remains available. The constraint is on the shape and privilege +of the query, not on the questions that may be asked. + +****************************** +Federated learning and privacy +****************************** + +Federated learning is what makes FLIP possible: models travel to the data rather than +the reverse. + +**Researcher-supplied training code runs on trust hardware with access to that trust's +data.** That is the nature of federated learning, and it is why the surrounding controls +matter. Model files are checked before use; the container that runs researcher code is +hardened; FL clients deliberately hold **no Central Hub credentials**, so compromising +one yields no access to the wider platform. + +**FL traffic is mutually authenticated.** Both supported backends — NVIDIA FLARE and +Flower — run over TLS with per-participant certificates issued during network +provisioning, so the FL server and each client authenticate *each other* rather than one +side trusting the network. A client cannot join a training network without a valid +provisioned identity. + +**Model updates are filtered before they leave a trust.** A privacy filter is applied to +training updates by default, so the aggregating server never sees a raw update. For +governance purposes this should be described precisely: it is **statistical clipping and +sparsification of model updates, not formal differential privacy** — there is no +calibrated noise and no privacy budget. It is a meaningful protection and it is on by +default, but describing it as differential privacy to an ethics committee or information +governance panel would misstate it. Formally differentially private aggregation is on the +roadmap. + +*************************** +Data in transit and at rest +*************************** + +**All traffic is encrypted in transit.** Every connection between a trust and the +Central Hub runs over HTTPS, outbound from the trust only. On top of that transport +encryption, task payloads carry their own **authenticated encryption**: any tampering +with a message causes decryption to fail outright rather than passing silently. Payload +keys carry key identifiers, and each trust has its own key, so a compromise at one trust +does not expose the traffic of any other. + +**At rest**, model and results storage uses S3 with managed encryption under a +customer-managed KMS key, versioning, blocked public access, HTTPS-only bucket policies, +and access logging. RDS storage and EC2 root volumes are encrypted, and database +connections require TLS on both hops. + +**Access links expire.** Pre-signed URLs used to upload and download model files are +time-limited, with a hard ceiling enforced centrally, because such a link is a +capability against the bucket in either direction. + +**Credentials are designed to be rotated.** A trust's API key, its trust-internal +service key, and its payload-encryption key are issued at registration and can be +re-issued without redeploying the platform. Payload keys carry identifiers precisely so +that a new key can be introduced and an old one retired without a synchronised cutover. + +**The trust imaging archive requires authentication to start.** Orthanc will not run +without credentials configured, an automated check verifies that authentication is +actually enforced before any new image is published, and interfaces with no consumer are +not enabled. + +********************************** +Diagnostics and disclosure control +********************************** + +Error messages are a quiet disclosure route: an unhandled error can return database +structure, internal hostnames, or fragments of a failing query to whoever triggered it. + +FLIP returns a fixed message accompanied by a **correlation identifier**. The identifier +is generated server-side, never accepted from the caller, and recorded alongside the full +technical detail in the internal logs. A user who encounters an error quotes that +identifier and an engineer can find exactly what happened — diagnosability is preserved +without disclosing internals. Automated checks in CI prevent raw exception text from +reappearing in responses, and logging is scoped so that secrets, pre-signed URLs, and +query text are not written out in the first place. + +**************************************** +Software supply chain and change control +**************************************** + +- **Secret scanning** runs on every push and pull request, with a scheduled + full-repository sweep, mirrored by pre-commit hooks that run before a commit is + created. +- **A 72-hour dependency cooldown** prevents newly published third-party packages being + adopted immediately — a direct defence against compromised-package attacks — enforced + by ``uv`` and ``npm`` configuration and backstopped by a CI gate. +- **Automated dependency vulnerability alerting** is enabled. +- **Container images publish only after their tests pass**, so a failing build cannot + become a deployable artefact, and deployments pin immutable commit-sha tags rather + than moving labels. +- **Every change is peer-reviewed**, with automated acceptance checks, DCO sign-off, and + a protected mainline. +- **Infrastructure is defined as code** and validated automatically in CI. + +************************************* +Assurance and vulnerability reporting +************************************* + +FLIP is subject to independent security review and to a commissioned penetration test, +with findings tracked and re-verified rather than left to age. Automated security +checking runs continuously in the delivery pipeline, so regressions are caught at the +point of change. + +Vulnerability reports are welcome. FLIP publishes a security policy with a private +reporting route and a coordinated disclosure process — see |SECURITY.md|_ in the +repository. Please do not open a public issue for a suspected vulnerability. + +.. |SECURITY.md| replace:: ``SECURITY.md`` +.. _SECURITY.md: https://github.com/londonaicentre/FLIP/blob/main/SECURITY.md diff --git a/docs/source/sys-admin/admin-platform-support.rst b/docs/source/sys-admin/admin-platform-support.rst index 1f218becc..aad5f1a42 100644 --- a/docs/source/sys-admin/admin-platform-support.rst +++ b/docs/source/sys-admin/admin-platform-support.rst @@ -2,14 +2,6 @@ Platform Support ################ -********* -Security -********* - -Trusts authenticate to the Central Hub using per-trust API keys. The hub identifies a trust solely by its API key — it looks up the trust whose stored SHA-256 hash (the ``api_key_hash`` column of the ``trust`` table) matches the presented key. Each trust holds its secret ``TRUST_API_KEY`` in its own environment. Trust communication payloads are encrypted with a shared ``AES_KEY_BASE64``. - -See :ref:`deploy-flip-node-on-prem` for details on trust provisioning and authentication setup. - *********** Networking *********** From 9fc10e5a91ff994587b51ce2c4c7a7a51625f0dd Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 31 Jul 2026 08:35:41 +0000 Subject: [PATCH 2/5] docs: cross-link User Roles, trim the Networking section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two follow-ups from review of the new Security page. **User Roles.** The roles sentence now links to the permission matrix at the point the three roles are named, rather than deferring to a pointer at the end of the paragraph — a reader who wants the detail is looking for it right there. Uses the existing `rbac-roles` cross-reference so it resolves in every build, including the PR preview. **Networking in Platform Support** repeated the outbound-only posture three times across the section and again in the ports preamble, most of which the Security page now covers. Trimmed to one paragraph of substance plus a pointer, keeping everything operational: the architecture figure, the ports table, the SSM port-forwarding command, and the fact that port 22 is never opened. The VPN paragraph also said the tunnel "is not yet implemented — current deployments rely on HTTPS over the public internet — but is planned as part of the production rollout", which no longer matches: it is provisioned on request. Reworded accordingly, and phrased so it does not imply every deployment has one. Sphinx builds clean; verified both new cross-links resolve in the rendered HTML and the ports table is intact. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01UY3k5789vkwUEL613TKxjU Signed-off-by: Claude --- docs/source/security.rst | 14 +++++----- .../sys-admin/admin-platform-support.rst | 26 +++++++++---------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/docs/source/security.rst b/docs/source/security.rst index 3bbc41c3a..b2f0a6547 100644 --- a/docs/source/security.rst +++ b/docs/source/security.rst @@ -89,13 +89,13 @@ session cannot outlive the requirement. A user who has not enrolled cannot reach protected function, and an administrative MFA reset takes effect immediately. **Access is role-based and default-deny.** Users hold one of three defined roles — -**Admin**, **Researcher**, or **Viewer** — each carrying an explicit permission set. -Permission is granted rather than assumed: a request with no matching grant is refused. -Role membership alone is not sufficient for project data, which additionally requires -membership of that specific project, re-checked on every access. Credential comparisons -are constant-time, so response timing cannot be used to guess a secret. Access is -reviewed annually and dormant accounts are removed. For the full permission matrix, see -:ref:`rbac-roles`. +**Admin**, **Researcher**, or **Viewer** — each carrying an explicit permission set (see +:ref:`User Roles ` for the full permission matrix). Permission is granted +rather than assumed: a request with no matching grant is refused. Role membership alone +is not sufficient for project data, which additionally requires membership of that +specific project, re-checked on every access. Credential comparisons are constant-time, +so response timing cannot be used to guess a secret. Access is reviewed annually and +dormant accounts are removed. **Role separation continues inside the trust.** Access control is not only a hub concern. XNAT enforces its own roles, and the ability to query and retrieve from the diff --git a/docs/source/sys-admin/admin-platform-support.rst b/docs/source/sys-admin/admin-platform-support.rst index aad5f1a42..eb506c3f8 100644 --- a/docs/source/sys-admin/admin-platform-support.rst +++ b/docs/source/sys-admin/admin-platform-support.rst @@ -6,28 +6,26 @@ Platform Support Networking *********** -All trust communication is **outbound** — trusts poll the Central Hub for tasks over HTTPS (via the ALB). -The hub never makes inbound connections to trusts. FL clients connect outbound to the FL server via the NLB. -No inbound firewall rules or port forwarding are required on trust hosts. +All trust communication is **outbound** — trusts poll the Central Hub for tasks over HTTPS +(via the ALB), and FL clients connect outbound to the FL server via the NLB. The hub never +makes inbound connections to trusts, so no inbound firewall rules or port forwarding are +required on trust hosts. Operator access is via AWS Systems Manager Session Manager +(SSH-over-SSM); XNAT, Orthanc, and the trust-api Swagger docs are reachable only through SSM +port forwarding (``make forward-trust``). -Both the Central Hub and Trust EC2 instances run in private subnets with no open inbound ports. -Operator access is via AWS Systems Manager Session Manager (SSH-over-SSM). -XNAT, Orthanc, and the Trust API swagger docs are accessible via SSM port forwarding only (``make forward-trust``). +Trust-to-hub traffic can additionally be carried over a site-to-site VPN between the trust +network and the Central Hub VPC, provisioned on request rather than by default. -In production, trust-to-hub communication will be carried over a site-to-site VPN between each Trust's network and the Central Hub VPC, providing an -encrypted tunnel for all outbound polling and FL client traffic in addition to the application-layer protections described above. This is not yet implemented -— current deployments rely on HTTPS over the public internet — but is planned as part of the production rollout. +See :ref:`security` for the rationale behind this design and the wider set of controls it +sits within. The operational detail — the architecture and the ports to open — follows here. .. figure:: ../assets/support/flip_architecture-flip_network_architecture.png :align: center FLIP network architecture. -The following is the list of ports required to be opened for trust-host communication. No inbound -ports are required on trust hosts; everything trust-side is outbound HTTPS to the Central Hub or -to the FL server NLB. Operator access is via AWS Systems Manager Session Manager — port 22 (SSH) -is never opened. Internal trust services (Orthanc, XNAT, trust-api Swagger) are accessible only -via SSM port forwarding (``make forward-trust``). +The ports required for trust-host communication are listed below. Port 22 (SSH) is never +opened. .. list-table:: Firewall Rules :header-rows: 1 From b0e5ad19afbe1acb0a7ede907a5da18bade1de44 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 31 Jul 2026 08:41:41 +0000 Subject: [PATCH 3/5] docs: fix the eight Sphinx warnings from generated API docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The docs build carried eight warnings, all from docstrings that autoapi renders as reStructuredText but which were written as if they were Markdown. Three source docstrings account for all eight — the constants one is counted twice because autoapi emits both a module page and a package page from it. - `flip/constants/flip_constants.py` — a bullet list with no blank line before it, so RST read the continuation line as an unexpected indent inside a paragraph (*Unexpected indentation*, *Block quote ends without a blank line*). Added the blank line; it now renders as an actual list. - `flip/flower/metrics.py` — an indented usage example after a plain `:` made the preceding line a definition-list term (*Definition list ends without a blank line*). Changed to `::` so it is an explicit literal block. - `flip_api/scripts/delete_trust.py` — a Markdown pipe table. RST parses leading `|` as a line block and reads `|---...---|` as a substitution reference, which produced the two *Undefined substitution referenced* errors and the *Line block ends without a blank line* warning. Moved into a literal block, which keeps the table readable both in the source and on the rendered page. All are comment-only changes; no behaviour is affected. `make -C docs docs` now reports **build succeeded** with no warnings, and the three sites were checked in the rendered HTML rather than only in the build log. Verified: ruff clean on all three files, mypy clean on `delete_trust.py`, and the flip-api tests covering it pass. flip-utils' suite could not run here — its dependencies are not installed in this environment and cannot be fetched — but both files parse and ruff is clean, and autoapi re-parsed them to regenerate the docs. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01UY3k5789vkwUEL613TKxjU Signed-off-by: Claude --- flip-api/src/flip_api/scripts/delete_trust.py | 26 +++++++++---------- flip-utils/flip/constants/flip_constants.py | 1 + flip-utils/flip/flower/metrics.py | 2 +- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/flip-api/src/flip_api/scripts/delete_trust.py b/flip-api/src/flip_api/scripts/delete_trust.py index 37ed1b012..875c00b52 100644 --- a/flip-api/src/flip_api/scripts/delete_trust.py +++ b/flip-api/src/flip_api/scripts/delete_trust.py @@ -20,19 +20,19 @@ script clears each dependent table in the right order before deleting the trust row. -Dependent tables (from flip_api.db.models.main_models): - -| Table | FK field | Strategy | -|--------------------------|---------------------------|-----------------| -| fl_kit_slot | assigned_to_trust_id | NULL the FK | -| fl_job_trust | trust_id (PK part) | DELETE rows | -| fl_metrics | trust (NOT NULL) | DELETE rows | -| fl_logs | trust (nullable) | DELETE rows | -| model_trust_intersect | trust_id (nullable) | DELETE rows | -| project_trust_intersect | trust_id (nullable) | DELETE rows | -| query_result | trust_id (nullable) | DELETE rows | -| trust_task | trust_id (NOT NULL) | DELETE rows | -| xnat_project_status | trust_id (nullable) | DELETE rows | +Dependent tables (from flip_api.db.models.main_models):: + + | Table | FK field | Strategy | + |--------------------------|---------------------------|-----------------| + | fl_kit_slot | assigned_to_trust_id | NULL the FK | + | fl_job_trust | trust_id (PK part) | DELETE rows | + | fl_metrics | trust (NOT NULL) | DELETE rows | + | fl_logs | trust (nullable) | DELETE rows | + | model_trust_intersect | trust_id (nullable) | DELETE rows | + | project_trust_intersect | trust_id (nullable) | DELETE rows | + | query_result | trust_id (nullable) | DELETE rows | + | trust_task | trust_id (NOT NULL) | DELETE rows | + | xnat_project_status | trust_id (nullable) | DELETE rows | For the nullable FKs we could either NULL or DELETE — DELETE makes the intent clearer (a metric/log/result tied to a now-gone trust serves no diff --git a/flip-utils/flip/constants/flip_constants.py b/flip-utils/flip/constants/flip_constants.py index 571f3fefe..f5f74539c 100644 --- a/flip-utils/flip/constants/flip_constants.py +++ b/flip-utils/flip/constants/flip_constants.py @@ -47,6 +47,7 @@ class ProdSettings(_Common): """Production environment configuration. Used when LOCAL_DEV=false. Settings are grouped by which FL role uses them: + - **Server-only** (fl-server on Central Hub): FLIP_API_INTERNAL_URL, INTERNAL_SERVICE_KEY* - **Client-only** (fl-client on trust side): DATA_ACCESS_API_URL, IMAGING_API_URL, TRUST_INTERNAL_SERVICE_KEY* diff --git a/flip-utils/flip/flower/metrics.py b/flip-utils/flip/flower/metrics.py index 1ddaa25b0..2a346a2d3 100644 --- a/flip-utils/flip/flower/metrics.py +++ b/flip-utils/flip/flower/metrics.py @@ -19,7 +19,7 @@ Only the fl-server should import from this module — it forwards to the Central Hub using credentials that must never reach the fl-client containers. -Usage (server-side, in a FedAvg strategy subclass): +Usage (server-side, in a FedAvg strategy subclass):: from flip.flower.metrics import handle_client_metrics, handle_client_exception From cb7a61a4b28dabc2b084697c9249e13b991f5884 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 31 Jul 2026 09:06:37 +0000 Subject: [PATCH 4/5] docs: add a top-level Governance and compliance page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Companion to the Security page, at the end of the Documentation toctree. Security answers "what protects this"; this answers "who decides, and how does it map onto the frameworks we are assessed against" — a different question, asked by a different reader (information governance and Caldicott functions, ethics committees, commissioners). Covers the governance properties that are not obvious from the code: - **Per-project, per-trust veto.** Approval is not a blanket agreement to participate. A trust approves each project individually, the decision runs on its own deployment so it cannot be bypassed, and declining excludes that trust without affecting the rest of the federation. - **Data residency** — what actually crosses the boundary in each direction, stated explicitly, since it is the first question an IG function asks. - **National Data Opt-Out** — applied by periodic reconciliation of the OMOP dataset rather than in the query layer, so it propagates to every query the platform can make. Two caveats are recorded rather than glossed: reconciliation is periodic rather than per-query, so the interval is the control question; and the platform inherits the decision rather than enforcing it. - **Network architecture as a governance guarantee** — kept short and pointed at the Security page for the mechanism, rather than restating it. - Access governance and independent assurance. Three appendices map the platform against **Cyber Essentials**, the **NHS DSPT** (via the National Data Guardian's ten standards), and the **NCSC Cyber Assessment Framework**, as `list-table`s with an explicit status column. Statuses distinguish controls that are the platform's from those that belong to the deploying trust, and from those that are shared — an all-green mapping would be misleading, since several of these standards are genuinely organisational. Cross-references Security, User Roles, and the on-prem deployment guide rather than duplicating them. Sphinx builds clean with no warnings; verified the six tables render and all three cross-links resolve. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01UY3k5789vkwUEL613TKxjU Signed-off-by: Claude --- docs/source/governance-and-compliance.rst | 378 ++++++++++++++++++++++ docs/source/index.rst | 1 + 2 files changed, 379 insertions(+) create mode 100644 docs/source/governance-and-compliance.rst diff --git a/docs/source/governance-and-compliance.rst b/docs/source/governance-and-compliance.rst new file mode 100644 index 000000000..c1399ed4c --- /dev/null +++ b/docs/source/governance-and-compliance.rst @@ -0,0 +1,378 @@ +.. _governance-and-compliance: + +######################### +Governance and compliance +######################### + +This page describes who decides what in FLIP, and how the platform maps onto the +assurance frameworks NHS organisations are held to. It is written for information +governance and Caldicott functions, research ethics committees, and anyone assessing +FLIP on behalf of a participating organisation. + +Its companion is :ref:`security`, which describes the technical controls at each layer. +Where a governance guarantee rests on a technical control, this page states the +guarantee and links there for the mechanism. + +FLIP is designed so that **each participating organisation keeps control of its own +data**. Patient data never leaves the trust that holds it. No project can use a trust's +data until that trust has explicitly approved that project. A trust can decline any +individual project without leaving the federation and without affecting any other +participant. Nothing in the platform can override that decision, because the data and +the approval both live on the trust's own infrastructure. + +***************************************** +Each site can veto any individual project +***************************************** + +Approval in FLIP is **per project, per trust** — not a blanket agreement to +participate. + +When a research project is created, it must be approved separately by an administrator +at each trust whose data it proposes to use. Approval is recorded against that specific +project–trust pairing, timestamped and attributed. A trust that declines is simply not +included: the project proceeds with the trusts that approved it, and the declining +trust's data is never queried, never imported, and never contributes to the model. + +Three consequences matter for information governance: + +- **Participation is not all-or-nothing.** A trust can support one research question and + decline another — on clinical, ethical, or capacity grounds — without renegotiating + its involvement in the platform. +- **The decision sits with the data holder.** The approval gate runs on the trust's own + deployment, so it cannot be bypassed by the Central Hub or by another participant. +- **The decision is auditable.** Who approved what, and when, is recorded and can be + produced for an audit or an ethics review. + +************************************** +Data residency and what actually moves +************************************** + +**Patient data stays in the trust.** Imaging retrieved for a project is held in the +trust's own XNAT instance. Cohort queries execute against the trust's own OMOP database. +Neither is copied to the Central Hub. + +**What leaves a trust** is limited to: aggregate cohort statistics, subject to a minimum +group size below which results are suppressed; model updates produced during federated +training, which pass through a privacy filter before transmission; and operational +telemetry such as task status. Model updates are mathematical parameters, not records. + +**What enters a trust** is the definition of work to be done — an approved project, a +cohort query, and the model application to train — and nothing else. The hub cannot read +into a trust: trusts poll outbound and the hub has no route back in. + +The controls that constrain a cohort query before it runs, and the suppression threshold +applied to its results, are described under :ref:`security`. + +********************************************** +Network architecture as a governance guarantee +********************************************** + +The network design is why the guarantees above are structural rather than procedural. +Trust systems accept no inbound connections: each trust polls the Central Hub outbound, +there are no inbound firewall rules to open, and there is no route from the internet — or +from the hub — into a trust's network. For a trust's own network team, onboarding FLIP +requires no inbound exposure at all. A site-to-site VPN can be provisioned on request +where a trust's policy calls for network-layer separation as well. + +The practical governance point: a trust does not have to rely on the Central Hub's access +controls to be confident its systems are unreachable. There is no path. + +See :ref:`security` for the detail, and :ref:`deploy-flip-node-on-prem` for the outbound +ports a trust host needs. + +********************* +National data opt-out +********************* + +The National Data Opt-Out applies to the use of confidential patient information for +research and planning, and each organisation is responsible for applying it to its own +data. + +**In FLIP this is applied where the data is prepared — in the OMOP database — rather than +in the platform's query layer.** The production OMOP database is periodically reconciled +against the national opt-out register, so patients who have opted out are removed from +the dataset FLIP is able to see. + +Because FLIP queries only what that database contains, an opt-out applied there +propagates automatically: to every subsequent cohort query, every project, and every +model trained thereafter. There is no separate platform-side list to keep in step, and no +route by which a project can reach data the extract has excluded. + +Two points an information governance function will want to record explicitly: + +- **Reconciliation is periodic, not per-query.** The control question is therefore the + reconciliation interval, which should be agreed with each trust and documented + alongside the data flow. +- **The platform inherits the decision rather than enforcing it.** FLIP holds no + independent view of opt-out status; its guarantee is the narrower one that it can only + ever see what the OMOP extract contains. This is a deliberate separation of concerns — + opt-out is a property of the dataset, applied once, rather than a filter each consuming + system has to reimplement correctly. + +Where a trust wishes to go further, the per-project approval gate is a second control +point: a project can be declined outright if its purpose falls outside what the trust's +patients have been informed of. + +***************** +Access governance +***************** + +**Role-based access control.** Users hold one of three defined roles — Admin, +Researcher, or Viewer — each with an explicit permission set, applied default-deny. +Access to a specific project's data additionally requires membership of that project. +See :ref:`User Roles ` for the full permission matrix. + +**Multi-factor authentication is mandatory** for every authenticated user, enforced on +every request rather than only at login. + +**Access review.** Access is granted by a FLIP administrator, reviewed annually, and +dormant accounts are removed. + +**Separation from the trust's wider estate.** A FLIP role does not confer access to the +trust's broader imaging systems. Retrieval from the trust PACS is restricted to the FLIP +service account and to accounts explicitly granted the DQR role; an ordinary XNAT account +cannot pull imaging on demand. + +**Audit.** Administrative actions, project approvals, and role changes are recorded. + +********************************** +Assurance and independent scrutiny +********************************** + +- FLIP is subject to **independent security review** and to a **commissioned penetration + test**, with findings tracked in a live register carrying an owner and a status for + each item, and re-verified rather than left to age. +- A **published security policy** provides a private reporting route and a coordinated + disclosure process. +- **Automated security checking** runs continuously in the delivery pipeline, so + regressions are caught at the point of change. +- FLIP is **open source under Apache 2.0** — the code, its change history, and its + automated checks are publicly inspectable, which is an unusually direct form of + supplier assurance. + +.. _compliance-mappings: + +***************************** +Appendix A — Cyber Essentials +***************************** + +.. list-table:: + :header-rows: 1 + :widths: 18 57 25 + + * - Theme + - How FLIP addresses it + - Status + * - **1. Firewalls and internet gateways** + - Trust environments expose no inbound ports and poll outbound only; services run in + private subnets with no public address; a single hardened CloudFront edge fronts + the hub, with an internal-only load balancer behind it; AWS WAF managed rules are + deployed. + - Operational (WAF rules moving from monitoring to enforcement) + * - **2. Secure configuration** + - Infrastructure defined as code with automated validation; immutable, pinned image + versions rather than mutable tags; insecure conveniences disabled in production, + including API documentation endpoints; configuration drift is detectable rather + than silent; default credentials removed from deployment templates, with services + refusing to start on a weak or unset credential. + - Operational + * - **3. Security update management** + - Automated dependency vulnerability monitoring; a 72-hour supply-chain cooldown on + newly published packages; prompt remediation of disclosed vulnerabilities; + releases gated on passing automated tests. + - Operational + * - **4. User access control** + - Managed identity provider with full token verification; MFA enforced on every + authenticated request; default-deny role-based access control across three defined + roles; per-project membership checks; least-privilege per-service cloud roles; + per-trust credential scoping; PACS retrieval restricted to service accounts and + explicitly granted roles; brokered, audited operator access with no exposed + remote-login port. + - Operational + * - **5. Malware and code protection** + - Uploaded model files are checked before use; the containers that execute training + workloads are isolated and hardened; automated secret scanning on every change; + supply-chain cooldown; cloud-native runtime threat detection. + - Operational; researcher-code review gate in delivery + +***************************************************** +Appendix B — NHS Data Security and Protection Toolkit +***************************************************** + +Mapped against the National Data Guardian's ten Data Security Standards. + +.. list-table:: + :header-rows: 1 + :widths: 20 55 25 + + * - NDG standard + - How FLIP addresses it + - Status + * - **1. Personal confidential data** + - The federated architecture means patient data never leaves the trust that holds + it; per-trust project approval enforces need-to-know; per-trust isolation is + structural, not procedural. The National Data Opt-Out is applied by periodic + reconciliation of the OMOP dataset, so it propagates to every query the platform + can make. + - Operational + * - **2. Staff responsibilities** + - An organisational responsibility of each deploying trust; the platform supports it + with defined roles, RBAC, and audit trails. + - Organisational (platform supports) + * - **3. Training** + - An organisational responsibility of each deploying trust; not a platform control. + - Organisational + * - **4. Managing data access** + - Default-deny RBAC with three defined roles; MFA on every request; per-trust, + per-project approval with timestamped audited decisions; immediate effect on + administrative MFA reset; least-privilege service accounts; PACS retrieval + restricted at the trust imaging platform. + - Operational + * - **5. Process reviews** + - A tracked security remediation programme with post-fix verification; independent + penetration testing; audit trails that support post-incident review. + - Operational + * - **6. Responding to incidents** + - A published coordinated disclosure and private advisory workflow with a defined + acknowledgement window; audit trails to support investigation. + - Operational + * - **7. Continuity planning** + - Versioned storage, immutable image versions, and infrastructure redeployable from + code; trust-side operational continuity remains an organisational responsibility. + - Shared with organisation + * - **8. Unsupported systems** + - Continuous dependency monitoring, supply-chain cooldown, prompt vulnerability + remediation, and explicit version pinning of platform components. + - Operational + * - **9. IT protected from cyber threats** + - Layered automated scanning and independent penetration testing; least-privilege + cloud design; encryption in transit and at rest; runtime threat detection; an AWS + Landing Zone Accelerator foundation. + - Operational + * - **10. Accountable suppliers** + - Transparent, documented controls; an open-source codebase; a published security + policy — supporting supplier-assurance due diligence between FLIP and partner + organisations. + - Supports organisational process + +******************************************** +Appendix C — NCSC Cyber Assessment Framework +******************************************** + +Objective A — Managing security risk +==================================== + +.. list-table:: + :header-rows: 1 + :widths: 20 55 25 + + * - CAF principle + - How FLIP addresses it + - Status + * - **A1 Governance** + - Documented security rules and engineering conventions; named ownership; a live + tracked remediation programme; a published coordinated disclosure policy. + - Operational + * - **A2 Risk management** + - Independent penetration testing with severity-prioritised, tracked, and verified + remediation; recurring independent review. + - Operational + * - **A3 Asset management** + - Infrastructure defined as code serves as the authoritative inventory; immutable + pinned image versions; dependency lockfiles act as a software bill of materials. + - Operational + * - **A4 Supply chain** + - Automated dependency vulnerability monitoring; 72-hour cooldown on new packages; + lockfile-pinned builds; curated base application templates. + - Operational; researcher-code review gate in delivery + +Objective B — Protecting against cyber attack +============================================= + +.. list-table:: + :header-rows: 1 + :widths: 20 55 25 + + * - CAF principle + - How FLIP addresses it + - Status + * - **B1 Policies and processes** + - A secure development lifecycle; peer-reviewed changes with automated acceptance + checks; contributor sign-off; security rules enforced by tooling rather than + convention. + - Operational + * - **B2 Identity and access control** + - Managed identity provider with token verification; MFA on every authenticated + request; default-deny RBAC; per-project membership checks; per-trust credential + scoping; PACS retrieval restricted to service accounts and granted roles; brokered + operator access with no exposed remote-login port. + - Operational + * - **B3 Data security** + - Patient data never leaves the trust; authenticated encryption in transit with + per-trust keys; mutually authenticated TLS between federated learning + participants; encryption at rest under managed keys; no standing production + database credential. + - Operational + * - **B4 System security** + - Container hardening with least privilege, dropped capabilities, and + no-new-privileges; secure-by-default configuration; immutable images; automated + secret scanning; services that refuse to start without required credentials. + - Operational; training-container isolation in delivery + * - **B5 Resilient networks and systems** + - Private-by-default networking; an outbound-only trust boundary; a single hardened + edge; an AWS Landing Zone Accelerator foundation providing account separation and + centrally managed guardrails. + - Operational + * - **B6 Staff awareness and training** + - An organisational responsibility of the deploying trust; the platform supports it + with documented conventions and clear role definitions. + - Organisational (platform supports) + +Objective C — Detecting cyber security events +============================================= + +.. list-table:: + :header-rows: 1 + :widths: 20 55 25 + + * - CAF principle + - How FLIP addresses it + - Status + * - **C1 Security monitoring** + - Cloud-native runtime threat detection; audit trails; scheduled and on-change secret + scanning; configuration drift detection with alerting; centralised logging under + the Landing Zone Accelerator. + - Operational + * - **C2 Proactive event discovery** + - Independent penetration testing; continuous automated scanning in the delivery + pipeline; triaged code-scanning alerts. + - Operational + +Objective D — Minimising the impact of incidents +================================================ + +.. list-table:: + :header-rows: 1 + :widths: 20 55 25 + + * - CAF principle + - How FLIP addresses it + - Status + * - **D1 Response and recovery planning** + - A coordinated disclosure and private advisory workflow with a defined + acknowledgement window; recovery supported by versioned storage, immutable images, + and infrastructure redeployable from code; trust-side operational response remains + an organisational responsibility. + - Shared with organisation + * - **D2 Lessons learned** + - A tracked remediation programme with post-fix verification; audit trails supporting + post-incident review and continuous improvement. + - Operational + +.. note:: + + **Status values.** *Operational* — the control is in place. *Organisational* — the + control belongs to the deploying trust rather than the platform. *Shared* — + responsibility is divided between the platform and the deploying organisation. Where a + control is noted as "in delivery", it is scheduled work rather than a gap left + unaddressed. diff --git a/docs/source/index.rst b/docs/source/index.rst index 816e76267..7388cd802 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -17,6 +17,7 @@ Federated Learning Interoperability Platform user-guides working-with-flip-apps security + governance-and-compliance .. toctree:: :maxdepth: 2 From 50c9034ac651486091eeb17035e8cd287f2f2a0b Mon Sep 17 00:00:00 2001 From: at24_bioeng625-pc Date: Mon, 3 Aug 2026 13:36:13 +0100 Subject: [PATCH 5/5] docs: state in-flight controls as in delivery, not shipped; trust-set threshold Address review: the security page described per-trust authenticated payload encryption (key identifiers, AEAD) and the correlation-ID error mechanism with its CI guard as current behaviour; both are in-flight work. Rewritten to state today's shared-key scheme and error paths precisely, with the upgrades marked as in delivery. The rotation paragraph and the CAF B3 appendix row are aligned to match. Also state that the cohort suppression threshold is the trust's own disclosure floor, set per trust in its deployment kit, on both the security page and the governance page. Signed-off-by: at24_bioeng625-pc --- docs/source/governance-and-compliance.rst | 13 +++++-- docs/source/security.rst | 47 +++++++++++++++-------- 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/docs/source/governance-and-compliance.rst b/docs/source/governance-and-compliance.rst index c1399ed4c..ec1c2b1c1 100644 --- a/docs/source/governance-and-compliance.rst +++ b/docs/source/governance-and-compliance.rst @@ -56,6 +56,13 @@ group size below which results are suppressed; model updates produced during fed training, which pass through a privacy filter before transmission; and operational telemetry such as task status. Model updates are mathematical parameters, not records. +**The suppression threshold is the trust's own setting.** Each trust sets its +disclosure floor in the deployment configuration it holds locally — the same kit file +that carries its credentials — so a trust whose small-numbers policy demands a higher +minimum simply raises its own floor. Trusts need not agree on a shared value, and the +Central Hub cannot lower it: the suppression runs on the trust's own deployment, next +to the data. + **What enters a trust** is the definition of work to be done — an approved project, a cohort query, and the model application to train — and nothing else. The hub cannot read into a trust: trusts poll outbound and the hub has no route back in. @@ -308,11 +315,11 @@ Objective B — Protecting against cyber attack operator access with no exposed remote-login port. - Operational * - **B3 Data security** - - Patient data never leaves the trust; authenticated encryption in transit with - per-trust keys; mutually authenticated TLS between federated learning + - Patient data never leaves the trust; encrypted transport with an additional + payload-encryption layer; mutually authenticated TLS between federated learning participants; encryption at rest under managed keys; no standing production database credential. - - Operational + - Operational; per-trust authenticated payload encryption in delivery * - **B4 System security** - Container hardening with least privilege, dropped capabilities, and no-new-privileges; secure-by-default configuration; immutable images; automated diff --git a/docs/source/security.rst b/docs/source/security.rst index b2f0a6547..f14868b2d 100644 --- a/docs/source/security.rst +++ b/docs/source/security.rst @@ -37,7 +37,7 @@ HSTS, AWS WAF managed rules, and an internal-only Application Load Balancer. Not else in the platform is reachable from the public internet. **A site-to-site VPN is available on request.** Trust-to-hub traffic is encrypted in -transit by default, and payloads carry their own authenticated encryption on top of +transit by default, and payloads carry their own encryption layer on top of that. Where a trust's own policy calls for network-layer separation as well, a site-to-site VPN between the trust's network and the hub VPC can be provisioned, carrying all outbound polling and FL client traffic. @@ -129,7 +129,9 @@ independent controls would each have to fail before anything unintended could ex literal values; - results below ``COHORT_QUERY_THRESHOLD`` are suppressed, and a genuine zero is deliberately indistinguishable from a small suppressed count, so a response cannot - reveal that a handful of patients matched; + reveal that a handful of patients matched — the threshold is the trust's own + disclosure floor (default 10), set by each trust in its deployment kit: trusts need + not agree on a shared value, and the hub cannot lower it; - cached results are scoped to the requesting project and expire in minutes, so no project is served another's data and no result outlives a withdrawal of consent or a correction to a record. @@ -172,10 +174,15 @@ Data in transit and at rest **All traffic is encrypted in transit.** Every connection between a trust and the Central Hub runs over HTTPS, outbound from the trust only. On top of that transport -encryption, task payloads carry their own **authenticated encryption**: any tampering -with a message causes decryption to fail outright rather than passing silently. Payload -keys carry key identifiers, and each trust has its own key, so a compromise at one trust -does not expose the traffic of any other. +encryption, task payloads are themselves encrypted before they are handed to the +transport, so the payload body is never carried in the clear inside an established +session. Stated precisely, because this page exists to be relied on: today the payload +layer uses a **single platform-wide symmetric key**, and message integrity is provided +by the TLS transport rather than by the payload cipher itself. An upgrade to +**authenticated encryption with per-trust keys** — tampering makes decryption fail +outright, each trust's traffic is protected by its own key so a compromise at one trust +exposes no other's, and keys carry identifiers so they can be rotated without a +synchronised cutover — is in delivery, not yet a shipped control. **At rest**, model and results storage uses S3 with managed encryption under a customer-managed KMS key, versioning, blocked public access, HTTPS-only bucket policies, @@ -186,10 +193,12 @@ connections require TLS on both hops. time-limited, with a hard ceiling enforced centrally, because such a link is a capability against the bucket in either direction. -**Credentials are designed to be rotated.** A trust's API key, its trust-internal -service key, and its payload-encryption key are issued at registration and can be -re-issued without redeploying the platform. Payload keys carry identifiers precisely so -that a new key can be introduced and an old one retired without a synchronised cutover. +**Credentials are designed to be rotated.** A trust's API key and its trust-internal +service key are issued at registration and can be re-issued without redeploying the +platform. Rotating the payload-encryption key currently means re-issuing the shared +key to every participant at once; removing that coordination — by giving each trust +its own identified key, so a new key can be introduced and an old one retired without +a synchronised cutover — is part of the per-trust key work described above. **The trust imaging archive requires authentication to start.** Orthanc will not run without credentials configured, an automated check verifies that authentication is @@ -203,13 +212,17 @@ Diagnostics and disclosure control Error messages are a quiet disclosure route: an unhandled error can return database structure, internal hostnames, or fragments of a failing query to whoever triggered it. -FLIP returns a fixed message accompanied by a **correlation identifier**. The identifier -is generated server-side, never accepted from the caller, and recorded alongside the full -technical detail in the internal logs. A user who encounters an error quotes that -identifier and an engineer can find exactly what happened — diagnosability is preserved -without disclosing internals. Automated checks in CI prevent raw exception text from -reappearing in responses, and logging is scoped so that secrets, pre-signed URLs, and -query text are not written out in the first place. +The platform's target here is a fixed client-facing message accompanied by a +**correlation identifier**: generated server-side, never accepted from the caller, and +recorded alongside the full technical detail in the internal logs, so a user who +encounters an error quotes the identifier and an engineer finds exactly what happened — +diagnosability preserved without disclosing internals — with automated checks in CI +keeping raw exception text from reappearing in responses. That mechanism is **in +delivery, not yet a shipped control**: today some error paths still return the +underlying exception text to the caller, and the CI guard does not yet exist. What +holds today is narrower: log lines are written to avoid carrying sensitive values +themselves — counts, file names, and hashed object identifiers stand in for pre-signed +URLs and storage keys. **************************************** Software supply chain and change control