From ff1ac0e01c9e333f8eb5b07cefbcb403d6f71912 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Mon, 22 Jun 2026 20:26:45 +1000 Subject: [PATCH] Fixes for a never-to-be-released, um, release --- src/content/docs.html | 4 +- src/content/news/0-7-0.html | 15 ++- src/content/security.html | 161 +++++++++++++++++++++++++++++-- src/content/troubleshooting.html | 59 +++++++++++ 4 files changed, 228 insertions(+), 11 deletions(-) diff --git a/src/content/docs.html b/src/content/docs.html index 69594c8..9477d27 100644 --- a/src/content/docs.html +++ b/src/content/docs.html @@ -292,11 +292,11 @@ description: "How Enroll works: harvest, manifest, modes, and configuration."

INI config file

-

If you're repeating flags (include/exclude patterns, SOPS settings, etc.), store defaults in enroll.ini and keep your muscle memory intact.

+

If you're repeating flags (include/exclude patterns, SOPS settings, etc.), store defaults in ~/config/enroll/enroll.ini and keep your muscle memory intact.

Discovery order
-
You can pass -c/--config, set ENROLL_CONFIG, or let Enroll auto-discover ./enroll.ini, ./.enroll.ini, or ~/.config/enroll/enroll.ini.
+
You can pass -c/--config, set ENROLL_CONFIG, let Enroll auto-discover either your XDG config default directory, or finally ~/.config/enroll/enroll.ini.
diff --git a/src/content/news/0-7-0.html b/src/content/news/0-7-0.html index 9fbb66f..096dcfe 100644 --- a/src/content/news/0-7-0.html +++ b/src/content/news/0-7-0.html @@ -132,10 +132,23 @@ localhost : ok=5 changed=0 unreachable=0 failed=0 s
  • .bashrc and similar files are now only harvested from user directories when --dangerous is used, since this is a common place for sensitive environment variables to be set. As always, remember that --dangerous gives better harvest coverage, but you should use --sops or some other means of your own to encrypt the harvested data at rest safely!
  • Some output during an Ansible play is hidden with no_log to avoid potentially sensitive output, particularly of systemd unit state.
  • In case you missed it in version 0.6.0: Enroll now harvests runtime iptables and ipset rules!
  • +
  • Enroll no longer reads from .enroll.ini or enroll.ini in the current working directory. Instead, it's recommended to put your config file in ~/.config/enroll/enroll.ini, or pass an explicit path with --config or through use of the ENROLL_CONFIG environment variable.
  • +
  • Lots of other hardening and safeguard improvements!
  • +
      +
    • enroll validate makes sure the harvest doesn't contain unsafe things such as symlinks traversing out of the artifacts tree.
    • +
    • enroll manifest takes an internal pass via validate to make sure the harvest validates ok before trying to render config management code. If you find that your harvest is not passing validation, and don't believe it's been tampered with, it may be that it was created on an older version of Enroll and so doesn't pass the current schema. Consider re-harvesting the host.
    • +
    • Other input validations, such as making sure the string passed to --fqdn is safe
    • +
    • Improving detection of sensitive strings in the IgnorePolicy
    • +
    • Safer quoting of command fragments when using remote mode
    • +
    • Warning if running as root and $PATH contains the cwd or a world-writable location (to resist attacks via malicious versions of binaries Enroll calls such as dpkg, rpm, systemctl etc)
    • +
    • TOCTOU avoidance when copying files to harvested artifacts
    • +
    • Permission hardening on manifested dir
    • +
    • "and more!" :)
    • +

    More coverage

    -

    With these changes comes a lot of new 'variance' and argument input to the app. Pytest coverage is now at about 86%, and there is a big suite unit tests for Ansible, Puppet and Salt too, in CI. I'm continuing to try and automate testing all the ways you can use this tool.

    +

    With these changes comes a lot of new 'variance' and argument input to the app. Pytest coverage is now at about 86%, and there is a big suite unit tests for Ansible, Puppet and Salt too, in CI, running across both Debian and AlmaLinux environments. I'm continuing to try and automate testing all the ways you can use this tool.


    Thanks to everyone who has reached out with suggestions, constructive criticism, and bug reports!

    diff --git a/src/content/security.html b/src/content/security.html index 2218f29..a56743a 100644 --- a/src/content/security.html +++ b/src/content/security.html @@ -48,6 +48,43 @@ description: "Security posture and safe workflows for Enroll outputs."
    In manifest --sops mode, you'll need to decrypt and extract the bundle before running ansible-playbook, puppet apply, or salt-call.
    + +
    +

    Filesystem hardening for root runs

    +

    Enroll is often run as root because it needs to inspect system state. Version 0.5.5 added stricter filesystem checks so root does not accidentally write harvests or generated output through an unsafe path.

    + +
    +
    +
    +
    Private output directories
    +

    Harvest and manifest outputs are created with restrictive permissions, and Enroll refuses to reuse an existing harvest directory unless that mode is explicitly expected for an internal workflow.

    +
    +
    +
    +
    +
    Symlink and parent checks
    +

    When running as root, Enroll checks path components and parent directories so output is not created below a parent controlled by another user. This reduces the risk of symlink and time-of-check/time-of-use path races.

    +
    +
    +
    +
    +
    Safer artifact handling
    +

    Captured files and generated artifacts are validated as regular files, with traversal, symlink, hardlink, and special-file cases rejected when bundles are validated or extracted.

    +
    +
    +
    +
    +
    Root PATH hygiene
    +

    For root runs, Enroll warns about unsafe PATH entries so helper commands are not resolved from relative, writable, or non-root-owned directories.

    +
    +
    +
    + +
    +
    Why this can feel strict
    +
    A command that writes under a user-owned directory may be convenient, but it can also be hard to prove safe when the process is running as root. Prefer a root-owned output area such as /var/tmp/enroll, /root, or a fresh directory directly under the normal sticky /tmp.
    +
    +
    @@ -85,22 +122,130 @@ description: "Security posture and safe workflows for Enroll outputs."
    -

    Threat model

    +

    Threat model and security scope

    +

    Enroll is a command-line systems administration tool. It is designed to be run intentionally by an administrator, often as root, to inspect a host, harvest selected system state, and optionally generate or apply configuration-management output.

    +

    That makes Enroll different from a web application, daemon, network service, or setuid helper. It is not intended to be a sandbox for hostile local users. Instead, it assumes an operator-controlled execution environment, while still adding defense-in-depth checks for common filesystem, path traversal, secret-handling, and command-resolution mistakes.

    + +
    +
    +
    +
    Trusted operator assumptions
    +
      +
    • If Enroll is run as root, the root user is assumed to control and understand the command line, environment, configuration file, and output location.
    • +
    • If an enroll.ini file is loaded, its location and contents are assumed to be owned, selected, and understood by the operator.
    • +
    • The operator is expected to understand the implications of options such as --dangerous, --assume-safe-path, --sops, --enforce, --remote-host, and --remote-ssh-config.
    • +
    • Harvest bundles used for manifest, diff, or diff --enforce are assumed to come from a trusted source unless the operator is deliberately inspecting them without applying them.
    • +
    +
    +
    +
    +
    +
    Trusted local tools
    +

    Enroll invokes ordinary administrative tools such as SSH, sudo, SOPS, package managers, Docker, Podman, Flatpak, Snap, Ansible, Puppet, Salt, and system utilities. These are assumed to be the trusted tools the operator intended to execute.

    +

    +

    If an attacker can replace or redirect those tools, the host already has a local trust-boundary problem outside Enroll's ability to fully solve.

    +
    +
    +
    +
    + +
    +

    What Enroll tries to protect against

    +

    Enroll still performs substantial hardening because privileged CLI tools can otherwise be easy to misuse. These controls are intended to protect careful administrators from common dangerous mistakes.

    -
    What Enroll tries to prevent
    • Accidentally copying obvious secrets in default mode
    • -
    • Harvesting huge/unbounded file sets by mistake
    • -
    • One host's difference causing problems for other hosts by keeping multi-site data in inventory, Hiera, or pillar
    • +
    • Harvesting huge or unbounded file sets by mistake
    • +
    • Writing root-run output through unsafe symlinks, hardlinks, special files, or path-race situations
    • +
    • Extracting harvest tar members outside the intended bundle directory
    • +
    • Copying unsafe harvested artifacts into generated manifests
    -
    What you still need to think about
      -
    • Where outputs are stored and who can access them
    • -
    • Reviewing what was captured before committing/sharing
    • -
    • Choosing encryption and secret-management strategy
    • +
    • Resolving helper commands from suspicious PATH entries during root runs
    • +
    • Generating manifest commands where ordinary harvested values could become shell injection
    • +
    • Accepting unknown SSH host keys during remote harvests
    • +
    • Confusing one host's data with another host's data in multi-site outputs
    • +
    • Applying structurally unsafe harvest bundles
    • +
    +
    +
    +
    + +
    +

    What is out of scope

    +

    The following are normally considered local compromise, operator-controlled behavior, or trust-boundary failures rather than Enroll vulnerabilities by themselves:

    +
    +
    +
      +
    • A malicious local user who can already control root's command line, shell environment, config file, working directory, PATH, or invoked binaries
    • +
    • A root user loading an enroll.ini file whose contents intentionally request dangerous behavior
    • +
    • A root user passing --dangerous and observing that Enroll may collect sensitive data
    • +
    • A root user passing --assume-safe-path and observing that Enroll does not prompt about PATH safety
    • +
    +
    +
    +
      +
    • A user applying generated Ansible, Puppet, or Salt output from a harvest bundle they do not trust
    • +
    • A user enforcing a malicious or manually edited harvest bundle with diff --enforce
    • +
    • A user configuring an untrusted webhook, SSH proxy command, SOPS binary, or configuration-management tool
    • +
    • Reports that amount to: root can run Enroll with malicious options and make the system do dangerous things
    • +
    +
    +
    +
    +
    Plain-language summary
    +
    Enroll is an administrator's tool, not a local privilege boundary. If the operator's root execution environment is already attacker-controlled, Enroll cannot make that safe. Enroll does, however, try to reduce the damage from common path, permission, traversal, secret-handling, and manifest-generation mistakes.
    +
    +
    + +
    +

    Trusted harvests and enforcement

    +

    Harvest bundles should be treated as sensitive administrative artifacts. A harvest may contain hostnames, usernames, package lists, service state, filesystem metadata, configuration files, firewall snapshots, container image references, Flatpak/Snap state, and other operational details. In --dangerous mode it may contain substantially more sensitive material.

    +

    Enroll validates harvest structure and artifact safety, but validation does not prove that the desired state represented by a harvest is safe to apply. Only run manifest, diff, or especially diff --enforce against bundles that came from hosts and people you trust.

    +
    + +
    +

    Security report scope

    +
    +
    +
    Useful reports
    +
      +
    • Enroll captures clearly sensitive default-denied files without --dangerous
    • +
    • Enroll follows a symlink or hardlink in a way that causes privileged file disclosure or overwrite
    • +
    • Enroll extracts a tar member outside the intended harvest directory
    • +
    • Enroll accepts an artifact path that escapes the artifact root
    • +
    • Ordinary harvested data can cause command injection in generated manifests
    • +
    • Enroll silently ignores a failed safety check and proceeds anyway
    • +
    +
    +
    +
    Normally out-of-scope reports
    +
      +
    • Root can configure Enroll to collect sensitive files
    • +
    • Root can pass --dangerous and collect dangerous data
    • +
    • Root can pass --assume-safe-path and bypass the root PATH warning
    • +
    • Root can point Enroll at a malicious config file
    • +
    • Root can enforce a malicious harvest bundle
    • +
    • A local attacker can influence Enroll after already controlling root's environment or binaries
    • +
    +
    +
    +
    + +
    + +
    +

    Security researchers

    +
    +
    +
    Found a vulnerability in Enroll?
    +
      +
    • Please contact me using the contact form or via Signal (mig5.55)
    • +
    • My GPG public key is here
    • +
    • Unfortunately, I cannot offer financial bounty rewards at this time, but you will receive full credit for valid security issues.
    diff --git a/src/content/troubleshooting.html b/src/content/troubleshooting.html index aa68499..ea70877 100644 --- a/src/content/troubleshooting.html +++ b/src/content/troubleshooting.html @@ -22,6 +22,7 @@ description: "Common Enroll errors and fixes for generated Ansible, Puppet, and Flatpak from_url error JinjaTurtle Missing files + Harvest output parent Validate artifacts Remote harvest Diff enforcement @@ -138,6 +139,49 @@ description: "Common Enroll errors and fixes for generated Ansible, Puppet, and
    +
    +

    Error: harvest output parent is owned by the wrong user

    +

    When Enroll is run as root, it deliberately refuses to create a harvest below a parent directory that is controlled by a non-root user. This is a safety check: a user-owned parent directory can be renamed or replaced while a root process is writing files, which can become a symlink or path-race problem.

    + +
    +
    error: harvest output parent is not owned by root; refusing root-run output: /home/alice
    +error: harvest output parent is not owned by root; refusing root-run output: /tmp/tmp.abcd1234
    +
    + +
    +
    What Enroll is protecting you from
    +

    Harvest output can contain detailed system state and, when --dangerous is used, may contain sensitive configuration material. If Enroll is running as root, it needs the output path to stay exactly where it was checked. Refusing user-owned parent directories helps avoid accidental writes through symlinks or paths that can be swapped underneath the process.

    +
    + +
    +
    +
    +
    Use a root-owned output area
    +

    Create a dedicated root-owned directory and place harvests underneath it:

    +
    $ sudo install -d -m 700 -o root -g root /var/tmp/enroll
    +$ sudo enroll harvest --out /var/tmp/enroll/host1
    +
    +
    +
    +
    +
    /tmp itself is okay
    +

    A fresh output directory directly under the normal root-owned sticky /tmp directory is accepted. The output path must not already exist.

    +
    $ sudo enroll harvest --out /tmp/host1-harvest
    +
    +
    +
    + +
    +
    Avoid root harvests into your home directory
    +
    A command such as sudo enroll harvest --out ~/harvest may expand to a path below a user-owned home directory. Use /var/tmp/enroll, /root, or a fresh directory directly under /tmp instead.
    +
    + +
    +
    Remote harvest note
    +

    In normal remote mode, Enroll uses sudo on the remote host so it can collect system-level state. Recent Enroll versions create a separate root-owned temporary directory for the remote bundle. If an older version reports this error for a path like /tmp/tmp.xxxxx during remote harvest, upgrade Enroll. Use --no-sudo only when you intentionally want a limited, non-root harvest.

    +
    +
    +

    Generated manifest references a missing artifact

    If a manifest task points at a missing source file, validate the original harvest first. Validation checks state.json, referenced artifacts, generated firewall/sysctl files, and unreferenced artifact warnings.

    @@ -183,6 +227,21 @@ description: "Common Enroll errors and fixes for generated Ansible, Puppet, and

    For non-Ansible targets, make sure puppet or salt-call is installed and on PATH before running enforcement.

    +
    +

    Error about schema when validating or manifesting a harvest

    +

    You see an error like error: harvest state does not match this Enroll version's schema; please re-harvest the host with this version of Enroll.

    +
    +
    +
    +
    Reharvest the host
    +

    The harvest has either been tampered with, become corrupt, or was made with an earlier version of Enroll, which means its layout is a different structure than what Enroll now expects.

    +

    The best thing to do is run the harvest again. Enroll will validate the harvest against the latest schema when you go to manifest it.

    +

    If you still experience the error, please report it as a bug!

    +
    +
    +
    +
    +