Overview
Enrollment is a one-time process that establishes trust between an agent and the collector. After enrollment, all communication uses mTLS (mutual TLS) with certificates signed by the collector's internal CA.
Security Model
| Component | Purpose |
|---|---|
| Enrollment Token | One-time use, proves admin authorized this enrollment |
| Machine Fingerprint | Binds certificate to specific hardware, prevents cert theft |
| CSR | Agent generates its own private key - never leaves the machine |
| Internal CA | Collector signs agent certificates, enabling mTLS |
Enrollment Flow
ADMIN COLLECTOR AGENT
│ │ │
┌────┴────┐ │ │
│ Step 1 │ │ │
│ Create │ │ │
│ Token │ │ │
└────┬────┘ │ │
│ │ │
│── token create ───────────►│ │
│ --manifest "standard" │ │
│ --allowed "std,monitor" │ │
│ │ │
│◄─── Token: ENROLL-7842 ────│ │
│ │ │
┌────┴────┐ │ │
│ Step 2 │ │ │
│ Deploy │ (out-of-band: SSH, config management, etc.) │
│ Token │─────────────────────────────────────────────────────► │
└─────────┘ │ │
│ │ ┌───────────────┤
│ │ │ Step 3 │
│ │ │ Agent Init │
│ │ │ • Fingerprint │
│ │ │ • Key pair │
│ │ │ • CSR │
│ │ └───────────────┤
│ │ │
│ │◄──────────────────────────────│
│ │ EnrollRequest │
│ │ • token │
│ │ • csr │
│ │ • fingerprint │
│ │ │
│ ┌─────┤ │
│ │ Step 4: Validate & Sign │
│ │ • Token valid? │
│ │ • Sign CSR with CA │
│ │ • Load manifest from token │
│ └─────┤ │
│ │ │
│ │───────────────────────────────►
│ │ EnrollResponse │
│ │ • agent_id │
│ │ • certificate │
│ │ • manifest_signing_pubkey │
│ │ • suggested_manifest ◄──────┼─ Manifest attached
│ │ • allowed_manifests │
│ │ │
│ │ ┌───────────────┤
│ │ │ Step 5 │
│ │ │ PENDING_MANIFEST
│ │ │ (dormant) │
│ │ └───────────────┤
│ │ │
LOCAL ADMIN ──────────────────────┼───────────────────────────────►
│ sudo eyelog-agent │ │ Step 6 │
│ manifests review │ │ Approve │
│ │ │ → ACTIVE │
│ │ └───────────────┤
│ │ │
│ │◄══════════════════════════════╡
│ │ FULLY OPERATIONAL │
│ │══════════════════════════════►│
Step 1: Create Token with Manifest
$ eyelog-collector token create \
--name "web-server-01" \
--group "production" \
--manifest "standard" \
--allowed "standard,monitor-only"
Token created!
Token: ENROLL-7842
Manifest: standard v1.0.0
Allowed: standard, monitor-only
Expires: 2026-01-24 15:30:00
Token Properties:
- Short display code (e.g., ENROLL-7842) - Easy to type
- Attached manifest - What the agent should use
- Allowed list - What alternatives are permitted
- Single use only
- Configurable expiration (default 24h)
Machine Fingerprint
The agent generates a hardware fingerprint using hardware-burned identifiers that survive OS reinstalls:
| Component | Source | Why |
|---|---|---|
| SMBIOS Product UUID | Motherboard firmware | Primary identifier, set at manufacturing |
| System Serial Number | OEM assigned | Unique per machine from manufacturer |
| Board Serial Number | Motherboard | Additional uniqueness factor |
Platform-specific collection:
- Linux:
/sys/class/dmi/id/* - macOS: IOKit (IOPlatformUUID, IOPlatformSerialNumber)
- Windows: WMIC (csproduct, baseboard)
Design Decision: We explicitly avoid MAC addresses, OS machine-id, disk serials, and hostname. These can change for legitimate reasons (NIC replacement, OS reinstall, disk upgrade) and shouldn't affect agent identity. Only motherboard replacement triggers a new agent.
Certificate Storage
/etc/eyelog/
├── agent.yaml # Configuration
└── certs/
├── agent.key # Private key (0600, never transmitted)
├── agent.crt # Signed certificate from collector
├── ca.crt # Collector's CA certificate
└── agent.id # Agent ID (for reference)
Re-Enrollment
If an agent with the same fingerprint enrolls again:
- Same machine, new token → Returns same agent_id, new certificate
- Different machine, stolen cert → Fingerprint won't match, rejected
- VM clone → Different fingerprint, treated as new agent
Certificate Renewal
Before the certificate expires (default 7 days), the agent requests renewal using mTLS with the existing certificate:
rpc RenewCertificate(RenewRequest) returns (RenewResponse);
message RenewRequest {
bytes csr = 1; // New CSR
string machine_fingerprint = 2; // Must match original
}