commit 53fbe024898e96d9564ba982448f5c048acd0be0 Author: Damian Johnson atagar@torproject.org Date: Sun Jan 31 20:08:40 2016 -0800
Retrieve connection information despite DisableDebuggerAttachment
For years tor's DisableDebuggerAttachment has been the bane of nyx. The feature wasn't intended to effect us, but screws with proc permissions breaking every connection resolver we have...
https://trac.torproject.org/projects/tor/ticket/15259
Currently we read /proc/<pid>/fd to get connection inodes, then use that determine what from /proc/net/tcp belongs to our process. Tor's DisableDebuggerAttachment breaks that by making /proc/<pid>/fd only readable by root. However, even without knowing the inodes we can identify tor related connections by if they go to a relay or our ORPort/DirPort/ControlPort. This is exactly what nyx already does to identify a connection's type.
TL;DR. Connection resolution works all the time now. Only drawbacks are...
* Connection resolution can't work until we have consensus information. This can take a few seconds so we don't show connections right away.
* When resolving this way we can't show client or exit connections. Nyx already scrubbed these so it's not a big loss, but means we now don't even show that they exist.
If the user sets 'DisableDebuggerAttachment 0' in their torrc we still do connection resolution via the normal method. --- nyx/controller.py | 33 +++------------------------------ nyx/util/__init__.py | 4 ++++ nyx/util/tracker.py | 37 ++++++++++++++++++++++++++++++++++--- 3 files changed, 41 insertions(+), 33 deletions(-)
diff --git a/nyx/controller.py b/nyx/controller.py index 7ff6e53..ee647a7 100644 --- a/nyx/controller.py +++ b/nyx/controller.py @@ -115,37 +115,10 @@ def init_controller(stdscr, start_time): if CONFIG['features.panels.show.connection']: page_panels.append([nyx.connection_panel.ConnectionPanel(stdscr)])
- # The DisableDebuggerAttachment will prevent our connection panel from really - # functioning. It'll have circuits, but little else. If this is the case then - # notify the user and tell them what they can do to fix it. - controller = tor_controller() - - if controller.get_conf('DisableDebuggerAttachment', None) == '1': - log.notice("Tor is preventing system utilities like netstat and lsof from working. This means that nyx can't provide you with connection information. You can change this by adding 'DisableDebuggerAttachment 0' to your torrc and restarting tor. For more information see...\nhttps://trac.torproject.org/3313") - nyx.util.tracker.get_connection_tracker().set_paused(True) - else: - # Configures connection resoultions. This is paused/unpaused according to - # if Tor's connected or not. - - controller.add_status_listener(conn_reset_listener) - - tor_pid = controller.get_pid(None) - - if tor_pid: - # use the tor pid to help narrow connection results - tor_cmd = system.name_by_pid(tor_pid) - - if tor_cmd is None: - tor_cmd = 'tor' - - resolver = nyx.util.tracker.get_connection_tracker() - log.info('Operating System: %s, Connection Resolvers: %s' % (os.uname()[0], ', '.join(resolver._resolvers))) - else: - # constructs singleton resolver and, if tor isn't connected, initizes - # it to be paused - - nyx.util.tracker.get_connection_tracker().set_paused(not controller.is_alive()) + controller.add_status_listener(conn_reset_listener) + resolver = nyx.util.tracker.get_connection_tracker() + log.info('Operating System: %s, Connection Resolvers: %s' % (os.uname()[0], ', '.join(resolver._resolvers)))
# third page: config
diff --git a/nyx/util/__init__.py b/nyx/util/__init__.py index 0f53ab6..a7b2e8d 100644 --- a/nyx/util/__init__.py +++ b/nyx/util/__init__.py @@ -31,6 +31,10 @@ TESTING = False
stem.control.CACHEABLE_GETINFO_PARAMS = list(stem.control.CACHEABLE_GETINFO_PARAMS) + ['address']
+# disable trace level messages about cache hits + +stem.control.LOG_CACHE_FETCHES = False + try: uses_settings = stem.util.conf.uses_settings('nyx', os.path.join(BASE_DIR, 'config'), lazy_load = False) except IOError as exc: diff --git a/nyx/util/tracker.py b/nyx/util/tracker.py index ffd24c9..f218630 100644 --- a/nyx/util/tracker.py +++ b/nyx/util/tracker.py @@ -52,7 +52,7 @@ import threading
import stem.control
-from stem.util import conf, connection, proc, str_tools, system +from stem.util import conf, connection, enum, proc, str_tools, system
from nyx.util import log, tor_controller
@@ -67,6 +67,10 @@ RESOURCE_TRACKER = None PORT_USAGE_TRACKER = None CONSENSUS_TRACKER = None
+CustomResolver = enum.Enum( + ('INFERENCE', 'by inference'), +) + # Extending stem's Connection tuple with attributes for the uptime of the # connection.
@@ -476,7 +480,6 @@ class ConnectionTracker(Daemon):
self._connections = [] self._start_times = {} # connection => (unix_timestamp, is_legacy) - self._resolvers = connection.system_resolvers() self._custom_resolver = None self._is_first_run = True
@@ -486,6 +489,15 @@ class ConnectionTracker(Daemon): self._failure_count = 0 self._rate_too_low_count = 0
+ # If 'DisableDebuggerAttachment 0' is set we can do normal connection + # resolution. Otherwise connection resolution by inference is the only game + # in town. + + if tor_controller().get_conf('DisableDebuggerAttachment', None) == '0': + self._resolvers = connection.system_resolvers() + else: + self._resolvers = [CustomResolver.INFERENCE] + def _task(self, process_pid, process_name): if self._custom_resolver: resolver = self._custom_resolver @@ -500,7 +512,26 @@ class ConnectionTracker(Daemon): start_time = time.time() new_connections, new_start_times = [], {}
- for conn in connection.get_connections(resolver, process_pid = process_pid, process_name = process_name): + if resolver == CustomResolver.INFERENCE: + # provide connections going to a relay or one of our tor ports + + connections = [] + controller = tor_controller() + consensus_tracker = get_consensus_tracker() + + for conn in proc.connections(user = controller.get_user(None)): + if conn.remote_port in consensus_tracker.get_relay_fingerprints(conn.remote_address): + connections.append(conn) # outbound to another relay + elif conn.local_port in controller.get_ports(stem.control.Listener.OR, []): + connections.append(conn) # inbound to our ORPort + elif conn.local_port in controller.get_ports(stem.control.Listener.DIR, []): + connections.append(conn) # inbound to our DirPort + elif conn.local_port in controller.get_ports(stem.control.Listener.CONTROL, []): + connections.append(conn) # controller connection + else: + connections = connection.get_connections(resolver, process_pid = process_pid, process_name = process_name) + + for conn in connections: conn_start_time, is_legacy = self._start_times.get(conn, (start_time, self._is_first_run)) new_start_times[conn] = (conn_start_time, is_legacy) new_connections.append(Connection(conn_start_time, is_legacy, *conn))
tor-commits@lists.torproject.org