Rework the tiers: thin mandatory core, opt-in blocks, and tested compliance profiles #29

Open
opened 2026-06-18 15:04:57 +00:00 by bram.buijs · 2 comments
Collaborator

This is a follow-up thought on the tier model in #16 (#8). Since opening that PR
I think we can do better, and since #16 is not merged yet I would rather rework
it than merge it and change it right after. Curious what you think before I touch
any code.

What is bugging me about the current split

Right now profiles-dawo-core forces a set of blocks on every device (mkForce),
and profiles-dawo-hardened is an aggregate that declares the opt-in ones. It
works, but two things rub:

  • A couple of the "core" blocks are really a policy choice, not a universal
    floor. usbControl (block-new) needs a tuned allowlist, or a machine that has to
    hotplug a dongle just does not work out of the box. apparmor can freeze app
    sandboxes (I hit a Chromium freeze). Forcing those on every device feels wrong;
    they belong with the consumer's own risk acceptance.
  • "What does standard X actually require?" lives in prose, not in code.

What I would like to do instead

  1. Keep a thin mandatory core: only the things that are universal, cannot lock
    you out, and need no per-site config. My shortlist is ssh, sysctlBaseline,
    timesync, audit. Those stay forced on in profiles-dawo-core.

  2. Make everything else opt-in: usbControl, apparmor, and the planned pam-u2f,
    pam-oath, egress-deny, disk-encryption, GNOME hardening. Each block just
    declares dawo..enable (default off), so they are a plain list of
    available blocks. No profiles-dawo-hardened aggregate anymore. Keep the
    granularity fine, so smartcard, YubiKey and FIDO2 are separate blocks: a
    municipality that does not use them never carries that support in its closure.

  3. Ship reference compliance profiles that bundle the opt-in blocks per standard,
    e.g. profiles-bio-1, profiles-bio-2, profiles-ncsc. A profile only flips
    dawo..enable = true (no mkForce, so a host can still tune). A consumer
    picks a compliance profile, or cherry-picks individual blocks.

The way I see it, this is basically the securix idea of offering real hardening,
but a la carte: the platform offers the controls, and the consumer composes only
what it needs, while a thin core still guarantees a floor.

The bar I want to hold for a profile

A profile is a compliance claim, so I do not want to ship one unless it is backed
up:

  • Substantiated: every block in a profile maps to a named control of that
    standard, in a mapping table (docs/compliance.md): control id, block, what it
    enforces. No block in a profile without a cited control, so the claim is
    auditable in one place.
  • Builds: each profile is a real host fixture, nix flake check green.
  • Verified at runtime: a nixosTest per profile that asserts the controls are
    actually in effect, not just that the config evaluates. So: sshd offers only
    the hardened crypto, the sysctl values are set, usbguard runs block-new, the
    dconf keys are locked, and so on. That is the real proof.
  • Cross-checked later (optional): an external scan (lynis or OpenSCAP) to
    sanity-check the mapping against an independent baseline.

What I am asking you

  • Does this direction make sense to you? It does walk back the mandatory-core
    idea from #8, so I want your read before building anything.
  • Are you ok with me superseding #16 and reworking it onto this, rather than
    merging #16 first? Happy either way, just want to avoid churn.
  • Core membership: ssh, sysctlBaseline, timesync, audit. Agreed, or would you
    pull any of those into opt-in too?
  • Profile set and naming: bio-1, bio-2, ncsc to start?
  • Test depth: is a nixosTest per profile the right bar, and do you want an
    external scan in scope now or later?

If you are on board I will do the PR, including the mapping table and the
per-profile tests, not just the modules.

This is a follow-up thought on the tier model in #16 (#8). Since opening that PR I think we can do better, and since #16 is not merged yet I would rather rework it than merge it and change it right after. Curious what you think before I touch any code. ## What is bugging me about the current split Right now profiles-dawo-core forces a set of blocks on every device (mkForce), and profiles-dawo-hardened is an aggregate that declares the opt-in ones. It works, but two things rub: - A couple of the "core" blocks are really a policy choice, not a universal floor. usbControl (block-new) needs a tuned allowlist, or a machine that has to hotplug a dongle just does not work out of the box. apparmor can freeze app sandboxes (I hit a Chromium freeze). Forcing those on every device feels wrong; they belong with the consumer's own risk acceptance. - "What does standard X actually require?" lives in prose, not in code. ## What I would like to do instead 1. Keep a thin mandatory core: only the things that are universal, cannot lock you out, and need no per-site config. My shortlist is ssh, sysctlBaseline, timesync, audit. Those stay forced on in profiles-dawo-core. 2. Make everything else opt-in: usbControl, apparmor, and the planned pam-u2f, pam-oath, egress-deny, disk-encryption, GNOME hardening. Each block just declares dawo.<block>.enable (default off), so they are a plain list of available blocks. No profiles-dawo-hardened aggregate anymore. Keep the granularity fine, so smartcard, YubiKey and FIDO2 are separate blocks: a municipality that does not use them never carries that support in its closure. 3. Ship reference compliance profiles that bundle the opt-in blocks per standard, e.g. profiles-bio-1, profiles-bio-2, profiles-ncsc. A profile only flips dawo.<block>.enable = true (no mkForce, so a host can still tune). A consumer picks a compliance profile, or cherry-picks individual blocks. The way I see it, this is basically the securix idea of offering real hardening, but a la carte: the platform offers the controls, and the consumer composes only what it needs, while a thin core still guarantees a floor. ## The bar I want to hold for a profile A profile is a compliance claim, so I do not want to ship one unless it is backed up: - Substantiated: every block in a profile maps to a named control of that standard, in a mapping table (docs/compliance.md): control id, block, what it enforces. No block in a profile without a cited control, so the claim is auditable in one place. - Builds: each profile is a real host fixture, nix flake check green. - Verified at runtime: a nixosTest per profile that asserts the controls are actually in effect, not just that the config evaluates. So: sshd offers only the hardened crypto, the sysctl values are set, usbguard runs block-new, the dconf keys are locked, and so on. That is the real proof. - Cross-checked later (optional): an external scan (lynis or OpenSCAP) to sanity-check the mapping against an independent baseline. ## What I am asking you - Does this direction make sense to you? It does walk back the mandatory-core idea from #8, so I want your read before building anything. - Are you ok with me superseding #16 and reworking it onto this, rather than merging #16 first? Happy either way, just want to avoid churn. - Core membership: ssh, sysctlBaseline, timesync, audit. Agreed, or would you pull any of those into opt-in too? - Profile set and naming: bio-1, bio-2, ncsc to start? - Test depth: is a nixosTest per profile the right bar, and do you want an external scan in scope now or later? If you are on board I will do the PR, including the mapping table and the per-profile tests, not just the modules.
Collaborator

I like your direction with one exception.

The core is something we agree on completely, only force things that have no impact on user experience.

But IMHO, a full opt-in chain is impractical and will cause less experienced (or more time constrained) organizations to simply ignore these settings. I prefer the opt-out method because this leads to a more "comply or explain" type of behavior at the downstream organizations and will allow us to see why some options are not adopted.

I am open to discussing opt-in on modules that are truly optional, but we should look at those on a case-by-case basis.

I like your direction with one exception. The core is something we agree on completely, only force things that have no impact on user experience. But IMHO, a full opt-in chain is impractical and will cause less experienced (or more time constrained) organizations to simply ignore these settings. I prefer the opt-out method because this leads to a more "comply or explain" type of behavior at the downstream organizations and will allow us to see why some options are not adopted. I am open to discussing opt-in on modules that are truly optional, but we should look at those on a case-by-case basis.
Author
Collaborator

Agreed,

My suggestion would then be to make all generic stuff (logging, security/hardening, KDE etc) as opt-out
Then more modular things like: FIDO2, yubikey support etc as opt-in

Agreed, My suggestion would then be to make all generic stuff (logging, security/hardening, KDE etc) as opt-out Then more modular things like: FIDO2, yubikey support etc as opt-in
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
MinBZK/DAWO-NixOS#29
No description provided.