Merge pull request #163009 from scvalex/nbd-service
nbd: add nbd service and test
This commit is contained in:
commit
af0f3944bd
8 changed files with 269 additions and 1 deletions
|
@ -256,6 +256,13 @@
|
||||||
<link xlink:href="options.html#opt-services.ethercalc.enable">services.ethercalc</link>.
|
<link xlink:href="options.html#opt-services.ethercalc.enable">services.ethercalc</link>.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<link xlink:href="https://nbd.sourceforge.io/">nbd</link>, a
|
||||||
|
Network Block Device server. Available as
|
||||||
|
<link xlink:href="options.html#opt-services.nbd.server.enable">services.nbd</link>.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
<link xlink:href="https://timetagger.app">timetagger</link>,
|
<link xlink:href="https://timetagger.app">timetagger</link>,
|
||||||
|
|
|
@ -75,6 +75,8 @@ In addition to numerous new and upgraded packages, this release has the followin
|
||||||
- [ethercalc](https://github.com/audreyt/ethercalc), an online collaborative
|
- [ethercalc](https://github.com/audreyt/ethercalc), an online collaborative
|
||||||
spreadsheet. Available as [services.ethercalc](options.html#opt-services.ethercalc.enable).
|
spreadsheet. Available as [services.ethercalc](options.html#opt-services.ethercalc.enable).
|
||||||
|
|
||||||
|
- [nbd](https://nbd.sourceforge.io/), a Network Block Device server. Available as [services.nbd](options.html#opt-services.nbd.server.enable).
|
||||||
|
|
||||||
- [timetagger](https://timetagger.app), an open source time-tracker with an intuitive user experience and powerful reporting. [services.timetagger](options.html#opt-services.timetagger.enable).
|
- [timetagger](https://timetagger.app), an open source time-tracker with an intuitive user experience and powerful reporting. [services.timetagger](options.html#opt-services.timetagger.enable).
|
||||||
|
|
||||||
- [rstudio-server](https://www.rstudio.com/products/rstudio/#rstudio-server), a browser-based version of the RStudio IDE for the R programming language. Available as [services.rstudio-server](options.html#opt-services.rstudio-server.enable).
|
- [rstudio-server](https://www.rstudio.com/products/rstudio/#rstudio-server), a browser-based version of the RStudio IDE for the R programming language. Available as [services.rstudio-server](options.html#opt-services.rstudio-server.enable).
|
||||||
|
|
|
@ -180,6 +180,7 @@
|
||||||
./programs/msmtp.nix
|
./programs/msmtp.nix
|
||||||
./programs/mtr.nix
|
./programs/mtr.nix
|
||||||
./programs/nano.nix
|
./programs/nano.nix
|
||||||
|
./programs/nbd.nix
|
||||||
./programs/neovim.nix
|
./programs/neovim.nix
|
||||||
./programs/nm-applet.nix
|
./programs/nm-applet.nix
|
||||||
./programs/npm.nix
|
./programs/npm.nix
|
||||||
|
@ -819,6 +820,7 @@
|
||||||
./services/networking/nar-serve.nix
|
./services/networking/nar-serve.nix
|
||||||
./services/networking/nat.nix
|
./services/networking/nat.nix
|
||||||
./services/networking/nats.nix
|
./services/networking/nats.nix
|
||||||
|
./services/networking/nbd.nix
|
||||||
./services/networking/ndppd.nix
|
./services/networking/ndppd.nix
|
||||||
./services/networking/nebula.nix
|
./services/networking/nebula.nix
|
||||||
./services/networking/networkmanager.nix
|
./services/networking/networkmanager.nix
|
||||||
|
|
19
nixos/modules/programs/nbd.nix
Normal file
19
nixos/modules/programs/nbd.nix
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.programs.nbd;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options = {
|
||||||
|
programs.nbd = {
|
||||||
|
enable = mkEnableOption "Network Block Device (nbd) support";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
environment.systemPackages = with pkgs; [ nbd ];
|
||||||
|
boot.kernelModules = [ "nbd" ];
|
||||||
|
};
|
||||||
|
}
|
146
nixos/modules/services/networking/nbd.nix
Normal file
146
nixos/modules/services/networking/nbd.nix
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.services.nbd;
|
||||||
|
configFormat = pkgs.formats.ini { };
|
||||||
|
iniFields = with types; attrsOf (oneOf [ bool int float str ]);
|
||||||
|
serverConfig = configFormat.generate "nbd-server-config"
|
||||||
|
({
|
||||||
|
generic =
|
||||||
|
(cfg.server.extraOptions // {
|
||||||
|
user = "root";
|
||||||
|
group = "root";
|
||||||
|
port = cfg.server.listenPort;
|
||||||
|
} // (optionalAttrs (cfg.server.listenAddress != null) {
|
||||||
|
listenaddr = cfg.server.listenAddress;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
// (mapAttrs
|
||||||
|
(_: { path, allowAddresses, extraOptions }:
|
||||||
|
extraOptions // {
|
||||||
|
exportname = path;
|
||||||
|
} // (optionalAttrs (allowAddresses != null) {
|
||||||
|
authfile = pkgs.writeText "authfile" (concatStringsSep "\n" allowAddresses);
|
||||||
|
}))
|
||||||
|
cfg.server.exports)
|
||||||
|
);
|
||||||
|
splitLists =
|
||||||
|
partition
|
||||||
|
(path: hasPrefix "/dev/" path)
|
||||||
|
(mapAttrsToList (_: { path, ... }: path) cfg.server.exports);
|
||||||
|
allowedDevices = splitLists.right;
|
||||||
|
boundPaths = splitLists.wrong;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options = {
|
||||||
|
services.nbd = {
|
||||||
|
server = {
|
||||||
|
enable = mkEnableOption "the Network Block Device (nbd) server";
|
||||||
|
|
||||||
|
listenPort = mkOption {
|
||||||
|
type = types.port;
|
||||||
|
default = 10809;
|
||||||
|
description = "Port to listen on. The port is NOT automatically opened in the firewall.";
|
||||||
|
};
|
||||||
|
|
||||||
|
extraOptions = mkOption {
|
||||||
|
type = iniFields;
|
||||||
|
default = {
|
||||||
|
allowlist = false;
|
||||||
|
};
|
||||||
|
description = ''
|
||||||
|
Extra options for the server. See
|
||||||
|
<citerefentry><refentrytitle>nbd-server</refentrytitle>
|
||||||
|
<manvolnum>5</manvolnum></citerefentry>.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
exports = mkOption {
|
||||||
|
description = "Files or block devices to make available over the network.";
|
||||||
|
default = { };
|
||||||
|
type = with types; attrsOf
|
||||||
|
(submodule {
|
||||||
|
options = {
|
||||||
|
path = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = "File or block device to export.";
|
||||||
|
example = "/dev/sdb1";
|
||||||
|
};
|
||||||
|
|
||||||
|
allowAddresses = mkOption {
|
||||||
|
type = nullOr (listOf str);
|
||||||
|
default = null;
|
||||||
|
example = [ "10.10.0.0/24" "127.0.0.1" ];
|
||||||
|
description = "IPs and subnets that are authorized to connect for this device. If not specified, the server will allow all connections.";
|
||||||
|
};
|
||||||
|
|
||||||
|
extraOptions = mkOption {
|
||||||
|
type = iniFields;
|
||||||
|
default = {
|
||||||
|
flush = true;
|
||||||
|
fua = true;
|
||||||
|
};
|
||||||
|
description = ''
|
||||||
|
Extra options for this export. See
|
||||||
|
<citerefentry><refentrytitle>nbd-server</refentrytitle>
|
||||||
|
<manvolnum>5</manvolnum></citerefentry>.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
listenAddress = mkOption {
|
||||||
|
type = with types; nullOr str;
|
||||||
|
description = "Address to listen on. If not specified, the server will listen on all interfaces.";
|
||||||
|
default = null;
|
||||||
|
example = "10.10.0.1";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.server.enable {
|
||||||
|
boot.kernelModules = [ "nbd" ];
|
||||||
|
|
||||||
|
systemd.services.nbd-server = {
|
||||||
|
after = [ "network-online.target" ];
|
||||||
|
before = [ "multi-user.target" ];
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
serviceConfig = {
|
||||||
|
ExecStart = "${pkgs.nbd}/bin/nbd-server -C ${serverConfig}";
|
||||||
|
Type = "forking";
|
||||||
|
|
||||||
|
DeviceAllow = map (path: "${path} rw") allowedDevices;
|
||||||
|
BindPaths = boundPaths;
|
||||||
|
|
||||||
|
CapabilityBoundingSet = "";
|
||||||
|
DevicePolicy = "closed";
|
||||||
|
LockPersonality = true;
|
||||||
|
MemoryDenyWriteExecute = true;
|
||||||
|
NoNewPrivileges = true;
|
||||||
|
PrivateDevices = false;
|
||||||
|
PrivateMounts = true;
|
||||||
|
PrivateTmp = true;
|
||||||
|
PrivateUsers = true;
|
||||||
|
ProcSubset = "pid";
|
||||||
|
ProtectClock = true;
|
||||||
|
ProtectControlGroups = true;
|
||||||
|
ProtectHome = true;
|
||||||
|
ProtectHostname = true;
|
||||||
|
ProtectKernelLogs = true;
|
||||||
|
ProtectKernelModules = true;
|
||||||
|
ProtectKernelTunables = true;
|
||||||
|
ProtectProc = "noaccess";
|
||||||
|
ProtectSystem = "strict";
|
||||||
|
RestrictAddressFamilies = "AF_INET AF_INET6";
|
||||||
|
RestrictNamespaces = true;
|
||||||
|
RestrictRealtime = true;
|
||||||
|
RestrictSUIDSGID = true;
|
||||||
|
UMask = "0077";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
|
@ -329,6 +329,7 @@ in
|
||||||
nat.standalone = handleTest ./nat.nix { withFirewall = false; };
|
nat.standalone = handleTest ./nat.nix { withFirewall = false; };
|
||||||
nats = handleTest ./nats.nix {};
|
nats = handleTest ./nats.nix {};
|
||||||
navidrome = handleTest ./navidrome.nix {};
|
navidrome = handleTest ./navidrome.nix {};
|
||||||
|
nbd = handleTest ./nbd.nix {};
|
||||||
ncdns = handleTest ./ncdns.nix {};
|
ncdns = handleTest ./ncdns.nix {};
|
||||||
ndppd = handleTest ./ndppd.nix {};
|
ndppd = handleTest ./ndppd.nix {};
|
||||||
nebula = handleTest ./nebula.nix {};
|
nebula = handleTest ./nebula.nix {};
|
||||||
|
|
87
nixos/tests/nbd.nix
Normal file
87
nixos/tests/nbd.nix
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
import ./make-test-python.nix ({ pkgs, ... }:
|
||||||
|
let
|
||||||
|
listenPort = 30123;
|
||||||
|
testString = "It works!";
|
||||||
|
mkCreateSmallFileService = { path, loop ? false }: {
|
||||||
|
script = ''
|
||||||
|
${pkgs.coreutils}/bin/dd if=/dev/zero of=${path} bs=1K count=100
|
||||||
|
${pkgs.lib.optionalString loop
|
||||||
|
"${pkgs.util-linux}/bin/losetup --find ${path}"}
|
||||||
|
'';
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
};
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
before = [ "nbd-server.service" ];
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
name = "nbd";
|
||||||
|
|
||||||
|
nodes = {
|
||||||
|
server = { config, pkgs, ... }: {
|
||||||
|
# Create some small files of zeros to use as the ndb disks
|
||||||
|
## `vault-pub.disk` is accessible from any IP
|
||||||
|
systemd.services.create-pub-file =
|
||||||
|
mkCreateSmallFileService { path = "/vault-pub.disk"; };
|
||||||
|
## `vault-priv.disk` is accessible only from localhost.
|
||||||
|
## It's also a loopback device to test exporting /dev/...
|
||||||
|
systemd.services.create-priv-file =
|
||||||
|
mkCreateSmallFileService { path = "/vault-priv.disk"; loop = true; };
|
||||||
|
|
||||||
|
# Needed only for nbd-client used in the tests.
|
||||||
|
environment.systemPackages = [ pkgs.nbd ];
|
||||||
|
|
||||||
|
# Open the nbd port in the firewall
|
||||||
|
networking.firewall.allowedTCPPorts = [ listenPort ];
|
||||||
|
|
||||||
|
# Run the nbd server and expose the small file created above
|
||||||
|
services.nbd.server = {
|
||||||
|
enable = true;
|
||||||
|
exports = {
|
||||||
|
vault-pub = {
|
||||||
|
path = "/vault-pub.disk";
|
||||||
|
};
|
||||||
|
vault-priv = {
|
||||||
|
path = "/dev/loop0";
|
||||||
|
allowAddresses = [ "127.0.0.1" "::1" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
listenAddress = "0.0.0.0";
|
||||||
|
listenPort = listenPort;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
client = { config, pkgs, ... }: {
|
||||||
|
programs.nbd.enable = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
testScript = ''
|
||||||
|
testString = "${testString}"
|
||||||
|
|
||||||
|
start_all()
|
||||||
|
server.wait_for_open_port(${toString listenPort})
|
||||||
|
|
||||||
|
# Client: Connect to the server, write a small string to the nbd disk, and cleanly disconnect
|
||||||
|
client.succeed("nbd-client server ${toString listenPort} /dev/nbd0 -name vault-pub -persist")
|
||||||
|
client.succeed(f"echo '{testString}' | dd of=/dev/nbd0 conv=notrunc")
|
||||||
|
client.succeed("nbd-client -d /dev/nbd0")
|
||||||
|
|
||||||
|
# Server: Check that the string written by the client is indeed in the file
|
||||||
|
foundString = server.succeed(f"dd status=none if=/vault-pub.disk count={len(testString)}")[:len(testString)]
|
||||||
|
if foundString != testString:
|
||||||
|
raise Exception(f"Read the wrong string from nbd disk. Expected: '{testString}'. Found: '{foundString}'")
|
||||||
|
|
||||||
|
# Client: Fail to connect to the private disk
|
||||||
|
client.fail("nbd-client server ${toString listenPort} /dev/nbd0 -name vault-priv -persist")
|
||||||
|
|
||||||
|
# Server: Successfully connect to the private disk
|
||||||
|
server.succeed("nbd-client localhost ${toString listenPort} /dev/nbd0 -name vault-priv -persist")
|
||||||
|
server.succeed(f"echo '{testString}' | dd of=/dev/nbd0 conv=notrunc")
|
||||||
|
foundString = server.succeed(f"dd status=none if=/dev/loop0 count={len(testString)}")[:len(testString)]
|
||||||
|
if foundString != testString:
|
||||||
|
raise Exception(f"Read the wrong string from nbd disk. Expected: '{testString}'. Found: '{foundString}'")
|
||||||
|
server.succeed("nbd-client -d /dev/nbd0")
|
||||||
|
'';
|
||||||
|
})
|
|
@ -1,4 +1,4 @@
|
||||||
{ lib, stdenv, fetchurl, pkg-config, glib, which }:
|
{ lib, stdenv, fetchurl, pkg-config, glib, which, nixosTests }:
|
||||||
|
|
||||||
stdenv.mkDerivation rec {
|
stdenv.mkDerivation rec {
|
||||||
pname = "nbd";
|
pname = "nbd";
|
||||||
|
@ -21,6 +21,10 @@ stdenv.mkDerivation rec {
|
||||||
|
|
||||||
doCheck = true;
|
doCheck = true;
|
||||||
|
|
||||||
|
passthru.tests = {
|
||||||
|
test = nixosTests.nbd;
|
||||||
|
};
|
||||||
|
|
||||||
# Glib calls `clock_gettime', which is in librt. Linking that library
|
# Glib calls `clock_gettime', which is in librt. Linking that library
|
||||||
# here ensures that a proper rpath is added to the executable so that
|
# here ensures that a proper rpath is added to the executable so that
|
||||||
# it can be loaded at run-time.
|
# it can be loaded at run-time.
|
||||||
|
|
Loading…
Reference in a new issue