Warning: Can't synchronize with repository "(default)" (Unsupported version control system "svn": No module named svn). Look in the Trac log for more information.

Ticket #1713 (closed defect: fixed)

Opened 10 years ago

Last modified 9 years ago

No access to Toolbox when server.socket_host not set

Reported by: Chris Arndt Owned by: Chris Arndt
Priority: high Milestone: 1.0.x bugfix
Component: Toolbox Version: 1.0.4.2
Severity: major Keywords:
Cc:

Description

When you run tg-admin tooolbox and server.socket_host is not set in the app's config, the browser will display the following error message on opening the toolbox page:

No access for ::ffff:127.0.0.1

By default only localhost (127.0.0.1) has access to the Toolbox

You can provide access to your client by passing your host address to Toolbox as a parameter. Ex:

                        tg-admin toolbox -c ::ffff:127.0.0.1

The error is exactly the same regardless of whether you access  http://localhost:7654/,  http://127.0.0.1:7654/ or  http://0.0.0.0:7654/.

If you set server.socket_host="localhost" in dev.cfg everything works normal.

Attachments

toolbox-ipv6-workaround.diff Download (523 bytes) - added by Chris Arndt 9 years ago.
Simple patch implementing workaround solution a. from comment no. 10
toolbox-no-ipv6-default.diff Download (2.9 KB) - added by Chris Arndt 9 years ago.
Patch implementing solution described in comment no. 11 (patch above included)
test_socket_server.py Download (1.7 KB) - added by Chris Arndt 9 years ago.
Minimal socket test server that emulates cherrypy.WSGIServer behaviour
match-ipv6.patch Download (6.7 KB) - added by chrisz 9 years ago.
This patch makes the match_ip function IPv6-aware

Change History

comment:1 Changed 10 years ago by faide

  • Milestone changed from 1.1 to 1.0.4

There is no such problem when using the toolbox on Windows. CP reports it is listening on 0.0.0.0 and it just works as expected otherwise.

We could force the socket_host value in the command line starter but I'd like someone to test and confirm the fix on thoses plateforms: Linux, Mac

comment:2 Changed 10 years ago by faide

I changed the target to 1.0.4 since we need to release a some fixes to 1.0.4 still and I feel the toolbox problem is big enough to be included in the 1.0 branch.

Moreover, this problem was reported after the security fix which forced an urgent upgrade to CP 2.3.0 and as such represents an regression and should be fixed.

comment:3 Changed 10 years ago by Chris Arndt

We could also consider to set server.socket_host="localhost" by default in dev.cfg of quickstarted projects. IMHO an app running in development mode should not be reachable from other hosts by default anyway.

Of course we should still make sure that the toolbox runs regardless of the setting dev.cfg.

comment:4 Changed 10 years ago by khorn

Looking through the mailing list, this problem seems to have only been reported when running under Ubuntu. Not sure if it's a Ubuntu-specific problem, or if its more general (Linux-only).

Perhaps Ubuntu has a IPv6 setup which is not being accounted for?

Possible workaround until this gets fixed: run tg-admin toolbox with "-c ::ffff:127.0.0.1"

Can anyone verify this?

comment:5 follow-up: ↓ 6 Changed 10 years ago by Chris Arndt

The workaround with the -c option works for me but the problem persists. Tested on two computers, both with Ubuntu Feisty 7.04, TG 1.0.4.

comment:6 in reply to: ↑ 5 Changed 9 years ago by alexandre

persists for me too (ubuntu 7.10) any version of tg with new cherrypy bugfix. cherrypy "remoteAddr" is also returning this address (seems to be an ipv6 like addr), I've been checked this on some tries to get the client IP for logging.

comment:7 follow-up: ↓ 8 Changed 9 years ago by faide

This seems to be happening on machines with IPV6 addresses (Linux boxes mainly).

The solution could be to force the socket host to be 127.0.0.1 by default in the config file...

Could someone who encounters the problem try this and report successes/failures?

comment:8 in reply to: ↑ 7 Changed 9 years ago by alexandre

The config workarround works for me.

comment:9 Changed 9 years ago by khorn

  • Milestone changed from 1.0.4 to 1.0.x bugfix

version 1.0.4 has already been released and this ticket hasn't been updated since the last 1.0.x release

moving to 1.0.x bugfix milestone

comment:10 Changed 9 years ago by Chris Arndt

  • Status changed from new to assigned
  • Owner changed from anonymous to Chris Arndt

Some investigation into this matter, but no solution yet:

  • The tg-admin toolbox command starts a CherryPy server with toolbox.Toolbox as the root controller, wrapped into a identity.SecureObject.
  • The predicate for the SecureObject is identity.from_any_host with the list of allowed hosts set to ['127.0.0.1', '::1'].
  • Ubuntu has a mixed IPV4/IPV6 setup, where the loopback device has an IPV6 address of '::1/128' (the /128 part is an alternative notation for ::1 , see  Wikipedia: IPV6 - Network notation).
  • identity.from_any_host resp. identity.from_host uses identity.conditions._match_ip to check if the IP address of the requesting host is in the list of allowed addresses and networks. It passes the hostname returned by identity.conditions._remoteHost, which reads the remote hostname from the X-Forwarded-For or Remote-Addr request headers.
  • identity.conditions._remoteHost reports the request coming from localhost with an IPV6 address ::ffff:127.0.0.1, which is an  IPV4 mapped address, equivalent to ::1.
  • identity.conditions._match_ip does not support IP addresses* :-( and so will return False, causing access to be rejected by identity.from_any_host.
  • It will somehow work for non-network IPV6 addresses (with a "/"), because it does a simple equality test for what it believes are host addresss, but if the address to check against contains a "/", it will fail for IPV6 addresses, because it uses socket.inet_aton to resolve network masks, which does not support IPV6 addresses.

Open questions:

  • Is the IPV6 address in the Remote-Addr request header set by CherryPy or the Linux IP stack? The fact that this phenomena only appeared after the switch to CherryPy 2.3 would indicate the former. Will investigate this.

Possible solutions:

  1. Add ::ffff:127.0.0.1 to self.hostlist in command.ToolboxCommand.
  2. Make identity.conditions._match_ip support IPV6 addresses (possibly by using the  IPy module?).

Changed 9 years ago by Chris Arndt

Simple patch implementing workaround solution a. from comment no. 10

comment:11 Changed 9 years ago by Chris Arndt

I nailed this down to the way CherryPy <3.0 handles the server listen socket. Apparently, CherryPy  changeset 1665 has not been backported to CherryPy 2.x, which means, that the default for socket.server_host is the empty string ('') and cherrypy.WSGIServer (which is the default HTTP server class used by CherryPy) will then  determine the IP address family to use for the server listen socket via  socket.getaddrinfo() with the host argument set to None.

This will return [(10, 1, 6, '', ('::', 7654, 0, 0)), (2, 1, 6, '', ('0.0.0.0', 7654))] on Ubuntu. Note the 10 in the first tuple, which means socket.AF_INET6. So the server listen socket will use IPV6 and the address of connecting clients as returned by server.socket.accept() and consequently stored in in cherrypy.request.headers('Remote-Addr')will be a IPV6 address.

Solution: set server.socket_hostto '0.0.0.0' in command.base.ToolboxCommand.run. Or better, provide an option for tg-admin toolbox, to set the server listen address. This will cause CherryPy to use an IPV4 socket, unless the server is IPV6 only.

For the latter reason, identity.conditions._match_ip still needs to be fixed to support IPV6 networks (see previous comment). The workaround with extending ToolboxCommand.hostlist (see first patch) will still be needed for pure IPV6 setups until then.

Changed 9 years ago by Chris Arndt

Patch implementing solution described in comment no. 11 (patch above included)

Changed 9 years ago by Chris Arndt

Minimal socket test server that emulates cherrypy.WSGIServer behaviour

comment:12 Changed 9 years ago by chrisz

Solution a. works for me, but solution b. throws errors on Win XP and OpenSUSE 10.3.

comment:13 Changed 9 years ago by Chris Arndt

What do you mean? I have not implemented solution b), i.e. support for IPV6 addresses in identity.conditions._match_ip.

The second patch is a new solution c), combining a) and setting server.socket_host to 0.0.0.0. What is the error you get?

comment:14 Changed 9 years ago by chrisz

I just tried both patches, i.e. solution c) then. Error with OpenSUSE:

Failed to add service: Invalid host name
01/May/2008:23:54:53 HTTP INFO Port 7654 not free on '0.0.0.0'
Traceback (most recent call last):
  File "/usr/local/bin/tg-admin", line 8, in <module>
  ...
  File "/usr/local/lib64/python2.5/site-packages/CherryPy-2.3.0-py2.5.egg/cherrypy/_cpserver.py", line 252, in wait_for_free_port
    raise cherrypy.NotReady("Port not free.")
cherrypy._cperror.NotReady: Port not free. 

Error with WinXP:

01/May/2008:23:55:54 HTTP INFO Port 7654 not bound on '0.0.0.0'
Exception in thread CPServer Callback Thread-1:
Traceback (most recent call last):
  File "C:\Programme\Python25\lib\threading.py", line 486, in __bootstrap_inner
    self.run()
  ...
  File "C:\Programme\Python25\lib\site-packages\cherrypy-2.3.0-py2.5.egg\cherrypy\_cpserver.py", line 162, in wait_for_http_ready
    raise cherrypy.NotReady("Port not bound.")
NotReady: Port not bound.

I will see if I can make _match_ip cope with IPV6; should not be too difficult.

comment:15 Changed 9 years ago by Chris Arndt

I tried my patch on OS X 10.4 (works) and Win 2000 (fails). The error on Windows is the same you get. The error is caused by cherrypy._cpserver.Server.check_port trying to connect to the given server.socket_host and server.socket_post. It seems that connecting to 0.0.0.0 does not work on all machines (maybe not on those without a IPV6 stack), only listening on it.

This seems like a CherryPy quirk to me. I have to investigate how CP 3 solves this.

comment:16 Changed 9 years ago by chrisz

Yes, 0.0.0.0 is an unspecified address that can be used if you want to listen on all interfaces, but of course you cannot connect to it.

I now have a working _match_ip that can deal with IPv6 addresses, plus unit test. It turned out that it was somewhat difficult, since the standard lib functions for converting IPv6 addresses is only available under Unix. Luckily some code from Twisted could be reused here.

However, I feel this auxiliary function (about 80 lines of code) does not belong in identity.conditions, but should go into a separate module, probably not even under identity. Shall I put it in turbogears.util? Any better idea?

comment:17 Changed 9 years ago by Chris Arndt

I suggest:

  • In TG 1.0 we simply add ::ffff:127.0.0.1and ::1/128 to self.hosts in ToolboxCommand (remember, critical bug-fixes only, we need to concentrate our efforts on TG 1.1).
  • In TG 1.1 we:
    • Do the same as above.
    • Add an option to tg-admin toolbox to set the interface address, but set no default.
    • Add the IPV6 address / network checking code to turbogears.util and use it in identity.conditions. The code may be useful in the validators module as well.

turbogears.util is such a mixed bag already, that we might consider turning this into a sub-package for TG 1.1.

comment:18 Changed 9 years ago by chrisz

Just to make that clear, if we compare the IP addresses with the IPv6 aware _match_ip then we don't need to add ::ffff:127.0.0.1, because it is considered equal with 127.0.0.1.

Yes, splitting util would make sense.

comment:19 Changed 9 years ago by Chris Arndt

Ok, I guessed so, but without seeing the code, I wasn't sure. Then scratch the first item from the changes for TG 1.1.

Will you prepare a patch?

comment:20 Changed 9 years ago by chrisz

Ok, I'll create a patch. Just wasn't sure whether we want to fix this in 1.0 as well, and where the code should go. I'll put it in tg.util for now. The refactoring of tg.util should become a ticket of its own. We may want to put some more of the toplevel modules in that package, not only the tg.util module itself.

Changed 9 years ago by chrisz

This patch makes the match_ip function IPv6-aware

comment:21 Changed 9 years ago by chrisz

  • Status changed from assigned to closed
  • Resolution set to fixed

Checked this in as discussed above in r4721. Reorganization of tg.util is now #1861.

Note: See TracTickets for help on using tickets.