Skip to content

Platform connectivity

Platform connectivity problems

If you think Enclave is having trouble reaching the Enclave platform, please work through the following checklist to identify the problem.

  1. Check https://status.enclave.io/ for any disruptions or service outages

  2. Check your systems are enrolled and showing as both connected and approved in the portal

  3. Check you're running on the latest version of Enclave with enclave version

  4. Check the output of enclave status doesn't contain any warnings or errors

  5. Check the output of enclave status lists at least one Peer and the state shows as Up

  6. Check network traffic is allowed out to tcp/*:443

  7. Check network traffic is allowed out to udp/*:1024-65355

  8. Check network traffic isn't forced through a SOCKS proxy, which is currently unsupported

  9. Check local anti-virus software is not interfering with Enclave by temporarily disabling it

  10. Run the platform reachability test script below to systematically verify connectivity to all Enclave platform services

  11. Check whether the system in question may have been cloned, duplicating its private key material

Platform reachability test script

If you need to systematically verify connectivity to all Enclave platform services, the following scripts resolve DNS for each hostname and test TCP port 443 (and ICMP where applicable) against every resolved address. They report latency and pass/fail status for each check.

The scripts test connectivity to the following hosts:

Host Checks Purpose
api.enclave.io TCP 443 Enclave API
install.enclave.io TCP 443 Installer and update downloads
discover.enclave.io TCP 443 Peer discovery service
relays.enclave.io TCP 443, ICMP Traffic relay servers
google.com TCP 443, ICMP General internet connectivity
management.azure.com TCP 443, ICMP Azure management plane
go.microsoft.com TCP 443, ICMP Microsoft update services

Save the script below as platform-reachability-report.ps1 and run it on Windows:

.\platform-reachability-report.ps1
# Define the list of domains with their respective checks
$domains = @(
    @{
        Name = "api.enclave.io"
        Checks = @("tcp/443")
    },
    @{
        Name = "install.enclave.io"
        Checks = @("tcp/443")
    },
    @{
        Name = "discover.enclave.io"
        Checks = @("tcp/443")
    },
    @{
        Name = "relays.enclave.io"
        Checks = @("tcp/443", "icmp")
    },
    @{
        Name = "google.com"
        Checks = @("tcp/443", "icmp")
    },
    @{
        Name = "management.azure.com"
        Checks = @("tcp/443", "icmp")
    },
    @{
        Name = "go.microsoft.com"
        Checks = @("tcp/443", "icmp")
    }
)

# Execute diagnostic commands
$psCmdlets = @(
    "Get-NetAdapter | Format-Table -AutoSize",
    "Get-NetIPConfiguration | Format-Table -AutoSize",
    "Get-DnsClientServerAddress | Format-Table -AutoSize",
    "Get-DnsClient | Format-Table -AutoSize",
    "Get-DnsClientGlobalSetting",
    "Get-NetIPInterface | Sort-Object InterfaceIndex | Format-Table -AutoSize"
)

$commands = @(
    "enclave status",
    "enclave self-test",
    "route print",
    "ipconfig /all",
    "nslookup discover.enclave.io",
    "nslookup discover.enclave.io 8.8.8.8",
    "nslookup google.com",
    "nslookup google.com 1.1.1.1",
    "nslookup google.com 8.8.8.8",
    "ping 8.8.8.8",
    "ping 1.1.1.1"
)

function Get-NameserversWithInterfaceNames {
    Write-Host "Nameservers"
    $onlineAdapters = Get-NetAdapter | Where-Object { $_.Status -eq 'Up' }
    $dnsInfo = @()

    foreach ($adapter in $onlineAdapters) {
        $nameserversIPv4 = Get-DnsClientServerAddress -InterfaceIndex $adapter.InterfaceIndex -AddressFamily IPv4
        $nameserversIPv6 = Get-DnsClientServerAddress -InterfaceIndex $adapter.InterfaceIndex -AddressFamily IPv6
        $nameservers = @($nameserversIPv4) + @($nameserversIPv6)

        foreach ($nameserver in $nameservers) {
            foreach ($address in $nameserver.ServerAddresses) {
                $dnsInfo += [PSCustomObject]@{
                    AddressFamily  = if ($server -match ":") { "IPv6" } else { "IPv4" }
                    State          = "Up"
                    InterfaceIndex = $nameserver.InterfaceIndex
                    InterfaceAlias = $nameserver.InterfaceAlias
                    Nameserver     = $address
                }
            }
        }
    }

    # Sort the information by AddressFamily (IPv4 first, then IPv6), then InterfaceIndex and InterfaceAlias
    $dnsInfo = $dnsInfo | Sort-Object -Property @{Expression="AddressFamily"; Ascending=$true}, InterfaceIndex, InterfaceAlias

    # Display the information
    $dnsInfo | Format-Table -AutoSize
}

# Function to resolve DNS
function Resolve-Dns {
    param (
        [string]$Domain
    )
    $addresses = Resolve-DnsName -Name $Domain -ErrorAction SilentlyContinue
    $results = @()

    $addresses | ForEach-Object {
        $result = [PSCustomObject]@{
            Name = $Domain
            Address = $_.IPAddress
            Type = $_.QueryType
        }
        # If the result is a CNAME, resolve the CNAME target recursively
        if ($_.QueryType -eq 'CNAME') {
            $cnameTarget = $_.NameHost
            $ip = Resolve-Dns -Domain $cnameTarget
            $results += $ip

        } else {
            $results += $result
        }
    }

    # Deduplicate the results
    $uniqueResults = $results | Sort-Object Address, Type -Unique

    return $uniqueResults
}

# Function to format output
function Format-Output {
    param (
        [string]$protocol,
        [string]$address,
        [string]$status,
        [int]$totalLength = 50
    )
    $line = "$protocol/$address"
    $dots = "." * ($totalLength - $line.Length - 4) # Subtract 4 for the brackets and spaces
    return "    $line $dots [$status]"
}

# Main script logic
Get-NameserversWithInterfaceNames

foreach ($domain in $domains) {
    Write-Host "$($domain.Name)"

    $resolvedAddresses = Resolve-Dns -Domain $domain.Name

    $jobs = @()

    foreach ($address in $resolvedAddresses) {
        foreach ($check in $domain.Checks) {
            if ($check -eq "icmp") {
                $jobs += @{
                    Job = Start-Job -ScriptBlock {
                        param ($addr)
                        function Test-IcmpPing {
                            param (
                                [string]$Address
                            )
                            try {
                                $pingResult = Test-Connection -ComputerName $Address -Count 1 -WarningAction SilentlyContinue -ErrorAction Stop
                                if ($pingResult.StatusCode -eq 0) {
                                    return "ok $($pingResult.ResponseTime)ms"
                                } else {
                                    return "failure $($pingResult.ResponseTime)ms (timeout waiting for a response)"
                                }
                            } catch {
                                if ($Address -match ":") {
                                    return "failure (A socket operation was attempted to an unreachable network [$Address])"
                                } else {
                                    return "failure ($($_.Exception.Message))"
                                }
                            }
                        }
                        $status = Test-IcmpPing -Address $addr
                        return @{ Protocol = "icmp"; Address = $addr; Status = $status }
                    } -ArgumentList $address.Address
                }
            } elseif ($check -match "tcp/(\d+)") {
                $port = [int]$matches[1]
                $jobs += @{
                    Job = Start-Job -ScriptBlock {
                        param ($addr, $prt)
                        function Test-TcpPort {
                            param (
                                [string]$ComputerName,
                                [int]$Port,
                                [int]$Timeout = 10000
                            )
                            try {
                                $address = [System.Net.IPAddress]::Parse($ComputerName)
                                $addressFamily = $address.AddressFamily
                                $tcpClient = New-Object System.Net.Sockets.TcpClient($addressFamily)
                                $asyncResult = $tcpClient.BeginConnect($address, $Port, $null, $null)
                                $waitHandle = $asyncResult.AsyncWaitHandle
                                if ($waitHandle.WaitOne($Timeout, $false)) {
                                    $tcpClient.EndConnect($asyncResult)
                                    $tcpClient.Close()
                                    return "ok"
                                } else {
                                    $tcpClient.Close()
                                    return "failure (no response)"
                                }
                            } catch [System.Net.Sockets.SocketException] {
                                return "failure $($stopwatch.ElapsedMilliseconds)ms ($($_.Exception.Message))"
                            } catch {
                                return "failure $($stopwatch.ElapsedMilliseconds)ms ($($_.Exception.Message))"
                            }
                        }
                        $stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
                        $tcpResult = Test-TcpPort -ComputerName $addr -Port $prt
                        $stopwatch.Stop()
                        $status = if ($tcpResult -match "ok") { "ok $($stopwatch.ElapsedMilliseconds)ms" } else { $tcpResult }
                        return @{ Protocol = "tcp"; Address = "$addr`:$prt"; Status = $status }
                    } -ArgumentList $address.Address, $port
                }
            }
        }
    }

    # Collect job results
    foreach ($job in $jobs) {
        $result = Receive-Job -Job $job.Job -Wait -AutoRemoveJob
        $formattedResult = Format-Output -protocol $result.Protocol -address $result.Address -status $result.Status
        Write-Host $formattedResult
    }
}

Write-Host "`nExecuting diagnostic commands..."

foreach ($cmd in $psCmdlets) {
    Write-Host "`nRunning: $cmd"
    Invoke-Expression $cmd
}

foreach ($cmd in $commands) {
    Write-Host "`nRunning: $cmd"
    Start-Process -FilePath "cmd.exe" -ArgumentList "/c $cmd" -NoNewWindow -Wait
}

Save the script below as platform-reachability-report.sh and run it on Linux or macOS. The script requires dig, ping, and timeout to be installed.

chmod +x platform-reachability-report.sh
./platform-reachability-report.sh
#!/bin/bash

# Define the list of domains with their respective checks
declare -A domains

domains=(
  ["api.enclave.io"]="tcp/443"
  ["install.enclave.io"]="tcp/443"
  ["discover.enclave.io"]="tcp/443"
  ["relays.enclave.io"]="tcp/443 icmp"
  ["google.com"]="tcp/443 icmp"
  ["management.azure.com"]="tcp/443 icmp"
  ["go.microsoft.com"]="tcp/443 icmp"
)

# Check if required commands are installed
for cmd in dig ping timeout; do
  if ! command -v $cmd &> /dev/null; then
    echo "Error: $cmd command not found. Please install it to proceed."
    exit 1
  fi
done

# Function to resolve DNS for both IPv4 and IPv6, including full CNAME resolution to IP addresses
resolve_dns() {
  local domain=$1
  local addresses=""
  local cname=$domain

  # Keep resolving CNAMEs until we reach the final A or AAAA records
  while :; do
    local new_cname=$(dig +short CNAME $cname)
    if [[ -z "$new_cname" ]]; then
      break
    fi
    cname=$new_cname
  done

  # Resolve A and AAAA records for the final resolved domain
  local ipv4_addresses=$(dig +short A $cname)
  local ipv6_addresses=$(dig +short AAAA $cname)
  addresses+="$ipv4_addresses $ipv6_addresses"

  if [[ -z "$addresses" || "$addresses" == " " ]]; then
    echo "No addresses found for $domain"
  else
    echo "$addresses"
  fi
}

# Function to test ICMP ping
test_icmp_ping() {
  local address=$1
  local start_time=$(date +%s%3N)
  ping -c 1 -W 1 $address &> /dev/null
  local end_time=$(date +%s%3N)
  local elapsed_time=$((end_time - start_time))
  if [ $? -eq 0 ]; then
    echo "ok ${elapsed_time}ms"
  else
    echo "failure (timeout waiting for a response)"
  fi
}

# Function to test TCP port
test_tcp_port() {
  local address=$1
  local port=$2
  local start_time=$(date +%s%3N)
  timeout 1 bash -c "</dev/tcp/$address/$port" &> /dev/null
  local end_time=$(date +%s%3N)
  local elapsed_time=$((end_time - start_time))
  if [ $? -eq 0 ]; then
    echo "ok ${elapsed_time}ms"
  else
    echo "failure (no response)"
  fi
}

# Function to format output
format_output() {
  local protocol=$1
  local address=$2
  local status=$3
  local total_length=50
  local line="$protocol/$address"
  local dots=$(printf '.%.0s' $(seq 1 $((total_length - ${#line} - 4))))
  echo " $line $dots [$status]"
}

# Function to display nameserver information
show_nameservers() {
  echo -e "\nNameservers\n"
  echo -e "AddressFamily    Nameserver"
  echo -e "-------------    ----------"

  if command -v nmcli &> /dev/null; then
    if nmcli dev show &> /dev/null; then
      nmcli dev show | awk '/DNS/ {print $2}' | while read -r ns; do
        echo -e "IPv4           $ns\n"
      done
      return
    fi
  fi

  if command -v resolvectl &> /dev/null; then
    if resolvectl status &> /dev/null; then
      resolvectl status | awk '/DNS Servers/ {print $3}' | while read -r ns; do
        echo -e "IPv4           $ns\n"
      done
      return
    fi
  fi

  if [ -f /etc/resolv.conf ]; then
    if grep "^nameserver" /etc/resolv.conf &> /dev/null; then
      grep "^nameserver" /etc/resolv.conf | awk '{print $2}' | while read -r ns; do
        echo -e "IPv4           $ns\n"
      done
      return
    fi
  fi

  echo "Nameserver information not available (requires nmcli, resolvectl, or /etc/resolv.conf)\n"
}

# Main script logic
show_nameservers

for domain in "${!domains[@]}"; do
  echo "$domain"
  addresses=$(resolve_dns $domain)
  if [[ "$addresses" == "No addresses found for $domain" ]]; then
    format_output "dns" "$domain" "failure (could not resolve)"
    continue
  fi
  for address in $addresses; do
    for check in ${domains[$domain]}; do
      if [[ $check == "icmp" ]]; then
        status=$(test_icmp_ping $address)
        format_output "icmp" "$address" "$status"
      elif [[ $check == tcp/* ]]; then
        port=$(echo $check | cut -d'/' -f2)
        status=$(test_tcp_port $address $port)
        format_output "tcp" "$address:$port" "$status"
      fi
    done
  done
done

Results show [ok <latency>] for successful connections and [failure ...] for problems. If all Enclave platform hosts show as ok, the machine has the connectivity it needs. Failures indicate a firewall rule, DNS resolution problem, or network configuration that needs attention. The PowerShell version also runs additional diagnostic commands including enclave status, enclave self-test, routing table, and DNS lookups.


If you're still experiencing connectivity problems after working through this checklist, see collecting logs and contact support.