-
Notifications
You must be signed in to change notification settings - Fork 5
Description
Specification
To automatically test for NAT-busting is a bit complex, you need to simulate the existence of multiple machines, and then simulate full-cone nat, restricted cone nat and then symmetric nat.
Since we don't have a relay proxy enabled yet, symmetric NATs is going to be left out for now. So we'll focus on all the NAT architectures except for symmetric NAT.
Additional context
Actually I don't think we should bother with QEMU or NixOS here. It's too complicated. QEMU might be good choice for being able to run the test cross-platform, but lacking expertise on QEMU here (I've already worked on it with respect to netboot work), and more experience with network namespaces should mean we can do this tests on just Linux. NixOS limits our environment even more and requires running in a NixOS environment.
Note that network namespaces with Linux stateful firewalls should be perfectly capable of simulating a port-restricted firewall.
Old context follows...
The best way to do this is with VM system using QEMU.
NixOS has a multi-machine testing system that can be used to do this, however such tests can only run on NixOS: https://nixos.org/manual/nixos/unstable/index.html#sec-nixos-tests We have pre-existing code for this:
NixOS NAT Module Test
# here is the testing base file: https://github.com/NixOS/nixpkgs/blob/master/nixos/lib/testing-python.nix
with import ../../pkgs.nix {};
let
pk = (callPackage ../../nix/default.nix {}).package;
in
import <nixpkgs/nixos/tests/make-test-python.nix> {
nodes =
{
privateNode1 =
{ nodes, pkgs, ... }:
{
virtualisation.vlans = [ 1 ];
environment.variables = {
PK_PATH = "$HOME/polykey";
};
environment.systemPackages = [ pk pkgs.tcpdump ];
networking.firewall.enable = false;
networking.defaultGateway = (pkgs.lib.head nodes.router1.config.networking.interfaces.eth1.ipv4.addresses).address;
};
privateNode2 =
{ nodes, pkgs, ... }:
{
virtualisation.vlans = [ 2 ];
environment.variables = {
PK_PATH = "$HOME/polykey";
};
environment.systemPackages = [ pk pkgs.tcpdump ];
networking.firewall.enable = false;
networking.defaultGateway = (pkgs.lib.head nodes.router2.config.networking.interfaces.eth1.ipv4.addresses).address;
};
router1 =
{ pkgs, ... }:
{
virtualisation.vlans = [ 1 3 ];
environment.systemPackages = [ pkgs.tcpdump ];
networking.firewall.enable = false;
networking.nat.externalInterface = "eth2";
networking.nat.internalIPs = [ "192.168.1.0/24" ];
networking.nat.enable = true;
};
router2 =
{ ... }:
{
virtualisation.vlans = [ 2 3 ];
environment.systemPackages = [ pkgs.tcpdump ];
networking.firewall.enable = false;
networking.nat.externalInterface = "eth2";
networking.nat.internalIPs = [ "192.168.2.0/24" ];
networking.nat.enable = true;
};
publicNode =
{ config, pkgs, ... }:
{
virtualisation.vlans = [ 3 ];
environment.variables = {
PK_PATH = "$HOME/polykey";
};
environment.systemPackages = [ pk pkgs.tcpdump ];
networking.firewall.enable = false;
};
};
testScript =''
start_all()
# can start polykey-agent in both public and private nodes
publicNode.succeed("pk agent start")
privateNode1.succeed("pk agent start")
privateNode2.succeed("pk agent start")
# can create a new keynode in both public and private nodes
create_node_command = "pk agent create -n {name} -e {name}@email.com -p passphrase"
publicNode.succeed(create_node_command.format(name="publicNode"))
privateNode1.succeed(create_node_command.format(name="privateNode1"))
privateNode2.succeed(create_node_command.format(name="privateNode2"))
# can add privateNode node info to publicNode
publicNodeNodeInfo = publicNode.succeed("pk nodes get -c -b")
privateNode1.succeed("pk nodes add -b '{}'".format(publicNodeNodeInfo))
privateNode2.succeed("pk nodes add -b '{}'".format(publicNodeNodeInfo))
# can add publicNode node info to privateNodes
privateNode1NodeInfo = privateNode1.succeed("pk nodes get -c -b")
privateNode2NodeInfo = privateNode2.succeed("pk nodes get -c -b")
publicNode.succeed("pk nodes add -b '{}'".format(privateNode1NodeInfo))
publicNode.succeed("pk nodes add -b '{}'".format(privateNode2NodeInfo))
# copy public keys over to node machines
publicNodePublicKey = publicNode.succeed("cat $HOME/.polykey/.keys/public_key")
privateNode1PublicKey = privateNode1.succeed("cat $HOME/.polykey/.keys/public_key")
privateNode2PublicKey = privateNode2.succeed("cat $HOME/.polykey/.keys/public_key")
privateNode1.succeed("echo '{}' > $HOME/publicNode.pub".format(publicNodePublicKey))
privateNode1.succeed("echo '{}' > $HOME/privateNode2.pub".format(privateNode2PublicKey))
privateNode2.succeed("echo '{}' > $HOME/publicNode.pub".format(publicNodePublicKey))
privateNode2.succeed("echo '{}' > $HOME/privateNode1.pub".format(privateNode1PublicKey))
publicNode.succeed("echo '{}' > $HOME/privateNode1.pub".format(privateNode1PublicKey))
publicNode.succeed("echo '{}' > $HOME/privateNode2.pub".format(privateNode2PublicKey))
# modify node info to match node machines' host address
publicNode.succeed("pk nodes update -p $HOME/privateNode1.pub -ch privateNode1")
publicNode.succeed("pk nodes update -p $HOME/privateNode2.pub -ch privateNode2")
privateNode1.succeed(
"pk nodes update -p $HOME/publicNode.pub -ch publicNode -r $HOME/publicNode.pub"
)
privateNode2.succeed(
"pk nodes update -p $HOME/publicNode.pub -ch publicNode -r $HOME/publicNode.pub"
)
# privateNodes can ping publicNode
privateNode1.succeed("pk nodes ping -p $HOME/publicNode.pub")
privateNode2.succeed("pk nodes ping -p $HOME/publicNode.pub")
# can create a new vault in publicNode and clone it from both privateNodes
publicNode.succeed("pk vaults new publicVault")
publicNode.succeed("echo 'secret content' > $HOME/secret")
publicNode.succeed("pk secrets new publicVault:Secret -f $HOME/secret")
privateNode1.succeed("pk vaults clone -n publicVault -p $HOME/publicNode.pub")
privateNode2.succeed("pk vaults clone -n publicVault -p $HOME/publicNode.pub")
# can create a new vault in privateNode1
privateNode1.succeed("pk vaults new privateVault1")
# can create a new secret in privateNode1
privateNode1.succeed("echo 'secret content' > $HOME/secret")
privateNode1.succeed("pk secrets new privateVault1:Secret -f $HOME/secret")
# setup a relay between privateNode1 and publicNode
privateNode1.succeed("pk nodes relay -p $HOME/publicNode.pub")
# add privateNode1 node info to privateNode2
privateNode1NodeInfo = privateNode1.succeed("pk nodes get -c -b")
privateNode2.succeed("pk nodes add -b '{}'".format(privateNode1NodeInfo))
# add privateNode2 node info to privateNode1
privateNode2NodeInfo = privateNode2.succeed("pk nodes get -c -b")
privateNode1.succeed("pk nodes add -b '{}'".format(privateNode2NodeInfo))
# can ping privateNode1 to privateNode2
privateNode2.succeed("pk nodes ping -p ~/privateNode1.pub")
# can pull a vault from privateNode1 to privateNode2
privateNode2.succeed("pk vaults clone -p ~/privateNode1.pub -n privateVault1")
'';
}
Tasks
- - Create test harness/fixture utilities that create a multi-node situation
- - Simulate a NAT table situation by making use of network namespaces
- - This test can only run on Linux that supports virtual network namespaces.
4. [ ] - The test will have to be run separately fromUsing conditional testing instead Conditional testing for platform-specific tests #380npm test
which runsjest
. This test can be done inside Gitlab CI/CD if the CI/CD on Linux supports creating network namespaces. If not, it's a manual test. - - Review my gist https://gist.github.com/CMCDragonkai/3f3649d7f1be9c7df36f which explains how to use network namespaces. The Linux iptables firewall has to be used that simulates a NAT that allows outgoing packets but denies incoming packets except for the connections that are already live. This is called a "stateful firewall". I've done this before, but I forgot the details.
- - You'll need to use https://stackabuse.com/executing-shell-commands-with-node-js/ to run the
ip netns
commands. Remember to check whether the OS is linux before allowing one to run these tests. [ ] - Add in testing involving- Reissued Integration Tests fortestnet.polykey.io
which should run only during integration testing after theintegration:deployment
job (because it has to deploy to the testnet in that job).testnet.polykey.com
Polykey-CLI#71