The tasks: I have one or more desktop/laptop machines with varying local time zones (because the persons using them are actually in different time zones, or because the one person using them travels). I also have a number of servers configured in some random time zones. (It could be the time zone where they are physically located, or the time zone of the person who installed it, or UTC for neutrality.)
Now what I would like to have happen is that if I log in using SSH
from a desktop to a server, I see time on that server in my local time
zone. For things like ls -l
, for example. Obviously, this
illusion
will never be perfect. Nothing (except something very
complicated) will adjust the time stamps in the syslog output, for
example. But the ls -l
case in particular seems to come up a lot,
to check how long ago was this file modified.
This should be completely doable in principle, because you can set the
TZ
environment variable to any time zone you like, and it will be
used for things like ls -l
. But how do you get the TZ
setting
from here to there?
First, you have to make the remote SSH server accept the TZ
environment variable. At least on Debian, this is not done by
default. So make a setting like this in /etc/ssh/sshd_config
:
# Allow client to pass locale environment variables
AcceptEnv LANG LC_* TZ
You also need to make the equivalent setting on the client side,
either in /etc/ssh/ssh_config
or in ~/.ssh/config
:
SendEnv LANG LC_* TZ
Which leaves the question, how do you get your local time zone into
the TZ
variable to pass to the remote server? The actual time zone
configuration is the file /etc/localtime
, which belongs to glibc.
In current Debian, this is (normally) a copy of some file under
/usr/share/zoneinfo/
. In the distant past, it was a symlink, which
would have made things easier, but now it's a copy, so you don't know
where it came from. But the name of the time zone is also written to
/etc/timezone
, so you can use that.
The format of the TZ
environment variable can be found in the glibc
documentation. If you skip past most of the text, you will see the
relevant parts:
The third format looks like this:
:CHARACTERS
Each operating system interprets this format differently; in the GNU C library, CHARACTERS is the name of a file which describes the time zone.
So what you could do is set
TZ=":$(cat /etc/timezone)"
Better yet, for hopping through multiple SSH hosts in particular, make sure to preserve an already set TZ:
TZ=${TZ:-":$(cat /etc/timezone)"}
And finally, how does one hook this into ssh
? The best I could
think of is a shell alias:
alias ssh='TZ=${TZ:-":$(cat /etc/timezone)"} ssh'
Now this set up has a number of flaws, including:
Possibly only works between Linux (Debian?) hosts.
Only works if available time zone names match.
Only works when calling
ssh
from the shell.
But it practice it has turned out to be quite useful.
Comments? Improvements? Better ideas?
Related thoughts:
With this system in hand, I have resorted to setting the time zone on most servers to UTC, since I will see my local time zone automatically.
Important for the complete server localization illusion: some ideas on dealing with locales on remote hosts.
If there is no security concern with TZ, maybe it could be in SendEnv and AcceptEnv by default?
ReplyDeleteThat would be a good target.
DeleteMaybe you ssh to systems where you aren't root, so you can't add AcceptEnv in sshd.conf.
ReplyDeleteTypically, the server will already accept LC_*, so send your timezone as LC__TZ. Manipulate this in a shell start-up script like ~/.bashrc, something like this:
if LC__TZ is set; then set TZ from it; else set LC__TZ for later ssh; fi
It's too bad that in the case where you're not restricted in the commands you can run, you're restricted in the environment you can send. After all, you can just use the remote command 'env MYVAR=value ...' to achieve the same end.
Yeah, that is pretty stupid. Nice workaround.
DeleteIf you set TZ based on /etc/timezone in your local environment scripts, then there's no need to alias SSH. Thus, SSH instances not invoked directly from a shell session (graphical sftp client?) will get it too. Is having the environment variable set for everything a problem in any way?
ReplyDeleteThe environment variable wouldn't be updated when the time zone of the machine changes. One would effectively have to restart one's desktop session to get the changes communicated everywhere. Call me traumatized. ;-) Of course, if the shell alias is really a problem, one could also divert and overwrite the ssh binaries. So there are a number of solutions available with different tradeoffs. None of them are really that nice.
Delete/etc/timezone is not on Fedora or FreeBSD, what about using "date +%Z" instead of "cat /etc/timezone" to get the current timezone so that it's more OS/distro agnostic?
ReplyDeleteAlso, AcceptEnv set to LC_* seems to be common to Debian systems. FreeBSD doesn't have it defined at all by default, and Fedora/CentOS have the list spelled out by default, (each individual LC_ var) so jepler's comment is a damn good idea but won't work in those other environments, unfortunately.
The time zone produced by "date +%Z" doesn't appear to be accepted as input for the TZ variable. That is consistent with the glibc documentation.
Delete