Mailing List Archive

Problems using RemoteForward for gpg-agent with multiple sessions
Hello!

Many of my colleages at work are running into an issue with the
RemoteForward feature on unix domain sockets. I believe eliminating the
specific problem would require OpenSSH Portable source code changes, and
I'm hoping to start a discussion on what sort of patch would be
acceptable before submitting one.

We're using the RemoteForward config as suggested by the gnupg.org wiki
to forward the gpg-agent process's unix socket.

https://wiki.gnupg.org/AgentForwarding#OpenSSH_.3E.3D_6.7

Following the above, ~/.ssh/config file typically looks like this:

Host example
RemoteForward /run/user/1000/gnupg/S.gpg-agent /home/local/.gnupg/S.gpg-agent.extra

## Problem Details

This works well, but intermittently the remote forward is unexpectedly
destroyed. We've narrowed down the problem to the RemoteForward config
not interacting well with multiple SSH clients. Specifically, each
subsequent client attempts to initiate a new RemoteForward, destroying
any previous forwards when doing so. When the most recently connected SSH
client disconnects, the forward is left in an unbound state for all other
existing clients. The typical workflow here is a single user opening
multiple terminal tabs and running "ssh example". A simple reproduction
would be:

1. Run "ssh example" in terminal tab A.
2. Leaving tab A open, run "ssh example" in terminal tab B.
3. Close terminal tab B.
4. Switch back to tab A.
5. Observe that S.gpg-agent is now a dead link.

In retrospect, this is probably obvious behavior and expected. For
RemoteForward sockets to stay alive and avoid multiple connections
fighting over managing it, clients would have to be aware of each other.
Such a design would add significant complexity. (We're not using
ControlMaster due to security concerns.)

I think there's a few alternatives that might be reasonable
based on how the ForwardAgent feature works.

## Possibility 1: New RemoteForward syntax

As opposed to gpg-agent forwarding, ssh-agent forwarding does not have
this problem. I suspect avoiding this problem was actually an intentional
part of ssh-agent forwarding's design. Instead of a hard-coded file
system path, the ForwardAgent config instructs the server to create a new
socket bind in a temporary location unique to that SSH client connection.
The SSH_AUTH_SOCK environment variable is initialized to this temporary
path so processes in the remote host know how to communicate with the
forwarded ssh-agent.

$ echo "$SSH_AUTH_SOCK"
/tmp/ssh-KbsgWY3jcN/agent.340671

My first proposal would be to extend the RemoteForward ssh_config to
allow similar behavior.

Host
RemoteForward env:GPG_AGENT_SOCK /home/local/.gnupg/S.gpg-agent.extra

In this proposal, the local path would be forwarded to a random path on
the remote and set in $GPG_AGENT_SOCK.

$ echo "$GPG_AGENT_SOCK"
/tmp/ssh-UIzrILLqkZ/sock.340851

This design enhances unix socket forwarding in general and is therefore
agnostic to gpg-agent, but to complete the example workflow we'd then set
the S.gpg-agent file to:

$ cat /run/user/1000/gnupg/S.gpg-agent
%Assuan%
socket=${GPG_AGENT_SOCK}

Note: The gpg command has socket redirection builtin through this special
%Assuan% directive and is documented in the assuan_sock_set_sockaddr_un
%function.

For security, the server config should set AcceptEnv.

AcceptEnv GPG_AGENT_SOCK

## Possiblity 2: New ForwardGpgAgent config

Another option is to build gpg-agent forwarding directly in OpenSSH.

Host
ForwardGpgAgent /home/local/.gnupg/S.gpg-agent.extra

I'm not particularly excited about this option since it's unnecessarily
specific, but seems to follow the pattern of the exiting ForwardAgent and
ForwardX11 ssh_config options.

## Possibility 3: Allow client-side TCP port to be used in bind path

Looking over the % token expansions available to RemoteForward, none of
the existing tokens would allow a different remote path to be used for
different SSH client connections. Since the overall problem is a
hard-coded static path, anything dynamic would fix the problem.

Experimenting a bit, I was able to add a percent encoding for the
client-side TCP port (%d in the example below), which would be specific
to each connection.

Host
RemoteForward /tmp/%d-gpg.sock /home/local/.gnupg/S.gpg-agent.extra

This was a clever workaround I got working, but it turns out the client
TCP port is undefined when using the ProxyCommand option. So this
unfortunately wouldn't solve the problem for my team at work.

---

I'd appreciate any and all thoughts. I mentioned a few things that I
think are obvious to OpenSSH developers, but elaborated regardless so
anyone could contribute thoughts on the problem.

Thanks everyone,

Brandon
_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@mindrot.org
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev
Re: Problems using RemoteForward for gpg-agent with multiple sessions [ In reply to ]
On Tue, 7 Jun 2022, Brandon Cheng wrote:

> not interacting well with multiple SSH clients. Specifically, each
> subsequent client attempts to initiate a new RemoteForward, destroying

This is the same for TCP port forwarding, except the next attempts
say they cannot bind.

Canonical solution for that is to use a muxmaster, which then does
the forwards, and subsequent connections just pick up the mux.

bye,
//mirabilos
--
Infrastrukturexperte • tarent solutions GmbH
Am Dickobskreuz 10, D-53121 Bonn • http://www.tarent.de/
Telephon +49 228 54881-393 • Fax: +49 228 54881-235
HRB AG Bonn 5168 • USt-ID (VAT): DE122264941
Geschäftsführer: Dr. Stefan Barth, Kai Ebenrett, Boris Esser, Alexander Steeg

****************************************************
/?\ The UTF-8 Ribbon
? ? Campaign against Mit dem tarent-Newsletter nichts mehr verpassen:
 ?  HTML eMail! Also, https://www.tarent.de/newsletter
? ? header encryption!
****************************************************
_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@mindrot.org
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev
Re: Problems using RemoteForward for gpg-agent with multiple sessions [ In reply to ]
Thanks for your thoughts Thorsten,

On Tue, 7 Jun 2022, Thorsten Glaser wrote:
> > not interacting well with multiple SSH clients. Specifically, each
subsequent client attempts to initiate a new RemoteForward, destroying
>
> This is the same for TCP port forwarding, except the next attempts
say they cannot bind.

TCP port forwarding has a dynamic port selection feature not available to
unix socket forwarding. From ssh_config:

If the port argument is 0, the listen port will be dynamically
allocated on the server and reported to the client at run time.

I think it seems reasonable to extend that to unix socket forwarding.
This is similar in principle to what ForwardAgent currently does.

Note that one problem with setting the TCP port argument to 0 is that the
bound value is reported to the client rather than the remote environment.
I think others have noticed that this could have more utility reported as
an environment variable on the remote.

Example: https://bugzilla.mindrot.org/show_bug.cgi?id=2336

> Canonical solution for that is to use a muxmaster, which then does
the forwards, and subsequent connections just pick up the mux.

Recognizing that this is a possible workaround, I think there's still
significant advantages to decoupling RemoteForward from ControlMaster.
With a muxer, all SSH connections go through a single TCP connection
which can cause shared latency. It seems unintuitive to couple
ControlMaster and its side effects to a seamless RemoteForward workflow,
especially when ForwardAgent has a first-party private implementation
that third-party tools like gpg-agent could benefit from.

_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@mindrot.org
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev
Re: Problems using RemoteForward for gpg-agent with multiple sessions [ In reply to ]
On Tue, 7 Jun 2022, Brandon Cheng wrote:

> > Canonical solution for that is to use a muxmaster, which then does
>> the forwards, and subsequent connections just pick up the mux.
>
> Recognizing that this is a possible workaround, I think there's still
> significant advantages to decoupling RemoteForward from ControlMaster.
> With a muxer, all SSH connections go through a single TCP connection
> which can cause shared latency. It seems unintuitive to couple

You can split that as well, though.

Just add a separate Host section to your SSH config for the muxer,
use it with -fNM to start the muxer, without to use it, and use a
different Host section to create separate connections. On the remote
side, just pick up the agent forwarded from the other connection.

This needs a little shell scripting but no more than your solution,
I believe. I’ve got a similar setup except I make all remote sessions
and all desktop sessions on the remote box (native X and xrdp and VNC)
pick up the ssh-agent and gpg-agent running on the remote box, instead
of forwarding one from the local box.

bye,
//mirabilos
--
Infrastrukturexperte • tarent solutions GmbH
Am Dickobskreuz 10, D-53121 Bonn • http://www.tarent.de/
Telephon +49 228 54881-393 • Fax: +49 228 54881-235
HRB AG Bonn 5168 • USt-ID (VAT): DE122264941
Geschäftsführer: Dr. Stefan Barth, Kai Ebenrett, Boris Esser, Alexander Steeg

****************************************************
/?\ The UTF-8 Ribbon
? ? Campaign against Mit dem tarent-Newsletter nichts mehr verpassen:
 ?  HTML eMail! Also, https://www.tarent.de/newsletter
? ? header encryption!
****************************************************
_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@mindrot.org
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev
Re: Problems using RemoteForward for gpg-agent with multiple sessions [ In reply to ]
On Tue, 7 June 2022, Thorsten Glaser wrote:
> On Tue, 7 Jun 2022, Brandon Cheng wrote:
> > Recognizing that this is a possible workaround, I think there's
> > still significant advantages to decoupling RemoteForward from
> > ControlMaster. With a muxer, all SSH connections go through a single
> > TCP connection which can cause shared latency. It seems unintuitive
> > to couple
>
> You can split that as well, though.
>
> Just add a separate Host section to your SSH config for the muxer,
> use it with -fNM to start the muxer, without to use it, and use a
> different Host section to create separate connections. On the remote
> side, just pick up the agent forwarded from the other connection.

I have a few workarounds of this type as well. It similarly uses -N and
I let it background through tmux.

I appreciate the solution you've offered. I agree this works, but I
do still believe OpenSSH could do better:

- While the script works well, it's cumbersome to remember to start
the command and for the right server. The script could be automated
to run at startup, but then you may be paying for network bandwith
that may not be used.
- The command intermittently disconnects due to spurious network
conditions. I'd like to add retry logic, but I'm hesitant to have a
busy while loop in the background that could go awry. Ideally this
is a script with incremental backoff, or watches network conditions
to know when it should re-attempt connections.

Our team began requiring GPG commit signing recently. Although I can
personally use this setup without problem, I've noticed significant
difficult managing this from my teammates less familiar with unix
tooling.

My intention was to offer any help I can to make this easier for all
OpenSSH and GPG users.

On Tue, 7 June 2022, Thorsten Glaser wrote:
> This needs a little shell scripting but no more than your solution,
> I believe.

While the solutions in the first email require one-time config setup, I
don't believe they require shell scripts unless I missed something.

Thanks for elaborating on your local setup. Always interesting to see
how others solve similar problems.

_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@mindrot.org
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev
Re: Problems using RemoteForward for gpg-agent with multiple sessions [ In reply to ]
On Tue, 7 Jun 2022, Brandon Cheng wrote:

> This works well, but intermittently the remote forward is unexpectedly
> destroyed. We've narrowed down the problem to the RemoteForward config
> not interacting well with multiple SSH clients. Specifically, each
> subsequent client attempts to initiate a new RemoteForward, destroying
> any previous forwards when doing so. When the most recently connected SSH
> client disconnects, the forward is left in an unbound state for all other
> existing clients.

[...]

> ## Possibility 1: New RemoteForward syntax

[...]

> ## Possiblity 2: New ForwardGpgAgent config

[...]

> ## Possibility 3: Allow client-side TCP port to be used in bind path

Another possibility would be to have some %-expansion that expands to
a random value that is long enough to be safely used as a temporary
path.

E.g. %R expanding to 24 base64 characters. You could use this to obtain
effectively unique paths.

-d
_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@mindrot.org
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev
Re: Problems using RemoteForward for gpg-agent with multiple sessions [ In reply to ]
On Fri, 10 Jun 2022, Damien Miller wrote:
> Another possibility would be to have some %-expansion that expands to
> a random value that is long enough to be safely used as a temporary
> path.
>
> E.g. %R expanding to 24 base64 characters. You could use this to
> obtain effectively unique paths.

This would be a great solution.

To complete this option, how might the server determine the unique path?
I'm leaning towards SetEnv and updating it to understand
%-expansions. (If it doesn't already.)

Host example
RemoteForward /tmp/%R.sock /home/local/.gnupg/S.gpg-agent.extra
SetEnv SSH_R_EXPANSION=%R

At the moment all %-expansions happen client-side, which is a nice and
simple design. The server could perform the %R expansion server-side if
that's the right approach, but it'd introduce a lot of new logic to the
server.

One other alternative to SetEnv would be to send the client-computed %R
as a SSH_CHANNEL_LARVAL state command, which is also involved.

_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@mindrot.org
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev
Re: Problems using RemoteForward for gpg-agent with multiple sessions [ In reply to ]
On Fri, 10 Jun 2022, Brandon Cheng wrote:

> On Fri, 10 Jun 2022, Damien Miller wrote:
> > Another possibility would be to have some %-expansion that expands to
> > a random value that is long enough to be safely used as a temporary
> > path.
> >
> > E.g. %R expanding to 24 base64 characters. You could use this to
> > obtain effectively unique paths.
>
> This would be a great solution.
>
> To complete this option, how might the server determine the unique path?
> I'm leaning towards SetEnv and updating it to understand
> %-expansions. (If it doesn't already.)
>
> Host example
> RemoteForward /tmp/%R.sock /home/local/.gnupg/S.gpg-agent.extra
> SetEnv SSH_R_EXPANSION=%R

I think that could be made to work - we'd need ssh's main() to generate
a single random token per connection, though it might be possible for
a user to shoot themselves in the foot if they reused the token for
multiple things and exposed it to an on-host attacker. We generally
avoid leaving footguns around.

> At the moment all %-expansions happen client-side, which is a nice and
> simple design. The server could perform the %R expansion server-side if
> that's the right approach, but it'd introduce a lot of new logic to the
> server.
>
> One other alternative to SetEnv would be to send the client-computed %R
> as a SSH_CHANNEL_LARVAL state command, which is also involved.

Another option: remote port-forwarding has a notion of server-allocated
ports, that are communicated back to the client. We could do something
similar for Unix domain sockets too, but with a server-generated /tmp
path instead of a port.

The downside is that this is only really useful when used in conjunction
with ControlPath, and they take a fair bit more connection setup.

Another option still: use the server-assigned port/temp-path feature, but
have the client automatically create SetEnv directives for it. Since
forwardings are processed before shell/command session execution, this
could be fairly automatic for users. The catch here is figuring out how
to communicate multiple (potentially many) forwardings via SetEnv.

-d
_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@mindrot.org
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev
Re: Problems using RemoteForward for gpg-agent with multiple sessions [ In reply to ]
On Mon, 13 Jun 2022, Damien Miller wrote:

> On Fri, 10 Jun 2022, Brandon Cheng wrote:
> > To complete this option, how might the server determine the unique
> > path? I'm leaning towards SetEnv and updating it to understand
> > %-expansions. (If it doesn't already.)
> >
> > Host example
> > RemoteForward /tmp/%R.sock /home/local/.gnupg/S.gpg-agent.extra
> > SetEnv SSH_R_EXPANSION=%R
>
> I think that could be made to work - we'd need ssh's main() to
> generate a single random token per connection, though it might be
> possible for a user to shoot themselves in the foot if they reused
> the token for multiple things and exposed it to an on-host attacker.
> We generally avoid leaving footguns around.

I think you're right. Although the random token derived socket paths
don't need to be secret for this use case, I could see users relying on
the random token for other use cases requiring secrecy.

Thanks for keeping security in mind. Although this could work, I'd
be nervous about creating a new common pattern of security
vulnerabilities.

On Mon, 13 Jun 2022, Damien Miller wrote:

> Another option: remote port-forwarding has a notion of
> server-allocated ports, that are communicated back to the client. We
> could do something similar for Unix domain sockets too, but with a
> server-generated /tmp path instead of a port.

> The downside is that this is only really useful when used in
> conjunction with ControlPath, and they take a fair bit more connection
> setup.

Does the server-generated path need to be communicated back to the
client? Taking a spin on the "Possibility 1: New RemoteForward syntax"
suggestion from the first email, could the server-generated tmp path be
placed in an $SSH_REMOTE_FORWARD_SOCKS_DIR environment variable?

Host example
RemoteForward 0:example-foo /home/local/.foo.sock
RemoteForward 0:example-bar /home/local/.bar.sock

$ ssh example
$ echo $SSH_REMOTE_FORWARD_SOCKS_DIR
/tmp/ssh-UIzrILLqkZ
$ ls -l $SSH_REMOTE_FORWARD_SOCKS_DIR
example-foo.sock
example-bar.sock

Some immediate caveats I can think of:

- This deviates from the TCP port behavior, which can be surprising.
- All forwarded sockets share the same server-generated path. Is this
a security concern? I think any unique path -> envvar approach would
leak RemoteForward socket paths to on-host attackers anyway, even if
each RemoteForward had a unique environment variable. By design this
is intentionally available to on-host processes.


_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@mindrot.org
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev
Re: Problems using RemoteForward for gpg-agent with multiple sessions [ In reply to ]
Hi Damien,

I was wondering if you had any further thoughts or if this isn't
solvable without significant tradeoffs. I'm still happy to implement any
of the suggestions and submit patches. I think I prefer the most
recently mentioned $SSH_REMOTE_FORWARD_SOCKS_DIR approach.

Thanks for your time.

> On Jun 14, 2022, at 12:48 AM, Brandon Cheng <brandon@brandoncheng.me> wrote:
>
> On Mon, 13 Jun 2022, Damien Miller wrote:
>
>> On Fri, 10 Jun 2022, Brandon Cheng wrote:
>>> To complete this option, how might the server determine the unique
>>> path? I'm leaning towards SetEnv and updating it to understand
>>> %-expansions. (If it doesn't already.)
>>>
>>> Host example
>>> RemoteForward /tmp/%R.sock /home/local/.gnupg/S.gpg-agent.extra
>>> SetEnv SSH_R_EXPANSION=%R
>>
>> I think that could be made to work - we'd need ssh's main() to
>> generate a single random token per connection, though it might be
>> possible for a user to shoot themselves in the foot if they reused
>> the token for multiple things and exposed it to an on-host attacker.
>> We generally avoid leaving footguns around.
>
> I think you're right. Although the random token derived socket paths
> don't need to be secret for this use case, I could see users relying on
> the random token for other use cases requiring secrecy.
>
> Thanks for keeping security in mind. Although this could work, I'd
> be nervous about creating a new common pattern of security
> vulnerabilities.
>
> On Mon, 13 Jun 2022, Damien Miller wrote:
>
>> Another option: remote port-forwarding has a notion of
>> server-allocated ports, that are communicated back to the client. We
>> could do something similar for Unix domain sockets too, but with a
>> server-generated /tmp path instead of a port.
>
>> The downside is that this is only really useful when used in
>> conjunction with ControlPath, and they take a fair bit more connection
>> setup.
>
> Does the server-generated path need to be communicated back to the
> client? Taking a spin on the "Possibility 1: New RemoteForward syntax"
> suggestion from the first email, could the server-generated tmp path be
> placed in an $SSH_REMOTE_FORWARD_SOCKS_DIR environment variable?
>
> Host example
> RemoteForward 0:example-foo /home/local/.foo.sock
> RemoteForward 0:example-bar /home/local/.bar.sock
>
> $ ssh example
> $ echo $SSH_REMOTE_FORWARD_SOCKS_DIR
> /tmp/ssh-UIzrILLqkZ
> $ ls -l $SSH_REMOTE_FORWARD_SOCKS_DIR
> example-foo.sock
> example-bar.sock
>
> Some immediate caveats I can think of:
>
> - This deviates from the TCP port behavior, which can be surprising.
> - All forwarded sockets share the same server-generated path. Is this
> a security concern? I think any unique path -> envvar approach would
> leak RemoteForward socket paths to on-host attackers anyway, even if
> each RemoteForward had a unique environment variable. By design this
> is intentionally available to on-host processes.
>
>
> _______________________________________________
> openssh-unix-dev mailing list
> openssh-unix-dev@mindrot.org
> https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev


_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@mindrot.org
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev