|
| 1 | +--- |
| 2 | +title: Fleet Device Management |
| 3 | +sidebar: |
| 4 | + label: Fleet MDM |
| 5 | +--- |
| 6 | + |
| 7 | +Fleet is an open-source device management platform that uses osquery to provide visibility and control over your device fleet. In Ghaf, Fleet Orbit is integrated to enable centralized device management, monitoring, and remote script execution. |
| 8 | + |
| 9 | +## Overview |
| 10 | + |
| 11 | +The Fleet integration in Ghaf provides: |
| 12 | + |
| 13 | +- **Device Visibility**: Query device state, installed software, and system configuration |
| 14 | +- **Remote Management**: Execute scripts and commands on managed devices |
| 15 | +- **Policy Enforcement**: Define and monitor compliance policies |
| 16 | +- **Fleet Desktop**: User-facing tray application for device status |
| 17 | + |
| 18 | +Fleet Orbit runs in the GUI VM and reports to a central Fleet server using a dynamic hostname derived from the device hardware, ensuring consistent identification across reboots. |
| 19 | + |
| 20 | +## Architecture |
| 21 | + |
| 22 | +``` |
| 23 | +┌─────────────────────────────────────────────────────────────┐ |
| 24 | +│ Host VM │ |
| 25 | +│ ┌─────────────────────────────────────────────────────┐ │ |
| 26 | +│ │ ghaf-dynamic-hostname service │ │ |
| 27 | +│ │ └─► /persist/common/ghaf/hostname │ │ |
| 28 | +│ └─────────────────────────────────────────────────────┘ │ |
| 29 | +│ │ (virtiofs) │ |
| 30 | +└───────────────────────────┼─────────────────────────────────┘ |
| 31 | + │ |
| 32 | +┌───────────────────────────┼─────────────────────────────────┐ |
| 33 | +│ GUI VM │ |
| 34 | +│ ▼ │ |
| 35 | +│ ┌─────────────────────────────────────────────────────┐ │ |
| 36 | +│ │ /etc/common/ghaf/hostname │ │ |
| 37 | +│ └──────────────────────┬──────────────────────────────┘ │ |
| 38 | +│ │ │ |
| 39 | +│ ┌──────────────────────▼──────────────────────────────┐ │ |
| 40 | +│ │ orbit.service │ │ |
| 41 | +│ │ └─► ORBIT_HOSTNAME_FILE=/etc/common/ghaf/hostname │ │ |
| 42 | +│ │ └─► Reports to Fleet Server │ │ |
| 43 | +│ └─────────────────────────────────────────────────────┘ │ |
| 44 | +│ │ |
| 45 | +│ ┌─────────────────────────────────────────────────────┐ │ |
| 46 | +│ │ fleet-desktop.service (user) │ │ |
| 47 | +│ │ └─► Tray icon for device status │ │ |
| 48 | +│ └─────────────────────────────────────────────────────┘ │ |
| 49 | +└─────────────────────────────────────────────────────────────┘ |
| 50 | +``` |
| 51 | + |
| 52 | +## Configuration |
| 53 | + |
| 54 | +Fleet is configured in the GUI VM via `services.orbit`. The following example shows the default configuration: |
| 55 | + |
| 56 | +```nix |
| 57 | +services.orbit = { |
| 58 | + enable = true; |
| 59 | + fleetUrl = "https://your-fleet.example.com"; |
| 60 | + enrollSecret = "your-enroll-secret"; # Use enrollSecretPath for production |
| 61 | + hostnameFile = "/etc/common/ghaf/hostname"; # Dynamic hostname from host |
| 62 | + enableScripts = true; # Allow remote script execution |
| 63 | +}; |
| 64 | +``` |
| 65 | + |
| 66 | +### Configuration Options |
| 67 | + |
| 68 | +| Option | Environment Variable | Description | |
| 69 | +|--------|---------------------|-------------| |
| 70 | +| `enable` | — | Enable Fleet Orbit systemd service | |
| 71 | +| `fleetUrl` | `ORBIT_FLEET_URL` | Base URL of the Fleet server | |
| 72 | +| `enrollSecret` | `ORBIT_ENROLL_SECRET` | Enroll secret for Fleet server | |
| 73 | +| `enrollSecretPath` | `ORBIT_ENROLL_SECRET_PATH` | Path to enroll secret file (recommended for production) | |
| 74 | +| `fleetCertificate` | `ORBIT_FLEET_CERTIFICATE` | Path to Fleet server certificate chain | |
| 75 | +| `hostnameFile` | `ORBIT_HOSTNAME_FILE` | Path to file containing hostname (Ghaf-specific) | |
| 76 | +| `hostIdentifier` | `ORBIT_HOST_IDENTIFIER` | Host identifier mode: `uuid` or `instance` | |
| 77 | +| `enableScripts` | `ORBIT_ENABLE_SCRIPTS` | Enable remote script execution | |
| 78 | +| `debug` | `ORBIT_DEBUG` | Enable debug logging | |
| 79 | +| `insecure` | `ORBIT_INSECURE` | Disable TLS certificate verification | |
| 80 | + |
| 81 | +### Secret Management |
| 82 | + |
| 83 | +For production deployments, use `enrollSecretPath` with a secrets management solution like sops-nix: |
| 84 | + |
| 85 | +```nix |
| 86 | +services.orbit = { |
| 87 | + enable = true; |
| 88 | + fleetUrl = "https://fleet.example.com"; |
| 89 | + enrollSecretPath = config.sops.secrets.fleet-enroll-secret.path; |
| 90 | + hostnameFile = "/etc/common/ghaf/hostname"; |
| 91 | +}; |
| 92 | +
|
| 93 | +sops.secrets.fleet-enroll-secret = { |
| 94 | + sopsFile = ./secrets.yaml; |
| 95 | +}; |
| 96 | +
|
| 97 | +For CI/dev images, keep secrets out of the image and inject the enroll secret at |
| 98 | +runtime. Ghaf uses a shared host path for dynamic hostname; you can reuse the |
| 99 | +same mechanism for the enroll secret (for example, `/etc/common/ghaf/fleet/enroll` |
| 100 | +inside the guest). |
| 101 | +``` |
| 102 | + |
| 103 | +## Dynamic Hostname |
| 104 | + |
| 105 | +Ghaf uses hardware-derived hostnames for device identification. The `ghaf-dynamic-hostname` service on the host generates a unique hostname based on hardware identifiers (DMI serial, disk UUID, or MAC address) and writes it to `/persist/common/ghaf/hostname`. |
| 106 | + |
| 107 | +This file is shared with VMs via virtiofs and mounted at `/etc/common/ghaf/hostname`. The Fleet module is patched to read the hostname from this file via the `hostnameFile` option, ensuring devices are consistently identified on the Fleet server. |
| 108 | + |
| 109 | +The orbit service includes a `ConditionPathExists` directive to wait for the hostname file before starting. |
| 110 | + |
| 111 | +## Systemd Services |
| 112 | + |
| 113 | +Two systemd services are created: |
| 114 | + |
| 115 | +- **`orbit.service`** (system): Runs the Orbit agent, connecting to the Fleet server |
| 116 | +- **`fleet-desktop.service`** (user): Runs Fleet Desktop tray application in graphical sessions |
| 117 | + |
| 118 | +## Logs |
| 119 | + |
| 120 | +Orbit logs are written to: |
| 121 | +- `/var/log/orbit/orbit.log` — Main Orbit log |
| 122 | +- `/var/log/orbit/osquery/` — osquery result and status logs |
| 123 | +- `journalctl -u orbit` — Systemd journal |
| 124 | + |
| 125 | +## NixOS-Specific Considerations |
| 126 | + |
| 127 | +The Ghaf Fleet integration includes patches for NixOS compatibility: |
| 128 | + |
| 129 | +- **Disabled auto-updates**: NixOS manages packages declaratively; Orbit's auto-update is disabled |
| 130 | +- **Disabled keystore**: Secrets are not stored in OS-specific keystores |
| 131 | +- **Fixed paths**: osqueryd binary path is set from Nix store |
| 132 | +- **Shebang patching**: Remote scripts have shebangs patched to use NixOS paths |
| 133 | + |
| 134 | +## Troubleshooting |
| 135 | + |
| 136 | +### Orbit not starting |
| 137 | + |
| 138 | +Check if the hostname file exists: |
| 139 | +```bash |
| 140 | +ls -la /etc/common/ghaf/hostname |
| 141 | +``` |
| 142 | + |
| 143 | +Check service status: |
| 144 | +```bash |
| 145 | +systemctl status orbit |
| 146 | +journalctl -u orbit -f |
| 147 | +``` |
| 148 | + |
| 149 | +### Connection issues |
| 150 | + |
| 151 | +Enable debug logging: |
| 152 | +```nix |
| 153 | +services.orbit = { |
| 154 | + debug = true; |
| 155 | + # For testing only: |
| 156 | + # insecure = true; |
| 157 | +}; |
| 158 | +``` |
| 159 | + |
| 160 | +### Script execution failures |
| 161 | + |
| 162 | +Verify scripts are enabled and check logs: |
| 163 | +```bash |
| 164 | +cat /var/log/orbit/orbit.log | grep -i script |
| 165 | +``` |
| 166 | + |
| 167 | +## References |
| 168 | + |
| 169 | +- [Fleet Documentation](https://fleetdm.com/docs) |
| 170 | +- [osquery Documentation](https://osquery.readthedocs.io/) |
| 171 | +- [Dynamic Hostname Generation](/ghaf/dev/ref/dynamic-hostname) — Ghaf hardware-derived hostname system |
0 commit comments