NDES Proxy Configuration

This document provides step-by-step instructions for installing and configuring HAProxy as a proxy for the Network Device Enrollment Service (NDES), covering both challenge and certificate enrollment requests.

HAProxy has been chosen for this setup because it offers flexible proxying capabilities at both Layer 4 (TCP) and Layer 7 (HTTP) without requiring additional modules. While alternatives such as NGINX and Apache HTTPD were considered, they rely on third-party modules to handle NTLM authentication, which adds complexity and potential overhead.

By using HAProxy, we can enforce strict access control at the network layer, ensuring that requests to the NDES service are only permitted from the Mobile Client Management (MCM) system. This design enhances security by preventing direct access to NDES while keeping the configuration lightweight.

1. Hybrid Configuration Approach

This setup uses a hybrid architecture combining both SSL passthrough and SSL termination:
  • SSL Passthrough for SCEP operations requiring NTLM authentication (ndes-admin.example.com) - NTLM requires passthrough
  • SSL Termination with HTTP Bridge for non-NTLM operations or admin interfaces (ndes.example.com) - provides HTTP-level visibility
Security Principle: Apply stricter controls (SSL termination + inspection) to untrusted traffic (end-user devices), while allowing efficient passthrough for trusted internal traffic (MCM server).
HAProxy hybrid architecture showing SSL passthrough and SSL termination paths

2. Steps to Install and Configure NDES Proxy

1. Install HAProxy
Run the following command to install HAProxy
sudo dnf install haproxy -y
2. Enable and Start HAProxy

Enable the HAProxy service to start on boot, then start the service and verify its status:

sudo systemctl enable haproxy
sudo systemctl start haproxy
sudo systemctl status
3. Backup the HAProxy configuration file

Rename the existing HAProxy configuration file to create a backup:

sudo mv /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg_bkup
4. Prepare SSL Certificates
This step configures the SSL certificates required for HAProxy (NDES Proxy) and ensures proper TLS handling for both:
  • SCEP endpoint (TLS termination at HAProxy)
  • NDES Admin endpoint (TLS passthrough to NDES server)
Create Certificate Directory
sudo mkdir -p /etc/haproxy/certs

Prepare Certificate File

Copy the SSL certificate and private key to the HAProxy server. The certificate must be in PEM format, containing:

  • Certificate
  • Private key
If separate files exist, combine them:
# If separate files exist, combine them:
sudo cat /path/to/certificate.crt /path/to/private.key > /etc/haproxy/certs/ndes.pem
Set Permissions
sudo chmod 600 /etc/haproxy/certs/ndes.pem
sudo chown haproxy:haproxy /etc/haproxy/certs/ndes.pem
Certificate Requirements: The certificate used by HAProxy must:
  • Include all hostnames exposed via HAProxy where TLS termination occurs
  • Be trusted by clients (BigFix MCM server)
  • Match the configured SNI routing

Required SAN Entries (HAProxy Certificate)

For the configuration described:
ndes.example.com
This is the SCEP endpoint, where TLS is terminated at HAProxy.

Admin Endpoint (Passthrough Mode)

Example configuration:
ndes-admin.example.com
TLS is NOT terminated at HAProxy
  • Traffic is passed through directly to the NDES server

Therefore:

  • HAProxy certificate does NOT need this SAN
  • NDES server certificate MUST include this SAN
Architecture and TLS Termination Flow
Architecture and TLS Termination Flow
Certificate Mapping Summary
Component Hostname TLS Termination Certificate Must Include
HAProxy (NDES Proxy - SCEP) ndes.example.com Yes ndes.example.com
HAProxy (Admin endpoint) ndes-admin.example.com No Not required
NDES Server (IIS) ndes-admin.example.com Yes ndes-admin.example.com
NDES Server (IIS) ndes-backend.example.com Yes ndes-backend.example.com
5. Create the HAProxy Configuration file for NDES Proxy
  1. Create a new HAProxy configuration file:
    sudo vi /etc/haproxy/haproxy.cfg
  2. Add the following configuration, updating the FQDN as needed for the environment:
    #---------------------------------------------------------------------
    # Global settings
    #---------------------------------------------------------------------
    global
        log         127.0.0.1 local2
        chroot      /var/lib/haproxy
        pidfile     /var/run/haproxy.pid
        maxconn     4000
        user        haproxy
        group       haproxy
        daemon
    
        stats socket /var/lib/haproxy/stats
        ssl-default-bind-ciphers PROFILE=SYSTEM
        ssl-default-server-ciphers PROFILE=SYSTEM
    
    #---------------------------------------------------------------------
    # Common defaults
    #---------------------------------------------------------------------
    defaults
        mode                    tcp
        log                     global
        option                  tcplog
        option                  dontlognull
        retries                 3
        timeout connect         10s
        timeout client          1m
        timeout server          1m
        maxconn                 3000
    
    #---------------------------------------------------------------------
    # Frontend for NDES
    #---------------------------------------------------------------------
    
    frontend ft_https
        mode tcp
        option tcplog
        
        # Bind to port 443 for SSL/TLS
        bind *:443
        
        # Wait for the Client Hello packet before making a routing decision
        tcp-request inspect-delay 5s
        tcp-request content accept if { req.ssl_hello_type 1 }
    
        # Route based on SNI
        # For ndes.example.com, use the HTTP frontend for SSL termination
        use_backend bk_http_frontend if { req.ssl_sni -i ndes.example.com }
        
        # For ndes-admin.example.com, use passthrough mode
        use_backend bk_passthrough if { req.ssl_sni -i ndes-admin.example.com }
    
    # HTTP frontend for SSL termination (used by bk_scep)
    frontend ft_http
        mode http
        option httplog
        
        # This frontend is accessed via the bk_http_frontend backend
        bind 127.0.0.1:8443 ssl crt /etc/haproxy/certs/ndes.pem
        
        # Route to the HTTP backend
        default_backend bk_scep
    
    # ----------------------------------------------------
    # BACKENDS
    # ----------------------------------------------------
    
    backend bk_http_frontend
        mode tcp
        # Bridge to the HTTP frontend for SSL termination
        server local_http 127.0.0.1:8443
    
    backend bk_passthrough
        mode tcp
        # Simple TCP passthrough to the backend - SSL is handled end-to-end
        server ndes_dc ndes-backend.example.com:443 check
    
    backend bk_scep
        mode http
        # HTTP backend with SSL re-encryption to the upstream server
        server ndes_dc ndes-backend.example.com:443 ssl verify none check
Configuration Notes:
  • Replace ndes.example.com with the NDES Proxy FQDN

  • Replace ndes-admin.example.com with the NDES Proxy - passthrough FQDN

  • Replace ndes-backend.example.com with the NDES server FQDN

Important: Port 8443 is used as an internal loopback port and is only accessible from localhost. External clients never connect to this port directly. It serves as a bridge between TCP mode (Layer 4) and HTTP mode (Layer 7) to enable SSL termination while maintaining SNI-based routing.

Understanding Key Configuration Elements:

TCP Inspection Lines:

tcp-request inspect-delay 5s
tcp-request content accept if { req.ssl_hello_type 1 }
  • tcp-request inspect-delay 5s: Tells HAProxy to wait up to 5 seconds to inspect incoming data
  • tcp-request content accept if { req.ssl_hello_type 1 }: Proceeds immediately when Client Hello is detected (doesn't wait full 5s)
  • These allow HAProxy to read the SNI from the SSL handshake without decrypting it
SNI-Based Routing:
use_backend bk_http_frontend if { req.ssl_sni -i ndes.example.com }
  • Routes traffic based on the Server Name Indication (SNI) in the SSL handshake
  • Case-insensitive matching (-i flag)
Internal Bridge Mechanism (Port 8443):
The configuration uses an internal loopback bridge to switch from TCP mode to HTTP mode:
# Step 1: TCP backend bridges to internal HTTP frontend
backend bk_http_frontend
 mode tcp
 server local_http 127.0.0.1:8443
# Step 2: HTTP frontend listens on internal port with SSL termination
frontend ft_http
 mode http
 bind 127.0.0.1:8443 ssl crt /etc/haproxy/certs/ndes.pem
 default_backend bk_scep

How it works:

  1. Main frontend (ft_https) operates in TCP mode to inspect SNI at Layer 4
  2. Traffic for ndes.example.com is routed to bk_http_frontend backend
  3. bk_http_frontend connects to internal port 127.0.0.1:8443 (localhost only)
  4. ft_http frontend listens on port 8443 in HTTP mode and terminates SSL
  5. Now operating at Layer 7, HTTP headers are visible and can be inspected
6. Validate the HAProxy Configuration
Run the following command to check the syntax and validity of your HAProxy configuration file:
sudo haproxy -c -f /etc/haproxy/haproxy.cfg
If the configuration is valid, there will be a message indicating success. If there are errors, review the output, correct the configuration, and re-validate.
7. Reload the HAProxy service
Apply the new configuration by reloading the HAProxy service:
sudo systemctl reload haproxy
Verify the service is running:
sudo systemctl status haproxy
8. Allow HTTPS through the Firewall

Open the HTTPS service in the firewall and reload the firewall rules:

sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload