Skip to navigation

APF Security Best Practices

View as Markdown

APF’s guarantee is that the only durable authority over agent policy is a bundle signed outside the runtime. Every control on this page protects part of that guarantee. Each table lists the control, its default, what to do, and what you lose by changing it.

Signing and Trust

ControlDefaultRecommendationRisk if weakened
Bundle signing keyThe repository’s committed apf-dev key in development setups.Generate your own key, ideally held by a signing service. Never trust apf-dev.pub outside local development.Anyone with the repository can sign bundles your hosts accept.
Private key locationNot on the host.Keep bundle signing keys in the authoring and publish path only.A host compromise yields a key that can sign policy for every host.
Trust-root directory./trust-roots unless set./etc/apf/trust-roots, owned by root:apf, mode 0750, files 0640.Whoever can write a trust root decides which bundles are accepted.
Document signing keyEphemeral if not set, recorded as a weakened control.Set a persistent key, root:root 0600, delivered as a systemd credential. Keep it distinct from the bundle signing key.An ephemeral key changes on every restart, and the gateway’s JWKS rotation must catch up before tokens verify.
Key rotationManual.Rotate with old and new trust roots side by side.Without a rotation practice, a compromised key stays trusted.

Policy Supply

ControlDefaultRecommendationRisk if weakened
ReviewYour source control.Require review for every policy change, including manifest order and data changes.One person can change what every agent may do.
Pre-publish validationNot enforced by APF.Validate schemas, compile offline, and build the evaluator before signing.A Cedar error passes the policy service and splits enforcement between old and new policy.
policy_revisionRequired.Increase it for every change. Start at 1.Different content at an accepted revision is rejected. A reset counter reopens rollback.
Validity windowAuthor-chosen.Keep expires_at short enough to force regular re-signing, and renew ahead of expiry.A long window keeps a superseded bundle loadable for longer. An expiry with no renewal stops the service at its next restart.
Bundle source transportAny URL.https:// with a token file.An on-path attacker can withhold updates. A token in arguments is visible to local processes.

OpenShell Integration

ControlDefaultRecommendationRisk if weakened
Interceptor failure_policyAPF declares it per binding.Keep APF’s per-binding values, and do not override them with per-binding entries in OpenShell. Set the registration’s failure_policy to fail_closed as well.An override to fail-open lets OpenShell admit sandboxes ungoverned while APF is unavailable.
Middleware on_errorSet in sandbox policy.fail_closed.Requests flow without content decisions while APF is unavailable.
Extension authenticationRequired unless the insecure flag is set.Authenticated listeners with TLS on every surface. Never set the insecure flags outside local development.Any local process can impersonate OpenShell, and sandbox binding is lost.
Interceptor binding_policyOpenShell’s dynamic.Keep dynamic. APF does not work with allowlist or exact in this release.With dynamic, a changed APF manifest changes APF’s authority without an operator decision, so review APF’s bindings when you upgrade it.
Agent policy proposalsOff in OpenShell.Set agent_policy_proposals_enabled = false at global scope and re-check it on every review.The effective policy becomes whatever OpenShell accepted last, not what was signed.
Middleware coverageOnly hosts listed in network_middlewares.Route every host that carries credentials or sensitive data through apf-content-policy.Hosts that are not listed get no content decisions.
allow_uninspected_credentialsAlways false. APF does not project the field in this release.Leave it unset, so the policy reads the way it is enforced.If a later release projects it, credentials could travel on paths no one inspects.

Hosts

ControlDefaultRecommendationRisk if weakened
Service accountapf, from apf.sysusers.conf.Run both services as apf. Keep OpenShell under its own account with no group overlap.A compromise of one daemon reads both daemons’ secrets.
Unix socketsMode 0600, peer UID checked.Keep the defaults.Other local accounts reach the policy service and the decision API.
OpenShell listener binds127.0.0.1 in the shipped env file.Loopback, plus a host firewall rule dropping non-loopback traffic to the ports.The extension surfaces become reachable off the host.
Policy service HTTP surfaceOff.Enable only on loopback, for the JWKS the gateway needs and local health checks. Firewall the port.Document and refresh routes become one misconfiguration away from off-host exposure.
Unit hardeningStrict sandboxing in the shipped units.Keep it. Record a reason next to every relaxation.A compromised daemon can modify its own binary or configuration.
Interactive restartsDenied by the shipped polkit rule.Restart only through your deploy pipeline, and restrict root with a sudoers wrapper.Ad hoc restarts can load unreviewed configuration.
Tamper detectionNot configured.Add auditd watches on the units, /etc/apf, and the binaries.Changes go unnoticed.

Evidence

ControlDefaultRecommendationRisk if weakened
Audit directoryRequired.Writable only by the service, readable by your collector.Missing records or tampered evidence.
ShippingNot configured.Ship records off the host promptly. Local files are not tamper-evident.An attacker on the host can alter the record.

Policy Authoring

  • Guard every optional attribute with has. A forbid whose condition errors does not fire.
  • Give every statement an @id, so deny reasons point at the statement.
  • Add spec.schema to rules that read arguments, so field typos fail when the evaluator is built.
  • Keep detection flags and their classification labels aligned, because a finding with label_source fails closed when the label is missing.

Next Steps