From 0aa401023edd1bbabe5a59eb2e5e6e751c67a278 Mon Sep 17 00:00:00 2001 From: Scott Date: Mon, 27 Jul 2026 22:31:25 -0500 Subject: [PATCH] Miner clients: turn TLS verification back on, stop shipping a lab IP Two client-side problems that put miner operators at risk rather than the node. The macOS miner disabled certificate verification on every request. A comment in the fallback path said it was falling back to direct HTTPS with TLS verification; it was not, and the comment was simply wrong. Any on-path attacker could read and rewrite attestations, including the wallet field. Verification is now on, with an explicit opt-out for the old PowerPC Macs that genuinely cannot complete a modern handshake. That is a choice the operator makes rather than a silent default shipped to everyone. The same file defaulted its proxy to http://192.168.0.160:8089, a private lab address. On anyone else's network that IP is whatever machine happens to hold it, so the miner would hand its full attestation, hardware serial, MAC addresses, hostname and wallet included, in cleartext to a stranger's host. There is no default proxy now. The Pico console bridge accepted --simulate, which fabricates hardware readings, while still posting to its configured node, and that node defaults to rustchain.org. That combination is a working fake-hardware faucet checked into the repo. Simulation now refuses to run against anything but a local node. Signed-off-by: Scott --- miners/macos/rustchain_mac_miner_v2.5.py | 17 +++++++++++++---- miners/pico_bridge/pico_bridge_miner.py | 11 +++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/miners/macos/rustchain_mac_miner_v2.5.py b/miners/macos/rustchain_mac_miner_v2.5.py index 68369000f..3a5817a5a 100644 --- a/miners/macos/rustchain_mac_miner_v2.5.py +++ b/miners/macos/rustchain_mac_miner_v2.5.py @@ -67,7 +67,16 @@ def error(msg): return msg MINER_VERSION = "2.5.0" NODE_URL = os.environ.get("RUSTCHAIN_NODE", "https://rustchain.org") -PROXY_URL = os.environ.get("RUSTCHAIN_PROXY", "http://192.168.0.160:8089") +# No default proxy. The previous default pointed at a private lab address +# (192.168.0.160), which on anyone else's LAN is whatever machine happens to +# hold that IP. The miner would hand its full attestation, including hardware +# serial, MAC addresses, hostname and wallet, in cleartext to a stranger. +PROXY_URL = os.environ.get("RUSTCHAIN_PROXY", "") + +# Verification is ON. Old PowerPC Macs genuinely cannot complete a modern +# TLS handshake, and the escape hatch for them is an explicit opt-out that +# the operator chooses, not a silent default that ships to everyone. +TLS_VERIFY = os.environ.get("RUSTCHAIN_TLS_VERIFY", "1") not in ("0", "false", "False") BLOCK_TIME = 600 # 10 minutes LOTTERY_CHECK_INTERVAL = 10 @@ -199,7 +208,7 @@ def _probe_transport(self): try: r = requests.get( self.node_url + "/health", - timeout=10, verify=False + timeout=10, verify=TLS_VERIFY ) if r.status_code == 200: print(success("[TRANSPORT] Direct HTTPS to node: OK")) @@ -237,14 +246,14 @@ def base_url(self): def get(self, path, **kwargs): """GET request through whichever transport works.""" kwargs.setdefault("timeout", 15) - kwargs.setdefault("verify", False) + kwargs.setdefault("verify", TLS_VERIFY) url = self.base_url + path return requests.get(url, **kwargs) def post(self, path, **kwargs): """POST request through whichever transport works.""" kwargs.setdefault("timeout", 15) - kwargs.setdefault("verify", False) + kwargs.setdefault("verify", TLS_VERIFY) url = self.base_url + path return requests.post(url, **kwargs) diff --git a/miners/pico_bridge/pico_bridge_miner.py b/miners/pico_bridge/pico_bridge_miner.py index 2380785d7..0d6eb34ae 100644 --- a/miners/pico_bridge/pico_bridge_miner.py +++ b/miners/pico_bridge/pico_bridge_miner.py @@ -706,6 +706,17 @@ def main(): config = load_config(args.config) # Override with CLI args + # --simulate fabricates hardware readings. Posting those to a node that + # pays real RTC is a fake-hardware faucet, so it is refused unless the + # operator has explicitly pointed the bridge at a local test node. + SIMULATE_LOCAL_ONLY = True + if args.simulate and SIMULATE_LOCAL_ONLY: + _n = config.get('node_url', '') + if not any(h in _n for h in ('localhost', '127.0.0.1', '0.0.0.0', '::1')): + print('refusing --simulate against a non-local node: ' + _n) + print('point RUSTCHAIN_NODE at a local test node to simulate') + return 2 + if args.simulate: config['simulation_mode'] = True if args.wallet: