Skip to content

Commit

Permalink
Merge pull request #49 from ngi-nix/algae-rosenpass
Browse files Browse the repository at this point in the history
rosenpass: Add package, module and tests
  • Loading branch information
lorenzleutgeb authored Sep 15, 2023
2 parents 0d1d22b + 577670c commit 93fbc19
Show file tree
Hide file tree
Showing 15 changed files with 683 additions and 1 deletion.
2 changes: 2 additions & 0 deletions all-packages.nix
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
withRedis = true;
withTest = true;
};
rosenpass = callPackage ./pkgs/rosenpass {};
rosenpass-tools = callPackage ./pkgs/rosenpass-tools {};
};

nixpkgs-candidates = {
Expand Down
3 changes: 2 additions & 1 deletion modules/all-modules.nix
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# LiberaForms is intentionally disabled.
# Refer to <https://github.com/ngi-nix/ngipkgs/issues/40>.
#liberaforms = import ./liberaforms.nix;
pretalx = import ./pretalx.nix;
flarum = import ./flarum.nix;
pretalx = import ./pretalx.nix;
rosenpass = import ./rosenpass.nix;
}
178 changes: 178 additions & 0 deletions modules/rosenpass.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
{
config,
lib,
options,
pkgs,
...
}:
with builtins;
with lib; let
cfg = config.services.rosenpass;
opt = options.services.rosenpass;
in {
options.services.rosenpass = with types; {
enable = mkEnableOption "Whether to enable the Rosenpass service to provide post-quantum secure key exchange for WireGuard.";

package = mkPackageOption pkgs "rosenpass" {};

user = mkOption {
type = str;
default = "rosenpass";
description = "User to run Rosenpass as.";
};

group = mkOption {
type = str;
default = "rosenpass";
description = "Primary group of the user running Rosenpass.";
};

publicKeyFile = mkOption {
type = path;
description = "Path to a file containing the public key of the local Rosenpass peer. Generate this by running `rosenpass gen-keys`.";
};

secretKeyFile = mkOption {
type = path;
description = "Path to a file containing the secret key of the local Rosenpass peer. Generate this by running `rosenpass gen-keys`.";
};

defaultDevice = mkOption {
type = nullOr str;
description = "Name of the network interface to use for all peers by default.";
example = "wg0";
};

listen = mkOption {
type = listOf str;
description = "List of local endpoints to listen for connections.";
default = [];
example = literalExpression "[ \"0.0.0.0:10000\" ]";
};

verbosity = mkOption {
type = enum ["Verbose" "Quiet"];
default = "Quiet";
description = "Verbosity of output produced by the service.";
};

peers = let
peer = submodule {
options = {
publicKeyFile = mkOption {
type = path;
description = "Path to a file containing the public key of the remote Rosenpass peer.";
};

endpoint = mkOption {
type = nullOr str;
default = null;
description = "Endpoint of the remote Rosenpass peer.";
};

device = mkOption {
type = str;
default = cfg.defaultDevice;
defaultText = literalExpression "config.${opt.defaultDevice}";
description = "Name of the local WireGuard interface to use for this peer.";
};

wireguard = mkOption {
type = submodule {
options = {
publicKey = mkOption {
type = str;
description = "WireGuard public key corresponding to the remote Rosenpass peer.";
};
};
};
description = "WireGuard configuration for this peer.";
};
};
};
in
mkOption {
type = listOf peer;
description = "List of peers to exchange keys with.";
default = [];
};

extraConfig = mkOption {
type = attrs;
description = ''
Extra configuration to be merged with the generated Rosenpass configuration file.
'';
default = {};
};
};

config = mkIf cfg.enable {
warnings = let
netdevsList = attrValues config.systemd.network.netdevs;
publicKeyInNetdevs = peer: any (netdev: any (publicKeyInWireguardPeers peer) netdev.wireguardPeers) netdevsList;
publicKeyInWireguardPeers = peer: x: x.wireguardPeerConfig ? PublicKey && x.wireguardPeerConfig.PublicKey == peer.wireguard.publicKey;

# NOTE: In the message below, we tried to refer to
# options.systemd.network.netdevs."<name>".wireguardPeers.*.PublicKey
# directly, but don't know how to traverse "<name>" and * in this path.
warningMsg = peer: "It appears that you have configured a Rosenpass peer with the Wireguard public key '${peer.wireguard.publicKey}' but there is no corresponding Wireguard peer configuration in any of `${options.systemd.network.netdevs}.\"<name>\".wireguardPeers.*.PublicKey`. While this may work as expected, such a scenario is unusual. Please double-check your configuration.";
in
concatMap (peer: optional (!publicKeyInNetdevs peer) (warningMsg peer)) cfg.peers;

environment.systemPackages = [cfg.package pkgs.wireguard-tools];

users.users."${cfg.user}" = {
isSystemUser = true;
createHome = false;
group = cfg.group;
};

users.groups."${cfg.group}" = {};

# NOTE: It would be possible to use systemd credentials for pqsk.
# <https://systemd.io/CREDENTIALS/>
systemd.services.rosenpass = let
generatePeerConfig = {
publicKeyFile,
endpoint,
device,
wireguard,
}:
{
inherit device;
public_key = publicKeyFile;
peer = wireguard.publicKey;
extra_params = [];
}
// (optionalAttrs (endpoint != null) {inherit endpoint;});

generateConfig = {
publicKeyFile,
secretKeyFile,
listen,
verbosity,
peers,
...
}: {
inherit listen verbosity;
public_key = publicKeyFile;
secret_key = secretKeyFile;
peers = map generatePeerConfig peers;
};
toml = pkgs.formats.toml {};
configFile = toml.generate "config.toml" (recursiveUpdate (generateConfig cfg) cfg.extraConfig);
in {
wantedBy = ["multi-user.target"];
after = ["network-online.target"];
path = [pkgs.wireguard-tools];

script = "${cfg.package}/bin/rosenpass exchange-config ${configFile}";

serviceConfig = {
User = cfg.user;
Group = cfg.group;
AmbientCapabilities = ["CAP_NET_ADMIN"];
};
};
};
}
35 changes: 35 additions & 0 deletions pkgs/rosenpass-tools/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
lib,
makeWrapper,
stdenv,
coreutils,
findutils,
gawk,
rosenpass,
wireguard-tools,
}:
stdenv.mkDerivation {
pname = "rosenpass-tools";
inherit (rosenpass) version src;

nativeBuildInputs = [makeWrapper];

postInstall = let
rpDependencies = [
coreutils
findutils
gawk
rosenpass
wireguard-tools
];
in ''
install -D $src/rp $out/bin/rp
install -D $src/doc/rp.1 $out/share/man/man1/rp.1
wrapProgram $out/bin/rp --prefix PATH : ${lib.makeBinPath rpDependencies}
'';

meta = {
inherit (rosenpass.meta) homepage license maintainers;
description = rosenpass.meta.description + " This package contains `rp`, which is a script that wraps the `rosenpass` binary.";
};
}
69 changes: 69 additions & 0 deletions pkgs/rosenpass/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
{
lib,
fetchFromGitHub,
nixosTests,
rustPlatform,
targetPlatform,
cmake,
libsodium,
pkg-config,
}:
rustPlatform.buildRustPackage rec {
pname = "rosenpass";
version = "0.2.0";
src = fetchFromGitHub {
owner = pname;
repo = pname;
rev = "v${version}";
hash = "sha256-r7/3C5DzXP+9w4rp9XwbP+/NK1axIP6s3Iiio1xRMbk=";
};

cargoHash = "sha256-g2w3lZXQ3Kg3ydKdFs8P2lOPfIkfTbAF0MhxsJoX/E4=";

nativeBuildInputs = [
cmake # for oqs build in the oqs-sys crate
pkg-config # let libsodium-sys-stable find libsodium
rustPlatform.bindgenHook # for C-bindings in the crypto libs
];

buildInputs = [libsodium];

# liboqs requires quite a lot of stack memory, thus we adjust
# Increase the default stack size picked for new threads (which is used
# by `cargo test`) to be _big enough_.
# Only set this value for the check phase (not as an environment variable for the derivation),
# because it is only required in this phase.
preCheck = "export RUST_MIN_STACK=${builtins.toString (8 * 1024 * 1024)}"; # 8 MiB

# nix defaults to building for aarch64 _without_ the armv8-a
# crypto extensions, but liboqs depends on these
preBuild =
lib.optionalString targetPlatform.isAarch
''NIX_CFLAGS_COMPILE="$NIX_CFLAGS_COMPILE -march=armv8-a+crypto"'';

preInstall = "install -D doc/rosenpass.1 $out/share/man/man1/rosenpass.1";

meta = with lib; {
description = "Build post-quantum-secure VPNs with WireGuard!";
homepage = "https://rosenpass.eu/";
license = with licenses; [
mit
/*
or
*/
asl20
];
platforms = platforms.all;
maintainers = with maintainers;
[
andresnav
imincik
lorenzleutgeb
]
++ (with (import ../../maintainers/maintainers-list.nix); [augustebaum kubaneko]);
};

passthru.tests = {
inherit (nixosTests) rosenpass;
};
}
9 changes: 9 additions & 0 deletions tests/all-tests.nix
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,13 @@
];
};
};

rosenpass = import ./rosenpass {
configurations.shared = {...}: {
imports = [
modules.rosenpass
modules.sops-nix
];
};
};
}
15 changes: 15 additions & 0 deletions tests/rosenpass/.sops.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
keys:
- &andres-nav age12qdlu87t3sh0m02eh5n3kx75d98h2evgme4uue2ucl4e4acg8ffszzlkss
- &augustebaum age16xvvc22xm35xzgc2yx6cpcz6wnww8tf6rwfmrag4vnem7nq6h9pqr33epg
- &imincik age1przhvgd5gy3kvcmlmsu47qmauhdwzzez7z89xyv623em72e9mfzskefwcl
- &lorenzleutgeb age1c0g6s6daxy79dlm9uqczwlkh0hvjpghw5h8zzljc3vs275rvvqus30hv9l
- &test age1fdlfm8xh97fwpf8fls9245wre9aktg6dzhhrr02s028kz4g38ajss2guwx
creation_rules:
- path_regex: ^(client|server)\/.*\.(yaml|bin)$
key_groups:
- age:
- *andres-nav
- *augustebaum
- *imincik
- *lorenzleutgeb
- *test
Binary file added tests/rosenpass/client/pqpk.bin
Binary file not shown.
36 changes: 36 additions & 0 deletions tests/rosenpass/client/pqsk.yaml

Large diffs are not rendered by default.

58 changes: 58 additions & 0 deletions tests/rosenpass/client/sops.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
wireguard:
wgsk: ENC[AES256_GCM,data:k+45ywsKGgTelWa3tjFZyaRzKya/yLVPTM2CwwQNzJ9fx54olcfzLlwpxi8=,iv:YoLWrnVZSEFyGmC2k7KmAiWOrtjtBEhR5cwX6ehWnVc=,tag:SkJh7vGBwL2nmIwJx++b7g==,type:str]
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age:
- recipient: age12qdlu87t3sh0m02eh5n3kx75d98h2evgme4uue2ucl4e4acg8ffszzlkss
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBISXREQ2tlVUtVUHNkSCsz
b2NBOG00UmZjZkVCQ2pyK3lQblRCMlBHQUJZCmFEcVVZNDZYWGZrcVl6RDZYVnAw
aTlmWTNsbkVaNC9FMVBlQW9OYmNpcDAKLS0tIEw1SThDcEJmSnpQYVBueHRLZFRr
NlVmQ3JXUTd6dStCSHlITW04WllyUjgKKLk6KAZV2496O+LiZNSXDSm92VSyT22y
wsiHb9wzuKP5qUqPsbNezSsyRUbgPpn3HTO+v7JBuYqu5GLJj3RIfA==
-----END AGE ENCRYPTED FILE-----
- recipient: age16xvvc22xm35xzgc2yx6cpcz6wnww8tf6rwfmrag4vnem7nq6h9pqr33epg
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBZZndSaWd0cktaL2dFbWxW
UWZHTk44V3Y1bzBSUnRhbFloVlpRWXJIU0c0Cng2ZmN3YnJWSitSWnNjUUhGeENS
SG1iQzkwaVUvUFZGVEF5VUhEaHpqNlUKLS0tIG5kV1Z3ODJmdEFNSm14N3IzblJq
eDltbkNNc0g5eVk4a1dMSTQ5VjRoRVUKs+Pxd0W6MRIjSsS2IFHdX4Q4m84v7K0H
JoY+r4I1NxAJWt6uoDhc9JIiagwPABjzN5nfe1+4o0nYmwmndG4XiQ==
-----END AGE ENCRYPTED FILE-----
- recipient: age1przhvgd5gy3kvcmlmsu47qmauhdwzzez7z89xyv623em72e9mfzskefwcl
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2dGI4Mk9yc3RXL2FEdFBH
RjRmalliZ0ZVTUJuQkUrQ1BSdHQraFo4L2wwCkVNdjM4QVpEZGoyMEllWjRpOW5u
TjJHOVFXeGxYYVNyZjBRMGd1amFLOEUKLS0tIHNhU0QrYUwwYXBZZjFKT29vMVpy
NTRaSFdhR05qMitNcEZGRkMwd0g1d28KYInWBN/Qmp1y8A0gCk9LDXshkf27/xzj
7Tdk57u52E7MqAcWUO8R8EbI79ePH2j4VXzhVNKsnjKODZ2MrcGvRg==
-----END AGE ENCRYPTED FILE-----
- recipient: age1c0g6s6daxy79dlm9uqczwlkh0hvjpghw5h8zzljc3vs275rvvqus30hv9l
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBxT1Z3MzlxMjgrY1VHR2xt
aFNreEszNFBUZGRoSEJ5QVRRMVc4Ty9FYnhzCkdzT2tWNXNKclQzVGJHMEtGR01E
ejlWbDAwTmYxNUgxL2dXbVg5ankwRTgKLS0tIHpvWUJ0UGhWMlgxMjk5Tko3Skdq
V05aR2JyU2hac2F1UWx6VnpxekF6UGsK9iBYVAkMCvmCIcxCkGU3Bq9t7kFs2gyz
uiUBUxgLTyfdmxZ4HDQFpMm66O6SHc5Zx0738/AClU89fhvtANJeyw==
-----END AGE ENCRYPTED FILE-----
- recipient: age1fdlfm8xh97fwpf8fls9245wre9aktg6dzhhrr02s028kz4g38ajss2guwx
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBJUTFQZzljQ3hCUHFESUlm
aGJOeXUvc0ZDTm1jOGlnMnM0QzFOOGRuODI0CjdlM2ZXWnJFUFFqVXFZRGFsZ2V2
SmxHUGhtM2Y2d016V0ZYZkp1cXFMSFkKLS0tIGlvRjNHWDFEUEY1bXRqaWtjOXpi
RWhubmtWdVZwbml6QzlMcXVwL0RZa1EKcBXzTtmaB4Rg+qHVQilx8jPVjHbJ366r
OOYx12stTCvAcbrLCUhZRhsm9mLEmudFQcC36QxqxJfhYB3tKukLaQ==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2023-09-05T18:39:18Z"
mac: ENC[AES256_GCM,data:ZLevHXqYLBzz9F+UDHWGXCv9gjgoWRlhOyPB2hDXbmTptiDwoeJ1AAyXWCXCmgdR4zsNv9UuIpY++Q7zY1r2TEsQSfdCH1sELcBfn41QidLvrQDo5Ua1k08SiNyAeytsDAXRCr0YuOSA7gKydKt40RXCAo9FYNpr6iII7XWHLyI=,iv:fqt6TaPKgZyxn+NqXeH1i5kV2i6nprAlkvmRGkRU7V4=,tag:nZLc9jdY5kKeQ+JoX8eWeA==,type:str]
pgp: []
unencrypted_suffix: _unencrypted
version: 3.7.3
Loading

0 comments on commit 93fbc19

Please sign in to comment.