Bug 3247 - SendEnv exclusion doesn't work as documented
Summary: SendEnv exclusion doesn't work as documented
Status: NEW
Alias: None
Product: Portable OpenSSH
Classification: Unclassified
Component: ssh (show other bugs)
Version: 8.4p1
Hardware: amd64 Linux
: P5 normal
Assignee: Assigned to nobody
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2021-01-03 01:35 AEDT by Guilhem
Modified: 2021-03-01 10:39 AEDT (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Guilhem 2021-01-03 01:35:26 AEDT
AFAICT it's not possible to clear a variable (or variable pattern) previously set in another file.  Consider a configuration file containing

    Host *
      SendEnv X_* XYZ

Assuming I read the manual correctly, the following command should send neither X_FOO nor XYZ:

    $ X_FOO=foo XYZ=xyz ssh -F/path/to/ssh.conf -oSendEnv=-X\* localhost env | grep -e^{X_,XYZ}
    X_FOO=foo
    XYZ=xyz

And this one to send neither X_FOO nor X_BAR nor XYZ, but only X_BAZ=baz:

    $ X_FOO=foo X_BAR=bar XYZ=xyz ssh -F/path/to/ssh.conf -oSendEnv=-X_\* -oSendEnv=-XYZ -oSetEnv=X_BAZ=baz localhost env | grep -e^{X_,XYZ}
    X_FOO=foo
    X_BAZ=baz
    X_BAR=bar
    XYZ=xyz

Interestingly, exclusion works differently when the SendEnv options are all at the same level (either in CLI options or in the same configuration file):

    $ X_FOO=foo X_BAR=bar ssh -F/dev/null -oSendEnv=X_{FOO,BAR} -oSendEnv=-X_BAR localhost env | grep ^X_
    X_FOO=foo

    $ X_FOO=foo X_BAR=bar ssh -F/dev/null -oSendEnv=X_\* -oSendEnv=-X_\* -oSetEnv=X_BAZ=baz localhost env | grep ^X_
    X_BAZ=baz

Both output are what one would expect.  However excluding a variable from wildcard is not: the following command should not have sent X_BAR, only X_FOO

    $ X_FOO=foo X_BAR=bar ssh -F/dev/null -oSendEnv=X_\* -oSendEnv=-X_BAR localhost env | grep ^X_
    X_FOO=foo
    X_BAR=bar

(For the context, Debian's /etc/ssh/ssh_config contains "SendEnv LC_*" for all hosts; I want to override that to send some selected hosts LC_ALL=C and no other LC_*.)
Comment 1 Guilhem 2021-01-04 08:56:20 AEDT
> AFAICT it's not possible to clear a variable (or variable pattern) previously set in another file.
> […]
> Assuming I read the manual correctly, the following command should send neither X_FOO nor XYZ:

Ah I think I understand why now, it seems clearing SendEnv/SetEnv is only done literally: -X_FOO and -X_* respectively clear X_FOO and X_*, but I was unable to send all variables matching a pattern except one.

Also, `SendEnv -X_*` in /etc/ssh/ssh_config clears `SendEnv X_*` from ~/.ssh/config (or respectively ~/.ssh/config and CLI option) not the opposite.

Is this really a bug or is it the intended behavior?  (Reading the manual again I can see how it could be interpreted that way, but if so the semantics are somewhat surprising.)
Comment 2 Damien Miller 2021-01-08 13:34:11 AEDT
Yes, it is intended behaviour and follows the option resolution ordering mentioned at the start of the ssh_config man page.

It's not really friendly unfortunately and that's a consequence of the mistake I made when originally implementing SendEnv of allowing multiple SendEnv directives to concatenate results. It's too late to fix this without causing widespread incompatibility though.

As far as achieving what you want to do, putting this in your ~/.ssh/config should do the trick:

Match final
   SendEnv -whatever

Unfortunately there's no nice way to do this on the command-line
Comment 3 Guilhem 2021-02-26 23:28:02 AEDT
(In reply to Damien Miller from comment #2)
> Yes, it is intended behaviour and follows the option resolution
> ordering mentioned at the start of the ssh_config man page.

I see, should I rename this issue into a documentation clarification request for SendEnv/SetEnv then?

> It's not really friendly unfortunately and that's a consequence of
> the mistake I made when originally implementing SendEnv of allowing
> multiple SendEnv directives to concatenate results. It's too late to
> fix this without causing widespread incompatibility though.
> 
> As far as achieving what you want to do, putting this in your
> ~/.ssh/config should do the trick:
> 
> Match final
>    SendEnv -whatever
> 
> Unfortunately there's no nice way to do this on the command-line

As far as I'm concerned a ssh_config snippet would do, but unfortunately I'm unable to get the above to work.  The distro-provided /etc/ssh/ssh_config contains

    Host *
      SendEnv LANG LC_*

(With a matching `AcceptEnv` setting in /etc/ssh/sshd_config.)  I'd like to add a custom Host/Match block in ~/.ssh/config to clear locales (or set to C) for a subset of selected hosts.  But even with the following ~/.ssh/config

    Match final
      SendEnv -LC_*
      SendEnv -LC_TIME
      SendEnv -LANG

LANG, LC_TIME, LC_PAPER etc. are sent along.  The debug log contains

    debug3: …/.ssh/config line 2: removing environment LC_*
    debug3: …/.ssh/config line 4: removing environment LANG
    debug3: Ignored env LANGUAGE
    debug1: Sending env LC_PAPER = sv_SE.UTF-8
    debug1: Sending env LANG = en_US.UTF-8
    debug1: Sending env LC_MEASUREMENT = sv_SE.UTF-8
    debug1: Sending env LC_TIME = C.UTF-8

AFAICT the `Match final` tricks works when I remove the `SendEnv LANG LC_*` from /etc/ssh/ssh_config and add it to ~/.ssh/config instead, but I'd prefer to preserve the system-provided configuration file and override in ~/.ssh/config instead, like for boolean settings PasswordAuthentication, CheckHostIP etc.  Is there a way to override in ~/.ssh/config a SendEnv setting defined in /etc/ssh/ssh_config?
Comment 4 Damien Miller 2021-03-01 10:20:30 AEDT
I see what is happening.

With "SendEnv LC_CTYPE" in /etc/ssh/ssh_config and "Match final\nSendEnv -*" in ~/.ssh/config:

[djm@tiresias ~]$ ssh -vvv charon
OpenSSH_8.4, LibreSSL 3.3.1
debug1: Reading configuration data /home/djm/.ssh/config
debug2: checking match for 'final' host charon originally charon
debug3: /home/djm/.ssh/config line 117: not matched 'final'
debug2: match not found
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: configuration requests final Match pass
debug1: re-parsing configuration
debug1: Reading configuration data /home/djm/.ssh/config
debug2: checking match for 'final' host charon originally charon
debug3: /home/djm/.ssh/config line 117: matched 'final'
debug2: match found
debug3: /home/djm/.ssh/config line 118: removing environment LC_CTYPE
debug1: Reading configuration data /etc/ssh/ssh_config

The configuration in ~/.ssh/config is working but, because /etc/ssh/ssh_config is reparsed after it, the "SendEnv LC_CTYPE" gets re-added.

A workaround for this would be to change /etc/ssh/ssh_config to wrap all SendEnv in "Match !final"
Comment 5 Guilhem 2021-03-01 10:39:06 AEDT
(In reply to Damien Miller from comment #4)
> A workaround for this would be to change /etc/ssh/ssh_config to wrap
> all SendEnv in "Match !final"

Ack, thanks!  That seems to work with simple `Host foo` blocks for clearing SendEnv/SetEnv.  I'll see if the distro maintainer accepts to ship a modified /etc/ssh/ssh_config.