Skip to content

Commit 644514d

Browse files
vadikabrianmcgillion
authored andcommitted
fleet: add FleetDM client with dynamic hostname support
Add Fleet MDM client (Orbit) integration for device management: - Add fleet module with Orbit and Fleet Desktop packages (v1.46.0) - Patch orbit to support --hostname-file flag for dynamic hostname identification from external file instead of system hostname - Add NixOS-specific patches for script execution and path handling - Enable Orbit service in guivm with dynamic hostname from /etc/common/ghaf/hostname (shared via virtiofs from host) - Add systemd ConditionPathExists to wait for hostname file This allows Fleet server to identify devices using the hardware-derived dynamic hostname generated by ghaf-dynamic-hostname service on the host. Signed-off-by: vadik likholetov <[email protected]>
1 parent 3fdd61f commit 644514d

File tree

20 files changed

+986
-4
lines changed

20 files changed

+986
-4
lines changed

docs/astro.config.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ export default defineConfig({
9696
"ghaf/dev/ref/idsvm-development",
9797
"ghaf/dev/ref/systemd-service-config",
9898
"ghaf/dev/ref/dynamic-hostname",
99+
"ghaf/dev/ref/fleet",
99100
"ghaf/dev/ref/memory-wipe",
100101
"ghaf/dev/ref/kill_switch",
101102
"ghaf/dev/ref/wireguard-gui",

docs/src/content/docs/ghaf/dev/ref/dynamic-hostname.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ ghaf.identity.dynamicHostName.digits = 6; # 1 million possibilities
222222
- **User Visibility**: Users can identify their specific hardware instance across host and VMs
223223
- **Network Troubleshooting**: Consistent hostname helps with debugging network issues and tracking device behavior
224224
- **Reproducible Testing**: Deterministic identities allow consistent testing environments across rebuilds
225-
- **Fleet Management**: Same hardware always produces same identities for reliable device tracking
225+
- **Fleet Management**: Same hardware always produces same identities for reliable device tracking (see [Fleet Device Management](/ghaf/dev/ref/fleet))
226226

227227
## Technical Details
228228

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
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

modules/common/identity/dynamic-hostname.nix

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ let
1717

1818
cfg = config.ghaf.identity.dynamicHostName;
1919

20+
# Get list of active microvm names (empty if microvms not configured)
21+
activeMicrovms =
22+
if config ? microvm && config.microvm ? vms then builtins.attrNames config.microvm.vms else [ ];
23+
2024
computeScript = pkgs.writeShellApplication {
2125
name = "ghaf-compute-hostname";
2226
runtimeInputs = [
@@ -146,6 +150,20 @@ let
146150
# Generate device-id from our hardware-derived ID (for backward compatibility)
147151
# Use the same ID but format as hex string with dashes like: 00-01-23-45-67
148152
printf "%010x" "$((10#$id))" | fold -w2 | paste -sd'-' | tr -d '\n' > "$shareDir/../device-id"
153+
154+
# Generate a stable UUID from the hardware key and export it for VMs.
155+
uuid_hash=$(echo -n "$key" | sha256sum | cut -d' ' -f1)
156+
uuid="''${uuid_hash:0:8}-''${uuid_hash:8:4}-5''${uuid_hash:13:3}-a''${uuid_hash:17:3}-''${uuid_hash:20:12}"
157+
printf "%s" "$uuid" > "$shareDir/uuid"
158+
159+
# Generate unique machine-ids for all VMs based on hardware ID
160+
# Each VM gets a deterministic ID derived from hardware + VM name
161+
${lib.concatMapStringsSep "\n" (vm: ''
162+
mkdir -p /persist/storagevm/${vm}/etc
163+
vm_key="$key-${vm}"
164+
vm_hash=$(echo -n "$vm_key" | sha256sum | cut -d' ' -f1)
165+
echo -n "$vm_hash" > /persist/storagevm/${vm}/etc/machine-id
166+
'') activeMicrovms}
149167
'';
150168
};
151169
in

modules/common/security/default.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@
88
./fail2ban.nix
99
./pwquality.nix
1010
./ssh-tarpit
11+
./fleet
1112
];
1213
}

0 commit comments

Comments
 (0)