Publish and Distribute Bundles
A signed bundle reaches the policy service in one of two ways. Either it is placed on the host as files, or the policy service fetches it from an HTTPS endpoint and polls for updates. In both cases the policy service verifies the bundle before it serves anything from it, so the delivery channel does not need to be trusted for integrity.
Publish to an Artifact Store
APF does not prescribe an artifact store or a layout. A layout that keeps every revision and a stable pointer to the newest one works well with the HTTP source:
Point each policy service at the latest archive. Keep earlier revisions for audit, but remember that the anti-rollback counter refuses to load them again.
The store must answer a HEAD request for the archive with a success status, because the policy service sends one before every polling download and treats a failed HEAD as a failed poll. The first fetch at startup does not use HEAD. If the store returns an x-checksum-sha256 or ETag header whose value is the archive’s hex SHA-256, the policy service skips the download when the archive has not changed. A quoted or opaque ETag never matches, so the archive is downloaded on every poll.
Deliver as Local Files
Copy the signed manifest and all of its members to the host, keeping the directory layout. On a systemd host, the manifest lives at /etc/apf/bundle/bundle.yaml, owned by root and readable by the apf group:
Anyone who can write these files can replace the bundle, but cannot get an unsigned or older bundle accepted.
Fetch over HTTP
Configure the policy service to fetch from a URL:
Use an https:// URL. The service accepts http:// without a warning. A tampered or unsigned bundle is still rejected, but an attacker on the path could read the bundle source token, which is sent in cleartext, withhold updates, or replay the current bundle until it expires.
systemd Hosts
The shipped apf-policy-service.service restricts the service to Unix sockets. To use the HTTP source on a systemd host:
- Add
AF_INETandAF_INET6toRestrictAddressFamiliesin the unit, and record why in a comment. - Add
LoadCredential=bundle-source-token:/etc/apf/bundle-source-token, and store the token at that path asroot:rootwith mode0600. - In
/etc/apf/policy-service.env, setAPF_POLICY_SOURCE=http,APF_BUNDLE_SOURCE_URL, andAPF_BUNDLE_SOURCE_TOKEN_FILE=/run/credentials/apf-policy-service.service/bundle-source-token. - Set the poll interval with
APF_BUNDLE_SOURCE_POLL_INTERVAL. The commented example in the shipped env file uses a different name,APF_BUNDLE_SOURCE_POLL_INTERVAL_SECS, which the service does not read.
Refer to Host and Workstation Deployment.
Per-Caller Bundle Variants
The HTTP source and the claims resolver contain the building blocks for serving different bundles to different callers: a {variant} placeholder in the bundle URL, and a --claims-config file that maps a caller’s user_subject to a variant name:
Per-caller variants do not work end to end in this release. With a {variant} URL, the policy service skips the initial fetch and nothing in the service loads a variant afterward, so no bundle is ever served. With a literal URL, a caller whose subject maps to a variant gets document not found. Use a literal URL with the default passthrough resolver.
How Updates Propagate
Each policy service fetches and verifies independently, and each policy gateway polls its own policy service. A newly published bundle therefore reaches each host within one poll interval of the policy service plus one refresh interval of the gateway, 10 seconds by default, with no restart and no software deployment. Refer to Update Policies.
Next Steps
- To see what the policy service checks before it activates a bundle, refer to Verify and Activate.
- To plan a policy service shared by many hosts, refer to Deployment Models.