modules/external-storage: support non-local backends, make encryption optional

This commit is contained in:
Max Headroom 2023-11-02 02:46:40 +01:00
parent 907bdf2c74
commit c4b7a72f99
2 changed files with 44 additions and 16 deletions

View file

@ -29,11 +29,11 @@ in
}; };
config = { config = {
boot.supportedFilesystems = [ "cifs" ]; boot.supportedFilesystems = lib.mkIf (cfg.underlays != {}) [ "cifs" ];
age.secrets = lib.mkMerge [ age.secrets = lib.mkMerge [
(create cfg.underlays (name: ul: lib.nameValuePair "cifsCredentials-${name}" { file = ul.credentialsFile; })) (create cfg.underlays (name: ul: lib.nameValuePair "cifsCredentials-${name}" { file = ul.credentialsFile; }))
(create cfg.fileSystems (name: fs: lib.nameValuePair "storageEncryptionKey-${name}" { file = fs.encryptionKeyFile; })) (create cfg.fileSystems (name: fs: lib.nameValuePair "storageAuth-${name}" { file = fs.authFile; }))
]; ];
fileSystems = create cfg.underlays (name: ul: { fileSystems = create cfg.underlays (name: ul: {
@ -71,7 +71,12 @@ in
services = create cfg.fileSystems (name: fs: { services = create cfg.fileSystems (name: fs: {
name = fs.unitName; name = fs.unitName;
value = let value = let
isUnderlay = fs.underlay != null;
underlayPath = cfg.underlays.${fs.underlay}.mountpoint; underlayPath = cfg.underlays.${fs.underlay}.mountpoint;
backendUrl = if isUnderlay then "local://${underlayPath}" else fs.backend;
fsType = if isUnderlay then "local" else lib.head (lib.strings.match "([a-z0-9]*)://.*" backendUrl);
in { in {
description = fs.unitDescription; description = fs.unitDescription;
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
@ -84,40 +89,52 @@ in
util-linux util-linux
]; ];
unitConfig.RequiresMountsFor = underlayPath; unitConfig.RequiresMountsFor = lib.mkIf isUnderlay underlayPath;
serviceConfig = let serviceConfig = let
commonOptions = [ commonOptions = [
"--cachedir" fs.cacheDir "--cachedir" fs.cacheDir
"--authfile" config.age.secrets."storageEncryptionKey-${name}".path "--authfile" config.age.secrets."storageAuth-${name}".path
]; ] ++ (lib.optionals (fs.backendOptions != []) [ "--backend-options" (lib.concatStringsSep "," fs.backendOptions) ]);
in { in {
Type = "notify"; Type = "notify";
ExecStartPre = map lib.escapeShellArgs [ ExecStartPre = map lib.escapeShellArgs [
[ [
(pkgs.writeShellScript "create-s3ql-filesystem" '' (let
if ! test -e ${underlayPath}/s3ql_passphrase; then mkfsEncrypted = ''
echo Creating new S3QL filesystem on ${underlayPath} ${pkgs.gnugrep}/bin/grep -m1 fs-passphrase: '${config.age.secrets."storageAuth-${name}".path}' \
${pkgs.gnugrep}/bin/grep -m1 fs-passphrase: '${config.age.secrets."storageEncryptionKey-${name}".path}' \
| cut -d' ' -f2- \ | cut -d' ' -f2- \
| ${s3ql}/bin/mkfs.s3ql ${lib.escapeShellArgs commonOptions} -L '${name}' 'local://${underlayPath}' | ${s3ql}/bin/mkfs.s3ql ${lib.escapeShellArgs commonOptions} -L '${name}' '${backendUrl}'
'';
mkfsPlain = ''
${s3ql}/bin/mkfs.s3ql ${lib.escapeShellArgs commonOptions} --plain -L '${name}' '${backendUrl}'
'';
detectFs = {
local = "test -e ${underlayPath}/s3ql_metadata";
}.${fsType} or null;
in pkgs.writeShellScript "create-s3ql-filesystem" (lib.optionalString (detectFs != null) ''
if ! ${detectFs}; then
echo Creating new S3QL filesystem on ${backendUrl}
${if fs.encrypt then mkfsEncrypted else mkfsPlain}
fi fi
'') ''))
] ]
[ [
"${pkgs.coreutils}/bin/install" "-dm755" fs.mountpoint "${pkgs.coreutils}/bin/install" "-dm755" fs.mountpoint
] ]
([ ([
"${s3ql}/bin/fsck.s3ql" "${s3ql}/bin/fsck.s3ql"
"local://${underlayPath}" backendUrl
"--compress" "none" "--compress" "none"
] ++ commonOptions) ] ++ commonOptions)
]; ];
ExecStart = lib.escapeShellArgs ([ ExecStart = lib.escapeShellArgs ([
"${s3ql}/bin/mount.s3ql" "${s3ql}/bin/mount.s3ql"
"local://${underlayPath}" backendUrl
fs.mountpoint fs.mountpoint
"--fs-name" "${fs.unitName}" "--fs-name" "${fs.unitName}"
"--allow-other" "--allow-other"

View file

@ -15,7 +15,11 @@ with lib;
type = types.str; type = types.str;
default = "Remote Storage | ${name}"; default = "Remote Storage | ${name}";
}; };
encryptionKeyFile = mkOption { encrypt = mkOption {
type = types.bool;
default = false;
};
authFile = mkOption {
type = types.path; type = types.path;
}; };
cacheDir = mkOption { cacheDir = mkOption {
@ -23,8 +27,15 @@ with lib;
default = "/var/cache/remote-storage/${name}"; default = "/var/cache/remote-storage/${name}";
}; };
underlay = mkOption { underlay = mkOption {
type = types.str; type = with types; nullOr str;
default = "default"; default = null;
};
backend = mkOption {
type = with types; nullOr str;
};
backendOptions = mkOption {
type = with types; listOf str;
default = [];
}; };
dependentServices = mkOption { dependentServices = mkOption {
type = with types; listOf str; type = with types; listOf str;