Skip to content

Commit 1d9e5b8

Browse files
committed
Add support for an explicit path for agent_forwarding and ForwardAgent
This commit adds support for setting an exlpicit path to forward SSH agent requests to, rather than having them always be forward to the samt path as client requests. This support is also available through the ForwardAgent config option, and that also provides support for SSH config percent expansion.
1 parent 95756fa commit 1d9e5b8

File tree

4 files changed

+50
-11
lines changed

4 files changed

+50
-11
lines changed

asyncssh/config.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,22 @@ def _set_bool(self, option: str, args: List[str]) -> None:
219219
if option not in self._options:
220220
self._options[option] = value
221221

222+
def _set_bool_or_str(self, option: str, args: List[str]) -> None:
223+
"""Set a boolean or string config option"""
224+
225+
value_str = args.pop(0)
226+
value_lower = value_str.lower()
227+
228+
if value_lower in ('yes', 'true'):
229+
value: Union[bool, str] = True
230+
elif value_lower in ('no', 'false'):
231+
value = False
232+
else:
233+
value = value_str
234+
235+
if option not in self._options:
236+
self._options[option] = value
237+
222238
def _set_int(self, option: str, args: List[str]) -> None:
223239
"""Set an integer config option"""
224240

@@ -468,7 +484,7 @@ class SSHClientConfig(SSHConfig):
468484

469485
_conditionals = {'host', 'match'}
470486
_no_split = {'proxycommand', 'remotecommand'}
471-
_percent_expand = {'CertificateFile', 'IdentityAgent',
487+
_percent_expand = {'CertificateFile', 'ForwardAgent', 'IdentityAgent',
472488
'IdentityFile', 'ProxyCommand', 'RemoteCommand'}
473489

474490
def __init__(self, last_config: 'SSHConfig', reload: bool,
@@ -587,7 +603,7 @@ def _set_tokens(self) -> None:
587603
('Compression', SSHConfig._set_bool),
588604
('ConnectTimeout', SSHConfig._set_int),
589605
('EnableSSHKeySign', SSHConfig._set_bool),
590-
('ForwardAgent', SSHConfig._set_bool),
606+
('ForwardAgent', SSHConfig._set_bool_or_str),
591607
('ForwardX11Trusted', SSHConfig._set_bool),
592608
('GlobalKnownHostsFile', SSHConfig._set_string_list),
593609
('GSSAPIAuthentication', SSHConfig._set_bool),

asyncssh/connection.py

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7640,8 +7640,11 @@ class SSHClientConnectionOptions(SSHConnectionOptions):
76407640
made available for use. This is the default.
76417641
:param agent_forwarding: (optional)
76427642
Whether or not to allow forwarding of ssh-agent requests from
7643-
processes running on the server. By default, ssh-agent forwarding
7644-
requests from the server are not allowed.
7643+
processes running on the server. This argument can also be set
7644+
to the path of a UNIX domain socket in cases where forwarded
7645+
agent requests should be sent to a different path than client
7646+
agent requests. By default, forwarding ssh-agent requests from
7647+
the server is not allowed.
76457648
:param pkcs11_provider: (optional)
76467649
The path of a shared library which should be used as a PKCS#11
76477650
provider for accessing keys on PIV security tokens. By default,
@@ -7874,7 +7877,7 @@ class SSHClientConnectionOptions(SSHConnectionOptions):
78747877
:type agent_path: `str`
78757878
:type agent_identities:
78767879
*see* :ref:`SpecifyingPublicKeys` and :ref:`SpecifyingCertificates`
7877-
:type agent_forwarding: `bool`
7880+
:type agent_forwarding: `bool` or `str`
78787881
:type pkcs11_provider: `str` or `None`
78797882
:type pkcs11_pin: `str`
78807883
:type client_version: `str`
@@ -8016,7 +8019,7 @@ def prepare(self, # type: ignore
80168019
disable_trivial_auth: bool = False,
80178020
agent_path: DefTuple[Optional[str]] = (),
80188021
agent_identities: DefTuple[Optional[IdentityListArg]] = (),
8019-
agent_forwarding: DefTuple[bool] = (),
8022+
agent_forwarding: DefTuple[Union[bool, str]] = (),
80208023
pkcs11_provider: DefTuple[Optional[str]] = (),
80218024
pkcs11_pin: Optional[str] = None,
80228025
command: DefTuple[Optional[str]] = (),
@@ -8242,9 +8245,17 @@ def prepare(self, # type: ignore
82428245
self.pkcs11_pin = None
82438246

82448247
if agent_forwarding == ():
8245-
agent_forwarding = cast(bool, config.get('ForwardAgent', False))
8248+
agent_forwarding = cast(Union[bool, str],
8249+
config.get('ForwardAgent', False))
82468250

8247-
self.agent_forward_path = agent_path if agent_forwarding else None
8251+
agent_forwarding: Union[bool, str]
8252+
8253+
if not agent_forwarding:
8254+
self.agent_forward_path = None
8255+
elif agent_forwarding is True:
8256+
self.agent_forward_path = agent_path
8257+
else:
8258+
self.agent_forward_path = agent_forwarding
82488259

82498260
self.command = cast(Optional[str], command if command != () else
82508261
config.get('RemoteCommand'))

tests/test_channel.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -862,14 +862,14 @@ async def test_unneeded_resume_reading(self):
862862
chan.close()
863863

864864
@asynctest
865-
async def test_agent_forwarding(self):
866-
"""Test SSH agent forwarding"""
865+
async def test_agent_forwarding_explicit(self):
866+
"""Test SSH agent forwarding with explicit path"""
867867

868868
if not self.agent_available(): # pragma: no cover
869869
self.skipTest('ssh-agent not available')
870870

871871
async with self.connect(username='ckey',
872-
agent_forwarding=True) as conn:
872+
agent_forwarding='agent') as conn:
873873
chan, session = await _create_session(conn, 'agent')
874874

875875
await chan.wait_closed()

tests/test_config.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,18 @@ def test_set_remote_command(self):
335335
config = self._parse_config(' RemoteCommand foo bar baz')
336336
self.assertEqual(config.get('RemoteCommand'), 'foo bar baz')
337337

338+
def test_set_forward_agent(self):
339+
"""Test agent forwarding path config option"""
340+
341+
for value, result in (('yes', True), ('true', True),
342+
('no', False), ('false', False),
343+
('agent', 'agent'), ('%d/agent', './agent')):
344+
config = self._parse_config(f'ForwardAgent {value}')
345+
self.assertEqual(config.get('ForwardAgent'), result)
346+
347+
config = self._parse_config('ForwardAgent yes\nForwardAgent no')
348+
self.assertEqual(config.get('ForwardAgent'), True)
349+
338350
def test_set_request_tty(self):
339351
"""Test pseudo-terminal request config option"""
340352

0 commit comments

Comments
 (0)