From 18437cef25e7f62168925a204185757263eb7f60 Mon Sep 17 00:00:00 2001 From: Joshua Karp Date: Tue, 15 Feb 2022 13:20:47 +1100 Subject: [PATCH 01/15] test: NAT Traversal testing utils and tests --- shell.nix | 1 + tests/nat/endpointDependentNAT.test.ts | 268 +++++ tests/nat/endpointIndependentNAT.test.ts | 372 +++++++ tests/nat/noNAT.test.ts | 239 +++++ tests/nat/utils.ts | 1164 ++++++++++++++++++++++ 5 files changed, 2044 insertions(+) create mode 100644 tests/nat/endpointDependentNAT.test.ts create mode 100644 tests/nat/endpointIndependentNAT.test.ts create mode 100644 tests/nat/noNAT.test.ts create mode 100644 tests/nat/utils.ts diff --git a/shell.nix b/shell.nix index 961a13c8a..37475b20e 100644 --- a/shell.nix +++ b/shell.nix @@ -10,6 +10,7 @@ in utils.node2nix grpc-tools grpcurl + iptables-legacy ]; PKG_CACHE_PATH = utils.pkgCachePath; PKG_IGNORE_TAG = 1; diff --git a/tests/nat/endpointDependentNAT.test.ts b/tests/nat/endpointDependentNAT.test.ts new file mode 100644 index 000000000..113c0004a --- /dev/null +++ b/tests/nat/endpointDependentNAT.test.ts @@ -0,0 +1,268 @@ +import os from 'os'; +import path from 'path'; +import fs from 'fs'; +import Logger, { LogLevel, StreamHandler } from '@matrixai/logger'; +import * as testNatUtils from './utils'; + +describe('endpoint dependent NAT traversal', () => { + const logger = new Logger('EDM NAT test', LogLevel.WARN, [ + new StreamHandler(), + ]); + let dataDir: string; + beforeEach(async () => { + dataDir = await fs.promises.mkdtemp( + path.join(os.tmpdir(), 'polykey-test-'), + ); + }); + afterEach(async () => { + await fs.promises.rm(dataDir, { + force: true, + recursive: true, + }); + }); + test( + 'Node1 behind EDM NAT connects to Node2', + async () => { + const { + userPid, + agent1Pid, + password, + dataDir, + agent1NodePath, + agent2NodeId, + agent2Host, + agent2ProxyPort, + tearDownNAT, + } = await testNatUtils.setupNAT('edm', 'dmz', logger); + // Since node2 is not behind a NAT can directly add its details + await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'add', agent2NodeId, agent2Host, agent2ProxyPort], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + ); + const { exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'ping', agent2NodeId, '--format', 'json'], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + ); + expect(exitCode).toBe(0); + expect(JSON.parse(stdout)).toEqual({ + success: true, + message: 'Node is Active.', + }); + await tearDownNAT(); + }, + global.defaultTimeout * 2, + ); + test( + 'Node1 connects to Node2 behind EDM NAT', + async () => { + const { + userPid, + agent1Pid, + agent2Pid, + password, + dataDir, + agent1NodePath, + agent2NodePath, + agent1NodeId, + agent1Host, + agent1ProxyPort, + agent2NodeId, + agent2Host, + agent2ProxyPort, + tearDownNAT, + } = await testNatUtils.setupNAT('dmz', 'edmSimple', logger); + await testNatUtils.pkExecNs( + userPid!, + agent2Pid!, + ['nodes', 'add', agent1NodeId, agent1Host, agent1ProxyPort], + { + PK_NODE_PATH: agent2NodePath, + PK_PASSWORD: password, + }, + dataDir, + ); + await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'add', agent2NodeId, agent2Host, agent2ProxyPort], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + ); + // If we try to ping Agent 2 it will fail + let exitCode, stdout; + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'ping', agent2NodeId, '--format', 'json'], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(1); + expect(JSON.parse(stdout)).toEqual({ + success: false, + message: 'No response received', + }); + // But Agent 2 can ping Agent 1 because Agent 1 is not behind a NAT + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent2Pid!, + ['nodes', 'ping', agent1NodeId, '--format', 'json'], + { + PK_NODE_PATH: agent2NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(0); + expect(JSON.parse(stdout)).toEqual({ + success: true, + message: 'Node is Active.', + }); + // Can now ping Agent 2 (it will be expecting a response) + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'ping', agent2NodeId, '--format', 'json'], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(0); + expect(JSON.parse(stdout)).toEqual({ + success: true, + message: 'Node is Active.', + }); + await tearDownNAT(); + }, + global.defaultTimeout * 2, + ); + test( + 'Node1 behind EDM NAT cannot connect to Node2 behind EDM NAT', + async () => { + const { + userPid, + agent1Pid, + agent2Pid, + password, + dataDir, + agent1NodePath, + agent2NodePath, + agent1NodeId, + agent2NodeId, + tearDownNAT, + } = await testNatUtils.setupNATWithSeedNode( + 'edmSimple', + 'edmSimple', + logger, + ); + // Contact details are retrieved from the seed node, but cannot be used + // since port mapping changes between targets in EDM mapping + // Node 2 -> Node 1 ping should fail (Node 1 behind NAT) + let exitCode, stdout; + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent2Pid!, + ['nodes', 'ping', agent1NodeId, '--format', 'json'], + { + PK_NODE_PATH: agent2NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(1); + expect(JSON.parse(stdout)).toEqual({ + success: false, + message: 'No response received', + }); + // Node 1 -> Node 2 ping should also fail + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'ping', agent2NodeId, '--format', 'json'], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(1); + expect(JSON.parse(stdout)).toEqual({ + success: false, + message: 'No response received', + }); + await tearDownNAT(); + }, + global.defaultTimeout * 2, + ); + test( + 'Node1 behind EDM NAT cannot connect to Node2 behind EIM NAT', + async () => { + const { + userPid, + agent1Pid, + agent2Pid, + password, + dataDir, + agent1NodePath, + agent2NodePath, + agent1NodeId, + agent2NodeId, + tearDownNAT, + } = await testNatUtils.setupNATWithSeedNode('edmSimple', 'eim', logger); + // Since one of the nodes uses EDM NAT we cannot punch through + let exitCode, stdout; + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent2Pid!, + ['nodes', 'ping', agent1NodeId, '--format', 'json', '-vv'], + { + PK_NODE_PATH: agent2NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(1); + expect(JSON.parse(stdout)).toEqual({ + success: false, + message: 'No response received', + }); + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'ping', agent2NodeId, '--format', 'json', '-vv'], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(1); + expect(JSON.parse(stdout)).toEqual({ + success: false, + message: 'No response received', + }); + await tearDownNAT(); + }, + global.defaultTimeout * 2, + ); +}); diff --git a/tests/nat/endpointIndependentNAT.test.ts b/tests/nat/endpointIndependentNAT.test.ts new file mode 100644 index 000000000..357db3d17 --- /dev/null +++ b/tests/nat/endpointIndependentNAT.test.ts @@ -0,0 +1,372 @@ +import os from 'os'; +import path from 'path'; +import fs from 'fs'; +import Logger, { LogLevel, StreamHandler } from '@matrixai/logger'; +import * as testNatUtils from './utils'; + +describe('endpoint independent NAT traversal', () => { + const logger = new Logger('EIM NAT test', LogLevel.WARN, [ + new StreamHandler(), + ]); + let dataDir: string; + beforeEach(async () => { + dataDir = await fs.promises.mkdtemp( + path.join(os.tmpdir(), 'polykey-test-'), + ); + }); + afterEach(async () => { + await fs.promises.rm(dataDir, { + force: true, + recursive: true, + }); + }); + test( + 'Node1 behind EIM NAT connects to Node2', + async () => { + const { + userPid, + agent1Pid, + password, + dataDir, + agent1NodePath, + agent2NodeId, + agent2Host, + agent2ProxyPort, + tearDownNAT, + } = await testNatUtils.setupNAT('eim', 'dmz', logger); + // Since node2 is not behind a NAT can directly add its details + await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'add', agent2NodeId, agent2Host, agent2ProxyPort], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + ); + const { exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'ping', agent2NodeId, '--format', 'json'], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + ); + expect(exitCode).toBe(0); + expect(JSON.parse(stdout)).toEqual({ + success: true, + message: 'Node is Active.', + }); + await tearDownNAT(); + }, + global.defaultTimeout * 2, + ); + test( + 'Node1 connects to Node2 behind EIM NAT', + async () => { + const { + userPid, + agent1Pid, + agent2Pid, + password, + dataDir, + agent1NodePath, + agent2NodePath, + agent1NodeId, + agent1Host, + agent1ProxyPort, + agent2NodeId, + agent2Host, + agent2ProxyPort, + tearDownNAT, + } = await testNatUtils.setupNAT('dmz', 'eim', logger); + await testNatUtils.pkExecNs( + userPid!, + agent2Pid!, + ['nodes', 'add', agent1NodeId, agent1Host, agent1ProxyPort], + { + PK_NODE_PATH: agent2NodePath, + PK_PASSWORD: password, + }, + dataDir, + ); + await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'add', agent2NodeId, agent2Host, agent2ProxyPort], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + ); + // If we try to ping Agent 2 it will fail + let exitCode, stdout; + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'ping', agent2NodeId, '--format', 'json'], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(1); + expect(JSON.parse(stdout)).toEqual({ + success: false, + message: 'No response received', + }); + // But Agent 2 can ping Agent 1 because Agent 1 is not behind a NAT + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent2Pid!, + ['nodes', 'ping', agent1NodeId, '--format', 'json'], + { + PK_NODE_PATH: agent2NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(0); + expect(JSON.parse(stdout)).toEqual({ + success: true, + message: 'Node is Active.', + }); + // Can now ping Agent 2 (it will be expecting a response) + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'ping', agent2NodeId, '--format', 'json'], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(0); + expect(JSON.parse(stdout)).toEqual({ + success: true, + message: 'Node is Active.', + }); + await tearDownNAT(); + }, + global.defaultTimeout * 2, + ); + test( + 'Node1 behind EIM NAT connects to Node2 behind EIM NAT', + async () => { + const { + userPid, + agent1Pid, + agent2Pid, + password, + dataDir, + agent1NodePath, + agent2NodePath, + agent1NodeId, + agent1Host, + agent1ProxyPort, + agent2NodeId, + agent2Host, + agent2ProxyPort, + tearDownNAT, + } = await testNatUtils.setupNAT('dmz', 'eim', logger); + await testNatUtils.pkExecNs( + userPid!, + agent2Pid!, + ['nodes', 'add', agent1NodeId, agent1Host, agent1ProxyPort], + { + PK_NODE_PATH: agent2NodePath, + PK_PASSWORD: password, + }, + dataDir, + ); + await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'add', agent2NodeId, agent2Host, agent2ProxyPort], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + ); + // If we try to ping Agent 2 it will fail + let exitCode, stdout; + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'ping', agent2NodeId, '--format', 'json'], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(1); + expect(JSON.parse(stdout)).toEqual({ + success: false, + message: 'No response received', + }); + // But Agent 2 can ping Agent 1 because it's expecting a response now + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent2Pid!, + ['nodes', 'ping', agent1NodeId, '--format', 'json'], + { + PK_NODE_PATH: agent2NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(0); + expect(JSON.parse(stdout)).toEqual({ + success: true, + message: 'Node is Active.', + }); + // Can now ping Agent 2 (it will be expecting a response too) + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'ping', agent2NodeId, '--format', 'json'], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(0); + expect(JSON.parse(stdout)).toEqual({ + success: true, + message: 'Node is Active.', + }); + await tearDownNAT(); + }, + global.defaultTimeout * 2, + ); + test( + 'Node1 behind EIM NAT connects to Node2 behind EIM NAT via seed node', + async () => { + const { + userPid, + agent1Pid, + agent2Pid, + password, + dataDir, + agent1NodePath, + agent2NodePath, + agent1NodeId, + agent2NodeId, + tearDownNAT, + } = await testNatUtils.setupNATWithSeedNode('eim', 'eim', logger); + // Contact details can be retrieved from the seed node so don't need to + // add manually + // If we try to ping Agent 2 it will fail + let exitCode, stdout; + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'ping', agent2NodeId, '--format', 'json'], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(1); + expect(JSON.parse(stdout)).toEqual({ + success: false, + message: 'No response received', + }); + // But Agent 2 can ping Agent 1 now because it's expecting a response + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent2Pid!, + ['nodes', 'ping', agent1NodeId, '--format', 'json'], + { + PK_NODE_PATH: agent2NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(0); + expect(JSON.parse(stdout)).toEqual({ + success: true, + message: 'Node is Active.', + }); + // Can now ping Agent 2 (it will also be expecting a response) + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'ping', agent2NodeId, '--format', 'json'], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(0); + expect(JSON.parse(stdout)).toEqual({ + success: true, + message: 'Node is Active.', + }); + await tearDownNAT(); + }, + global.defaultTimeout * 2, + ); + test( + 'Node1 behind EIM NAT cannot connect to Node2 behind EDM NAT', + async () => { + const { + userPid, + agent1Pid, + agent2Pid, + password, + dataDir, + agent1NodePath, + agent2NodePath, + agent1NodeId, + agent2NodeId, + tearDownNAT, + } = await testNatUtils.setupNATWithSeedNode('eim', 'edmSimple', logger); + // Since one of the nodes uses EDM NAT we cannot punch through + let exitCode, stdout; + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent2Pid!, + ['nodes', 'ping', agent1NodeId, '--format', 'json', '-vv'], + { + PK_NODE_PATH: agent2NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(1); + expect(JSON.parse(stdout)).toEqual({ + success: false, + message: 'No response received', + }); + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'ping', agent2NodeId, '--format', 'json', '-vv'], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(1); + expect(JSON.parse(stdout)).toEqual({ + success: false, + message: 'No response received', + }); + await tearDownNAT(); + }, + global.defaultTimeout * 2, + ); +}); diff --git a/tests/nat/noNAT.test.ts b/tests/nat/noNAT.test.ts new file mode 100644 index 000000000..bd8a85623 --- /dev/null +++ b/tests/nat/noNAT.test.ts @@ -0,0 +1,239 @@ +import os from 'os'; +import path from 'path'; +import fs from 'fs'; +import readline from 'readline'; +import Logger, { LogLevel, StreamHandler } from '@matrixai/logger'; +import Status from '@/status/Status'; +import config from '@/config'; +import * as testNatUtils from './utils'; +import * as testBinUtils from '../bin/utils'; + +describe('no NAT', () => { + const logger = new Logger('no NAT test', LogLevel.WARN, [ + new StreamHandler(), + ]); + let dataDir: string; + beforeEach(async () => { + dataDir = await fs.promises.mkdtemp( + path.join(os.tmpdir(), 'polykey-test-'), + ); + }); + afterEach(async () => { + await fs.promises.rm(dataDir, { + force: true, + recursive: true, + }); + }); + test( + 'can create an agent in a namespace', + async () => { + const password = 'abc123'; + const usrns = testNatUtils.createUserNamespace(); + const netns = testNatUtils.createNetworkNamespace(usrns.pid!); + const agentProcess = await testNatUtils.pkSpawnNs( + usrns.pid!, + netns.pid!, + [ + 'agent', + 'start', + '--node-path', + path.join(dataDir, 'polykey'), + '--root-key-pair-bits', + '1024', + '--client-host', + '127.0.0.1', + '--proxy-host', + '127.0.0.1', + '--workers', + '0', + '--verbose', + '--format', + 'json', + ], + { + PK_PASSWORD: password, + }, + dataDir, + logger.getChild('agentProcess'), + ); + const rlOut = readline.createInterface(agentProcess.stdout!); + const stdout = await new Promise((resolve, reject) => { + rlOut.once('line', resolve); + rlOut.once('close', reject); + }); + const statusLiveData = JSON.parse(stdout); + expect(statusLiveData).toMatchObject({ + pid: agentProcess.pid, + nodeId: expect.any(String), + clientHost: expect.any(String), + clientPort: expect.any(Number), + agentHost: expect.any(String), + agentPort: expect.any(Number), + forwardHost: expect.any(String), + forwardPort: expect.any(Number), + proxyHost: expect.any(String), + proxyPort: expect.any(Number), + recoveryCode: expect.any(String), + }); + expect( + statusLiveData.recoveryCode.split(' ').length === 12 || + statusLiveData.recoveryCode.split(' ').length === 24, + ).toBe(true); + agentProcess.kill('SIGTERM'); + let exitCode, signal; + [exitCode, signal] = await testBinUtils.processExit(agentProcess); + expect(exitCode).toBe(null); + expect(signal).toBe('SIGTERM'); + // Check for graceful exit + const status = new Status({ + statusPath: path.join(dataDir, 'polykey', config.defaults.statusBase), + statusLockPath: path.join( + dataDir, + 'polykey', + config.defaults.statusLockBase, + ), + fs, + logger, + }); + const statusInfo = (await status.readStatus())!; + expect(statusInfo.status).toBe('DEAD'); + netns.kill('SIGTERM'); + [exitCode, signal] = await testBinUtils.processExit(netns); + expect(exitCode).toBe(null); + expect(signal).toBe('SIGTERM'); + usrns.kill('SIGTERM'); + [exitCode, signal] = await testBinUtils.processExit(usrns); + expect(exitCode).toBe(null); + expect(signal).toBe('SIGTERM'); + }, + global.defaultTimeout * 2, + ); + test( + 'agents in different namespaces can ping each other', + async () => { + const { + userPid, + agent1Pid, + agent2Pid, + password, + dataDir, + agent1NodePath, + agent2NodePath, + agent1NodeId, + agent1Host, + agent1ProxyPort, + agent2NodeId, + agent2Host, + agent2ProxyPort, + tearDownNAT, + } = await testNatUtils.setupNAT('dmz', 'dmz', logger); + // Since neither node is behind a NAT can directly add eachother's + // details using pk nodes add + await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'add', agent2NodeId, agent2Host, agent2ProxyPort], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + ); + await testNatUtils.pkExecNs( + userPid!, + agent2Pid!, + ['nodes', 'add', agent1NodeId, agent1Host, agent1ProxyPort], + { + PK_NODE_PATH: agent2NodePath, + PK_PASSWORD: password, + }, + dataDir, + ); + let exitCode, stdout; + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'ping', agent2NodeId, '--format', 'json', '--verbose'], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(0); + expect(JSON.parse(stdout)).toEqual({ + success: true, + message: 'Node is Active.', + }); + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent2Pid!, + ['nodes', 'ping', agent1NodeId, '--format', 'json', '--verbose'], + { + PK_NODE_PATH: agent2NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(0); + expect(JSON.parse(stdout)).toEqual({ + success: true, + message: 'Node is Active.', + }); + await tearDownNAT(); + }, + global.defaultTimeout * 2, + ); + test( + 'agents in different namespaces can ping each other via seed node', + async () => { + const { + userPid, + agent1Pid, + agent2Pid, + password, + dataDir, + agent1NodePath, + agent2NodePath, + agent1NodeId, + agent2NodeId, + tearDownNAT, + } = await testNatUtils.setupNATWithSeedNode('dmz', 'dmz', logger); + // Should be able to ping straight away using the details from the + // seed node + let exitCode, stdout; + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'ping', agent2NodeId, '--format', 'json', '--verbose'], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(0); + expect(JSON.parse(stdout)).toEqual({ + success: true, + message: 'Node is Active.', + }); + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent2Pid!, + ['nodes', 'ping', agent1NodeId, '--format', 'json', '--verbose'], + { + PK_NODE_PATH: agent2NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(0); + expect(JSON.parse(stdout)).toEqual({ + success: true, + message: 'Node is Active.', + }); + await tearDownNAT(); + }, + global.defaultTimeout * 2, + ); +}); diff --git a/tests/nat/utils.ts b/tests/nat/utils.ts new file mode 100644 index 000000000..95125adf8 --- /dev/null +++ b/tests/nat/utils.ts @@ -0,0 +1,1164 @@ +import type { ChildProcess } from 'child_process'; +import os from 'os'; +import fs from 'fs'; +import path from 'path'; +import process from 'process'; +import child_process from 'child_process'; +import readline from 'readline'; +import Logger, { LogLevel, StreamHandler } from '@matrixai/logger'; +import * as testBinUtils from '../bin/utils'; + +type NATType = 'eim' | 'edm' | 'dmz' | 'edmSimple'; + +// Constants for all util functions +// Veth pairs (ends) +const agent1Host = 'agent1'; +const agent2Host = 'agent2'; +const agent1RouterHostInt = 'router1-int'; +const agent1RouterHostExt = 'router1-ext'; +const agent2RouterHostInt = 'router2-int'; +const agent2RouterHostExt = 'router2-ext'; +const router1SeedHost = 'router1-seed'; +const router2SeedHost = 'router2-seed'; +const seedRouter1Host = 'seed-router1'; +const seedRouter2Host = 'seed-router2'; +// Subnets +const agent1HostIp = '10.0.0.2'; +const agent2HostIp = '10.0.0.2'; +const agent1RouterHostIntIp = '10.0.0.1'; +const agent2RouterHostIntIp = '10.0.0.1'; +const agent1RouterHostExtIp = '192.168.0.1'; +const agent2RouterHostExtIp = '192.168.0.2'; +const router1SeedHostIp = '192.168.0.1'; +const seedHostIp = '192.168.0.3'; +const router2SeedHostIp = '192.168.0.2'; +// Subnet mask +const subnetMask = '/24'; +// Ports +const agent1Port = '55551'; +const agent2Port = '55552'; +const mappedPort = '55555'; + +/** + * Formats the command to enter a namespace to run a process inside it + */ +const nsenter = (usrnsPid: number, netnsPid: number) => { + return `nsenter --target ${usrnsPid} --user --preserve-credentials `.concat( + `nsenter --target ${netnsPid} --net `, + ); +}; + +/** + * Create a user namespace from which network namespaces can be created without + * requiring sudo + */ +function createUserNamespace(): ChildProcess { + return child_process.spawn('unshare', ['--user', '--map-root-user'], { + shell: true, + }); +} + +/** + * Create a network namespace inside a user namespace + */ +function createNetworkNamespace(usrnsPid: number): ChildProcess { + return child_process.spawn( + 'nsenter', + [ + '--target', + usrnsPid.toString(), + '--user', + '--preserve-credentials', + 'unshare', + '--net', + ], + { shell: true }, + ); +} + +/** + * Set up four network namespaces to allow communication between two agents + * each behind a router + * Brings up loopback interfaces, creates and brings up a veth pair + * between each pair of adjacent namespaces, and adds default routing to allow + * cross-communication + */ +function setupNetworkNamespaceInterfaces( + usrnsPid: number, + agent1NetnsPid: number, + router1NetnsPid: number, + router2NetnsPid: number, + agent2NetnsPid: number, +) { + // Bring up loopback + child_process.exec(nsenter(usrnsPid, agent1NetnsPid) + `ip link set lo up`); + child_process.exec(nsenter(usrnsPid, router1NetnsPid) + `ip link set lo up`); + child_process.exec(nsenter(usrnsPid, router2NetnsPid) + `ip link set lo up`); + child_process.exec(nsenter(usrnsPid, agent2NetnsPid) + `ip link set lo up`); + // Create veth pair to link the namespaces + child_process.exec( + nsenter(usrnsPid, agent1NetnsPid) + + `ip link add ${agent1Host} type veth peer name ${agent1RouterHostInt}`, + ); + child_process.exec( + nsenter(usrnsPid, router1NetnsPid) + + `ip link add ${agent1RouterHostExt} type veth peer name ${agent2RouterHostExt}`, + ); + child_process.exec( + nsenter(usrnsPid, router2NetnsPid) + + `ip link add ${agent2RouterHostInt} type veth peer name ${agent2Host}`, + ); + // Link up the ends to the correct namespaces + child_process.exec( + nsenter(usrnsPid, agent1NetnsPid) + + `ip link set dev ${agent1RouterHostInt} netns ${router1NetnsPid}`, + ); + child_process.exec( + nsenter(usrnsPid, router1NetnsPid) + + `ip link set dev ${agent2RouterHostExt} netns ${router2NetnsPid}`, + ); + child_process.exec( + nsenter(usrnsPid, router2NetnsPid) + + `ip link set dev ${agent2Host} netns ${agent2NetnsPid}`, + ); + // Bring up each end + child_process.exec( + nsenter(usrnsPid, agent1NetnsPid) + `ip link set ${agent1Host} up`, + ); + child_process.exec( + nsenter(usrnsPid, router1NetnsPid) + + `ip link set ${agent1RouterHostInt} up`, + ); + child_process.exec( + nsenter(usrnsPid, router1NetnsPid) + + `ip link set ${agent1RouterHostExt} up`, + ); + child_process.exec( + nsenter(usrnsPid, router2NetnsPid) + + `ip link set ${agent2RouterHostExt} up`, + ); + child_process.exec( + nsenter(usrnsPid, router2NetnsPid) + + `ip link set ${agent2RouterHostInt} up`, + ); + child_process.exec( + nsenter(usrnsPid, agent2NetnsPid) + `ip link set ${agent2Host} up`, + ); + // Assign ip addresses to each end + child_process.exec( + nsenter(usrnsPid, agent1NetnsPid) + + `ip addr add ${agent1HostIp}${subnetMask} dev ${agent1Host}`, + ); + child_process.exec( + nsenter(usrnsPid, router1NetnsPid) + + `ip addr add ${agent1RouterHostIntIp}${subnetMask} dev ${agent1RouterHostInt}`, + ); + child_process.exec( + nsenter(usrnsPid, router1NetnsPid) + + `ip addr add ${agent1RouterHostExtIp}${subnetMask} dev ${agent1RouterHostExt}`, + ); + child_process.exec( + nsenter(usrnsPid, router2NetnsPid) + + `ip addr add ${agent2RouterHostExtIp}${subnetMask} dev ${agent2RouterHostExt}`, + ); + child_process.exec( + nsenter(usrnsPid, router2NetnsPid) + + `ip addr add ${agent2RouterHostIntIp}${subnetMask} dev ${agent2RouterHostInt}`, + ); + child_process.exec( + nsenter(usrnsPid, agent2NetnsPid) + + `ip addr add ${agent2HostIp}${subnetMask} dev ${agent2Host}`, + ); + // Add default routing + child_process.exec( + nsenter(usrnsPid, agent1NetnsPid) + + `ip route add default via ${agent1RouterHostIntIp}`, + ); + child_process.exec( + nsenter(usrnsPid, router1NetnsPid) + + `ip route add default via ${agent2RouterHostExtIp}`, + ); + child_process.exec( + nsenter(usrnsPid, router2NetnsPid) + + `ip route add default via ${agent1RouterHostExtIp}`, + ); + child_process.exec( + nsenter(usrnsPid, agent2NetnsPid) + + `ip route add default via ${agent2RouterHostIntIp}`, + ); +} + +/** + * Set up four network namespaces to allow communication between two agents + * each behind a router + * Brings up loopback interfaces, creates and brings up a veth pair + * between each pair of adjacent namespaces, and adds default routing to allow + * cross-communication + */ +function setupSeedNamespaceInterfaces( + usrnsPid: number, + seedNetnsPid: number, + router1NetnsPid: number, + router2NetnsPid: number, +) { + // Bring up loopback + child_process.exec(nsenter(usrnsPid, seedNetnsPid) + `ip link set lo up`); + // Create veth pairs to link the namespaces + child_process.exec( + nsenter(usrnsPid, router1NetnsPid) + + `ip link add ${router1SeedHost} type veth peer name ${seedRouter1Host}`, + ); + child_process.exec( + nsenter(usrnsPid, router2NetnsPid) + + `ip link add ${router2SeedHost} type veth peer name ${seedRouter2Host}`, + ); + // Move seed ends into seed network namespace + child_process.exec( + nsenter(usrnsPid, router1NetnsPid) + + `ip link set dev ${seedRouter1Host} netns ${seedNetnsPid}`, + ); + child_process.exec( + nsenter(usrnsPid, router2NetnsPid) + + `ip link set dev ${seedRouter2Host} netns ${seedNetnsPid}`, + ); + // Bring up each end + child_process.exec( + nsenter(usrnsPid, router1NetnsPid) + `ip link set ${router1SeedHost} up`, + ); + child_process.exec( + nsenter(usrnsPid, seedNetnsPid) + `ip link set ${seedRouter1Host} up`, + ); + child_process.exec( + nsenter(usrnsPid, seedNetnsPid) + `ip link set ${seedRouter2Host} up`, + ); + child_process.exec( + nsenter(usrnsPid, router2NetnsPid) + `ip link set ${router2SeedHost} up`, + ); + // Assign ip addresses to each end + child_process.exec( + nsenter(usrnsPid, router1NetnsPid) + + `ip addr add ${router1SeedHostIp}${subnetMask} dev ${router1SeedHost}`, + ); + child_process.exec( + nsenter(usrnsPid, seedNetnsPid) + + `ip addr add ${seedHostIp}${subnetMask} dev ${seedRouter1Host}`, + ); + child_process.exec( + nsenter(usrnsPid, seedNetnsPid) + + `ip addr add ${seedHostIp}${subnetMask} dev ${seedRouter2Host}`, + ); + child_process.exec( + nsenter(usrnsPid, router2NetnsPid) + + `ip addr add ${router2SeedHostIp}${subnetMask} dev ${router2SeedHost}`, + ); + child_process.exec( + nsenter(usrnsPid, router1NetnsPid) + + `ip route add ${seedHostIp} dev ${router1SeedHost}`, + ); + child_process.exec( + nsenter(usrnsPid, router2NetnsPid) + + `ip route add ${seedHostIp} dev ${router2SeedHost}`, + ); + // Add default routing + child_process.exec( + nsenter(usrnsPid, seedNetnsPid) + + `ip route add ${router1SeedHostIp} dev ${seedRouter1Host}`, + ); + child_process.exec( + nsenter(usrnsPid, seedNetnsPid) + + `ip route add ${router2SeedHostIp} dev ${seedRouter2Host}`, + ); +} + +/** + * Runs pk command through subprocess inside a network namespace + * This is used when a subprocess functionality needs to be used + * This is intended for terminating subprocesses + * Both stdout and stderr are the entire output including newlines + * @param env Augments env for command execution + * @param cwd Defaults to temporary directory + */ +async function pkExecNs( + usrnsPid: number, + netnsPid: number, + args: Array = [], + env: Record = {}, + cwd?: string, +): Promise<{ + exitCode: number; + stdout: string; + stderr: string; +}> { + cwd = + cwd ?? (await fs.promises.mkdtemp(path.join(os.tmpdir(), 'polykey-test-'))); + env = { + ...process.env, + ...env, + }; + // Recall that we attempt to connect to all specified seed nodes on agent start. + // Therefore, for testing purposes only, we default the seed nodes as empty + // (if not defined in the env) to ensure no attempted connections. A regular + // PolykeyAgent is expected to initially connect to the mainnet seed nodes + env['PK_SEED_NODES'] = env['PK_SEED_NODES'] ?? ''; + const tsConfigPath = path.resolve( + path.join(global.projectDir, 'tsconfig.json'), + ); + const tsConfigPathsRegisterPath = path.resolve( + path.join(global.projectDir, 'node_modules/tsconfig-paths/register'), + ); + const polykeyPath = path.resolve( + path.join(global.projectDir, 'src/bin/polykey.ts'), + ); + return new Promise((resolve, reject) => { + child_process.execFile( + 'nsenter', + [ + '--target', + usrnsPid.toString(), + '--user', + '--preserve-credentials', + 'nsenter', + '--target', + netnsPid.toString(), + '--net', + 'ts-node', + '--project', + tsConfigPath, + '--require', + tsConfigPathsRegisterPath, + '--compiler', + 'typescript-cached-transpile', + '--transpile-only', + polykeyPath, + ...args, + ], + { + env, + cwd, + windowsHide: true, + }, + (error, stdout, stderr) => { + if (error != null && error.code === undefined) { + // This can only happen when the command is killed + return reject(error); + } else { + // Success and Unsuccessful exits are valid here + return resolve({ + exitCode: error && error.code != null ? error.code : 0, + stdout, + stderr, + }); + } + }, + ); + }); +} + +/** + * Launch pk command through subprocess inside a network namespace + * This is used when a subprocess functionality needs to be used + * This is intended for non-terminating subprocesses + * @param env Augments env for command execution + * @param cwd Defaults to temporary directory + */ +async function pkSpawnNs( + usrnsPid: number, + netnsPid: number, + args: Array = [], + env: Record = {}, + cwd?: string, + logger: Logger = new Logger(pkSpawnNs.name), +): Promise { + cwd = + cwd ?? (await fs.promises.mkdtemp(path.join(os.tmpdir(), 'polykey-test-'))); + env = { + ...process.env, + ...env, + }; + // Recall that we attempt to connect to all specified seed nodes on agent start. + // Therefore, for testing purposes only, we default the seed nodes as empty + // (if not defined in the env) to ensure no attempted connections. A regular + // PolykeyAgent is expected to initially connect to the mainnet seed nodes + env['PK_SEED_NODES'] = env['PK_SEED_NODES'] ?? ''; + const tsConfigPath = path.resolve( + path.join(global.projectDir, 'tsconfig.json'), + ); + const tsConfigPathsRegisterPath = path.resolve( + path.join(global.projectDir, 'node_modules/tsconfig-paths/register'), + ); + const polykeyPath = path.resolve( + path.join(global.projectDir, 'src/bin/polykey.ts'), + ); + const subprocess = child_process.spawn( + 'nsenter', + [ + '--target', + usrnsPid.toString(), + '--user', + '--preserve-credentials', + 'nsenter', + '--target', + netnsPid.toString(), + '--net', + 'ts-node', + '--project', + tsConfigPath, + '--require', + tsConfigPathsRegisterPath, + '--compiler', + 'typescript-cached-transpile', + '--transpile-only', + polykeyPath, + ...args, + ], + { + env, + cwd, + stdio: ['pipe', 'pipe', 'pipe'], + windowsHide: true, + shell: true, + }, + ); + const rlErr = readline.createInterface(subprocess.stderr!); + rlErr.on('line', (l) => { + // The readline library will trim newlines + logger.info(l); + }); + return subprocess; +} + +/** + * Setup routing between an agent and router with no NAT rules + */ +function setupDMZ( + usrnsPid: number, + routerNsPid: number, + agentIp: string, + agentPort: string, + routerExt: string, + routerExtIp: string, +) { + const postroutingCommand = nsenter(usrnsPid, routerNsPid).concat( + 'iptables --table nat ', + '--append POSTROUTING ', + '--protocol udp ', + `--source ${agentIp}${subnetMask} `, + `--out-interface ${routerExt} `, + '--jump SNAT ', + `--to-source ${routerExtIp}:${mappedPort}`, + ); + const preroutingCommand = nsenter(usrnsPid, routerNsPid).concat( + 'iptables --table nat ', + '--append PREROUTING ', + '--protocol udp ', + `--destination-port ${mappedPort} `, + `--in-interface ${routerExt} `, + '--jump DNAT ', + `--to-destination ${agentIp}:${agentPort}`, + ); + child_process.exec(postroutingCommand); + child_process.exec(preroutingCommand); +} + +/** + * Setup Port-Restricted Cone NAT for a namespace (on the router namespace) + */ +function setupNATEndpointIndependentMapping( + usrnsPid: number, + routerNsPid: number, + agentIp: string, + routerExt: string, + routerInt: string, +) { + const natCommand = nsenter(usrnsPid, routerNsPid).concat( + 'iptables --table nat ', + '--append POSTROUTING ', + '--protocol udp ', + `--source ${agentIp}${subnetMask} `, + `--out-interface ${routerExt} `, + '--jump MASQUERADE', + ); + const acceptLocalCommand = nsenter(usrnsPid, routerNsPid).concat( + 'iptables --table filter ', + '--append INPUT ', + `--in-interface ${routerInt} `, + '--jump ACCEPT', + ); + const acceptEstablishedCommand = nsenter(usrnsPid, routerNsPid).concat( + 'iptables --table filter ', + '--append INPUT ', + `--match conntrack `, + '--cstate RELATED,ESTABLISHED ', + '--jump ACCEPT', + ); + const dropCommand = nsenter(usrnsPid, routerNsPid).concat( + 'iptables --table filter ', + '--append INPUT ', + '--jump DROP', + ); + child_process.exec(acceptLocalCommand); + child_process.exec(acceptEstablishedCommand); + child_process.exec(dropCommand); + child_process.exec(natCommand); +} + +/** + * Setup Symmetric NAT for a namespace (on the router namespace) + */ +function setupNATEndpointDependentMapping( + usrnsPid: number, + routerNsPid: number, + routerExt: string, +) { + const command = nsenter(usrnsPid, routerNsPid).concat( + 'iptables --table nat ', + '--append POSTROUTING ', + '--protocol udp ', + `--out-interface ${routerExt} `, + '--jump MASQUERADE ', + `--random`, + ); + child_process.exec(command); +} + +/** + * Setup Port-Restricted Cone NAT for a namespace (on the router namespace) + */ +function setupNATSimplifiedEDMAgent( + usrnsPid: number, + routerNsPid: number, + agentIp: string, + routerExt: string, + routerInt: string, +) { + const natCommand = nsenter(usrnsPid, routerNsPid).concat( + 'iptables --table nat ', + '--append POSTROUTING ', + '--protocol udp ', + `--source ${agentIp}${subnetMask} `, + `--out-interface ${routerExt} `, + '--jump MASQUERADE ', + '--to-ports 44444', + ); + const acceptLocalCommand = nsenter(usrnsPid, routerNsPid).concat( + 'iptables --table filter ', + '--append INPUT ', + `--in-interface ${routerInt} `, + '--jump ACCEPT', + ); + const acceptEstablishedCommand = nsenter(usrnsPid, routerNsPid).concat( + 'iptables --table filter ', + '--append INPUT ', + `--match conntrack `, + '--cstate RELATED,ESTABLISHED ', + '--jump ACCEPT', + ); + const dropCommand = nsenter(usrnsPid, routerNsPid).concat( + 'iptables --table filter ', + '--append INPUT ', + '--jump DROP', + ); + child_process.exec(acceptLocalCommand); + child_process.exec(acceptEstablishedCommand); + child_process.exec(dropCommand); + child_process.exec(natCommand); +} + +/** + * Setup Port-Restricted Cone NAT for a namespace (on the router namespace) + */ +function setupNATSimplifiedEDMSeed( + usrnsPid: number, + routerNsPid: number, + agentIp: string, + routerExt: string, +) { + const natCommand = nsenter(usrnsPid, routerNsPid).concat( + 'iptables --table nat ', + '--append POSTROUTING ', + '--protocol udp ', + `--source ${agentIp}${subnetMask} `, + `--out-interface ${routerExt} `, + '--jump MASQUERADE ', + '--to-ports 55555', + ); + child_process.exec(natCommand); +} + +async function setupNATWithSeedNode( + agent1NAT: NATType, + agent2NAT: NATType, + logger: Logger = new Logger(setupNAT.name, LogLevel.WARN, [ + new StreamHandler(), + ]), +) { + const dataDir = await fs.promises.mkdtemp( + path.join(os.tmpdir(), 'polykey-test-'), + ); + const password = 'password'; + // Create a user namespace containing five network namespaces + // Two agents, two routers, one seed node + const usrns = createUserNamespace(); + const seedNetns = createNetworkNamespace(usrns.pid!); + const agent1Netns = createNetworkNamespace(usrns.pid!); + const agent2Netns = createNetworkNamespace(usrns.pid!); + const router1Netns = createNetworkNamespace(usrns.pid!); + const router2Netns = createNetworkNamespace(usrns.pid!); + // Apply appropriate NAT rules + switch (agent1NAT) { + case 'dmz': { + setupDMZ( + usrns.pid!, + router1Netns.pid!, + agent1HostIp, + agent1Port, + agent1RouterHostExt, + agent1RouterHostExtIp, + ); + setupDMZ( + usrns.pid!, + router1Netns.pid!, + agent1HostIp, + agent1Port, + router1SeedHost, + router1SeedHostIp, + ); + break; + } + case 'eim': { + setupNATEndpointIndependentMapping( + usrns.pid!, + router1Netns.pid!, + agent1HostIp, + agent1RouterHostExt, + agent1RouterHostInt, + ); + setupNATEndpointIndependentMapping( + usrns.pid!, + router1Netns.pid!, + agent1HostIp, + router1SeedHost, + agent1RouterHostInt, + ); + break; + } + case 'edm': { + setupNATEndpointDependentMapping( + usrns.pid!, + router1Netns.pid!, + agent1RouterHostExt, + ); + setupNATEndpointDependentMapping( + usrns.pid!, + router1Netns.pid!, + router1SeedHost, + ); + break; + } + case 'edmSimple': { + setupNATSimplifiedEDMAgent( + usrns.pid!, + router1Netns.pid!, + agent1HostIp, + agent1RouterHostExt, + agent1RouterHostInt, + ); + setupNATSimplifiedEDMSeed( + usrns.pid!, + router1Netns.pid!, + agent1HostIp, + router1SeedHost, + ); + break; + } + } + switch (agent2NAT) { + case 'dmz': { + setupDMZ( + usrns.pid!, + router2Netns.pid!, + agent2HostIp, + agent2Port, + agent2RouterHostExt, + agent2RouterHostExtIp, + ); + setupDMZ( + usrns.pid!, + router2Netns.pid!, + agent2HostIp, + agent2Port, + router2SeedHost, + router2SeedHostIp, + ); + break; + } + case 'eim': { + setupNATEndpointIndependentMapping( + usrns.pid!, + router2Netns.pid!, + agent2HostIp, + agent2RouterHostExt, + agent2RouterHostInt, + ); + setupNATEndpointIndependentMapping( + usrns.pid!, + router2Netns.pid!, + agent2HostIp, + router2SeedHost, + agent2RouterHostInt, + ); + break; + } + case 'edm': { + setupNATEndpointDependentMapping( + usrns.pid!, + router2Netns.pid!, + agent2RouterHostExt, + ); + setupNATEndpointDependentMapping( + usrns.pid!, + router2Netns.pid!, + router2SeedHost, + ); + break; + } + case 'edmSimple': { + setupNATSimplifiedEDMAgent( + usrns.pid!, + router2Netns.pid!, + agent2HostIp, + agent2RouterHostExt, + agent2RouterHostInt, + ); + setupNATSimplifiedEDMSeed( + usrns.pid!, + router2Netns.pid!, + agent2HostIp, + router2SeedHost, + ); + break; + } + } + setupNetworkNamespaceInterfaces( + usrns.pid!, + agent1Netns.pid!, + router1Netns.pid!, + router2Netns.pid!, + agent2Netns.pid!, + ); + setupSeedNamespaceInterfaces( + usrns.pid!, + seedNetns.pid!, + router1Netns.pid!, + router2Netns.pid!, + ); + const seedNode = await pkSpawnNs( + usrns.pid!, + seedNetns.pid!, + [ + 'agent', + 'start', + '--node-path', + path.join(dataDir, 'seed'), + '--root-key-pair-bits', + '1024', + '--client-host', + '127.0.0.1', + '--proxy-host', + '0.0.0.0', + '--connection-timeout', + '1000', + '--workers', + '0', + '--verbose', + '--format', + 'json', + ], + { + PK_PASSWORD: password, + }, + dataDir, + logger.getChild('seed'), + ); + const rlOutSeed = readline.createInterface(seedNode.stdout!); + const stdoutSeed = await new Promise((resolve, reject) => { + rlOutSeed.once('line', resolve); + rlOutSeed.once('close', reject); + }); + const nodeIdSeed = JSON.parse(stdoutSeed).nodeId; + const proxyPortSeed = JSON.parse(stdoutSeed).proxyPort; + const agent1 = await pkSpawnNs( + usrns.pid!, + agent1Netns.pid!, + [ + 'agent', + 'start', + '--node-path', + path.join(dataDir, 'agent1'), + '--root-key-pair-bits', + '1024', + '--client-host', + '127.0.0.1', + '--proxy-host', + `${agent1HostIp}`, + '--proxy-port', + `${agent1Port}`, + '--workers', + '0', + '--connection-timeout', + '1000', + '--seed-nodes', + `${nodeIdSeed}@${seedHostIp}:${proxyPortSeed}`, + '--verbose', + '--format', + 'json', + ], + { + PK_PASSWORD: password, + }, + dataDir, + logger.getChild('agent1'), + ); + const rlOutNode1 = readline.createInterface(agent1.stdout!); + const stdoutNode1 = await new Promise((resolve, reject) => { + rlOutNode1.once('line', resolve); + rlOutNode1.once('close', reject); + }); + const nodeId1 = JSON.parse(stdoutNode1).nodeId; + const agent2 = await pkSpawnNs( + usrns.pid!, + agent2Netns.pid!, + [ + 'agent', + 'start', + '--node-path', + path.join(dataDir, 'agent2'), + '--root-key-pair-bits', + '1024', + '--client-host', + '127.0.0.1', + '--proxy-host', + `${agent2HostIp}`, + '--proxy-port', + `${agent2Port}`, + '--workers', + '0', + '--connection-timeout', + '1000', + '--seed-nodes', + `${nodeIdSeed}@${seedHostIp}:${proxyPortSeed}`, + '--verbose', + '--format', + 'json', + ], + { + PK_PASSWORD: password, + }, + dataDir, + logger.getChild('agent2'), + ); + const rlOutNode2 = readline.createInterface(agent2.stdout!); + const stdoutNode2 = await new Promise((resolve, reject) => { + rlOutNode2.once('line', resolve); + rlOutNode2.once('close', reject); + }); + const nodeId2 = JSON.parse(stdoutNode2).nodeId; + // Until nodes add the information of nodes that connect to them must + // do it manually + const agent1ProxyPort = + agent1NAT === 'dmz' || agent1NAT === 'edmSimple' ? mappedPort : agent1Port; + const agent2ProxyPort = + agent2NAT === 'dmz' || agent2NAT === 'edmSimple' ? mappedPort : agent2Port; + await pkExecNs( + usrns.pid!, + seedNode.pid!, + ['nodes', 'add', nodeId1, agent1RouterHostExtIp, agent1ProxyPort], + { + PK_NODE_PATH: path.join(dataDir, 'seed'), + PK_PASSWORD: password, + }, + dataDir, + ); + await pkExecNs( + usrns.pid!, + seedNode.pid!, + ['nodes', 'add', nodeId2, agent2RouterHostExtIp, agent2ProxyPort], + { + PK_NODE_PATH: path.join(dataDir, 'seed'), + PK_PASSWORD: password, + }, + dataDir, + ); + return { + userPid: usrns.pid, + agent1Pid: agent1Netns.pid, + agent2Pid: agent2Netns.pid, + password, + dataDir, + agent1NodePath: path.join(dataDir, 'agent1'), + agent2NodePath: path.join(dataDir, 'agent2'), + agent1NodeId: nodeId1, + agent2NodeId: nodeId2, + tearDownNAT: async () => { + agent2.kill('SIGTERM'); + await testBinUtils.processExit(agent2); + agent1.kill('SIGTERM'); + await testBinUtils.processExit(agent1); + seedNode.kill('SIGTERM'); + await testBinUtils.processExit(seedNode); + router2Netns.kill('SIGTERM'); + await testBinUtils.processExit(router2Netns); + router1Netns.kill('SIGTERM'); + await testBinUtils.processExit(router1Netns); + agent2Netns.kill('SIGTERM'); + await testBinUtils.processExit(agent2Netns); + agent1Netns.kill('SIGTERM'); + await testBinUtils.processExit(agent1Netns); + seedNetns.kill('SIGTERM'); + await testBinUtils.processExit(seedNetns); + usrns.kill('SIGTERM'); + await testBinUtils.processExit(usrns); + await fs.promises.rm(dataDir, { + force: true, + recursive: true, + }); + }, + }; +} + +async function setupNAT( + agent1NAT: NATType, + agent2NAT: NATType, + logger: Logger = new Logger(setupNAT.name, LogLevel.WARN, [ + new StreamHandler(), + ]), +) { + const dataDir = await fs.promises.mkdtemp( + path.join(os.tmpdir(), 'polykey-test-'), + ); + const password = 'password'; + // Create a user namespace containing four network namespaces + // Two agents and two routers + const usrns = createUserNamespace(); + const agent1Netns = createNetworkNamespace(usrns.pid!); + const agent2Netns = createNetworkNamespace(usrns.pid!); + const router1Netns = createNetworkNamespace(usrns.pid!); + const router2Netns = createNetworkNamespace(usrns.pid!); + // Apply appropriate NAT rules + switch (agent1NAT) { + case 'dmz': { + setupDMZ( + usrns.pid!, + router1Netns.pid!, + agent1HostIp, + agent1Port, + agent1RouterHostExt, + agent1RouterHostExtIp, + ); + break; + } + case 'eim': { + setupNATEndpointIndependentMapping( + usrns.pid!, + router1Netns.pid!, + agent1HostIp, + agent1RouterHostExt, + agent1RouterHostInt, + ); + break; + } + case 'edm': { + setupNATEndpointDependentMapping( + usrns.pid!, + router1Netns.pid!, + agent1RouterHostExt, + ); + break; + } + case 'edmSimple': { + setupNATSimplifiedEDMAgent( + usrns.pid!, + router1Netns.pid!, + agent1HostIp, + agent1RouterHostExt, + agent1RouterHostInt, + ); + break; + } + } + switch (agent2NAT) { + case 'dmz': { + setupDMZ( + usrns.pid!, + router2Netns.pid!, + agent2HostIp, + agent2Port, + agent2RouterHostExt, + agent2RouterHostExtIp, + ); + break; + } + case 'eim': { + setupNATEndpointIndependentMapping( + usrns.pid!, + router2Netns.pid!, + agent2HostIp, + agent2RouterHostExt, + agent2RouterHostInt, + ); + break; + } + case 'edm': { + setupNATEndpointDependentMapping( + usrns.pid!, + router2Netns.pid!, + agent2RouterHostExt, + ); + break; + } + case 'edmSimple': { + setupNATSimplifiedEDMAgent( + usrns.pid!, + router2Netns.pid!, + agent2HostIp, + agent2RouterHostExt, + agent2RouterHostInt, + ); + break; + } + } + setupNetworkNamespaceInterfaces( + usrns.pid!, + agent1Netns.pid!, + router1Netns.pid!, + router2Netns.pid!, + agent2Netns.pid!, + ); + const agent1 = await pkSpawnNs( + usrns.pid!, + agent1Netns.pid!, + [ + 'agent', + 'start', + '--node-path', + path.join(dataDir, 'agent1'), + '--root-key-pair-bits', + '1024', + '--client-host', + '127.0.0.1', + '--proxy-host', + `${agent1HostIp}`, + '--proxy-port', + `${agent1Port}`, + '--connection-timeout', + '1000', + '--workers', + '0', + '--verbose', + '--format', + 'json', + ], + { + PK_PASSWORD: password, + }, + dataDir, + logger.getChild('agent1'), + ); + const rlOutNode1 = readline.createInterface(agent1.stdout!); + const stdoutNode1 = await new Promise((resolve, reject) => { + rlOutNode1.once('line', resolve); + rlOutNode1.once('close', reject); + }); + const nodeId1 = JSON.parse(stdoutNode1).nodeId; + const agent2 = await pkSpawnNs( + usrns.pid!, + agent2Netns.pid!, + [ + 'agent', + 'start', + '--node-path', + path.join(dataDir, 'agent2'), + '--root-key-pair-bits', + '1024', + '--client-host', + '127.0.0.1', + '--proxy-host', + `${agent2HostIp}`, + '--proxy-port', + `${agent2Port}`, + '--connection-timeout', + '1000', + '--workers', + '0', + '--verbose', + '--format', + 'json', + ], + { + PK_PASSWORD: password, + }, + dataDir, + logger.getChild('agent2'), + ); + const rlOutNode2 = readline.createInterface(agent2.stdout!); + const stdoutNode2 = await new Promise((resolve, reject) => { + rlOutNode2.once('line', resolve); + rlOutNode2.once('close', reject); + }); + const nodeId2 = JSON.parse(stdoutNode2).nodeId; + return { + userPid: usrns.pid, + agent1Pid: agent1Netns.pid, + agent2Pid: agent2Netns.pid, + password, + dataDir, + agent1NodePath: path.join(dataDir, 'agent1'), + agent2NodePath: path.join(dataDir, 'agent2'), + agent1NodeId: nodeId1, + agent1Host: agent1RouterHostExtIp, + agent1ProxyPort: + agent1NAT === 'dmz' + ? mappedPort + : agent1NAT === 'edmSimple' + ? '44444' + : agent1Port, + agent2NodeId: nodeId2, + agent2Host: agent2RouterHostExtIp, + agent2ProxyPort: + agent2NAT === 'dmz' + ? mappedPort + : agent2NAT === 'edmSimple' + ? '44444' + : agent2Port, + tearDownNAT: async () => { + agent2.kill('SIGTERM'); + await testBinUtils.processExit(agent2); + agent1.kill('SIGTERM'); + await testBinUtils.processExit(agent1); + router2Netns.kill('SIGTERM'); + await testBinUtils.processExit(router2Netns); + router1Netns.kill('SIGTERM'); + await testBinUtils.processExit(router1Netns); + agent2Netns.kill('SIGTERM'); + await testBinUtils.processExit(agent2Netns); + agent1Netns.kill('SIGTERM'); + await testBinUtils.processExit(agent1Netns); + usrns.kill('SIGTERM'); + await testBinUtils.processExit(usrns); + await fs.promises.rm(dataDir, { + force: true, + recursive: true, + }); + }, + }; +} + +export { + createUserNamespace, + createNetworkNamespace, + setupNetworkNamespaceInterfaces, + pkExecNs, + pkSpawnNs, + setupNAT, + setupNATWithSeedNode, +}; From 2110634eb0a2a033c1766a93e46db55ad732036e Mon Sep 17 00:00:00 2001 From: Emma Casolin Date: Thu, 9 Jun 2022 15:17:58 +1000 Subject: [PATCH 02/15] test: `describeIf` and `testIf` utilities for conditional testing --- package-lock.json | 1 + package.json | 1 + shell.nix | 1 - tests/bin/utils.ts | 30 + tests/nat/endpointDependentNAT.test.ts | 531 +++++----- tests/nat/endpointIndependentNAT.test.ts | 739 +++++++------- tests/nat/noNAT.test.ts | 463 ++++----- tests/nat/utils.ts | 1170 ++++++++++++++++------ tests/utils.ts | 18 + 9 files changed, 1772 insertions(+), 1182 deletions(-) diff --git a/package-lock.json b/package-lock.json index acdf5c67a..56ae4cab3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -76,6 +76,7 @@ "node-gyp-build": "^4.4.0", "pkg": "5.6.0", "prettier": "^2.6.2", + "shelljs": "^0.8.5", "shx": "^0.3.4", "ts-jest": "^27.0.5", "ts-node": "10.7.0", diff --git a/package.json b/package.json index acffa9aa7..2fdc8cbff 100644 --- a/package.json +++ b/package.json @@ -138,6 +138,7 @@ "node-gyp-build": "^4.4.0", "pkg": "5.6.0", "prettier": "^2.6.2", + "shelljs": "^0.8.5", "shx": "^0.3.4", "ts-jest": "^27.0.5", "ts-node": "10.7.0", diff --git a/shell.nix b/shell.nix index 37475b20e..961a13c8a 100644 --- a/shell.nix +++ b/shell.nix @@ -10,7 +10,6 @@ in utils.node2nix grpc-tools grpcurl - iptables-legacy ]; PKG_CACHE_PATH = utils.pkgCachePath; PKG_IGNORE_TAG = 1; diff --git a/tests/bin/utils.ts b/tests/bin/utils.ts index b0acefed2..c6cc42c54 100644 --- a/tests/bin/utils.ts +++ b/tests/bin/utils.ts @@ -12,6 +12,35 @@ import nexpect from 'nexpect'; import Logger from '@matrixai/logger'; import main from '@/bin/polykey'; +/** + * Wrapper for execFile to make it asynchronous and non-blocking + */ +async function exec( + command: string, + args: Array = [], +): Promise<{ + stdout: string; + stderr: string; +}> { + return new Promise((resolve, reject) => { + child_process.execFile( + command, + args, + { windowsHide: true }, + (error, stdout, stderr) => { + if (error) { + reject(error); + } else { + return resolve({ + stdout, + stderr, + }); + } + }, + ); + }); +} + /** * Runs pk command functionally */ @@ -361,6 +390,7 @@ function expectProcessError( } export { + exec, pk, pkStdio, pkExec, diff --git a/tests/nat/endpointDependentNAT.test.ts b/tests/nat/endpointDependentNAT.test.ts index 113c0004a..4d8cfcec7 100644 --- a/tests/nat/endpointDependentNAT.test.ts +++ b/tests/nat/endpointDependentNAT.test.ts @@ -1,268 +1,279 @@ import os from 'os'; import path from 'path'; import fs from 'fs'; +import process from 'process'; +import shell from 'shelljs'; import Logger, { LogLevel, StreamHandler } from '@matrixai/logger'; import * as testNatUtils from './utils'; +import { describeIf } from '../utils'; -describe('endpoint dependent NAT traversal', () => { - const logger = new Logger('EDM NAT test', LogLevel.WARN, [ - new StreamHandler(), - ]); - let dataDir: string; - beforeEach(async () => { - dataDir = await fs.promises.mkdtemp( - path.join(os.tmpdir(), 'polykey-test-'), - ); - }); - afterEach(async () => { - await fs.promises.rm(dataDir, { - force: true, - recursive: true, - }); - }); - test( - 'Node1 behind EDM NAT connects to Node2', - async () => { - const { - userPid, - agent1Pid, - password, - dataDir, - agent1NodePath, - agent2NodeId, - agent2Host, - agent2ProxyPort, - tearDownNAT, - } = await testNatUtils.setupNAT('edm', 'dmz', logger); - // Since node2 is not behind a NAT can directly add its details - await testNatUtils.pkExecNs( - userPid!, - agent1Pid!, - ['nodes', 'add', agent2NodeId, agent2Host, agent2ProxyPort], - { - PK_NODE_PATH: agent1NodePath, - PK_PASSWORD: password, - }, - dataDir, - ); - const { exitCode, stdout } = await testNatUtils.pkExecNs( - userPid!, - agent1Pid!, - ['nodes', 'ping', agent2NodeId, '--format', 'json'], - { - PK_NODE_PATH: agent1NodePath, - PK_PASSWORD: password, - }, - dataDir, - ); - expect(exitCode).toBe(0); - expect(JSON.parse(stdout)).toEqual({ - success: true, - message: 'Node is Active.', - }); - await tearDownNAT(); - }, - global.defaultTimeout * 2, - ); - test( - 'Node1 connects to Node2 behind EDM NAT', - async () => { - const { - userPid, - agent1Pid, - agent2Pid, - password, - dataDir, - agent1NodePath, - agent2NodePath, - agent1NodeId, - agent1Host, - agent1ProxyPort, - agent2NodeId, - agent2Host, - agent2ProxyPort, - tearDownNAT, - } = await testNatUtils.setupNAT('dmz', 'edmSimple', logger); - await testNatUtils.pkExecNs( - userPid!, - agent2Pid!, - ['nodes', 'add', agent1NodeId, agent1Host, agent1ProxyPort], - { - PK_NODE_PATH: agent2NodePath, - PK_PASSWORD: password, - }, - dataDir, - ); - await testNatUtils.pkExecNs( - userPid!, - agent1Pid!, - ['nodes', 'add', agent2NodeId, agent2Host, agent2ProxyPort], - { - PK_NODE_PATH: agent1NodePath, - PK_PASSWORD: password, - }, - dataDir, - ); - // If we try to ping Agent 2 it will fail - let exitCode, stdout; - ({ exitCode, stdout } = await testNatUtils.pkExecNs( - userPid!, - agent1Pid!, - ['nodes', 'ping', agent2NodeId, '--format', 'json'], - { - PK_NODE_PATH: agent1NodePath, - PK_PASSWORD: password, - }, - dataDir, - )); - expect(exitCode).toBe(1); - expect(JSON.parse(stdout)).toEqual({ - success: false, - message: 'No response received', - }); - // But Agent 2 can ping Agent 1 because Agent 1 is not behind a NAT - ({ exitCode, stdout } = await testNatUtils.pkExecNs( - userPid!, - agent2Pid!, - ['nodes', 'ping', agent1NodeId, '--format', 'json'], - { - PK_NODE_PATH: agent2NodePath, - PK_PASSWORD: password, - }, - dataDir, - )); - expect(exitCode).toBe(0); - expect(JSON.parse(stdout)).toEqual({ - success: true, - message: 'Node is Active.', - }); - // Can now ping Agent 2 (it will be expecting a response) - ({ exitCode, stdout } = await testNatUtils.pkExecNs( - userPid!, - agent1Pid!, - ['nodes', 'ping', agent2NodeId, '--format', 'json'], - { - PK_NODE_PATH: agent1NodePath, - PK_PASSWORD: password, - }, - dataDir, - )); - expect(exitCode).toBe(0); - expect(JSON.parse(stdout)).toEqual({ - success: true, - message: 'Node is Active.', - }); - await tearDownNAT(); - }, - global.defaultTimeout * 2, - ); - test( - 'Node1 behind EDM NAT cannot connect to Node2 behind EDM NAT', - async () => { - const { - userPid, - agent1Pid, - agent2Pid, - password, - dataDir, - agent1NodePath, - agent2NodePath, - agent1NodeId, - agent2NodeId, - tearDownNAT, - } = await testNatUtils.setupNATWithSeedNode( - 'edmSimple', - 'edmSimple', - logger, +describeIf( + process.platform === 'linux' && + shell.which('ip') && + shell.which('iptables') && + shell.which('nsenter') && + shell.which('unshare'), + 'endpoint dependent NAT traversal', + () => { + const logger = new Logger('EDM NAT test', LogLevel.WARN, [ + new StreamHandler(), + ]); + let dataDir: string; + beforeEach(async () => { + dataDir = await fs.promises.mkdtemp( + path.join(os.tmpdir(), 'polykey-test-'), ); - // Contact details are retrieved from the seed node, but cannot be used - // since port mapping changes between targets in EDM mapping - // Node 2 -> Node 1 ping should fail (Node 1 behind NAT) - let exitCode, stdout; - ({ exitCode, stdout } = await testNatUtils.pkExecNs( - userPid!, - agent2Pid!, - ['nodes', 'ping', agent1NodeId, '--format', 'json'], - { - PK_NODE_PATH: agent2NodePath, - PK_PASSWORD: password, - }, - dataDir, - )); - expect(exitCode).toBe(1); - expect(JSON.parse(stdout)).toEqual({ - success: false, - message: 'No response received', - }); - // Node 1 -> Node 2 ping should also fail - ({ exitCode, stdout } = await testNatUtils.pkExecNs( - userPid!, - agent1Pid!, - ['nodes', 'ping', agent2NodeId, '--format', 'json'], - { - PK_NODE_PATH: agent1NodePath, - PK_PASSWORD: password, - }, - dataDir, - )); - expect(exitCode).toBe(1); - expect(JSON.parse(stdout)).toEqual({ - success: false, - message: 'No response received', - }); - await tearDownNAT(); - }, - global.defaultTimeout * 2, - ); - test( - 'Node1 behind EDM NAT cannot connect to Node2 behind EIM NAT', - async () => { - const { - userPid, - agent1Pid, - agent2Pid, - password, - dataDir, - agent1NodePath, - agent2NodePath, - agent1NodeId, - agent2NodeId, - tearDownNAT, - } = await testNatUtils.setupNATWithSeedNode('edmSimple', 'eim', logger); - // Since one of the nodes uses EDM NAT we cannot punch through - let exitCode, stdout; - ({ exitCode, stdout } = await testNatUtils.pkExecNs( - userPid!, - agent2Pid!, - ['nodes', 'ping', agent1NodeId, '--format', 'json', '-vv'], - { - PK_NODE_PATH: agent2NodePath, - PK_PASSWORD: password, - }, - dataDir, - )); - expect(exitCode).toBe(1); - expect(JSON.parse(stdout)).toEqual({ - success: false, - message: 'No response received', - }); - ({ exitCode, stdout } = await testNatUtils.pkExecNs( - userPid!, - agent1Pid!, - ['nodes', 'ping', agent2NodeId, '--format', 'json', '-vv'], - { - PK_NODE_PATH: agent1NodePath, - PK_PASSWORD: password, - }, - dataDir, - )); - expect(exitCode).toBe(1); - expect(JSON.parse(stdout)).toEqual({ - success: false, - message: 'No response received', + }); + afterEach(async () => { + await fs.promises.rm(dataDir, { + force: true, + recursive: true, }); - await tearDownNAT(); - }, - global.defaultTimeout * 2, - ); -}); + }); + test( + 'Node1 behind EDM NAT connects to Node2', + async () => { + const { + userPid, + agent1Pid, + password, + dataDir, + agent1NodePath, + agent2NodeId, + agent2Host, + agent2ProxyPort, + tearDownNAT, + } = await testNatUtils.setupNAT('edm', 'dmz', logger); + // Since node2 is not behind a NAT can directly add its details + await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'add', agent2NodeId, agent2Host, agent2ProxyPort], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + ); + const { exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'ping', agent2NodeId, '--format', 'json'], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + ); + expect(exitCode).toBe(0); + expect(JSON.parse(stdout)).toEqual({ + success: true, + message: 'Node is Active.', + }); + await tearDownNAT(); + }, + global.defaultTimeout * 2, + ); + test( + 'Node1 connects to Node2 behind EDM NAT', + async () => { + const { + userPid, + agent1Pid, + agent2Pid, + password, + dataDir, + agent1NodePath, + agent2NodePath, + agent1NodeId, + agent1Host, + agent1ProxyPort, + agent2NodeId, + agent2Host, + agent2ProxyPort, + tearDownNAT, + } = await testNatUtils.setupNAT('dmz', 'edmSimple', logger); + await testNatUtils.pkExecNs( + userPid!, + agent2Pid!, + ['nodes', 'add', agent1NodeId, agent1Host, agent1ProxyPort], + { + PK_NODE_PATH: agent2NodePath, + PK_PASSWORD: password, + }, + dataDir, + ); + await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'add', agent2NodeId, agent2Host, agent2ProxyPort], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + ); + // If we try to ping Agent 2 it will fail + let exitCode, stdout; + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'ping', agent2NodeId, '--format', 'json'], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(1); + expect(JSON.parse(stdout)).toEqual({ + success: false, + message: 'No response received', + }); + // But Agent 2 can ping Agent 1 because Agent 1 is not behind a NAT + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent2Pid!, + ['nodes', 'ping', agent1NodeId, '--format', 'json'], + { + PK_NODE_PATH: agent2NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(0); + expect(JSON.parse(stdout)).toEqual({ + success: true, + message: 'Node is Active.', + }); + // Can now ping Agent 2 (it will be expecting a response) + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'ping', agent2NodeId, '--format', 'json'], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(0); + expect(JSON.parse(stdout)).toEqual({ + success: true, + message: 'Node is Active.', + }); + await tearDownNAT(); + }, + global.defaultTimeout * 2, + ); + test( + 'Node1 behind EDM NAT cannot connect to Node2 behind EDM NAT', + async () => { + const { + userPid, + agent1Pid, + agent2Pid, + password, + dataDir, + agent1NodePath, + agent2NodePath, + agent1NodeId, + agent2NodeId, + tearDownNAT, + } = await testNatUtils.setupNATWithSeedNode( + 'edmSimple', + 'edmSimple', + logger, + ); + // Contact details are retrieved from the seed node, but cannot be used + // since port mapping changes between targets in EDM mapping + // Node 2 -> Node 1 ping should fail (Node 1 behind NAT) + let exitCode, stdout; + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent2Pid!, + ['nodes', 'ping', agent1NodeId, '--format', 'json'], + { + PK_NODE_PATH: agent2NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(1); + expect(JSON.parse(stdout)).toEqual({ + success: false, + message: 'No response received', + }); + // Node 1 -> Node 2 ping should also fail + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'ping', agent2NodeId, '--format', 'json'], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(1); + expect(JSON.parse(stdout)).toEqual({ + success: false, + message: 'No response received', + }); + await tearDownNAT(); + }, + global.defaultTimeout * 2, + ); + test( + 'Node1 behind EDM NAT cannot connect to Node2 behind EIM NAT', + async () => { + const { + userPid, + agent1Pid, + agent2Pid, + password, + dataDir, + agent1NodePath, + agent2NodePath, + agent1NodeId, + agent2NodeId, + tearDownNAT, + } = await testNatUtils.setupNATWithSeedNode('edmSimple', 'eim', logger); + // Since one of the nodes uses EDM NAT we cannot punch through + let exitCode, stdout; + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent2Pid!, + ['nodes', 'ping', agent1NodeId, '--format', 'json', '-vv'], + { + PK_NODE_PATH: agent2NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(1); + expect(JSON.parse(stdout)).toEqual({ + success: false, + message: 'No response received', + }); + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'ping', agent2NodeId, '--format', 'json', '-vv'], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(1); + expect(JSON.parse(stdout)).toEqual({ + success: false, + message: 'No response received', + }); + await tearDownNAT(); + }, + global.defaultTimeout * 2, + ); + }, +); diff --git a/tests/nat/endpointIndependentNAT.test.ts b/tests/nat/endpointIndependentNAT.test.ts index 357db3d17..ff44117ff 100644 --- a/tests/nat/endpointIndependentNAT.test.ts +++ b/tests/nat/endpointIndependentNAT.test.ts @@ -1,372 +1,383 @@ import os from 'os'; import path from 'path'; import fs from 'fs'; +import process from 'process'; +import shell from 'shelljs'; import Logger, { LogLevel, StreamHandler } from '@matrixai/logger'; import * as testNatUtils from './utils'; +import { describeIf } from '../utils'; -describe('endpoint independent NAT traversal', () => { - const logger = new Logger('EIM NAT test', LogLevel.WARN, [ - new StreamHandler(), - ]); - let dataDir: string; - beforeEach(async () => { - dataDir = await fs.promises.mkdtemp( - path.join(os.tmpdir(), 'polykey-test-'), - ); - }); - afterEach(async () => { - await fs.promises.rm(dataDir, { - force: true, - recursive: true, - }); - }); - test( - 'Node1 behind EIM NAT connects to Node2', - async () => { - const { - userPid, - agent1Pid, - password, - dataDir, - agent1NodePath, - agent2NodeId, - agent2Host, - agent2ProxyPort, - tearDownNAT, - } = await testNatUtils.setupNAT('eim', 'dmz', logger); - // Since node2 is not behind a NAT can directly add its details - await testNatUtils.pkExecNs( - userPid!, - agent1Pid!, - ['nodes', 'add', agent2NodeId, agent2Host, agent2ProxyPort], - { - PK_NODE_PATH: agent1NodePath, - PK_PASSWORD: password, - }, - dataDir, +describeIf( + process.platform === 'linux' && + shell.which('ip') && + shell.which('iptables') && + shell.which('nsenter') && + shell.which('unshare'), + 'endpoint independent NAT traversal', + () => { + const logger = new Logger('EIM NAT test', LogLevel.WARN, [ + new StreamHandler(), + ]); + let dataDir: string; + beforeEach(async () => { + dataDir = await fs.promises.mkdtemp( + path.join(os.tmpdir(), 'polykey-test-'), ); - const { exitCode, stdout } = await testNatUtils.pkExecNs( - userPid!, - agent1Pid!, - ['nodes', 'ping', agent2NodeId, '--format', 'json'], - { - PK_NODE_PATH: agent1NodePath, - PK_PASSWORD: password, - }, - dataDir, - ); - expect(exitCode).toBe(0); - expect(JSON.parse(stdout)).toEqual({ - success: true, - message: 'Node is Active.', - }); - await tearDownNAT(); - }, - global.defaultTimeout * 2, - ); - test( - 'Node1 connects to Node2 behind EIM NAT', - async () => { - const { - userPid, - agent1Pid, - agent2Pid, - password, - dataDir, - agent1NodePath, - agent2NodePath, - agent1NodeId, - agent1Host, - agent1ProxyPort, - agent2NodeId, - agent2Host, - agent2ProxyPort, - tearDownNAT, - } = await testNatUtils.setupNAT('dmz', 'eim', logger); - await testNatUtils.pkExecNs( - userPid!, - agent2Pid!, - ['nodes', 'add', agent1NodeId, agent1Host, agent1ProxyPort], - { - PK_NODE_PATH: agent2NodePath, - PK_PASSWORD: password, - }, - dataDir, - ); - await testNatUtils.pkExecNs( - userPid!, - agent1Pid!, - ['nodes', 'add', agent2NodeId, agent2Host, agent2ProxyPort], - { - PK_NODE_PATH: agent1NodePath, - PK_PASSWORD: password, - }, - dataDir, - ); - // If we try to ping Agent 2 it will fail - let exitCode, stdout; - ({ exitCode, stdout } = await testNatUtils.pkExecNs( - userPid!, - agent1Pid!, - ['nodes', 'ping', agent2NodeId, '--format', 'json'], - { - PK_NODE_PATH: agent1NodePath, - PK_PASSWORD: password, - }, - dataDir, - )); - expect(exitCode).toBe(1); - expect(JSON.parse(stdout)).toEqual({ - success: false, - message: 'No response received', - }); - // But Agent 2 can ping Agent 1 because Agent 1 is not behind a NAT - ({ exitCode, stdout } = await testNatUtils.pkExecNs( - userPid!, - agent2Pid!, - ['nodes', 'ping', agent1NodeId, '--format', 'json'], - { - PK_NODE_PATH: agent2NodePath, - PK_PASSWORD: password, - }, - dataDir, - )); - expect(exitCode).toBe(0); - expect(JSON.parse(stdout)).toEqual({ - success: true, - message: 'Node is Active.', - }); - // Can now ping Agent 2 (it will be expecting a response) - ({ exitCode, stdout } = await testNatUtils.pkExecNs( - userPid!, - agent1Pid!, - ['nodes', 'ping', agent2NodeId, '--format', 'json'], - { - PK_NODE_PATH: agent1NodePath, - PK_PASSWORD: password, - }, - dataDir, - )); - expect(exitCode).toBe(0); - expect(JSON.parse(stdout)).toEqual({ - success: true, - message: 'Node is Active.', - }); - await tearDownNAT(); - }, - global.defaultTimeout * 2, - ); - test( - 'Node1 behind EIM NAT connects to Node2 behind EIM NAT', - async () => { - const { - userPid, - agent1Pid, - agent2Pid, - password, - dataDir, - agent1NodePath, - agent2NodePath, - agent1NodeId, - agent1Host, - agent1ProxyPort, - agent2NodeId, - agent2Host, - agent2ProxyPort, - tearDownNAT, - } = await testNatUtils.setupNAT('dmz', 'eim', logger); - await testNatUtils.pkExecNs( - userPid!, - agent2Pid!, - ['nodes', 'add', agent1NodeId, agent1Host, agent1ProxyPort], - { - PK_NODE_PATH: agent2NodePath, - PK_PASSWORD: password, - }, - dataDir, - ); - await testNatUtils.pkExecNs( - userPid!, - agent1Pid!, - ['nodes', 'add', agent2NodeId, agent2Host, agent2ProxyPort], - { - PK_NODE_PATH: agent1NodePath, - PK_PASSWORD: password, - }, - dataDir, - ); - // If we try to ping Agent 2 it will fail - let exitCode, stdout; - ({ exitCode, stdout } = await testNatUtils.pkExecNs( - userPid!, - agent1Pid!, - ['nodes', 'ping', agent2NodeId, '--format', 'json'], - { - PK_NODE_PATH: agent1NodePath, - PK_PASSWORD: password, - }, - dataDir, - )); - expect(exitCode).toBe(1); - expect(JSON.parse(stdout)).toEqual({ - success: false, - message: 'No response received', - }); - // But Agent 2 can ping Agent 1 because it's expecting a response now - ({ exitCode, stdout } = await testNatUtils.pkExecNs( - userPid!, - agent2Pid!, - ['nodes', 'ping', agent1NodeId, '--format', 'json'], - { - PK_NODE_PATH: agent2NodePath, - PK_PASSWORD: password, - }, - dataDir, - )); - expect(exitCode).toBe(0); - expect(JSON.parse(stdout)).toEqual({ - success: true, - message: 'Node is Active.', - }); - // Can now ping Agent 2 (it will be expecting a response too) - ({ exitCode, stdout } = await testNatUtils.pkExecNs( - userPid!, - agent1Pid!, - ['nodes', 'ping', agent2NodeId, '--format', 'json'], - { - PK_NODE_PATH: agent1NodePath, - PK_PASSWORD: password, - }, - dataDir, - )); - expect(exitCode).toBe(0); - expect(JSON.parse(stdout)).toEqual({ - success: true, - message: 'Node is Active.', - }); - await tearDownNAT(); - }, - global.defaultTimeout * 2, - ); - test( - 'Node1 behind EIM NAT connects to Node2 behind EIM NAT via seed node', - async () => { - const { - userPid, - agent1Pid, - agent2Pid, - password, - dataDir, - agent1NodePath, - agent2NodePath, - agent1NodeId, - agent2NodeId, - tearDownNAT, - } = await testNatUtils.setupNATWithSeedNode('eim', 'eim', logger); - // Contact details can be retrieved from the seed node so don't need to - // add manually - // If we try to ping Agent 2 it will fail - let exitCode, stdout; - ({ exitCode, stdout } = await testNatUtils.pkExecNs( - userPid!, - agent1Pid!, - ['nodes', 'ping', agent2NodeId, '--format', 'json'], - { - PK_NODE_PATH: agent1NodePath, - PK_PASSWORD: password, - }, - dataDir, - )); - expect(exitCode).toBe(1); - expect(JSON.parse(stdout)).toEqual({ - success: false, - message: 'No response received', - }); - // But Agent 2 can ping Agent 1 now because it's expecting a response - ({ exitCode, stdout } = await testNatUtils.pkExecNs( - userPid!, - agent2Pid!, - ['nodes', 'ping', agent1NodeId, '--format', 'json'], - { - PK_NODE_PATH: agent2NodePath, - PK_PASSWORD: password, - }, - dataDir, - )); - expect(exitCode).toBe(0); - expect(JSON.parse(stdout)).toEqual({ - success: true, - message: 'Node is Active.', - }); - // Can now ping Agent 2 (it will also be expecting a response) - ({ exitCode, stdout } = await testNatUtils.pkExecNs( - userPid!, - agent1Pid!, - ['nodes', 'ping', agent2NodeId, '--format', 'json'], - { - PK_NODE_PATH: agent1NodePath, - PK_PASSWORD: password, - }, - dataDir, - )); - expect(exitCode).toBe(0); - expect(JSON.parse(stdout)).toEqual({ - success: true, - message: 'Node is Active.', - }); - await tearDownNAT(); - }, - global.defaultTimeout * 2, - ); - test( - 'Node1 behind EIM NAT cannot connect to Node2 behind EDM NAT', - async () => { - const { - userPid, - agent1Pid, - agent2Pid, - password, - dataDir, - agent1NodePath, - agent2NodePath, - agent1NodeId, - agent2NodeId, - tearDownNAT, - } = await testNatUtils.setupNATWithSeedNode('eim', 'edmSimple', logger); - // Since one of the nodes uses EDM NAT we cannot punch through - let exitCode, stdout; - ({ exitCode, stdout } = await testNatUtils.pkExecNs( - userPid!, - agent2Pid!, - ['nodes', 'ping', agent1NodeId, '--format', 'json', '-vv'], - { - PK_NODE_PATH: agent2NodePath, - PK_PASSWORD: password, - }, - dataDir, - )); - expect(exitCode).toBe(1); - expect(JSON.parse(stdout)).toEqual({ - success: false, - message: 'No response received', - }); - ({ exitCode, stdout } = await testNatUtils.pkExecNs( - userPid!, - agent1Pid!, - ['nodes', 'ping', agent2NodeId, '--format', 'json', '-vv'], - { - PK_NODE_PATH: agent1NodePath, - PK_PASSWORD: password, - }, - dataDir, - )); - expect(exitCode).toBe(1); - expect(JSON.parse(stdout)).toEqual({ - success: false, - message: 'No response received', + }); + afterEach(async () => { + await fs.promises.rm(dataDir, { + force: true, + recursive: true, }); - await tearDownNAT(); - }, - global.defaultTimeout * 2, - ); -}); + }); + test( + 'Node1 behind EIM NAT connects to Node2', + async () => { + const { + userPid, + agent1Pid, + password, + dataDir, + agent1NodePath, + agent2NodeId, + agent2Host, + agent2ProxyPort, + tearDownNAT, + } = await testNatUtils.setupNAT('eim', 'dmz', logger); + // Since node2 is not behind a NAT can directly add its details + await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'add', agent2NodeId, agent2Host, agent2ProxyPort], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + ); + const { exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'ping', agent2NodeId, '--format', 'json'], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + ); + expect(exitCode).toBe(0); + expect(JSON.parse(stdout)).toEqual({ + success: true, + message: 'Node is Active.', + }); + await tearDownNAT(); + }, + global.defaultTimeout * 2, + ); + test( + 'Node1 connects to Node2 behind EIM NAT', + async () => { + const { + userPid, + agent1Pid, + agent2Pid, + password, + dataDir, + agent1NodePath, + agent2NodePath, + agent1NodeId, + agent1Host, + agent1ProxyPort, + agent2NodeId, + agent2Host, + agent2ProxyPort, + tearDownNAT, + } = await testNatUtils.setupNAT('dmz', 'eim', logger); + await testNatUtils.pkExecNs( + userPid!, + agent2Pid!, + ['nodes', 'add', agent1NodeId, agent1Host, agent1ProxyPort], + { + PK_NODE_PATH: agent2NodePath, + PK_PASSWORD: password, + }, + dataDir, + ); + await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'add', agent2NodeId, agent2Host, agent2ProxyPort], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + ); + // If we try to ping Agent 2 it will fail + let exitCode, stdout; + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'ping', agent2NodeId, '--format', 'json'], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(1); + expect(JSON.parse(stdout)).toEqual({ + success: false, + message: 'No response received', + }); + // But Agent 2 can ping Agent 1 because Agent 1 is not behind a NAT + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent2Pid!, + ['nodes', 'ping', agent1NodeId, '--format', 'json'], + { + PK_NODE_PATH: agent2NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(0); + expect(JSON.parse(stdout)).toEqual({ + success: true, + message: 'Node is Active.', + }); + // Can now ping Agent 2 (it will be expecting a response) + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'ping', agent2NodeId, '--format', 'json'], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(0); + expect(JSON.parse(stdout)).toEqual({ + success: true, + message: 'Node is Active.', + }); + await tearDownNAT(); + }, + global.defaultTimeout * 2, + ); + test( + 'Node1 behind EIM NAT connects to Node2 behind EIM NAT', + async () => { + const { + userPid, + agent1Pid, + agent2Pid, + password, + dataDir, + agent1NodePath, + agent2NodePath, + agent1NodeId, + agent1Host, + agent1ProxyPort, + agent2NodeId, + agent2Host, + agent2ProxyPort, + tearDownNAT, + } = await testNatUtils.setupNAT('dmz', 'eim', logger); + await testNatUtils.pkExecNs( + userPid!, + agent2Pid!, + ['nodes', 'add', agent1NodeId, agent1Host, agent1ProxyPort], + { + PK_NODE_PATH: agent2NodePath, + PK_PASSWORD: password, + }, + dataDir, + ); + await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'add', agent2NodeId, agent2Host, agent2ProxyPort], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + ); + // If we try to ping Agent 2 it will fail + let exitCode, stdout; + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'ping', agent2NodeId, '--format', 'json'], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(1); + expect(JSON.parse(stdout)).toEqual({ + success: false, + message: 'No response received', + }); + // But Agent 2 can ping Agent 1 because it's expecting a response now + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent2Pid!, + ['nodes', 'ping', agent1NodeId, '--format', 'json'], + { + PK_NODE_PATH: agent2NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(0); + expect(JSON.parse(stdout)).toEqual({ + success: true, + message: 'Node is Active.', + }); + // Can now ping Agent 2 (it will be expecting a response too) + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'ping', agent2NodeId, '--format', 'json'], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(0); + expect(JSON.parse(stdout)).toEqual({ + success: true, + message: 'Node is Active.', + }); + await tearDownNAT(); + }, + global.defaultTimeout * 2, + ); + test( + 'Node1 behind EIM NAT connects to Node2 behind EIM NAT via seed node', + async () => { + const { + userPid, + agent1Pid, + agent2Pid, + password, + dataDir, + agent1NodePath, + agent2NodePath, + agent1NodeId, + agent2NodeId, + tearDownNAT, + } = await testNatUtils.setupNATWithSeedNode('eim', 'eim', logger); + // Contact details can be retrieved from the seed node so don't need to + // add manually + // If we try to ping Agent 2 it will fail + let exitCode, stdout; + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'ping', agent2NodeId, '--format', 'json'], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(1); + expect(JSON.parse(stdout)).toEqual({ + success: false, + message: 'No response received', + }); + // But Agent 2 can ping Agent 1 now because it's expecting a response + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent2Pid!, + ['nodes', 'ping', agent1NodeId, '--format', 'json'], + { + PK_NODE_PATH: agent2NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(0); + expect(JSON.parse(stdout)).toEqual({ + success: true, + message: 'Node is Active.', + }); + // Can now ping Agent 2 (it will also be expecting a response) + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'ping', agent2NodeId, '--format', 'json'], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(0); + expect(JSON.parse(stdout)).toEqual({ + success: true, + message: 'Node is Active.', + }); + await tearDownNAT(); + }, + global.defaultTimeout * 2, + ); + test( + 'Node1 behind EIM NAT cannot connect to Node2 behind EDM NAT', + async () => { + const { + userPid, + agent1Pid, + agent2Pid, + password, + dataDir, + agent1NodePath, + agent2NodePath, + agent1NodeId, + agent2NodeId, + tearDownNAT, + } = await testNatUtils.setupNATWithSeedNode('eim', 'edmSimple', logger); + // Since one of the nodes uses EDM NAT we cannot punch through + let exitCode, stdout; + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent2Pid!, + ['nodes', 'ping', agent1NodeId, '--format', 'json', '-vv'], + { + PK_NODE_PATH: agent2NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(1); + expect(JSON.parse(stdout)).toEqual({ + success: false, + message: 'No response received', + }); + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'ping', agent2NodeId, '--format', 'json', '-vv'], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(1); + expect(JSON.parse(stdout)).toEqual({ + success: false, + message: 'No response received', + }); + await tearDownNAT(); + }, + global.defaultTimeout * 2, + ); + }, +); diff --git a/tests/nat/noNAT.test.ts b/tests/nat/noNAT.test.ts index bd8a85623..737f36c08 100644 --- a/tests/nat/noNAT.test.ts +++ b/tests/nat/noNAT.test.ts @@ -2,238 +2,249 @@ import os from 'os'; import path from 'path'; import fs from 'fs'; import readline from 'readline'; +import process from 'process'; +import shell from 'shelljs'; import Logger, { LogLevel, StreamHandler } from '@matrixai/logger'; import Status from '@/status/Status'; import config from '@/config'; import * as testNatUtils from './utils'; +import { describeIf } from '../utils'; import * as testBinUtils from '../bin/utils'; -describe('no NAT', () => { - const logger = new Logger('no NAT test', LogLevel.WARN, [ - new StreamHandler(), - ]); - let dataDir: string; - beforeEach(async () => { - dataDir = await fs.promises.mkdtemp( - path.join(os.tmpdir(), 'polykey-test-'), - ); - }); - afterEach(async () => { - await fs.promises.rm(dataDir, { - force: true, - recursive: true, - }); - }); - test( - 'can create an agent in a namespace', - async () => { - const password = 'abc123'; - const usrns = testNatUtils.createUserNamespace(); - const netns = testNatUtils.createNetworkNamespace(usrns.pid!); - const agentProcess = await testNatUtils.pkSpawnNs( - usrns.pid!, - netns.pid!, - [ - 'agent', - 'start', - '--node-path', - path.join(dataDir, 'polykey'), - '--root-key-pair-bits', - '1024', - '--client-host', - '127.0.0.1', - '--proxy-host', - '127.0.0.1', - '--workers', - '0', - '--verbose', - '--format', - 'json', - ], - { - PK_PASSWORD: password, - }, - dataDir, - logger.getChild('agentProcess'), +describeIf( + process.platform === 'linux' && + shell.which('ip') && + shell.which('iptables') && + shell.which('nsenter') && + shell.which('unshare'), + 'no NAT', + () => { + const logger = new Logger('no NAT test', LogLevel.WARN, [ + new StreamHandler(), + ]); + let dataDir: string; + beforeEach(async () => { + dataDir = await fs.promises.mkdtemp( + path.join(os.tmpdir(), 'polykey-test-'), ); - const rlOut = readline.createInterface(agentProcess.stdout!); - const stdout = await new Promise((resolve, reject) => { - rlOut.once('line', resolve); - rlOut.once('close', reject); - }); - const statusLiveData = JSON.parse(stdout); - expect(statusLiveData).toMatchObject({ - pid: agentProcess.pid, - nodeId: expect.any(String), - clientHost: expect.any(String), - clientPort: expect.any(Number), - agentHost: expect.any(String), - agentPort: expect.any(Number), - forwardHost: expect.any(String), - forwardPort: expect.any(Number), - proxyHost: expect.any(String), - proxyPort: expect.any(Number), - recoveryCode: expect.any(String), + }); + afterEach(async () => { + await fs.promises.rm(dataDir, { + force: true, + recursive: true, }); - expect( - statusLiveData.recoveryCode.split(' ').length === 12 || - statusLiveData.recoveryCode.split(' ').length === 24, - ).toBe(true); - agentProcess.kill('SIGTERM'); - let exitCode, signal; - [exitCode, signal] = await testBinUtils.processExit(agentProcess); - expect(exitCode).toBe(null); - expect(signal).toBe('SIGTERM'); - // Check for graceful exit - const status = new Status({ - statusPath: path.join(dataDir, 'polykey', config.defaults.statusBase), - statusLockPath: path.join( + }); + test( + 'can create an agent in a namespace', + async () => { + const password = 'abc123'; + const usrns = testNatUtils.createUserNamespace(logger); + const netns = testNatUtils.createNetworkNamespace(usrns.pid!, logger); + const agentProcess = await testNatUtils.pkSpawnNs( + usrns.pid!, + netns.pid!, + [ + 'agent', + 'start', + '--node-path', + path.join(dataDir, 'polykey'), + '--root-key-pair-bits', + '1024', + '--client-host', + '127.0.0.1', + '--proxy-host', + '127.0.0.1', + '--workers', + '0', + '--verbose', + '--format', + 'json', + ], + { + PK_PASSWORD: password, + }, dataDir, - 'polykey', - config.defaults.statusLockBase, - ), - fs, - logger, - }); - const statusInfo = (await status.readStatus())!; - expect(statusInfo.status).toBe('DEAD'); - netns.kill('SIGTERM'); - [exitCode, signal] = await testBinUtils.processExit(netns); - expect(exitCode).toBe(null); - expect(signal).toBe('SIGTERM'); - usrns.kill('SIGTERM'); - [exitCode, signal] = await testBinUtils.processExit(usrns); - expect(exitCode).toBe(null); - expect(signal).toBe('SIGTERM'); - }, - global.defaultTimeout * 2, - ); - test( - 'agents in different namespaces can ping each other', - async () => { - const { - userPid, - agent1Pid, - agent2Pid, - password, - dataDir, - agent1NodePath, - agent2NodePath, - agent1NodeId, - agent1Host, - agent1ProxyPort, - agent2NodeId, - agent2Host, - agent2ProxyPort, - tearDownNAT, - } = await testNatUtils.setupNAT('dmz', 'dmz', logger); - // Since neither node is behind a NAT can directly add eachother's - // details using pk nodes add - await testNatUtils.pkExecNs( - userPid!, - agent1Pid!, - ['nodes', 'add', agent2NodeId, agent2Host, agent2ProxyPort], - { - PK_NODE_PATH: agent1NodePath, - PK_PASSWORD: password, - }, - dataDir, - ); - await testNatUtils.pkExecNs( - userPid!, - agent2Pid!, - ['nodes', 'add', agent1NodeId, agent1Host, agent1ProxyPort], - { - PK_NODE_PATH: agent2NodePath, - PK_PASSWORD: password, - }, - dataDir, - ); - let exitCode, stdout; - ({ exitCode, stdout } = await testNatUtils.pkExecNs( - userPid!, - agent1Pid!, - ['nodes', 'ping', agent2NodeId, '--format', 'json', '--verbose'], - { - PK_NODE_PATH: agent1NodePath, - PK_PASSWORD: password, - }, - dataDir, - )); - expect(exitCode).toBe(0); - expect(JSON.parse(stdout)).toEqual({ - success: true, - message: 'Node is Active.', - }); - ({ exitCode, stdout } = await testNatUtils.pkExecNs( - userPid!, - agent2Pid!, - ['nodes', 'ping', agent1NodeId, '--format', 'json', '--verbose'], - { - PK_NODE_PATH: agent2NodePath, - PK_PASSWORD: password, - }, - dataDir, - )); - expect(exitCode).toBe(0); - expect(JSON.parse(stdout)).toEqual({ - success: true, - message: 'Node is Active.', - }); - await tearDownNAT(); - }, - global.defaultTimeout * 2, - ); - test( - 'agents in different namespaces can ping each other via seed node', - async () => { - const { - userPid, - agent1Pid, - agent2Pid, - password, - dataDir, - agent1NodePath, - agent2NodePath, - agent1NodeId, - agent2NodeId, - tearDownNAT, - } = await testNatUtils.setupNATWithSeedNode('dmz', 'dmz', logger); - // Should be able to ping straight away using the details from the - // seed node - let exitCode, stdout; - ({ exitCode, stdout } = await testNatUtils.pkExecNs( - userPid!, - agent1Pid!, - ['nodes', 'ping', agent2NodeId, '--format', 'json', '--verbose'], - { - PK_NODE_PATH: agent1NodePath, - PK_PASSWORD: password, - }, - dataDir, - )); - expect(exitCode).toBe(0); - expect(JSON.parse(stdout)).toEqual({ - success: true, - message: 'Node is Active.', - }); - ({ exitCode, stdout } = await testNatUtils.pkExecNs( - userPid!, - agent2Pid!, - ['nodes', 'ping', agent1NodeId, '--format', 'json', '--verbose'], - { - PK_NODE_PATH: agent2NodePath, - PK_PASSWORD: password, - }, - dataDir, - )); - expect(exitCode).toBe(0); - expect(JSON.parse(stdout)).toEqual({ - success: true, - message: 'Node is Active.', - }); - await tearDownNAT(); - }, - global.defaultTimeout * 2, - ); -}); + logger.getChild('agentProcess'), + ); + const rlOut = readline.createInterface(agentProcess.stdout!); + const stdout = await new Promise((resolve, reject) => { + rlOut.once('line', resolve); + rlOut.once('close', reject); + }); + const statusLiveData = JSON.parse(stdout); + expect(statusLiveData).toMatchObject({ + pid: agentProcess.pid, + nodeId: expect.any(String), + clientHost: expect.any(String), + clientPort: expect.any(Number), + agentHost: expect.any(String), + agentPort: expect.any(Number), + forwardHost: expect.any(String), + forwardPort: expect.any(Number), + proxyHost: expect.any(String), + proxyPort: expect.any(Number), + recoveryCode: expect.any(String), + }); + expect( + statusLiveData.recoveryCode.split(' ').length === 12 || + statusLiveData.recoveryCode.split(' ').length === 24, + ).toBe(true); + agentProcess.kill('SIGTERM'); + let exitCode, signal; + [exitCode, signal] = await testBinUtils.processExit(agentProcess); + expect(exitCode).toBe(null); + expect(signal).toBe('SIGTERM'); + // Check for graceful exit + const status = new Status({ + statusPath: path.join(dataDir, 'polykey', config.defaults.statusBase), + statusLockPath: path.join( + dataDir, + 'polykey', + config.defaults.statusLockBase, + ), + fs, + logger, + }); + const statusInfo = (await status.readStatus())!; + expect(statusInfo.status).toBe('DEAD'); + netns.kill('SIGTERM'); + [exitCode, signal] = await testBinUtils.processExit(netns); + expect(exitCode).toBe(null); + expect(signal).toBe('SIGTERM'); + usrns.kill('SIGTERM'); + [exitCode, signal] = await testBinUtils.processExit(usrns); + expect(exitCode).toBe(null); + expect(signal).toBe('SIGTERM'); + }, + global.defaultTimeout * 2, + ); + test( + 'agents in different namespaces can ping each other', + async () => { + const { + userPid, + agent1Pid, + agent2Pid, + password, + dataDir, + agent1NodePath, + agent2NodePath, + agent1NodeId, + agent1Host, + agent1ProxyPort, + agent2NodeId, + agent2Host, + agent2ProxyPort, + tearDownNAT, + } = await testNatUtils.setupNAT('dmz', 'dmz', logger); + // Since neither node is behind a NAT can directly add eachother's + // details using pk nodes add + await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'add', agent2NodeId, agent2Host, agent2ProxyPort], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + ); + await testNatUtils.pkExecNs( + userPid!, + agent2Pid!, + ['nodes', 'add', agent1NodeId, agent1Host, agent1ProxyPort], + { + PK_NODE_PATH: agent2NodePath, + PK_PASSWORD: password, + }, + dataDir, + ); + let exitCode, stdout; + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'ping', agent2NodeId, '--format', 'json', '--verbose'], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(0); + expect(JSON.parse(stdout)).toEqual({ + success: true, + message: 'Node is Active.', + }); + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent2Pid!, + ['nodes', 'ping', agent1NodeId, '--format', 'json', '--verbose'], + { + PK_NODE_PATH: agent2NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(0); + expect(JSON.parse(stdout)).toEqual({ + success: true, + message: 'Node is Active.', + }); + await tearDownNAT(); + }, + global.defaultTimeout * 2, + ); + test( + 'agents in different namespaces can ping each other via seed node', + async () => { + const { + userPid, + agent1Pid, + agent2Pid, + password, + dataDir, + agent1NodePath, + agent2NodePath, + agent1NodeId, + agent2NodeId, + tearDownNAT, + } = await testNatUtils.setupNATWithSeedNode('dmz', 'dmz', logger); + // Should be able to ping straight away using the details from the + // seed node + let exitCode, stdout; + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent1Pid!, + ['nodes', 'ping', agent2NodeId, '--format', 'json', '--verbose'], + { + PK_NODE_PATH: agent1NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(0); + expect(JSON.parse(stdout)).toEqual({ + success: true, + message: 'Node is Active.', + }); + ({ exitCode, stdout } = await testNatUtils.pkExecNs( + userPid!, + agent2Pid!, + ['nodes', 'ping', agent1NodeId, '--format', 'json', '--verbose'], + { + PK_NODE_PATH: agent2NodePath, + PK_PASSWORD: password, + }, + dataDir, + )); + expect(exitCode).toBe(0); + expect(JSON.parse(stdout)).toEqual({ + success: true, + message: 'Node is Active.', + }); + await tearDownNAT(); + }, + global.defaultTimeout * 2, + ); + }, +); diff --git a/tests/nat/utils.ts b/tests/nat/utils.ts index 95125adf8..9b83fdab3 100644 --- a/tests/nat/utils.ts +++ b/tests/nat/utils.ts @@ -43,26 +43,52 @@ const mappedPort = '55555'; * Formats the command to enter a namespace to run a process inside it */ const nsenter = (usrnsPid: number, netnsPid: number) => { - return `nsenter --target ${usrnsPid} --user --preserve-credentials `.concat( - `nsenter --target ${netnsPid} --net `, - ); + return [ + '--target', + usrnsPid.toString(), + '--user', + '--preserve-credentials', + 'nsenter', + '--target', + netnsPid.toString(), + '--net', + ]; }; /** * Create a user namespace from which network namespaces can be created without * requiring sudo */ -function createUserNamespace(): ChildProcess { - return child_process.spawn('unshare', ['--user', '--map-root-user'], { - shell: true, +function createUserNamespace( + logger: Logger = new Logger(createUserNamespace.name), +): ChildProcess { + logger.info('unshare --user --map-root-user'); + const subprocess = child_process.spawn( + 'unshare', + ['--user', '--map-root-user'], + { + shell: true, + }, + ); + const rlErr = readline.createInterface(subprocess.stderr!); + rlErr.on('line', (l) => { + // The readline library will trim newlines + logger.info(l); }); + return subprocess; } /** * Create a network namespace inside a user namespace */ -function createNetworkNamespace(usrnsPid: number): ChildProcess { - return child_process.spawn( +function createNetworkNamespace( + usrnsPid: number, + logger: Logger = new Logger(createNetworkNamespace.name), +): ChildProcess { + logger.info( + `nsenter --target ${usrnsPid.toString()} --user --preserve-credentials unshare --net`, + ); + const subprocess = child_process.spawn( 'nsenter', [ '--target', @@ -74,6 +100,12 @@ function createNetworkNamespace(usrnsPid: number): ChildProcess { ], { shell: true }, ); + const rlErr = readline.createInterface(subprocess.stderr!); + rlErr.on('line', (l) => { + // The readline library will trim newlines + logger.info(l); + }); + return subprocess; } /** @@ -83,109 +115,313 @@ function createNetworkNamespace(usrnsPid: number): ChildProcess { * between each pair of adjacent namespaces, and adds default routing to allow * cross-communication */ -function setupNetworkNamespaceInterfaces( +async function setupNetworkNamespaceInterfaces( usrnsPid: number, agent1NetnsPid: number, router1NetnsPid: number, router2NetnsPid: number, agent2NetnsPid: number, + logger: Logger = new Logger(setupNetworkNamespaceInterfaces.name), ) { - // Bring up loopback - child_process.exec(nsenter(usrnsPid, agent1NetnsPid) + `ip link set lo up`); - child_process.exec(nsenter(usrnsPid, router1NetnsPid) + `ip link set lo up`); - child_process.exec(nsenter(usrnsPid, router2NetnsPid) + `ip link set lo up`); - child_process.exec(nsenter(usrnsPid, agent2NetnsPid) + `ip link set lo up`); - // Create veth pair to link the namespaces - child_process.exec( - nsenter(usrnsPid, agent1NetnsPid) + - `ip link add ${agent1Host} type veth peer name ${agent1RouterHostInt}`, - ); - child_process.exec( - nsenter(usrnsPid, router1NetnsPid) + - `ip link add ${agent1RouterHostExt} type veth peer name ${agent2RouterHostExt}`, - ); - child_process.exec( - nsenter(usrnsPid, router2NetnsPid) + - `ip link add ${agent2RouterHostInt} type veth peer name ${agent2Host}`, - ); - // Link up the ends to the correct namespaces - child_process.exec( - nsenter(usrnsPid, agent1NetnsPid) + - `ip link set dev ${agent1RouterHostInt} netns ${router1NetnsPid}`, - ); - child_process.exec( - nsenter(usrnsPid, router1NetnsPid) + - `ip link set dev ${agent2RouterHostExt} netns ${router2NetnsPid}`, - ); - child_process.exec( - nsenter(usrnsPid, router2NetnsPid) + - `ip link set dev ${agent2Host} netns ${agent2NetnsPid}`, - ); - // Bring up each end - child_process.exec( - nsenter(usrnsPid, agent1NetnsPid) + `ip link set ${agent1Host} up`, - ); - child_process.exec( - nsenter(usrnsPid, router1NetnsPid) + - `ip link set ${agent1RouterHostInt} up`, - ); - child_process.exec( - nsenter(usrnsPid, router1NetnsPid) + - `ip link set ${agent1RouterHostExt} up`, - ); - child_process.exec( - nsenter(usrnsPid, router2NetnsPid) + - `ip link set ${agent2RouterHostExt} up`, - ); - child_process.exec( - nsenter(usrnsPid, router2NetnsPid) + - `ip link set ${agent2RouterHostInt} up`, - ); - child_process.exec( - nsenter(usrnsPid, agent2NetnsPid) + `ip link set ${agent2Host} up`, - ); - // Assign ip addresses to each end - child_process.exec( - nsenter(usrnsPid, agent1NetnsPid) + - `ip addr add ${agent1HostIp}${subnetMask} dev ${agent1Host}`, - ); - child_process.exec( - nsenter(usrnsPid, router1NetnsPid) + - `ip addr add ${agent1RouterHostIntIp}${subnetMask} dev ${agent1RouterHostInt}`, - ); - child_process.exec( - nsenter(usrnsPid, router1NetnsPid) + - `ip addr add ${agent1RouterHostExtIp}${subnetMask} dev ${agent1RouterHostExt}`, - ); - child_process.exec( - nsenter(usrnsPid, router2NetnsPid) + - `ip addr add ${agent2RouterHostExtIp}${subnetMask} dev ${agent2RouterHostExt}`, - ); - child_process.exec( - nsenter(usrnsPid, router2NetnsPid) + - `ip addr add ${agent2RouterHostIntIp}${subnetMask} dev ${agent2RouterHostInt}`, - ); - child_process.exec( - nsenter(usrnsPid, agent2NetnsPid) + - `ip addr add ${agent2HostIp}${subnetMask} dev ${agent2Host}`, - ); - // Add default routing - child_process.exec( - nsenter(usrnsPid, agent1NetnsPid) + - `ip route add default via ${agent1RouterHostIntIp}`, - ); - child_process.exec( - nsenter(usrnsPid, router1NetnsPid) + - `ip route add default via ${agent2RouterHostExtIp}`, - ); - child_process.exec( - nsenter(usrnsPid, router2NetnsPid) + - `ip route add default via ${agent1RouterHostExtIp}`, - ); - child_process.exec( - nsenter(usrnsPid, agent2NetnsPid) + - `ip route add default via ${agent2RouterHostIntIp}`, - ); + let args: Array = []; + try { + // Bring up loopback + args = [ + ...nsenter(usrnsPid, agent1NetnsPid), + 'ip', + 'link', + 'set', + 'lo', + 'up', + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + args = [ + ...nsenter(usrnsPid, router1NetnsPid), + 'ip', + 'link', + 'set', + 'lo', + 'up', + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + args = [ + ...nsenter(usrnsPid, router2NetnsPid), + 'ip', + 'link', + 'set', + 'lo', + 'up', + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + args = [ + ...nsenter(usrnsPid, agent2NetnsPid), + 'ip', + 'link', + 'set', + 'lo', + 'up', + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + // Create veth pair to link the namespaces + args = [ + ...nsenter(usrnsPid, agent1NetnsPid), + 'ip', + 'link', + 'add', + agent1Host, + 'type', + 'veth', + 'peer', + 'name', + agent1RouterHostInt, + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + args = [ + ...nsenter(usrnsPid, router1NetnsPid), + 'ip', + 'link', + 'add', + agent1RouterHostExt, + 'type', + 'veth', + 'peer', + 'name', + agent2RouterHostExt, + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + args = [ + ...nsenter(usrnsPid, router2NetnsPid), + 'ip', + 'link', + 'add', + agent2RouterHostInt, + 'type', + 'veth', + 'peer', + 'name', + agent2Host, + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + // Link up the ends to the correct namespaces + args = [ + ...nsenter(usrnsPid, agent1NetnsPid), + 'ip', + 'link', + 'set', + 'dev', + agent1RouterHostInt, + 'netns', + router1NetnsPid.toString(), + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + args = [ + ...nsenter(usrnsPid, router1NetnsPid), + 'ip', + 'link', + 'set', + 'dev', + agent2RouterHostExt, + 'netns', + router2NetnsPid.toString(), + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + args = [ + ...nsenter(usrnsPid, router2NetnsPid), + 'ip', + 'link', + 'set', + 'dev', + agent2Host, + 'netns', + agent2NetnsPid.toString(), + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + // Bring up each end + args = [ + ...nsenter(usrnsPid, agent1NetnsPid), + 'ip', + 'link', + 'set', + agent1Host, + 'up', + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + args = [ + ...nsenter(usrnsPid, router1NetnsPid), + 'ip', + 'link', + 'set', + agent1RouterHostInt, + 'up', + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + args = [ + ...nsenter(usrnsPid, router1NetnsPid), + 'ip', + 'link', + 'set', + agent1RouterHostExt, + 'up', + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + args = [ + ...nsenter(usrnsPid, router2NetnsPid), + 'ip', + 'link', + 'set', + agent2RouterHostExt, + 'up', + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + args = [ + ...nsenter(usrnsPid, router2NetnsPid), + 'ip', + 'link', + 'set', + agent2RouterHostInt, + 'up', + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + args = [ + ...nsenter(usrnsPid, agent2NetnsPid), + 'ip', + 'link', + 'set', + agent2Host, + 'up', + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + // Assign ip addresses to each end + args = [ + ...nsenter(usrnsPid, agent1NetnsPid), + 'ip', + 'addr', + 'add', + `${agent1HostIp}${subnetMask}`, + 'dev', + agent1Host, + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + args = [ + ...nsenter(usrnsPid, router1NetnsPid), + 'ip', + 'addr', + 'add', + `${agent1RouterHostIntIp}${subnetMask}`, + 'dev', + agent1RouterHostInt, + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + args = [ + ...nsenter(usrnsPid, router1NetnsPid), + 'ip', + 'addr', + 'add', + `${agent1RouterHostExtIp}${subnetMask}`, + 'dev', + agent1RouterHostExt, + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + args = [ + ...nsenter(usrnsPid, router2NetnsPid), + 'ip', + 'addr', + 'add', + `${agent2RouterHostExtIp}${subnetMask}`, + 'dev', + agent2RouterHostExt, + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + args = [ + ...nsenter(usrnsPid, router2NetnsPid), + 'ip', + 'addr', + 'add', + `${agent2RouterHostIntIp}${subnetMask}`, + 'dev', + agent2RouterHostInt, + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + args = [ + ...nsenter(usrnsPid, agent2NetnsPid), + 'ip', + 'addr', + 'add', + `${agent2HostIp}${subnetMask}`, + 'dev', + agent2Host, + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + // Add default routing + args = [ + ...nsenter(usrnsPid, agent1NetnsPid), + 'ip', + 'route', + 'add', + 'default', + 'via', + agent1RouterHostIntIp, + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + args = [ + ...nsenter(usrnsPid, router1NetnsPid), + 'ip', + 'route', + 'add', + 'default', + 'via', + agent2RouterHostExtIp, + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + args = [ + ...nsenter(usrnsPid, router2NetnsPid), + 'ip', + 'route', + 'add', + 'default', + 'via', + agent1RouterHostExtIp, + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + args = [ + ...nsenter(usrnsPid, agent2NetnsPid), + 'ip', + 'route', + 'add', + 'default', + 'via', + agent2RouterHostIntIp, + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + } catch (e) { + logger.error(e.message); + } } /** @@ -195,79 +431,214 @@ function setupNetworkNamespaceInterfaces( * between each pair of adjacent namespaces, and adds default routing to allow * cross-communication */ -function setupSeedNamespaceInterfaces( +async function setupSeedNamespaceInterfaces( usrnsPid: number, seedNetnsPid: number, router1NetnsPid: number, router2NetnsPid: number, + logger: Logger = new Logger(setupSeedNamespaceInterfaces.name), ) { - // Bring up loopback - child_process.exec(nsenter(usrnsPid, seedNetnsPid) + `ip link set lo up`); - // Create veth pairs to link the namespaces - child_process.exec( - nsenter(usrnsPid, router1NetnsPid) + - `ip link add ${router1SeedHost} type veth peer name ${seedRouter1Host}`, - ); - child_process.exec( - nsenter(usrnsPid, router2NetnsPid) + - `ip link add ${router2SeedHost} type veth peer name ${seedRouter2Host}`, - ); - // Move seed ends into seed network namespace - child_process.exec( - nsenter(usrnsPid, router1NetnsPid) + - `ip link set dev ${seedRouter1Host} netns ${seedNetnsPid}`, - ); - child_process.exec( - nsenter(usrnsPid, router2NetnsPid) + - `ip link set dev ${seedRouter2Host} netns ${seedNetnsPid}`, - ); - // Bring up each end - child_process.exec( - nsenter(usrnsPid, router1NetnsPid) + `ip link set ${router1SeedHost} up`, - ); - child_process.exec( - nsenter(usrnsPid, seedNetnsPid) + `ip link set ${seedRouter1Host} up`, - ); - child_process.exec( - nsenter(usrnsPid, seedNetnsPid) + `ip link set ${seedRouter2Host} up`, - ); - child_process.exec( - nsenter(usrnsPid, router2NetnsPid) + `ip link set ${router2SeedHost} up`, - ); - // Assign ip addresses to each end - child_process.exec( - nsenter(usrnsPid, router1NetnsPid) + - `ip addr add ${router1SeedHostIp}${subnetMask} dev ${router1SeedHost}`, - ); - child_process.exec( - nsenter(usrnsPid, seedNetnsPid) + - `ip addr add ${seedHostIp}${subnetMask} dev ${seedRouter1Host}`, - ); - child_process.exec( - nsenter(usrnsPid, seedNetnsPid) + - `ip addr add ${seedHostIp}${subnetMask} dev ${seedRouter2Host}`, - ); - child_process.exec( - nsenter(usrnsPid, router2NetnsPid) + - `ip addr add ${router2SeedHostIp}${subnetMask} dev ${router2SeedHost}`, - ); - child_process.exec( - nsenter(usrnsPid, router1NetnsPid) + - `ip route add ${seedHostIp} dev ${router1SeedHost}`, - ); - child_process.exec( - nsenter(usrnsPid, router2NetnsPid) + - `ip route add ${seedHostIp} dev ${router2SeedHost}`, - ); - // Add default routing - child_process.exec( - nsenter(usrnsPid, seedNetnsPid) + - `ip route add ${router1SeedHostIp} dev ${seedRouter1Host}`, - ); - child_process.exec( - nsenter(usrnsPid, seedNetnsPid) + - `ip route add ${router2SeedHostIp} dev ${seedRouter2Host}`, - ); + let args: Array = []; + try { + // Bring up loopback + args = [ + ...nsenter(usrnsPid, seedNetnsPid), + 'ip', + 'link', + 'set', + 'lo', + 'up', + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + // Create veth pairs to link the namespaces + args = [ + ...nsenter(usrnsPid, router1NetnsPid), + 'ip', + 'link', + 'add', + router1SeedHost, + 'type', + 'veth', + 'peer', + 'name', + seedRouter1Host, + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + args = [ + ...nsenter(usrnsPid, router2NetnsPid), + 'ip', + 'link', + 'add', + router2SeedHost, + 'type', + 'veth', + 'peer', + 'name', + seedRouter2Host, + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + // Move seed ends into seed network namespace + args = [ + ...nsenter(usrnsPid, router1NetnsPid), + 'ip', + 'link', + 'set', + 'dev', + seedRouter1Host, + 'netns', + seedNetnsPid.toString(), + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + args = [ + ...nsenter(usrnsPid, router2NetnsPid), + 'ip', + 'link', + 'set', + 'dev', + seedRouter2Host, + 'netns', + seedNetnsPid.toString(), + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + // Bring up each end + args = [ + ...nsenter(usrnsPid, router1NetnsPid), + 'ip', + 'link', + 'set', + router1SeedHost, + 'up', + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + args = [ + ...nsenter(usrnsPid, seedNetnsPid), + 'ip', + 'link', + 'set', + seedRouter1Host, + 'up', + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + args = [ + ...nsenter(usrnsPid, seedNetnsPid), + 'ip', + 'link', + 'set', + seedRouter2Host, + 'up', + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + args = [ + ...nsenter(usrnsPid, router2NetnsPid), + 'ip', + 'link', + 'set', + router2SeedHost, + 'up', + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + // Assign ip addresses to each end + args = [ + ...nsenter(usrnsPid, router1NetnsPid), + 'ip', + 'addr', + 'add', + `${router1SeedHostIp}${subnetMask}`, + 'dev', + router1SeedHost, + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + args = [ + ...nsenter(usrnsPid, seedNetnsPid), + 'ip', + 'addr', + 'add', + `${seedHostIp}${subnetMask}`, + 'dev', + seedRouter1Host, + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + args = [ + ...nsenter(usrnsPid, seedNetnsPid), + 'ip', + 'addr', + 'add', + `${seedHostIp}${subnetMask}`, + 'dev', + seedRouter2Host, + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + args = [ + ...nsenter(usrnsPid, router2NetnsPid), + 'ip', + 'addr', + 'add', + `${router2SeedHostIp}${subnetMask}`, + 'dev', + router2SeedHost, + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + // Add default routing + args = [ + ...nsenter(usrnsPid, router1NetnsPid), + 'ip', + 'route', + 'add', + seedHostIp, + 'dev', + router1SeedHost, + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + args = [ + ...nsenter(usrnsPid, router2NetnsPid), + 'ip', + 'route', + 'add', + seedHostIp, + 'dev', + router2SeedHost, + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + args = [ + ...nsenter(usrnsPid, seedNetnsPid), + 'ip', + 'route', + 'add', + router1SeedHostIp, + 'dev', + seedRouter1Host, + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + args = [ + ...nsenter(usrnsPid, seedNetnsPid), + 'ip', + 'route', + 'add', + router2SeedHostIp, + 'dev', + seedRouter2Host, + ]; + logger.info(['nsenter', ...args].join(' ')); + await testBinUtils.exec('nsenter', args); + } catch (e) { + logger.error(e.message); + } } /** @@ -313,14 +684,7 @@ async function pkExecNs( child_process.execFile( 'nsenter', [ - '--target', - usrnsPid.toString(), - '--user', - '--preserve-credentials', - 'nsenter', - '--target', - netnsPid.toString(), - '--net', + ...nsenter(usrnsPid, netnsPid), 'ts-node', '--project', tsConfigPath, @@ -392,14 +756,7 @@ async function pkSpawnNs( const subprocess = child_process.spawn( 'nsenter', [ - '--target', - usrnsPid.toString(), - '--user', - '--preserve-credentials', - 'nsenter', - '--target', - netnsPid.toString(), - '--net', + ...nsenter(usrnsPid, netnsPid), 'ts-node', '--project', tsConfigPath, @@ -430,159 +787,283 @@ async function pkSpawnNs( /** * Setup routing between an agent and router with no NAT rules */ -function setupDMZ( +async function setupDMZ( usrnsPid: number, routerNsPid: number, agentIp: string, agentPort: string, routerExt: string, routerExtIp: string, + logger: Logger = new Logger(setupDMZ.name), ) { - const postroutingCommand = nsenter(usrnsPid, routerNsPid).concat( - 'iptables --table nat ', - '--append POSTROUTING ', - '--protocol udp ', - `--source ${agentIp}${subnetMask} `, - `--out-interface ${routerExt} `, - '--jump SNAT ', - `--to-source ${routerExtIp}:${mappedPort}`, - ); - const preroutingCommand = nsenter(usrnsPid, routerNsPid).concat( - 'iptables --table nat ', - '--append PREROUTING ', - '--protocol udp ', - `--destination-port ${mappedPort} `, - `--in-interface ${routerExt} `, - '--jump DNAT ', - `--to-destination ${agentIp}:${agentPort}`, - ); - child_process.exec(postroutingCommand); - child_process.exec(preroutingCommand); + const postroutingCommand = [ + ...nsenter(usrnsPid, routerNsPid), + 'iptables', + '--table', + 'nat', + '--append', + 'POSTROUTING', + '--protocol', + 'udp', + '--source', + `${agentIp}${subnetMask}`, + '--out-interface', + routerExt, + '--jump', + 'SNAT', + '--to-source', + `${routerExtIp}:${mappedPort}`, + ]; + const preroutingCommand = [ + ...nsenter(usrnsPid, routerNsPid), + 'iptables', + '--table', + 'nat', + '--append', + 'PREROUTING', + '--protocol', + 'udp', + '--destination-port', + mappedPort, + '--in-interface', + routerExt, + '--jump', + 'DNAT', + '--to-destination', + `${agentIp}:${agentPort}`, + ]; + try { + logger.info(['nsenter', ...postroutingCommand].join(' ')); + await testBinUtils.exec('nsenter', postroutingCommand); + logger.info(['nsenter', ...preroutingCommand].join(' ')); + await testBinUtils.exec('nsenter', preroutingCommand); + } catch (e) { + logger.error(e.message); + } } /** * Setup Port-Restricted Cone NAT for a namespace (on the router namespace) */ -function setupNATEndpointIndependentMapping( +async function setupNATEndpointIndependentMapping( usrnsPid: number, routerNsPid: number, agentIp: string, routerExt: string, routerInt: string, + logger: Logger = new Logger(setupNATEndpointIndependentMapping.name), ) { - const natCommand = nsenter(usrnsPid, routerNsPid).concat( - 'iptables --table nat ', - '--append POSTROUTING ', - '--protocol udp ', - `--source ${agentIp}${subnetMask} `, - `--out-interface ${routerExt} `, - '--jump MASQUERADE', - ); - const acceptLocalCommand = nsenter(usrnsPid, routerNsPid).concat( - 'iptables --table filter ', - '--append INPUT ', - `--in-interface ${routerInt} `, - '--jump ACCEPT', - ); - const acceptEstablishedCommand = nsenter(usrnsPid, routerNsPid).concat( - 'iptables --table filter ', - '--append INPUT ', - `--match conntrack `, - '--cstate RELATED,ESTABLISHED ', - '--jump ACCEPT', - ); - const dropCommand = nsenter(usrnsPid, routerNsPid).concat( - 'iptables --table filter ', - '--append INPUT ', - '--jump DROP', - ); - child_process.exec(acceptLocalCommand); - child_process.exec(acceptEstablishedCommand); - child_process.exec(dropCommand); - child_process.exec(natCommand); + const natCommand = [ + ...nsenter(usrnsPid, routerNsPid), + 'iptables', + '--table', + 'nat', + '--append', + 'POSTROUTING', + '--protocol', + 'udp', + '--source', + `${agentIp}${subnetMask}`, + '--out-interface', + routerExt, + '--jump', + 'MASQUERADE', + ]; + const acceptLocalCommand = [ + ...nsenter(usrnsPid, routerNsPid), + 'iptables', + '--table', + 'filter', + '--append', + 'INPUT', + '--in-interface', + routerInt, + '--jump', + 'ACCEPT', + ]; + const acceptEstablishedCommand = [ + ...nsenter(usrnsPid, routerNsPid), + 'iptables', + '--table', + 'filter', + '--append', + 'INPUT', + '--match', + 'conntrack', + '--ctstate', + 'RELATED,ESTABLISHED', + '--jump', + 'ACCEPT', + ]; + const dropCommand = [ + ...nsenter(usrnsPid, routerNsPid), + 'iptables', + '--table', + 'filter', + '--append', + 'INPUT', + '--jump', + 'DROP', + ]; + try { + logger.info(['nsenter', ...acceptLocalCommand].join(' ')); + await testBinUtils.exec('nsenter', acceptLocalCommand); + logger.info(['nsenter', ...acceptEstablishedCommand].join(' ')); + await testBinUtils.exec('nsenter', acceptEstablishedCommand); + logger.info(['nsenter', ...dropCommand].join(' ')); + await testBinUtils.exec('nsenter', dropCommand); + logger.info(['nsenter', ...natCommand].join(' ')); + await testBinUtils.exec('nsenter', natCommand); + } catch (e) { + logger.error(e.message); + } } /** * Setup Symmetric NAT for a namespace (on the router namespace) */ -function setupNATEndpointDependentMapping( +async function setupNATEndpointDependentMapping( usrnsPid: number, routerNsPid: number, routerExt: string, + logger: Logger = new Logger(setupNATEndpointDependentMapping.name), ) { - const command = nsenter(usrnsPid, routerNsPid).concat( - 'iptables --table nat ', - '--append POSTROUTING ', - '--protocol udp ', - `--out-interface ${routerExt} `, - '--jump MASQUERADE ', + const command = [ + ...nsenter(usrnsPid, routerNsPid), + 'iptables', + '--table', + 'nat', + '--append', + 'POSTROUTING', + '--protocol', + 'udp', + '--out-interface', + routerExt, + '--jump', + 'MASQUERADE', `--random`, - ); - child_process.exec(command); + ]; + try { + logger.info(['nsenter', ...command].join(' ')); + await testBinUtils.exec('nsenter', command); + } catch (e) { + logger.error(e.message); + } } /** * Setup Port-Restricted Cone NAT for a namespace (on the router namespace) */ -function setupNATSimplifiedEDMAgent( +async function setupNATSimplifiedEDMAgent( usrnsPid: number, routerNsPid: number, agentIp: string, routerExt: string, routerInt: string, + logger: Logger = new Logger(setupNATSimplifiedEDMAgent.name), ) { - const natCommand = nsenter(usrnsPid, routerNsPid).concat( - 'iptables --table nat ', - '--append POSTROUTING ', - '--protocol udp ', - `--source ${agentIp}${subnetMask} `, - `--out-interface ${routerExt} `, - '--jump MASQUERADE ', - '--to-ports 44444', - ); - const acceptLocalCommand = nsenter(usrnsPid, routerNsPid).concat( - 'iptables --table filter ', - '--append INPUT ', - `--in-interface ${routerInt} `, - '--jump ACCEPT', - ); - const acceptEstablishedCommand = nsenter(usrnsPid, routerNsPid).concat( - 'iptables --table filter ', - '--append INPUT ', - `--match conntrack `, - '--cstate RELATED,ESTABLISHED ', - '--jump ACCEPT', - ); - const dropCommand = nsenter(usrnsPid, routerNsPid).concat( - 'iptables --table filter ', - '--append INPUT ', - '--jump DROP', - ); - child_process.exec(acceptLocalCommand); - child_process.exec(acceptEstablishedCommand); - child_process.exec(dropCommand); - child_process.exec(natCommand); + const natCommand = [ + ...nsenter(usrnsPid, routerNsPid), + 'iptables', + '--table', + 'nat', + '--append', + 'POSTROUTING', + '--protocol', + 'udp', + '--source', + `${agentIp}${subnetMask}`, + '--out-interface', + routerExt, + '--jump', + 'MASQUERADE', + '--to-ports', + '44444', + ]; + const acceptLocalCommand = [ + ...nsenter(usrnsPid, routerNsPid), + 'iptables', + '--table', + 'filter', + '--append', + 'INPUT', + '--in-interface', + routerInt, + '--jump', + 'ACCEPT', + ]; + const acceptEstablishedCommand = [ + ...nsenter(usrnsPid, routerNsPid), + 'iptables', + '--table', + 'filter', + '--append', + 'INPUT', + '--match', + 'conntrack', + '--ctstate', + 'RELATED,ESTABLISHED', + '--jump', + 'ACCEPT', + ]; + const dropCommand = [ + ...nsenter(usrnsPid, routerNsPid), + 'iptables', + '--table', + 'filter', + '--append', + 'INPUT', + '--jump', + 'DROP', + ]; + try { + logger.info(['nsenter', ...acceptLocalCommand].join(' ')); + await testBinUtils.exec('nsenter', acceptLocalCommand); + logger.info(['nsenter', ...acceptEstablishedCommand].join(' ')); + await testBinUtils.exec('nsenter', acceptEstablishedCommand); + logger.info(['nsenter', ...dropCommand].join(' ')); + await testBinUtils.exec('nsenter', dropCommand); + logger.info(['nsenter', ...natCommand].join(' ')); + await testBinUtils.exec('nsenter', natCommand); + } catch (e) { + logger.error(e.message); + } } /** * Setup Port-Restricted Cone NAT for a namespace (on the router namespace) */ -function setupNATSimplifiedEDMSeed( +async function setupNATSimplifiedEDMSeed( usrnsPid: number, routerNsPid: number, agentIp: string, routerExt: string, + logger: Logger = new Logger(setupNATSimplifiedEDMSeed.name), ) { - const natCommand = nsenter(usrnsPid, routerNsPid).concat( - 'iptables --table nat ', - '--append POSTROUTING ', - '--protocol udp ', - `--source ${agentIp}${subnetMask} `, - `--out-interface ${routerExt} `, - '--jump MASQUERADE ', - '--to-ports 55555', - ); - child_process.exec(natCommand); + const natCommand = [ + ...nsenter(usrnsPid, routerNsPid), + 'iptables', + '--table', + 'nat', + '--append', + 'POSTROUTING', + '--protocol', + 'udp', + '--source', + `${agentIp}${subnetMask}`, + '--out-interface', + routerExt, + '--jump', + 'MASQUERADE', + '--to-ports', + '55555', + ]; + try { + logger.info(['nsenter', ...natCommand].join(' ')); + await testBinUtils.exec('nsenter', natCommand); + } catch (e) { + logger.error(e.message); + } } async function setupNATWithSeedNode( @@ -598,159 +1079,177 @@ async function setupNATWithSeedNode( const password = 'password'; // Create a user namespace containing five network namespaces // Two agents, two routers, one seed node - const usrns = createUserNamespace(); - const seedNetns = createNetworkNamespace(usrns.pid!); - const agent1Netns = createNetworkNamespace(usrns.pid!); - const agent2Netns = createNetworkNamespace(usrns.pid!); - const router1Netns = createNetworkNamespace(usrns.pid!); - const router2Netns = createNetworkNamespace(usrns.pid!); + const usrns = createUserNamespace(logger); + const seedNetns = createNetworkNamespace(usrns.pid!, logger); + const agent1Netns = createNetworkNamespace(usrns.pid!, logger); + const agent2Netns = createNetworkNamespace(usrns.pid!, logger); + const router1Netns = createNetworkNamespace(usrns.pid!, logger); + const router2Netns = createNetworkNamespace(usrns.pid!, logger); // Apply appropriate NAT rules switch (agent1NAT) { case 'dmz': { - setupDMZ( + await setupDMZ( usrns.pid!, router1Netns.pid!, agent1HostIp, agent1Port, agent1RouterHostExt, agent1RouterHostExtIp, + logger, ); - setupDMZ( + await setupDMZ( usrns.pid!, router1Netns.pid!, agent1HostIp, agent1Port, router1SeedHost, router1SeedHostIp, + logger, ); break; } case 'eim': { - setupNATEndpointIndependentMapping( + await setupNATEndpointIndependentMapping( usrns.pid!, router1Netns.pid!, agent1HostIp, agent1RouterHostExt, agent1RouterHostInt, + logger, ); - setupNATEndpointIndependentMapping( + await setupNATEndpointIndependentMapping( usrns.pid!, router1Netns.pid!, agent1HostIp, router1SeedHost, agent1RouterHostInt, + logger, ); break; } case 'edm': { - setupNATEndpointDependentMapping( + await setupNATEndpointDependentMapping( usrns.pid!, router1Netns.pid!, agent1RouterHostExt, + logger, ); - setupNATEndpointDependentMapping( + await setupNATEndpointDependentMapping( usrns.pid!, router1Netns.pid!, router1SeedHost, + logger, ); break; } case 'edmSimple': { - setupNATSimplifiedEDMAgent( + await setupNATSimplifiedEDMAgent( usrns.pid!, router1Netns.pid!, agent1HostIp, agent1RouterHostExt, agent1RouterHostInt, + logger, ); - setupNATSimplifiedEDMSeed( + await setupNATSimplifiedEDMSeed( usrns.pid!, router1Netns.pid!, agent1HostIp, router1SeedHost, + logger, ); break; } } switch (agent2NAT) { case 'dmz': { - setupDMZ( + await setupDMZ( usrns.pid!, router2Netns.pid!, agent2HostIp, agent2Port, agent2RouterHostExt, agent2RouterHostExtIp, + logger, ); - setupDMZ( + await setupDMZ( usrns.pid!, router2Netns.pid!, agent2HostIp, agent2Port, router2SeedHost, router2SeedHostIp, + logger, ); break; } case 'eim': { - setupNATEndpointIndependentMapping( + await setupNATEndpointIndependentMapping( usrns.pid!, router2Netns.pid!, agent2HostIp, agent2RouterHostExt, agent2RouterHostInt, + logger, ); - setupNATEndpointIndependentMapping( + await setupNATEndpointIndependentMapping( usrns.pid!, router2Netns.pid!, agent2HostIp, router2SeedHost, agent2RouterHostInt, + logger, ); break; } case 'edm': { - setupNATEndpointDependentMapping( + await setupNATEndpointDependentMapping( usrns.pid!, router2Netns.pid!, agent2RouterHostExt, + logger, ); - setupNATEndpointDependentMapping( + await setupNATEndpointDependentMapping( usrns.pid!, router2Netns.pid!, router2SeedHost, + logger, ); break; } case 'edmSimple': { - setupNATSimplifiedEDMAgent( + await setupNATSimplifiedEDMAgent( usrns.pid!, router2Netns.pid!, agent2HostIp, agent2RouterHostExt, agent2RouterHostInt, + logger, ); - setupNATSimplifiedEDMSeed( + await setupNATSimplifiedEDMSeed( usrns.pid!, router2Netns.pid!, agent2HostIp, router2SeedHost, + logger, ); break; } } - setupNetworkNamespaceInterfaces( + await setupNetworkNamespaceInterfaces( usrns.pid!, agent1Netns.pid!, router1Netns.pid!, router2Netns.pid!, agent2Netns.pid!, + logger, ); - setupSeedNamespaceInterfaces( + await setupSeedNamespaceInterfaces( usrns.pid!, seedNetns.pid!, router1Netns.pid!, router2Netns.pid!, + logger, ); const seedNode = await pkSpawnNs( usrns.pid!, @@ -939,100 +1438,109 @@ async function setupNAT( const password = 'password'; // Create a user namespace containing four network namespaces // Two agents and two routers - const usrns = createUserNamespace(); - const agent1Netns = createNetworkNamespace(usrns.pid!); - const agent2Netns = createNetworkNamespace(usrns.pid!); - const router1Netns = createNetworkNamespace(usrns.pid!); - const router2Netns = createNetworkNamespace(usrns.pid!); + const usrns = createUserNamespace(logger); + const agent1Netns = createNetworkNamespace(usrns.pid!, logger); + const agent2Netns = createNetworkNamespace(usrns.pid!, logger); + const router1Netns = createNetworkNamespace(usrns.pid!, logger); + const router2Netns = createNetworkNamespace(usrns.pid!, logger); // Apply appropriate NAT rules switch (agent1NAT) { case 'dmz': { - setupDMZ( + await setupDMZ( usrns.pid!, router1Netns.pid!, agent1HostIp, agent1Port, agent1RouterHostExt, agent1RouterHostExtIp, + logger, ); break; } case 'eim': { - setupNATEndpointIndependentMapping( + await setupNATEndpointIndependentMapping( usrns.pid!, router1Netns.pid!, agent1HostIp, agent1RouterHostExt, agent1RouterHostInt, + logger, ); break; } case 'edm': { - setupNATEndpointDependentMapping( + await setupNATEndpointDependentMapping( usrns.pid!, router1Netns.pid!, agent1RouterHostExt, + logger, ); break; } case 'edmSimple': { - setupNATSimplifiedEDMAgent( + await setupNATSimplifiedEDMAgent( usrns.pid!, router1Netns.pid!, agent1HostIp, agent1RouterHostExt, agent1RouterHostInt, + logger, ); break; } } switch (agent2NAT) { case 'dmz': { - setupDMZ( + await setupDMZ( usrns.pid!, router2Netns.pid!, agent2HostIp, agent2Port, agent2RouterHostExt, agent2RouterHostExtIp, + logger, ); break; } case 'eim': { - setupNATEndpointIndependentMapping( + await setupNATEndpointIndependentMapping( usrns.pid!, router2Netns.pid!, agent2HostIp, agent2RouterHostExt, agent2RouterHostInt, + logger, ); break; } case 'edm': { - setupNATEndpointDependentMapping( + await setupNATEndpointDependentMapping( usrns.pid!, router2Netns.pid!, agent2RouterHostExt, + logger, ); break; } case 'edmSimple': { - setupNATSimplifiedEDMAgent( + await setupNATSimplifiedEDMAgent( usrns.pid!, router2Netns.pid!, agent2HostIp, agent2RouterHostExt, agent2RouterHostInt, + logger, ); break; } } - setupNetworkNamespaceInterfaces( + await setupNetworkNamespaceInterfaces( usrns.pid!, agent1Netns.pid!, router1Netns.pid!, router2Netns.pid!, agent2Netns.pid!, + logger, ); const agent1 = await pkSpawnNs( usrns.pid!, diff --git a/tests/utils.ts b/tests/utils.ts index 3ac9a7499..e607faff1 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -198,9 +198,27 @@ const expectRemoteError = async ( } }; +function describeIf(condition, name, f) { + if (condition) { + describe(name, f); + } else { + describe.skip(name, f); + } +} + +function testIf(condition, name, f, timeout?) { + if (condition) { + test(name, f, timeout); + } else { + test.skip(name, f, timeout); + } +} + export { setupGlobalKeypair, generateRandomNodeId, expectRemoteError, setupGlobalAgent, + describeIf, + testIf, }; From 8cf52a4fd348e42dec5ed1bddc9cbf18ac165d23 Mon Sep 17 00:00:00 2001 From: Emma Casolin Date: Tue, 21 Jun 2022 15:07:37 +1000 Subject: [PATCH 03/15] feat: better logging for service handler errors If an error was logged out by a service handler it would previously appear as `createClientService:`, which was too vague. The logger will now state the name of the handler. --- src/PolykeyAgent.ts | 2 +- src/agent/service/index.ts | 2 +- src/agent/service/nodesChainDataGet.ts | 2 +- src/agent/service/nodesClosestLocalNodesGet.ts | 2 +- src/agent/service/nodesCrossSignClaim.ts | 2 +- src/agent/service/nodesHolePunchMessageSend.ts | 3 ++- src/agent/service/notificationsSend.ts | 2 +- src/agent/service/vaultsGitInfoGet.ts | 2 +- src/agent/service/vaultsGitPackGet.ts | 2 +- src/agent/service/vaultsScan.ts | 2 +- src/client/GRPCClientClient.ts | 2 +- src/client/service/agentLockAll.ts | 2 +- src/client/service/agentStatus.ts | 2 +- src/client/service/agentStop.ts | 2 +- src/client/service/agentUnlock.ts | 2 +- .../service/gestaltsActionsGetByIdentity.ts | 2 +- src/client/service/gestaltsActionsGetByNode.ts | 2 +- .../service/gestaltsActionsSetByIdentity.ts | 2 +- src/client/service/gestaltsActionsSetByNode.ts | 2 +- .../service/gestaltsActionsUnsetByIdentity.ts | 2 +- src/client/service/gestaltsActionsUnsetByNode.ts | 2 +- .../service/gestaltsDiscoveryByIdentity.ts | 2 +- src/client/service/gestaltsDiscoveryByNode.ts | 2 +- .../service/gestaltsGestaltGetByIdentity.ts | 2 +- src/client/service/gestaltsGestaltGetByNode.ts | 2 +- src/client/service/gestaltsGestaltList.ts | 2 +- .../service/gestaltsGestaltTrustByIdentity.ts | 2 +- src/client/service/gestaltsGestaltTrustByNode.ts | 2 +- src/client/service/identitiesAuthenticate.ts | 2 +- src/client/service/identitiesAuthenticatedGet.ts | 2 +- src/client/service/identitiesClaim.ts | 2 +- src/client/service/identitiesInfoConnectedGet.ts | 2 +- src/client/service/identitiesInfoGet.ts | 2 +- src/client/service/identitiesProvidersList.ts | 2 +- src/client/service/identitiesTokenDelete.ts | 2 +- src/client/service/identitiesTokenGet.ts | 2 +- src/client/service/identitiesTokenPut.ts | 2 +- src/client/service/index.ts | 2 +- src/client/service/keysCertsChainGet.ts | 2 +- src/client/service/keysCertsGet.ts | 2 +- src/client/service/keysDecrypt.ts | 2 +- src/client/service/keysEncrypt.ts | 2 +- src/client/service/keysKeyPairRenew.ts | 2 +- src/client/service/keysKeyPairReset.ts | 2 +- src/client/service/keysKeyPairRoot.ts | 2 +- src/client/service/keysPasswordChange.ts | 2 +- src/client/service/keysSign.ts | 2 +- src/client/service/keysVerify.ts | 2 +- src/client/service/nodesAdd.ts | 2 +- src/client/service/nodesClaim.ts | 2 +- src/client/service/nodesFind.ts | 2 +- src/client/service/nodesGetAll.ts | 16 ++++++++++++---- src/client/service/nodesPing.ts | 2 +- src/client/service/notificationsClear.ts | 2 +- src/client/service/notificationsRead.ts | 2 +- src/client/service/notificationsSend.ts | 2 +- src/client/service/vaultsClone.ts | 2 +- src/client/service/vaultsCreate.ts | 2 +- src/client/service/vaultsDelete.ts | 2 +- src/client/service/vaultsList.ts | 2 +- src/client/service/vaultsLog.ts | 2 +- src/client/service/vaultsPermissionGet.ts | 2 +- src/client/service/vaultsPermissionSet.ts | 2 +- src/client/service/vaultsPermissionUnset.ts | 2 +- src/client/service/vaultsPull.ts | 2 +- src/client/service/vaultsRename.ts | 2 +- src/client/service/vaultsScan.ts | 2 +- src/client/service/vaultsSecretsDelete.ts | 2 +- src/client/service/vaultsSecretsEdit.ts | 2 +- src/client/service/vaultsSecretsGet.ts | 2 +- src/client/service/vaultsSecretsList.ts | 2 +- src/client/service/vaultsSecretsMkdir.ts | 2 +- src/client/service/vaultsSecretsNew.ts | 2 +- src/client/service/vaultsSecretsNewDir.ts | 2 +- src/client/service/vaultsSecretsRename.ts | 2 +- src/client/service/vaultsSecretsStat.ts | 2 +- src/client/service/vaultsVersion.ts | 2 +- 77 files changed, 89 insertions(+), 80 deletions(-) diff --git a/src/PolykeyAgent.ts b/src/PolykeyAgent.ts index e3f033c71..1e905fa9e 100644 --- a/src/PolykeyAgent.ts +++ b/src/PolykeyAgent.ts @@ -627,7 +627,7 @@ class PolykeyAgent { grpcServerAgent: this.grpcServerAgent, proxy: this.proxy, fs: this.fs, - logger: this.logger.getChild(createClientService.name), + logger: this.logger.getChild('GRPCClientClientService'), }); // Starting modules await this.keyManager.start({ diff --git a/src/agent/service/index.ts b/src/agent/service/index.ts index 6342c2ba5..2e51b699e 100644 --- a/src/agent/service/index.ts +++ b/src/agent/service/index.ts @@ -27,7 +27,7 @@ import * as agentUtils from '../utils'; function createService({ proxy, db, - logger = new Logger(createService.name), + logger = new Logger('GRPCClientAgentService'), ...containerRest }: { db: DB; diff --git a/src/agent/service/nodesChainDataGet.ts b/src/agent/service/nodesChainDataGet.ts index e0f838b33..5d0f263ed 100644 --- a/src/agent/service/nodesChainDataGet.ts +++ b/src/agent/service/nodesChainDataGet.ts @@ -51,7 +51,7 @@ function nodesChainDataGet({ return; } catch (e) { callback(grpcUtils.fromError(e, true)); - !agentUtils.isAgentClientError(e) && logger.error(e); + !agentUtils.isAgentClientError(e) && logger.error(`${nodesChainDataGet.name}:${e}`); return; } }; diff --git a/src/agent/service/nodesClosestLocalNodesGet.ts b/src/agent/service/nodesClosestLocalNodesGet.ts index 36a172b12..9cb9f45f6 100644 --- a/src/agent/service/nodesClosestLocalNodesGet.ts +++ b/src/agent/service/nodesClosestLocalNodesGet.ts @@ -63,7 +63,7 @@ function nodesClosestLocalNodesGet({ return; } catch (e) { callback(grpcUtils.fromError(e, true)); - !agentUtils.isAgentClientError(e) && logger.error(e); + !agentUtils.isAgentClientError(e) && logger.error(`${nodesClosestLocalNodesGet.name}:${e}`); return; } }; diff --git a/src/agent/service/nodesCrossSignClaim.ts b/src/agent/service/nodesCrossSignClaim.ts index 20d5ac575..2c9793ba0 100644 --- a/src/agent/service/nodesCrossSignClaim.ts +++ b/src/agent/service/nodesCrossSignClaim.ts @@ -179,7 +179,7 @@ function nodesCrossSignClaim({ claimsErrors.ErrorUndefinedSignature, claimsErrors.ErrorNodesClaimType, claimsErrors.ErrorUndefinedDoublySignedClaim, - ]) && logger.error(e); + ]) && logger.error(`${nodesCrossSignClaim.name}:${e}`); return; } }; diff --git a/src/agent/service/nodesHolePunchMessageSend.ts b/src/agent/service/nodesHolePunchMessageSend.ts index 088787264..5e388809b 100644 --- a/src/agent/service/nodesHolePunchMessageSend.ts +++ b/src/agent/service/nodesHolePunchMessageSend.ts @@ -55,6 +55,7 @@ function nodesHolePunchMessageSend({ sourceId: call.request.getSrcId(), }, ); + logger.info('IN AGENT CLIENT'); // Firstly, check if this node is the desired node // If so, then we want to make this node start sending hole punching packets // back to the source node. @@ -74,7 +75,7 @@ function nodesHolePunchMessageSend({ return; } catch (e) { callback(grpcUtils.fromError(e, true)); - !agentUtils.isAgentClientError(e) && logger.error(e); + !agentUtils.isAgentClientError(e) && logger.error(`${nodesHolePunchMessageSend.name}:${e}`); return; } }; diff --git a/src/agent/service/notificationsSend.ts b/src/agent/service/notificationsSend.ts index eb336243c..cd1b43c76 100644 --- a/src/agent/service/notificationsSend.ts +++ b/src/agent/service/notificationsSend.ts @@ -41,7 +41,7 @@ function notificationsSend({ notificationsErrors.ErrorNotificationsValidationFailed, notificationsErrors.ErrorNotificationsParse, notificationsErrors.ErrorNotificationsPermissionsNotFound, - ]) && logger.error(e); + ]) && logger.error(`${notificationsSend.name}:${e}`); return; } }; diff --git a/src/agent/service/vaultsGitInfoGet.ts b/src/agent/service/vaultsGitInfoGet.ts index 7fe9831ff..0fb18c96a 100644 --- a/src/agent/service/vaultsGitInfoGet.ts +++ b/src/agent/service/vaultsGitInfoGet.ts @@ -111,7 +111,7 @@ function vaultsGitInfoGet({ vaultsErrors.ErrorVaultsVaultUndefined, agentErrors.ErrorConnectionInfoMissing, vaultsErrors.ErrorVaultsPermissionDenied, - ]) && logger.error(e); + ]) && logger.error(`${vaultsGitInfoGet.name}:${e}`); return; } }; diff --git a/src/agent/service/vaultsGitPackGet.ts b/src/agent/service/vaultsGitPackGet.ts index 50db9da28..f8aa5dc3d 100644 --- a/src/agent/service/vaultsGitPackGet.ts +++ b/src/agent/service/vaultsGitPackGet.ts @@ -124,7 +124,7 @@ function vaultsGitPackGet({ agentErrors.ErrorConnectionInfoMissing, vaultsErrors.ErrorVaultsPermissionDenied, vaultsErrors.ErrorVaultsVaultUndefined, - ]) && logger.error(e); + ]) && logger.error(`${vaultsGitPackGet.name}:${e}`); return; } }; diff --git a/src/agent/service/vaultsScan.ts b/src/agent/service/vaultsScan.ts index f1a94c2a9..f82719108 100644 --- a/src/agent/service/vaultsScan.ts +++ b/src/agent/service/vaultsScan.ts @@ -56,7 +56,7 @@ function vaultsScan({ !agentUtils.isAgentClientError(e, [ agentErrors.ErrorConnectionInfoMissing, vaultsErrors.ErrorVaultsPermissionDenied, - ]) && logger.error(e); + ]) && logger.error(`${vaultsScan.name}:${e}`); return; } }; diff --git a/src/client/GRPCClientClient.ts b/src/client/GRPCClientClient.ts index 8b9816536..2a0a4626f 100644 --- a/src/client/GRPCClientClient.ts +++ b/src/client/GRPCClientClient.ts @@ -909,7 +909,7 @@ class GRPCClientClient extends GRPCClient { nodeId: this.nodeId, host: this.host, port: this.port, - command: this.identitiesAuthenticate.name, + command: this.nodesGetAll.name, }, this.client.nodesGetAll, )(...args); diff --git a/src/client/service/agentLockAll.ts b/src/client/service/agentLockAll.ts index 29e64bcad..9bb315c8f 100644 --- a/src/client/service/agentLockAll.ts +++ b/src/client/service/agentLockAll.ts @@ -33,7 +33,7 @@ function agentLockAll({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(e); + !clientUtils.isClientClientError(e) && logger.error(`${agentLockAll.name}:${e}`); return; } }; diff --git a/src/client/service/agentStatus.ts b/src/client/service/agentStatus.ts index 110555cf8..4ddb3b316 100644 --- a/src/client/service/agentStatus.ts +++ b/src/client/service/agentStatus.ts @@ -50,7 +50,7 @@ function agentStatus({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(e); + !clientUtils.isClientClientError(e) && logger.error(`${agentStatus.name}:${e}`); return; } }; diff --git a/src/client/service/agentStop.ts b/src/client/service/agentStop.ts index f88885f09..e6a640d43 100644 --- a/src/client/service/agentStop.ts +++ b/src/client/service/agentStop.ts @@ -33,7 +33,7 @@ function agentStop({ callback(null, response); } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(e); + !clientUtils.isClientClientError(e) && logger.error(`${agentStop.name}:${e}`); return; } // Stop is called after GRPC resources are cleared diff --git a/src/client/service/agentUnlock.ts b/src/client/service/agentUnlock.ts index 5147e2718..0480ab097 100644 --- a/src/client/service/agentUnlock.ts +++ b/src/client/service/agentUnlock.ts @@ -24,7 +24,7 @@ function agentUnlock({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(e); + !clientUtils.isClientClientError(e) && logger.error(`${agentUnlock.name}:${e}`); return; } }; diff --git a/src/client/service/gestaltsActionsGetByIdentity.ts b/src/client/service/gestaltsActionsGetByIdentity.ts index ef45d57e7..ec8e8e316 100644 --- a/src/client/service/gestaltsActionsGetByIdentity.ts +++ b/src/client/service/gestaltsActionsGetByIdentity.ts @@ -63,7 +63,7 @@ function gestaltsActionsGetByIdentity({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(e); + !clientUtils.isClientClientError(e) && logger.error(`${gestaltsActionsGetByIdentity.name}:${e}`); return; } }; diff --git a/src/client/service/gestaltsActionsGetByNode.ts b/src/client/service/gestaltsActionsGetByNode.ts index b5652d3bb..ba572d818 100644 --- a/src/client/service/gestaltsActionsGetByNode.ts +++ b/src/client/service/gestaltsActionsGetByNode.ts @@ -57,7 +57,7 @@ function gestaltsActionsGetByNode({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(e); + !clientUtils.isClientClientError(e) && logger.error(`${gestaltsActionsGetByNode.name}:${e}`); return; } }; diff --git a/src/client/service/gestaltsActionsSetByIdentity.ts b/src/client/service/gestaltsActionsSetByIdentity.ts index 0628b2a98..b60d3aa84 100644 --- a/src/client/service/gestaltsActionsSetByIdentity.ts +++ b/src/client/service/gestaltsActionsSetByIdentity.ts @@ -71,7 +71,7 @@ function gestaltsActionsSetByIdentity({ !clientUtils.isClientClientError(e, [ gestaltsErrors.ErrorGestaltsGraphIdentityIdMissing, gestaltsErrors.ErrorGestaltsGraphNodeIdMissing, - ]) && logger.error(e); + ]) && logger.error(`${gestaltsActionsSetByIdentity.name}:${e}`); return; } }; diff --git a/src/client/service/gestaltsActionsSetByNode.ts b/src/client/service/gestaltsActionsSetByNode.ts index 2aa9a7050..187c634a7 100644 --- a/src/client/service/gestaltsActionsSetByNode.ts +++ b/src/client/service/gestaltsActionsSetByNode.ts @@ -56,7 +56,7 @@ function gestaltsActionsSetByNode({ callback(grpcUtils.fromError(e)); !clientUtils.isClientClientError(e, [ gestaltsErrors.ErrorGestaltsGraphNodeIdMissing, - ]) && logger.error(e); + ]) && logger.error(`${gestaltsActionsSetByNode.name}:${e}`); return; } }; diff --git a/src/client/service/gestaltsActionsUnsetByIdentity.ts b/src/client/service/gestaltsActionsUnsetByIdentity.ts index 88745bc64..b2467bee5 100644 --- a/src/client/service/gestaltsActionsUnsetByIdentity.ts +++ b/src/client/service/gestaltsActionsUnsetByIdentity.ts @@ -71,7 +71,7 @@ function gestaltsActionsUnsetByIdentity({ !clientUtils.isClientClientError(e, [ gestaltsErrors.ErrorGestaltsGraphIdentityIdMissing, gestaltsErrors.ErrorGestaltsGraphNodeIdMissing, - ]) && logger.error(e); + ]) && logger.error(`${gestaltsActionsUnsetByIdentity.name}:${e}`); return; } }; diff --git a/src/client/service/gestaltsActionsUnsetByNode.ts b/src/client/service/gestaltsActionsUnsetByNode.ts index d7a62d68a..bc39dc569 100644 --- a/src/client/service/gestaltsActionsUnsetByNode.ts +++ b/src/client/service/gestaltsActionsUnsetByNode.ts @@ -56,7 +56,7 @@ function gestaltsActionsUnsetByNode({ callback(grpcUtils.fromError(e)); !clientUtils.isClientClientError(e, [ gestaltsErrors.ErrorGestaltsGraphNodeIdMissing, - ]) && logger.error(e); + ]) && logger.error(`${gestaltsActionsUnsetByNode.name}:${e}`); return; } }; diff --git a/src/client/service/gestaltsDiscoveryByIdentity.ts b/src/client/service/gestaltsDiscoveryByIdentity.ts index 37efbf7d3..18bcb2ee8 100644 --- a/src/client/service/gestaltsDiscoveryByIdentity.ts +++ b/src/client/service/gestaltsDiscoveryByIdentity.ts @@ -52,7 +52,7 @@ function gestaltsDiscoveryByIdentity({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(e); + !clientUtils.isClientClientError(e) && logger.error(`${gestaltsDiscoveryByIdentity.name}:${e}`); return; } }; diff --git a/src/client/service/gestaltsDiscoveryByNode.ts b/src/client/service/gestaltsDiscoveryByNode.ts index 0c8d1ea4c..0584e55c3 100644 --- a/src/client/service/gestaltsDiscoveryByNode.ts +++ b/src/client/service/gestaltsDiscoveryByNode.ts @@ -48,7 +48,7 @@ function gestaltsDiscoveryByNode({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(e); + !clientUtils.isClientClientError(e) && logger.error(`${gestaltsDiscoveryByNode.name}:${e}`); return; } }; diff --git a/src/client/service/gestaltsGestaltGetByIdentity.ts b/src/client/service/gestaltsGestaltGetByIdentity.ts index 81e3ffc54..b81df554d 100644 --- a/src/client/service/gestaltsGestaltGetByIdentity.ts +++ b/src/client/service/gestaltsGestaltGetByIdentity.ts @@ -60,7 +60,7 @@ function gestaltsGestaltGetByIdentity({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(e); + !clientUtils.isClientClientError(e) && logger.error(`${gestaltsGestaltGetByIdentity.name}:${e}`); return; } }; diff --git a/src/client/service/gestaltsGestaltGetByNode.ts b/src/client/service/gestaltsGestaltGetByNode.ts index fb3e63f56..48aff4729 100644 --- a/src/client/service/gestaltsGestaltGetByNode.ts +++ b/src/client/service/gestaltsGestaltGetByNode.ts @@ -56,7 +56,7 @@ function gestaltsGestaltGetByNode({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(e); + !clientUtils.isClientClientError(e) && logger.error(`${gestaltsGestaltGetByNode.name}:${e}`); return; } }; diff --git a/src/client/service/gestaltsGestaltList.ts b/src/client/service/gestaltsGestaltList.ts index 7b4725892..c5d49123c 100644 --- a/src/client/service/gestaltsGestaltList.ts +++ b/src/client/service/gestaltsGestaltList.ts @@ -40,7 +40,7 @@ function gestaltsGestaltList({ return; } catch (e) { await genWritable.throw(e); - !clientUtils.isClientClientError(e) && logger.error(e); + !clientUtils.isClientClientError(e) && logger.error(`${gestaltsGestaltList.name}:${e}`); return; } }; diff --git a/src/client/service/gestaltsGestaltTrustByIdentity.ts b/src/client/service/gestaltsGestaltTrustByIdentity.ts index 36a0aa982..06a9eb6c4 100644 --- a/src/client/service/gestaltsGestaltTrustByIdentity.ts +++ b/src/client/service/gestaltsGestaltTrustByIdentity.ts @@ -87,7 +87,7 @@ function gestaltsGestaltTrustByIdentity({ !clientUtils.isClientClientError(e, [ gestaltsErrors.ErrorGestaltsGraphIdentityIdMissing, gestaltsErrors.ErrorGestaltsGraphNodeIdMissing, - ]) && logger.error(e); + ]) && logger.error(`${gestaltsGestaltTrustByIdentity.name}:${e}`); return; } }; diff --git a/src/client/service/gestaltsGestaltTrustByNode.ts b/src/client/service/gestaltsGestaltTrustByNode.ts index 8fa7299a0..4e01de903 100644 --- a/src/client/service/gestaltsGestaltTrustByNode.ts +++ b/src/client/service/gestaltsGestaltTrustByNode.ts @@ -73,7 +73,7 @@ function gestaltsGestaltTrustByNode({ callback(grpcUtils.fromError(e)); !clientUtils.isClientClientError(e, [ gestaltsErrors.ErrorGestaltsGraphNodeIdMissing, - ]) && logger.error(e); + ]) && logger.error(`${gestaltsGestaltTrustByNode.name}:${e}`); return; } }; diff --git a/src/client/service/identitiesAuthenticate.ts b/src/client/service/identitiesAuthenticate.ts index d9c216c6c..0950abddd 100644 --- a/src/client/service/identitiesAuthenticate.ts +++ b/src/client/service/identitiesAuthenticate.ts @@ -77,7 +77,7 @@ function identitiesAuthenticate({ await genWritable.throw(e); !clientUtils.isClientClientError(e, [ identitiesErrors.ErrorProviderMissing, - ]) && logger.error(e); + ]) && logger.error(`${identitiesAuthenticate.name}:${e}`); return; } }; diff --git a/src/client/service/identitiesAuthenticatedGet.ts b/src/client/service/identitiesAuthenticatedGet.ts index 3ab4cc941..cae567830 100644 --- a/src/client/service/identitiesAuthenticatedGet.ts +++ b/src/client/service/identitiesAuthenticatedGet.ts @@ -64,7 +64,7 @@ function identitiesAuthenticatedGet({ return; } catch (e) { await genWritable.throw(e); - !clientUtils.isClientClientError(e) && logger.error(e); + !clientUtils.isClientClientError(e) && logger.error(`${identitiesAuthenticatedGet.name}:${e}`); return; } }; diff --git a/src/client/service/identitiesClaim.ts b/src/client/service/identitiesClaim.ts index de5e1df57..6677c77d4 100644 --- a/src/client/service/identitiesClaim.ts +++ b/src/client/service/identitiesClaim.ts @@ -96,7 +96,7 @@ function identitiesClaim({ !clientUtils.isClientClientError(e, [ identitiesErrors.ErrorProviderMissing, identitiesErrors.ErrorProviderUnauthenticated, - ]) && logger.error(e); + ]) && logger.error(`${identitiesClaim.name}:${e}`); return; } }; diff --git a/src/client/service/identitiesInfoConnectedGet.ts b/src/client/service/identitiesInfoConnectedGet.ts index 63d681700..f8f906807 100644 --- a/src/client/service/identitiesInfoConnectedGet.ts +++ b/src/client/service/identitiesInfoConnectedGet.ts @@ -124,7 +124,7 @@ function identitiesInfoConnectedGet({ identitiesErrors.ErrorProviderMissing, identitiesErrors.ErrorProviderUnauthenticated, identitiesErrors.ErrorProviderUnimplemented, - ]) && logger.error(e); + ]) && logger.error(`${identitiesInfoConnectedGet.name}:${e}`); return; } }; diff --git a/src/client/service/identitiesInfoGet.ts b/src/client/service/identitiesInfoGet.ts index e8df1153b..3fa2bdbc1 100644 --- a/src/client/service/identitiesInfoGet.ts +++ b/src/client/service/identitiesInfoGet.ts @@ -116,7 +116,7 @@ function identitiesInfoGet({ !clientUtils.isClientClientError(e, [ identitiesErrors.ErrorProviderMissing, identitiesErrors.ErrorProviderUnauthenticated, - ]) && logger.error(e); + ]) && logger.error(`${identitiesInfoGet.name}:${e}`); return; } }; diff --git a/src/client/service/identitiesProvidersList.ts b/src/client/service/identitiesProvidersList.ts index 70cbc9ccd..752d9eea5 100644 --- a/src/client/service/identitiesProvidersList.ts +++ b/src/client/service/identitiesProvidersList.ts @@ -30,7 +30,7 @@ function identitiesProvidersList({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(e); + !clientUtils.isClientClientError(e) && logger.error(`${identitiesProvidersList.name}:${e}`); return; } }; diff --git a/src/client/service/identitiesTokenDelete.ts b/src/client/service/identitiesTokenDelete.ts index 779ffea47..3bf584d8d 100644 --- a/src/client/service/identitiesTokenDelete.ts +++ b/src/client/service/identitiesTokenDelete.ts @@ -57,7 +57,7 @@ function identitiesTokenDelete({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(e); + !clientUtils.isClientClientError(e) && logger.error(`${identitiesTokenDelete.name}:${e}`); return; } }; diff --git a/src/client/service/identitiesTokenGet.ts b/src/client/service/identitiesTokenGet.ts index 5ca76f585..41872941e 100644 --- a/src/client/service/identitiesTokenGet.ts +++ b/src/client/service/identitiesTokenGet.ts @@ -57,7 +57,7 @@ function identitiesTokenGet({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(e); + !clientUtils.isClientClientError(e) && logger.error(`${identitiesTokenGet.name}:${e}`); return; } }; diff --git a/src/client/service/identitiesTokenPut.ts b/src/client/service/identitiesTokenPut.ts index 261b88a12..d8a19ed9e 100644 --- a/src/client/service/identitiesTokenPut.ts +++ b/src/client/service/identitiesTokenPut.ts @@ -67,7 +67,7 @@ function identitiesTokenPut({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(e); + !clientUtils.isClientClientError(e) && logger.error(`${identitiesTokenPut.name}:${e}`); return; } }; diff --git a/src/client/service/index.ts b/src/client/service/index.ts index d6b1dff6f..68f98ac8c 100644 --- a/src/client/service/index.ts +++ b/src/client/service/index.ts @@ -91,7 +91,7 @@ function createService({ keyManager, sessionManager, db, - logger = new Logger(createService.name), + logger = new Logger('GRPCClientClientService'), fs = require('fs'), ...containerRest }: { diff --git a/src/client/service/keysCertsChainGet.ts b/src/client/service/keysCertsChainGet.ts index fed99bf2a..fabb933d4 100644 --- a/src/client/service/keysCertsChainGet.ts +++ b/src/client/service/keysCertsChainGet.ts @@ -34,7 +34,7 @@ function keysCertsChainGet({ return; } catch (e) { await genWritable.throw(e); - !clientUtils.isClientClientError(e) && logger.error(e); + !clientUtils.isClientClientError(e) && logger.error(`${keysCertsChainGet.name}:${e}`); return; } }; diff --git a/src/client/service/keysCertsGet.ts b/src/client/service/keysCertsGet.ts index 1b8222612..2b89ddb34 100644 --- a/src/client/service/keysCertsGet.ts +++ b/src/client/service/keysCertsGet.ts @@ -30,7 +30,7 @@ function keysCertsGet({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(e); + !clientUtils.isClientClientError(e) && logger.error(`${keysCertsGet.name}:${e}`); return; } }; diff --git a/src/client/service/keysDecrypt.ts b/src/client/service/keysDecrypt.ts index b1a0f6bad..fcd95f2ae 100644 --- a/src/client/service/keysDecrypt.ts +++ b/src/client/service/keysDecrypt.ts @@ -31,7 +31,7 @@ function keysDecrypt({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(e); + !clientUtils.isClientClientError(e) && logger.error(`${keysDecrypt.name}:${e}`); return; } }; diff --git a/src/client/service/keysEncrypt.ts b/src/client/service/keysEncrypt.ts index b8c5c60bb..9c9264dc6 100644 --- a/src/client/service/keysEncrypt.ts +++ b/src/client/service/keysEncrypt.ts @@ -31,7 +31,7 @@ function keysEncrypt({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(e); + !clientUtils.isClientClientError(e) && logger.error(`${keysEncrypt.name}:${e}`); return; } }; diff --git a/src/client/service/keysKeyPairRenew.ts b/src/client/service/keysKeyPairRenew.ts index ada854cad..89d03fd51 100644 --- a/src/client/service/keysKeyPairRenew.ts +++ b/src/client/service/keysKeyPairRenew.ts @@ -31,7 +31,7 @@ function keysKeyPairRenew({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(e); + !clientUtils.isClientClientError(e) && logger.error(`${keysKeyPairRenew.name}:${e}`); return; } }; diff --git a/src/client/service/keysKeyPairReset.ts b/src/client/service/keysKeyPairReset.ts index af3b25426..3f0b7bf33 100644 --- a/src/client/service/keysKeyPairReset.ts +++ b/src/client/service/keysKeyPairReset.ts @@ -31,7 +31,7 @@ function keysKeyPairReset({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(e); + !clientUtils.isClientClientError(e) && logger.error(`${keysKeyPairReset.name}:${e}`); return; } }; diff --git a/src/client/service/keysKeyPairRoot.ts b/src/client/service/keysKeyPairRoot.ts index 14f543d9f..8da9aa3b8 100644 --- a/src/client/service/keysKeyPairRoot.ts +++ b/src/client/service/keysKeyPairRoot.ts @@ -31,7 +31,7 @@ function keysKeyPairRoot({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(e); + !clientUtils.isClientClientError(e) && logger.error(`${keysKeyPairRoot.name}:${e}`); return; } }; diff --git a/src/client/service/keysPasswordChange.ts b/src/client/service/keysPasswordChange.ts index b1d116db5..d94a650ed 100644 --- a/src/client/service/keysPasswordChange.ts +++ b/src/client/service/keysPasswordChange.ts @@ -29,7 +29,7 @@ function keysPasswordChange({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(e); + !clientUtils.isClientClientError(e) && logger.error(`${keysPasswordChange.name}:${e}`); return; } }; diff --git a/src/client/service/keysSign.ts b/src/client/service/keysSign.ts index 9c3aa2ab3..77d2d4d34 100644 --- a/src/client/service/keysSign.ts +++ b/src/client/service/keysSign.ts @@ -32,7 +32,7 @@ function keysSign({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(e); + !clientUtils.isClientClientError(e) && logger.error(`${keysSign.name}:${e}`); return; } }; diff --git a/src/client/service/keysVerify.ts b/src/client/service/keysVerify.ts index 1c53fff74..6e7cee48e 100644 --- a/src/client/service/keysVerify.ts +++ b/src/client/service/keysVerify.ts @@ -33,7 +33,7 @@ function keysVerify({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(e); + !clientUtils.isClientClientError(e) && logger.error(`${keysVerify.name}:${e}`); return; } }; diff --git a/src/client/service/nodesAdd.ts b/src/client/service/nodesAdd.ts index 3b6043219..c5c1b8949 100644 --- a/src/client/service/nodesAdd.ts +++ b/src/client/service/nodesAdd.ts @@ -89,7 +89,7 @@ function nodesAdd({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(e); + !clientUtils.isClientClientError(e) && logger.error(`${nodesAdd.name}:${e}`); return; } }; diff --git a/src/client/service/nodesClaim.ts b/src/client/service/nodesClaim.ts index c9e1d6db9..991ed9548 100644 --- a/src/client/service/nodesClaim.ts +++ b/src/client/service/nodesClaim.ts @@ -79,7 +79,7 @@ function nodesClaim({ callback(grpcUtils.fromError(e)); !clientUtils.isClientClientError(e, [ nodesErrors.ErrorNodeGraphNodeIdNotFound, - ]) && logger.error(e); + ]) && logger.error(`${nodesClaim.name}:${e}`); return; } }; diff --git a/src/client/service/nodesFind.ts b/src/client/service/nodesFind.ts index 324e1c0e9..7b02f9347 100644 --- a/src/client/service/nodesFind.ts +++ b/src/client/service/nodesFind.ts @@ -62,7 +62,7 @@ function nodesFind({ callback(grpcUtils.fromError(e)); !clientUtils.isClientClientError(e, [ nodesErrors.ErrorNodeGraphNodeIdNotFound, - ]) && logger.error(e); + ]) && logger.error(`${nodesFind.name}:${e}`); return; } }; diff --git a/src/client/service/nodesGetAll.ts b/src/client/service/nodesGetAll.ts index 8c021a248..ad6ab9b6e 100644 --- a/src/client/service/nodesGetAll.ts +++ b/src/client/service/nodesGetAll.ts @@ -1,25 +1,30 @@ import type * as grpc from '@grpc/grpc-js'; +import type Logger from '@matrixai/logger'; import type { Authenticate } from '../types'; import type KeyManager from '../../keys/KeyManager'; import type { NodeId } from '../../nodes/types'; import type * as utilsPB from '../../proto/js/polykey/v1/utils/utils_pb'; import type NodeGraph from '../../nodes/NodeGraph'; import { IdInternal } from '@matrixai/id'; -import { utils as nodesUtils } from '../../nodes'; -import { utils as grpcUtils } from '../../grpc'; import * as nodesPB from '../../proto/js/polykey/v1/nodes/nodes_pb'; +import * as nodesUtils from '../../nodes/utils'; +import * as nodesErrors from '../../nodes/errors'; +import * as grpcUtils from '../../grpc/utils'; +import * as clientUtils from '../utils'; /** * Retrieves all nodes from all buckets in the NodeGraph. */ function nodesGetAll({ + authenticate, nodeGraph, keyManager, - authenticate, + logger, }: { + authenticate: Authenticate; nodeGraph: NodeGraph; keyManager: KeyManager; - authenticate: Authenticate; + logger: Logger; }) { return async ( call: grpc.ServerUnaryCall, @@ -62,6 +67,9 @@ function nodesGetAll({ return; } catch (e) { callback(grpcUtils.fromError(e)); + !clientUtils.isClientClientError(e, [ + nodesErrors.ErrorNodeGraphNodeIdNotFound, + ]) && logger.error(`${nodesGetAll.name}:${e}`); return; } }; diff --git a/src/client/service/nodesPing.ts b/src/client/service/nodesPing.ts index 5adc83f54..eaf69983c 100644 --- a/src/client/service/nodesPing.ts +++ b/src/client/service/nodesPing.ts @@ -55,7 +55,7 @@ function nodesPing({ callback(grpcUtils.fromError(e)); !clientUtils.isClientClientError(e, [ nodesErrors.ErrorNodeGraphNodeIdNotFound, - ]) && logger.error(e); + ]) && logger.error(`${nodesPing.name}:${e}`); return; } }; diff --git a/src/client/service/notificationsClear.ts b/src/client/service/notificationsClear.ts index acc05724e..dbbfc761a 100644 --- a/src/client/service/notificationsClear.ts +++ b/src/client/service/notificationsClear.ts @@ -33,7 +33,7 @@ function notificationsClear({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(e); + !clientUtils.isClientClientError(e) && logger.error(`${notificationsClear.name}:${e}`); return; } }; diff --git a/src/client/service/notificationsRead.ts b/src/client/service/notificationsRead.ts index 4e6cbb92d..051f517c8 100644 --- a/src/client/service/notificationsRead.ts +++ b/src/client/service/notificationsRead.ts @@ -75,7 +75,7 @@ function notificationsRead({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(e); + !clientUtils.isClientClientError(e) && logger.error(`${notificationsRead.name}:${e}`); return; } }; diff --git a/src/client/service/notificationsSend.ts b/src/client/service/notificationsSend.ts index fcdb72606..118d5edbf 100644 --- a/src/client/service/notificationsSend.ts +++ b/src/client/service/notificationsSend.ts @@ -58,7 +58,7 @@ function notificationsSend({ callback(grpcUtils.fromError(e)); !clientUtils.isClientClientError(e, [ nodesErrors.ErrorNodeGraphNodeIdNotFound, - ]) && logger.error(e); + ]) && logger.error(`${notificationsSend.name}:${e}`); return; } }; diff --git a/src/client/service/vaultsClone.ts b/src/client/service/vaultsClone.ts index ec60df21a..14b799837 100644 --- a/src/client/service/vaultsClone.ts +++ b/src/client/service/vaultsClone.ts @@ -68,7 +68,7 @@ function vaultsClone({ nodesErrors.ErrorNodeGraphNodeIdNotFound, vaultsErrors.ErrorVaultsNameConflict, [grpcErrors.ErrorPolykeyRemote, vaultsErrors.ErrorVaultsVaultUndefined], - ]) && logger.error(e); + ]) && logger.error(`${vaultsClone.name}:${e}`); return; } }; diff --git a/src/client/service/vaultsCreate.ts b/src/client/service/vaultsCreate.ts index c181b4b76..df7c6cfac 100644 --- a/src/client/service/vaultsCreate.ts +++ b/src/client/service/vaultsCreate.ts @@ -41,7 +41,7 @@ function vaultsCreate({ callback(grpcUtils.fromError(e)); !clientUtils.isClientClientError(e, [ vaultsErrors.ErrorVaultsVaultDefined, - ]) && logger.error(e); + ]) && logger.error(`${vaultsCreate.name}:${e}`); return; } }; diff --git a/src/client/service/vaultsDelete.ts b/src/client/service/vaultsDelete.ts index 61fbfbc27..8e04bf0ab 100644 --- a/src/client/service/vaultsDelete.ts +++ b/src/client/service/vaultsDelete.ts @@ -50,7 +50,7 @@ function vaultsDelete({ callback(grpcUtils.fromError(e)); !clientUtils.isClientClientError(e, [ vaultsErrors.ErrorVaultsVaultUndefined, - ]) && logger.error(e); + ]) && logger.error(`${vaultsDelete.name}:${e}`); return; } }; diff --git a/src/client/service/vaultsList.ts b/src/client/service/vaultsList.ts index 2e9f1a79a..f8c6a92fb 100644 --- a/src/client/service/vaultsList.ts +++ b/src/client/service/vaultsList.ts @@ -40,7 +40,7 @@ function vaultsList({ return; } catch (e) { await genWritable.throw(e); - !clientUtils.isClientClientError(e) && logger.error(e); + !clientUtils.isClientClientError(e) && logger.error(`${vaultsList.name}:${e}`); return; } }; diff --git a/src/client/service/vaultsLog.ts b/src/client/service/vaultsLog.ts index 32d014b05..c028006dc 100644 --- a/src/client/service/vaultsLog.ts +++ b/src/client/service/vaultsLog.ts @@ -69,7 +69,7 @@ function vaultsLog({ !clientUtils.isClientClientError(e, [ vaultsErrors.ErrorVaultsVaultUndefined, vaultsErrors.ErrorVaultReferenceInvalid, - ]) && logger.error(e); + ]) && logger.error(`${vaultsLog.name}:${e}`); return; } }; diff --git a/src/client/service/vaultsPermissionGet.ts b/src/client/service/vaultsPermissionGet.ts index 86377f531..e89d9b500 100644 --- a/src/client/service/vaultsPermissionGet.ts +++ b/src/client/service/vaultsPermissionGet.ts @@ -75,7 +75,7 @@ function vaultsPermissionGet({ await genWritable.throw(e); !clientUtils.isClientClientError(e, [ vaultsErrors.ErrorVaultsVaultUndefined, - ]) && logger.error(e); + ]) && logger.error(`${vaultsPermissionGet.name}:${e}`); return; } }; diff --git a/src/client/service/vaultsPermissionSet.ts b/src/client/service/vaultsPermissionSet.ts index 9bc2a3b8d..37b30c29a 100644 --- a/src/client/service/vaultsPermissionSet.ts +++ b/src/client/service/vaultsPermissionSet.ts @@ -103,7 +103,7 @@ function vaultsPermissionSet({ vaultsErrors.ErrorVaultsVaultUndefined, aclErrors.ErrorACLNodeIdMissing, nodesErrors.ErrorNodeGraphNodeIdNotFound, - ]) && logger.error(e); + ]) && logger.error(`${vaultsPermissionSet.name}:${e}`); return; } }; diff --git a/src/client/service/vaultsPermissionUnset.ts b/src/client/service/vaultsPermissionUnset.ts index 7906207a3..5aa386d6b 100644 --- a/src/client/service/vaultsPermissionUnset.ts +++ b/src/client/service/vaultsPermissionUnset.ts @@ -102,7 +102,7 @@ function vaultsPermissionUnset({ !clientUtils.isClientClientError(e, [ vaultsErrors.ErrorVaultsVaultUndefined, gestaltsErrors.ErrorGestaltsGraphNodeIdMissing, - ]) && logger.error(e); + ]) && logger.error(`${vaultsPermissionUnset.name}:${e}`); return; } }; diff --git a/src/client/service/vaultsPull.ts b/src/client/service/vaultsPull.ts index 00fa44880..ea83519a4 100644 --- a/src/client/service/vaultsPull.ts +++ b/src/client/service/vaultsPull.ts @@ -90,7 +90,7 @@ function vaultsPull({ vaultsErrors.ErrorVaultsVaultUndefined, nodesErrors.ErrorNodeGraphNodeIdNotFound, [grpcErrors.ErrorPolykeyRemote, vaultsErrors.ErrorVaultsVaultUndefined], - ]) && logger.error(e); + ]) && logger.error(`${vaultsPull.name}:${e}`); return; } }; diff --git a/src/client/service/vaultsRename.ts b/src/client/service/vaultsRename.ts index 29d2bb04b..21bcd7626 100644 --- a/src/client/service/vaultsRename.ts +++ b/src/client/service/vaultsRename.ts @@ -51,7 +51,7 @@ function vaultsRename({ !clientUtils.isClientClientError(e, [ vaultsErrors.ErrorVaultsVaultUndefined, vaultsErrors.ErrorVaultsVaultDefined, - ]) && logger.error(e); + ]) && logger.error(`${vaultsRename.name}:${e}`); return; } }; diff --git a/src/client/service/vaultsScan.ts b/src/client/service/vaultsScan.ts index c7a0def19..248a3c5d8 100644 --- a/src/client/service/vaultsScan.ts +++ b/src/client/service/vaultsScan.ts @@ -60,7 +60,7 @@ function vaultsScan({ await genWritable.throw(e); !clientUtils.isClientClientError(e, [ nodesErrors.ErrorNodeGraphNodeIdNotFound, - ]) && logger.error(e); + ]) && logger.error(`${vaultsScan.name}:${e}`); return; } }; diff --git a/src/client/service/vaultsSecretsDelete.ts b/src/client/service/vaultsSecretsDelete.ts index 625c03cab..c35ba8ce8 100644 --- a/src/client/service/vaultsSecretsDelete.ts +++ b/src/client/service/vaultsSecretsDelete.ts @@ -59,7 +59,7 @@ function vaultsSecretsDelete({ !clientUtils.isClientClientError(e, [ vaultsErrors.ErrorVaultsVaultUndefined, vaultsErrors.ErrorSecretsSecretUndefined, - ]) && logger.error(e); + ]) && logger.error(`${vaultsSecretsDelete.name}:${e}`); return; } }; diff --git a/src/client/service/vaultsSecretsEdit.ts b/src/client/service/vaultsSecretsEdit.ts index a40ea71e9..d114e56d2 100644 --- a/src/client/service/vaultsSecretsEdit.ts +++ b/src/client/service/vaultsSecretsEdit.ts @@ -61,7 +61,7 @@ function vaultsSecretsEdit({ vaultsErrors.ErrorVaultsVaultUndefined, vaultsErrors.ErrorSecretsSecretUndefined, vaultsErrors.ErrorVaultRemoteDefined, - ]) && logger.error(e); + ]) && logger.error(`${vaultsSecretsEdit.name}:${e}`); return; } }; diff --git a/src/client/service/vaultsSecretsGet.ts b/src/client/service/vaultsSecretsGet.ts index 4f826551f..61eafbf16 100644 --- a/src/client/service/vaultsSecretsGet.ts +++ b/src/client/service/vaultsSecretsGet.ts @@ -59,7 +59,7 @@ function vaultsSecretsGet({ !clientUtils.isClientClientError(e, [ vaultsErrors.ErrorVaultsVaultUndefined, vaultsErrors.ErrorSecretsSecretUndefined, - ]) && logger.error(e); + ]) && logger.error(`${vaultsSecretsGet.name}:${e}`); return; } }; diff --git a/src/client/service/vaultsSecretsList.ts b/src/client/service/vaultsSecretsList.ts index a0774565f..c55aa59d0 100644 --- a/src/client/service/vaultsSecretsList.ts +++ b/src/client/service/vaultsSecretsList.ts @@ -61,7 +61,7 @@ function vaultsSecretsList({ await genWritable.throw(e); !clientUtils.isClientClientError(e, [ vaultsErrors.ErrorVaultsVaultUndefined, - ]) && logger.error(e); + ]) && logger.error(`${vaultsSecretsList.name}:${e}`); return; } }; diff --git a/src/client/service/vaultsSecretsMkdir.ts b/src/client/service/vaultsSecretsMkdir.ts index 623a28ec9..a9a545704 100644 --- a/src/client/service/vaultsSecretsMkdir.ts +++ b/src/client/service/vaultsSecretsMkdir.ts @@ -60,7 +60,7 @@ function vaultsSecretsMkdir({ !clientUtils.isClientClientError(e, [ vaultsErrors.ErrorVaultsVaultUndefined, vaultsErrors.ErrorVaultsRecursive, - ]) && logger.error(e); + ]) && logger.error(`${vaultsSecretsMkdir.name}:${e}`); return; } }; diff --git a/src/client/service/vaultsSecretsNew.ts b/src/client/service/vaultsSecretsNew.ts index c481a4cda..2414e6bf6 100644 --- a/src/client/service/vaultsSecretsNew.ts +++ b/src/client/service/vaultsSecretsNew.ts @@ -60,7 +60,7 @@ function vaultsSecretsNew({ !clientUtils.isClientClientError(e, [ vaultsErrors.ErrorVaultsVaultUndefined, vaultsErrors.ErrorSecretsSecretUndefined, - ]) && logger.error(e); + ]) && logger.error(`${vaultsSecretsNew.name}:${e}`); return; } }; diff --git a/src/client/service/vaultsSecretsNewDir.ts b/src/client/service/vaultsSecretsNewDir.ts index e9a0d0878..3ed67ce68 100644 --- a/src/client/service/vaultsSecretsNewDir.ts +++ b/src/client/service/vaultsSecretsNewDir.ts @@ -61,7 +61,7 @@ function vaultsSecretsNewDir({ callback(grpcUtils.fromError(e)); !clientUtils.isClientClientError(e, [ vaultsErrors.ErrorVaultsVaultUndefined, - ]) && logger.error(e); + ]) && logger.error(`${vaultsSecretsNewDir.name}:${e}`); return; } }; diff --git a/src/client/service/vaultsSecretsRename.ts b/src/client/service/vaultsSecretsRename.ts index 9362c2b08..d65a87f02 100644 --- a/src/client/service/vaultsSecretsRename.ts +++ b/src/client/service/vaultsSecretsRename.ts @@ -65,7 +65,7 @@ function vaultsSecretsRename({ !clientUtils.isClientClientError(e, [ vaultsErrors.ErrorVaultsVaultUndefined, vaultsErrors.ErrorSecretsSecretUndefined, - ]) && logger.error(e); + ]) && logger.error(`${vaultsSecretsRename.name}:${e}`); return; } }; diff --git a/src/client/service/vaultsSecretsStat.ts b/src/client/service/vaultsSecretsStat.ts index 78a7d2800..9d4477443 100644 --- a/src/client/service/vaultsSecretsStat.ts +++ b/src/client/service/vaultsSecretsStat.ts @@ -58,7 +58,7 @@ function vaultsSecretsStat({ !clientUtils.isClientClientError(e, [ vaultsErrors.ErrorVaultsVaultUndefined, vaultsErrors.ErrorSecretsSecretUndefined, - ]) && logger.error(e); + ]) && logger.error(`${vaultsSecretsStat.name}:${e}`); return; } }; diff --git a/src/client/service/vaultsVersion.ts b/src/client/service/vaultsVersion.ts index a3871b526..df1dc3300 100644 --- a/src/client/service/vaultsVersion.ts +++ b/src/client/service/vaultsVersion.ts @@ -68,7 +68,7 @@ function vaultsVersion({ vaultsErrors.ErrorVaultsVaultUndefined, vaultsErrors.ErrorVaultReferenceInvalid, vaultsErrors.ErrorVaultReferenceMissing, - ]) && logger.error(e); + ]) && logger.error(`${vaultsVersion.name}:${e}`); return; } }; From fc365b41c50effae67c09b5575cb5d83ce251066 Mon Sep 17 00:00:00 2001 From: Emma Casolin Date: Tue, 21 Jun 2022 15:09:12 +1000 Subject: [PATCH 04/15] fix: PolykeyAgent now stops nodes domains when startup fails --- src/PolykeyAgent.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/PolykeyAgent.ts b/src/PolykeyAgent.ts index 1e905fa9e..3cd247700 100644 --- a/src/PolykeyAgent.ts +++ b/src/PolykeyAgent.ts @@ -606,7 +606,7 @@ class PolykeyAgent { acl: this.acl, gestaltGraph: this.gestaltGraph, proxy: this.proxy, - logger: this.logger.getChild(createAgentService.name), + logger: this.logger.getChild('GRPCClientAgentService'), }); const clientService = createClientService({ pkAgent: this, @@ -696,6 +696,10 @@ class PolykeyAgent { await this.notificationsManager?.stop(); await this.vaultManager?.stop(); await this.discovery?.stop(); + await this.queue?.stop(); + await this.nodeGraph?.stop(); + await this.nodeConnectionManager?.stop(); + await this.nodeManager?.stop(); await this.proxy?.stop(); await this.grpcServerAgent?.stop(); await this.grpcServerClient?.stop(); From eebeee34381c96dddea26f431bc1f5bbb786c6ca Mon Sep 17 00:00:00 2001 From: Emma Casolin Date: Tue, 21 Jun 2022 15:09:42 +1000 Subject: [PATCH 05/15] fix: ICE protocol uses `Promise.any` instead of `Promise.all` --- src/nodes/NodeConnectionManager.ts | 32 ++++++++++++++++-------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/nodes/NodeConnectionManager.ts b/src/nodes/NodeConnectionManager.ts index f39f333d8..ffa54acaa 100644 --- a/src/nodes/NodeConnectionManager.ts +++ b/src/nodes/NodeConnectionManager.ts @@ -610,21 +610,23 @@ class NodeConnectionManager { timer, ); for (const [nodeId, nodeData] of nodes) { - const pingAndAddNode = async () => { - const port = nodeData.address.port; - const host = await networkUtils.resolveHost(nodeData.address.host); - if (await this.pingNode(nodeId, host, port)) { - await this.nodeManager!.setNode(nodeId, nodeData.address, true); - } - }; + if (!nodeId.equals(this.keyManager.getNodeId())) { + const pingAndAddNode = async () => { + const port = nodeData.address.port; + const host = await networkUtils.resolveHost(nodeData.address.host); + if (await this.pingNode(nodeId, host, port)) { + await this.nodeManager!.setNode(nodeId, nodeData.address, true); + } + }; - if (!block) { - this.queue.push(pingAndAddNode); - } else { - try { - await pingAndAddNode(); - } catch (e) { - if (!(e instanceof nodesErrors.ErrorNodeGraphSameNodeId)) throw e; + if (!block) { + this.queue.push(pingAndAddNode); + } else { + try { + await pingAndAddNode(); + } catch (e) { + if (!(e instanceof nodesErrors.ErrorNodeGraphSameNodeId)) throw e; + } } } } @@ -759,7 +761,7 @@ class NodeConnectionManager { ); try { - await Promise.all([forwardPunchPromise, ...holePunchPromises]); + await Promise.any([forwardPunchPromise, ...holePunchPromises]); } catch (e) { return false; } From ed2973e1ea5bf9fb1ef6512c6b72f81a79a617d4 Mon Sep 17 00:00:00 2001 From: Emma Casolin Date: Tue, 21 Jun 2022 15:13:22 +1000 Subject: [PATCH 06/15] tests: fixes from testnet changes The testnet PR brought in some changes that affect the NAT tests, so they needed to be modified slightly. - Adding a node to the node graph pings the node by default. We want to disable this in the NAT tests so we have more control over the pings. - Nodes add the details of any node that pings them. We can now remove some additional `nodes add` calls that were required to imitate this previously missing functionality. --- tests/nat/endpointIndependentNAT.test.ts | 14 ++++++------- tests/nat/noNAT.test.ts | 4 ++-- tests/nat/utils.ts | 26 ------------------------ 3 files changed, 9 insertions(+), 35 deletions(-) diff --git a/tests/nat/endpointIndependentNAT.test.ts b/tests/nat/endpointIndependentNAT.test.ts index ff44117ff..23d46e84d 100644 --- a/tests/nat/endpointIndependentNAT.test.ts +++ b/tests/nat/endpointIndependentNAT.test.ts @@ -48,7 +48,7 @@ describeIf( await testNatUtils.pkExecNs( userPid!, agent1Pid!, - ['nodes', 'add', agent2NodeId, agent2Host, agent2ProxyPort], + ['nodes', 'add', agent2NodeId, agent2Host, agent2ProxyPort, '--no-ping'], { PK_NODE_PATH: agent1NodePath, PK_PASSWORD: password, @@ -96,7 +96,7 @@ describeIf( await testNatUtils.pkExecNs( userPid!, agent2Pid!, - ['nodes', 'add', agent1NodeId, agent1Host, agent1ProxyPort], + ['nodes', 'add', agent1NodeId, agent1Host, agent1ProxyPort, '--no-ping'], { PK_NODE_PATH: agent2NodePath, PK_PASSWORD: password, @@ -106,7 +106,7 @@ describeIf( await testNatUtils.pkExecNs( userPid!, agent1Pid!, - ['nodes', 'add', agent2NodeId, agent2Host, agent2ProxyPort], + ['nodes', 'add', agent2NodeId, agent2Host, agent2ProxyPort, '--no-ping'], { PK_NODE_PATH: agent1NodePath, PK_PASSWORD: password, @@ -188,7 +188,7 @@ describeIf( await testNatUtils.pkExecNs( userPid!, agent2Pid!, - ['nodes', 'add', agent1NodeId, agent1Host, agent1ProxyPort], + ['nodes', 'add', agent1NodeId, agent1Host, agent1ProxyPort, '--no-ping'], { PK_NODE_PATH: agent2NodePath, PK_PASSWORD: password, @@ -198,7 +198,7 @@ describeIf( await testNatUtils.pkExecNs( userPid!, agent1Pid!, - ['nodes', 'add', agent2NodeId, agent2Host, agent2ProxyPort], + ['nodes', 'add', agent2NodeId, agent2Host, agent2ProxyPort, '--no-ping'], { PK_NODE_PATH: agent1NodePath, PK_PASSWORD: password, @@ -348,7 +348,7 @@ describeIf( ({ exitCode, stdout } = await testNatUtils.pkExecNs( userPid!, agent2Pid!, - ['nodes', 'ping', agent1NodeId, '--format', 'json', '-vv'], + ['nodes', 'ping', agent1NodeId, '--format', 'json'], { PK_NODE_PATH: agent2NodePath, PK_PASSWORD: password, @@ -363,7 +363,7 @@ describeIf( ({ exitCode, stdout } = await testNatUtils.pkExecNs( userPid!, agent1Pid!, - ['nodes', 'ping', agent2NodeId, '--format', 'json', '-vv'], + ['nodes', 'ping', agent2NodeId, '--format', 'json'], { PK_NODE_PATH: agent1NodePath, PK_PASSWORD: password, diff --git a/tests/nat/noNAT.test.ts b/tests/nat/noNAT.test.ts index 737f36c08..0690261bb 100644 --- a/tests/nat/noNAT.test.ts +++ b/tests/nat/noNAT.test.ts @@ -142,7 +142,7 @@ describeIf( await testNatUtils.pkExecNs( userPid!, agent1Pid!, - ['nodes', 'add', agent2NodeId, agent2Host, agent2ProxyPort], + ['nodes', 'add', agent2NodeId, agent2Host, agent2ProxyPort, '--no-ping'], { PK_NODE_PATH: agent1NodePath, PK_PASSWORD: password, @@ -152,7 +152,7 @@ describeIf( await testNatUtils.pkExecNs( userPid!, agent2Pid!, - ['nodes', 'add', agent1NodeId, agent1Host, agent1ProxyPort], + ['nodes', 'add', agent1NodeId, agent1Host, agent1ProxyPort, '--no-ping'], { PK_NODE_PATH: agent2NodePath, PK_PASSWORD: password, diff --git a/tests/nat/utils.ts b/tests/nat/utils.ts index 9b83fdab3..13aa13cea 100644 --- a/tests/nat/utils.ts +++ b/tests/nat/utils.ts @@ -1362,32 +1362,6 @@ async function setupNATWithSeedNode( rlOutNode2.once('close', reject); }); const nodeId2 = JSON.parse(stdoutNode2).nodeId; - // Until nodes add the information of nodes that connect to them must - // do it manually - const agent1ProxyPort = - agent1NAT === 'dmz' || agent1NAT === 'edmSimple' ? mappedPort : agent1Port; - const agent2ProxyPort = - agent2NAT === 'dmz' || agent2NAT === 'edmSimple' ? mappedPort : agent2Port; - await pkExecNs( - usrns.pid!, - seedNode.pid!, - ['nodes', 'add', nodeId1, agent1RouterHostExtIp, agent1ProxyPort], - { - PK_NODE_PATH: path.join(dataDir, 'seed'), - PK_PASSWORD: password, - }, - dataDir, - ); - await pkExecNs( - usrns.pid!, - seedNode.pid!, - ['nodes', 'add', nodeId2, agent2RouterHostExtIp, agent2ProxyPort], - { - PK_NODE_PATH: path.join(dataDir, 'seed'), - PK_PASSWORD: password, - }, - dataDir, - ); return { userPid: usrns.pid, agent1Pid: agent1Netns.pid, From a0adb358e18d8ac5fd90fbcae4f683531850f6b3 Mon Sep 17 00:00:00 2001 From: Emma Casolin Date: Wed, 22 Jun 2022 16:09:14 +1000 Subject: [PATCH 07/15] build: upgraded `async-init`, `async-locks` and `js-logger` --- package-lock.json | 46 +++++++++++++++++++++++----------------------- package.json | 6 +++--- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/package-lock.json b/package-lock.json index 56ae4cab3..b12a9624a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,12 +10,12 @@ "license": "GPL-3.0", "dependencies": { "@grpc/grpc-js": "1.6.7", - "@matrixai/async-init": "^1.7.3", - "@matrixai/async-locks": "^2.2.5", + "@matrixai/async-init": "^1.8.0", + "@matrixai/async-locks": "^2.3.1", "@matrixai/db": "^4.0.5", "@matrixai/errors": "^1.1.1", "@matrixai/id": "^3.3.3", - "@matrixai/logger": "^2.1.1", + "@matrixai/logger": "^2.2.0", "@matrixai/resources": "^1.1.3", "@matrixai/workers": "^1.3.3", "ajv": "^7.0.4", @@ -2537,18 +2537,18 @@ } }, "node_modules/@matrixai/async-init": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@matrixai/async-init/-/async-init-1.7.3.tgz", - "integrity": "sha512-Sf3q5ODhVJqrYiAdGXmwj606956lgEMKGM9LMFU5scIOh13WokHo3GthjB1yh/umCV75NYvHJn60R9gnudVZ3Q==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@matrixai/async-init/-/async-init-1.8.0.tgz", + "integrity": "sha512-Y2yxG0s5UQQR/WqIvMhud0tRmcGrUynwE/vdbkSsLeLARpAAOmSgbIzP963VqOiAs8/W3FVJgMaMjo4rIaJPtg==", "dependencies": { - "@matrixai/async-locks": "^2.2.4", + "@matrixai/async-locks": "^2.3.1", "@matrixai/errors": "^1.1.1" } }, "node_modules/@matrixai/async-locks": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/@matrixai/async-locks/-/async-locks-2.2.5.tgz", - "integrity": "sha512-Yokd3p64FciLNSW04Qox+UHJulxnWQIhHt3h9sMmgoiTsyZcOgeYfPAJrtZiRnhaUXhfBczPy4VPP2e5lrXgig==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@matrixai/async-locks/-/async-locks-2.3.1.tgz", + "integrity": "sha512-STz8VyiIXleaa72zMsq01x/ZO1gPzukUgMe25+uqMWn/nPrC9EtJOR7e3CW0DODfYDZ0748z196GeOjS3jh+4g==", "dependencies": { "@matrixai/errors": "^1.1.1", "@matrixai/resources": "^1.1.3", @@ -2588,9 +2588,9 @@ } }, "node_modules/@matrixai/logger": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@matrixai/logger/-/logger-2.1.1.tgz", - "integrity": "sha512-79KM0PyJTpfkALf9DK2xGniU+9gngsb5O8hcdUviWz+zR2W0hnTQq/g7tJW0YnIEhmDe/GkJf0Bnbs+gWfj3BA==" + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@matrixai/logger/-/logger-2.2.0.tgz", + "integrity": "sha512-GNAFEophgqhJZI01s5OuosbxJO/GSmDZU4rfKmNS3l/dbaOuI32RKEv6XNd6O9HpjmX886rGSxks1/rBZw+cRw==" }, "node_modules/@matrixai/resources": { "version": "1.1.3", @@ -13644,18 +13644,18 @@ } }, "@matrixai/async-init": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@matrixai/async-init/-/async-init-1.7.3.tgz", - "integrity": "sha512-Sf3q5ODhVJqrYiAdGXmwj606956lgEMKGM9LMFU5scIOh13WokHo3GthjB1yh/umCV75NYvHJn60R9gnudVZ3Q==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@matrixai/async-init/-/async-init-1.8.0.tgz", + "integrity": "sha512-Y2yxG0s5UQQR/WqIvMhud0tRmcGrUynwE/vdbkSsLeLARpAAOmSgbIzP963VqOiAs8/W3FVJgMaMjo4rIaJPtg==", "requires": { - "@matrixai/async-locks": "^2.2.4", + "@matrixai/async-locks": "^2.3.1", "@matrixai/errors": "^1.1.1" } }, "@matrixai/async-locks": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/@matrixai/async-locks/-/async-locks-2.2.5.tgz", - "integrity": "sha512-Yokd3p64FciLNSW04Qox+UHJulxnWQIhHt3h9sMmgoiTsyZcOgeYfPAJrtZiRnhaUXhfBczPy4VPP2e5lrXgig==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@matrixai/async-locks/-/async-locks-2.3.1.tgz", + "integrity": "sha512-STz8VyiIXleaa72zMsq01x/ZO1gPzukUgMe25+uqMWn/nPrC9EtJOR7e3CW0DODfYDZ0748z196GeOjS3jh+4g==", "requires": { "@matrixai/errors": "^1.1.1", "@matrixai/resources": "^1.1.3", @@ -13695,9 +13695,9 @@ } }, "@matrixai/logger": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@matrixai/logger/-/logger-2.1.1.tgz", - "integrity": "sha512-79KM0PyJTpfkALf9DK2xGniU+9gngsb5O8hcdUviWz+zR2W0hnTQq/g7tJW0YnIEhmDe/GkJf0Bnbs+gWfj3BA==" + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@matrixai/logger/-/logger-2.2.0.tgz", + "integrity": "sha512-GNAFEophgqhJZI01s5OuosbxJO/GSmDZU4rfKmNS3l/dbaOuI32RKEv6XNd6O9HpjmX886rGSxks1/rBZw+cRw==" }, "@matrixai/resources": { "version": "1.1.3", diff --git a/package.json b/package.json index 2fdc8cbff..5fe9c750d 100644 --- a/package.json +++ b/package.json @@ -76,12 +76,12 @@ }, "dependencies": { "@grpc/grpc-js": "1.6.7", - "@matrixai/async-init": "^1.7.3", - "@matrixai/async-locks": "^2.2.5", + "@matrixai/async-init": "^1.8.0", + "@matrixai/async-locks": "^2.3.1", "@matrixai/db": "^4.0.5", "@matrixai/errors": "^1.1.1", "@matrixai/id": "^3.3.3", - "@matrixai/logger": "^2.1.1", + "@matrixai/logger": "^2.2.0", "@matrixai/resources": "^1.1.3", "@matrixai/workers": "^1.3.3", "ajv": "^7.0.4", From 8fdde5450d4ff12c918b7f6638da8a186aa5c987 Mon Sep 17 00:00:00 2001 From: Emma Casolin Date: Wed, 22 Jun 2022 16:10:49 +1000 Subject: [PATCH 08/15] feat: removing `edmSimple` configuration Now that nodes add the details of a node that contacts them, we no longer need the `edmSimple` configuration to do this manually. --- .../service/nodesHolePunchMessageSend.ts | 1 - tests/nat/endpointDependentNAT.test.ts | 54 ++---- tests/nat/endpointIndependentNAT.test.ts | 34 +--- tests/nat/utils.ts | 179 +----------------- 4 files changed, 22 insertions(+), 246 deletions(-) diff --git a/src/agent/service/nodesHolePunchMessageSend.ts b/src/agent/service/nodesHolePunchMessageSend.ts index 5e388809b..beafc3bd2 100644 --- a/src/agent/service/nodesHolePunchMessageSend.ts +++ b/src/agent/service/nodesHolePunchMessageSend.ts @@ -55,7 +55,6 @@ function nodesHolePunchMessageSend({ sourceId: call.request.getSrcId(), }, ); - logger.info('IN AGENT CLIENT'); // Firstly, check if this node is the desired node // If so, then we want to make this node start sending hole punching packets // back to the source node. diff --git a/tests/nat/endpointDependentNAT.test.ts b/tests/nat/endpointDependentNAT.test.ts index 4d8cfcec7..432796226 100644 --- a/tests/nat/endpointDependentNAT.test.ts +++ b/tests/nat/endpointDependentNAT.test.ts @@ -48,7 +48,7 @@ describeIf( await testNatUtils.pkExecNs( userPid!, agent1Pid!, - ['nodes', 'add', agent2NodeId, agent2Host, agent2ProxyPort], + ['nodes', 'add', agent2NodeId, agent2Host, agent2ProxyPort, '--no-ping'], { PK_NODE_PATH: agent1NodePath, PK_PASSWORD: password, @@ -89,48 +89,20 @@ describeIf( agent1Host, agent1ProxyPort, agent2NodeId, - agent2Host, - agent2ProxyPort, tearDownNAT, - } = await testNatUtils.setupNAT('dmz', 'edmSimple', logger); + } = await testNatUtils.setupNAT('dmz', 'edm', logger); + // Agent 2 must ping Agent 1 first, since Agent 2 is behind a NAT await testNatUtils.pkExecNs( userPid!, agent2Pid!, - ['nodes', 'add', agent1NodeId, agent1Host, agent1ProxyPort], + ['nodes', 'add', agent1NodeId, agent1Host, agent1ProxyPort, '--no-ping'], { PK_NODE_PATH: agent2NodePath, PK_PASSWORD: password, }, dataDir, ); - await testNatUtils.pkExecNs( - userPid!, - agent1Pid!, - ['nodes', 'add', agent2NodeId, agent2Host, agent2ProxyPort], - { - PK_NODE_PATH: agent1NodePath, - PK_PASSWORD: password, - }, - dataDir, - ); - // If we try to ping Agent 2 it will fail let exitCode, stdout; - ({ exitCode, stdout } = await testNatUtils.pkExecNs( - userPid!, - agent1Pid!, - ['nodes', 'ping', agent2NodeId, '--format', 'json'], - { - PK_NODE_PATH: agent1NodePath, - PK_PASSWORD: password, - }, - dataDir, - )); - expect(exitCode).toBe(1); - expect(JSON.parse(stdout)).toEqual({ - success: false, - message: 'No response received', - }); - // But Agent 2 can ping Agent 1 because Agent 1 is not behind a NAT ({ exitCode, stdout } = await testNatUtils.pkExecNs( userPid!, agent2Pid!, @@ -181,8 +153,8 @@ describeIf( agent2NodeId, tearDownNAT, } = await testNatUtils.setupNATWithSeedNode( - 'edmSimple', - 'edmSimple', + 'edm', + 'edm', logger, ); // Contact details are retrieved from the seed node, but cannot be used @@ -202,9 +174,9 @@ describeIf( expect(exitCode).toBe(1); expect(JSON.parse(stdout)).toEqual({ success: false, - message: 'No response received', + message: `Failed to resolve node ID ${agent1NodeId} to an address.`, }); - // Node 1 -> Node 2 ping should also fail + // Node 1 -> Node 2 ping should also fail for the same reason ({ exitCode, stdout } = await testNatUtils.pkExecNs( userPid!, agent1Pid!, @@ -218,7 +190,7 @@ describeIf( expect(exitCode).toBe(1); expect(JSON.parse(stdout)).toEqual({ success: false, - message: 'No response received', + message: `Failed to resolve node ID ${agent2NodeId} to an address.`, }); await tearDownNAT(); }, @@ -238,13 +210,13 @@ describeIf( agent1NodeId, agent2NodeId, tearDownNAT, - } = await testNatUtils.setupNATWithSeedNode('edmSimple', 'eim', logger); + } = await testNatUtils.setupNATWithSeedNode('edm', 'eim', logger); // Since one of the nodes uses EDM NAT we cannot punch through let exitCode, stdout; ({ exitCode, stdout } = await testNatUtils.pkExecNs( userPid!, agent2Pid!, - ['nodes', 'ping', agent1NodeId, '--format', 'json', '-vv'], + ['nodes', 'ping', agent1NodeId, '--format', 'json'], { PK_NODE_PATH: agent2NodePath, PK_PASSWORD: password, @@ -254,7 +226,7 @@ describeIf( expect(exitCode).toBe(1); expect(JSON.parse(stdout)).toEqual({ success: false, - message: 'No response received', + message: `Failed to resolve node ID ${agent1NodeId} to an address.`, }); ({ exitCode, stdout } = await testNatUtils.pkExecNs( userPid!, @@ -269,7 +241,7 @@ describeIf( expect(exitCode).toBe(1); expect(JSON.parse(stdout)).toEqual({ success: false, - message: 'No response received', + message: `Failed to resolve node ID ${agent2NodeId} to an address.`, }); await tearDownNAT(); }, diff --git a/tests/nat/endpointIndependentNAT.test.ts b/tests/nat/endpointIndependentNAT.test.ts index 23d46e84d..f6023010a 100644 --- a/tests/nat/endpointIndependentNAT.test.ts +++ b/tests/nat/endpointIndependentNAT.test.ts @@ -273,9 +273,8 @@ describeIf( agent2NodeId, tearDownNAT, } = await testNatUtils.setupNATWithSeedNode('eim', 'eim', logger); - // Contact details can be retrieved from the seed node so don't need to - // add manually - // If we try to ping Agent 2 it will fail + // Should be able to ping straight away using the seed node as a + // signaller let exitCode, stdout; ({ exitCode, stdout } = await testNatUtils.pkExecNs( userPid!, @@ -287,34 +286,17 @@ describeIf( }, dataDir, )); - expect(exitCode).toBe(1); - expect(JSON.parse(stdout)).toEqual({ - success: false, - message: 'No response received', - }); - // But Agent 2 can ping Agent 1 now because it's expecting a response - ({ exitCode, stdout } = await testNatUtils.pkExecNs( - userPid!, - agent2Pid!, - ['nodes', 'ping', agent1NodeId, '--format', 'json'], - { - PK_NODE_PATH: agent2NodePath, - PK_PASSWORD: password, - }, - dataDir, - )); expect(exitCode).toBe(0); expect(JSON.parse(stdout)).toEqual({ success: true, message: 'Node is Active.', }); - // Can now ping Agent 2 (it will also be expecting a response) ({ exitCode, stdout } = await testNatUtils.pkExecNs( userPid!, - agent1Pid!, - ['nodes', 'ping', agent2NodeId, '--format', 'json'], + agent2Pid!, + ['nodes', 'ping', agent1NodeId, '--format', 'json'], { - PK_NODE_PATH: agent1NodePath, + PK_NODE_PATH: agent2NodePath, PK_PASSWORD: password, }, dataDir, @@ -342,7 +324,7 @@ describeIf( agent1NodeId, agent2NodeId, tearDownNAT, - } = await testNatUtils.setupNATWithSeedNode('eim', 'edmSimple', logger); + } = await testNatUtils.setupNATWithSeedNode('eim', 'edm', logger); // Since one of the nodes uses EDM NAT we cannot punch through let exitCode, stdout; ({ exitCode, stdout } = await testNatUtils.pkExecNs( @@ -358,7 +340,7 @@ describeIf( expect(exitCode).toBe(1); expect(JSON.parse(stdout)).toEqual({ success: false, - message: 'No response received', + message: `Failed to resolve node ID ${agent1NodeId} to an address.`, }); ({ exitCode, stdout } = await testNatUtils.pkExecNs( userPid!, @@ -373,7 +355,7 @@ describeIf( expect(exitCode).toBe(1); expect(JSON.parse(stdout)).toEqual({ success: false, - message: 'No response received', + message: `Failed to resolve node ID ${agent2NodeId} to an address.`, }); await tearDownNAT(); }, diff --git a/tests/nat/utils.ts b/tests/nat/utils.ts index 13aa13cea..c2efe6b25 100644 --- a/tests/nat/utils.ts +++ b/tests/nat/utils.ts @@ -8,7 +8,7 @@ import readline from 'readline'; import Logger, { LogLevel, StreamHandler } from '@matrixai/logger'; import * as testBinUtils from '../bin/utils'; -type NATType = 'eim' | 'edm' | 'dmz' | 'edmSimple'; +type NATType = 'eim' | 'edm' | 'dmz'; // Constants for all util functions // Veth pairs (ends) @@ -951,121 +951,6 @@ async function setupNATEndpointDependentMapping( } } -/** - * Setup Port-Restricted Cone NAT for a namespace (on the router namespace) - */ -async function setupNATSimplifiedEDMAgent( - usrnsPid: number, - routerNsPid: number, - agentIp: string, - routerExt: string, - routerInt: string, - logger: Logger = new Logger(setupNATSimplifiedEDMAgent.name), -) { - const natCommand = [ - ...nsenter(usrnsPid, routerNsPid), - 'iptables', - '--table', - 'nat', - '--append', - 'POSTROUTING', - '--protocol', - 'udp', - '--source', - `${agentIp}${subnetMask}`, - '--out-interface', - routerExt, - '--jump', - 'MASQUERADE', - '--to-ports', - '44444', - ]; - const acceptLocalCommand = [ - ...nsenter(usrnsPid, routerNsPid), - 'iptables', - '--table', - 'filter', - '--append', - 'INPUT', - '--in-interface', - routerInt, - '--jump', - 'ACCEPT', - ]; - const acceptEstablishedCommand = [ - ...nsenter(usrnsPid, routerNsPid), - 'iptables', - '--table', - 'filter', - '--append', - 'INPUT', - '--match', - 'conntrack', - '--ctstate', - 'RELATED,ESTABLISHED', - '--jump', - 'ACCEPT', - ]; - const dropCommand = [ - ...nsenter(usrnsPid, routerNsPid), - 'iptables', - '--table', - 'filter', - '--append', - 'INPUT', - '--jump', - 'DROP', - ]; - try { - logger.info(['nsenter', ...acceptLocalCommand].join(' ')); - await testBinUtils.exec('nsenter', acceptLocalCommand); - logger.info(['nsenter', ...acceptEstablishedCommand].join(' ')); - await testBinUtils.exec('nsenter', acceptEstablishedCommand); - logger.info(['nsenter', ...dropCommand].join(' ')); - await testBinUtils.exec('nsenter', dropCommand); - logger.info(['nsenter', ...natCommand].join(' ')); - await testBinUtils.exec('nsenter', natCommand); - } catch (e) { - logger.error(e.message); - } -} - -/** - * Setup Port-Restricted Cone NAT for a namespace (on the router namespace) - */ -async function setupNATSimplifiedEDMSeed( - usrnsPid: number, - routerNsPid: number, - agentIp: string, - routerExt: string, - logger: Logger = new Logger(setupNATSimplifiedEDMSeed.name), -) { - const natCommand = [ - ...nsenter(usrnsPid, routerNsPid), - 'iptables', - '--table', - 'nat', - '--append', - 'POSTROUTING', - '--protocol', - 'udp', - '--source', - `${agentIp}${subnetMask}`, - '--out-interface', - routerExt, - '--jump', - 'MASQUERADE', - '--to-ports', - '55555', - ]; - try { - logger.info(['nsenter', ...natCommand].join(' ')); - await testBinUtils.exec('nsenter', natCommand); - } catch (e) { - logger.error(e.message); - } -} - async function setupNATWithSeedNode( agent1NAT: NATType, agent2NAT: NATType, @@ -1142,24 +1027,6 @@ async function setupNATWithSeedNode( ); break; } - case 'edmSimple': { - await setupNATSimplifiedEDMAgent( - usrns.pid!, - router1Netns.pid!, - agent1HostIp, - agent1RouterHostExt, - agent1RouterHostInt, - logger, - ); - await setupNATSimplifiedEDMSeed( - usrns.pid!, - router1Netns.pid!, - agent1HostIp, - router1SeedHost, - logger, - ); - break; - } } switch (agent2NAT) { case 'dmz': { @@ -1217,24 +1084,6 @@ async function setupNATWithSeedNode( ); break; } - case 'edmSimple': { - await setupNATSimplifiedEDMAgent( - usrns.pid!, - router2Netns.pid!, - agent2HostIp, - agent2RouterHostExt, - agent2RouterHostInt, - logger, - ); - await setupNATSimplifiedEDMSeed( - usrns.pid!, - router2Netns.pid!, - agent2HostIp, - router2SeedHost, - logger, - ); - break; - } } await setupNetworkNamespaceInterfaces( usrns.pid!, @@ -1451,17 +1300,6 @@ async function setupNAT( ); break; } - case 'edmSimple': { - await setupNATSimplifiedEDMAgent( - usrns.pid!, - router1Netns.pid!, - agent1HostIp, - agent1RouterHostExt, - agent1RouterHostInt, - logger, - ); - break; - } } switch (agent2NAT) { case 'dmz': { @@ -1496,17 +1334,6 @@ async function setupNAT( ); break; } - case 'edmSimple': { - await setupNATSimplifiedEDMAgent( - usrns.pid!, - router2Netns.pid!, - agent2HostIp, - agent2RouterHostExt, - agent2RouterHostInt, - logger, - ); - break; - } } await setupNetworkNamespaceInterfaces( usrns.pid!, @@ -1601,16 +1428,12 @@ async function setupNAT( agent1ProxyPort: agent1NAT === 'dmz' ? mappedPort - : agent1NAT === 'edmSimple' - ? '44444' : agent1Port, agent2NodeId: nodeId2, agent2Host: agent2RouterHostExtIp, agent2ProxyPort: agent2NAT === 'dmz' ? mappedPort - : agent2NAT === 'edmSimple' - ? '44444' : agent2Port, tearDownNAT: async () => { agent2.kill('SIGTERM'); From 9800c4abb58b64d4382abd116f8407ea54bd8da2 Mon Sep 17 00:00:00 2001 From: Emma Casolin Date: Wed, 22 Jun 2022 16:28:16 +1000 Subject: [PATCH 09/15] style: linting and other style fixes General linting, using capitals for constants in NAT utils, and lowercase test descriptions --- src/agent/service/nodesChainDataGet.ts | 3 +- .../service/nodesClosestLocalNodesGet.ts | 3 +- .../service/nodesHolePunchMessageSend.ts | 3 +- src/client/service/agentLockAll.ts | 3 +- src/client/service/agentStatus.ts | 3 +- src/client/service/agentStop.ts | 3 +- src/client/service/agentUnlock.ts | 3 +- .../service/gestaltsActionsGetByIdentity.ts | 3 +- .../service/gestaltsActionsGetByNode.ts | 3 +- .../service/gestaltsDiscoveryByIdentity.ts | 3 +- src/client/service/gestaltsDiscoveryByNode.ts | 3 +- .../service/gestaltsGestaltGetByIdentity.ts | 3 +- .../service/gestaltsGestaltGetByNode.ts | 3 +- src/client/service/gestaltsGestaltList.ts | 3 +- .../service/identitiesAuthenticatedGet.ts | 3 +- src/client/service/identitiesProvidersList.ts | 3 +- src/client/service/identitiesTokenDelete.ts | 3 +- src/client/service/identitiesTokenGet.ts | 3 +- src/client/service/identitiesTokenPut.ts | 3 +- src/client/service/keysCertsChainGet.ts | 3 +- src/client/service/keysCertsGet.ts | 3 +- src/client/service/keysDecrypt.ts | 3 +- src/client/service/keysEncrypt.ts | 3 +- src/client/service/keysKeyPairRenew.ts | 3 +- src/client/service/keysKeyPairReset.ts | 3 +- src/client/service/keysKeyPairRoot.ts | 3 +- src/client/service/keysPasswordChange.ts | 3 +- src/client/service/keysSign.ts | 3 +- src/client/service/keysVerify.ts | 3 +- src/client/service/nodesAdd.ts | 3 +- src/client/service/notificationsClear.ts | 3 +- src/client/service/notificationsRead.ts | 3 +- src/client/service/vaultsList.ts | 3 +- tests/nat/{noNAT.test.ts => DMZ.test.ts} | 24 +- tests/nat/endpointDependentNAT.test.ts | 32 +- tests/nat/endpointIndependentNAT.test.ts | 55 ++- tests/nat/utils.ts | 391 +++++++++++------- 37 files changed, 384 insertions(+), 217 deletions(-) rename tests/nat/{noNAT.test.ts => DMZ.test.ts} (94%) diff --git a/src/agent/service/nodesChainDataGet.ts b/src/agent/service/nodesChainDataGet.ts index 5d0f263ed..10175c706 100644 --- a/src/agent/service/nodesChainDataGet.ts +++ b/src/agent/service/nodesChainDataGet.ts @@ -51,7 +51,8 @@ function nodesChainDataGet({ return; } catch (e) { callback(grpcUtils.fromError(e, true)); - !agentUtils.isAgentClientError(e) && logger.error(`${nodesChainDataGet.name}:${e}`); + !agentUtils.isAgentClientError(e) && + logger.error(`${nodesChainDataGet.name}:${e}`); return; } }; diff --git a/src/agent/service/nodesClosestLocalNodesGet.ts b/src/agent/service/nodesClosestLocalNodesGet.ts index 9cb9f45f6..4c987667d 100644 --- a/src/agent/service/nodesClosestLocalNodesGet.ts +++ b/src/agent/service/nodesClosestLocalNodesGet.ts @@ -63,7 +63,8 @@ function nodesClosestLocalNodesGet({ return; } catch (e) { callback(grpcUtils.fromError(e, true)); - !agentUtils.isAgentClientError(e) && logger.error(`${nodesClosestLocalNodesGet.name}:${e}`); + !agentUtils.isAgentClientError(e) && + logger.error(`${nodesClosestLocalNodesGet.name}:${e}`); return; } }; diff --git a/src/agent/service/nodesHolePunchMessageSend.ts b/src/agent/service/nodesHolePunchMessageSend.ts index beafc3bd2..c610d7428 100644 --- a/src/agent/service/nodesHolePunchMessageSend.ts +++ b/src/agent/service/nodesHolePunchMessageSend.ts @@ -74,7 +74,8 @@ function nodesHolePunchMessageSend({ return; } catch (e) { callback(grpcUtils.fromError(e, true)); - !agentUtils.isAgentClientError(e) && logger.error(`${nodesHolePunchMessageSend.name}:${e}`); + !agentUtils.isAgentClientError(e) && + logger.error(`${nodesHolePunchMessageSend.name}:${e}`); return; } }; diff --git a/src/client/service/agentLockAll.ts b/src/client/service/agentLockAll.ts index 9bb315c8f..2c2c7505e 100644 --- a/src/client/service/agentLockAll.ts +++ b/src/client/service/agentLockAll.ts @@ -33,7 +33,8 @@ function agentLockAll({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(`${agentLockAll.name}:${e}`); + !clientUtils.isClientClientError(e) && + logger.error(`${agentLockAll.name}:${e}`); return; } }; diff --git a/src/client/service/agentStatus.ts b/src/client/service/agentStatus.ts index 4ddb3b316..3ebb00b5d 100644 --- a/src/client/service/agentStatus.ts +++ b/src/client/service/agentStatus.ts @@ -50,7 +50,8 @@ function agentStatus({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(`${agentStatus.name}:${e}`); + !clientUtils.isClientClientError(e) && + logger.error(`${agentStatus.name}:${e}`); return; } }; diff --git a/src/client/service/agentStop.ts b/src/client/service/agentStop.ts index e6a640d43..2332c732e 100644 --- a/src/client/service/agentStop.ts +++ b/src/client/service/agentStop.ts @@ -33,7 +33,8 @@ function agentStop({ callback(null, response); } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(`${agentStop.name}:${e}`); + !clientUtils.isClientClientError(e) && + logger.error(`${agentStop.name}:${e}`); return; } // Stop is called after GRPC resources are cleared diff --git a/src/client/service/agentUnlock.ts b/src/client/service/agentUnlock.ts index 0480ab097..991a51c9f 100644 --- a/src/client/service/agentUnlock.ts +++ b/src/client/service/agentUnlock.ts @@ -24,7 +24,8 @@ function agentUnlock({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(`${agentUnlock.name}:${e}`); + !clientUtils.isClientClientError(e) && + logger.error(`${agentUnlock.name}:${e}`); return; } }; diff --git a/src/client/service/gestaltsActionsGetByIdentity.ts b/src/client/service/gestaltsActionsGetByIdentity.ts index ec8e8e316..3375ed15d 100644 --- a/src/client/service/gestaltsActionsGetByIdentity.ts +++ b/src/client/service/gestaltsActionsGetByIdentity.ts @@ -63,7 +63,8 @@ function gestaltsActionsGetByIdentity({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(`${gestaltsActionsGetByIdentity.name}:${e}`); + !clientUtils.isClientClientError(e) && + logger.error(`${gestaltsActionsGetByIdentity.name}:${e}`); return; } }; diff --git a/src/client/service/gestaltsActionsGetByNode.ts b/src/client/service/gestaltsActionsGetByNode.ts index ba572d818..ea0e4298d 100644 --- a/src/client/service/gestaltsActionsGetByNode.ts +++ b/src/client/service/gestaltsActionsGetByNode.ts @@ -57,7 +57,8 @@ function gestaltsActionsGetByNode({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(`${gestaltsActionsGetByNode.name}:${e}`); + !clientUtils.isClientClientError(e) && + logger.error(`${gestaltsActionsGetByNode.name}:${e}`); return; } }; diff --git a/src/client/service/gestaltsDiscoveryByIdentity.ts b/src/client/service/gestaltsDiscoveryByIdentity.ts index 18bcb2ee8..08f2df64e 100644 --- a/src/client/service/gestaltsDiscoveryByIdentity.ts +++ b/src/client/service/gestaltsDiscoveryByIdentity.ts @@ -52,7 +52,8 @@ function gestaltsDiscoveryByIdentity({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(`${gestaltsDiscoveryByIdentity.name}:${e}`); + !clientUtils.isClientClientError(e) && + logger.error(`${gestaltsDiscoveryByIdentity.name}:${e}`); return; } }; diff --git a/src/client/service/gestaltsDiscoveryByNode.ts b/src/client/service/gestaltsDiscoveryByNode.ts index 0584e55c3..f6ed454b0 100644 --- a/src/client/service/gestaltsDiscoveryByNode.ts +++ b/src/client/service/gestaltsDiscoveryByNode.ts @@ -48,7 +48,8 @@ function gestaltsDiscoveryByNode({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(`${gestaltsDiscoveryByNode.name}:${e}`); + !clientUtils.isClientClientError(e) && + logger.error(`${gestaltsDiscoveryByNode.name}:${e}`); return; } }; diff --git a/src/client/service/gestaltsGestaltGetByIdentity.ts b/src/client/service/gestaltsGestaltGetByIdentity.ts index b81df554d..8768ad136 100644 --- a/src/client/service/gestaltsGestaltGetByIdentity.ts +++ b/src/client/service/gestaltsGestaltGetByIdentity.ts @@ -60,7 +60,8 @@ function gestaltsGestaltGetByIdentity({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(`${gestaltsGestaltGetByIdentity.name}:${e}`); + !clientUtils.isClientClientError(e) && + logger.error(`${gestaltsGestaltGetByIdentity.name}:${e}`); return; } }; diff --git a/src/client/service/gestaltsGestaltGetByNode.ts b/src/client/service/gestaltsGestaltGetByNode.ts index 48aff4729..207859fb5 100644 --- a/src/client/service/gestaltsGestaltGetByNode.ts +++ b/src/client/service/gestaltsGestaltGetByNode.ts @@ -56,7 +56,8 @@ function gestaltsGestaltGetByNode({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(`${gestaltsGestaltGetByNode.name}:${e}`); + !clientUtils.isClientClientError(e) && + logger.error(`${gestaltsGestaltGetByNode.name}:${e}`); return; } }; diff --git a/src/client/service/gestaltsGestaltList.ts b/src/client/service/gestaltsGestaltList.ts index c5d49123c..d07fb9f32 100644 --- a/src/client/service/gestaltsGestaltList.ts +++ b/src/client/service/gestaltsGestaltList.ts @@ -40,7 +40,8 @@ function gestaltsGestaltList({ return; } catch (e) { await genWritable.throw(e); - !clientUtils.isClientClientError(e) && logger.error(`${gestaltsGestaltList.name}:${e}`); + !clientUtils.isClientClientError(e) && + logger.error(`${gestaltsGestaltList.name}:${e}`); return; } }; diff --git a/src/client/service/identitiesAuthenticatedGet.ts b/src/client/service/identitiesAuthenticatedGet.ts index cae567830..106a1f53a 100644 --- a/src/client/service/identitiesAuthenticatedGet.ts +++ b/src/client/service/identitiesAuthenticatedGet.ts @@ -64,7 +64,8 @@ function identitiesAuthenticatedGet({ return; } catch (e) { await genWritable.throw(e); - !clientUtils.isClientClientError(e) && logger.error(`${identitiesAuthenticatedGet.name}:${e}`); + !clientUtils.isClientClientError(e) && + logger.error(`${identitiesAuthenticatedGet.name}:${e}`); return; } }; diff --git a/src/client/service/identitiesProvidersList.ts b/src/client/service/identitiesProvidersList.ts index 752d9eea5..17ae7bf31 100644 --- a/src/client/service/identitiesProvidersList.ts +++ b/src/client/service/identitiesProvidersList.ts @@ -30,7 +30,8 @@ function identitiesProvidersList({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(`${identitiesProvidersList.name}:${e}`); + !clientUtils.isClientClientError(e) && + logger.error(`${identitiesProvidersList.name}:${e}`); return; } }; diff --git a/src/client/service/identitiesTokenDelete.ts b/src/client/service/identitiesTokenDelete.ts index 3bf584d8d..2b4a78b9b 100644 --- a/src/client/service/identitiesTokenDelete.ts +++ b/src/client/service/identitiesTokenDelete.ts @@ -57,7 +57,8 @@ function identitiesTokenDelete({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(`${identitiesTokenDelete.name}:${e}`); + !clientUtils.isClientClientError(e) && + logger.error(`${identitiesTokenDelete.name}:${e}`); return; } }; diff --git a/src/client/service/identitiesTokenGet.ts b/src/client/service/identitiesTokenGet.ts index 41872941e..c829da281 100644 --- a/src/client/service/identitiesTokenGet.ts +++ b/src/client/service/identitiesTokenGet.ts @@ -57,7 +57,8 @@ function identitiesTokenGet({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(`${identitiesTokenGet.name}:${e}`); + !clientUtils.isClientClientError(e) && + logger.error(`${identitiesTokenGet.name}:${e}`); return; } }; diff --git a/src/client/service/identitiesTokenPut.ts b/src/client/service/identitiesTokenPut.ts index d8a19ed9e..b7ae0139f 100644 --- a/src/client/service/identitiesTokenPut.ts +++ b/src/client/service/identitiesTokenPut.ts @@ -67,7 +67,8 @@ function identitiesTokenPut({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(`${identitiesTokenPut.name}:${e}`); + !clientUtils.isClientClientError(e) && + logger.error(`${identitiesTokenPut.name}:${e}`); return; } }; diff --git a/src/client/service/keysCertsChainGet.ts b/src/client/service/keysCertsChainGet.ts index fabb933d4..fc074a239 100644 --- a/src/client/service/keysCertsChainGet.ts +++ b/src/client/service/keysCertsChainGet.ts @@ -34,7 +34,8 @@ function keysCertsChainGet({ return; } catch (e) { await genWritable.throw(e); - !clientUtils.isClientClientError(e) && logger.error(`${keysCertsChainGet.name}:${e}`); + !clientUtils.isClientClientError(e) && + logger.error(`${keysCertsChainGet.name}:${e}`); return; } }; diff --git a/src/client/service/keysCertsGet.ts b/src/client/service/keysCertsGet.ts index 2b89ddb34..ff5e3d525 100644 --- a/src/client/service/keysCertsGet.ts +++ b/src/client/service/keysCertsGet.ts @@ -30,7 +30,8 @@ function keysCertsGet({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(`${keysCertsGet.name}:${e}`); + !clientUtils.isClientClientError(e) && + logger.error(`${keysCertsGet.name}:${e}`); return; } }; diff --git a/src/client/service/keysDecrypt.ts b/src/client/service/keysDecrypt.ts index fcd95f2ae..c155c0d8d 100644 --- a/src/client/service/keysDecrypt.ts +++ b/src/client/service/keysDecrypt.ts @@ -31,7 +31,8 @@ function keysDecrypt({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(`${keysDecrypt.name}:${e}`); + !clientUtils.isClientClientError(e) && + logger.error(`${keysDecrypt.name}:${e}`); return; } }; diff --git a/src/client/service/keysEncrypt.ts b/src/client/service/keysEncrypt.ts index 9c9264dc6..71a5a84ff 100644 --- a/src/client/service/keysEncrypt.ts +++ b/src/client/service/keysEncrypt.ts @@ -31,7 +31,8 @@ function keysEncrypt({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(`${keysEncrypt.name}:${e}`); + !clientUtils.isClientClientError(e) && + logger.error(`${keysEncrypt.name}:${e}`); return; } }; diff --git a/src/client/service/keysKeyPairRenew.ts b/src/client/service/keysKeyPairRenew.ts index 89d03fd51..a44df0628 100644 --- a/src/client/service/keysKeyPairRenew.ts +++ b/src/client/service/keysKeyPairRenew.ts @@ -31,7 +31,8 @@ function keysKeyPairRenew({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(`${keysKeyPairRenew.name}:${e}`); + !clientUtils.isClientClientError(e) && + logger.error(`${keysKeyPairRenew.name}:${e}`); return; } }; diff --git a/src/client/service/keysKeyPairReset.ts b/src/client/service/keysKeyPairReset.ts index 3f0b7bf33..a1ba20365 100644 --- a/src/client/service/keysKeyPairReset.ts +++ b/src/client/service/keysKeyPairReset.ts @@ -31,7 +31,8 @@ function keysKeyPairReset({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(`${keysKeyPairReset.name}:${e}`); + !clientUtils.isClientClientError(e) && + logger.error(`${keysKeyPairReset.name}:${e}`); return; } }; diff --git a/src/client/service/keysKeyPairRoot.ts b/src/client/service/keysKeyPairRoot.ts index 8da9aa3b8..2b1cdce82 100644 --- a/src/client/service/keysKeyPairRoot.ts +++ b/src/client/service/keysKeyPairRoot.ts @@ -31,7 +31,8 @@ function keysKeyPairRoot({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(`${keysKeyPairRoot.name}:${e}`); + !clientUtils.isClientClientError(e) && + logger.error(`${keysKeyPairRoot.name}:${e}`); return; } }; diff --git a/src/client/service/keysPasswordChange.ts b/src/client/service/keysPasswordChange.ts index d94a650ed..a7f4ed029 100644 --- a/src/client/service/keysPasswordChange.ts +++ b/src/client/service/keysPasswordChange.ts @@ -29,7 +29,8 @@ function keysPasswordChange({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(`${keysPasswordChange.name}:${e}`); + !clientUtils.isClientClientError(e) && + logger.error(`${keysPasswordChange.name}:${e}`); return; } }; diff --git a/src/client/service/keysSign.ts b/src/client/service/keysSign.ts index 77d2d4d34..126d0c149 100644 --- a/src/client/service/keysSign.ts +++ b/src/client/service/keysSign.ts @@ -32,7 +32,8 @@ function keysSign({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(`${keysSign.name}:${e}`); + !clientUtils.isClientClientError(e) && + logger.error(`${keysSign.name}:${e}`); return; } }; diff --git a/src/client/service/keysVerify.ts b/src/client/service/keysVerify.ts index 6e7cee48e..d2c82bfba 100644 --- a/src/client/service/keysVerify.ts +++ b/src/client/service/keysVerify.ts @@ -33,7 +33,8 @@ function keysVerify({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(`${keysVerify.name}:${e}`); + !clientUtils.isClientClientError(e) && + logger.error(`${keysVerify.name}:${e}`); return; } }; diff --git a/src/client/service/nodesAdd.ts b/src/client/service/nodesAdd.ts index c5c1b8949..92de5581d 100644 --- a/src/client/service/nodesAdd.ts +++ b/src/client/service/nodesAdd.ts @@ -89,7 +89,8 @@ function nodesAdd({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(`${nodesAdd.name}:${e}`); + !clientUtils.isClientClientError(e) && + logger.error(`${nodesAdd.name}:${e}`); return; } }; diff --git a/src/client/service/notificationsClear.ts b/src/client/service/notificationsClear.ts index dbbfc761a..ebcea2af0 100644 --- a/src/client/service/notificationsClear.ts +++ b/src/client/service/notificationsClear.ts @@ -33,7 +33,8 @@ function notificationsClear({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(`${notificationsClear.name}:${e}`); + !clientUtils.isClientClientError(e) && + logger.error(`${notificationsClear.name}:${e}`); return; } }; diff --git a/src/client/service/notificationsRead.ts b/src/client/service/notificationsRead.ts index 051f517c8..f706b5bd2 100644 --- a/src/client/service/notificationsRead.ts +++ b/src/client/service/notificationsRead.ts @@ -75,7 +75,8 @@ function notificationsRead({ return; } catch (e) { callback(grpcUtils.fromError(e)); - !clientUtils.isClientClientError(e) && logger.error(`${notificationsRead.name}:${e}`); + !clientUtils.isClientClientError(e) && + logger.error(`${notificationsRead.name}:${e}`); return; } }; diff --git a/src/client/service/vaultsList.ts b/src/client/service/vaultsList.ts index f8c6a92fb..c7d3da737 100644 --- a/src/client/service/vaultsList.ts +++ b/src/client/service/vaultsList.ts @@ -40,7 +40,8 @@ function vaultsList({ return; } catch (e) { await genWritable.throw(e); - !clientUtils.isClientClientError(e) && logger.error(`${vaultsList.name}:${e}`); + !clientUtils.isClientClientError(e) && + logger.error(`${vaultsList.name}:${e}`); return; } }; diff --git a/tests/nat/noNAT.test.ts b/tests/nat/DMZ.test.ts similarity index 94% rename from tests/nat/noNAT.test.ts rename to tests/nat/DMZ.test.ts index 0690261bb..9f78ffd56 100644 --- a/tests/nat/noNAT.test.ts +++ b/tests/nat/DMZ.test.ts @@ -17,11 +17,9 @@ describeIf( shell.which('iptables') && shell.which('nsenter') && shell.which('unshare'), - 'no NAT', + 'DMZ', () => { - const logger = new Logger('no NAT test', LogLevel.WARN, [ - new StreamHandler(), - ]); + const logger = new Logger('DMZ test', LogLevel.WARN, [new StreamHandler()]); let dataDir: string; beforeEach(async () => { dataDir = await fs.promises.mkdtemp( @@ -142,7 +140,14 @@ describeIf( await testNatUtils.pkExecNs( userPid!, agent1Pid!, - ['nodes', 'add', agent2NodeId, agent2Host, agent2ProxyPort, '--no-ping'], + [ + 'nodes', + 'add', + agent2NodeId, + agent2Host, + agent2ProxyPort, + '--no-ping', + ], { PK_NODE_PATH: agent1NodePath, PK_PASSWORD: password, @@ -152,7 +157,14 @@ describeIf( await testNatUtils.pkExecNs( userPid!, agent2Pid!, - ['nodes', 'add', agent1NodeId, agent1Host, agent1ProxyPort, '--no-ping'], + [ + 'nodes', + 'add', + agent1NodeId, + agent1Host, + agent1ProxyPort, + '--no-ping', + ], { PK_NODE_PATH: agent2NodePath, PK_PASSWORD: password, diff --git a/tests/nat/endpointDependentNAT.test.ts b/tests/nat/endpointDependentNAT.test.ts index 432796226..c815f6d6b 100644 --- a/tests/nat/endpointDependentNAT.test.ts +++ b/tests/nat/endpointDependentNAT.test.ts @@ -31,7 +31,7 @@ describeIf( }); }); test( - 'Node1 behind EDM NAT connects to Node2', + 'node1 behind EDM NAT connects to node2', async () => { const { userPid, @@ -48,7 +48,14 @@ describeIf( await testNatUtils.pkExecNs( userPid!, agent1Pid!, - ['nodes', 'add', agent2NodeId, agent2Host, agent2ProxyPort, '--no-ping'], + [ + 'nodes', + 'add', + agent2NodeId, + agent2Host, + agent2ProxyPort, + '--no-ping', + ], { PK_NODE_PATH: agent1NodePath, PK_PASSWORD: password, @@ -75,7 +82,7 @@ describeIf( global.defaultTimeout * 2, ); test( - 'Node1 connects to Node2 behind EDM NAT', + 'node1 connects to node2 behind EDM NAT', async () => { const { userPid, @@ -95,7 +102,14 @@ describeIf( await testNatUtils.pkExecNs( userPid!, agent2Pid!, - ['nodes', 'add', agent1NodeId, agent1Host, agent1ProxyPort, '--no-ping'], + [ + 'nodes', + 'add', + agent1NodeId, + agent1Host, + agent1ProxyPort, + '--no-ping', + ], { PK_NODE_PATH: agent2NodePath, PK_PASSWORD: password, @@ -139,7 +153,7 @@ describeIf( global.defaultTimeout * 2, ); test( - 'Node1 behind EDM NAT cannot connect to Node2 behind EDM NAT', + 'node1 behind EDM NAT cannot connect to node2 behind EDM NAT', async () => { const { userPid, @@ -152,11 +166,7 @@ describeIf( agent1NodeId, agent2NodeId, tearDownNAT, - } = await testNatUtils.setupNATWithSeedNode( - 'edm', - 'edm', - logger, - ); + } = await testNatUtils.setupNATWithSeedNode('edm', 'edm', logger); // Contact details are retrieved from the seed node, but cannot be used // since port mapping changes between targets in EDM mapping // Node 2 -> Node 1 ping should fail (Node 1 behind NAT) @@ -197,7 +207,7 @@ describeIf( global.defaultTimeout * 2, ); test( - 'Node1 behind EDM NAT cannot connect to Node2 behind EIM NAT', + 'node1 behind EDM NAT cannot connect to node2 behind EIM NAT', async () => { const { userPid, diff --git a/tests/nat/endpointIndependentNAT.test.ts b/tests/nat/endpointIndependentNAT.test.ts index f6023010a..9bdbf2abd 100644 --- a/tests/nat/endpointIndependentNAT.test.ts +++ b/tests/nat/endpointIndependentNAT.test.ts @@ -31,7 +31,7 @@ describeIf( }); }); test( - 'Node1 behind EIM NAT connects to Node2', + 'node1 behind EIM NAT connects to node2', async () => { const { userPid, @@ -48,7 +48,14 @@ describeIf( await testNatUtils.pkExecNs( userPid!, agent1Pid!, - ['nodes', 'add', agent2NodeId, agent2Host, agent2ProxyPort, '--no-ping'], + [ + 'nodes', + 'add', + agent2NodeId, + agent2Host, + agent2ProxyPort, + '--no-ping', + ], { PK_NODE_PATH: agent1NodePath, PK_PASSWORD: password, @@ -75,7 +82,7 @@ describeIf( global.defaultTimeout * 2, ); test( - 'Node1 connects to Node2 behind EIM NAT', + 'node1 connects to node2 behind EIM NAT', async () => { const { userPid, @@ -96,7 +103,14 @@ describeIf( await testNatUtils.pkExecNs( userPid!, agent2Pid!, - ['nodes', 'add', agent1NodeId, agent1Host, agent1ProxyPort, '--no-ping'], + [ + 'nodes', + 'add', + agent1NodeId, + agent1Host, + agent1ProxyPort, + '--no-ping', + ], { PK_NODE_PATH: agent2NodePath, PK_PASSWORD: password, @@ -106,7 +120,14 @@ describeIf( await testNatUtils.pkExecNs( userPid!, agent1Pid!, - ['nodes', 'add', agent2NodeId, agent2Host, agent2ProxyPort, '--no-ping'], + [ + 'nodes', + 'add', + agent2NodeId, + agent2Host, + agent2ProxyPort, + '--no-ping', + ], { PK_NODE_PATH: agent1NodePath, PK_PASSWORD: password, @@ -167,7 +188,7 @@ describeIf( global.defaultTimeout * 2, ); test( - 'Node1 behind EIM NAT connects to Node2 behind EIM NAT', + 'node1 behind EIM NAT connects to node2 behind EIM NAT', async () => { const { userPid, @@ -188,7 +209,14 @@ describeIf( await testNatUtils.pkExecNs( userPid!, agent2Pid!, - ['nodes', 'add', agent1NodeId, agent1Host, agent1ProxyPort, '--no-ping'], + [ + 'nodes', + 'add', + agent1NodeId, + agent1Host, + agent1ProxyPort, + '--no-ping', + ], { PK_NODE_PATH: agent2NodePath, PK_PASSWORD: password, @@ -198,7 +226,14 @@ describeIf( await testNatUtils.pkExecNs( userPid!, agent1Pid!, - ['nodes', 'add', agent2NodeId, agent2Host, agent2ProxyPort, '--no-ping'], + [ + 'nodes', + 'add', + agent2NodeId, + agent2Host, + agent2ProxyPort, + '--no-ping', + ], { PK_NODE_PATH: agent1NodePath, PK_PASSWORD: password, @@ -259,7 +294,7 @@ describeIf( global.defaultTimeout * 2, ); test( - 'Node1 behind EIM NAT connects to Node2 behind EIM NAT via seed node', + 'node1 behind EIM NAT connects to node2 behind EIM NAT via seed node', async () => { const { userPid, @@ -311,7 +346,7 @@ describeIf( global.defaultTimeout * 2, ); test( - 'Node1 behind EIM NAT cannot connect to Node2 behind EDM NAT', + 'node1 behind EIM NAT cannot connect to node2 behind EDM NAT', async () => { const { userPid, diff --git a/tests/nat/utils.ts b/tests/nat/utils.ts index c2efe6b25..4509ebacc 100644 --- a/tests/nat/utils.ts +++ b/tests/nat/utils.ts @@ -10,34 +10,117 @@ import * as testBinUtils from '../bin/utils'; type NATType = 'eim' | 'edm' | 'dmz'; -// Constants for all util functions -// Veth pairs (ends) -const agent1Host = 'agent1'; -const agent2Host = 'agent2'; -const agent1RouterHostInt = 'router1-int'; -const agent1RouterHostExt = 'router1-ext'; -const agent2RouterHostInt = 'router2-int'; -const agent2RouterHostExt = 'router2-ext'; -const router1SeedHost = 'router1-seed'; -const router2SeedHost = 'router2-seed'; -const seedRouter1Host = 'seed-router1'; -const seedRouter2Host = 'seed-router2'; -// Subnets -const agent1HostIp = '10.0.0.2'; -const agent2HostIp = '10.0.0.2'; -const agent1RouterHostIntIp = '10.0.0.1'; -const agent2RouterHostIntIp = '10.0.0.1'; -const agent1RouterHostExtIp = '192.168.0.1'; -const agent2RouterHostExtIp = '192.168.0.2'; -const router1SeedHostIp = '192.168.0.1'; -const seedHostIp = '192.168.0.3'; -const router2SeedHostIp = '192.168.0.2'; -// Subnet mask -const subnetMask = '/24'; -// Ports -const agent1Port = '55551'; -const agent2Port = '55552'; -const mappedPort = '55555'; +/** + * Veth end for Agent 1 + * Connects to Router 1 + */ +const AGENT1_VETH = 'agent1'; +/** + * Veth end for Agent 2 + * Connects to Router 2 + */ +const AGENT2_VETH = 'agent2'; +/** + * Internal veth end for Router 1 + * Connects to Agent 1 + */ +const ROUTER1_VETH_INT = 'router1-int'; +/** + * External veth end for Router 1 + * Connects to Router 2 + */ +const ROUTER1_VETH_EXT = 'router1-ext'; +/** + * Internal veth end for Router 2 + * Connects to Agent 2 + */ +const ROUTER2_VETH_INT = 'router2-int'; +/** + * External veth end for Router 2 + * Connects to Router 1 + */ +const ROUTER2_VETH_EXT = 'router2-ext'; +/** + * External veth end for Router 1 + * Connects to a seed node + */ +const ROUTER1_VETH_SEED = 'router1-seed'; +/** + * External veth end for Router 2 + * Connects to a seed node + */ +const ROUTER2_VETH_SEED = 'router2-seed'; +/** + * Veth end for a seed node + * Connects to Router 1 + */ +const SEED_VETH_ROUTER1 = 'seed-router1'; +/** + * Veth end for a seed node + * Connects to Router 2 + */ +const SEED_VETH_ROUTER2 = 'seed-router2'; + +/** + * Subnet for Agent 1 + */ +const AGENT1_HOST = '10.0.0.2'; +/** + * Subnet for Agent 2 + */ +const AGENT2_HOST = '10.0.0.2'; +/** + * Subnet for internal communication from Router 1 + * Forwards to Agent 1 + */ +const ROUTER1_HOST_INT = '10.0.0.1'; +/** + * Subnet for internal communication from Router 2 + * Forwards to Agent 2 + */ +const ROUTER2_HOST_INT = '10.0.0.1'; +/** + * Subnet for external communication from Router 1 + * Forwards to Router 2 + */ +const ROUTER1_HOST_EXT = '192.168.0.1'; +/** + * Subnet for external communication from Router 2 + * Forwards to Router 1 + */ +const ROUTER2_HOST_EXT = '192.168.0.2'; +/** + * Subnet for external communication from Router 1 + * Forwards to a seed node + */ +const ROUTER1_HOST_SEED = '192.168.0.1'; +/** + * Subnet for external communication from a seed node + */ +const SEED_HOST = '192.168.0.3'; +/** + * Subnet for external communication from Router 2 + * Forwards to a seed node + */ +const ROUTER2_HOST_SEED = '192.168.0.2'; + +/** + * Subnet mask + */ +const SUBNET_MASK = '/24'; + +/** + * Port on Agent 1 + */ +const AGENT1_PORT = '55551'; +/** + * Port on Agent 2 + */ +const AGENT2_PORT = '55552'; +/** + * Mapped port for DMZ + */ +const DMZ_PORT = '55555'; /** * Formats the command to enter a namespace to run a process inside it @@ -172,12 +255,12 @@ async function setupNetworkNamespaceInterfaces( 'ip', 'link', 'add', - agent1Host, + AGENT1_VETH, 'type', 'veth', 'peer', 'name', - agent1RouterHostInt, + ROUTER1_VETH_INT, ]; logger.info(['nsenter', ...args].join(' ')); await testBinUtils.exec('nsenter', args); @@ -186,12 +269,12 @@ async function setupNetworkNamespaceInterfaces( 'ip', 'link', 'add', - agent1RouterHostExt, + ROUTER1_VETH_EXT, 'type', 'veth', 'peer', 'name', - agent2RouterHostExt, + ROUTER2_VETH_EXT, ]; logger.info(['nsenter', ...args].join(' ')); await testBinUtils.exec('nsenter', args); @@ -200,12 +283,12 @@ async function setupNetworkNamespaceInterfaces( 'ip', 'link', 'add', - agent2RouterHostInt, + ROUTER2_VETH_INT, 'type', 'veth', 'peer', 'name', - agent2Host, + AGENT2_VETH, ]; logger.info(['nsenter', ...args].join(' ')); await testBinUtils.exec('nsenter', args); @@ -216,7 +299,7 @@ async function setupNetworkNamespaceInterfaces( 'link', 'set', 'dev', - agent1RouterHostInt, + ROUTER1_VETH_INT, 'netns', router1NetnsPid.toString(), ]; @@ -228,7 +311,7 @@ async function setupNetworkNamespaceInterfaces( 'link', 'set', 'dev', - agent2RouterHostExt, + ROUTER2_VETH_EXT, 'netns', router2NetnsPid.toString(), ]; @@ -240,7 +323,7 @@ async function setupNetworkNamespaceInterfaces( 'link', 'set', 'dev', - agent2Host, + AGENT2_VETH, 'netns', agent2NetnsPid.toString(), ]; @@ -252,7 +335,7 @@ async function setupNetworkNamespaceInterfaces( 'ip', 'link', 'set', - agent1Host, + AGENT1_VETH, 'up', ]; logger.info(['nsenter', ...args].join(' ')); @@ -262,7 +345,7 @@ async function setupNetworkNamespaceInterfaces( 'ip', 'link', 'set', - agent1RouterHostInt, + ROUTER1_VETH_INT, 'up', ]; logger.info(['nsenter', ...args].join(' ')); @@ -272,7 +355,7 @@ async function setupNetworkNamespaceInterfaces( 'ip', 'link', 'set', - agent1RouterHostExt, + ROUTER1_VETH_EXT, 'up', ]; logger.info(['nsenter', ...args].join(' ')); @@ -282,7 +365,7 @@ async function setupNetworkNamespaceInterfaces( 'ip', 'link', 'set', - agent2RouterHostExt, + ROUTER2_VETH_EXT, 'up', ]; logger.info(['nsenter', ...args].join(' ')); @@ -292,7 +375,7 @@ async function setupNetworkNamespaceInterfaces( 'ip', 'link', 'set', - agent2RouterHostInt, + ROUTER2_VETH_INT, 'up', ]; logger.info(['nsenter', ...args].join(' ')); @@ -302,7 +385,7 @@ async function setupNetworkNamespaceInterfaces( 'ip', 'link', 'set', - agent2Host, + AGENT2_VETH, 'up', ]; logger.info(['nsenter', ...args].join(' ')); @@ -313,9 +396,9 @@ async function setupNetworkNamespaceInterfaces( 'ip', 'addr', 'add', - `${agent1HostIp}${subnetMask}`, + `${AGENT1_HOST}${SUBNET_MASK}`, 'dev', - agent1Host, + AGENT1_VETH, ]; logger.info(['nsenter', ...args].join(' ')); await testBinUtils.exec('nsenter', args); @@ -324,9 +407,9 @@ async function setupNetworkNamespaceInterfaces( 'ip', 'addr', 'add', - `${agent1RouterHostIntIp}${subnetMask}`, + `${ROUTER1_HOST_INT}${SUBNET_MASK}`, 'dev', - agent1RouterHostInt, + ROUTER1_VETH_INT, ]; logger.info(['nsenter', ...args].join(' ')); await testBinUtils.exec('nsenter', args); @@ -335,9 +418,9 @@ async function setupNetworkNamespaceInterfaces( 'ip', 'addr', 'add', - `${agent1RouterHostExtIp}${subnetMask}`, + `${ROUTER1_HOST_EXT}${SUBNET_MASK}`, 'dev', - agent1RouterHostExt, + ROUTER1_VETH_EXT, ]; logger.info(['nsenter', ...args].join(' ')); await testBinUtils.exec('nsenter', args); @@ -346,9 +429,9 @@ async function setupNetworkNamespaceInterfaces( 'ip', 'addr', 'add', - `${agent2RouterHostExtIp}${subnetMask}`, + `${ROUTER2_HOST_EXT}${SUBNET_MASK}`, 'dev', - agent2RouterHostExt, + ROUTER2_VETH_EXT, ]; logger.info(['nsenter', ...args].join(' ')); await testBinUtils.exec('nsenter', args); @@ -357,9 +440,9 @@ async function setupNetworkNamespaceInterfaces( 'ip', 'addr', 'add', - `${agent2RouterHostIntIp}${subnetMask}`, + `${ROUTER2_HOST_INT}${SUBNET_MASK}`, 'dev', - agent2RouterHostInt, + ROUTER2_VETH_INT, ]; logger.info(['nsenter', ...args].join(' ')); await testBinUtils.exec('nsenter', args); @@ -368,9 +451,9 @@ async function setupNetworkNamespaceInterfaces( 'ip', 'addr', 'add', - `${agent2HostIp}${subnetMask}`, + `${AGENT2_HOST}${SUBNET_MASK}`, 'dev', - agent2Host, + AGENT2_VETH, ]; logger.info(['nsenter', ...args].join(' ')); await testBinUtils.exec('nsenter', args); @@ -382,7 +465,7 @@ async function setupNetworkNamespaceInterfaces( 'add', 'default', 'via', - agent1RouterHostIntIp, + ROUTER1_HOST_INT, ]; logger.info(['nsenter', ...args].join(' ')); await testBinUtils.exec('nsenter', args); @@ -393,7 +476,7 @@ async function setupNetworkNamespaceInterfaces( 'add', 'default', 'via', - agent2RouterHostExtIp, + ROUTER2_HOST_EXT, ]; logger.info(['nsenter', ...args].join(' ')); await testBinUtils.exec('nsenter', args); @@ -404,7 +487,7 @@ async function setupNetworkNamespaceInterfaces( 'add', 'default', 'via', - agent1RouterHostExtIp, + ROUTER1_HOST_EXT, ]; logger.info(['nsenter', ...args].join(' ')); await testBinUtils.exec('nsenter', args); @@ -415,7 +498,7 @@ async function setupNetworkNamespaceInterfaces( 'add', 'default', 'via', - agent2RouterHostIntIp, + ROUTER2_HOST_INT, ]; logger.info(['nsenter', ...args].join(' ')); await testBinUtils.exec('nsenter', args); @@ -457,12 +540,12 @@ async function setupSeedNamespaceInterfaces( 'ip', 'link', 'add', - router1SeedHost, + ROUTER1_VETH_SEED, 'type', 'veth', 'peer', 'name', - seedRouter1Host, + SEED_VETH_ROUTER1, ]; logger.info(['nsenter', ...args].join(' ')); await testBinUtils.exec('nsenter', args); @@ -471,12 +554,12 @@ async function setupSeedNamespaceInterfaces( 'ip', 'link', 'add', - router2SeedHost, + ROUTER2_VETH_SEED, 'type', 'veth', 'peer', 'name', - seedRouter2Host, + SEED_VETH_ROUTER2, ]; logger.info(['nsenter', ...args].join(' ')); await testBinUtils.exec('nsenter', args); @@ -487,7 +570,7 @@ async function setupSeedNamespaceInterfaces( 'link', 'set', 'dev', - seedRouter1Host, + SEED_VETH_ROUTER1, 'netns', seedNetnsPid.toString(), ]; @@ -499,7 +582,7 @@ async function setupSeedNamespaceInterfaces( 'link', 'set', 'dev', - seedRouter2Host, + SEED_VETH_ROUTER2, 'netns', seedNetnsPid.toString(), ]; @@ -511,7 +594,7 @@ async function setupSeedNamespaceInterfaces( 'ip', 'link', 'set', - router1SeedHost, + ROUTER1_VETH_SEED, 'up', ]; logger.info(['nsenter', ...args].join(' ')); @@ -521,7 +604,7 @@ async function setupSeedNamespaceInterfaces( 'ip', 'link', 'set', - seedRouter1Host, + SEED_VETH_ROUTER1, 'up', ]; logger.info(['nsenter', ...args].join(' ')); @@ -531,7 +614,7 @@ async function setupSeedNamespaceInterfaces( 'ip', 'link', 'set', - seedRouter2Host, + SEED_VETH_ROUTER2, 'up', ]; logger.info(['nsenter', ...args].join(' ')); @@ -541,7 +624,7 @@ async function setupSeedNamespaceInterfaces( 'ip', 'link', 'set', - router2SeedHost, + ROUTER2_VETH_SEED, 'up', ]; logger.info(['nsenter', ...args].join(' ')); @@ -552,9 +635,9 @@ async function setupSeedNamespaceInterfaces( 'ip', 'addr', 'add', - `${router1SeedHostIp}${subnetMask}`, + `${ROUTER1_HOST_SEED}${SUBNET_MASK}`, 'dev', - router1SeedHost, + ROUTER1_VETH_SEED, ]; logger.info(['nsenter', ...args].join(' ')); await testBinUtils.exec('nsenter', args); @@ -563,9 +646,9 @@ async function setupSeedNamespaceInterfaces( 'ip', 'addr', 'add', - `${seedHostIp}${subnetMask}`, + `${SEED_HOST}${SUBNET_MASK}`, 'dev', - seedRouter1Host, + SEED_VETH_ROUTER1, ]; logger.info(['nsenter', ...args].join(' ')); await testBinUtils.exec('nsenter', args); @@ -574,9 +657,9 @@ async function setupSeedNamespaceInterfaces( 'ip', 'addr', 'add', - `${seedHostIp}${subnetMask}`, + `${SEED_HOST}${SUBNET_MASK}`, 'dev', - seedRouter2Host, + SEED_VETH_ROUTER2, ]; logger.info(['nsenter', ...args].join(' ')); await testBinUtils.exec('nsenter', args); @@ -585,9 +668,9 @@ async function setupSeedNamespaceInterfaces( 'ip', 'addr', 'add', - `${router2SeedHostIp}${subnetMask}`, + `${ROUTER2_HOST_SEED}${SUBNET_MASK}`, 'dev', - router2SeedHost, + ROUTER2_VETH_SEED, ]; logger.info(['nsenter', ...args].join(' ')); await testBinUtils.exec('nsenter', args); @@ -597,9 +680,9 @@ async function setupSeedNamespaceInterfaces( 'ip', 'route', 'add', - seedHostIp, + SEED_HOST, 'dev', - router1SeedHost, + ROUTER1_VETH_SEED, ]; logger.info(['nsenter', ...args].join(' ')); await testBinUtils.exec('nsenter', args); @@ -608,9 +691,9 @@ async function setupSeedNamespaceInterfaces( 'ip', 'route', 'add', - seedHostIp, + SEED_HOST, 'dev', - router2SeedHost, + ROUTER2_VETH_SEED, ]; logger.info(['nsenter', ...args].join(' ')); await testBinUtils.exec('nsenter', args); @@ -619,9 +702,9 @@ async function setupSeedNamespaceInterfaces( 'ip', 'route', 'add', - router1SeedHostIp, + ROUTER1_HOST_SEED, 'dev', - seedRouter1Host, + SEED_VETH_ROUTER1, ]; logger.info(['nsenter', ...args].join(' ')); await testBinUtils.exec('nsenter', args); @@ -630,9 +713,9 @@ async function setupSeedNamespaceInterfaces( 'ip', 'route', 'add', - router2SeedHostIp, + ROUTER2_HOST_SEED, 'dev', - seedRouter2Host, + SEED_VETH_ROUTER2, ]; logger.info(['nsenter', ...args].join(' ')); await testBinUtils.exec('nsenter', args); @@ -806,13 +889,13 @@ async function setupDMZ( '--protocol', 'udp', '--source', - `${agentIp}${subnetMask}`, + `${agentIp}${SUBNET_MASK}`, '--out-interface', routerExt, '--jump', 'SNAT', '--to-source', - `${routerExtIp}:${mappedPort}`, + `${routerExtIp}:${DMZ_PORT}`, ]; const preroutingCommand = [ ...nsenter(usrnsPid, routerNsPid), @@ -824,7 +907,7 @@ async function setupDMZ( '--protocol', 'udp', '--destination-port', - mappedPort, + DMZ_PORT, '--in-interface', routerExt, '--jump', @@ -863,7 +946,7 @@ async function setupNATEndpointIndependentMapping( '--protocol', 'udp', '--source', - `${agentIp}${subnetMask}`, + `${agentIp}${SUBNET_MASK}`, '--out-interface', routerExt, '--jump', @@ -976,19 +1059,19 @@ async function setupNATWithSeedNode( await setupDMZ( usrns.pid!, router1Netns.pid!, - agent1HostIp, - agent1Port, - agent1RouterHostExt, - agent1RouterHostExtIp, + AGENT1_HOST, + AGENT1_PORT, + ROUTER1_VETH_EXT, + ROUTER1_HOST_EXT, logger, ); await setupDMZ( usrns.pid!, router1Netns.pid!, - agent1HostIp, - agent1Port, - router1SeedHost, - router1SeedHostIp, + AGENT1_HOST, + AGENT1_PORT, + ROUTER1_VETH_SEED, + ROUTER1_HOST_SEED, logger, ); break; @@ -997,17 +1080,17 @@ async function setupNATWithSeedNode( await setupNATEndpointIndependentMapping( usrns.pid!, router1Netns.pid!, - agent1HostIp, - agent1RouterHostExt, - agent1RouterHostInt, + AGENT1_HOST, + ROUTER1_VETH_EXT, + ROUTER1_VETH_INT, logger, ); await setupNATEndpointIndependentMapping( usrns.pid!, router1Netns.pid!, - agent1HostIp, - router1SeedHost, - agent1RouterHostInt, + AGENT1_HOST, + ROUTER1_VETH_SEED, + ROUTER1_VETH_INT, logger, ); break; @@ -1016,13 +1099,13 @@ async function setupNATWithSeedNode( await setupNATEndpointDependentMapping( usrns.pid!, router1Netns.pid!, - agent1RouterHostExt, + ROUTER1_VETH_EXT, logger, ); await setupNATEndpointDependentMapping( usrns.pid!, router1Netns.pid!, - router1SeedHost, + ROUTER1_VETH_SEED, logger, ); break; @@ -1033,19 +1116,19 @@ async function setupNATWithSeedNode( await setupDMZ( usrns.pid!, router2Netns.pid!, - agent2HostIp, - agent2Port, - agent2RouterHostExt, - agent2RouterHostExtIp, + AGENT2_HOST, + AGENT2_PORT, + ROUTER2_VETH_EXT, + ROUTER2_HOST_EXT, logger, ); await setupDMZ( usrns.pid!, router2Netns.pid!, - agent2HostIp, - agent2Port, - router2SeedHost, - router2SeedHostIp, + AGENT2_HOST, + AGENT2_PORT, + ROUTER2_VETH_SEED, + ROUTER2_HOST_SEED, logger, ); break; @@ -1054,17 +1137,17 @@ async function setupNATWithSeedNode( await setupNATEndpointIndependentMapping( usrns.pid!, router2Netns.pid!, - agent2HostIp, - agent2RouterHostExt, - agent2RouterHostInt, + AGENT2_HOST, + ROUTER2_VETH_EXT, + ROUTER2_VETH_INT, logger, ); await setupNATEndpointIndependentMapping( usrns.pid!, router2Netns.pid!, - agent2HostIp, - router2SeedHost, - agent2RouterHostInt, + AGENT2_HOST, + ROUTER2_VETH_SEED, + ROUTER2_VETH_INT, logger, ); break; @@ -1073,13 +1156,13 @@ async function setupNATWithSeedNode( await setupNATEndpointDependentMapping( usrns.pid!, router2Netns.pid!, - agent2RouterHostExt, + ROUTER2_VETH_EXT, logger, ); await setupNATEndpointDependentMapping( usrns.pid!, router2Netns.pid!, - router2SeedHost, + ROUTER2_VETH_SEED, logger, ); break; @@ -1148,15 +1231,15 @@ async function setupNATWithSeedNode( '--client-host', '127.0.0.1', '--proxy-host', - `${agent1HostIp}`, + `${AGENT1_HOST}`, '--proxy-port', - `${agent1Port}`, + `${AGENT1_PORT}`, '--workers', '0', '--connection-timeout', '1000', '--seed-nodes', - `${nodeIdSeed}@${seedHostIp}:${proxyPortSeed}`, + `${nodeIdSeed}@${SEED_HOST}:${proxyPortSeed}`, '--verbose', '--format', 'json', @@ -1186,15 +1269,15 @@ async function setupNATWithSeedNode( '--client-host', '127.0.0.1', '--proxy-host', - `${agent2HostIp}`, + `${AGENT2_HOST}`, '--proxy-port', - `${agent2Port}`, + `${AGENT2_PORT}`, '--workers', '0', '--connection-timeout', '1000', '--seed-nodes', - `${nodeIdSeed}@${seedHostIp}:${proxyPortSeed}`, + `${nodeIdSeed}@${SEED_HOST}:${proxyPortSeed}`, '--verbose', '--format', 'json', @@ -1272,10 +1355,10 @@ async function setupNAT( await setupDMZ( usrns.pid!, router1Netns.pid!, - agent1HostIp, - agent1Port, - agent1RouterHostExt, - agent1RouterHostExtIp, + AGENT1_HOST, + AGENT1_PORT, + ROUTER1_VETH_EXT, + ROUTER1_HOST_EXT, logger, ); break; @@ -1284,9 +1367,9 @@ async function setupNAT( await setupNATEndpointIndependentMapping( usrns.pid!, router1Netns.pid!, - agent1HostIp, - agent1RouterHostExt, - agent1RouterHostInt, + AGENT1_HOST, + ROUTER1_VETH_EXT, + ROUTER1_VETH_INT, logger, ); break; @@ -1295,7 +1378,7 @@ async function setupNAT( await setupNATEndpointDependentMapping( usrns.pid!, router1Netns.pid!, - agent1RouterHostExt, + ROUTER1_VETH_EXT, logger, ); break; @@ -1306,10 +1389,10 @@ async function setupNAT( await setupDMZ( usrns.pid!, router2Netns.pid!, - agent2HostIp, - agent2Port, - agent2RouterHostExt, - agent2RouterHostExtIp, + AGENT2_HOST, + AGENT2_PORT, + ROUTER2_VETH_EXT, + ROUTER2_HOST_EXT, logger, ); break; @@ -1318,9 +1401,9 @@ async function setupNAT( await setupNATEndpointIndependentMapping( usrns.pid!, router2Netns.pid!, - agent2HostIp, - agent2RouterHostExt, - agent2RouterHostInt, + AGENT2_HOST, + ROUTER2_VETH_EXT, + ROUTER2_VETH_INT, logger, ); break; @@ -1329,7 +1412,7 @@ async function setupNAT( await setupNATEndpointDependentMapping( usrns.pid!, router2Netns.pid!, - agent2RouterHostExt, + ROUTER2_VETH_EXT, logger, ); break; @@ -1356,9 +1439,9 @@ async function setupNAT( '--client-host', '127.0.0.1', '--proxy-host', - `${agent1HostIp}`, + `${AGENT1_HOST}`, '--proxy-port', - `${agent1Port}`, + `${AGENT1_PORT}`, '--connection-timeout', '1000', '--workers', @@ -1392,9 +1475,9 @@ async function setupNAT( '--client-host', '127.0.0.1', '--proxy-host', - `${agent2HostIp}`, + `${AGENT2_HOST}`, '--proxy-port', - `${agent2Port}`, + `${AGENT2_PORT}`, '--connection-timeout', '1000', '--workers', @@ -1424,17 +1507,11 @@ async function setupNAT( agent1NodePath: path.join(dataDir, 'agent1'), agent2NodePath: path.join(dataDir, 'agent2'), agent1NodeId: nodeId1, - agent1Host: agent1RouterHostExtIp, - agent1ProxyPort: - agent1NAT === 'dmz' - ? mappedPort - : agent1Port, + agent1Host: ROUTER1_HOST_EXT, + agent1ProxyPort: agent1NAT === 'dmz' ? DMZ_PORT : AGENT1_PORT, agent2NodeId: nodeId2, - agent2Host: agent2RouterHostExtIp, - agent2ProxyPort: - agent2NAT === 'dmz' - ? mappedPort - : agent2Port, + agent2Host: ROUTER2_HOST_EXT, + agent2ProxyPort: agent2NAT === 'dmz' ? DMZ_PORT : AGENT2_PORT, tearDownNAT: async () => { agent2.kill('SIGTERM'); await testBinUtils.processExit(agent2); From 4f8d17e154606ceb2b8eba0f25eb67aed71a72cb Mon Sep 17 00:00:00 2001 From: Emma Casolin Date: Fri, 24 Jun 2022 13:58:50 +1000 Subject: [PATCH 10/15] fix: relay node for hole punch message now re-writes proxy address A relay node for a hole punch message was previously not modifying the proxy address in the message (which is the "return address" used to contact the source node). For nodes behind a NAT, who do not know their own public address, they rely on this overwriting so that nodes do not try to contact them on their private, inaccessible address. --- src/nodes/NodeConnectionManager.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/nodes/NodeConnectionManager.ts b/src/nodes/NodeConnectionManager.ts index ffa54acaa..9b1189651 100644 --- a/src/nodes/NodeConnectionManager.ts +++ b/src/nodes/NodeConnectionManager.ts @@ -698,11 +698,20 @@ class NodeConnectionManager { message: nodesPB.Relay, timer?: Timer, ): Promise { + // First check if we already have an existing ID -> address record + // If we're relaying then we trust our own node graph records over + // what was provided in the message + const sourceNode = validationUtils.parseNodeId(message.getSrcId()); + const knownAddress = (await this.nodeGraph.getNode(sourceNode))?.address; + let proxyAddress = message.getProxyAddress(); + if (knownAddress != null) { + proxyAddress = networkUtils.buildAddress(knownAddress.host as Host, knownAddress.port); + } await this.sendHolePunchMessage( validationUtils.parseNodeId(message.getTargetId()), - validationUtils.parseNodeId(message.getSrcId()), + sourceNode, validationUtils.parseNodeId(message.getTargetId()), - message.getProxyAddress(), + proxyAddress, Buffer.from(message.getSignature()), timer, ); From 1095f8f71261efd88a30846d2b90a4ed6f40d0a6 Mon Sep 17 00:00:00 2001 From: Brian Botha Date: Fri, 24 Jun 2022 15:32:35 +1000 Subject: [PATCH 11/15] feat: added ability to pause `refreshBucketQueue` This will allow us to disable the queue for testing. --- src/nodes/NodeManager.ts | 17 ++++++++++ tests/nodes/NodeManager.test.ts | 59 +++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/src/nodes/NodeManager.ts b/src/nodes/NodeManager.ts index bb264de4f..7245ab5c4 100644 --- a/src/nodes/NodeManager.ts +++ b/src/nodes/NodeManager.ts @@ -47,6 +47,7 @@ class NodeManager { protected refreshBucketQueueRunner: Promise; protected refreshBucketQueuePlug_: PromiseDeconstructed = promise(); protected refreshBucketQueueDrained_: PromiseDeconstructed = promise(); + protected refreshBucketQueuePause_: PromiseDeconstructed = promise(); protected refreshBucketQueueAbortController: AbortController; constructor({ @@ -664,12 +665,25 @@ class NodeManager { await this.refreshBucketQueueDrained_.p; } + public refreshBucketQueuePause() { + this.logger.debug('Pausing refreshBucketQueue'); + this.refreshBucketQueuePause_ = promise(); + } + + public refreshBucketQueueResume() { + this.logger.debug('Resuming refreshBucketQueue'); + this.refreshBucketQueuePause_.resolveP(); + } + private async startRefreshBucketQueue(): Promise { this.refreshBucketQueueRunning = true; this.refreshBucketQueuePlug(); + this.refreshBucketQueueResume(); let iterator: IterableIterator | undefined; this.refreshBucketQueueAbortController = new AbortController(); const pace = async () => { + // Wait if paused + await this.refreshBucketQueuePause_.p; // Wait for plug await this.refreshBucketQueuePlug_.p; if (iterator == null) { @@ -709,14 +723,17 @@ class NodeManager { this.refreshBucketQueueAbortController.abort(); this.refreshBucketQueueRunning = false; this.refreshBucketQueueUnplug(); + this.refreshBucketQueueResume(); } private refreshBucketQueuePlug() { + this.logger.debug('refresh bucket queue has plugged'); this.refreshBucketQueuePlug_ = promise(); this.refreshBucketQueueDrained_?.resolveP(); } private refreshBucketQueueUnplug() { + this.logger.debug('refresh bucket queue has unplugged'); this.refreshBucketQueueDrained_ = promise(); this.refreshBucketQueuePlug_?.resolveP(); } diff --git a/tests/nodes/NodeManager.test.ts b/tests/nodes/NodeManager.test.ts index 66bd40999..d32c869d9 100644 --- a/tests/nodes/NodeManager.test.ts +++ b/tests/nodes/NodeManager.test.ts @@ -1086,4 +1086,63 @@ describe(`${NodeManager.name} test`, () => { await queue.stop(); } }); + test('should pause, resume and stop queue while paused', async () => { + const refreshBucketTimeout = 1000000; + const queue = new Queue({ logger }); + const nodeManager = new NodeManager({ + db, + sigchain: {} as Sigchain, + keyManager, + nodeGraph, + nodeConnectionManager: dummyNodeConnectionManager, + queue, + refreshBucketTimerDefault: refreshBucketTimeout, + logger, + }); + const mockRefreshBucket = jest.spyOn( + NodeManager.prototype, + 'refreshBucket', + ); + try { + logger.setLevel(LogLevel.DEBUG); + await queue.start(); + await nodeManager.start(); + await nodeConnectionManager.start({ nodeManager }); + mockRefreshBucket.mockImplementation( + async (bucket, options: { signal?: AbortSignal } = {}) => { + const { signal } = { ...options }; + const prom = promise(); + const timer = setTimeout(prom.resolveP, 10); + signal?.addEventListener('abort', () => { + clearTimeout(timer); + prom.rejectP(new nodesErrors.ErrorNodeAborted()); + }); + await prom.p; + }, + ); + nodeManager.refreshBucketQueueAdd(1); + nodeManager.refreshBucketQueueAdd(2); + nodeManager.refreshBucketQueueAdd(3); + nodeManager.refreshBucketQueueAdd(4); + nodeManager.refreshBucketQueueAdd(5); + + // Can pause and resume + nodeManager.refreshBucketQueuePause(); + nodeManager.refreshBucketQueueAdd(6); + nodeManager.refreshBucketQueueAdd(7); + nodeManager.refreshBucketQueueResume(); + await nodeManager.refreshBucketQueueDrained(); + + // Can pause and stop + nodeManager.refreshBucketQueuePause(); + nodeManager.refreshBucketQueueAdd(8); + nodeManager.refreshBucketQueueAdd(9); + nodeManager.refreshBucketQueueAdd(10); + await nodeManager.stop(); + } finally { + mockRefreshBucket.mockRestore(); + await nodeManager.stop(); + await queue.stop(); + } + }); }); From 520b799fc9ff8d5fba251b2634ff26a4e130bf0a Mon Sep 17 00:00:00 2001 From: Emma Casolin Date: Fri, 24 Jun 2022 15:45:15 +1000 Subject: [PATCH 12/15] build: package upgrades to aid testing --- package-lock.json | 28 ++++++++++++++-------------- package.json | 4 ++-- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index b12a9624a..be8b574bf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,12 +10,12 @@ "license": "GPL-3.0", "dependencies": { "@grpc/grpc-js": "1.6.7", - "@matrixai/async-init": "^1.8.0", + "@matrixai/async-init": "^1.8.1", "@matrixai/async-locks": "^2.3.1", "@matrixai/db": "^4.0.5", "@matrixai/errors": "^1.1.1", "@matrixai/id": "^3.3.3", - "@matrixai/logger": "^2.2.0", + "@matrixai/logger": "^2.2.2", "@matrixai/resources": "^1.1.3", "@matrixai/workers": "^1.3.3", "ajv": "^7.0.4", @@ -2537,9 +2537,9 @@ } }, "node_modules/@matrixai/async-init": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@matrixai/async-init/-/async-init-1.8.0.tgz", - "integrity": "sha512-Y2yxG0s5UQQR/WqIvMhud0tRmcGrUynwE/vdbkSsLeLARpAAOmSgbIzP963VqOiAs8/W3FVJgMaMjo4rIaJPtg==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@matrixai/async-init/-/async-init-1.8.1.tgz", + "integrity": "sha512-ZAS1yd/PC+r3NwvT9fEz3OtAm68A8mKXXGdZRcYQF1ajl43jsV8/B4aDwr2oLFlV+RYZgWl7UwjZj4rtoZSycQ==", "dependencies": { "@matrixai/async-locks": "^2.3.1", "@matrixai/errors": "^1.1.1" @@ -2588,9 +2588,9 @@ } }, "node_modules/@matrixai/logger": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@matrixai/logger/-/logger-2.2.0.tgz", - "integrity": "sha512-GNAFEophgqhJZI01s5OuosbxJO/GSmDZU4rfKmNS3l/dbaOuI32RKEv6XNd6O9HpjmX886rGSxks1/rBZw+cRw==" + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@matrixai/logger/-/logger-2.2.2.tgz", + "integrity": "sha512-6/G1svkcFiBMvmIdBv6YbxoLKwMWpXNzt93Cc4XbXXygCQrsn6oYwLvnRk/JNr6uM29M2T+Aa7K1o3n2XMTuLw==" }, "node_modules/@matrixai/resources": { "version": "1.1.3", @@ -13644,9 +13644,9 @@ } }, "@matrixai/async-init": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@matrixai/async-init/-/async-init-1.8.0.tgz", - "integrity": "sha512-Y2yxG0s5UQQR/WqIvMhud0tRmcGrUynwE/vdbkSsLeLARpAAOmSgbIzP963VqOiAs8/W3FVJgMaMjo4rIaJPtg==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@matrixai/async-init/-/async-init-1.8.1.tgz", + "integrity": "sha512-ZAS1yd/PC+r3NwvT9fEz3OtAm68A8mKXXGdZRcYQF1ajl43jsV8/B4aDwr2oLFlV+RYZgWl7UwjZj4rtoZSycQ==", "requires": { "@matrixai/async-locks": "^2.3.1", "@matrixai/errors": "^1.1.1" @@ -13695,9 +13695,9 @@ } }, "@matrixai/logger": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@matrixai/logger/-/logger-2.2.0.tgz", - "integrity": "sha512-GNAFEophgqhJZI01s5OuosbxJO/GSmDZU4rfKmNS3l/dbaOuI32RKEv6XNd6O9HpjmX886rGSxks1/rBZw+cRw==" + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@matrixai/logger/-/logger-2.2.2.tgz", + "integrity": "sha512-6/G1svkcFiBMvmIdBv6YbxoLKwMWpXNzt93Cc4XbXXygCQrsn6oYwLvnRk/JNr6uM29M2T+Aa7K1o3n2XMTuLw==" }, "@matrixai/resources": { "version": "1.1.3", diff --git a/package.json b/package.json index 5fe9c750d..47f502c95 100644 --- a/package.json +++ b/package.json @@ -76,12 +76,12 @@ }, "dependencies": { "@grpc/grpc-js": "1.6.7", - "@matrixai/async-init": "^1.8.0", + "@matrixai/async-init": "^1.8.1", "@matrixai/async-locks": "^2.3.1", "@matrixai/db": "^4.0.5", "@matrixai/errors": "^1.1.1", "@matrixai/id": "^3.3.3", - "@matrixai/logger": "^2.2.0", + "@matrixai/logger": "^2.2.2", "@matrixai/resources": "^1.1.3", "@matrixai/workers": "^1.3.3", "ajv": "^7.0.4", From 1e35180c668911dd7e401986307ed641b45d71ba Mon Sep 17 00:00:00 2001 From: Emma Casolin Date: Fri, 24 Jun 2022 15:45:33 +1000 Subject: [PATCH 13/15] style: lintfix --- src/nodes/NodeConnectionManager.ts | 5 ++++- tests/nat/DMZ.test.ts | 8 ++++---- tests/nat/endpointDependentNAT.test.ts | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/nodes/NodeConnectionManager.ts b/src/nodes/NodeConnectionManager.ts index 9b1189651..bd2288ca7 100644 --- a/src/nodes/NodeConnectionManager.ts +++ b/src/nodes/NodeConnectionManager.ts @@ -705,7 +705,10 @@ class NodeConnectionManager { const knownAddress = (await this.nodeGraph.getNode(sourceNode))?.address; let proxyAddress = message.getProxyAddress(); if (knownAddress != null) { - proxyAddress = networkUtils.buildAddress(knownAddress.host as Host, knownAddress.port); + proxyAddress = networkUtils.buildAddress( + knownAddress.host as Host, + knownAddress.port, + ); } await this.sendHolePunchMessage( validationUtils.parseNodeId(message.getTargetId()), diff --git a/tests/nat/DMZ.test.ts b/tests/nat/DMZ.test.ts index 9f78ffd56..3795dfaf0 100644 --- a/tests/nat/DMZ.test.ts +++ b/tests/nat/DMZ.test.ts @@ -175,7 +175,7 @@ describeIf( ({ exitCode, stdout } = await testNatUtils.pkExecNs( userPid!, agent1Pid!, - ['nodes', 'ping', agent2NodeId, '--format', 'json', '--verbose'], + ['nodes', 'ping', agent2NodeId, '--format', 'json'], { PK_NODE_PATH: agent1NodePath, PK_PASSWORD: password, @@ -190,7 +190,7 @@ describeIf( ({ exitCode, stdout } = await testNatUtils.pkExecNs( userPid!, agent2Pid!, - ['nodes', 'ping', agent1NodeId, '--format', 'json', '--verbose'], + ['nodes', 'ping', agent1NodeId, '--format', 'json'], { PK_NODE_PATH: agent2NodePath, PK_PASSWORD: password, @@ -227,7 +227,7 @@ describeIf( ({ exitCode, stdout } = await testNatUtils.pkExecNs( userPid!, agent1Pid!, - ['nodes', 'ping', agent2NodeId, '--format', 'json', '--verbose'], + ['nodes', 'ping', agent2NodeId, '--format', 'json'], { PK_NODE_PATH: agent1NodePath, PK_PASSWORD: password, @@ -242,7 +242,7 @@ describeIf( ({ exitCode, stdout } = await testNatUtils.pkExecNs( userPid!, agent2Pid!, - ['nodes', 'ping', agent1NodeId, '--format', 'json', '--verbose'], + ['nodes', 'ping', agent1NodeId, '--format', 'json'], { PK_NODE_PATH: agent2NodePath, PK_PASSWORD: password, diff --git a/tests/nat/endpointDependentNAT.test.ts b/tests/nat/endpointDependentNAT.test.ts index c815f6d6b..663293f4a 100644 --- a/tests/nat/endpointDependentNAT.test.ts +++ b/tests/nat/endpointDependentNAT.test.ts @@ -241,7 +241,7 @@ describeIf( ({ exitCode, stdout } = await testNatUtils.pkExecNs( userPid!, agent1Pid!, - ['nodes', 'ping', agent2NodeId, '--format', 'json', '-vv'], + ['nodes', 'ping', agent2NodeId, '--format', 'json'], { PK_NODE_PATH: agent1NodePath, PK_PASSWORD: password, From 856388b65775cf95609f304beb46eab377f35f2b Mon Sep 17 00:00:00 2001 From: Emma Casolin Date: Fri, 24 Jun 2022 16:21:12 +1000 Subject: [PATCH 14/15] feat: added diagrams to DMZ tests #388 --- tests/nat/DMZ.test.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/nat/DMZ.test.ts b/tests/nat/DMZ.test.ts index 3795dfaf0..ae54d2d15 100644 --- a/tests/nat/DMZ.test.ts +++ b/tests/nat/DMZ.test.ts @@ -135,6 +135,15 @@ describeIf( agent2ProxyPort, tearDownNAT, } = await testNatUtils.setupNAT('dmz', 'dmz', logger); + // Namespace1 Namespace2 + // ┌────────────────────────────────────┐ ┌────────────────────────────────────┐ + // │ │ │ │ + // │ ┌────────┐ ┌─────────┐ │ │ ┌─────────┐ ┌────────┐ │ + // │ │ Agent1 ├────────┤ Router1 │ │ │ │ Router2 ├────────┤ Agent2 │ │ + // │ └────────┘ └─────────┘ │ │ └─────────┘ └────────┘ │ + // │ 10.0.0.2:55551 192.168.0.1:55555 │ │ 192.168.0.2:55555 10.0.0.2:55552 │ + // │ │ │ │ + // └────────────────────────────────────┘ └────────────────────────────────────┘ // Since neither node is behind a NAT can directly add eachother's // details using pk nodes add await testNatUtils.pkExecNs( @@ -221,6 +230,15 @@ describeIf( agent2NodeId, tearDownNAT, } = await testNatUtils.setupNATWithSeedNode('dmz', 'dmz', logger); + // Namespace1 Namespace3 Namespace2 + // ┌────────────────────────────────────┐ ┌──────────────────┐ ┌────────────────────────────────────┐ + // │ │ │ │ │ │ + // │ ┌────────┐ ┌─────────┐ │ │ ┌──────────┐ │ │ ┌─────────┐ ┌────────┐ │ + // │ │ Agent1 ├────────┤ Router1 │ │ │ │ SeedNode │ │ │ │ Router2 ├────────┤ Agent2 │ │ + // │ └────────┘ └─────────┘ │ │ └──────────┘ │ │ └─────────┘ └────────┘ │ + // │ 10.0.0.2:55551 192.168.0.1:55555 │ │ 192.168.0.3:PORT │ │ 192.168.0.2:55555 10.0.0.2:55552 │ + // │ │ │ │ │ │ + // └────────────────────────────────────┘ └──────────────────┘ └────────────────────────────────────┘ // Should be able to ping straight away using the details from the // seed node let exitCode, stdout; From 7c91acecd67420d16419c14455067dc7842849d2 Mon Sep 17 00:00:00 2001 From: Brian Botha Date: Fri, 24 Jun 2022 16:22:44 +1000 Subject: [PATCH 15/15] fix: async bug with composing connections the composed flag was set at the beginning of the compose function causing another function to throw an error due to an undefined property if it was called at the same time. --- src/network/ConnectionReverse.ts | 2 +- src/nodes/NodeConnection.ts | 2 +- src/nodes/NodeConnectionManager.ts | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/network/ConnectionReverse.ts b/src/network/ConnectionReverse.ts index ada434649..d7000d9d0 100644 --- a/src/network/ConnectionReverse.ts +++ b/src/network/ConnectionReverse.ts @@ -217,7 +217,6 @@ class ConnectionReverse extends Connection { if (this._composed) { throw new networkErrors.ErrorConnectionComposed(); } - this._composed = true; this.logger.info('Composing Connection Reverse'); // Promise for secure establishment const { p: secureP, resolveP: resolveSecureP } = promise(); @@ -306,6 +305,7 @@ class ConnectionReverse extends Connection { }); this.clientCertChain = clientCertChain; this.logger.info('Composed Connection Reverse'); + this._composed = true; } catch (e) { this._composed = false; throw e; diff --git a/src/nodes/NodeConnection.ts b/src/nodes/NodeConnection.ts index f4f4fbfb3..c90260afc 100644 --- a/src/nodes/NodeConnection.ts +++ b/src/nodes/NodeConnection.ts @@ -128,7 +128,7 @@ class NodeConnection { }, timer: timer, }), - holePunchPromises, + ...holePunchPromises, ]); // 5. When finished, you have a connection to other node // The GRPCClient is ready to be used for requests diff --git a/src/nodes/NodeConnectionManager.ts b/src/nodes/NodeConnectionManager.ts index bd2288ca7..30550b6a4 100644 --- a/src/nodes/NodeConnectionManager.ts +++ b/src/nodes/NodeConnectionManager.ts @@ -756,6 +756,7 @@ class NodeConnectionManager { const signature = await this.keyManager.signWithRootKeyPair( Buffer.from(proxyAddress), ); + // FIXME: this needs to handle aborting const holePunchPromises = Array.from(this.getSeedNodes(), (seedNodeId) => { return this.sendHolePunchMessage( seedNodeId,