Improve hostname detectiong for SCP and Automatic Profile Switching [was: Shell Integration determined hostnames on Debian and Ubuntu Linux hosts not working with mDNS]
Note: Revised the issue title to be more descriptive of the problem
Revised Summary: The shell integration script calculates hostnames for non-domain linux hosts that do not work with mDNS (multiCast DNS) a.k.a. users in non-DNS environment. Non-DNS environments are common when working with test VMs and programming embedded devices (like Raspberry Pis or BeagleBoards)
There are two variants of this problem (for non-domain hosts):
Debian Linux hosts will have a hostname in the format HOSTNAME.localdomain
Ubuntu Linux hosts will have a hostname in the format HOSTNAME (this is technically correct, but on a non-DNS system, it should default to .local as OSX does.)
These hostnames result in the hostname not resolving with mDNS, which breaks SCP functionality, and makes defining the hostname for automatic profile switching a challenge. mDNS requires a .local suffix (which IS properly identified on OSX).
Reproduction Steps
SSH into a Debian or Ubuntu Linux host with that does not have a domain setup and install the shell integration script (bash)
start a new SSH session
Note the SCP command in the right-click menu displays a HOSTNAME ending in ".localdomain" (on debian), or without a suffix (on Ubuntu)
Attempting to use the SCP command fails with a "could not connect error"
The 'File Transfer Summary' also displays a HOSTNAME ending in ".localdomain"
Auto Profile switching does not work correctly on these hosts
Likely Cause:
It appears iterm2_hostname is set via the hostname -f command (in line 258 of the Bash Shell Integration Script)
On Debian, hostname -f returns a HOSTNAME.localdomain if the domain is not set
On Ubuntu, hostname -f returns a HOSTNAME if the domain is not set
Both of these differ from OSX, where hosts without a domain have a ".local" suffix
Possible Workaround:
I added the following code to my Shell integration script (line 252) used on all of my hosts. It's been working for the last month on all platforms (OSX, Debian, Ubuntu)
if [ -z "$iterm2_hostname" ]; then if [ "$(domainname 2> /dev/null)" = "(none)" ]; then # no domain set # domainname=(none) implies an OS where 'hostname -f' ends in ".localdomain" or null iterm2_hostname="$(hostname -s).local" else # domainname set or on OS where 'hostname -f' ends in ".local" iterm2_hostname=$(hostname -f) fi fi
Version Info:
iTerm 2.9.20160209 (nightly) - has been broken since I started using the beta about a month ago.
OSX Yosemite 10.10.5 - shell extensions installed
Target Hosts exhibiting bad behavior - Debian Linux 8.2 (Jessie) & Raspian (Rasberry Pi version of Debian)
Designs
Child items
...
Show closed items
Linked items
0
Link issues together to show that they're related.
Learn more.
Why does it work any better without the .local suffix? Doesn't seem like it would resolve. Or is the hostname valid just within your network because of a domain search path?
This seems like a good idea but I'll need to update our localhost detection algorithm to do the same thing.
HOSTNAME.local resolves on Mac and Linux boxes via multicast DNS as part of RFC6762. (originally written by Apple in Feb 2013, but now supported by "most linux implementations" according to Wikipedia).
My observations of hostname resolution of non-domain systems
resolving HOSTNAME.local does not require adding .local to the search path (on OSX, Debian and Ubuntu)
non-domain hosts don't resolve if HOSTNAME is specified without a .local suffix (on both OSX & linux).
making HOSTNAME resolve without .local requires unicast DNS, a hosts file, or adding to the DNS search string
A quick followup regarding the question about the localhost detection algorithm
I don't think you need to change localhost detection algorithm at all, as it works fine if you simply specify hostnames that end in .local
This is only applies for users in non-DNS environments. If you haven't had to append .local to a hostname before, you won't have to after this change. This just fixes what is broken for non-DNS users who ssh to linux hosts.
For users working in non-DNS environment, adding .local to HOSTNAMES is standard practice for CLI network commands; everything from ping to ssh requires it
Even before my mod, this is already required for OSX machines without domains, as they are identified by iterm2_hostname as ending in .localNote: this is a good thing, if they didn't end in .local the SCP commands wouldn't work
My suggested mod just makes the .local suffix consistent for Debian & Ubuntu
username-removed-378216Title changed from Shell Integration incorrectly determining hostname on Debian Linux hosts without a domain to Shell Integration determined hostnames on Debian and Ubuntu Linux hosts not working with mDNS
Title changed from Shell Integration incorrectly determining hostname on Debian Linux hosts without a domain to Shell Integration determined hostnames on Debian and Ubuntu Linux hosts not working with mDNS
Thanks for the info about mDNS. I wasn't aware of it, and it's pretty cool! To sum up:
If you are in a non-(unicast)DNS environment then your hostname should end in .local. Unfortunately, hostname -f does not do this on all platforms. We'd like to detect the case where the following two cases are true:
We are in a non-DNS environment
hostname -f does not provide the .local suffix
When that is the case we need to:
Strip off a .localdomain suffix, if one exists.
Append a .local suffix, if one does not exist.
You propose that to determine if we are in a non-DNS environment we could use domainname(1). I don't think it will work, though. Based on the man page, it's for NIS/YP, not plain old DNS. On an account on a Ubuntu 12 machine, for example, I see this:
Thanks for the great summarization, and discovering my mistaken use of domainname on real-DNS systems.
I've researched and tested different options (on both mDNS and DNS environments), and the easiest test I found is: checking if uname -n equals hostname -s
This works because uname -n is the 'node-name', and hostname-s is machine-name with no suffix.
Here's the truth table for that test in the different scenarios:
linux mDNS only = TRUE (so we need to append ".local")
linux real-DNS = FALSE, since uname -n is an FQDN
OSX mDNS = FALSE, since uname -n already has ".local"
OSX real-DNS = FALSE, uname -n should be an FQDN (un-verified)
the above check written as code for shell integration script
if [ -z "$iterm2_hostname" ]; then nodename="$(uname -n 2> /dev/null)" shorthostname="$(hostname -s 2> /dev/null)" if [ "$nodename" = "$shorthostname" ]; then # we're on a linux box in mDNS mode - add ".local" iterm2_hostname="${shorthostname}.local" else # we have a real FQDN or are on OSX iterm2_hostname="$nodename" fi fi
Notes:
I tested this on mDNS Hosts: OSX, Debian & Ubuntu, and "real-DNS" Host: Debian (corrected: RHEL)
In these tests, I verified proper function of iTerm's new SCP feature and auto-profile switching
In the possible code above, the iterm2_hostname var after else could also be set to the output of hostname -f.
I used nodename since it had already been defined, and it won't "cost us" more execution time by spawning a 3rd shell command to run hostname -f (and get the same result)
Thanks for testing this, and I'm sorry about wasting your time with another workaround that didnt apply to all linux distros.
Some preliminary notes:
We may be able to build an algorithm out of the dnsdomainname command. Although it doesn't exist on OSX, we can script around that (since the hostname issue only exists on linux mDNS hosts).
Apparently uname -n isn't consistent between Linux distros; as on the one DNS-configured linux machine I had access to, it returned "HOSTNAME.domain.com" and worked.
I'll make sure to test future proposals on a variety of distros.
I now have test environments for DNS-enabled hosts on: RHEL, Ubuntu, SUSE, and Amazon Linux. (I still do not have access to OSX on real-DNS, so I hope you can test things there.)
Can you post the output of dnsdomainname (and hostname -f) on a DNS-connected OSX machine?
Jason's results are as expected and actually a good thing! They mean that dnsdomainname consistently fails on OSX, which we can check for. On OSX we don't need the info it returns as the results from hostname -f are correct for both mDNS and DNS environments.
Regarding OpenBSD issue #4225 - I would break that out into two sub-issues:
Determining the FQDN on OpenBSD. This may require two steps, checking if we're on OpenBSD, then using a unique command to determine the FQDN. In the worst case, it can be retrieved with cat /etc/myname(see 6.2.4 here) Note: if the host is in a non-DNS environment, it may have the same problem as my original issue (a non-resolvable name). If so, our eventual fix will apply to that as well.
a "feature request" to determine the specific address we connected to for hosts with multiple connections. (IMO - this is a much harder problem.)
Questions for you George:
What shells are supporting with your "shell integration" feature?
(I want to make sure I don't use any shell-specific checks if you need to port it to other shells).
can you run hostname, dnsdomainname and cat /etc/hostname on you Ubuntu host and post the results? I've made a table of the results which I'll post up to Google Docs and post a link.
I'll post in the other thread and get some more info, as I don't have access to an OpenBSD host.
Here's what man dnsdomainname has to say about fqdn's on Ubuntu:
You can't change the FQDN (as returned by hostname --fqdn) or the DNS domain name (as returned by dnsdomainname) with this
command. The FQDN of the system is the name that the resolver(3) returns for the host name.
Technically: The FQDN is the name getaddrinfo(3) returns for the host name returned by gethostname(2). The DNS domain name is the
part after the first dot.
Therefore it depends on the configuration (usually in /etc/host.conf) how you can change it. Usually (if the hosts file is parsed
before DNS or NIS) you can change it in /etc/hosts.
If a machine has multiple network interfaces/addresses or is used in a mobile environment, then it may either have multiple
FQDNs/domain names or none at all. Therefore avoid using hostname --fqdn, hostname --domain and dnsdomainname. hostname --ip-address
is subject to the same limitations so it should be avoided as well.
I don't know how popular YP/NIS is, but it could throw a monkey wrench in the ointment.
It contains a table of all of the various command outputs on the different OS and DNS environments
It also includes data on the connect-ability of the hostname, and different IP addresses types. (as related to issue #4225)
Here's some initial code for the OSX & Linux on mDNS & DNS issue. (OpenBSD not yet incorporated)
I tested this on OSX, and Linux hosts in both mDNS and DNS scenarios.
It does not yet incorporate code to assign a name for OpenBSD, but I included a spot in for it.
I also ran into the multi-homed host problem from issue #4225 on my AWS test instances. The FQDN hostnames returned on AWS are internal addresses which are not resolvable from the internet.
I thought of a potential way to address it via a "feature addition". Add an option in an iTerm profile to specify how the host should be identified in SCP - by hostname, internal IP, or external IP. In most cases it would be hostname, but with AWS it would be 'external IP'. The would likely address part of issue #4225 as well.
To assist with your testing of this, the code below adds two IP vars: IP_internal, and IP_external, which are the value for the remote host you're connected to. They can be queried after connecting to a host with echo $IP_external. Note: IP_internal represents the IP address from the remote SSH host connected to. So when queried on a terminal for the local device, it will be null.
IP_internal is generated from the BASH Environment var SSH_CONNECTION. I'm not sure if there's an equivalent on the other shells - however, I'm not even sure if this var is needed, as my tests seemed to indicate that if the hostname didn't resolve that I needed to connect to IP_external.
Here's the initial code:
if [ -z "$iterm2_hostname" ]; then OS=$(uname -s 2> /dev/null) IP_internal=$(echo $SSH_CONNECTION | awk '{split($3, ary, /%/); print ary[1]}') IP_external=$(curl -s ipinfo.io/ip 2> /dev/null) case $OS in Linux) domain_result=$(dnsdomainname 2> /dev/null) if [ -z "$domain_result" ] || [ "$domain_result" = "localdomain" ]; then # dnsdomainname is null or "localdomain", set to "HOSTNAME.local" hostname_short=$(hostname -s 2> /dev/null) iterm2_hostname="${hostname_short}.local" else iterm2_hostname=$(hostname -f 2> /dev/null) fi ;; OpenBSD) iterm2_hostname=TBD # NEED CODE ;; *) # this catch all includes Darwin (OSX) iterm2_hostname=$(hostname -f 2> /dev/null) ;; esacfi
Update 2/21/16 - removed "FreeBSD" from OpenBSD case as hostname -f works on it. Reference
This looks great! I didn't know about $SSH_CONNECTION, but that seems really useful.
Is the issue on AWS that you need to go jump through an intermediate host to ssh to them? Or just that their DNS info is wacky and you need to ssh directly to the IP?
FYI, I may not be able to implement these changes right away. The reaction to the v3 announcement was bigger than I anticipated and I have to claw my way out from under a mountain of bugs. I am totally in to this, though, and will make it happen as soon as the fires are out.
I was wondering why there were so many issues submitted recently, I've been so focused that I didn't even notice that you officially release v3. Congrats!
No worries about time from me, I realize there are more serious issues to fix, and this one is easy to self repair.
Some info on the AWS host resolution issues:
It's caused by the fact that the AWS hosts return internal IP addresses and hostnames when you use standard CLI commands.
for example: hostname -f returns ip-170-31-5-98.us-west-1.compute.internal versus a useable public address like ec2-52-36-5-98.us-west-1.compute.amazonaws.com
IP addresses have the same problem, with it returning addresses in a completely different private address space
You can only use the returned values if you are connecting from another host within the same datacenter (this isn't due to a security setting in my case, it just because of their DNS config)
To make things even more difficult, the "PublicDnsName" and "PublicIpAddress" change each time you shut-down & start up instances.
So anytime you've restarted you need to use their CLI tools to query the "PublicDnsName" (or PublicIpAddress) for your "Instance ID" before you know the address.
I've written a "helper" script to fetch these when I want to start an SSH session, but I haven't thought of a way to be able to use auto-profile switching with these yet (since the hostname string is different every time they've been shutdown/restarted.)
I'd like to help out in light of all of the other issues pulling at your time
would it be useful or me to submit a pull request with the minimum changes via Guthub?
I noticed that there isn't a Develop Branch on the repo you linked: https://github.com/robertpeteuil/iterm2-website/tree/master/source/misc
Is there a specific way I should submit this pull request (rather than to Master)?
Can we add version information/reporting to the integration scripts? Since this file doesn't get updated automatically, it would be nice if there was a way to capture what version people are using when they report issues.
Specifics on the changes I would submit
These changes would address the hard-error on OpenBSD, and address mDNS connectivity on Linux, but otherwise leave everything alone & assign the hostname var the old-way in other scenarios.
They would be similar to my last code-suggestion above, but contain iterm2_hostname=$(cat /etc/myname) for OpenBSD, and it would LEAVE OUT the IP test Vars (as we don't know how to use those yet)
The quicker we get this out there, the fewer people will submit issues on this same thing.
I may want to add to the help text where it talks about adding hostnames for auto-profile-switching, to add a bit about ".local" (as I think a lot of embedded devs and home users may run into this problem).
AWS seems like a particularly difficult problem that will need special handling. We could detect that you're on an AWS host in the installer script and add the extra code for AWS hostname detection at that time.
A pull request would be lovely. I periodically push the master branch to production. Since this seems more complex than the average change, let's work on it in a branch named something like improvedHostnameDetection.
While shell integration scripts do report a version number, I'm reluctant to use it because it'll prod users to install everywhere. Analytics is something I've wanted to add for a long time but right now there's basically nothing. It's a touchy privacy issue and I've been pretty conservative about that. I think it's fine to report data up to iTerm2, and some day I'll close the loop by having iTerm2 report analytics up to the server in some kind of opt-in privacy-friendly way.
I'm excited about fixing the OpenBSD issue, as that is an easy win. mDNS on Linux is probably less used, so if you want to split this up into pieces OpenBSD would be a good place to start.
What was the ultimate conclusion about .local? I am not a huge fan of help text because my experience is that users rarely read it. If we can show them the help text at the key moment they're trying to do something it'll get more attention and fewer issues filed.
Thanks for your help on this! I really appreciate the time you've invested so far.
I've already forked the repo and I'll create the improvedHostnameDetection branch tomorrow and add the initial changes.
The fix for OpenBSD has a lot of shared code for the linux mDNS issue (both require a select case on the OS), so adding the fix for both shouldn't take any more time (or add any more risk).
The conclusion of ".local" was that I have a fix that works, and I only run the tests/change on linux hosts, which limits the chances of it causing problems. I've tested this on a bunch of DNS and non-DNS distributions (both Linux and OSX machines).
I'll make my initial mods in Bash code (without any bash-isms), and hopefully someone with more experience in fish, zsh and tcsh can assist in porting it to those. (the code I'm adding is very basic and just if/else, select case, and assigns)
I agree that AWS is very unique problem and challenging to solve; classifying that as a "future feature" is spot on. (I'll defer spending any more time sharing additional details until we're ready.)
A couple of notes why I consider ".local" rather important
The ".local" issue is going to get increasingly important from two things (1) iTerm users who try to implement auto-profile switching and discover that it supports hostname but not IP (thus it's literally driving people to use hostnames for their SSH connections) and (2) the spread of IPv6 which makes memorizing addresses virtually impossible
This is exactly what happened to me; I used to do my embedded development work with SSH via hardcoded IPv4 address until I discovered the new iTerm auto-profiles - which required the use of hostname. (Which led me to discover all about mDNS and ".local" in order to get it working.)
I know there are millions of embedded devs and Raspberry Pi users who are using SSH with static IPv4 addresses today. When they learn about iTerm & auto-profile switching, they will likely follow the path I did, and I suspect you'll see a lot more "issues" on this as they try to use it.
In fact, I strongly suspect the two other issues that I posted in are also related to this; even the OpenBSD user. He had an additional problem with the hostname command erroring, but I'm pretty sure even if the hostname had been calculated that it wont work for him (as he mentioned that his hostname was non-resolvable, implying non-DNS, and he was connecting to a private IP.)
This is also why I mentioned a feature request for an option to allow profile switching based on IP (both internal and/or public - user definable in each profile - for local hosts running behind a NAT Router they will need to connect to an internal IP; for remote internet facing hosts, they will need to use the Public IP.)
I could've sworn my Mac had a .local suffix on its hostname -f but I just checked and it does not. At any rate, limiting it to Linux seems fine and if we can find a Mac that has it then we can get it working there too.
However, I was able to get profile switching to work using the fully-qualified domain name -- which 'hostname -f' but not just 'hostname' returns. 'hostname' just gives the leftmost token, and that's what I tend to think of as the 'hostname', so I hadn't thought to try the f.q. version. Probably worth emphasizing in the doc, or maybe a tooltip that tells people to try such variants. This feature is a huge motivator to use iterm2, imho....
Target system uname -a gives: Linux julian 3.13.0-59-generic #98 (closed)-Ubuntu SMP Fri Jul 24 21:05:26 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux
This is great feedback, Steve, and I'm glad you were finally able to get it working. I also agree that this is a huge motivator - in fact that how I came to spend so much time researching and working on this feature area.
I agree with your thoughts and hopefully future revisions will make this more easier. Here's some background info so you understand some of the rational and challenges:
iTerm's hostname variable is used for both identifying the host (for auto profile switching), and also as a resolvable network name for SCP.
From a "user association" perspective, I agree that the "short hostname" is a logical assumption (this can be retrieved with hostname -s on most systems). Unfortunately, this "short hostname" will not necessarily resolve for SCP.
For SCP to work, the required hostname format depends on wether you have a DNS or are using simple zero-configuration networking (mDNS). With a DNS, the FQDN works, while in small office / home environment HOSTNAME.local works (on OSX, and most current Linux distros). So it's important for the script to accurately determine which environment you're in and assign a hostname accordingly.
One of the feature requests I've made is for the the script to return a set of variables from the host (hostname, private IP, and public IP) and then allow the user to specify a var & value combination in the auto-profile settings in iTerm. (ex: for Profile ABC switch if "private IP = 10.10.5.5"). I will add a short hostname variable to this request, so it can be used for auto-profile switching (but still use the FQDN in the background for SCP).
username-removed-55954Title changed from Shell Integration determined hostnames on Debian and Ubuntu Linux hosts not working with mDNS to Improve hostname detectiong for SCP and Automatic Profile Switching [was: Shell Integration determined hostnames on Debian and Ubuntu Linux hosts not working with mDNS]
Title changed from Shell Integration determined hostnames on Debian and Ubuntu Linux hosts not working with mDNS to Improve hostname detectiong for SCP and Automatic Profile Switching [was: Shell Integration determined hostnames on Debian and Ubuntu Linux hosts not working with mDNS]
I've noticed all of the recent updates, and they are awesome. I'm not sure if my host-identification idea above is even necessary anymore, but I'll type it here for your reference.
My idea a few weeks ago was to populate some additional variables in the "shell integration scripts" that could be used as additional profile trigger items (in addition to what they have now). These are easy to implement, and would be useful to users who refer to hosts by a short-name or IP address.
The variables that I was thinking of are:
Full hostname - network resolvable name used by SCP - from the updated hostname code (that I submitted for BASH a few weeks ago)
Short hostname - from hostname -s
SSH IP (SSH IP is a more accurate description than private IP that I used above) - only has a value when host has been SSH'd into - from IP_ssh=$(echo $SSH_CONNECTION | awk '{split($3, ary, /%/); print ary[1]}')
Internet IP - only has a value if the host has curl installed - from IP_internet=$(curl -s ipinfo.io/ip 2> /dev/null)
I think it's a good idea, although I'm not sure how we could guess which one to use for scp. For automatic profile switching, though, it's pretty obvious that all of these are good representations of a hostname.
Internet IP is something users would need to opt in to because of possible security and privacy issues.
Anything like this will need to go in version 3.1 because I have more bugs than I can handle in 3.0 that block the release.
Thanks for the quick feedback and I agree on all points.
I had envisioned these additional variables for use in profile switching. So, for SCP, I'd continue to use full hostname, as you do now; perhaps you could add an option to use the SSH IP for SCP, but I would consider that a separate use-case.
Yes, security / privacy of IP address is a concern. Although my first thought is if it's use is limited to iTerm on their desktop machine, then I don't think there's any big issues.
I agree, these are definitely "enhancements" and thus a future feature.