fix: Opt-in lockdown for global config (#278)

* fix: Reject overriding managed flags

* fix: Lockdown overrides when global config present

* fix: Opt-in lock-down enforcement for global config

* fix: Code review fixes

* fix: Code review fixes

* fix: Code review fixes
This commit is contained in:
Abhisek Datta
2026-05-21 16:37:26 +05:30
committed by GitHub
parent b15ce33fe4
commit 0e3bb52f8c
9 changed files with 487 additions and 51 deletions
+61
View File
@@ -72,6 +72,8 @@ PMG_PROXY_INSTALL_ONLY=true pmg npm install express
3. Config file (`config.yml`)
4. Built-in defaults
Under a [globally managed config](#globally-managed-configuration) with `global_lockdown` enabled, PMG disables `PMG_*` and managed-flag overrides.
**Limitation**
@@ -79,3 +81,62 @@ PMG_PROXY_INSTALL_ONLY=true pmg npm install express
If a key is commented out (e.g. `# endpoint_id: "my-machine"`) or missing entirely, `set` will
return a "key not found" error. To fix this, uncomment or add the key manually via `pmg config edit`,
or run `pmg setup install` to merge missing template keys into your config.
## Globally Managed Configuration
For centrally managed or fleet deployments, PMG can read an OS-level **global config file**. When this file exists, it is authoritative: PMG uses it and ignores the per-user `config.yml` (the two are never merged). An administrator ships a machine-wide baseline this way, and can lock it (see [Lockdown](#lockdown)) to forbid user overrides.
**Paths** (used when the file is present):
| OS | Global config path |
|---|---|
| macOS | `/Library/Application Support/safedep/pmg/config.yml` |
| Linux | `/etc/safedep/pmg/config.yml` |
| Windows | `%PROGRAMDATA%\safedep\pmg\config.yml` |
Check whether a global config is active with `pmg setup info`:
```bash
pmg setup info
# Config Source: global <- global config, overrides allowed
# Config Source: global (locked) <- global config with lockdown enabled
# Config Source: user <- per-user config in effect
```
### Behaviour
Whenever a global config file is present:
- **It is authoritative.** PMG ignores the per-user `config.yml`. The file may be **partial**. Keys it does not set fall back to PMG's built-in defaults, not to a user's values.
- **`config set` and `config edit` fail.** They return an error stating the config is globally managed. To change it, deploy an updated file at the OS path, which is root-owned and not writable by users.
- **`pmg setup install` skips the per-user config.** It still creates shell aliases and shims per user.
By default a user can still override the global config's values at runtime through `PMG_*` environment variables and CLI flags. Enable lockdown to forbid that.
### Lockdown
Add `global_lockdown: true` to the global config to enforce it:
```yaml
# Only meaningful in the global config file.
global_lockdown: true
```
When lockdown is on:
- **CLI flags that would change a managed value fail fast.** For example, `pmg --sandbox=false ...` or `pmg --paranoid ...` errors out instead of overriding policy. Governed flags: `--transitive`, `--transitive-depth`, `--include-dev-dependencies`, `--paranoid`, `--skip-event-log`, `--proxy-mode`, `--sandbox`, `--sandbox-enforce`, `--sandbox-profile`, `--sandbox-allow`, `--skip-dependency-cooldown`. Operational flags such as `--dry-run` keep working.
- **`PMG_*` variables cannot change the config**, including `PMG_INSECURE_INSTALLATION` (which otherwise bypasses malicious-package blocking).
PMG reads `global_lockdown` straight from the global file, so a user cannot flip it through env or CLI. If the global file exists but cannot be read or parsed, PMG fails closed and treats it as locked. `PMG_CONFIG_DIR` and `PMG_CACHE_DIR` still relocate per-user state directories (logs, cache) in any mode, but leave the managed config alone.
### Precedence
| Mode | Effective order (highest to lowest) |
|---|---|
| No global config | CLI flags > `PMG_*` env > per-user `config.yml` > built-in defaults |
| Global config, no lockdown | CLI flags > `PMG_*` env > global config > built-in defaults |
| Global config, `global_lockdown: true` | global config > built-in defaults (env and managed-flag overrides refused) |
### Deploying via MDM (macOS)
Scripts to install or update PMG and deploy a global config across a macOS fleet (Jamf, Mosyle, Kandji, Intune) live in [`scripts/mdm`](../scripts/mdm). Bundle a `config.yml` next to the scripts. The installer places it at the global path, and the uninstaller removes it. See the [`scripts/mdm` README](../scripts/mdm/README.md) for details.