Skip to content

feature: Enroll with a fleet server from the installer - #1402

Merged
mickem merged 3 commits into
mainfrom
feature/installer-fleet-enrollment
Aug 13, 2026
Merged

feature: Enroll with a fleet server from the installer#1402
mickem merged 3 commits into
mainfrom
feature/installer-fleet-enrollment

Conversation

@mickem

@mickem mickem commented Aug 13, 2026

Copy link
Copy Markdown
Owner

No description provided.

mickem added 3 commits August 12, 2026 19:36
Add FLEET_SERVER and FLEET_TOKEN (plus FLEET_HOSTNAME, FLEET_CA,
FLEET_VERIFY_MODE and FLEET_INSECURE) to the Windows MSI so a host can be
handed over to an NSClient fleet server as it is installed, instead of
running `nscp enroll` afterwards:

    msiexec /qn /i NSCP-<ver>-x64.msi \
        FLEET_SERVER=https://fleet.example.com FLEET_TOKEN=<token>

Two custom actions do the work. ScheduleEnrollFleet (immediate) validates
the properties and hands them to the deferred half, failing the install
before a single file is copied when the token is missing, the url has no
scheme, the url is plain HTTP without FLEET_INSECURE=1, or verification is
turned off without it - the same rules `nscp enroll` enforces, since the
token is a credential and the enrollment response supplies the trust
anchors the agent uses from then on.

ExecEnrollFleet (deferred, elevated, after InstallFiles) generates the key
pair, posts the CSR and stores the returned material as
${certificate-path}/agent-state.json inside the install folder. An
existing manifest is kept, so an upgrade, repair or retry after a failed
install does not need a second bootstrap token. The fleet server is
verified against a temporary export of the Windows ROOT store, since the
service has not written ${ca-path} yet during an install. Any failure is
reported as a described error and fails the install rather than leaving an
agent that silently never joined the fleet.

The configuration gains the include of the fleet-managed file (with a
placeholder so it is not a dangling reference until the first sync), the
bootstrap token is hidden from the MSI log, and uninstall removes the
enrollment manifest along with the other key material.

Assisted-by: Claude Code:claude-opus-5
Signed-off-by: Michael Medin <michael@medin.name>
The enrollment POST had no deadline: http_client_options::timeout_seconds_
defaults to 0 (wait forever) and default_post never set it. max_attempts does
not bound that on its own - it only counts requests that finished, and a fleet
server that accepts the connection and then stops answering never produces one.
From the installer the call runs inside a deferred custom action, so an
unresponsive server wedges msiexec mid-script on an unattended install with
nobody there to interrupt it. enrollment_request now carries a timeout (60s,
the same default as the fleet sync), overridable with `nscp enroll --timeout`.

ExecEnrollFleet decided whether to export the Windows ROOT store from the
verify mode alone, so an enrollment over an http:// url with FLEET_INSECURE=1
exported it anyway - and failed the whole install when the store yielded no
certificates, over a verification that a plain HTTP call never performs. Only
export when the scheme is https, which is the rule `nscp enroll` already
follows.

Also document that uninstalling removes agent-state.json: bootstrap tokens are
one-time, so an uninstall/reinstall cycle needs a new install command from the
fleet server rather than keeping the host's identity.

Assisted-by: Claude Code:claude-opus-5
Signed-off-by: Michael Medin <michael@medin.name>
ScheduleEnrollFleet only logged a NOTE when the installer was not allowed to
change the configuration, and then enrolled anyway. The result is a host that
generates a key pair, spends its one-time bootstrap token, registers with the
fleet server and syncs - while nothing ever reads the configuration that comes
back, because ScheduleWriteConfig never added the include that points at it.
Managed on paper, unmanaged in practice, and the note lands in an MSI log that
nobody reads on an unattended install.

Fail instead, before the token is spent. The test is the same one
ScheduleWriteConfig applies (CONF_CAN_CHANGE = 1), so the install fails exactly
when the include would not have been written, and the error carries
CONF_CAN_CHANGE_REASON so the log says which of the several reasons applied.

Assisted-by: Claude Code:claude-opus-5
Signed-off-by: Michael Medin <michael@medin.name>
@mickem
mickem merged commit fb59f8e into main Aug 13, 2026
29 checks passed
@mickem
mickem deleted the feature/installer-fleet-enrollment branch August 13, 2026 07:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant