267 lines
13 KiB
Markdown
267 lines
13 KiB
Markdown
# Feature 0009: Shared Pipeline Filesystem and Execution
|
|
|
|
Status: done
|
|
|
|
## 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.
|