Skip to content

Public key auth fails silently for LOCAL accounts on domain-joined servers when AuthorizedKeysFile uses the default relative path #2445

Description

@TFujiwara

Prerequisites

  • Write a descriptive title.
  • Make sure you are able to repro it on the latest version
  • Search the existing issues.

Steps to reproduce

  1. Domain-joined Windows Server 2016 host.
  2. Create a local account (not a domain account) — in our case a
    dedicated service account with no interactive logon rights, not a
    member of Administrators.
  3. Install Win32-OpenSSH manually (tested on both v9.5.0.0p1-Beta and
    the latest 10.0p2 at time of writing — same result on both).
  4. Leave AuthorizedKeysFile at its default value in sshd_config:
   AuthorizedKeysFile	.ssh/authorized_keys
  1. Populate C:\Users\<local_account>\.ssh\authorized_keys with a valid
    public key (tested with both ED25519 and RSA-4096 — same result).
  2. Set restrictive ACLs on the account's home directory, .ssh, and the
    authorized_keys file (SYSTEM + Administrators + the account only, no
    inheritance) — confirmed correct with icacls at every level,
    including C:\ProgramData\ssh itself.
  3. Attempt to connect from a Linux client with the matching private key.

Expected behavior

Successful public key authentication.

Actual behavior

`
debug1: Server accepts key: ...   <- never appears
Authentications that can continue: publickey,keyboard-interactive
debug1: No more authentication methods to try.
user@host: Permission denied (publickey,keyboard-interactive).
`
 
`OpenSSH/Operational` event log only shows:
`
sshd-session: Failed publickey for <user> from <ip> port <port> ssh2: <ED25519|RSA> SHA256:<fingerprint>
//no further detail even with `LogLevel DEBUG3`.

Error details

## What we ruled out (with evidence)
 
We spent a long troubleshooting session confirming, at every layer, that
none of the usual suspects applied:
 
- **Key integrity**: `ssh-keygen -y -f <private>` on the client matches
  the fingerprint in `authorized_keys` exactly. Reproduced with two
  completely independent key pairs (ED25519 and a freshly-generated
  RSA-4096), both fail identically.
- **`authorized_keys` parsing**: running `ssh-keygen.exe -lf
  authorized_keys` **with the installed sshd's own `ssh-keygen.exe`**
  returns the correct, matching fingerprint — the binary reads the file
  correctly.
- **ACLs**: verified at every level (home dir, `.ssh`, `authorized_keys`
  file, and `C:\ProgramData\ssh` itself) — no `Everyone`/`Authenticated
  Users` entries anywhere, owner set correctly, matches Microsoft's
  documented recommendations for manual installs.
- **Group membership**: account is confirmed NOT a member of
  `Administrators` (ruling out the `administrators_authorized_keys`
  redirection documented in #2359).
- **`Match`/`ForceCommand` block**: confirmed active for this account via
  `sshd.exe -T -C user=<account>,host=...,addr=...,laddr=...,lport=22`.
- **Account status**: `Enabled: True`, `PasswordExpired: False`, no
  logon-right restrictions (`SeNetworkLogonRight` includes `Everyone`).
- **Windows-level authentication — the key finding**: Security event log
  shows the logon **succeeding** at the Windows level:

  Event 4776 (credential validation): Error Code: 0x0
  Event 4624 (successful logon): Account Name: <local_account>, Logon Type: 3,
      Process Name: C:\Program Files\OpenSSH\sshd.exe
`
  This happens via the internal S4U mechanism (`sshd_<pid>` virtual
  account, events 4717/4718 granting/revoking `SeBatchLogonRight`
  temporarily) — so Windows itself has no problem authorizing the account.
- **sshd version**: reproduced identically on two different releases
  (`v9.5.0.0p1-Beta` and `10.0p2`), ruling out a regression specific to
  one build.
## The fix that resolved it
 
Changing `AuthorizedKeysFile` from the default relative path to an
**absolute, quoted path** resolved the issue immediately, with no other
change:
 
`
AuthorizedKeysFile	"C:\Users\<local_account>\.ssh\authorized_keys"
`
 
After this change and a service restart, the exact same key that was
being rejected authenticates successfully on the first attempt.
 
## Hypothesis
 
Since Windows itself confirms successful authentication (event 4624) but
`sshd-session.exe` still rejects the key, the failure appears to happen
**after** the token is generated, when the session process resolves the
user's home directory to build the relative `AuthorizedKeysFile` path.
We suspect the home-directory lookup API used here behaves differently
for local accounts on a domain-joined host than for domain accounts or
local accounts on a workgroup host — but we don't have visibility into
the relevant source to confirm further. Filing this primarily so the
symptom + confirmed workaround are searchable, since we could not find
this specific combination (domain-joined host + local account + default
relative `AuthorizedKeysFile`) documented anywhere, including in the
related-but-different #2359.

Environment data

`
OpenSSH_for_Windows_10.0p2 Win32-OpenSSH-GitHub, LibreSSL 4.2.0
(also reproduced on v9.5.0.0p1-Beta)
 
Server OS: Microsoft Windows Server 2016 Standard, 10.0.14393
Domain-joined: yes
Client OS: Ubuntu (OpenSSH_9.6p1 Ubuntu-3ubuntu13.18)
`

Version

OpenSSH_for_Windows_10.0p2 Win32-OpenSSH-GitHub, LibreSSL 4.2.0 (also reproduced on v9.5.0.0p1-Beta)

Visuals

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions