summary refs log tree commit diff
path: root/nixos/tests/nfs
diff options
context:
space:
mode:
authorKai Wohlfahrt <kai.wohlfahrt@gmail.com>2019-11-27 14:37:27 +0000
committerKai Wohlfahrt <kai.wohlfahrt@gmail.com>2019-12-12 15:42:44 +0000
commit0dce66a6ec4efefa421e17de7fba433acf7cd6fd (patch)
tree1d7906cc7d73e15193ecd32d021644d20439f54b /nixos/tests/nfs
parentea55a2d8a9eb030b58a494c13d1fcf6e5e71e625 (diff)
downloadnixpkgs-0dce66a6ec4efefa421e17de7fba433acf7cd6fd.tar
nixpkgs-0dce66a6ec4efefa421e17de7fba433acf7cd6fd.tar.gz
nixpkgs-0dce66a6ec4efefa421e17de7fba433acf7cd6fd.tar.bz2
nixpkgs-0dce66a6ec4efefa421e17de7fba433acf7cd6fd.tar.lz
nixpkgs-0dce66a6ec4efefa421e17de7fba433acf7cd6fd.tar.xz
nixpkgs-0dce66a6ec4efefa421e17de7fba433acf7cd6fd.tar.zst
nixpkgs-0dce66a6ec4efefa421e17de7fba433acf7cd6fd.zip
nixos/nfs: test nfs with kerberos authentication
Diffstat (limited to 'nixos/tests/nfs')
-rw-r--r--nixos/tests/nfs/default.nix9
-rw-r--r--nixos/tests/nfs/kerberos.nix140
-rw-r--r--nixos/tests/nfs/simple.nix94
3 files changed, 243 insertions, 0 deletions
diff --git a/nixos/tests/nfs/default.nix b/nixos/tests/nfs/default.nix
new file mode 100644
index 00000000000..6bc803c91b4
--- /dev/null
+++ b/nixos/tests/nfs/default.nix
@@ -0,0 +1,9 @@
+{ version ? 4
+, system ? builtins.currentSystem
+, pkgs ? import ../../.. { inherit system; }
+}: {
+  simple = import ./simple.nix { inherit version system pkgs; };
+} // pkgs.lib.optionalAttrs (version == 4) {
+  # TODO: Test kerberos + nfsv3
+  kerberos = import ./kerberos.nix { inherit version system pkgs; };
+}
diff --git a/nixos/tests/nfs/kerberos.nix b/nixos/tests/nfs/kerberos.nix
new file mode 100644
index 00000000000..1c45b6542fd
--- /dev/null
+++ b/nixos/tests/nfs/kerberos.nix
@@ -0,0 +1,140 @@
+import ../make-test-python.nix ({ pkgs, lib, ... }:
+
+with lib;
+
+let
+  krb5 = 
+    { enable = true;
+      domain_realm."nfs.test"   = "NFS.TEST";
+      libdefaults.default_realm = "NFS.TEST";
+      realms."NFS.TEST" =
+        { admin_server = "server.nfs.test";
+          kdc = "server.nfs.test";
+        };
+    };
+
+  hosts =
+    ''
+      192.168.1.1 client.nfs.test
+      192.168.1.2 server.nfs.test
+    '';
+
+  users = {
+    users.alice = {
+        isNormalUser = true;
+        name = "alice";
+        uid = 1000;
+      };
+  };
+
+  environment = {
+    etc."request-key.conf".text = ''
+      create id_resolver * * ${pkgs.nfs-utils}/bin/nfsidmap -t 600 %k %d
+    '';
+    systemPackages = with pkgs; [ keyutils ];
+  };
+
+in
+
+{
+  name = "nfsv4-with-kerberos";
+ 
+  nodes = {
+    client = { lib, ... }:
+      { inherit krb5 users environment;
+
+        networking.extraHosts = hosts;
+        networking.domain = "nfs.test";
+        networking.hostName = "client";
+
+        fileSystems = lib.mkVMOverride
+          { "/data" = {
+              device  = "server.nfs.test:/";
+              fsType  = "nfs";
+              options = [ "nfsvers=4" "sec=krb5p" "noauto" ];
+            };
+          };
+      };
+
+    server = { lib, ...}:
+      { inherit krb5 users environment;
+
+        networking.extraHosts = hosts;
+        networking.domain = "nfs.test";
+        networking.hostName = "server";
+
+        networking.firewall.allowedTCPPorts = [
+          111  # rpc
+          2049 # nfs
+          88   # kerberos
+          749  # kerberos admin
+        ];
+
+        services.kerberos_server.enable = true;
+        services.kerberos_server.realms =
+          { "NFS.TEST".acl =
+            [ { access = "all"; principal = "admin/admin"; } ];
+          };
+
+        services.nfs.server.enable = true;
+        services.nfs.server.createMountPoints = true;
+        services.nfs.server.exports =
+          ''
+            /data *(rw,no_root_squash,fsid=0,sec=krb5p)
+          '';
+      };
+  };
+
+  testScript =
+    ''
+      server.succeed("mkdir -p /data/alice")
+      server.succeed("chown alice:users /data/alice")
+
+      # set up kerberos database
+      server.succeed(
+          "kdb5_util create -s -r NFS.TEST -P master_key",
+          "systemctl restart kadmind.service kdc.service",
+      )
+      server.wait_for_unit(f"kadmind.service")
+      server.wait_for_unit(f"kdc.service")
+
+      # create principals
+      server.succeed(
+          "kadmin.local add_principal -randkey nfs/server.nfs.test",
+          "kadmin.local add_principal -randkey nfs/client.nfs.test",
+          "kadmin.local add_principal -pw admin_pw admin/admin",
+          "kadmin.local add_principal -pw alice_pw alice",
+      )
+
+      # add principals to server keytab
+      server.succeed("kadmin.local ktadd nfs/server.nfs.test")
+      server.succeed("systemctl start rpc-gssd.service rpc-svcgssd.service")
+      server.wait_for_unit(f"rpc-gssd.service")
+      server.wait_for_unit(f"rpc-svcgssd.service")
+
+      client.wait_for_unit("network-online.target")
+
+      # add principals to client keytab
+      client.succeed("echo admin_pw | kadmin -p admin/admin ktadd nfs/client.nfs.test")
+      client.succeed("systemctl start rpc-gssd.service")
+      client.wait_for_unit("rpc-gssd.service")
+
+      with subtest("nfs share mounts"):
+          client.succeed("systemctl restart data.mount")
+          client.wait_for_unit("data.mount")
+
+      with subtest("permissions on nfs share are enforced"):
+          client.fail("su alice -c 'ls /data'")
+          client.succeed("su alice -c 'echo alice_pw | kinit'")
+          client.succeed("su alice -c 'ls /data'")
+
+          client.fail("su alice -c 'echo bla >> /data/foo'")
+          client.succeed("su alice -c 'echo bla >> /data/alice/foo'")
+          server.succeed("test -e /data/alice/foo")
+
+      with subtest("uids/gids are mapped correctly on nfs share"):
+          ids = client.succeed("stat -c '%U %G' /data/alice").split()
+          expected = ["alice", "users"]
+          assert ids == expected, f"ids incorrect: got {ids} expected {expected}"
+    '';
+})
diff --git a/nixos/tests/nfs/simple.nix b/nixos/tests/nfs/simple.nix
new file mode 100644
index 00000000000..a1a09ee0f45
--- /dev/null
+++ b/nixos/tests/nfs/simple.nix
@@ -0,0 +1,94 @@
+import ../make-test-python.nix ({ pkgs, version ? 4, ... }:
+
+let
+
+  client =
+    { pkgs, ... }:
+    { fileSystems = pkgs.lib.mkVMOverride
+        [ { mountPoint = "/data";
+            # nfs4 exports the export with fsid=0 as a virtual root directory
+            device = if (version == 4) then "server:/" else "server:/data";
+            fsType = "nfs";
+            options = [ "vers=${toString version}" ];
+          }
+        ];
+      networking.firewall.enable = false; # FIXME: only open statd
+    };
+
+in
+
+{
+  name = "nfs";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ eelco ];
+  };
+
+  nodes =
+    { client1 = client;
+      client2 = client;
+
+      server =
+        { ... }:
+        { services.nfs.server.enable = true;
+          services.nfs.server.exports =
+            ''
+              /data 192.168.1.0/255.255.255.0(rw,no_root_squash,no_subtree_check,fsid=0)
+            '';
+          services.nfs.server.createMountPoints = true;
+          networking.firewall.enable = false; # FIXME: figure out what ports need to be allowed
+        };
+    };
+
+  testScript =
+    ''
+      import time
+
+      server.wait_for_unit("nfs-server")
+      server.succeed("systemctl start network-online.target")
+      server.wait_for_unit("network-online.target")
+
+      start_all()
+
+      client1.wait_for_unit("data.mount")
+      client1.succeed("echo bla > /data/foo")
+      server.succeed("test -e /data/foo")
+
+      client2.wait_for_unit("data.mount")
+      client2.succeed("echo bla > /data/bar")
+      server.succeed("test -e /data/bar")
+
+      with subtest("restarting 'nfs-server' works correctly"):
+          server.succeed("systemctl restart nfs-server")
+          # will take 90 seconds due to the NFS grace period
+          client2.succeed("echo bla >> /data/bar")
+
+      with subtest("can get a lock"):
+          client2.succeed("time flock -n -s /data/lock true")
+
+      with subtest("client 2 fails to acquire lock held by client 1"):
+          client1.succeed("flock -x /data/lock -c 'touch locked; sleep 100000' &")
+          client1.wait_for_file("locked")
+          client2.fail("flock -n -s /data/lock true")
+
+      with subtest("client 2 obtains lock after resetting client 1"):
+          client2.succeed(
+              "flock -x /data/lock -c 'echo acquired; touch locked; sleep 100000' >&2 &"
+          )
+          client1.crash()
+          client1.start()
+          client2.wait_for_file("locked")
+
+      with subtest("locks survive server reboot"):
+          client1.wait_for_unit("data.mount")
+          server.shutdown()
+          server.start()
+          client1.succeed("touch /data/xyzzy")
+          client1.fail("time flock -n -s /data/lock true")
+
+      with subtest("unmounting during shutdown happens quickly"):
+          t1 = time.monotonic()
+          client1.shutdown()
+          duration = time.monotonic() - t1
+          assert duration < 30, f"shutdown took too long ({duration} seconds)"
+    '';
+})