Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ansible_fqdn variable uses socket.getfqdn() which can provide weird results. #9972

Closed
paulczar opened this issue Jan 9, 2015 · 12 comments
Closed
Labels
bug This issue/PR relates to a bug. P2 Priority 2 - Issue Blocks Release

Comments

@paulczar
Copy link
Contributor

paulczar commented Jan 9, 2015

I've been seeing some weird results show up in ansible_fqdn and finally tracked down why.

ansible_fqdn uses socket.getfqdn() which appears to collect the system hostname and then loook it up in DNS. If DNS doesn't exist for the host, or worse, it exists out on the internets because now just about anything is allowed as a TLD you get someone elses result.

For example I have a VM who's hostname is set to allinone.computer

$ hostname
allinone.computer

I run ansible -m test against it and I get these values in the results -

"ansible_fqdn": "yurika.gransy.com", 
"ansible_hostname": "allinone", 
"ansible_nodename": "allinone.computer",

yurika dot WTF?!?!?!

So I do some digging, namely an strace against a python script that calls socket.fqdn() ( see here for the script and strace - https://gist.github.com/paulczar/ee2a9cf3871038fd8beb ).

Turns out socket.fqdn() calls uname from which it collects the name allinone.computer.

uname({sys="Linux", node="allinone.computer", ...}) = 0
socket(PF_FILE, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 3
connect(3, {sa_family=AF_FILE, path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
close(3)  

It then calls out to resolve that via DNS, which since I don't have a DNS server with records for .computer it calls out to the TLD dns hints which happens to actually return a result because .computer is now a valid TLD.

sendto(3, "\314M\1\0\0\1\0\0\0\0\0\0\10allinone\10computer\0\0"..., 35, MSG_NOSIGNAL, NULL, 0) = 35
poll([{fd=3, events=POLLIN}], 1, 5000)  = 1 ([{fd=3, revents=POLLIN}])
ioctl(3, FIONREAD, [51])                = 0
recvfrom(3, "\314M\201\200\0\1\0\1\0\0\0\0\10allinone\10computer\0\0"..., 1024, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("10.0.2.3")}, [16]) = 51

it gets back an IP address ( 77.78.104.3 ) and the it looks up the PTR record for that IP which gives it

munmap(0x7fa34607a000, 4096)            = 0
stat("/etc/resolv.conf", {st_mode=S_IFREG|0644, st_size=194, ...}) = 0
socket(PF_INET, SOCK_DGRAM|SOCK_NONBLOCK, IPPROTO_IP) = 3
connect(3, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("10.0.2.3")}, 16) = 0
poll([{fd=3, events=POLLOUT}], 1, 0)    = 1 ([{fd=3, revents=POLLOUT}])
sendto(3, "\24\257\1\0\0\1\0\0\0\0\0\0\0013\003104\00278\00277\7in-addr"..., 42, MSG_NOSIGNAL, NULL, 0) = 42
poll([{fd=3, events=POLLIN}], 1, 5000)  = 1 ([{fd=3, revents=POLLIN}])
ioctl(3, FIONREAD, [73])                = 0
recvfrom(3, "\24\257\201\200\0\1\0\1\0\0\0\0\0013\003104\00278\00277\7in-addr"..., 1024, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("10.0.2.3")}, [16]) = 73
close(3)                                = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 2), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa34607a000
write(1, "('fqdn:', 'yurika.gransy.com')\n", 31('fqdn:', 'yurika.gransy.com')

thus ansible thinks my FQDN is yurika.gransy.com.

There are many reasons for a person not to have DNS confgured ( or worse, to have had their dns poisoned, or broken for some reason) for the hostname that they're using, and in my opinion it could lead to some very undesirable behavior. if I had used ansible_fqdn as the destination for syslog then I'd be sending all of my logs out to some random host on the internets.

@bcoca bcoca added P2 Priority 2 - Issue Blocks Release bug_report labels Jan 9, 2015
@retr0h
Copy link
Contributor

retr0h commented Jan 10, 2015

I'm not sure this is really an issue is it? Ansible is calling socket.gethostname() which will perform a f/rDNS lookup.

Seems like you are using an IP which already has DNS, and IMO should update your workflow to make sure DNS configured properly prior to provisioning. I guess I view it as an OOO issue.

@paulczar
Copy link
Contributor Author

Relying on external lookups for local facts like hostname ( fqdn ) is unreliable at best. Facts about a local machine should come from that machine, or at least be obvious ( in documentation and in naming ) that the fact uses external resources to determine its result.

The issue here is that I'm actually using a TLD that already exists ( .computer ) for which I had no idea ( looks like it was created in Dec 2013 ) until I ran across this issue. This combined with the fact that even if it wasn't a valid TLD a shady DNS server upstream could still have a record for it ( Many ISPs will respond with an IP for a landing page on bad DNS lookups ).

Therefore I cannot reliably use any hostname that I don't control the domain for in my own DNS servers if I don't want to risk ansible_fqdn giving me a potentially malicious result. It's not always practical to run your own DNS server ( development environments on laptops for distributed teams is a perfect example ).

This is a massive potential security hole that unless addressed with changes to the way ansible looks up this fact or at least an abundance of documentation and warnings about the potential hazards could make this a very hazardous fact to use.

@claco
Copy link
Contributor

claco commented Jan 10, 2015

What about in environments where you have public/private/multiple interfaces? Depending on provisioning, you have multiple ips in the hosts file for the same name. Doing a PTR lookup then is a free for all at best.

Is there any reason for ansible_fqdn to be anything other than hostname -f ?

@retr0h
Copy link
Contributor

retr0h commented Jan 10, 2015

Ah, I see. You simply named your system allinone.computer, and socket.gethostname() performs f/rDNS lookups which end up resulting in yurika.gransy.com.

I can see your point, if you don't want to manage DNS for hosts you are booting. Just want the functionality @claco touches on.

I personally like to manage DNS prior to systems I boot in route 53/dynect/self managed DNS, but I do see your point.

@paulczar
Copy link
Contributor Author

you got it! I get what you're saying about you should set up DNS before booting system but this specific case was throwaway vagrant VMs, so not worth messing with DNS.

I bet there are plenty of other use cases where setting up DNS is unnesesary overhead, and this behavior is unexpected and undocumented and potentially dangerous.

@mscherer
Copy link
Contributor

Then what about using ansible_nodename rather than ansible_fqdn ?

And ansible_fqdn kinda imply to use a resolver for that, cf hostname(1), so I do not think the current behavior should change. It could however be better documented, like all the facts. For now, we just have this http://docs.ansible.com/playbooks_variables.html#information-discovered-from-systems-facts , which just say "here is a bunch of facts about the system", and people are expected to look at the output and know what they mean, constraints, and this kind of things, which is not very intuitive.

@claco
Copy link
Contributor

claco commented Jan 12, 2015

@ansibot
Copy link
Contributor

ansibot commented Jan 12, 2015

Can You Help Us Out?

Thanks for filing a ticket! I am the friendly GitHub Ansibot.

It looks like you might not have filled out the issue description based on our standard issue template. You might not have known about that, and that's ok too, we'll tell you how to do it.

We have a standard template because Ansible is a really busy project and it helps to have some standard information in each ticket, and GitHub doesn't yet provide a standard facility to do this like some other bug trackers. We hope you understand as this is really valuable to us!.

Solving this is simple: please copy the contents of this template and paste it into the description of your ticket. That's it!

If You Had A Question To Ask Instead

If you happened to have a "how do I do this in Ansible" type of question, that's probably more of a user-list question than a bug report, and you should probably ask this question on the project mailing list instead.

However, if you think you have a bug, the report is the way to go! We definitely want all the bugs filed :) Just trying to help!

About Priority Tags

Since you're here, we'll also share some useful information at this time.

In general tickets will be assigned a priority between P1 (highest) and P5, and then worked in priority order. We may also have some follow up questions along the way, so keeping up with follow up comments via GitHub notifications is a good idea.

Due to large interest in Ansible, humans may not comment on your ticket immediately.

Mailing Lists

If you have concerns or questions, you're welcome to stop by the ansible-project or ansible-development mailing lists, as appropriate. Here are the links:

Thanks again for the interest in Ansible!

ypid added a commit to ypid/ansible-bootstrap that referenced this issue Sep 29, 2015
* Since 22fc14a the `ansible_fqdn`
  resolves to "localhost". Related to: ansible/ansible#9972
* This patch does not fix hosts already provisioned with the version
  22fc14a because it is expected that
  not many users already used it.
* Moving the v6 entry above localhost solves the issue.

Problem:

/etc/hosts

```
::1     localhost ip6-localhost ip6-loopback
0::1    host.testing   test
```

returns "localhost" for `python -c "import socket; print socket.getfqdn()"`

```
0::1    host.testing   test
::1     localhost ip6-localhost ip6-loopback
```

returns "host.testing" for `python -c "import socket; print socket.getfqdn()"`
@375gnu
Copy link

375gnu commented Feb 4, 2016

From man 1 hostname:

   The FQDN (Fully Qualified Domain Name) of the system is the name that the resolver(3) returns for the host name, such as, ursula.exam‐
   ple.com.  It is usually the hostname followed by the DNS domain name (the part after the first dot).  You can  check  the  FQDN  using
   hostname --fqdn or the domain name using dnsdomainname.

   You cannot change the FQDN with hostname or dnsdomainname.

   The  recommended method of setting the FQDN is to make the hostname be an alias for the fully qualified name using /etc/hosts, DNS, or
   NIS. For example, if the hostname was "ursula", one might have a line in /etc/hosts which reads

          127.0.1.1    ursula.example.com ursula

   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 of the resolver (usually in /etc/host.conf) how you can change it. Usually the hosts file is
   parsed before DNS or NIS, so it is most common to change the FQDN 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.

So, technically ansible does it right, and if you have incorrect ansible_fqdn either fix your DNS or /etc/hosts.

@nhooey
Copy link

nhooey commented Feb 4, 2016

I can confirm that putting a line like the following in /etc/hosts as described above fixes the issue for me:

127.0.1.1    ursula.example.com ursula

@jctanner
Copy link
Contributor

Hi!

Thanks very much for your submission to Ansible. It sincerely means a lot to us.

We're not sure this is a bug, and we don't mean for this to be confrontational. Let's explain what we're thinking:

  • Based on the discussions above, the current behavior seems correct and there are other ways to obtain the output of hostname -f if that is the primary need.

As such, we're going to close this ticket. However, we're open to being corrected, should you wish to discuss. You can stop by one of our two mailing lists
to talk about this and we might be persuaded otherwise.

Comments on closed tickets aren't something we monitor, so if you do disagree with this, a mailing list thread is probably appropriate.

Thank you once again for this and your interest in Ansible!

@nhooey
Copy link

nhooey commented May 26, 2016

@paulczar Use the ansible-hostname role to set your hostname and ensure the proper line exists in /etc/hosts, automagically.

@ansibot ansibot added bug This issue/PR relates to a bug. and removed bug_report labels Mar 6, 2018
@ansible ansible locked and limited conversation to collaborators Apr 25, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug This issue/PR relates to a bug. P2 Priority 2 - Issue Blocks Release
Projects
None yet
Development

No branches or pull requests

9 participants