122 lines
No EOL
3.8 KiB
Nix
122 lines
No EOL
3.8 KiB
Nix
{ config, lib, pkgs, ... }:
|
|
|
|
{
|
|
# todo: ideally we should support multiple encrypted directories
|
|
options.services.ecryptfs = with lib; {
|
|
enable = mkEnableOption "ecryptfs";
|
|
package = mkOption {
|
|
type = types.package;
|
|
default = pkgs.ecryptfs;
|
|
defaultText = "pkgs.ecryptfs";
|
|
description = "ecryptfs package";
|
|
};
|
|
serviceName = mkOption {
|
|
type = types.str;
|
|
default = "ecryptfs";
|
|
description = "ecryptfs service name";
|
|
};
|
|
|
|
cipherDir = mkOption {
|
|
type = types.str;
|
|
description = "path to the directory used as the underlying encrypted storage";
|
|
};
|
|
|
|
passphrasePath = mkOption {
|
|
type = types.str;
|
|
description = "path to the file containing the passphrase (this can be rotated down the line without needing to re-encrypt the data)";
|
|
};
|
|
|
|
masterKeyPath = mkOption {
|
|
type = types.str;
|
|
description = "path to the master key (i.e. the wrapped passphrase file)";
|
|
};
|
|
|
|
mountPoint = mkOption {
|
|
type = types.str;
|
|
description = "ecryptfs mount point";
|
|
};
|
|
|
|
encryptFilenames = mkOption {
|
|
type = types.bool;
|
|
default = true;
|
|
description = "whether to encrypt filenames";
|
|
};
|
|
|
|
encryptionKeySize = mkOption {
|
|
type = types.int;
|
|
default = 32;
|
|
description = "size of the encryption key in bytes";
|
|
};
|
|
|
|
extraOptions = mkOption {
|
|
type = types.listOf types.str;
|
|
default = [ ];
|
|
description = "additional options to pass to ecryptfs";
|
|
};
|
|
};
|
|
|
|
config = let
|
|
cfg = config.services.ecryptfs;
|
|
|
|
mountOptions =
|
|
[
|
|
"ecryptfs_sig=$sig"
|
|
"ecryptfs_key_bytes=${toString cfg.encryptionKeySize}"
|
|
"ecryptfs_cipher=aes"
|
|
"ecryptfs_unlink_sigs"
|
|
"no_sig_cache"
|
|
] ++
|
|
(lib.optional cfg.encryptFilenames "ecryptfs_fnek_sig=$sig") ++
|
|
cfg.extraOptions;
|
|
in {
|
|
systemd.services.${cfg.serviceName} = {
|
|
description = "${cfg.serviceName} setup";
|
|
wantedBy = [ "multi-user.target" ];
|
|
path = [ cfg.package pkgs.keyutils ];
|
|
serviceConfig = {
|
|
User = "root";
|
|
Group = "root";
|
|
Type = "oneshot";
|
|
RemainAfterExit = true;
|
|
ExecStop = "${pkgs.utillinux}/bin/umount ${lib.escapeShellArg cfg.mountPoint}";
|
|
};
|
|
script = ''
|
|
set -euo pipefail
|
|
if [ ! -f ${lib.escapeShellArg cfg.masterKeyPath} ]; then
|
|
echo "master key file ${cfg.masterKeyPath} does not exist, generating..."
|
|
passphrase=$(${pkgs.coreutils}/bin/head -c 48 /dev/random | base64)
|
|
wrapping_passphrase=$(cat ${lib.escapeShellArg cfg.passphrasePath})
|
|
printf "%s\n%s" "$passphrase" "$wrapping_passphrase" | ecryptfs-wrap-passphrase ${lib.escapeShellArg cfg.masterKeyPath} -
|
|
fi
|
|
|
|
if [ ! -d ${lib.escapeShellArg cfg.cipherDir} ]; then
|
|
echo "ecryptfs: directory ${lib.escapeShellArg cfg.cipherDir} does not exist"
|
|
exit 1
|
|
fi
|
|
|
|
if [ ! -d ${lib.escapeShellArg cfg.mountPoint} ]; then
|
|
mkdir -p ${lib.escapeShellArg cfg.mountPoint}
|
|
fi
|
|
|
|
result=$(cat ${lib.escapeShellArg cfg.passphrasePath} | ecryptfs-insert-wrapped-passphrase-into-keyring ${lib.escapeShellArg cfg.masterKeyPath} -)
|
|
sig=$(echo "$result" | sed -r 's/^.*sig \[(.*)\] into.*$/\1/')
|
|
|
|
if [ -z "$sig" ]; then
|
|
echo "failed to extract signature"
|
|
echo "$result"
|
|
exit 1
|
|
fi
|
|
|
|
echo "Keyring signature: $sig"
|
|
|
|
if ! keyctl show | grep -q "_uid.0"; then
|
|
# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=870126
|
|
# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=870335
|
|
keyctl link @u @s
|
|
fi
|
|
|
|
${pkgs.utillinux}/bin/mount -i -t ecryptfs ${lib.escapeShellArg cfg.cipherDir} ${lib.escapeShellArg cfg.mountPoint} -o "${lib.concatStringsSep "," mountOptions}"
|
|
'';
|
|
};
|
|
};
|
|
} |