[empty image] [empty image]
[empty image]
[empty image] [empty image] [empty image]
[empty image]

Locale environment passing
in ssh sessions

(unfortunately login shells aren't ready yet)

Preface:

The Secure Shell (SSH) [RFC4251] is a "protocol for secure remote login and other secure network services over an insecure network".

"Secure Shell (SSH) Connection Protocol" as defined in [RFC4254] provides "interactive login sessions, remote execution of commands, forwarded TCP/IP connections, and forwarded X11 connections. All of these channels are multiplexed into a single encrypted tunnel". The connection protocol describe channel request for "Environment Variable Passing".

OpenSSH is a FREE implementation of the SSH protocol. As of version 3.9 OpenSSH is able to pass environment variables. Environment passing is only supported for SSH protocol version 2.
On server side control is done via AcceptEnv(1) configuration option. Client use option SendEnv(2) to select variables from the local environment that should be sent to the server. Both options accept the wildcard characters "*" and "?".

User locale:

Main issue is to pass variables related to user locale. Lets to prepare list of variables to pass. "The locale program writes information about the current locale environment(3)(4)".
Let environment variable LANG is set to bg_BG.ISO8859-5. Command locale return:

LANG=bg_BG.ISO8859-5
LC_CTYPE="bg_BG.ISO8859-5"
LC_NUMERIC="bg_BG.ISO8859-5"
LC_TIME="bg_BG.ISO8859-5"
LC_COLLATE=bg_BG.ISO8859-5
LC_MONETARY="bg_BG.ISO8859-5"
LC_MESSAGES="bg_BG.ISO8859-5"
LC_PAPER="bg_BG.ISO8859-5"
LC_NAME="bg_BG.ISO8859-5"
LC_ADDRESS="bg_BG.ISO8859-5"
LC_TELEPHONE="bg_BG.ISO8859-5"
LC_MEASUREMENT="bg_BG.ISO8859-5"
LC_IDENTIFICATION="bg_BG.ISO8859-5"
LC_ALL=

Note for historical reason user may set LC_ALL too.
In additional to these variables is good to pass LANGUAGE - in use by GNU gettext(5). As result the list of necessary variables to pass on remote host is LANG, LC_* and LANGUAGE.

OpenSSH setup:

On the server side admin should put in sshd_config, usually located in directory /etc/ssh, following line:

AcceptEnv LANG LC_* LANGUAGE

Client program "ssh" can obtain configuration data from different sources:

  1. command line
  2. user configuration file ($HOME/.ssh/config)
  3. system-wide configuration file (usually /etc/ssh/ssh_config)

It is not recommended to put option SendEnv in system-wide configuration file. Good place is user configuration file in sections for selected hosts.

So far so good:
All that depend from openssh is done. We assume that locale database on hosts is properly setup. Unfortunately environment passing is secure shell session don't work at all.
Test case:

To test we should logon on remote system with following command:

$ LANG=testLANG \
  LC_ALL=testLC_ALL \
  LC_CTYPE=testLC_CTYPE \
  LC_TEST=testTEST \
  ssh <remote_host>

, where <remote_host> is name or ip_address on remote host and we override some environment variables from command line. Note that value of LC_* and LANG is invalid locale. In logon session we should execute:

$ echo "TEST=$LANG $LC_ALL $LC_CTYPE $LC_TEST"
TEST=testLANG testLC_ALL testLC_CTYPE testTEST

When result is not as shown above (in green) we should modify startup files read by login shell. Note that only root can modify system-wide startup files.

Solution:

The startup files we can obtain from shell manual page.

First step is to modify system-wide startup files. Note that files can contain commands that instruct shell to read from other files. To do this we should check all assignments to variables LANG, LC_CTYPE and LC_ALL. At beginning of startup file read first by shell we should check whether at least one of variables is set and when is set we should assign to variable LOCALE_IS_SET nonempty string, as example yes.

  • "Bourne" like shells:
    All shells read system-wide startup file /etc/profile. At beginning of file we should put statement:
    if test -n "$LANG" ||
       test -n "$LC_CTYPE" ||
       test -n "$LC_ALL"; then
      LOCALE_IS_SET="yes"
    fi
  • "C" like shells:
    For C-shells system-wide file vary on different platforms. On linux with tcsh implementation it is /etc/csh.login. At beginning of file we should put:
    if ($?LANG || $?LC_CTYPE || $?LC_ALL) then
      setenv LOCALE_IS_SET yes
    endif

Later in this file or in files read by shell we should find where user locale is set. Lines that set user locale we should enclose in if-statement:

  • "Bourne" like shells:
    if test -z "$LOCALE_IS_SET"; then
      #lines that set user locale, as example
      LANG=C
      export LANG
    fi
  • "C" like shells:
    if (! $?LOCALE_IS_SET) then
      #lines that set user locale, as example
      setenv LC_ALL POSIX
    endif

In personal startup files for all users we should enclose in if-statement lines that set locale. As example for german locale:

  • "Bourne" like shells (~/.profile):
    if test -z "$LOCALE_IS_SET"; then
      LANG=de_DE.ISO8859-1
      export LANG
    fi
  • "C" like shells (~/.login):
    if (! $?LOCALE_IS_SET) then
      setenv LC_ALL de_DE.ISO8859-1
    endif

It is good to find and modify template files in directory /etc/skel.

Tip: Following two commands help us to find system-wide startup files that we should modify so "locale environment passing in ssh sessions" to work.

$ grep -w LANG /etc/* /etc/*/* 2>/dev/null
$ grep -w LC_.* /etc/* /etc/*/* 2>/dev/null

One of files is /etc/profile.d/lang.sh if exist.

If exist /etc/environment take a look too.

Others:

In addition to variables that set user locale we might pass some others variables:

  • used by print spooler to set default printer:
    LPDEST, PRINTER or PDPRINTER
  • used by Xprint:
    XPRINTER and XPSERVERLIST
  • timezone:
    TZ
Developers might found that is usefull to pass CVSROOT and CVS_RSH.

Manual pages:
  1. sshd_config(5)
  2. ssh_config(5)
  3. locale(1)
  4. setlocale(3)
  5. gettext(3)
[empty image]
[empty image] [empty image] Last modified : Sunday May 12, 2019 [empty image]