diff --git a/packages/dashmate/templates/core/dash.conf.dot b/packages/dashmate/templates/core/dash.conf.dot index 957a589bd93..4f2cb5763b2 100644 --- a/packages/dashmate/templates/core/dash.conf.dot +++ b/packages/dashmate/templates/core/dash.conf.dot @@ -61,8 +61,9 @@ externalip={{=it.externalIp}} # Tor. The sidecar shares this container's network namespace, so its SOCKS and # control ports are on loopback. Only onion destinations use the proxy: clearnet # peers (including quorum traffic) stay direct, so `proxy=` is deliberately not -# set. Core registers an onion service through the control port and binds the -# default onion target (127.0.0.1:) for its inbound side. +# set. An explicit bind disables Core's automatic onion target, and listenonion=1 +# requires a dedicated non-wildcard onion bind. Since bind is network-only, the +# onion bind is rendered beside the network bind in the # network block below. {{? it.core.tor.enabled }} onion=127.0.0.1:9050 listenonion=1 @@ -148,5 +149,8 @@ addnode={{=seed.host}}:{{=seed.port}}{{~}} # network port={{=it.core.p2p.port}} bind=0.0.0.0 +{{? it.core.tor.enabled }} +bind=127.0.0.1=onion +{{?}} rpcbind=0.0.0.0 rpcport={{=it.core.rpc.port}} diff --git a/packages/dashmate/test/unit/config/configFile/migrateConfigFileFactory.spec.js b/packages/dashmate/test/unit/config/configFile/migrateConfigFileFactory.spec.js index 24aecb9cb57..0deaca22515 100644 --- a/packages/dashmate/test/unit/config/configFile/migrateConfigFileFactory.spec.js +++ b/packages/dashmate/test/unit/config/configFile/migrateConfigFileFactory.spec.js @@ -342,6 +342,40 @@ describe('migrateConfigFileFactory', () => { }); }); + it('should render a migrated legacy config before and after enabling Tor', async () => { + const { version } = JSON.parse(fs.readFileSync(path.join(PACKAGE_ROOT_DIR, 'package.json'), 'utf8')); + const baseConfig = container.resolve('defaultConfigs').get('base'); + const renderServiceTemplates = container.resolve('renderServiceTemplates'); + + for (const fromVersion of ['4.1.0', '4.1.1', '4.2.0-dev.1']) { + const configFileData = createConfigFile().toObject(); + configFileData.configFormatVersion = fromVersion; + const devnetConfig = new Config('devnet', baseConfig.getStoredOptions()); + devnetConfig.set('network', 'devnet'); + configFileData.configs.devnet = devnetConfig.getStoredOptions(); + for (const options of Object.values(configFileData.configs)) { + delete options.core.tor; + } + + const migrated = migrateConfigFile(configFileData, fromVersion, version); + + for (const [name, options] of Object.entries(migrated.configs)) { + const config = new Config(name, options); + const disabledConf = renderServiceTemplates(config)['core/dash.conf']; + expect(disabledConf).to.match(/^listenonion=0$/m); + expect(disabledConf.match(/^bind=.*$/gm)).to.deep.equal(['bind=0.0.0.0']); + + config.set('core.tor.enabled', true); + const enabledConf = renderServiceTemplates(config)['core/dash.conf']; + expect(enabledConf).to.match(/^listenonion=1$/m); + expect(enabledConf.match(/^bind=.*$/gm)).to.deep.equal([ + 'bind=0.0.0.0', + 'bind=127.0.0.1=onion', + ]); + } + } + }); + it('should add the quorum list reload interval to a 5.0.0-beta.3 config, short only on local networks', async () => { // Loading is what fails without it: the schema requires the option. const fromVersion = '5.0.0-beta.3'; diff --git a/packages/dashmate/test/unit/templates/torTemplates.spec.js b/packages/dashmate/test/unit/templates/torTemplates.spec.js index 9598d0a564e..fcbc4ff4958 100644 --- a/packages/dashmate/test/unit/templates/torTemplates.spec.js +++ b/packages/dashmate/test/unit/templates/torTemplates.spec.js @@ -13,15 +13,45 @@ describe('Tor templates', () => { renderServiceTemplates = renderServiceTemplatesFactory(renderTemplateFactory()); }); - it('should leave onion listening off and set no proxy when Tor is disabled', () => { - config.set('core.tor.enabled', false); + [ + ['mainnet', null], + ['testnet', '[test]'], + ['devnet', '[devnet]'], + ['local', '[regtest]'], + ].forEach(([network, section]) => { + it(`should provide the dedicated onion bind required by Core 24 on ${network}`, () => { + config.set('network', network); + config.set('core.tor.enabled', true); + // The onion target is independent of an operator's clearnet P2P port. + config.set('core.p2p.port', 21000); - const dashConf = renderServiceTemplates(config)['core/dash.conf']; + const dashConf = renderServiceTemplates(config)['core/dash.conf']; + // -bind is network-only, so non-mainnet binds must be in their network section. + const networkConf = section ? dashConf.split(section)[1] : dashConf; - expect(dashConf).to.match(/^listenonion=0$/m); - expect(dashConf).to.not.match(/^onion=/m); - expect(dashConf).to.not.match(/^torcontrol=/m); - expect(dashConf).to.not.match(/^proxy=/m); + expect(dashConf).to.match(/^listenonion=1$/m); + expect(networkConf.match(/^bind=.*$/gm)).to.deep.equal([ + 'bind=0.0.0.0', + 'bind=127.0.0.1=onion', + ]); + if (network === 'mainnet') { + const beforeOnionBind = dashConf.slice(0, dashConf.indexOf('bind=127.0.0.1=onion')); + expect(beforeOnionBind).to.not.match(/^\s*\[.*\]\s*$/m); + } + }); + + it(`should leave onion listening off and set no proxy when Tor is disabled on ${network}`, () => { + config.set('network', network); + config.set('core.tor.enabled', false); + + const dashConf = renderServiceTemplates(config)['core/dash.conf']; + + expect(dashConf).to.match(/^listenonion=0$/m); + expect(dashConf.match(/^bind=.*$/gm)).to.deep.equal(['bind=0.0.0.0']); + expect(dashConf).to.not.match(/^onion=/m); + expect(dashConf).to.not.match(/^torcontrol=/m); + expect(dashConf).to.not.match(/^proxy=/m); + }); }); it('should point Core at the sidecar on loopback by default', () => {