From bc55acd6bbf42918ad23ee37c1d10e6218407f5a Mon Sep 17 00:00:00 2001 From: Ivan Shumkov Date: Sun, 29 Sep 2024 10:42:41 +0300 Subject: [PATCH] feat(dashmate): validate external IP (#2183) --- .../doctor/analyse/analyseConfigFactory.js | 60 +++++++++++++++---- .../tasks/doctor/collectSamplesTaskFactory.js | 6 +- packages/dashmate/src/status/providers.js | 11 +++- packages/dashmate/src/status/scopes/core.js | 2 +- .../dashmate/src/status/scopes/platform.js | 4 +- 5 files changed, 63 insertions(+), 20 deletions(-) diff --git a/packages/dashmate/src/doctor/analyse/analyseConfigFactory.js b/packages/dashmate/src/doctor/analyse/analyseConfigFactory.js index 4bb65d0ffb..32c4355bde 100644 --- a/packages/dashmate/src/doctor/analyse/analyseConfigFactory.js +++ b/packages/dashmate/src/doctor/analyse/analyseConfigFactory.js @@ -160,13 +160,25 @@ and revoke the previous certificate in the ZeroSSL dashboard`, if (coreP2pPort && coreP2pPort !== 'OPEN') { const port = config.get('core.p2p.port'); const externalIp = config.get('externalIp'); - const problem = new Problem( - 'Core P2P port is unavailable for incoming connections.', - chalk`Please ensure that port ${port} on your public IP address ${externalIp} is open + + let solution = chalk`Please ensure that port ${port} on your public IP address ${externalIp} is open +for incoming connections. You may need to configure your firewall to +ensure this port is accessible from the public internet. If you are using +Network Address Translation (NAT), please enable port forwarding for port 80 +and all Dash service ports listed above.`; + if (externalIp) { + solution = chalk`Please ensure your configured IP address ${externalIp} is your public IP. +You can change it using {bold.cyanBright dashmate config set externalIp [IP]}. +Also, ensure that port ${port} on your public IP address is open for incoming connections. You may need to configure your firewall to ensure this port is accessible from the public internet. If you are using Network Address Translation (NAT), please enable port forwarding for port 80 -and all Dash service ports listed above.`, +and all Dash service ports listed above.`; + } + + const problem = new Problem( + 'Core P2P port is unavailable for incoming connections.', + solution, SEVERITY.HIGH, ); @@ -178,13 +190,25 @@ and all Dash service ports listed above.`, if (gatewayHttpPort && gatewayHttpPort !== 'OPEN') { const port = config.get('platform.gateway.listeners.dapiAndDrive.port'); const externalIp = config.get('externalIp'); - const problem = new Problem( - 'Gateway HTTP port is unavailable for incoming connections.', - chalk`Please ensure that port ${port} on your public IP address ${externalIp} is open + + let solution = chalk`Please ensure that port ${port} on your public IP address ${externalIp} is open +for incoming connections. You may need to configure your firewall to +ensure this port is accessible from the public internet. If you are using +Network Address Translation (NAT), please enable port forwarding for port 80 +and all Dash service ports listed above.`; + if (externalIp) { + solution = chalk`Please ensure your configured IP address ${externalIp} is your public IP. +You can change it using {bold.cyanBright dashmate config set externalIp [IP]}. +Also, ensure that port ${port} on your public IP address is open for incoming connections. You may need to configure your firewall to ensure this port is accessible from the public internet. If you are using Network Address Translation (NAT), please enable port forwarding for port 80 -and all Dash service ports listed above.`, +and all Dash service ports listed above.`; + } + + const problem = new Problem( + 'Gateway HTTP port is unavailable for incoming connections.', + solution, SEVERITY.HIGH, ); @@ -196,13 +220,25 @@ and all Dash service ports listed above.`, if (tenderdashP2pPort && tenderdashP2pPort !== 'OPEN') { const port = config.get('platform.drive.tenderdash.p2p.port'); const externalIp = config.get('externalIp'); - const problem = new Problem( - 'Tenderdash P2P port is unavailable for incoming connections.', - chalk`Please ensure that port ${port} on your public IP address ${externalIp} is open + + let solution = chalk`Please ensure that port ${port} on your public IP address ${externalIp} is open for incoming connections. You may need to configure your firewall to ensure this port is accessible from the public internet. If you are using Network Address Translation (NAT), please enable port forwarding for port 80 -and all Dash service ports listed above.`, +and all Dash service ports listed above.`; + if (externalIp) { + solution = chalk`Please ensure your configured IP address ${externalIp} is your public IP. +You can change it using {bold.cyanBright dashmate config set externalIp [IP]}. +Also, ensure that port ${port} on your public IP address is open +for incoming connections. You may need to configure your firewall to +ensure this port is accessible from the public internet. If you are using +Network Address Translation (NAT), please enable port forwarding for port 80 +and all Dash service ports listed above.`; + } + + const problem = new Problem( + 'Tenderdash P2P port is unavailable for incoming connections.', + solution, SEVERITY.HIGH, ); diff --git a/packages/dashmate/src/listr/tasks/doctor/collectSamplesTaskFactory.js b/packages/dashmate/src/listr/tasks/doctor/collectSamplesTaskFactory.js index 8e4ae5794a..c766b316ad 100644 --- a/packages/dashmate/src/listr/tasks/doctor/collectSamplesTaskFactory.js +++ b/packages/dashmate/src/listr/tasks/doctor/collectSamplesTaskFactory.js @@ -166,7 +166,7 @@ export default function collectSamplesTaskFactory( title: 'Core P2P port', task: async () => { const port = config.get('core.p2p.port'); - const response = await providers.mnowatch.checkPortStatus(port); + const response = await providers.mnowatch.checkPortStatus(port, config.get('externalIp')); ctx.samples.setServiceInfo('core', 'p2pPort', response); }, @@ -176,7 +176,7 @@ export default function collectSamplesTaskFactory( enabled: () => config.get('platform.enable'), task: async () => { const port = config.get('platform.gateway.listeners.dapiAndDrive.port'); - const response = await providers.mnowatch.checkPortStatus(port); + const response = await providers.mnowatch.checkPortStatus(port, config.get('externalIp')); ctx.samples.setServiceInfo('gateway', 'httpPort', response); }, @@ -185,7 +185,7 @@ export default function collectSamplesTaskFactory( title: 'Tenderdash P2P port', task: async () => { const port = config.get('platform.drive.tenderdash.p2p.port'); - const response = await providers.mnowatch.checkPortStatus(port); + const response = await providers.mnowatch.checkPortStatus(port, config.get('externalIp')); ctx.samples.setServiceInfo('drive_tenderdash', 'p2pPort', response); }, diff --git a/packages/dashmate/src/status/providers.js b/packages/dashmate/src/status/providers.js index 2eee8a72f5..46c491c920 100644 --- a/packages/dashmate/src/status/providers.js +++ b/packages/dashmate/src/status/providers.js @@ -63,7 +63,14 @@ export default { }, }, mnowatch: { - checkPortStatus: async (port) => { + /** + * Check the status of a port and optionally validate an IP address. + * + * @param {number} port - The port number to check. + * @param {string} [ip] - Optional. The IP address to validate. + * @returns {Promise} A promise that resolves to the port status. + */ + checkPortStatus: async (port, ip = undefined) => { // We use http request instead fetch function to force // using IPv4 otherwise mnwatch could try to connect to IPv6 node address // and fail (Core listens for IPv4 only) @@ -72,7 +79,7 @@ export default { const options = { hostname: 'mnowatch.org', port: 443, - path: `/${port}/`, + path: ip ? `/${port}/?validateIp=${ip}` : `/${port}/`, method: 'GET', family: 4, // Force IPv4 timeout: MAX_REQUEST_TIMEOUT, diff --git a/packages/dashmate/src/status/scopes/core.js b/packages/dashmate/src/status/scopes/core.js index d983bcd67c..c460f305de 100644 --- a/packages/dashmate/src/status/scopes/core.js +++ b/packages/dashmate/src/status/scopes/core.js @@ -116,7 +116,7 @@ export default function getCoreScopeFactory( const providersResult = await Promise.allSettled([ providers.github.release('dashpay/dash'), - providers.mnowatch.checkPortStatus(config.get('core.p2p.port')), + providers.mnowatch.checkPortStatus(config.get('core.p2p.port'), config.get('externalIp')), providers.insight(config.get('network')).status(), ]); diff --git a/packages/dashmate/src/status/scopes/platform.js b/packages/dashmate/src/status/scopes/platform.js index f18b291549..7605104ecb 100644 --- a/packages/dashmate/src/status/scopes/platform.js +++ b/packages/dashmate/src/status/scopes/platform.js @@ -89,8 +89,8 @@ export default function getPlatformScopeFactory( // Collecting platform data fails if Tenderdash is waiting for core to sync if (info.serviceStatus === ServiceStatusEnum.up) { const portStatusResult = await Promise.allSettled([ - providers.mnowatch.checkPortStatus(config.get('platform.gateway.listeners.dapiAndDrive.port')), - providers.mnowatch.checkPortStatus(config.get('platform.drive.tenderdash.p2p.port')), + providers.mnowatch.checkPortStatus(config.get('platform.gateway.listeners.dapiAndDrive.port'), config.get('externalIp')), + providers.mnowatch.checkPortStatus(config.get('platform.drive.tenderdash.p2p.port'), config.get('externalIp')), ]); const [httpPortState, p2pPortState] = portStatusResult.map((result) => (result.status === 'fulfilled' ? result.value : null));