Initial code commit
CI / test (3.11) (push) Canceled after 0s
CI / test (3.12) (push) Canceled after 0s
CI / test (3.13) (push) Canceled after 0s
CI / test (3.14) (push) Canceled after 0s
CI / package (push) Canceled after 0s
CI / precommit-and-security (push) Canceled after 11s
CI / typecheck (push) Canceled after 11s
CI / test (3.11) (push) Canceled after 0s
CI / test (3.12) (push) Canceled after 0s
CI / test (3.13) (push) Canceled after 0s
CI / test (3.14) (push) Canceled after 0s
CI / package (push) Canceled after 0s
CI / precommit-and-security (push) Canceled after 11s
CI / typecheck (push) Canceled after 11s
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
# Security model
|
||||
|
||||
This document describes what `schedls` trusts, what it does not, and how the
|
||||
main threats are mitigated. It complements [SECURITY.md](../SECURITY.md).
|
||||
|
||||
## Trust model
|
||||
|
||||
- **Native schedulers are authoritative.** systemd and cron own execution.
|
||||
`schedls` only reads and writes their configuration.
|
||||
- **Discovered configuration is data.** Commands and schedules found on the
|
||||
host are parsed and displayed, never executed.
|
||||
- **Unmanaged objects are protected.** A timer or cron entry not created by
|
||||
`schedls` cannot be modified or removed by default.
|
||||
- **The user chooses privilege.** `schedls` never escalates on the user's
|
||||
behalf.
|
||||
|
||||
## Threats and mitigations
|
||||
|
||||
### Malicious schedule names
|
||||
|
||||
Names must match `[A-Za-z0-9][A-Za-z0-9_.-]{0,63}`. Path separators, traversal,
|
||||
whitespace and shell metacharacters are rejected. Names are never interpolated
|
||||
into shell strings.
|
||||
|
||||
### Malicious command arguments
|
||||
|
||||
Arguments are kept as an argv internally. systemd units are rendered with a
|
||||
dedicated serializer that always quotes arguments and escapes backslash,
|
||||
double quotes, literal `$` and `%`; arguments containing NUL or newline are
|
||||
rejected. cron commands are serialized with shell quoting and then have every
|
||||
`%` escaped for cron's own parser. Calendar expressions are validated with
|
||||
`systemd-analyze` and must not contain control characters or begin with `-`;
|
||||
timer durations (`--jitter`, `--accuracy`) are validated with
|
||||
`systemd-analyze timespan`. If a safe representation cannot be guaranteed,
|
||||
creation is refused.
|
||||
|
||||
### Symlink attacks during privileged writes
|
||||
|
||||
Before replacing a file, `schedls` uses `lstat`, rejects symlinks and
|
||||
non-regular files, and verifies ownership. Destination directories must be
|
||||
real directories owned by the expected user (`root` when running as root) and
|
||||
must not be group- or other-writable. Temporary files are created in the same
|
||||
directory and atomically renamed.
|
||||
|
||||
### PATH hijacking
|
||||
|
||||
Critical helpers (`systemctl`, `systemd-analyze`, `journalctl`, `crontab`,
|
||||
`loginctl`) are resolved once with a controlled lookup. A helper is rejected
|
||||
unless the executable **and** its realpath target are regular files, are owned
|
||||
by root (when `schedls` runs as root) or by root or the current user, and live
|
||||
in a directory that is not group- or other-writable. Helper processes run with
|
||||
a fixed `PATH` rather than the caller's.
|
||||
|
||||
### Path traversal through discovered unit names
|
||||
|
||||
A `Unit=` value read from a timer file is treated as untrusted data. Mutation
|
||||
never uses it to build a filesystem path: unit names are always derived from
|
||||
the validated job name (`schedls-<name>.timer` / `.service`). Generated unit
|
||||
names are re-validated before they are written to a temporary directory, so a
|
||||
crafted unit cannot redirect writes or removals outside the trusted unit
|
||||
directory.
|
||||
|
||||
### Untrusted output
|
||||
|
||||
`journalctl` output is program output, not configuration. When stdout is a
|
||||
terminal, control characters are escaped before display so a log line cannot
|
||||
inject terminal escape sequences; redirected output is passed through
|
||||
unchanged. Machine-readable `--json` output escapes control characters as part
|
||||
of JSON encoding.
|
||||
|
||||
### Environment injection and pagers
|
||||
|
||||
Helper commands run with a controlled environment (`LC_ALL=C`,
|
||||
`SYSTEMD_COLORS=0`, `SYSTEMD_PAGER=cat`, `PAGER=cat`) and never with a shell.
|
||||
Pagers and editors are never launched while `schedls` is running. Environment
|
||||
values passed with `--env` are written to unit files with systemd quoting and
|
||||
are world-readable like any other unit; do not put secrets in them.
|
||||
|
||||
### TOCTOU races
|
||||
|
||||
Plans snapshot existing content. At apply time the current content is compared
|
||||
against the snapshot; a mismatch aborts the operation. Privileged critical
|
||||
sections are kept small and use file descriptors / atomic rename where
|
||||
practical.
|
||||
|
||||
### Corrupt or malicious existing configuration
|
||||
|
||||
Configuration is treated as opaque data. Malformed `schedls` cron markers cause
|
||||
mutation to fail closed rather than guess. Unknown crontab lines are preserved
|
||||
byte-for-byte. A systemd unit whose name starts with `schedls-` is only treated
|
||||
as managed when its file carries the `# Managed by schedls` and `# Name:`
|
||||
markers, so a same-named foreign unit is not modified.
|
||||
|
||||
### Partial operations
|
||||
|
||||
systemd creation writes both units, reloads the manager, and enables the timer;
|
||||
if a later step fails, or the operation is interrupted, the previous state is
|
||||
restored and a partially enabled timer is disabled again. Cron installs are
|
||||
validated first and restored from the previous crontab text on failure.
|
||||
|
||||
### Supply chain
|
||||
|
||||
Runtime dependencies are zero. Development dependencies are locked and audited
|
||||
in CI.
|
||||
|
||||
## Availability, privacy
|
||||
|
||||
`0.1.0` requires `systemd-analyze` to create or validate calendar expressions
|
||||
and refuses the operation if it is unavailable. `schedls` makes no network
|
||||
requests and collects no telemetry.
|
||||
Reference in New Issue
Block a user