Installing Windows Mesh Expansion (WME)

The installation method for BEL Windows Mesh Expansion is the bel-wme-installer.msi, a WiX v6 MSI that installs runtime components, registers the driver, and starts the harness service in a single step.

Supported Platforms

The driver included in this release is signed by Microsoft for Windows Server 2025. Microsoft’s kernel signing policy for Server 2025 also covers Windows 11, so the same driver loads on both platforms.

OSArchitecture
Windows Server 2025x64
Windows 11x64

Prerequisites

1. Control Plane Reachability

The harness must connect to three Linkerd control plane services. By default, the installer resolves these using standard Kubernetes DNS names:

ServiceDefault Address
Autoregistrationlinkerd-autoregistration.linkerd.svc.cluster.local:8081
Destinationlinkerd-dst.linkerd.svc.cluster.local:8086
Policylinkerd-policy.linkerd.svc.cluster.local:8090

The VM must resolve these names and reach the addresses. It is recommended to run CoreDNS on the VM configured to forward *.cluster.local queries to the cluster’s DNS service.

  • To use a non-default cluster domain, pass CLUSTER_DOMAIN=<domain>.
  • To override individual addresses, set CONTROL_ADDRESS, DESTINATION_SVC_ADDR, or POLICY_SVC_ADDR directly.

2. SPIRE

A SPIRE agent must be running on the VM before the MSI executes, connected to a SPIRE server. The documented configuration runs a SPIRE server locally on each VM; a centralized SPIRE server shared across multiple VMs is architecturally valid but untested with BEL WME.
For security and mutual TLS (mTLS) trust, the setup relies on a specific PKI chain:

  • SVID Issuance: The VM’s SPIRE server uses a dedicated SPIRE Upstream CA Certificate (an intermediate CA with pathlen:1), which is signed by the Linkerd root CA, to issue local SPIFFE SVIDs. This approach limits the blast radius, as the VM never holds the root private key.
  • Control Plane Verification: The data-plane proxy requires the Cluster Root CA Certificate to verify the Linkerd control plane’s TLS certificates.

Certificate Provisioning

Ensure the following necessary certificate files are provisioned on the VM. Note: These paths reflect the expected layout from the BEL WME SPIRE setup script and may vary based on your deployment decision (the files are typically pulled from cluster secrets and configmaps):

  • C:\spire\certs\ca.crt: The SPIRE upstream CA certificate (intermediate).
  • C:\spire\certs\ca.key: The SPIRE upstream CA private key.
  • C:\spire\certs\bundle.crt: The concatenated trust bundle built by combining the SPIRE upstream CA cert (ca.crt) with the cluster root CA cert (root-ca.crt).

Verification

Before running the installer, ensure the SPIRE agent’s named pipe is ready:

Test-Path "\\.\pipe\spire-agent\public\api"

3. Windows Firewall

The driver intercepts outbound traffic only. Inbound mTLS connections from the cluster arrive directly at the proxy’s inbound listening port (PROXY_INBOUND_PORT, default 4143, bound on all interfaces). The driver plays no role in the inbound path - any inbound port that is not the proxy’s listening port receives no mTLS enforcement from the mesh. Windows Firewall is the appropriate control for restricting inbound access.

Allow the required ports and set the firewall profile default to block all other unsolicited inbound traffic:

New-NetFirewallRule -DisplayName "Linkerd Inbound Proxy (4143)" -Direction Inbound -Protocol TCP -LocalPort 4143 -Action Allow

New-NetFirewallRule -DisplayName "Linkerd Admin (4191)" -Direction Inbound -Protocol TCP -LocalPort 4191 -Action Allow

New-NetFirewallRule -DisplayName "Linkerd Harness Readiness (4192)" -Direction Inbound -Protocol TCP -LocalPort 4192 -Action Allow

# Recommended: set the default inbound action to Block. The allow rules above still permit the required ports.
# The mesh does not enforce mTLS on traffic arriving at any other port.
Set-NetFirewallProfile -Profile Domain,Private,Public -DefaultInboundAction Block

If you changed PROXY_INBOUND_PORT from its default, substitute that port in the first rule. If you set a default inbound block policy, add any other needed allow rules for ports your workload needs to accept directly before applying it.

4. ExternalGroup

An ExternalGroup resource must exist in the target namespace before installation. The installer properties WORKLOAD_GROUP_NAME and WORKLOAD_GROUP_NAMESPACE must match this resource.

apiVersion: workload.buoyant.io/v1alpha1
kind: ExternalGroup
metadata:
  name: <WORKLOAD\_GROUP\_NAME>
  namespace: <WORKLOAD\_GROUP\_NAMESPACE>
spec:
  probes:
    - failureThreshold: 3
      httpGet:
        path: /ready
        port: 4192
        scheme: HTTP
        host: 127.0.0.1
      initialDelaySeconds: 5
      periodSeconds: 10
      successThreshold: 1
      timeoutSeconds: 2
  template:
    metadata:
      labels:
        app: <WORKLOAD\_LABEL>
    ports:
      - port: <SERVICE\_PORT>
        name: <PORT\_NAME>

Adjust template.metadata.labels and template.ports to match your workload. The probe target (4192) is fixed.

Install

Required Values

Ensure you have the following information:

  • VM IP Address: Reachable from the cluster (INBOUND_NETWORK_ADDRESS).
  • ExternalGroup: The name and namespace of the ExternalGroup created in prerequisite 4.

Execution

Run the installer silently using msiexec. For non-automated deployments, you can also run the MSI interactively via the GUI.

Standard Installation:

msiexec /i bel_wme_installer.msi /quiet /l*vx C:\temp\install.log `
  INBOUND_NETWORK_ADDRESS="<THIS_VM_IP>" `
  WORKLOAD_GROUP_NAME="<GROUP_NAME>" `
  WORKLOAD_GROUP_NAMESPACE="<NAMESPACE>"

Non-Default Configuration

If your cluster domain is not cluster.local, pass CLUSTER_DOMAIN and the installer derives all three control plane addresses automatically:

msiexec /i bel_wme_installer.msi /quiet /l*vx C:\temp\install.log `
  INBOUND_NETWORK_ADDRESS="<THIS_VM_IP>" `
  WORKLOAD_GROUP_NAME="<GROUP_NAME>" `
  WORKLOAD_GROUP_NAMESPACE="<NAMESPACE>" `
  CLUSTER_DOMAIN="<CLUSTER_DOMAIN>"

If the control plane is only reachable by IP (DNS does not resolve the cluster domain), override the three addresses directly instead:

msiexec /i bel_wme_installer.msi /quiet /l*vx C:\temp\install.log `
  INBOUND_NETWORK_ADDRESS="<THIS_VM_IP>" `
  WORKLOAD_GROUP_NAME="<GROUP_NAME>" `
  WORKLOAD_GROUP_NAMESPACE="<NAMESPACE>" `
  CONTROL_ADDRESS="<AUTOREGISTRATION_IP>:<AUTOREGISTRATION_PORT>" `
  DESTINATION_SVC_ADDR="<DESTINATION_IP>:<DESTINATION_PORT>" `
  POLICY_SVC_ADDR="<POLICY_IP>:<POLICY_PORT>"

MSI Properties

PropertyDefaultDescription
INBOUND_NETWORK_ADDRESS(required)VM IP reachable from the cluster
WORKLOAD_GROUP_NAME(required)ExternalGroup name
WORKLOAD_GROUP_NAMESPACE(required)ExternalGroup namespace
CLUSTER_DOMAINcluster.localCluster DNS domain
CONTROL_ADDRESSlinkerd-autoregistration.linkerd.svc.<CD>:8081Control plane host:port
DESTINATION_SVC_ADDRlinkerd-dst.linkerd.svc.<CD>:8086Destination service host:port
POLICY_SVC_ADDRlinkerd-policy.linkerd.svc.<CD>:8090Policy service host:port
INSTALLFOLDER%ProgramFiles%\Buoyant\LinkerdInstallation directory
CONTROL_SERVER_NAMElinkerd-autoregistration.linkerd.serviceaccount.identity.linkerd.<CLUSTER_DOMAIN>TLS server name for control plane
SPIRE_SOCKET_ADDR\\.\pipe\spire-agent\public\apiSPIRE agent named pipe path
LOGDIR[INSTALLFOLDER]Harness log output directory
LOG_LEVELinfoTrace, debug, info, warn, error
PROXY_INBOUND_PORT4143Port the proxy binds for inbound connections (0.0.0.0:<port>). The driver does not redirect inbound traffic - this configures the proxy’s own listening address only. Must match the firewall allow rule above.
PROXY_OUTBOUND_PORT4140Driver outbound redirect port
OUTBOUND_PORTS_TO_IGNORE8081Ports excluded from outbound redirection. Default 8081 exempts SPIRE agent traffic; harness/proxy PIDs are always exempt

Post-Install Configuration

Service Dependency

One step must be applied manually after install to prevent the harness from starting before SPIRE is available:

sc.exe config linkerd-proxy-harness depend= spire-agent

Note: spire-agent must match the actual SCM service name for the SPIRE agent on your VM. This reflects the name registered by the BEL WME SPIRE setup script.

The installer automatically configures SCM recovery (restart on failure). The following is an optional override for different recovery settings:

sc.exe failure linkerd-proxy-harness reset= 60 actions= restart/5000/restart/10000/restart/30000

Verify Installation

  1. Check Services: sc query linkerdtcpredirect and sc query linkerd-proxy-harness should both be RUNNING.

  2. Check Logs: Check the harness log for successful identity acquisition and control plane connection status. See the Logs section in Troubleshooting for the path and command.

  3. Cluster Registration: Run kubectl get externalworkload -n <namespace> -w from a machine with cluster access. READY should become True within 30-90 seconds.

Uninstall

msiexec /x bel_wme_installer.msi /quiet /l*vx C:\temp\uninstall.log

Troubleshooting

Logs

The MSI registers an ETW AutoLogger session automatically. Traces are written to [INSTALLFOLDER]\linkerd-tcp-redirect-driver.etl (default: %ProgramFiles%\Buoyant\Linkerd\linkerd-tcp-redirect-driver.etl). To decode:

$d = "$env:ProgramFiles\Buoyant\Linkerd"
tracefmt -p "$d\driver" -o "$d\driver.log" "$d\linkerd-tcp-redirect-driver.etl"

To check the harness service log:

Get-Content "$env:ProgramFiles\Buoyant\Linkerd\harness.log" -Tail 30

Quick Diagnostics

Run these PowerShell commands for initial checks. All service should be running and Test-Path should return True.

Test-Path "\\.\pipe\spire-agent\public\api"

sc.exe query linkerdtcpredirect
sc.exe query linkerd-proxy-harness

# ExternalWorkload registration (run on cluster)
kubectl get externalworkload -n <namespace>

SPIRE / Certificate Issues

invalid peer certificate: UnknownIssuer

This error can be caused by two issues:

  • VM Side (Incomplete Trust Bundle): SPIRE’s cached trust bundle may be missing the root CA. Verify the bundle contains both the SPIRE sub-CA and the cluster root CA. Force a re-bootstrap if only one cert appears.
  • Cluster Side (Stale Trust Bundle): Application proxies in the cluster started before the SPIRE CA was added to linkerd-identity-trust-roots.
    • Fix: Restart application pods to force proxies to reload the current trust bundle: kubectl rollout restart deployment -n <namespace>

Re-Attestation (Expired SVID)

Re-attestation is required. Contact support or refer to the SPIRE documentation for the necessary re-attestation script, which must run as SYSTEM via a scheduled task.

Harness / Service Issues

Harness Service Won’t Stop

If the harness is in a connection retry loop, kill the process directly:

Get-Process -Name "*harness*" -ErrorAction SilentlyContinue | Stop-Process -Force

Harness Not Starting After Install

  1. Verify SPIRE was running before install.
  2. Check the install log (at the path you specified in the msiexec /l*vx argument) and harness log (refer to the Logs section above).
  3. Verify the SPIRE pipe is present: Test-Path "\\.\pipe\spire-agent\public\api"

Harness Fails to Connect to Control Plane

  1. Verify control plane IPs/NodePorts are correct.
  2. Test connectivity from the VM using the correct IPs for your environment:
Test-NetConnection <AUTOREGISTRATION_IP> -Port <AUTOREGISTRATION_PORT>
Test-NetConnection <DESTINATION_IP> -Port <DESTINATION_PORT>
Test-NetConnection <POLICY_IP> -Port <POLICY_PORT>

Use the ports from your CONTROL_ADDRESS, DESTINATION_SVC_ADDR, and POLICY_SVC_ADDR values (defaults: 8081, 8086, 8090).

ExternalWorkload Not Registering

This is a common failure during the first installation. If the workload does not appear as READY, check the harness log (see Logs above) for connectivity errors and ensure the cluster check in Quick Diagnostics shows the ExternalWorkload exists.

Harness Won’t Stop / Uninstaller Hangs

If the harness is in a control-plane connection retry loop (typically when the cluster is unreachable), it will not respond to Stop-Service or Restart-Service within the SCM timeout. The uninstaller stalls for the same reason - the MSI stop step times out waiting for the service. In either case, kill the process directly:

Get-Process -Name "*harness*" -ErrorAction SilentlyContinue | Stop-Process -Force

Then re-run the uninstaller or restart the service as needed.