`keyfile` config is ignored by `InteractiveSSHClient` SSH config `IdentityFile` entries used instead, causing unrelated keys to be tried

Open Beginner friendly
#145 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
78/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Quiet
Tech stack
python

Research direction

Start in dvc_ssh/client.py at InteractiveSSHClient.public_key_auth_requested() and trace how options.client_keys and the parsed SSH config are used during interactive authentication. Reproduce the two-key scenario, then verify that only the explicitly configured keyfile is attempted while SSH config fallback still works when no explicit key is set.

Written by the indexing model from the issue text.

Description

Labels: bug

dvc-ssh version: 4.3.0
asyncssh version: 2.23.0

Description

When keyfile is configured for an SSH remote, DVC correctly passes it to asyncssh.connect() as client_keys. However, when the key requires a passphrase (triggering the interactive auth path), InteractiveSSHClient.public_key_auth_requested() ignores options.client_keys and instead reads IdentityFile entries directly from the SSH config file:

# dvc_ssh/client.py — public_key_auth_requested()
config = options.config
client_keys = cast("Sequence[FilePath]", config.get("IdentityFile", ()))

This means any IdentityFile entries inherited from a Host * block in ~/.ssh/config are tried as well, including keys that are unrelated to the configured remote (e.g. a git commit signing key). This produces unexpected passphrase prompts for keys the user never intended to use with DVC.

Steps to reproduce

  1. Have two keys in ~/.ssh/config under Host *, e.g. id_ed25519 (auth) and id_ed25519_signing (git signing key)
  2. Configure a DVC SSH remote with keyfile pointing to only id_ed25519:
    dvc remote modify --local myremote keyfile ~/.ssh/id_ed25519
    
  3. Run dvc pull

Expected: only ~/.ssh/id_ed25519 is tried; no prompt for id_ed25519_signing

Actual: both keys are tried; user is prompted for the passphrase of id_ed25519_signing

Root cause

public_key_auth_requested() sources its key list from options.config.get("IdentityFile") (the parsed SSH config) rather than from options.client_keys (the explicit client_keys argument passed to asyncssh.connect()). The explicitly configured keyfile is therefore only respected for the initial non-interactive auth attempt, not for the interactive passphrase-prompting fallback.

Suggested fix

In public_key_auth_requested(), prefer options.client_keys when it is set, falling back to SSH config only when no explicit keys are configured:

# Prefer explicitly configured client_keys over SSH config discovery
client_keys = list(options.client_keys) if options.client_keys else None
if not client_keys:
    client_keys = cast("Sequence[FilePath]", config.get("IdentityFile", ()))
if not client_keys:
    client_keys = [
        os.path.expanduser(os.path.join("~", ".ssh", path))
        for path, cond in _DEFAULT_KEY_FILES
        if cond
    ]

Workaround

Remove unintended keys from Host * in ~/.ssh/config so they are not picked up by asyncssh's config parsing.

Dominant language
Python
Stars
2
Forks
6
PR merge metrics
No merged PRs in 30d

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from treeverse/dvc-ssh

All issues in treeverse/dvc-ssh

Similar issues

More Python issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.