- Python 96.1%
- Makefile 3.9%
| config | ||
| packaging/rpm | ||
| selinux | ||
| src/pica_agent | ||
| systemd | ||
| tests | ||
| LICENSE | ||
| Makefile | ||
| pyproject.toml | ||
| README.md | ||
pica-agent
Linux companion agent for Pica. It runs next to
Falco on each server, polls the Pica server for the host's desired ruleset
commit, validates downloads with falco --validate before applying,
installs the rules atomically, reloads Falco, and reports the applied commit
back. Stdlib-only Python, systemd service, RPM for RHEL 9/10 with a bundled
SELinux policy module.
Layout
| Path | Purpose |
|---|---|
src/pica_agent/ |
agent code (config, api, apply, agent, cli) |
config/ |
agent.toml.example, environment.example (secrets) |
systemd/ |
pica-agent.service, sysusers/tmpfiles entries |
selinux/ |
pica_agent.te + pica_agent.fc, built to pica_agent.pp |
packaging/rpm/pica-agent.spec |
RPM (agent + unit + policy via semodule) |
tests/ |
pytest suite (in-process fake server, no sockets) |
Operate
- Install Falco first and include
rules_dir(/etc/falco/rules.d/pica) in the Falco config. Without Falco the agent still polls and the host still appears in Pica, but rule applies do nothing. - Issue a per-host token on the server (printed once):
flask --app pica agent-token create <hostname>. - Configure the node: copy
config/agent.toml.exampleto/etc/pica/agent.toml, setserver_url/hostname/auth_token(or putPICA_AUTH_TOKENin/etc/pica/environment, mode 0600). systemctl enable --now pica-agent.- Verify in Pica: host auto-registers into
defaulton first poll; assign a group, promote a commit, confirm In sync on/rules/rollout.
Settings (file agent.toml, [agent] table + PICA_* env overrides, env wins):
| Setting | Env | Default |
|---|---|---|
server_url |
PICA_SERVER_URL |
http://127.0.0.1:8001 |
hostname |
PICA_HOSTNAME |
system hostname |
poll_interval |
PICA_POLL_INTERVAL |
60 (min 5) |
rules_dir |
PICA_RULES_DIR |
/etc/falco/rules.d/pica |
state_dir |
PICA_STATE_DIR |
/var/lib/pica-agent |
validate_cmd |
PICA_VALIDATE_CMD |
falco --validate -r {dir} |
reload_cmd |
PICA_RELOAD_CMD |
systemctl reload falco |
auth_token |
PICA_AUTH_TOKEN |
empty |
request_timeout |
PICA_REQUEST_TIMEOUT |
15 |
CLI
pica-agent run # poll/apply/report loop (what systemd runs)
pica-agent run --once # single cycle then exit
pica-agent once # same as above
pica-agent status # local applied vs server desired, as JSON
pica-agent --config /path/to/agent.toml once
once exits 0 even when the server is unreachable or the token is rejected —
it logs the failure and keeps the last known-good rules, never deleting live
rules. status exits 1 when the server cannot be reached.
Build the RPM (RHEL 9/10)
dnf install rpm-build python3-devel systemd-rpm-macros selinux-policy-devel
make rpm # -> rpmbuild/RPMS/noarch/pica-agent-0.1.0-1.*.noarch.rpm
dnf install ./rpmbuild/RPMS/noarch/pica-agent-0.1.0-1.*.noarch.rpm
The RPM installs /usr/bin/pica-agent, the systemd unit, config skeletons
(%config(noreplace) — upgrades never clobber local config), and the SELinux
module %{_datadir}/selinux/packages/pica_agent.pp, loaded in %post with
semodule -i and removed in %preun with semodule -r. File contexts map
/usr/bin/pica-agent, /etc/pica, /var/lib/pica-agent, and
/etc/falco/rules.d/pica to dedicated types; the service runs as the
unprivileged pica-agent user with a hardened unit (ProtectSystem=strict,
writes only to rules_dir + state dir, logs to the journal).
Troubleshooting SELinux: ausearch -m avc -ts recent | audit2why after
reproducing a denial; the policy source lives in selinux/ — bump the
policy_module version, rebuild with make -C selinux, reinstall.
Develop
python3 -m pytest -q # 11 tests, no network/sockets needed
python3 -m py_compile $(find src tests -name '*.py')
PYTHONPATH=src python3 -m pica_agent --help
Convergence rules (from docs/agent.md): null desired → report only; new
desired → download exactly the pinned commit, validate, atomic swap
(temp dir + rename), reload via the explicit reload_cmd, report; validation
failure → keep old rules + old commit; download 404 → keep + retry; 401 →
log + backoff, never crash; network error → backoff, keep serving
last known-good.