Managed · Managed configuration
Sign, deploy and verify managed.toml
Signing #
A signature proves the file is the one the company published, not one an administrator account wrote by hand. v1 supports keyless cosign only: the file is signed by a CI workflow whose identity is in the certificate. Signing with a key pair waits for lockkit#15 and is not supported.
-
Keep
managed.tomlin a repository of the company, and sign it with the workflowexamples/managed/sign.yml(.github/workflows/sign.ymlthere). Its core:permissions: contents: write id-token: write # the OIDC token cosign signs with steps: - uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2 - run: cosign sign-blob --yes --bundle managed.toml.sigstore.json managed.toml -
Write
signer.toml, the identity the tools accept. Both keys are required:certificate_identity_regexpmust start with^https://andoidc_issuermust be anhttps://URL (examples/managed/signer.toml):certificate_identity_regexp = "^https://github.com/<org>/<repo>/.github/workflows/sign.yml@refs/heads/main$" oidc_issuer = "https://token.actions.githubusercontent.com" -
Deploy the three files together, root-owned, in the managed directory (every recipe has a switch for it), and install
cosignon the hosts (brew install cosign, or the release the family's installer pins: docs/install.md). -
Set
[family] require_signature = trueonce every host has the bundle: from then on, a host without it refuses to start.
Check a bundle before deploying it, with the same identity:
cosign verify-blob --bundle managed.toml.sigstore.json \
--certificate-identity-regexp '^https://github.com/<org>/<repo>/.github/workflows/sign.yml@refs/heads/main$' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
managed.toml
<tool> managed show and lox managed show then print signed: yes and
Managed by <organisation> (sha256:…, signed). Any change to one byte of
managed.toml, of the bundle or of signer.toml without signing again is a
refusal: the signature does not verify. The tools re-check the signature
only when one of the three files changes.
Deploying #
Each recipe installs the directory 0755 and the files 0644, owned by
root:root (macOS root:wheel). It installs managed.toml, and
signer.toml with the bundle when you sign. No restart is needed: a
running console reads the new file at its next policy check, and lox run
applies a change of the file alone without an Apply? question.
Ansible #
examples/managed/ansible/tasks.yml
creates the directory and copies the files with ansible.builtin.copy
(owner, group, mode);
playbook.yml picks the paths
and group per OS (Linux and macOS):
- name: Lock Family managed files
ansible.builtin.copy:
src: "{{ lockfamily_files }}/{{ item }}"
dest: "{{ lockfamily_managed_dir }}/{{ item }}"
owner: root
group: "{{ lockfamily_root_group }}"
mode: "0644"
loop: "{{ ['managed.toml'] + (['signer.toml', 'managed.toml.sigstore.json'] if lockfamily_signed | bool else []) }}"
ansible-playbook -i inventory examples/managed/ansible/playbook.yml -e lockfamily_signed=true
Debian and rpm packages #
examples/managed/nfpm.yaml builds a deb
and an rpm with nfpm: contents: entries
with file_info modes and owners, the signature entries ready to
uncomment. Bump version with every change of the policy and publish the
package in the repository your hosts already update from:
cd examples/managed
nfpm package -p deb -f nfpm.yaml # example-corp-lockfamily-managed_1.0.0-1_all.deb
nfpm package -p rpm -f nfpm.yaml
sudo apt install ./example-corp-lockfamily-managed_1.0.0-1_all.deb
The files are not marked as configuration files: an upgrade of the package replaces them, even if someone changed them by hand.
macOS #
examples/managed/macos/build.sh
builds a .pkg with pkgbuild: its payload is
/Library/Application Support/lockfamily/, and its
postinstall script sets
root:wheel, 0755 and 0644:
cd examples/managed
sh macos/build.sh 1.0.0 # example-corp-lockfamily-managed-1.0.0.pkg
sudo installer -pkg example-corp-lockfamily-managed-1.0.0.pkg -target /
To deploy it through an MDM, sign the .pkg with your Developer ID
Installer certificate (productsign) and upload it as a package:
- Jamf Pro: Settings → Computer management → Packages, then a policy that installs it, scoped to the fleet, triggered at check-in.
- Microsoft Intune: Apps → macOS → Add → macOS app (PKG), assigned as required to the device groups.
Verifying a deployment #
On a host, after the deployment:
ls -ld <directory> <directory>/*:root root(root wheelon macOS),drwxr-xr-xand-rw-r--r--.lox managed show:Managed by <organisation> (sha256:…),signed: yeswhen you sign, and the rules you wrote.<tool> managed checkin a project of each Lock:… within the company policy, or the lines to fix in the user's or project's profiles.lox doctor:managed: Managed by ….
Troubleshooting #
Every tool reports the same reason, after managed: <path>: (lox run: …, <tool> managed show: …, blocked by company policy: … in managed check); a refused file always exits 3.
| Message | Fix |
|---|---|
must be writable by root only (mode 0666) |
chmod 0644 the file (0755 its directory). |
must be owned by root (it is owned by uid 1000) |
chown root:root (root:wheel) the file and its directory. |
is not a regular file |
Copy the file in place of the symbolic link. |
invalid TOML at line N |
Fix the syntax; python3 -c 'import tomllib,sys; tomllib.load(open(sys.argv[1],"rb"))' managed.toml finds it. |
version = 1 is required, version 2 is not supported (this build reads version 1) |
Set version = 1. |
unknown key "lockvpn", [lox]: unknown key "egress_allow" |
A section or key of no tool, or a typo: only the tool whose section it is refuses. |
key "token" looks like a secret; the managed file holds no secrets |
Remove it: the file is readable by every user. |
[locksql.profiles.prod-erp]: the managed profile is beyond the managed bounds: locksql.bounds.auto.ddl = deny (profile prod-erp: auto.ddl = ask) |
A managed profile must sit within the bounds, built-in defaults included: set the field in the profile (auto = { ddl = "deny" }). |
is signed but …/signer.toml, the company identity to check it against, is missing |
Deploy signer.toml with the bundle. |
certificate_identity_regexp must be an anchored ^https:// regexp and oidc_issuer an https:// URL |
Fix signer.toml. |
the signature does not verify: cosign is not installed |
Install cosign on the host's PATH. |
the signature does not verify: … |
The file, the bundle or signer.toml changed since signing, or the identity does not match: sign again, or fix signer.toml. |
[family] require_signature is set but managed.toml.sigstore.json is missing |
Deploy the bundle. |
is signed, but this lox cannot vouch for its signature inside the locks' containers (…): upgrade lox |
Upgrade lox to v0.11.0 or later. |
<tool> vX.Y.Z predates managed support: upgrade it or set [lox] image_min |
Upgrade that Lock (lox run updates it unless --no-upgrade). |
blocked by company policy: lox.image_min.locksql = v0.18.0 (the image is locksql v0.17.2) |
Run lox run without --no-upgrade, or pin a newer release. |
blocked by company policy: <key> … in managed check |
The user's or project's profile goes beyond the bounds: tighten it (see Violations). |
profile "<name>" is provided by the company's managed configuration and cannot be redefined in the project config: rename yours |
Rename the user's or project's profile. |
managed: … changed while lox read it: lox run again |
The file was replaced during lox run's read: run it again. |