net: require a dedicated bind for automatic Tor #36170

pull l0rinc wants to merge 4 commits into bitcoin:master from l0rinc:l0rinc/identify-tor-shared-binds changing 6 files +38 −11
  1. l0rinc commented at 9:25 PM on September 4, 2026: contributor

    Problem: When the automatic onion service has no dedicated -bind=<addr>=onion, Tor forwards incoming connections to the first normal P2P bind. The node cannot distinguish these connections from direct connections, so network classification is incorrect and Tor peers may inherit permissions granted to the Tor daemon's IP address.

    Fix: Refuse startup when -listenonion is enabled and an explicit -bind configuration lacks a dedicated onion bind, following [the recommendation in #34892](/github-metadata-backup-bitcoin-bitcoin/34892/#issuecomment-5541662236). Users should add -bind=<addr>=onion or set -listenonion=0.

    Because -listenonion is enabled by default when listening, existing configurations with only a normal -bind must also be updated. Nodes without explicit -bind options retain the default dedicated onion listener.

  2. test: give Tor tests dedicated onion binds
    Several functional tests exercise Tor behavior without relying on a normal bind shared with the automatic onion service.
    
    Configure dedicated onion binds for those scenarios. This prepares the tests for rejecting shared Tor binds.
    
    Co-authored-by: HouseOfHufflepuff <ahrens@gmail.com>
    f004c68d80
  3. test: characterize shared Tor bind startup
    Record that the node currently starts when the automatic onion service shares a normal bind, alongside successful startup with a dedicated onion bind.
    
    Co-authored-by: HouseOfHufflepuff <ahrens@gmail.com>
    0d468fb5d1
  4. init: reject shared Tor binds
    When `-bind` is specified without a dedicated `-bind=<addr>=onion`, the automatic onion service routes incoming connections to the normal bind address.
    We cannot then distinguish Tor-forwarded connections from direct connections, which makes network classification and address-based permissions unreliable.
    
    Refuse to start and direct operators to configure a dedicated onion bind or disable `-listenonion`.
    
    Co-authored-by: Vasil Dimov <vd@FreeBSD.org>
    7cac786d3a
  5. doc: note dedicated Tor bind requirement 7fb68e3927
  6. DrahtBot added the label P2P on Sep 4, 2026
  7. DrahtBot commented at 9:25 PM on September 4, 2026: contributor

    <!--e57a25ab6845829454e8d69fc972939a-->

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    <!--006a51241073e994b41acfe9ec718e94-->

    Code Coverage & Benchmarks

    For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/36170.

    <!--021abf342d371248e50ceaed478a90ca-->

    Reviews

    See the guideline and AI policy for information on the review process.

    Type Reviewers
    Concept ACK jeanpablojp
    Approach ACK winterrdog

    If your review is incorrectly listed, please copy-paste <code>&lt;!--meta-tag:bot-skip--&gt;</code> into the comment that the bot should ignore.

    <!--174a7506f384e20aa4161008e828411d-->

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #34486 (net: Reduce local network activity when networkactive=0 by willcl-ark)

    If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

    <!--5faf32d7da4f0f540f40219e4f7537a3-->

    LLM Linter (✨ experimental)

    Possible places where named args for integral literals may be used (e.g. func(x, /*named_arg=*/0) in C++, and func(x, named_arg=0) in Python):

    • assert_raises(FailedToStartError, self.start_node, 2, extra_args=...) in test/functional/feature_bind_extra.py

    <sup>2026-09-04 21:25:56</sup>

  8. jeanpablojp commented at 12:55 AM on September 6, 2026: contributor

    Concept ACK

    0.0.0.0 is the default for -bind itself, and -bind=0.0.0.0:8334=onion passes the new check while tagging nothing. addr_bind comes from GetBindAddress on the accepted socket, so what CreateNodeFromAcceptedSocket compares against m_onion_binds is the concrete address the peer reached.

    Same node, same arriving address, same -whitelist. With -bind=0.0.0.0:P=onion the inbound peer is classified by its IP network in getpeerinfo and keeps its noban. Spelling that same address out instead of the wildcard gives onion with no permissions. [::] is classified the same way, and either wildcard is what reaches Tor as the ADD_ONION target.

    Rejecting a wildcard onion bind would close that. Naming 127.0.0.1:8334=onion in the message would only make it less likely. Is the first one in scope here?

  9. in doc/release-notes-36170.md:9 in 7fb68e3927
       0 | @@ -0,0 +1,9 @@
       1 | +P2P and network changes
       2 | +-----------------------
       3 | +
       4 | +Nodes configured with `-bind` but no dedicated `-bind=<addr>=onion` now refuse
       5 | +to start when `-listenonion` is enabled, which is the default when listening.
       6 | +A shared bind cannot distinguish Tor-forwarded connections from direct connections,
       7 | +which can grant Tor peers unintended IP-based whitelist permissions.
       8 | +Users should add a dedicated onion bind to accept incoming Tor connections, or set
       9 | +`-listenonion=0` to disable automatic onion service creation.
    


    jeanpablojp commented at 12:55 AM on September 6, 2026:

    The release note covers upgraders. For someone setting Tor up from scratch, the -bind help advertises default: 127.0.0.1:8334=onion, which still holds with no explicit -bind but not once one is given, and section 2 of doc/tor.md does not mention the dedicated bind. Would a line in one of the two be worth it?

  10. in test/functional/feature_bind_extra.py:103 in 7fb68e3927
      96 | @@ -95,6 +97,14 @@ def run_test(self):
      97 |  
      98 |          self.stop_node(0)
      99 |  
     100 | +        self.log.info("Test -listenonion with a normal bind and no dedicated onion bind")
     101 | +        self.stop_node(2)
     102 | +        assert_raises(FailedToStartError, self.start_node, 2, extra_args=self.expected[2][0] + ["-listenonion=1"])
     103 | +        self.nodes[2].wait_until_stopped(expected_ret_code=1, expected_stderr=re.compile("Tor onion service cannot share"))
    


    jeanpablojp commented at 12:55 AM on September 6, 2026:

    nit: this could use the helper the file already uses further down, and then re, assert_raises and FailedToStartError drop out of the imports. It does loosen the exit status from exactly 1 to any non-zero. Ran it that way and it passes.

            self.nodes[2].assert_start_raises_init_error(
                        self.expected[2][0] + ["-listenonion=1"],
                        "Error: The Tor onion service cannot share a -bind address",
                        match=ErrorMatch.PARTIAL_REGEX)
    
  11. winterrdog commented at 7:12 AM on September 8, 2026: contributor

    Approach ACK


github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-09-09 07:56 UTC