10.3 empty-principals change leaves no compatible wildcard host certificate; request for review

Jeffrey Kane Johnson jeff.johnson at fortrobotics.com
Fri Sep 18 10:46:49 AEST 2026


Hi,

We operate an internal deployment tool that issues wildcard host certificates (as in, host certificates with an empty principals section) to a large fleet of embedded devices. I'd like to add an operator data point to the thread started by Damien in November 2025 and picked up by Ashutosh Ghosh and Kegan Myers in August, and ask the project to reconsider part of the change that shipped in 10.3.

Our devices are reached over a mix of names depending on network state, configuration etc. so the set of names or addresses a device will be reached by is not knowable at provisioning time, and the empty principals section has been the mechanism we relied on, as documented in PROTOCOL.certkeys:

  "As a special case, a zero-length 'valid principals' field means the
   certificate is valid for any principal of the specified type."

After 10.3 there is no single certificate that both pre-10.3 and post-10.3 clients will accept:

  - empty principals: accepted by pre-10.3, rejected by 10.3+
  - principals = "*": rejected by pre-10.3, accepted by 10.3+

This is not a slow-moving problem for us. 10.3p1 is already shipping in macOS 26.6, so our installed base now contains both behaviours at once and we cannot control the upgrade schedule of client machines belonging to customers and field technicians.

HostKeyAlias=* on the client side requires touching the ssh_config of every client that will ever connect. For a fleet of third-party and customer-operated clients that is not a practical solution, and it disables the host identity check rather than scoping it (and widens the attack window beyond that of just incorrect usages of empty principals lists).

Issuing two certificates per device is the remaining option and it is poor. Two host certificates require two distinct host key algorithms, and which one is used is chosen by the client's HostKeyAlgorithms ordering, not by the server, so roughly half the fleet lands on the certificate its client can't validate.

The result isn't a clean failure, which in some ways makes it worse. The client emits

  Certificate lacks principal list

or, on pre-10.3 clients seeing the '*' certificate

  "Certificate invalid: name is not a listed principal"

then downgrades the certificate to a plain host key and retries against known_hosts. Note that the rejection is printed via error() at the default LogLevel while the explanatory "No matching CA found. Retry with plain key" is debug1 only, so what the user actually sees is an unexplained certificate rejection.

For us that downgrade is exactly where we didn't want to be. There is no known_hosts entry for these devices because not having to enumerate them is the entire reason we deployed a host CA so an interactive user gets a TOFU prompt for an unknown host key, and anything running with StrictHostKeyChecking=yes or in automation fails outright.

We deployed host certificates specifically to train users NOT to blindly say yes to a TOFU prompt and now they'll be forced to.

My request is that the host certificate case be treated separately, because its exposure is different. A host CA is trusted through a @cert-authority line in known_hosts, which already carries a host pattern that scopes the CA, and the accidental-issuance scenario produces a certificate that is wildcard for host names only. So, in rough order of preference:

1. Retain the zero-length-principals wildcard for host certificates
   while keeping the ban for user certificates. This restores
   interoperability for existing deployments without reinstating the
   footgun that motivated the change, and '*' principals remain
   available as the forward-looking way to express the same intent.

2. Failing that, an opt-in configuration option for the legacy
   behaviour with a stated deprecation window -- something along the
   lines of PermitEmptyCertPrincipals in ssh_config (and sshd_config
   for hostbased), default off. Kegan Myers suggested essentially this
   in his 30 August message. Apache MINA SSHD 2.18.0 has already taken
   that route with its ALLOW_EMPTY_CERTIFICATE_PRINCIPALS flag, so
   there is precedent for the shape of the knob among other
   implementations tracking this change.

3. Failing both, a documented migration path in UPGRADING for
   deployments that cannot enumerate host names in advance. I have not
   been able to construct one and would genuinely welcome being told
   I've missed something.

I'm happy to test patches against a representative fleet and to report back with real-world results or offer patches if there's a specific design in mind that would resolve this regression.

Thanks for your time, and for the work on OpenSSH generally.


Regards,
Jeff


--
Jeffrey Kane Johnson
VP Engineering
FORT Robotics
fortrobotics.com
+1-812-929-0689



More information about the openssh-unix-dev mailing list