Migration discovery

Find out which connections depend on publicly issued client certificates before anything changes. mt discover probes endpoints, inventories client certificates and inspects gateway configuration, read-only, and labels every finding verified, inferred or unknown.

Documentation menu

Why discover first

Sectigo published 10 February 2027 as the final cutoff for clientAuth in newly issued publicly trusted Sectigo SSL/TLS certificates. A connection is affected when its callers authenticate with publicly issued client certificates: the first renewal after the cutoff produces a certificate that no longer works as a client certificate. Inventory and renewal timing decide the effect on each deployment, which is why migration starts with discovery rather than a change.

The change is about client certificates. A public server certificate keeps working as it is, and mutual TLS itself is not going away. The usual outcome is the smallest safe change: keep the gateway, the application and the server certificate, and move callers to a private client-certificate issuer that the gateway trusts next to the current one until the new path is proven.

mt discover answers four questions for each connection and labels every answer:

  • Does the endpoint ask for client certificates, from which issuers, and does it refuse callers without one?
  • Which client certificates do callers present, who issued them, do they carry the clientAuth EKU, and when do they renew?
  • What does the gateway configuration say about trusted client CAs, verification depth and how a certificate identity maps to access?
  • Where does TLS terminate, and what are that gateway's limitations for a migration?

Find affected connections

Run the collectors you can, in any order, save each result with --out, then combine them. Each step is read-only.

Shell
# 1. The endpoint, from anywhere that can reach it (no certificate, no credentials) mt discover endpoint api.example.com --http-probe --out endpoint.json # 2. The gateway configuration, on the gateway host or from a sanitized copy mt discover config /etc/nginx --out config.json # 3. The client certificates, on the calling side mt discover certs /etc/partner/client.p12 --target api.example.com --out certs.json mt discover certs --os-store --target api.example.com --out keychain.json # 4. One evidence document per endpoint, explained in plain language mt discover report --from endpoint.json --from config.json --from certs.json

mt discover report can also run the collectors itself: mt discover report --endpoint api.example.com --config /etc/nginx --certs client.pem. The summary has three parts: What we found (verified and inferred findings), What it means (the assessment in plain language) and What is unknown (what to collect or confirm next). --json prints the evidence documents instead.

Assessment outcomes
AssessmentFindingMeaning
Affectedconnection_affectedA client certificate is publicly issued with clientAuth. The connection fails at that certificate's first renewal after the cutoff unless callers move to a private issuer.
Likely affectedconnection_likely_affectedThe server trusts a public CA for client certificates. Callers that use such certificates are affected; inventory them to confirm.
Not affectedconnection_not_affectedEvery inventoried client certificate comes from a private issuer. Certificates not inventoried are not covered.
No client authenticationclient_auth_not_in_useThe endpoint does not ask for client certificates.
Incompleteassessment_incompleteNot enough evidence yet; the unknowns say what to collect.

Delegate discovery

The person who owns the relationship is often not the person who can reach the gateway. From the connection workspace, invite the network or security engineer to a discovery task. The invitation is scoped to that task and that workspace, the engineer authenticates without a full product account, and every submission is audited.

  1. The engineer installs mt (CLI reference) on a host inside the network.
  2. They run the collectors. Inside a private network, --allow-private lets mt discover endpoint probe internal addresses; without it, private, loopback and link-local addresses are refused.
  3. They submit with the delegated task token from the invitation. Host redaction is on by default for submissions, so internal hostnames, private addresses, upstreams and file paths do not leave the network.
Shell
export MT_TASK_TOKEN=... # from the discovery task invitation mt discover report \ --endpoint orders.internal.example:8443 --allow-private --http-probe \ --config /etc/haproxy/haproxy.cfg \ --certs /opt/partner/keystore.jks \ --submit --workspace cws_...

--submit reads the workspace, then sends the documents that describe its endpoint to POST /v1/workspaces/{id}/assessments as {"evidence": [...]} (up to 20 per request) with Authorization: Bearer and an Idempotency-Key. Documents about any other endpoint are left out with a warning, because discovery is limited to the approved asset; bind certificates to the workspace endpoint with --target. The control plane scans and stores each document and returns the updated assessment. The credential is $MT_TASK_TOKEN, then $MT_API_KEY (an API credential), or the file named by --token-file; it is never accepted on the command line. A container password for certs comes from $MT_DISCOVER_PASSWORD or a hidden prompt and is never written anywhere.

Verified, inferred and unknown

Every finding carries one label and ends with its source (the probe, the file and line, or the certificate), so a reviewer can trace each claim.

Confidence labels
LabelMeansExamples
verifiedObserved directly by this collector.The server sent a CertificateRequest naming these CAs; the handshake failed without a certificate; ssl_verify_client on at nginx.conf:14; the certificate's EKU is clientAuth and its chain verifies to a public root.
inferredDerived from indirect signals that can be wrong.The terminator is Cloudflare (from the CF-RAY header); the issuer is public (its key identifier is in the Mozilla CCADB list, but the chain was not in the file); gateway limitations from the adapter manifest.
unknownNot observable by this collector. A person must confirm it.Whether a server that accepts the handshake without a certificate refuses the request; trusted CAs referenced by a file that was not provided; the gateway version during offline inspection.
What each collector can see
Questionendpointconfigcerts
Client certificates requestedVerified (CertificateRequest)Verified (the directive)No
Requests without a certificate refusedVerified when the handshake fails, or with --http-probe; otherwise unknownVerified (required or optional)No
Trusted client CAsVerified when the server names them; many servers name noneVerified when the CA file is includedCA certificates found in stores
Authorization mappingNoVerified (ACLs, subject checks, external authorizers); application checks inferredNo
Client certificate issuer, EKU, renewalNoNoVerified
TLS termination pointInferred from headers and certificatesVerified for the file inspectedNo
Gateway limitationsInferred from the detected kindInferred from the adapter manifestNo

Endpoint probe

mt discover endpoint HOST[:PORT] opens TLS connections without presenting a certificate. The first handshake captures the server chain and, when the server sends a CertificateRequest, the acceptable CA names. One handshake per TLS version (1.3, 1.2, 1.1, 1.0) records the supported versions. With --http-probe, one GET / with no cookies, no authorization header and no redirects reads the Server, Via, CF-RAY, x-amzn-*, x-envoy-*, x-kong-* and similar headers and the names (never the values) of cookies. The response body is read only to match fixed error-page signatures and is not kept.

  • How refusal is detected. A server that requires a certificate either ends the handshake (Envoy, HAProxy, Traefik, Go servers; with TLS 1.3 the alert arrives just after the handshake) or completes it and refuses the request at the HTTP layer. NGINX with ssl_verify_client on does the second and answers 400; --http-probe sees that, and without it the result is requested with enforcement unknown.
  • Path-specific requests. A server can ask for a certificate only for some paths (TLS 1.2 renegotiation, TLS 1.3 post-handshake authentication). The probe does not exercise those, and says so.
  • Limits. At most 8 connections per endpoint, at least --interval apart (default 250 ms), each bounded by --timeout (default 10 s). Only the addresses checked against the private-address rule are dialed, so a second DNS answer cannot redirect the probe.
  • Private addresses. Loopback, RFC 1918, link-local, CGNAT, unique local IPv6 and similar addresses are refused (exit code 6) unless --allow-private is given. A name that resolves to any such address is refused as a whole.

Certificate inventory

Certificate sources
SourceSupport
PEM and DER filesEvery certificate in the file; a private key block is noted (has a key) but never parsed.
PKCS#12 (.p12, .pfx)Key and certificate containers and trust stores. The empty password is tried first, then $MT_DISCOVER_PASSWORD or a hidden prompt. The format requires decrypting the key bag to reach the certificates; the key is dropped at once and never output.
JKSRead natively. The store password verifies integrity; key entries stay encrypted (their certificate chains are stored in the clear).
JCEKSThrough the JDK keytool when installed (the password reaches it through an environment variable of the child process only); otherwise reported as unsupported with the conversion command.
macOS Keychain (--os-store)Identities (a certificate with a private key) in the keychain search list, through /usr/bin/security. Keys stay in the keychain; no unlock prompt.
Windows certificate store (--os-store)The CurrentUser and LocalMachine personal (MY) stores through CryptoAPI, read-only; key presence is checked without opening the key.
LinuxNo system store of client identities exists; pass the files your clients load.

For each end-entity certificate the inventory records subject, issuer, serial, validity, SHA-256 fingerprint, EKUs, key type and size, whether the container holds its key, and whether the issuer is public:

  • Public, verified when the chain (from the same file, plus the bundled Sectigo intermediates) verifies to a root trusted by the operating system or to a Sectigo public root.
  • Public, inferred when no chain can be built but the issuer's key identifier (or, without one, its name) matches a publicly trusted CA disclosed in the Mozilla CCADB list bundled with mt. A private CA that copies a public CA's name does not match, because the key identifier differs.
  • Private otherwise: verified when the chain ends at a root in the same input, inferred when the issuer is simply not public.

Renewal is expected --renewal-lead days (default 30) before expiry. A public certificate with clientAuth whose renewal falls on or after the cutoff gets public_clientauth_renewal_at_risk; one that renews earlier gets public_clientauth_renews_before_cutoff, because the renewal after that will not carry clientAuth. Private certificates get private_clientauth_unaffected. --cutoff assesses against another date, for a public CA that publishes its own schedule.

Configuration inspection

mt discover config FILE|DIR runs the same adapter inspectors as mt adapt inspect, offline: no gateway binary is executed, nothing is written next to the files, and a directory is read recursively with includes followed once. The platform is detected from the content unless --platform names it.

Configuration platforms
PlatformInputInspected by
nginxnginx.conf with includes, or a conf.d server blockNGINX adapter
envoyBootstrap YAML or JSON (static listeners; xDS ownership reported)Envoy adapter
haproxyhaproxy.cfgHAProxy adapter
traefikStatic configuration with the file provider, or a dynamic file aloneTraefik adapter
kongdecK or declarative kong.yml (mtls-auth, acl, CA certificates)Read-only parser
f5bigip.conf client-ssl profiles, virtual servers and iRules, or an AS3 declarationRead-only parser
apacheSSLVerifyClient, CA files, SSLRequire and Require expr per virtual hostRead-only parser
k8singress-nginx Ingress annotations, Istio Gateway MUTUAL, Traefik TLSOptionRead-only parser
javaSpring Boot server.ssl.* (properties or YAML), Tomcat server.xmlRead-only parser
awsaws apigateway get-domain-name output (mutualTlsAuthentication)Read-only parser
azureAPI Management ARM JSON (negotiateClientCertificate) or policy XML (validate-client-certificate)Read-only parser

Each termination point reports the client-certificate mode, CA files and their certificates, verification depth, CRLs and the authorization mapping (none, a native ACL or subject check, an external authorizer, or the application), plus the adapter's findings and the documented limitations, parallel-trust support and revocation behavior from its capability manifest. Cloudflare API Shield, live AWS and Azure resources, Kong Admin API and F5 iControl REST are inspected with mt adapt inspect against the platform API instead.

Untrusted content

Response headers, certificate fields, acceptable CA names and configuration comments come from systems you may not control, and the evidence they produce is read by people and by AI agents planning a migration. Discovery treats all of it as data:

  • Control characters, invisible and reordering Unicode characters and markup characters are stripped from every captured value, and lengths are bounded.
  • Text that reads like instructions (to ignore previous instructions, approve or trust something, call a tool, reveal secrets, or that carries chat role markers and prompt template tokens) is withheld: the evidence holds a marker with its length and a hash, and a untrusted_instructions finding names where it was found. The text is never printed, so an agent running mt discover cannot be steered by it. Review it at the source.
  • Configuration comments are scanned but never copied into evidence.

Evidence documents

Each document is a MigrationEvidenceV1, the shape the workspace assessments endpoint accepts. Fingerprints are lowercase hex SHA-256 of the DER certificate. client_auth_requested is null when no collector could tell. Documents are kept within the contract's bounds (100 findings, 50 client certificates); when something is left out, a findings_truncated or client_certs_truncated finding says so, and the full local report is written with --out.

MigrationEvidenceV1
{ "source": "mt_discover", "collected_at": "2026-11-14T10:00:00Z", "collector": { "kind": "mt", "version": "1.0.0" }, "target": { "hostname": "api.example.com", "port": 443 }, "tls": { "server_cert_chain_fingerprints": ["3f1c...", "9a02..."], "client_auth_requested": true, "acceptable_ca_dns": ["CN=Sectigo RSA Organization Validation Secure Server CA,O=Sectigo Limited,L=Salford,ST=Greater Manchester,C=GB"], "tls_versions": ["TLS1.3", "TLS1.2"] }, "client_certs": [{ "fingerprint": "c47e...", "issuer_dn": "CN=Sectigo RSA Organization Validation Secure Server CA,...", "subject_dn": "CN=partner-client.example.com", "ekus": ["serverAuth", "clientAuth"], "not_after": "2027-06-30T23:59:59Z", "public_ca": true, "key_type": "RSA-2048" }], "gateway": { "detected_kind": "nginx", "version": "1.25.3", "confidence": "inferred" }, "findings": [{ "code": "public_clientauth_renewal_at_risk", "label": "Public client certificate whose renewal falls after the clientAuth cutoff", "confidence": "verified", "detail": "... (source: certificate c47e... in client.p12)" }], "redactions_applied": true }

Secrets are always redacted with the same rules the control plane uses. --redact-hosts (on by default with --submit) also replaces internal hostnames, private addresses, upstream addresses and file paths (reduced to base names); the target hostname stays, because the workspace is about it. redactions_applied is true when anything was masked or host redaction was on.