No description
  • Python 96.1%
  • Makefile 3.9%
Find a file
2026-09-09 08:47:08 +10:00
config Initial pica-agent: poll/apply/report loop, systemd unit, SELinux policy, RPM spec 2026-09-09 08:47:08 +10:00
packaging/rpm Initial pica-agent: poll/apply/report loop, systemd unit, SELinux policy, RPM spec 2026-09-09 08:47:08 +10:00
selinux Initial pica-agent: poll/apply/report loop, systemd unit, SELinux policy, RPM spec 2026-09-09 08:47:08 +10:00
src/pica_agent Initial pica-agent: poll/apply/report loop, systemd unit, SELinux policy, RPM spec 2026-09-09 08:47:08 +10:00
systemd Initial pica-agent: poll/apply/report loop, systemd unit, SELinux policy, RPM spec 2026-09-09 08:47:08 +10:00
tests Initial pica-agent: poll/apply/report loop, systemd unit, SELinux policy, RPM spec 2026-09-09 08:47:08 +10:00
LICENSE Initial pica-agent: poll/apply/report loop, systemd unit, SELinux policy, RPM spec 2026-09-09 08:47:08 +10:00
Makefile Initial pica-agent: poll/apply/report loop, systemd unit, SELinux policy, RPM spec 2026-09-09 08:47:08 +10:00
pyproject.toml Initial pica-agent: poll/apply/report loop, systemd unit, SELinux policy, RPM spec 2026-09-09 08:47:08 +10:00
README.md Initial pica-agent: poll/apply/report loop, systemd unit, SELinux policy, RPM spec 2026-09-09 08:47:08 +10:00

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

  1. 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.
  2. Issue a per-host token on the server (printed once): flask --app pica agent-token create <hostname>.
  3. Configure the node: copy config/agent.toml.example to /etc/pica/agent.toml, set server_url/hostname/auth_token (or put PICA_AUTH_TOKEN in /etc/pica/environment, mode 0600).
  4. systemctl enable --now pica-agent.
  5. Verify in Pica: host auto-registers into default on 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.