Document shared pipeline filesystem policy
This commit is contained in:
@@ -0,0 +1,266 @@
|
||||
# Feature 0009: Shared Pipeline Filesystem and Execution
|
||||
|
||||
Status: proposed
|
||||
|
||||
## Summary
|
||||
|
||||
Establish one coherent filesystem and invocation policy for the D&D suite.
|
||||
Standalone application roles will expose sensible generic configuration
|
||||
ownership and mode controls, while the pipeline role will use those controls
|
||||
to make configuration service-owned and world-readable and to make writable
|
||||
state safely shareable by the service account and existing human members of
|
||||
its group.
|
||||
|
||||
## Problem or Opportunity
|
||||
|
||||
The current standalone roles hard-code their configuration ownership and
|
||||
modes. Those defaults are reasonable in isolation, but the pipeline cannot
|
||||
select its intended `narratio:narratio` ownership without adding a second
|
||||
convergence pass that would fight the dependency roles and report changes on
|
||||
every run.
|
||||
|
||||
Narratio's writable roots already use setgid mode `2775`, but directory mode
|
||||
alone does not preserve group write access when different invocation identities
|
||||
have different umasks. Notarius v0.7.0 has removed its application-enforced
|
||||
private cache modes, so deployment provisioning can now establish a consistent
|
||||
shared policy before Notarius integration begins.
|
||||
|
||||
## Governing Policies
|
||||
|
||||
- [The Ansible house style](../../ansible.md) governs the generic role
|
||||
interfaces, direct module-driven convergence, and suite overrides.
|
||||
- [The testing house style](../../testing.md) governs behavioral access tests
|
||||
and clean repeated convergence.
|
||||
- [The MVP roadmap](../mvp.md) assigns operator access and cross-application
|
||||
policy to the pipeline role while keeping standalone roles independently
|
||||
useful.
|
||||
- [Decision 0001](../../decisions/0001-minimal-module-driven-ansible.md)
|
||||
requires direct idempotent modules without preliminary state inspection.
|
||||
- [Decision 0002](../../decisions/0002-shared-readable-pipeline-filesystem-policy.md)
|
||||
defines the service ownership, shared writable state, world-readable content,
|
||||
and restricted credential boundary implemented by this feature.
|
||||
|
||||
## Scope
|
||||
|
||||
- Add configuration owner, group, directory-mode, and file-mode variables to
|
||||
the Audita, Notarius, Narratio, and Scriptorium roles.
|
||||
- Preserve standalone defaults appropriate to each application rather than
|
||||
embedding D&D suite policy in those roles.
|
||||
- Add suite-level runtime identity and configuration-mode variables to the
|
||||
pipeline role and pass them explicitly to all four configuration-bearing
|
||||
dependencies.
|
||||
- Run the Narratio dependency before the other application dependencies so it
|
||||
creates the selected service account and group before they write
|
||||
service-owned configuration.
|
||||
- Converge suite application configuration as `narratio:narratio`, with
|
||||
directory mode `0755` and file mode `0644` by default.
|
||||
- Keep `/var/lib/narratio`, `/var/spool/narratio`, and
|
||||
`/var/cache/narratio` as `narratio:narratio` mode `2775` shared writable
|
||||
roots.
|
||||
- Install the Ubuntu `acl` package, declare a pinned `ansible.posix`
|
||||
collection dependency, and manage access and default ACL entries that retain
|
||||
owner/group write and world read/traverse access beneath shared roots.
|
||||
- Support invocation as either the service account or an existing human
|
||||
member of the selected runtime group.
|
||||
- Document the standalone defaults, pipeline overrides, expected group
|
||||
membership, world-readable non-secret boundary, and reserved restricted
|
||||
`secrets/` policy.
|
||||
- Add focused integration coverage for public configuration-mode overrides,
|
||||
cross-identity access, and immediate clean repeated convergence.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Upgrading Notarius, adding its ConfigKit import interface, deploying its
|
||||
D&D pipeline, creating `/var/cache/notarius`, or enabling Narratio's
|
||||
extraction stage; those belong to Feature 0010.
|
||||
- Changing any application's native configuration fields or runtime behavior.
|
||||
- Selecting human operators or changing their group membership.
|
||||
- Creating or populating a `secrets/` directory, delivering credentials, or
|
||||
defining secret injection.
|
||||
- Making application configuration group-writable or supporting manual edits
|
||||
to Ansible-managed configuration.
|
||||
- Changing ownership of root-managed source checkouts, installed binaries,
|
||||
campaign assets, or prompt assets.
|
||||
- Recursively migrating pre-existing runtime content with incompatible modes
|
||||
or ACLs.
|
||||
- Running transcript processing, calling external services, or adding
|
||||
scheduling and service management.
|
||||
|
||||
## Target End State
|
||||
|
||||
Each standalone role exposes the same four concepts using its application
|
||||
prefix. Its defaults remain appropriate for an independent installation:
|
||||
|
||||
| Role | Configuration owner | Configuration group | Directory mode | File mode |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| Audita | `root` | `root` | `0755` | `0644` |
|
||||
| Notarius | `root` | `root` | `0755` | `0644` |
|
||||
| Scriptorium | `root` | `root` | `0755` | `0644` |
|
||||
| Narratio | `root` | `narratio` | `0750` | `0640` |
|
||||
|
||||
For example, the Notarius variables are
|
||||
`notarius_config_owner`, `notarius_config_group`,
|
||||
`notarius_config_directory_mode`, and `notarius_config_file_mode`; the other
|
||||
roles use their corresponding prefixes. Each role uses these variables in the
|
||||
ordinary directory and template tasks that already own its configuration. No
|
||||
role adds a follow-up permission-repair task or preliminary inspection.
|
||||
|
||||
The pipeline role defines `dnd_pipeline_runtime_user` and
|
||||
`dnd_pipeline_runtime_group`, both defaulting to `narratio`, plus
|
||||
`dnd_pipeline_config_directory_mode: "0755"` and
|
||||
`dnd_pipeline_config_file_mode: "0644"`. It passes those values as the four
|
||||
roles' configuration owner, group, and mode inputs. Narratio executes first in
|
||||
dependency order and creates the selected identity before another dependency
|
||||
references it.
|
||||
|
||||
The resulting application configuration beneath
|
||||
`/usr/local/etc/{audita,notarius,narratio,scriptorium}` is owned by
|
||||
`narratio:narratio` and is world-readable. It remains under Ansible control;
|
||||
neither the service account nor human operators are expected to edit managed
|
||||
files in place.
|
||||
|
||||
The three existing Narratio writable roots are `narratio:narratio` mode
|
||||
`2775`. Their access and default ACLs are equivalent to `u::rwx`, `g::rwx`,
|
||||
`o::r-x`, and `m::rwx`. Inherited permissions remain subject to the mode
|
||||
requested for each new file type, so ordinary files are non-executable while
|
||||
remaining group-writable and world-readable. A later feature applies the same
|
||||
policy when it introduces another shared root such as
|
||||
`/var/cache/notarius`.
|
||||
|
||||
A human account already belonging to `narratio` can use the same configuration
|
||||
and writable roots as the service account. This feature does not choose that
|
||||
account. Credential files are absent; the reserved future
|
||||
`/usr/local/etc/narratio/secrets` subtree uses the distinct `0750` directory
|
||||
and `0640` file policy when a credential-delivery feature creates it.
|
||||
|
||||
## Repository Ownership
|
||||
|
||||
- `ansible-role-audita`, `ansible-role-notarius`, and
|
||||
`ansible-role-scriptorium` own their generic configuration ownership and
|
||||
mode variables, standalone defaults, documentation, and focused integration
|
||||
coverage.
|
||||
- `ansible-role-narratio` owns the equivalent generic controls while retaining
|
||||
its existing standalone relationship to the service group it creates.
|
||||
- `ansible-role-dnd-pipeline` owns dependency ordering, the suite runtime
|
||||
identity and configuration-mode defaults, explicit dependency overrides,
|
||||
the `acl` package and `ansible.posix` dependency, shared-root ACL
|
||||
convergence, consumer documentation, and cross-identity integration
|
||||
coverage.
|
||||
- `ansible-workspace-dnd` owns this roadmap, Decision 0002, the later staged
|
||||
implementation plan, and aggregate status.
|
||||
- The five application source repositories are unchanged; their runtime
|
||||
contracts are not needed to implement configuration-file ownership.
|
||||
|
||||
## Constraints and Settled Decisions
|
||||
|
||||
- Standalone application roles remain independently usable. Their defaults do
|
||||
not assume that a `narratio` account exists unless the role itself creates
|
||||
it.
|
||||
- The pipeline role selects suite policy by passing public role variables; it
|
||||
does not reapply ownership after dependencies have completed.
|
||||
- Configuration owner and group variables are strings, while mode variables
|
||||
use the quoted four-digit strings expected by Ansible file-management
|
||||
modules.
|
||||
- The pipeline's selected runtime user and group are the configuration owner
|
||||
and group. Supporting different configuration and runtime identities would
|
||||
require a concrete later use case.
|
||||
- Application configuration is world-readable but not group-writable. Shared
|
||||
writable roots and their ordinary content are group-writable and
|
||||
world-readable.
|
||||
- The role uses `ansible.builtin.apt` for the `acl` package and
|
||||
`ansible.posix.acl` for access/default ACL state. The collection dependency
|
||||
is pinned in the pipeline role's manifest and installed by its integration
|
||||
driver.
|
||||
- ACL management covers only the three existing writable roots and their
|
||||
inheritance policy. It does not recursively rewrite existing descendants.
|
||||
- A `secrets/` subtree is a documented future boundary, not state created by
|
||||
this feature.
|
||||
- Tests exercise the public operating contract and one representative
|
||||
cross-identity workflow. They do not enumerate every path or restate every
|
||||
module argument.
|
||||
|
||||
## Simplest Viable Implementation
|
||||
|
||||
1. Add four defaults, four argument-specification entries, and direct task
|
||||
substitutions to each configuration-bearing standalone role; update its
|
||||
README and focused integration inputs without adding new task flow.
|
||||
2. Reorder the pipeline dependencies so Narratio creates the service identity
|
||||
first, add four suite policy variables, and pass them directly to each
|
||||
configuration-bearing role.
|
||||
3. Add the pinned `ansible.posix` requirement, install `acl`, and apply the
|
||||
selected access/default ACL entries to the three existing writable roots
|
||||
with ordinary state-management modules.
|
||||
4. Update pipeline consumer documentation and extend its integration driver
|
||||
with one representative human group member, native configuration
|
||||
inspection, shared-write behavior, and a clean repeat.
|
||||
|
||||
No task gathers preliminary filesystem or account state. Existing `file`,
|
||||
`template`, `apt`, and `acl` modules own convergence and change reporting.
|
||||
|
||||
## Rationale and Alternatives
|
||||
|
||||
Public ownership and mode variables let each standalone role express its own
|
||||
sensible installation while allowing a composing role to select different
|
||||
policy through normal Ansible precedence. A corrective pipeline task would
|
||||
fight the dependency templates and make every otherwise unchanged run report
|
||||
changes.
|
||||
|
||||
Setgid alone preserves group identity but does not guarantee group-write bits
|
||||
for files created under an arbitrary human user's umask. A managed default ACL
|
||||
expresses the required inherited state directly. A wrapper command that sets a
|
||||
umask would add another invocation path and would not protect direct use of the
|
||||
installed applications.
|
||||
|
||||
Mode `2775`, rather than `2770`, is deliberate: D&D transcript content is not
|
||||
treated as secret in this deployment. Only API keys and similar credentials
|
||||
belong in the separately restricted future subtree.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- Each configuration-bearing standalone role exposes and documents owner,
|
||||
group, directory-mode, and file-mode variables with the standalone defaults
|
||||
listed above.
|
||||
- Each role applies those inputs through the tasks that already create its
|
||||
configuration directory and file, without a second convergence pass.
|
||||
- The pipeline role runs Narratio first and explicitly selects
|
||||
`narratio:narratio`, `0755` directories, and `0644` files for all application
|
||||
configuration.
|
||||
- The deployed configuration for Audita, Notarius, Narratio, and Scriptorium
|
||||
is owned by `narratio:narratio`, is readable by other local users, and
|
||||
contains no credential values.
|
||||
- The three existing Narratio writable roots are `narratio:narratio` mode
|
||||
`2775` and have access/default ACLs that preserve group write and world
|
||||
read/traverse access for new content.
|
||||
- The pipeline role declares a pinned `ansible.posix` dependency and installs
|
||||
the required Ubuntu `acl` package.
|
||||
- The service account and a representative existing human member of the
|
||||
`narratio` group can use the same configuration and exchange ordinary
|
||||
writable-state content.
|
||||
- The role does not create users other than the established service account,
|
||||
alter human group membership, create a secrets directory, or migrate
|
||||
existing descendants recursively.
|
||||
- Syntax checking and production-profile lint pass in every changed role.
|
||||
- Focused standalone-role integration checks and the complete pipeline
|
||||
integration entry point pass.
|
||||
- An immediate repeated application in every changed role reports no changes.
|
||||
- Tests observe the shared operating contract without snapshotting defaults,
|
||||
enumerating implementation steps, or testing trusted module behavior.
|
||||
|
||||
## Dependencies and Follow-up Work
|
||||
|
||||
Features 0001 through 0008 and the standalone-role test-suite simplification
|
||||
are complete. Notarius v0.7.0's deployment-owned cache permissions remove the
|
||||
application blocker that previously prevented this policy, but Feature 0009
|
||||
does not otherwise depend on or modify the Notarius application.
|
||||
|
||||
Feature 0010 applies this policy to `/var/cache/notarius` while adding the D&D
|
||||
extraction integration. Later work may add optional operator-selection and
|
||||
group-membership variables or deliver credentials beneath the restricted
|
||||
`secrets/` subtree.
|
||||
|
||||
## Potential Future Hardening
|
||||
|
||||
- Offer a non-world-readable deployment profile if a concrete environment
|
||||
later treats campaign material as confidential.
|
||||
- Select a recursive migration policy if supported upgrades must normalize
|
||||
pre-existing descendants rather than only managed roots and new content.
|
||||
Reference in New Issue
Block a user