Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nixos/nfs: Allow Kerberized NFS #73989

Merged
merged 7 commits into from
Dec 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions nixos/modules/tasks/filesystems/nfs.nix
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ let
'';

nfsConfFile = pkgs.writeText "nfs.conf" cfg.extraConfig;
requestKeyConfFile = pkgs.writeText "request-key.conf" ''
create id_resolver * * ${pkgs.nfs-utils}/bin/nfsidmap -t 600 %k %d
'';

cfg = config.services.nfs;

Expand Down Expand Up @@ -57,9 +60,12 @@ in

systemd.packages = [ pkgs.nfs-utils ];

environment.systemPackages = [ pkgs.keyutils ];

environment.etc = {
"idmapd.conf".source = idmapdConfFile;
"nfs.conf".source = nfsConfFile;
"request-key.conf".source = requestKeyConfFile;
};

systemd.services.nfs-blkmap =
Expand Down
4 changes: 2 additions & 2 deletions nixos/release-combined.nix
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,8 @@ in rec {
(all nixos.tests.networking.scripted.macvlan)
(all nixos.tests.networking.scripted.sit)
(all nixos.tests.networking.scripted.vlan)
(all nixos.tests.nfs3)
(all nixos.tests.nfs4)
(all nixos.tests.nfs3.simple)
(all nixos.tests.nfs4.simple)
(all nixos.tests.openssh)
(all nixos.tests.php-pcre)
(all nixos.tests.predictable-interface-names.predictable)
Expand Down
5 changes: 3 additions & 2 deletions nixos/tests/all-tests.nix
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,9 @@ in
networkingProxy = handleTest ./networking-proxy.nix {};
nextcloud = handleTest ./nextcloud {};
nexus = handleTest ./nexus.nix {};
nfs3 = handleTest ./nfs.nix { version = 3; };
nfs4 = handleTest ./nfs.nix { version = 4; };
# TODO: Test nfsv3 + Kerberos
nfs3 = handleTest ./nfs { version = 3; };
nfs4 = handleTest ./nfs { version = 4; };
nghttpx = handleTest ./nghttpx.nix {};
nginx = handleTest ./nginx.nix {};
nginx-sso = handleTest ./nginx-sso.nix {};
Expand Down
90 changes: 0 additions & 90 deletions nixos/tests/nfs.nix

This file was deleted.

9 changes: 9 additions & 0 deletions nixos/tests/nfs/default.nix
Original file line number Diff line number Diff line change
@@ -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; };
}
133 changes: 133 additions & 0 deletions nixos/tests/nfs/kerberos.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
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;
};
};

in

{
name = "nfsv4-with-kerberos";

nodes = {
client = { lib, ... }:
{ inherit krb5 users;

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;

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the comments here that tell what happens on a higher level could all be subtests, so this information even appears in the log.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh sorry i already stated this in another comment in the same PR and didn't remember.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've changed most of the other ones. Here, this is really set-up code that is not related to the functionality under test. Is subtest intended for this use as well?

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}"
'';
})
94 changes: 94 additions & 0 deletions nixos/tests/nfs/simple.nix
Original file line number Diff line number Diff line change
@@ -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)"
'';
})
5 changes: 5 additions & 0 deletions pkgs/os-specific/linux/kernel/patches.nix
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
patch = ./bridge-stp-helper.patch;
};

request_key_helper =
{ name = "request-key-helper";
patch = ./request-key-helper.patch;
};

p9_fixes =
{ name = "p9-fixes";
patch = ./p9-fixes.patch;
Expand Down
Loading