diff --git a/tests/nat/endpointDependentNAT.test.ts b/tests/nat/endpointDependentNAT.test.ts index 1f5a6e22c..8bced4ff4 100644 --- a/tests/nat/endpointDependentNAT.test.ts +++ b/tests/nat/endpointDependentNAT.test.ts @@ -30,8 +30,21 @@ describe('endpoint dependent NAT traversal', () => { 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, @@ -57,22 +70,83 @@ describe('endpoint dependent NAT traversal', () => { const { userPid, agent1Pid, + agent2Pid, password, dataDir, agent1NodePath, + agent2NodePath, + agent1NodeId, + agent1Host, + agent1ProxyPort, agent2NodeId, + agent2Host, + agent2ProxyPort, tearDownNAT, - } = await testNatUtils.setupNAT('dmz', 'edm', logger); - const { exitCode, stdout } = await testNatUtils.pkExecNs( + } = 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', 'ping', agent2NodeId, '--format', 'json'], + ['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, @@ -88,13 +162,40 @@ describe('endpoint dependent NAT traversal', () => { const { userPid, agent1Pid, + agent2Pid, password, dataDir, agent1NodePath, + agent2NodePath, + agent1NodeId, agent2NodeId, tearDownNAT, - } = await testNatUtils.setupNAT('edm', 'edm', logger); - const { exitCode, stdout } = await testNatUtils.pkExecNs( + } = 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'], @@ -103,11 +204,11 @@ describe('endpoint dependent NAT traversal', () => { PK_PASSWORD: password, }, dataDir, - ); - expect(exitCode).not.toBe(0); + )); + expect(exitCode).toBe(1); expect(JSON.parse(stdout)).toEqual({ success: false, - message: `No response received`, + message: 'No response received', }); await tearDownNAT(); }, @@ -119,26 +220,46 @@ describe('endpoint dependent NAT traversal', () => { const { userPid, agent1Pid, + agent2Pid, password, dataDir, agent1NodePath, + agent2NodePath, + agent1NodeId, agent2NodeId, tearDownNAT, - } = await testNatUtils.setupNAT('edm', 'eim', logger); - const { exitCode, stdout } = await testNatUtils.pkExecNs( + } = 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'], + ['nodes', 'ping', agent2NodeId, '--format', 'json', '-vv'], { PK_NODE_PATH: agent1NodePath, PK_PASSWORD: password, }, dataDir, - ); - expect(exitCode).not.toBe(0); + )); + expect(exitCode).toBe(1); expect(JSON.parse(stdout)).toEqual({ success: false, - message: `No response received`, + message: 'No response received', }); await tearDownNAT(); }, diff --git a/tests/nat/endpointIndependentNAT.test.ts b/tests/nat/endpointIndependentNAT.test.ts index 99df25159..c6b8f85a2 100644 --- a/tests/nat/endpointIndependentNAT.test.ts +++ b/tests/nat/endpointIndependentNAT.test.ts @@ -83,7 +83,6 @@ describe('endpoint independent NAT traversal', () => { agent2ProxyPort, tearDownNAT, } = await testNatUtils.setupNAT('dmz', 'eim', logger); - // Since node2 is behind a NAT we need it to contact us first await testNatUtils.pkExecNs( userPid, agent2Pid, @@ -94,8 +93,6 @@ describe('endpoint independent NAT traversal', () => { }, dataDir, ); - // This add call can be removed once nodes add connection info of - // connecting nodes await testNatUtils.pkExecNs( userPid, agent1Pid, @@ -106,7 +103,25 @@ describe('endpoint independent NAT traversal', () => { }, dataDir, ); - await testNatUtils.pkExecNs( + // 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'], @@ -115,9 +130,14 @@ describe('endpoint independent NAT traversal', () => { PK_PASSWORD: password, }, dataDir, - ); - // We should now be able to ping back - const { exitCode, stdout } = await testNatUtils.pkExecNs( + )); + 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'], @@ -126,7 +146,7 @@ describe('endpoint independent NAT traversal', () => { PK_PASSWORD: password, }, dataDir, - ); + )); expect(exitCode).toBe(0); expect(JSON.parse(stdout)).toEqual({ success: true, @@ -148,32 +168,77 @@ describe('endpoint independent NAT traversal', () => { agent1NodePath, agent2NodePath, agent1NodeId, + agent1Host, + agent1ProxyPort, agent2NodeId, + agent2Host, + agent2ProxyPort, tearDownNAT, - } = await testNatUtils.setupNATWithSeedNode('eim', 'eim', logger); - // Since node2 is behind a NAT we need it to attempt to contact us first - // This won't be successfull, but will allow to get past its router with - // our own ping + } = await testNatUtils.setupNAT('dmz', 'eim', logger); await testNatUtils.pkExecNs( userPid, agent2Pid, - ['nodes', 'ping', agent1NodeId, '--format', 'json', '-vv'], + ['nodes', 'add', agent1NodeId, agent1Host, agent1ProxyPort], { PK_NODE_PATH: agent2NodePath, PK_PASSWORD: password, }, dataDir, ); - const { exitCode, stdout } = await testNatUtils.pkExecNs( + await testNatUtils.pkExecNs( userPid, agent1Pid, - ['nodes', 'ping', agent2NodeId, '--format', 'json', '-vv'], + ['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, @@ -181,10 +246,10 @@ describe('endpoint independent NAT traversal', () => { }); await tearDownNAT(); }, - global.defaultTimeout * 20, + global.defaultTimeout * 2, ); - test.skip( - 'Node1 behind EIM NAT cannot connect to Node2 behind EDM NAT', + test( + 'Node1 behind EIM NAT connects to Node2 behind EIM NAT via seed node', async () => { const { userPid, @@ -195,63 +260,108 @@ describe('endpoint independent NAT traversal', () => { agent1NodePath, agent2NodePath, agent1NodeId, - agent1Host, - agent1ProxyPort, agent2NodeId, - agent2Host, - agent2ProxyPort, tearDownNAT, - } = await testNatUtils.setupNAT('eim', 'edm', logger); - // Since one of the nodes uses EDM NAT we cannot punch through - await testNatUtils.pkExecNs( + } = 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', 'add', agent2NodeId, agent2Host, agent2ProxyPort], + ['nodes', 'ping', agent2NodeId, '--format', 'json'], { PK_NODE_PATH: agent1NodePath, PK_PASSWORD: password, }, dataDir, - ); - await testNatUtils.pkExecNs( + )); + 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', 'add', agent1NodeId, agent1Host, agent1ProxyPort], + ['nodes', 'ping', agent1NodeId, '--format', 'json'], { PK_NODE_PATH: agent2NodePath, PK_PASSWORD: password, }, dataDir, - ); - const [ping12, ping21] = await Promise.all([ - testNatUtils.pkExecNs( - userPid, - agent1Pid, - ['nodes', 'ping', agent2NodeId, '--format', 'json'], - { - PK_NODE_PATH: agent1NodePath, - PK_PASSWORD: password, - }, - dataDir, - ), - testNatUtils.pkExecNs( - userPid, - agent2Pid, - ['nodes', 'ping', agent1NodeId, '--format', 'json'], - { - PK_NODE_PATH: agent2NodePath, - PK_PASSWORD: password, - }, - dataDir, - ), - ]); - expect(ping12.exitCode).toBe(1); - expect(JSON.parse(ping12.stdout)).toEqual({ + )); + 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', }); - expect(ping21.exitCode).toBe(1); - expect(JSON.parse(ping21.stdout)).toEqual({ + ({ 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', }); diff --git a/tests/nat/utils.ts b/tests/nat/utils.ts index d59446b3e..fbac1eb04 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'; +type NATType = 'eim' | 'edm' | 'dmz' | 'edmSimple'; // Constants for all util functions // Veth pairs (ends) @@ -35,6 +35,8 @@ const router2SeedHostIp = '192.168.0.2'; // Subnet mask const subnetMask = '/24'; // Ports +const agent1Port = '55551'; +const agent2Port = '55552'; const mappedPort = '55555'; /** @@ -266,28 +268,6 @@ function setupSeedNamespaceInterfaces( nsenter(usrnsPid, seedNetnsPid) + `ip route add ${router2SeedHostIp} dev ${seedRouter2Host}`, ); - const router1SeedRouting = nsenter(usrnsPid, router1NetnsPid).concat( - 'iptables --table nat ', - '--append POSTROUTING ', - '--protocol udp ', - `--source ${agent1HostIp}${subnetMask} `, - `--out-interface ${router1SeedHost} `, - '--jump SNAT ', - `--to-source ${router1SeedHostIp}:${mappedPort} `, - '--persistent', - ); - const router2SeedRouting = nsenter(usrnsPid, router2NetnsPid).concat( - 'iptables --table nat ', - '--append POSTROUTING ', - '--protocol udp ', - `--source ${agent2HostIp}${subnetMask} `, - `--out-interface ${router2SeedHost} `, - '--jump SNAT ', - `--to-source ${router2SeedHostIp}:${mappedPort} `, - '--persistent', - ); - child_process.exec(router1SeedRouting); - child_process.exec(router2SeedRouting); } /** @@ -486,23 +466,40 @@ function setupDMZ( 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 ', - `--to-ports ${mappedPort}`, + '--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(natCommand); + child_process.exec(acceptLocalCommand); + child_process.exec(acceptEstablishedCommand); child_process.exec(dropCommand); + child_process.exec(natCommand); } /** @@ -524,6 +521,70 @@ function setupNATEndpointDependentMapping( 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, @@ -535,14 +596,149 @@ async function setupNATWithSeedNode( path.join(os.tmpdir(), 'polykey-test-'), ); const password = 'password'; - // Create a user namespace containing four network namespaces - // Two agents and two routers + // 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, @@ -605,6 +801,8 @@ async function setupNATWithSeedNode( '127.0.0.1', '--proxy-host', `${agent1HostIp}`, + '--proxy-port', + `${agent1Port}`, '--workers', '0', '--connection-timeout', @@ -641,6 +839,8 @@ async function setupNATWithSeedNode( '127.0.0.1', '--proxy-host', `${agent2HostIp}`, + '--proxy-port', + `${agent2Port}`, '--workers', '0', '--connection-timeout', @@ -665,10 +865,14 @@ async function setupNATWithSeedNode( 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, mappedPort], + ['nodes', 'add', nodeId1, agent1RouterHostExtIp, agent1ProxyPort], { PK_NODE_PATH: path.join(dataDir, 'seed'), PK_PASSWORD: password, @@ -678,13 +882,68 @@ async function setupNATWithSeedNode( await pkExecNs( usrns.pid, seedNode.pid, - ['nodes', 'add', nodeId2, agent2RouterHostExtIp, mappedPort], + ['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': { @@ -692,7 +951,7 @@ async function setupNATWithSeedNode( usrns.pid, router1Netns.pid, agent1HostIp, - JSON.parse(stdoutNode1).proxyPort, + agent1Port, agent1RouterHostExt, agent1RouterHostExtIp, ); @@ -702,7 +961,9 @@ async function setupNATWithSeedNode( setupNATEndpointIndependentMapping( usrns.pid, router1Netns.pid, + agent1HostIp, agent1RouterHostExt, + agent1RouterHostInt, ); break; } @@ -714,6 +975,16 @@ async function setupNATWithSeedNode( ); break; } + case 'edmSimple': { + setupNATSimplifiedEDMAgent( + usrns.pid, + router1Netns.pid, + agent1HostIp, + agent1RouterHostExt, + agent1RouterHostInt, + ); + break; + } } switch (agent2NAT) { case 'dmz': { @@ -721,7 +992,7 @@ async function setupNATWithSeedNode( usrns.pid, router2Netns.pid, agent2HostIp, - JSON.parse(stdoutNode2).proxyPort, + agent2Port, agent2RouterHostExt, agent2RouterHostExtIp, ); @@ -731,7 +1002,9 @@ async function setupNATWithSeedNode( setupNATEndpointIndependentMapping( usrns.pid, router2Netns.pid, + agent2HostIp, agent2RouterHostExt, + agent2RouterHostInt, ); break; } @@ -743,62 +1016,17 @@ async function setupNATWithSeedNode( ); break; } + case 'edmSimple': { + setupNATSimplifiedEDMAgent( + usrns.pid, + router2Netns.pid, + agent2HostIp, + agent2RouterHostExt, + agent2RouterHostInt, + ); + break; + } } - 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); setupNetworkNamespaceInterfaces( usrns.pid, agent1Netns.pid, @@ -820,11 +1048,13 @@ async function setupNAT( '127.0.0.1', '--proxy-host', `${agent1HostIp}`, + '--proxy-port', + `${agent1Port}`, '--connection-timeout', '1000', '--workers', '0', - '-vv', + '--verbose', '--format', 'json', ], @@ -840,7 +1070,6 @@ async function setupNAT( rlOutNode1.once('close', reject); }); const nodeId1 = JSON.parse(stdoutNode1).nodeId; - const proxyPort1 = JSON.parse(stdoutNode1).proxyPort; const agent2 = await pkSpawnNs( usrns.pid, agent2Netns.pid, @@ -855,11 +1084,13 @@ async function setupNAT( '127.0.0.1', '--proxy-host', `${agent2HostIp}`, + '--proxy-port', + `${agent2Port}`, '--connection-timeout', '1000', '--workers', '0', - '-vv', + '--verbose', '--format', 'json', ], @@ -875,66 +1106,6 @@ async function setupNAT( rlOutNode2.once('close', reject); }); const nodeId2 = JSON.parse(stdoutNode2).nodeId; - const proxyPort2 = JSON.parse(stdoutNode2).proxyPort; - // Apply appropriate NAT rules - switch (agent1NAT) { - case 'dmz': { - setupDMZ( - usrns.pid, - router1Netns.pid, - agent1HostIp, - proxyPort1, - agent1RouterHostExt, - agent1RouterHostExtIp, - ); - break; - } - case 'eim': { - setupNATEndpointIndependentMapping( - usrns.pid, - router1Netns.pid, - agent1RouterHostExt, - ); - break; - } - case 'edm': { - setupNATEndpointDependentMapping( - usrns.pid, - router1Netns.pid, - agent1RouterHostExt, - ); - break; - } - } - switch (agent2NAT) { - case 'dmz': { - setupDMZ( - usrns.pid, - router2Netns.pid, - agent2HostIp, - proxyPort2, - agent2RouterHostExt, - agent2RouterHostExtIp, - ); - break; - } - case 'eim': { - setupNATEndpointIndependentMapping( - usrns.pid, - router2Netns.pid, - agent2RouterHostExt, - ); - break; - } - case 'edm': { - setupNATEndpointDependentMapping( - usrns.pid, - router2Netns.pid, - agent2RouterHostExt, - ); - break; - } - } return { userPid: usrns.pid, agent1Pid: agent1Netns.pid, @@ -945,10 +1116,20 @@ async function setupNAT( agent2NodePath: path.join(dataDir, 'agent2'), agent1NodeId: nodeId1, agent1Host: agent1RouterHostExtIp, - agent1ProxyPort: mappedPort, + agent1ProxyPort: + agent1NAT === 'dmz' + ? mappedPort + : agent1NAT === 'edmSimple' + ? '44444' + : agent1Port, agent2NodeId: nodeId2, agent2Host: agent2RouterHostExtIp, - agent2ProxyPort: mappedPort, + agent2ProxyPort: + agent2NAT === 'dmz' + ? mappedPort + : agent2NAT === 'edmSimple' + ? '44444' + : agent2Port, tearDownNAT: async () => { agent2.kill('SIGTERM'); await testBinUtils.processExit(agent2);