Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SSH: Passthrough to native OpenSSH agent for better forwarding support #479

Open
jacobgreenleaf opened this issue Mar 30, 2024 · 0 comments
Assignees

Comments

@jacobgreenleaf
Copy link

jacobgreenleaf commented Mar 30, 2024

Much of OpenSSH appears to me to largely have the assumption that there will only ever be a single SSH agent in use at a time. I see no place in the ssh agent protocol for multiplexing over multiple agents, agent forwarding using -A appears to only support a single agent, IdentityAgent specifies the agent to use (not an agent to use), the SSH_AUTH_SOCK environment variable is a single path to a single agent, command line flags override the environment variables, etc. Since trezor-agent is not a full drop-in replacement for ssh-agent, it would be nice if there was a way to mix usage of ssh-agent and trezor-agent, because ssh-agent provides support for temporary keys and certificates that can be added and removed, whereas trezor-agent cannot do this (it refuses SSH_AGENTC_ADD_IDENTITY as noted below).

As a little use case to think about, suppose the user has a SSH key A stored in their local ssh-agent instance and also an SSH key B derived from the locally attached hardware key provided by trezor-agent. Perhaps A is an SSH certificate issued by a certificate authority, or else it can be any other key that is able to be provided by ssh-agent. In that case, since the user can only select one agent to forward, they must either choose to forward either trezor-agent or ssh-agent, but not both, so that the user when connected to the server now only has the single agent (and thus only key A or B) rather than their local machine which has access to both.

SSH configuration files can solve this by allowing the user to select at the time of invoking SSH which agent using the matching primitives like Host, User, Match exec, etc. which appears to be what is commonly used (e.g. #75), but this only works on the first hop that has access to both agents to select among. I believe the closest you could get is that one could write a script that could answer the Match exec directive, i.e.:

# .ssh/config

# ...

Match exec "trezor-agent-check-ident %r %h"
    IdentityAgent /run/user/1000/trezor-agent/S.ssh

# ...

# $HOME/.ssh/trezor.conf

ssh-ed25519 ... <ssh://user@host1|ed25519>
ssh-ed25519  ... <ssh://user@host2|ed25519>
#!/bin/bash

# trezor-agent-check-ident
#
# Usage: trezor-agent-check-ident [user] [host]
# 
# Exits with status code 0 if 'user@host' is a key that `trezor-agent` can provide, 1 otherwise

set -euo pipefail

user=$1
host=$2

grep ".* <ssh://$user@$host|.*>\$" $HOME/.ssh/trezor.conf
exit $?

However this still only works on the local host and not with agent forwarding to a remote server. Perhaps some hack can be used to forward the trezor-agent socket as well, or some crazy socat nc magic, but perhaps a better solution is transparently proxying commands to/from the standard OpenSSH agent.

trezor-agent would basically wrap an existing agent. When called to sign for an identity it would first check if the identity could be provided by the local hardware key, and if so then it would handle that, otherwise it would pass through the command to the chosen agent.

As I read the SSH agent protocol, here are the messages, by number:

       SSH_AGENTC_REQUEST_IDENTITIES                  11
       SSH_AGENTC_SIGN_REQUEST                        13
       SSH_AGENTC_ADD_IDENTITY                        17
       SSH_AGENTC_REMOVE_IDENTITY                     18
       SSH_AGENTC_REMOVE_ALL_IDENTITIES               19
       SSH_AGENTC_ADD_ID_CONSTRAINED                  25
       SSH_AGENTC_ADD_SMARTCARD_KEY                   20
       SSH_AGENTC_REMOVE_SMARTCARD_KEY                21
       SSH_AGENTC_LOCK                                22
       SSH_AGENTC_UNLOCK                              23
       SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED       26
       SSH_AGENTC_EXTENSION                           27

trezor-agent currently only responds to four messages and really only implements two:

  • SSH_AGENTC_REQUEST_RSA_IDENTITIES (legacy SSH protocol 1 reserved message number 1), trezor-agent constructs a "successful" response containing an empty list
  • SSH_AGENTC_REQUEST_IDENTITIES (message 11), trezor-agent replies with key list from local hardware key
  • SSH_AGENTC_SIGN_REQUEST (message 13), trezor-agent replies with signature
  • SSH_AGENTC_EXTENSION (message 27), trezor-agent constructs a protocol error response compliant with Section 3.8, namely SSH_AGENT_EXTENSION_FAILURE.

For SSH_AGENTC_REQUEST_IDENTITIES, trezor-agent could simply union the keys from the hardware key as well as the chosen passthrough agent.

For SSH_AGENTC_SIGN_REQUEST, trezor-agent uses the key identity to decide whether to sign or not. If the key identity is one it has on the local hardware key, it passes through to the local hardware key, and if not, passes through to the chosen agent.

For all the other messages, it appears to me that trezor-agent could simply forward the messages as-is to the chosen agent. Perhaps SSH_AGENTC_LOCK and SSH_AGENTC_UNLOCK could be implemented by revoking any cached session and then also forwarding the message to the chosen agent so as to lock both agents, and unlocking could be done specially, but I don't think agent locking is commonly used anyway, so it seems to me that it would be permissible to simply refuse the messages as well as it does today.

If this set of changes is too large in scope, I'd be happy to move this into a separate tool that could be composed with trezor-agent, however I think from my initial browsing of the code it would be largely contained to the SSH server implementation in

class Handler:
"""ssh-agent protocol handler."""
, basically a little proxy in front that decides "which SSH agent server to send it to" as well as the plumbing for the user to provide the passthrough agent socket path with defaults to the default OpenSSH agent.

@jacobgreenleaf jacobgreenleaf changed the title SSH: Passthrough to native OpenSSH agent SSH: Passthrough to native OpenSSH agent for better forwarding support Mar 30, 2024
@romanz romanz self-assigned this Mar 30, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants