mirror of
https://github.com/rosenpass/rosenpass.git
synced 2025-12-12 07:40:30 -08:00
chore(rosenpass): Add integration tests for basic connectivity, backwards compatability and multi-peer connectivity
This commit is contained in:
29
tests/integration/README.md
Normal file
29
tests/integration/README.md
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# Integration Tests
|
||||||
|
|
||||||
|
This directory contains integration tests for rosenpass in the form of a nix flake. Put simply, in order to run the integration tests for the main branch as they are on github right now, just run the following on a linux machine with nix installed and flakes enabled:
|
||||||
|
|
||||||
|
```
|
||||||
|
nix flake check
|
||||||
|
```
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The integration tests recognize two rosenpass versions, a new version and an old version. If not adapted, both are set to the version of the current main branch of rosenpass on github. We describe below how to change this.
|
||||||
|
All integration tests install rosenpass on virtual machines, run the key exchange, create a connection via wireguard that uses rosenpass and then checks whether all peers can ping each other via wireguard. Overall there are four integration tests:
|
||||||
|
|
||||||
|
- `basicConnectivity` -- This test only uses the new rosenpass version and checks whether the key exchange between two peers works such that they can ping each other.
|
||||||
|
- `backwardClient` -- This test is the same as the `basicConnectivity` test, but with the client using the old rosenpass version.
|
||||||
|
- `backwardServer` -- This test is the same as the `backwardClient` test, but with the server using the old rosenpass version.
|
||||||
|
- `multiPeer` -- This test again only uses the new rosenpass version, but with three peers. The first peer acts as a server towards the other two peers. The second peer acts as a client towards the first peer and as a server towards the third peer. The third peer acts as a client towards all peers.
|
||||||
|
|
||||||
|
## Testing specific versions
|
||||||
|
|
||||||
|
You can specify specific versions of rosenpass to test compatability. The proper way to do so is by overriding the respective inputs to the nix flake. As an example, say you want to test the compatability of your local version of rosenpass with the branch `new-feature` on github. You can achieve this by running the following command:
|
||||||
|
|
||||||
|
```
|
||||||
|
nix flake check --override-input rosenpass-old ../../ --override-input rosenpass-new github:rosenpass/rosenpass/new-feature
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage in the CI
|
||||||
|
|
||||||
|
In the CI, the integration tests are used differently, depending on whether the CI run is triggered by a push to the main branch or by a pull request. If the CI run is triggered by a pull request, then the result of merging the main branch and the PR branch is set as the new version and the current state of the main branch is set as the old version. For push events, the CI is only triggered if the push is onto the main branch. In that case, the state before the push event is considered the old version and the state after the push event is considered as the new version.
|
||||||
320
tests/integration/flake.lock
generated
Normal file
320
tests/integration/flake.lock
generated
Normal file
@@ -0,0 +1,320 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-parts": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs-lib": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1751413152,
|
||||||
|
"narHash": "sha256-Tyw1RjYEsp5scoigs1384gIg6e0GoBVjms4aXFfRssQ=",
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"rev": "77826244401ea9de6e3bac47c2db46005e1f30b5",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1726560853,
|
||||||
|
"narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-utils_2": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems_2"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1726560853,
|
||||||
|
"narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nix-vm-test": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"rosenpass-new",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1734355073,
|
||||||
|
"narHash": "sha256-FfdPOGy1zElTwKzjgIMp5K2D3gfPn6VWjVa4MJ9L1Tc=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "nix-vm-test",
|
||||||
|
"rev": "5948de39a616f2261dbbf4b6f25cbe1cbefd788c",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "nix-vm-test",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nix-vm-test_2": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"rosenpass-old",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1734355073,
|
||||||
|
"narHash": "sha256-FfdPOGy1zElTwKzjgIMp5K2D3gfPn6VWjVa4MJ9L1Tc=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "nix-vm-test",
|
||||||
|
"rev": "5948de39a616f2261dbbf4b6f25cbe1cbefd788c",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "nix-vm-test",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1751792365,
|
||||||
|
"narHash": "sha256-J1kI6oAj25IG4EdVlg2hQz8NZTBNYvIS0l4wpr9KcUo=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "1fd8bada0b6117e6c7eb54aad5813023eed37ccb",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1728193676,
|
||||||
|
"narHash": "sha256-PbDWAIjKJdlVg+qQRhzdSor04bAPApDqIv2DofTyynk=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "ecbc1ca8ffd6aea8372ad16be9ebbb39889e55b6",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-24.05",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs_3": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1728193676,
|
||||||
|
"narHash": "sha256-PbDWAIjKJdlVg+qQRhzdSor04bAPApDqIv2DofTyynk=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "ecbc1ca8ffd6aea8372ad16be9ebbb39889e55b6",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-24.05",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-parts": "flake-parts",
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"rosenpass-new": "rosenpass-new",
|
||||||
|
"rosenpass-old": "rosenpass-old"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rosenpass-new": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nix-vm-test": "nix-vm-test",
|
||||||
|
"nixpkgs": "nixpkgs_2",
|
||||||
|
"rust-overlay": "rust-overlay",
|
||||||
|
"treefmt-nix": "treefmt-nix"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1752081615,
|
||||||
|
"narHash": "sha256-g9jC1HNCMSMPzArA8RCPGaxCCFH6dzQuq20RDsRwRT8=",
|
||||||
|
"owner": "rosenpass",
|
||||||
|
"repo": "rosenpass",
|
||||||
|
"rev": "3e03e479350551d11b81bde1bb55f5fdf8246f7c",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "rosenpass",
|
||||||
|
"ref": "main",
|
||||||
|
"repo": "rosenpass",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rosenpass-old": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils_2",
|
||||||
|
"nix-vm-test": "nix-vm-test_2",
|
||||||
|
"nixpkgs": "nixpkgs_3",
|
||||||
|
"rust-overlay": "rust-overlay_2",
|
||||||
|
"treefmt-nix": "treefmt-nix_2"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1752081615,
|
||||||
|
"narHash": "sha256-g9jC1HNCMSMPzArA8RCPGaxCCFH6dzQuq20RDsRwRT8=",
|
||||||
|
"owner": "rosenpass",
|
||||||
|
"repo": "rosenpass",
|
||||||
|
"rev": "3e03e479350551d11b81bde1bb55f5fdf8246f7c",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "rosenpass",
|
||||||
|
"ref": "main",
|
||||||
|
"repo": "rosenpass",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rust-overlay": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"rosenpass-new",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1744513456,
|
||||||
|
"narHash": "sha256-NLVluTmK8d01Iz+WyarQhwFcXpHEwU7m5hH3YQQFJS0=",
|
||||||
|
"owner": "oxalica",
|
||||||
|
"repo": "rust-overlay",
|
||||||
|
"rev": "730fd8e82799219754418483fabe1844262fd1e2",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "oxalica",
|
||||||
|
"repo": "rust-overlay",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rust-overlay_2": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"rosenpass-old",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1744513456,
|
||||||
|
"narHash": "sha256-NLVluTmK8d01Iz+WyarQhwFcXpHEwU7m5hH3YQQFJS0=",
|
||||||
|
"owner": "oxalica",
|
||||||
|
"repo": "rust-overlay",
|
||||||
|
"rev": "730fd8e82799219754418483fabe1844262fd1e2",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "oxalica",
|
||||||
|
"repo": "rust-overlay",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"treefmt-nix": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"rosenpass-new",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1743748085,
|
||||||
|
"narHash": "sha256-uhjnlaVTWo5iD3LXics1rp9gaKgDRQj6660+gbUU3cE=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "treefmt-nix",
|
||||||
|
"rev": "815e4121d6a5d504c0f96e5be2dd7f871e4fd99d",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "treefmt-nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"treefmt-nix_2": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"rosenpass-old",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1743748085,
|
||||||
|
"narHash": "sha256-uhjnlaVTWo5iD3LXics1rp9gaKgDRQj6660+gbUU3cE=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "treefmt-nix",
|
||||||
|
"rev": "815e4121d6a5d504c0f96e5be2dd7f871e4fd99d",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "treefmt-nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
137
tests/integration/flake.nix
Normal file
137
tests/integration/flake.nix
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
{
|
||||||
|
description = "Integration tests for rosenpass";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||||
|
flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs";
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
|
||||||
|
# Override or change these inputs for testing new Integrations. They are overriden automatically when run in the CI
|
||||||
|
rosenpass-old.url = "github:rosenpass/rosenpass/main";
|
||||||
|
rosenpass-new.url = "github:rosenpass/rosenpass/main";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs =
|
||||||
|
inputs:
|
||||||
|
inputs.flake-parts.lib.mkFlake { inherit inputs; } {
|
||||||
|
systems = [
|
||||||
|
"i686-linux"
|
||||||
|
"x86_64-linux"
|
||||||
|
"aarch64-linux"
|
||||||
|
"aarch64-darwin"
|
||||||
|
];
|
||||||
|
|
||||||
|
perSystem =
|
||||||
|
{ system, lib, ... }:
|
||||||
|
let
|
||||||
|
|
||||||
|
# Since other parts of the CI are already doing the unit tests, we deactivate them here.
|
||||||
|
rosenpass-old = inputs.rosenpass-old.packages.${system}.default.overrideAttrs (old: {
|
||||||
|
doCheck = false;
|
||||||
|
});
|
||||||
|
rosenpass-new = inputs.rosenpass-new.packages.${system}.default.overrideAttrs (old: {
|
||||||
|
doCheck = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
basicConnectivityOverlay = final: prev: {
|
||||||
|
rosenpass-peer-a = rosenpass-new;
|
||||||
|
rosenpass-peer-b = rosenpass-new;
|
||||||
|
};
|
||||||
|
|
||||||
|
backwardServerOverlay = final: prev: {
|
||||||
|
rosenpass-peer-a = rosenpass-old;
|
||||||
|
rosenpass-peer-b = rosenpass-new;
|
||||||
|
};
|
||||||
|
|
||||||
|
backwardClientOverlay = final: prev: {
|
||||||
|
rosenpass-peer-a = rosenpass-new;
|
||||||
|
rosenpass-peer-b = rosenpass-old;
|
||||||
|
};
|
||||||
|
|
||||||
|
multiPeerOverlay = final: prev: {
|
||||||
|
rosenpass-peer-a = rosenpass-new;
|
||||||
|
rosenpass-peer-b = rosenpass-new;
|
||||||
|
rosenpass-peer-c = rosenpass-new;
|
||||||
|
};
|
||||||
|
|
||||||
|
# The current version of ipython fails to build on i686 linux.
|
||||||
|
# We therefore pin an older version that works for the time beeing.
|
||||||
|
ipythonOverlay = final: prev: {
|
||||||
|
python313 = prev.python313.override {
|
||||||
|
packageOverrides = python-final: python-prev: {
|
||||||
|
ipython = python-prev.ipython.overridePythonAttrs (old: {
|
||||||
|
version = "8.37.0";
|
||||||
|
src = python-final.fetchPypi {
|
||||||
|
pname = "ipython";
|
||||||
|
version = "8.37.0";
|
||||||
|
hash = "sha256-yoFYQeGkGh5rc6CwjzA4r5siUlZNAfxAU1bTQDMBIhY=";
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
pkgsBasicConnectivity = import inputs.nixpkgs {
|
||||||
|
inherit system;
|
||||||
|
overlays = [
|
||||||
|
basicConnectivityOverlay
|
||||||
|
ipythonOverlay
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
pkgsBackwardServer = import inputs.nixpkgs {
|
||||||
|
inherit system;
|
||||||
|
overlays = [
|
||||||
|
backwardServerOverlay
|
||||||
|
ipythonOverlay
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
pkgsBackwardClient = import inputs.nixpkgs {
|
||||||
|
inherit system;
|
||||||
|
overlays = [
|
||||||
|
backwardClientOverlay
|
||||||
|
ipythonOverlay
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
pkgsMultiPeer = import inputs.nixpkgs {
|
||||||
|
inherit system;
|
||||||
|
overlays = [
|
||||||
|
multiPeerOverlay
|
||||||
|
ipythonOverlay
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
in
|
||||||
|
{
|
||||||
|
checks.basicConnectivity = pkgsBasicConnectivity.testers.runNixOSTest (
|
||||||
|
import ./rpsc-test.nix {
|
||||||
|
pkgs = pkgsBasicConnectivity;
|
||||||
|
inherit lib;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
checks.backwardServer = pkgsBackwardServer.testers.runNixOSTest (
|
||||||
|
import ./rpsc-test.nix {
|
||||||
|
pkgs = pkgsBackwardServer;
|
||||||
|
inherit lib;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
checks.backwardClient = pkgsBackwardClient.testers.runNixOSTest (
|
||||||
|
import ./rpsc-test.nix {
|
||||||
|
pkgs = pkgsBackwardClient;
|
||||||
|
inherit lib;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
checks.multiPeer = pkgsMultiPeer.testers.runNixOSTest (
|
||||||
|
import ./rpsc-test.nix {
|
||||||
|
pkgs = pkgsMultiPeer;
|
||||||
|
inherit lib;
|
||||||
|
multiPeer = true;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
BIN
tests/integration/rosenpass-keys/peer-a.pk
Normal file
BIN
tests/integration/rosenpass-keys/peer-a.pk
Normal file
Binary file not shown.
BIN
tests/integration/rosenpass-keys/peer-a.sk
Normal file
BIN
tests/integration/rosenpass-keys/peer-a.sk
Normal file
Binary file not shown.
BIN
tests/integration/rosenpass-keys/peer-b.pk
Normal file
BIN
tests/integration/rosenpass-keys/peer-b.pk
Normal file
Binary file not shown.
BIN
tests/integration/rosenpass-keys/peer-b.sk
Normal file
BIN
tests/integration/rosenpass-keys/peer-b.sk
Normal file
Binary file not shown.
BIN
tests/integration/rosenpass-keys/peer-c.pk
Normal file
BIN
tests/integration/rosenpass-keys/peer-c.pk
Normal file
Binary file not shown.
BIN
tests/integration/rosenpass-keys/peer-c.sk
Normal file
BIN
tests/integration/rosenpass-keys/peer-c.sk
Normal file
Binary file not shown.
38
tests/integration/rp-key-exchange.nix
Normal file
38
tests/integration/rp-key-exchange.nix
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
config,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.services.rosenpassKeyExchange;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.services.rosenpassKeyExchange = {
|
||||||
|
enable = lib.mkEnableOption "rosenpass key-exchange";
|
||||||
|
config = lib.mkOption {
|
||||||
|
type = lib.types.path;
|
||||||
|
description = "Path to rosenpass configuration";
|
||||||
|
};
|
||||||
|
rosenpassVersion = lib.mkOption {
|
||||||
|
type = lib.types.package;
|
||||||
|
description = "Rosenpass package to use";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
systemd.services.rp-exchange = {
|
||||||
|
description = "Rosenpass Key Exchanger";
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
requires = [ "network-online.target" ];
|
||||||
|
script = ''
|
||||||
|
${cfg.rosenpassVersion}/bin/rosenpass exchange-config ${cfg.config}
|
||||||
|
'';
|
||||||
|
serviceConfig = {
|
||||||
|
Restart = "always";
|
||||||
|
RestartSec = 1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
85
tests/integration/rp-key-sync.nix
Normal file
85
tests/integration/rp-key-sync.nix
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
config,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.services.rosenpassKeySync;
|
||||||
|
servicePrefix = "rp-key-sync-";
|
||||||
|
timerPrefix = "rp-key-sync-timer-";
|
||||||
|
rpKeySyncOpts =
|
||||||
|
{ name, ... }:
|
||||||
|
{
|
||||||
|
# Each instance of ths service is defined by the following information:
|
||||||
|
options = {
|
||||||
|
enable = lib.mkEnableOption "RP Keysync for ${name}";
|
||||||
|
|
||||||
|
wgInterface = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = "Wireguard interface name";
|
||||||
|
};
|
||||||
|
|
||||||
|
rpHost = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = "network address of the host that runs rosenpass";
|
||||||
|
};
|
||||||
|
|
||||||
|
peerPubkey = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = "Public key of wireguard peer";
|
||||||
|
};
|
||||||
|
|
||||||
|
remoteKeyPath = lib.mkOption {
|
||||||
|
type = lib.types.path;
|
||||||
|
description = "Location of the .osk file on the key exchange server";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.services.rosenpassKeySync = {
|
||||||
|
instances = lib.mkOption {
|
||||||
|
type = lib.types.attrsOf (lib.types.submodule rpKeySyncOpts);
|
||||||
|
default = { };
|
||||||
|
description = "RP key sync instances";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
systemd.services = lib.mapAttrs' (instanceName: instanceCfg: {
|
||||||
|
name = "${servicePrefix}${instanceName}";
|
||||||
|
value = {
|
||||||
|
description = "Rosenpass Key Downloader ${instanceName}";
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
requires = [ "network-online.target" ];
|
||||||
|
# The script downloads the key generated by rosenpass from the key exchange node and sets it as the preshared key for the specified wireguard peer.
|
||||||
|
script = ''
|
||||||
|
set -euo pipefail
|
||||||
|
${pkgs.openssh}/bin/ssh ${instanceCfg.rpHost} "cat ${instanceCfg.remoteKeyPath}" \
|
||||||
|
| ${pkgs.wireguard-tools}/bin/wg \
|
||||||
|
set ${instanceCfg.wgInterface} \
|
||||||
|
peer ${instanceCfg.peerPubkey} \
|
||||||
|
preshared-key /dev/stdin
|
||||||
|
'';
|
||||||
|
serviceConfig = {
|
||||||
|
Restart = "always";
|
||||||
|
RestartSec = 10;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}) (lib.filterAttrs (_: cfg: cfg.enable) cfg.instances); # this creates one systemd service (as above) per configured instance.
|
||||||
|
|
||||||
|
systemd.timers = lib.mapAttrs' (instanceName: instanceCfg: {
|
||||||
|
name = "${timerPrefix}${instanceName}";
|
||||||
|
value = {
|
||||||
|
wantedBy = [ "timers.target" ];
|
||||||
|
timerConfig = {
|
||||||
|
requires = [ "network-online.target" ];
|
||||||
|
OnUnitActiveSec = "1m";
|
||||||
|
Unit = "${servicePrefix}${instanceName}.service";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}) (lib.filterAttrs (_: cfg: cfg.enable) cfg.instances); # this creates one systemd time (as above) per configured instance.
|
||||||
|
};
|
||||||
|
}
|
||||||
473
tests/integration/rpsc-test.nix
Normal file
473
tests/integration/rpsc-test.nix
Normal file
@@ -0,0 +1,473 @@
|
|||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
multiPeer ? false,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
wgInterface = "mywg";
|
||||||
|
wgPort = 51820;
|
||||||
|
rpPort = 51821;
|
||||||
|
|
||||||
|
demoRosenpassKeys = ./rosenpass-keys;
|
||||||
|
rosenpassKeyFolder = "/var/secrets";
|
||||||
|
keyExchangePathAB = "/root/peer-ab.osk";
|
||||||
|
keyExchangePathBA = "/root/peer-ba.osk";
|
||||||
|
keyExchangePathAC = "/root/peer-ac.osk";
|
||||||
|
keyExchangePathCA = "/root/peer-ca.osk";
|
||||||
|
keyExchangePathBC = "/root/peer-bc.osk";
|
||||||
|
keyExchangePathCB = "/root/peer-cb.osk";
|
||||||
|
|
||||||
|
staticConfig =
|
||||||
|
{
|
||||||
|
peerA = {
|
||||||
|
innerIp = "10.100.0.1";
|
||||||
|
privateKey = "cB+EYXqf63F+8Kqn3Q1dr9ds5tQi4PkQU+WfLpZf2nU=";
|
||||||
|
publicKey = "+gsv8wlhKGKXUOYTw5r2tPpSr7CEeVBgH/kxZzeo9E8=";
|
||||||
|
rosenpassConfig = builtins.toFile "peer-a.toml" (
|
||||||
|
''
|
||||||
|
public_key = "${rosenpassKeyFolder}/self.pk"
|
||||||
|
secret_key = "${rosenpassKeyFolder}/self.sk"
|
||||||
|
listen = ["[::]:${builtins.toString rpPort}"]
|
||||||
|
verbosity = "Verbose"
|
||||||
|
|
||||||
|
[[peers]]
|
||||||
|
public_key = "${rosenpassKeyFolder}/peer-b.pk"
|
||||||
|
endpoint = "peerbkeyexchanger:${builtins.toString rpPort}"
|
||||||
|
key_out = "${keyExchangePathAB}"
|
||||||
|
''
|
||||||
|
+ (lib.optionalString multiPeer ''
|
||||||
|
[[peers]]
|
||||||
|
public_key = "${rosenpassKeyFolder}/peer-c.pk"
|
||||||
|
endpoint = "peerckeyexchanger:${builtins.toString rpPort}"
|
||||||
|
key_out = "${keyExchangePathAC}"
|
||||||
|
'')
|
||||||
|
);
|
||||||
|
};
|
||||||
|
peerB = {
|
||||||
|
innerIp = "10.100.0.2";
|
||||||
|
privateKey = "sL+9z4HAzkV01QYTQX5TA645PV8Vprk09vNNWSKjjW4=";
|
||||||
|
publicKey = "ZErZhjoSTiLCfPXl3TcnWyfvUtjP1mIQUH+2sRxI/wE=";
|
||||||
|
rosenpassConfig = builtins.toFile "peer-b.toml" (
|
||||||
|
''
|
||||||
|
public_key = "${rosenpassKeyFolder}/self.pk"
|
||||||
|
secret_key = "${rosenpassKeyFolder}/self.sk"
|
||||||
|
listen = ["[::]:${builtins.toString rpPort}"]
|
||||||
|
verbosity = "Verbose"
|
||||||
|
|
||||||
|
[[peers]]
|
||||||
|
public_key = "${rosenpassKeyFolder}/peer-a.pk"
|
||||||
|
endpoint = "peerakeyexchanger:${builtins.toString rpPort}"
|
||||||
|
key_out = "${keyExchangePathBA}"
|
||||||
|
''
|
||||||
|
+ (lib.optionalString multiPeer ''
|
||||||
|
[[peers]]
|
||||||
|
public_key = "${rosenpassKeyFolder}/peer-c.pk"
|
||||||
|
endpoint = "peerckeyexchanger:${builtins.toString rpPort}"
|
||||||
|
key_out = "${keyExchangePathBC}"
|
||||||
|
'')
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// lib.optionalAttrs multiPeer {
|
||||||
|
# peerC is only defined if we are in a multiPeer context.
|
||||||
|
peerC = {
|
||||||
|
innerIp = "10.100.0.3";
|
||||||
|
privateKey = "gOrlrKattR+hdpGc/0X2qFXWSbw0hW7AMLzb68cWBmI=";
|
||||||
|
publicKey = "23S38TaISe+GlrNJL5DyoN+EC6g2fSYbT1Kt1LUxhRA=";
|
||||||
|
rosenpassConfig = builtins.toFile "peer-c.toml" ''
|
||||||
|
public_key = "${rosenpassKeyFolder}/self.pk"
|
||||||
|
secret_key = "${rosenpassKeyFolder}/self.sk"
|
||||||
|
listen = ["[::]:${builtins.toString rpPort}"]
|
||||||
|
verbosity = "Verbose"
|
||||||
|
[[peers]]
|
||||||
|
public_key = "${rosenpassKeyFolder}/peer-a.pk"
|
||||||
|
endpoint = "peerakeyexchanger:${builtins.toString rpPort}"
|
||||||
|
key_out = "${keyExchangePathCA}"
|
||||||
|
[[peers]]
|
||||||
|
public_key = "${rosenpassKeyFolder}/peer-b.pk"
|
||||||
|
endpoint = "peerckeyexchanger:${builtins.toString rpPort}"
|
||||||
|
key_out = "${keyExchangePathCB}"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
inherit (import (pkgs.path + "/nixos/tests/ssh-keys.nix") pkgs)
|
||||||
|
snakeOilPublicKey
|
||||||
|
snakeOilPrivateKey
|
||||||
|
;
|
||||||
|
|
||||||
|
# All hosts in this scenario use the same key pair
|
||||||
|
# The script takes the host as parameter and prepares passwordless login
|
||||||
|
prepareSshLogin = pkgs.writeShellScriptBin "prepare-ssh-login" ''
|
||||||
|
set -euo pipefail
|
||||||
|
mkdir -p /root/.ssh
|
||||||
|
cp ${snakeOilPrivateKey} /root/.ssh/id_ecdsa
|
||||||
|
chmod 0400 /root/.ssh/id_ecdsa
|
||||||
|
${pkgs.openssh}/bin/ssh -o StrictHostKeyChecking=no "$1" true
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
{
|
||||||
|
name = "rosenpass with key exchangers";
|
||||||
|
defaults = {
|
||||||
|
imports = [
|
||||||
|
./rp-key-exchange.nix
|
||||||
|
./rp-key-sync.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
systemd.tmpfiles.rules = [ "d ${rosenpassKeyFolder} 0400 root root - -" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
nodes =
|
||||||
|
{
|
||||||
|
# peerA and peerB are the only neccessary peers unless we are in the multiPeer test.
|
||||||
|
peerA = {
|
||||||
|
networking.wireguard.interfaces.${wgInterface} = {
|
||||||
|
listenPort = wgPort;
|
||||||
|
ips = [ "${staticConfig.peerA.innerIp}/24" ];
|
||||||
|
inherit (staticConfig.peerA) privateKey;
|
||||||
|
peers =
|
||||||
|
[
|
||||||
|
{
|
||||||
|
inherit (staticConfig.peerB) publicKey;
|
||||||
|
allowedIPs = [ "${staticConfig.peerB.innerIp}/32" ];
|
||||||
|
presharedKey = "AR/yvSvMAzW6eS27PsRHUMWwC8cLhaD96t42cysxrb0=";
|
||||||
|
} # NOTE: We use mismatching preshared keys on purpose to make the wireguard key exchange fail until the rosenpass key exchange succeeded.
|
||||||
|
]
|
||||||
|
++ (lib.optional multiPeer {
|
||||||
|
inherit (staticConfig.peerC) publicKey;
|
||||||
|
allowedIPs = [ "${staticConfig.peerC.innerIp}/32" ];
|
||||||
|
presharedKey = "LfWvJCN8h7NhS+JWRG7GMIY20JxUV4WUs7MJ45ZGoCE=";
|
||||||
|
} # NOTE: We use mismatching preshared keys on purpose to make the wireguard key exchange fail until the rosenpass key exchange succeeded.
|
||||||
|
);
|
||||||
|
};
|
||||||
|
networking.firewall.allowedUDPPorts = [ wgPort ];
|
||||||
|
|
||||||
|
# Each instance of the key sync service loads a symmetric key from a rosenpass keyexchanger node and sets it as the preshared key for the appropriate wireguard tunnel.
|
||||||
|
services.rosenpassKeySync.instances =
|
||||||
|
{
|
||||||
|
AB = {
|
||||||
|
enable = true;
|
||||||
|
inherit wgInterface;
|
||||||
|
rpHost = "peerakeyexchanger";
|
||||||
|
peerPubkey = staticConfig.peerB.publicKey;
|
||||||
|
remoteKeyPath = keyExchangePathAB;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// lib.optionalAttrs multiPeer {
|
||||||
|
AC = {
|
||||||
|
enable = true;
|
||||||
|
inherit wgInterface;
|
||||||
|
rpHost = "peerakeyexchanger";
|
||||||
|
peerPubkey = staticConfig.peerC.publicKey;
|
||||||
|
remoteKeyPath = keyExchangePathAC;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
peerB = {
|
||||||
|
networking.wireguard.interfaces.${wgInterface} = {
|
||||||
|
listenPort = wgPort;
|
||||||
|
ips = [ "${staticConfig.peerB.innerIp}/24" ];
|
||||||
|
inherit (staticConfig.peerB) privateKey;
|
||||||
|
peers =
|
||||||
|
[
|
||||||
|
{
|
||||||
|
inherit (staticConfig.peerA) publicKey;
|
||||||
|
allowedIPs = [ "${staticConfig.peerA.innerIp}/32" ];
|
||||||
|
endpoint = "peerA:${builtins.toString wgPort}";
|
||||||
|
presharedKey = "o25fjoIOI623cnRyhvD4YEGtuSY4BFRZmY3UHvZ0BCA=";
|
||||||
|
# NOTE: We use mismatching preshared keys on purpose to make the wireguard key exchange fail until the rosenpass key exchange succeeded.
|
||||||
|
}
|
||||||
|
]
|
||||||
|
++ (lib.optional multiPeer {
|
||||||
|
inherit (staticConfig.peerC) publicKey;
|
||||||
|
allowedIPs = [ "${staticConfig.peerC.innerIp}/32" ];
|
||||||
|
presharedKey = "GsYTUd/4Ph7wMy5r+W1no9yGe0UeZlmCPeiyu4tb6yM=";
|
||||||
|
# NOTE: We use mismatching preshared keys on purpose to make the wireguard key exchange fail until the rosenpass key exchange succeeded.
|
||||||
|
});
|
||||||
|
};
|
||||||
|
networking.firewall.allowedUDPPorts = [ wgPort ];
|
||||||
|
|
||||||
|
# Each instance of the key sync service loads a symmetric key from a rosenpass keyexchanger node and sets it as the preshared key for the appropriate wireguard tunnel.
|
||||||
|
services.rosenpassKeySync.instances =
|
||||||
|
{
|
||||||
|
BA = {
|
||||||
|
enable = true;
|
||||||
|
inherit wgInterface;
|
||||||
|
rpHost = "peerbkeyexchanger";
|
||||||
|
peerPubkey = staticConfig.peerA.publicKey;
|
||||||
|
remoteKeyPath = keyExchangePathBA;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// lib.optionalAttrs multiPeer {
|
||||||
|
BC = {
|
||||||
|
enable = true;
|
||||||
|
inherit wgInterface;
|
||||||
|
rpHost = "peerbkeyexchanger";
|
||||||
|
peerPubkey = staticConfig.peerC.publicKey;
|
||||||
|
remoteKeyPath = keyExchangePathBC;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# The key exchanger node for peerA is the node that actually runs rosenpass. It takes the rosenpass confguration for peerA and runs it.
|
||||||
|
# The key sync services of peerA will ssh into this node and download the exchanged keys from here.
|
||||||
|
peerakeyexchanger = {
|
||||||
|
services.openssh.enable = true;
|
||||||
|
users.users.root.openssh.authorizedKeys.keys = [ snakeOilPublicKey ];
|
||||||
|
networking.firewall.allowedUDPPorts = [ rpPort ];
|
||||||
|
|
||||||
|
services.rosenpassKeyExchange = {
|
||||||
|
enable = true;
|
||||||
|
config = staticConfig.peerA.rosenpassConfig;
|
||||||
|
rosenpassVersion = pkgs.rosenpass-peer-a;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# The key exchanger node for peerB is the node that actually runs rosenpass. It takes the rosenpass confguration for peerB and runs it.
|
||||||
|
# The key sync services of peerB will ssh into this node and download the exchanged keys from here.
|
||||||
|
peerbkeyexchanger = {
|
||||||
|
services.openssh.enable = true;
|
||||||
|
users.users.root.openssh.authorizedKeys.keys = [ snakeOilPublicKey ];
|
||||||
|
|
||||||
|
services.rosenpassKeyExchange = {
|
||||||
|
enable = true;
|
||||||
|
config = staticConfig.peerB.rosenpassConfig;
|
||||||
|
rosenpassVersion = pkgs.rosenpass-peer-b;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// lib.optionalAttrs multiPeer {
|
||||||
|
peerC = {
|
||||||
|
networking.wireguard.interfaces.${wgInterface} = {
|
||||||
|
listenPort = wgPort;
|
||||||
|
ips = [ "${staticConfig.peerC.innerIp}/24" ];
|
||||||
|
inherit (staticConfig.peerC) privateKey;
|
||||||
|
peers = [
|
||||||
|
{
|
||||||
|
inherit (staticConfig.peerA) publicKey;
|
||||||
|
allowedIPs = [ "${staticConfig.peerA.innerIp}/32" ];
|
||||||
|
endpoint = "peerA:${builtins.toString wgPort}";
|
||||||
|
presharedKey = "s9aIG1pY6nj2lH6p61tP8WRETNgQvoTfgel5BmVjYeI=";
|
||||||
|
} # NOTE: We use mismatching preshared keys on purpose to make the wireguard key exchange fail until the rosenpass key exchange succeeded.
|
||||||
|
{
|
||||||
|
inherit (staticConfig.peerB) publicKey;
|
||||||
|
allowedIPs = [ "${staticConfig.peerB.innerIp}/32" ];
|
||||||
|
endpoint = "peerB:${builtins.toString wgPort}";
|
||||||
|
presharedKey = "DYlFqWg/M6EfnMolBO+b4DFNrRyS6YWr4lM/2xRE1FQ=";
|
||||||
|
} # NOTE: We use mismatching preshared keys on purpose to make the wireguard key exchange fail until the rosenpass key exchange succeeded.
|
||||||
|
];
|
||||||
|
};
|
||||||
|
networking.firewall.allowedUDPPorts = [ wgPort ];
|
||||||
|
|
||||||
|
# Each instance of the key sync service loads a symmetric key from a rosenpass keyexchanger node and sets it as the preshared key for the appropriate wireguard tunnel.
|
||||||
|
services.rosenpassKeySync.instances = {
|
||||||
|
CA = {
|
||||||
|
enable = true;
|
||||||
|
inherit wgInterface;
|
||||||
|
rpHost = "peerckeyexchanger";
|
||||||
|
peerPubkey = staticConfig.peerA.publicKey;
|
||||||
|
remoteKeyPath = keyExchangePathCA;
|
||||||
|
};
|
||||||
|
CB = {
|
||||||
|
enable = true;
|
||||||
|
inherit wgInterface;
|
||||||
|
rpHost = "peerckeyexchanger";
|
||||||
|
peerPubkey = staticConfig.peerB.publicKey;
|
||||||
|
remoteKeyPath = keyExchangePathCB;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# The key exchanger node for peerC is the node that actually runs rosenpass. It takes the rosenpass confguration for peerC and runs it.
|
||||||
|
# The key sync services of peerC will ssh into this node and download the exchanged keys from here.
|
||||||
|
peerckeyexchanger = {
|
||||||
|
services.openssh.enable = true;
|
||||||
|
users.users.root.openssh.authorizedKeys.keys = [ snakeOilPublicKey ];
|
||||||
|
networking.firewall.allowedUDPPorts = [ rpPort ];
|
||||||
|
|
||||||
|
services.rosenpassKeyExchange = {
|
||||||
|
enable = true;
|
||||||
|
config = staticConfig.peerC.rosenpassConfig;
|
||||||
|
rosenpassVersion = pkgs.rosenpass-peer-c;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
interactive = {
|
||||||
|
defaults = {
|
||||||
|
users.extraUsers.root.initialPassword = "";
|
||||||
|
services.openssh = {
|
||||||
|
enable = true;
|
||||||
|
settings = {
|
||||||
|
PermitRootLogin = "yes";
|
||||||
|
PermitEmptyPasswords = "yes";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
security.pam.services.sshd.allowNullPassword = true;
|
||||||
|
environment.systemPackages = [
|
||||||
|
prepareSshLogin
|
||||||
|
|
||||||
|
(pkgs.writeSellScriptBin "install-rosenpass-keys" (
|
||||||
|
''
|
||||||
|
${pkgs.openssh}/bin/scp ${demoRosenpassKeys}/peer-a.sk peerakeyexchanger:${rosenpassKeyFolder}/self.sk
|
||||||
|
${pkgs.openssh}/bin/scp ${demoRosenpassKeys}/peer-a.pk peerakeyexchanger:${rosenpassKeyFolder}/self.pk
|
||||||
|
${pkgs.openssh}/bin/scp ${demoRosenpassKeys}/peer-b.pk peerakeyexchanger:${rosenpassKeyFolder}/peer-b.pk
|
||||||
|
${pkgs.openssh}/bin/scp ${demoRosenpassKeys}/peer-b.sk peerbkeyexchanger:${rosenpassKeyFolder}/self.sk
|
||||||
|
${pkgs.openssh}/bin/scp ${demoRosenpassKeys}/peer-b.pk peerbkeyexchanger:${rosenpassKeyFolder}/self.pk
|
||||||
|
${pkgs.openssh}/bin/scp ${demoRosenpassKeys}/peer-a.pk peerbkeyexchanger:${rosenpassKeyFolder}/peer-a.pk
|
||||||
|
''
|
||||||
|
+ lib.optionalString multiPeer ''
|
||||||
|
${pkgs.openssh}/bin/scp ${demoRosenpassKeys}/peer-c.sk peerckeyexchanger:${rosenpassKeyFolder}/self.sk
|
||||||
|
${pkgs.openssh}/bin/scp ${demoRosenpassKeys}/peer-c.pk peerckeyexchanger:${rosenpassKeyFolder}/self.pk
|
||||||
|
${pkgs.openssh}/bin/scp ${demoRosenpassKeys}/peer-a.pk peerckeyexchanger:${rosenpassKeyFolder}/peer-a.pk
|
||||||
|
${pkgs.openssh}/bin/scp ${demoRosenpassKeys}/peer-b.pk peerckeyexchanger:${rosenpassKeyFolder}/peer-b.pk
|
||||||
|
${pkgs.openssh}/bin/scp ${demoRosenpassKeys}/peer-c.pk peerakeyexchanger:${rosenpassKeyFolder}/peer-c.pk
|
||||||
|
${pkgs.openssh}/bin/scp ${demoRosenpassKeys}/peer-c.pk peerbkeyexchanger:${rosenpassKeyFolder}/peer-c.pk
|
||||||
|
''
|
||||||
|
))
|
||||||
|
|
||||||
|
(pkgs.writeShellScriptBin "watch-wg" ''
|
||||||
|
${pkgs.procps}/bin/watch -n1 \
|
||||||
|
${pkgs.wireguard-tools}/bin/wg show all preshared-keys
|
||||||
|
'')
|
||||||
|
];
|
||||||
|
};
|
||||||
|
nodes.peerA = {
|
||||||
|
virtualisation.forwardPorts = [
|
||||||
|
{
|
||||||
|
from = "host";
|
||||||
|
host.port = 2222;
|
||||||
|
guest.port = 22;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
nodes.peerB = {
|
||||||
|
virtualisation.forwardPorts = [
|
||||||
|
{
|
||||||
|
from = "host";
|
||||||
|
host.port = 2223;
|
||||||
|
guest.port = 22;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
nodes.peerC = {
|
||||||
|
virtualisation.forwardPorts = [
|
||||||
|
{
|
||||||
|
from = "host";
|
||||||
|
host.port = 2224;
|
||||||
|
guest.port = 22;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
testScript = (''
|
||||||
|
start_all()
|
||||||
|
|
||||||
|
for m in [peerA, peerB, peerakeyexchanger, peerbkeyexchanger]:
|
||||||
|
m.wait_for_unit("network-online.target")
|
||||||
|
|
||||||
|
${lib.optionalString multiPeer ''
|
||||||
|
for m in [peerC, peerckeyexchanger]:
|
||||||
|
m.wait_for_unit("network-online.target")
|
||||||
|
''}
|
||||||
|
|
||||||
|
# The wireguard connection can't work because the sync services fail on
|
||||||
|
# non-recognized SSH host keys, we didn't deploy the secrets and because the preshared keyes don't match.
|
||||||
|
peerB.fail("ping -c 1 ${staticConfig.peerA.innerIp}")
|
||||||
|
peerA.fail("ping -c 1 ${staticConfig.peerB.innerIp}")
|
||||||
|
${lib.optionalString multiPeer ''
|
||||||
|
peerA.fail("ping -c 1 ${staticConfig.peerC.innerIp}")
|
||||||
|
peerB.fail("ping -c 1 ${staticConfig.peerC.innerIp}")
|
||||||
|
peerC.fail("ping -c 1 ${staticConfig.peerA.innerIp}")
|
||||||
|
peerC.fail("ping -c 1 ${staticConfig.peerB.innerIp}")
|
||||||
|
''}
|
||||||
|
|
||||||
|
# In admin-reality, this should be done with your favorite secret
|
||||||
|
# provisioning/deployment tool
|
||||||
|
peerakeyexchanger.succeed(
|
||||||
|
"cp ${demoRosenpassKeys}/peer-a.sk ${rosenpassKeyFolder}/self.sk"
|
||||||
|
)
|
||||||
|
peerakeyexchanger.succeed(
|
||||||
|
"cp ${demoRosenpassKeys}/peer-a.pk ${rosenpassKeyFolder}/self.pk"
|
||||||
|
)
|
||||||
|
peerakeyexchanger.succeed(
|
||||||
|
"cp ${demoRosenpassKeys}/peer-b.pk ${rosenpassKeyFolder}/peer-b.pk"
|
||||||
|
)
|
||||||
|
peerbkeyexchanger.succeed(
|
||||||
|
"cp ${demoRosenpassKeys}/peer-b.sk ${rosenpassKeyFolder}/self.sk"
|
||||||
|
)
|
||||||
|
peerbkeyexchanger.succeed(
|
||||||
|
"cp ${demoRosenpassKeys}/peer-b.pk ${rosenpassKeyFolder}/self.pk"
|
||||||
|
)
|
||||||
|
peerbkeyexchanger.succeed(
|
||||||
|
"cp ${demoRosenpassKeys}/peer-a.pk ${rosenpassKeyFolder}/peer-a.pk"
|
||||||
|
)
|
||||||
|
${lib.optionalString multiPeer ''
|
||||||
|
peerakeyexchanger.succeed(
|
||||||
|
"cp ${demoRosenpassKeys}/peer-c.pk ${rosenpassKeyFolder}/peer-c.pk"
|
||||||
|
)
|
||||||
|
peerbkeyexchanger.succeed(
|
||||||
|
"cp ${demoRosenpassKeys}/peer-c.pk ${rosenpassKeyFolder}/peer-c.pk"
|
||||||
|
)
|
||||||
|
peerckeyexchanger.succeed(
|
||||||
|
"cp ${demoRosenpassKeys}/peer-c.sk ${rosenpassKeyFolder}/self.sk"
|
||||||
|
)
|
||||||
|
peerckeyexchanger.succeed(
|
||||||
|
"cp ${demoRosenpassKeys}/peer-c.pk ${rosenpassKeyFolder}/self.pk"
|
||||||
|
)
|
||||||
|
peerckeyexchanger.succeed(
|
||||||
|
"cp ${demoRosenpassKeys}/peer-a.pk ${rosenpassKeyFolder}/peer-a.pk"
|
||||||
|
)
|
||||||
|
peerckeyexchanger.succeed(
|
||||||
|
"cp ${demoRosenpassKeys}/peer-b.pk ${rosenpassKeyFolder}/peer-b.pk"
|
||||||
|
)
|
||||||
|
''}
|
||||||
|
|
||||||
|
# Until now, the services must have failed due to lack of keys
|
||||||
|
peerakeyexchanger.succeed("systemctl restart rp-exchange.service")
|
||||||
|
peerbkeyexchanger.succeed("systemctl restart rp-exchange.service")
|
||||||
|
|
||||||
|
${lib.optionalString multiPeer ''
|
||||||
|
peerckeyexchanger.succeed("systemctl restart rp-exchange.service")
|
||||||
|
''}
|
||||||
|
|
||||||
|
|
||||||
|
# In reality, admins would carefully manage known SSH host keys with
|
||||||
|
# their favorite secret provisioning/deployment tool
|
||||||
|
peerA.succeed("${prepareSshLogin}/bin/prepare-ssh-login peerakeyexchanger")
|
||||||
|
peerB.succeed("${prepareSshLogin}/bin/prepare-ssh-login peerbkeyexchanger")
|
||||||
|
|
||||||
|
${lib.optionalString multiPeer ''
|
||||||
|
peerC.succeed("${prepareSshLogin}/bin/prepare-ssh-login peerckeyexchanger")
|
||||||
|
''}
|
||||||
|
for m in [peerbkeyexchanger, peerakeyexchanger]:
|
||||||
|
m.wait_for_unit("rp-exchange.service")
|
||||||
|
|
||||||
|
${lib.optionalString multiPeer ''
|
||||||
|
peerckeyexchanger.wait_for_unit("rp-exchange.service")
|
||||||
|
''}
|
||||||
|
|
||||||
|
peerA.wait_for_unit("rp-key-sync-AB.service")
|
||||||
|
peerB.wait_for_unit("rp-key-sync-BA.service")
|
||||||
|
|
||||||
|
${lib.optionalString multiPeer ''
|
||||||
|
peerA.wait_for_unit("rp-key-sync-AC.service")
|
||||||
|
peerB.wait_for_unit("rp-key-sync-BC.service")
|
||||||
|
peerC.wait_for_unit("rp-key-sync-CA.service")
|
||||||
|
peerC.wait_for_unit("rp-key-sync-CB.service")
|
||||||
|
''}
|
||||||
|
|
||||||
|
|
||||||
|
# Voila!
|
||||||
|
peerA.succeed("ping -c 1 ${staticConfig.peerB.innerIp}")
|
||||||
|
peerB.succeed("ping -c 1 ${staticConfig.peerA.innerIp}")
|
||||||
|
${lib.optionalString multiPeer ''
|
||||||
|
peerA.succeed("ping -c 1 ${staticConfig.peerC.innerIp}")
|
||||||
|
peerB.succeed("ping -c 1 ${staticConfig.peerC.innerIp}")
|
||||||
|
peerC.succeed("ping -c 1 ${staticConfig.peerA.innerIp}")
|
||||||
|
peerC.succeed("ping -c 1 ${staticConfig.peerB.innerIp}")
|
||||||
|
''}
|
||||||
|
'');
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user