Fix: Missing guide: How to try Windows MDM (fleetctl preview) (#42451)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** Resolves #32773 # Checklist for submitter - [x] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. See [Changes files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files) for more information. ## Testing - [x] QA'd all new/changed functionality manually I ran `./build/fleetctl preview --preview-config-path ./tools/osquery/in-a-box`. Not sure if this is the standard way of running `fleetctl preview`. #### Before https://github.com/user-attachments/assets/d1aef9e4-83fe-4e8e-8ec9-91b6258caefa #### After https://github.com/user-attachments/assets/67937211-0d7c-421a-99ba-e10842c503b3 #### Test on Windows 11 desktop PC ``` $ go build -o fleetctl.exe .\cmd\fleetctl $ .\fleetctl.exe preview --preview-config-path .\tools\osquery\in-a-box ``` <img width="650" height="256" alt="1" src="https://github.com/user-attachments/assets/9072010a-2182-4a10-a30b-c7a10bb1a76e" /> <img width="884" height="304" alt="2" src="https://github.com/user-attachments/assets/322e3f66-4543-4a38-90ad-29be3df22863" />
This commit is contained in:
@@ -0,0 +1 @@
|
||||
* Enabled Windows MDM in `fleetctl preview` by auto-generating WSTEP certificates on startup.
|
||||
@@ -3,12 +3,17 @@ package fleetctl
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/hex"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
@@ -269,6 +274,18 @@ Use the stop and reset subcommands to manage the server and dependencies once st
|
||||
return fmt.Errorf("failed to set private key: %w", err)
|
||||
}
|
||||
|
||||
// Generate a self-signed WSTEP certificate and key for Windows MDM
|
||||
// and save them to the config directory for use in subsequent runs.
|
||||
if err := ensureWSTEPCerts(filepath.Join(previewDir, "config")); err != nil {
|
||||
return fmt.Errorf("generating WSTEP certificates: %w", err)
|
||||
}
|
||||
if err := os.Setenv("FLEET_MDM_WINDOWS_WSTEP_IDENTITY_CERT", "/config/wstep.crt"); err != nil {
|
||||
return fmt.Errorf("failed to set WSTEP cert path: %w", err)
|
||||
}
|
||||
if err := os.Setenv("FLEET_MDM_WINDOWS_WSTEP_IDENTITY_KEY", "/config/wstep.key"); err != nil {
|
||||
return fmt.Errorf("failed to set WSTEP key path: %w", err)
|
||||
}
|
||||
|
||||
if err := os.Setenv("FLEET_VERSION", c.String(tagFlagName)); err != nil {
|
||||
return fmt.Errorf("failed to set Fleet version: %w", err)
|
||||
}
|
||||
@@ -489,6 +506,83 @@ func copyDirectory(destDir, sourceDir string) error {
|
||||
})
|
||||
}
|
||||
|
||||
// ensureWSTEPCerts generates a self-signed WSTEP identity certificate and key
|
||||
// for Windows MDM if they don't already exist in configDir. The generated files
|
||||
// match the parameters recommended in the Windows MDM setup guide.
|
||||
func ensureWSTEPCerts(configDir string) error {
|
||||
certPath := filepath.Join(configDir, "wstep.crt")
|
||||
keyPath := filepath.Join(configDir, "wstep.key")
|
||||
|
||||
_, certErr := os.Stat(certPath)
|
||||
if certErr != nil && !os.IsNotExist(certErr) {
|
||||
return fmt.Errorf("checking WSTEP certificate: %w", certErr)
|
||||
}
|
||||
|
||||
_, keyErr := os.Stat(keyPath)
|
||||
if keyErr != nil && !os.IsNotExist(keyErr) {
|
||||
return fmt.Errorf("checking WSTEP key: %w", keyErr)
|
||||
}
|
||||
|
||||
certExists := certErr == nil
|
||||
keyExists := keyErr == nil
|
||||
|
||||
// Both cert and key already exist; nothing to do.
|
||||
if certExists && keyExists {
|
||||
return nil
|
||||
}
|
||||
|
||||
// One exists without the other: inconsistent state; refuse to overwrite and provide remediation.
|
||||
if certExists != keyExists {
|
||||
return fmt.Errorf("inconsistent WSTEP certificate/key state: certificate exists=%t, key exists=%t. Please delete the existing wstep.crt and/or wstep.key in %q and re-run `fleetctl preview` to regenerate them", certExists, keyExists, configDir)
|
||||
}
|
||||
|
||||
// Both files are missing; generate a new keypair and self-signed certificate.
|
||||
key, err := rsa.GenerateKey(rand.Reader, 4096)
|
||||
if err != nil {
|
||||
return fmt.Errorf("generating RSA key: %w", err)
|
||||
}
|
||||
|
||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||
if err != nil {
|
||||
return fmt.Errorf("generating certificate serial number: %w", err)
|
||||
}
|
||||
if serialNumber.Sign() <= 0 {
|
||||
serialNumber = big.NewInt(1)
|
||||
}
|
||||
|
||||
template := x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
Subject: pkix.Name{
|
||||
CommonName: "Fleet Root CA",
|
||||
Country: []string{"US"},
|
||||
Organization: []string{"Fleet."},
|
||||
},
|
||||
NotBefore: time.Now().Add(-10 * time.Minute),
|
||||
NotAfter: time.Now().AddDate(10, 0, 0),
|
||||
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
|
||||
BasicConstraintsValid: true,
|
||||
IsCA: true,
|
||||
}
|
||||
|
||||
certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating certificate: %w", err)
|
||||
}
|
||||
|
||||
certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})
|
||||
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})
|
||||
|
||||
if err := os.WriteFile(certPath, certPEM, 0o644); err != nil {
|
||||
return fmt.Errorf("writing WSTEP certificate: %w", err)
|
||||
}
|
||||
if err := os.WriteFile(keyPath, keyPEM, 0o644); err != nil {
|
||||
return fmt.Errorf("writing WSTEP key: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var TestOverridePreviewDirectory string
|
||||
|
||||
func previewDirectory() string {
|
||||
|
||||
@@ -64,6 +64,8 @@ services:
|
||||
FLEET_VULNERABILITIES_PERIODICITY: 5m
|
||||
FLEET_LOGGING_DEBUG: 'true'
|
||||
FLEET_SERVER_PRIVATE_KEY: ${FLEET_SERVER_PRIVATE_KEY}
|
||||
FLEET_MDM_WINDOWS_WSTEP_IDENTITY_CERT: "${FLEET_MDM_WINDOWS_WSTEP_IDENTITY_CERT:-}"
|
||||
FLEET_MDM_WINDOWS_WSTEP_IDENTITY_KEY: "${FLEET_MDM_WINDOWS_WSTEP_IDENTITY_KEY:-}"
|
||||
# This can be configured for testing purposes but otherwise uses the
|
||||
# typical default of provided.
|
||||
FLEET_OSQUERY_HOST_IDENTIFIER: ${FLEET_OSQUERY_HOST_IDENTIFIER:-provided}
|
||||
@@ -121,6 +123,8 @@ services:
|
||||
FLEET_OSQUERY_LABEL_UPDATE_INTERVAL: 1m
|
||||
FLEET_VULNERABILITIES_CURRENT_INSTANCE_CHECKS: "no"
|
||||
FLEET_SERVER_PRIVATE_KEY: ${FLEET_SERVER_PRIVATE_KEY}
|
||||
FLEET_MDM_WINDOWS_WSTEP_IDENTITY_CERT: "${FLEET_MDM_WINDOWS_WSTEP_IDENTITY_CERT:-}"
|
||||
FLEET_MDM_WINDOWS_WSTEP_IDENTITY_KEY: "${FLEET_MDM_WINDOWS_WSTEP_IDENTITY_KEY:-}"
|
||||
# This can be configured for testing purposes but otherwise uses the
|
||||
# typical default of provided.
|
||||
FLEET_OSQUERY_HOST_IDENTIFIER: ${FLEET_OSQUERY_HOST_IDENTIFIER:-provided}
|
||||
|
||||
Reference in New Issue
Block a user