mirror of
https://github.com/nix-community/impermanence
synced 2024-11-10 05:44:17 +00:00
nixos: Rewrite directory creation for saner default permissions
Construct directory items for all parent directories of the user specified files and directories, assigning better default permissions and ownership to each and removing this responsibility from the create-directories script. This means that all parent directories of root directories will now have the default permissions and ownership, not inherit them from the child. User directories are assigned default user ownership. The home directory itself is handled specially to make sure it is owned by the user, not readable by anyone else and its parent gets default root ownership. To illustrate this with an example, here is a directory specification and the ownership and permissions that could potentially be assigned to the parent directories, given none of them yet exist in persistent storage: environment.persistence."/persistent" = { users.talyz = { directories = [ { directory = ".local/share/secret"; mode = "0500"; } ]; }; }; Before: /home talyz:talyz 0500 /home/talyz talyz:talyz 0500 /home/talyz/.local talyz:talyz 0500 /home/talyz/.local/share talyz:talyz 0500 /home/talyz/.local/share/secret talyz:talyz 0500 After: /home root:root 0755 /home/talyz talyz:talyz 0700 /home/talyz/.local talyz:talyz 0755 /home/talyz/.local/share talyz:talyz 0755 /home/talyz/.local/share/secret talyz:talyz 0500
This commit is contained in:
parent
d30c421e4e
commit
b4160ba71d
4 changed files with 134 additions and 44 deletions
|
@ -109,10 +109,10 @@
|
|||
- ~mode~, the permissions to set for the directory. If the
|
||||
directory doesn't already exist in persistent storage, it will
|
||||
be created with this mode. Can be either an octal mode
|
||||
(e.g. ~0700~) or a symbolic mode (e.g. ~u=rwx,g=,o=~). This also
|
||||
applies to any parent directories which don't yet exist.
|
||||
Changing this once the directory has been created has no
|
||||
effect.
|
||||
(e.g. ~0700~) or a symbolic mode (e.g. ~u=rwx,g=,o=~). Parent
|
||||
directories that don't yet exist are created with default
|
||||
permissions. Changing this once the directory has been created
|
||||
has no effect.
|
||||
|
||||
- ~files~ are all files you want to link or bind to persistent
|
||||
storage. A file can be represented either as a string, simply
|
||||
|
|
|
@ -47,44 +47,17 @@ fi
|
|||
sourceBase="${sourceBase%/}"
|
||||
target="${target%/}"
|
||||
|
||||
# check that the source exists and warn the user if it doesn't
|
||||
# check that the source exists and warn the user if it doesn't, then
|
||||
# create them with the specified permissions
|
||||
realSource="$(realpath -m "$sourceBase$target")"
|
||||
if [[ ! -d "$realSource" ]]; then
|
||||
printf "Warning: Source directory '%s' does not exist; it will be created for you with the following permissions: owner: '%s:%s', mode: '%s'.\n" "$realSource" "$user" "$group" "$mode"
|
||||
mkdir --mode="$mode" "$realSource"
|
||||
chown "$user:$group" "$realSource"
|
||||
fi
|
||||
|
||||
# iterate over each part of the target path, e.g. var, lib, iwd
|
||||
previousPath="/"
|
||||
[[ -d "$target" ]] || mkdir "$target"
|
||||
|
||||
OLD_IFS=$IFS
|
||||
IFS=/ # split the path on /
|
||||
for pathPart in $target; do
|
||||
IFS=$OLD_IFS
|
||||
|
||||
# skip empty parts caused by the prefix slash and multiple
|
||||
# consecutive slashes
|
||||
[[ "$pathPart" == "" ]] && continue
|
||||
|
||||
# construct the incremental path, e.g. /var, /var/lib, /var/lib/iwd
|
||||
currentTargetPath="$previousPath$pathPart/"
|
||||
|
||||
# construct the source path, e.g. /state/var, /state/var/lib, ...
|
||||
currentSourcePath="$sourceBase$currentTargetPath"
|
||||
|
||||
# create the source and target directories if they don't exist
|
||||
if [[ ! -d "$currentSourcePath" ]]; then
|
||||
mkdir --mode="$mode" "$currentSourcePath"
|
||||
chown "$user:$group" "$currentSourcePath"
|
||||
fi
|
||||
[[ -d "$currentTargetPath" ]] || mkdir "$currentTargetPath"
|
||||
|
||||
# resolve the source path to avoid symlinks
|
||||
currentRealSourcePath="$(realpath -m "$currentSourcePath")"
|
||||
|
||||
# synchronize perms between source and target
|
||||
chown --reference="$currentRealSourcePath" "$currentTargetPath"
|
||||
chmod --reference="$currentRealSourcePath" "$currentTargetPath"
|
||||
|
||||
# lastly we update the previousPath to continue down the tree
|
||||
previousPath="$currentTargetPath"
|
||||
done
|
||||
# synchronize perms between source and target
|
||||
chown --reference="$realSource" "$target"
|
||||
chmod --reference="$realSource" "$target"
|
||||
|
|
22
lib.nix
22
lib.nix
|
@ -11,6 +11,9 @@ let
|
|||
removePrefix
|
||||
foldl'
|
||||
elem
|
||||
take
|
||||
length
|
||||
last
|
||||
;
|
||||
inherit (lib.strings)
|
||||
sanitizeDerivationName
|
||||
|
@ -34,6 +37,24 @@ let
|
|||
in
|
||||
prefix + path;
|
||||
|
||||
|
||||
parentsOf = path:
|
||||
let
|
||||
prefix = optionalString (hasPrefix "/" path) "/";
|
||||
split = splitPath [ path ];
|
||||
parents = take ((length split) - 1) split;
|
||||
in
|
||||
foldl'
|
||||
(state: item:
|
||||
state ++ [
|
||||
(concatPaths [
|
||||
(if state != [ ] then last state else prefix)
|
||||
item
|
||||
])
|
||||
])
|
||||
[ ]
|
||||
parents;
|
||||
|
||||
sanitizeName = name:
|
||||
replaceStrings
|
||||
[ "." ] [ "" ]
|
||||
|
@ -63,6 +84,7 @@ in
|
|||
splitPath
|
||||
dirListToPath
|
||||
concatPaths
|
||||
parentsOf
|
||||
sanitizeName
|
||||
duplicates
|
||||
;
|
||||
|
|
105
nixos.nix
105
nixos.nix
|
@ -26,6 +26,9 @@ let
|
|||
catAttrs
|
||||
optional
|
||||
literalExpression
|
||||
optionalString
|
||||
elem
|
||||
mapAttrs
|
||||
;
|
||||
|
||||
inherit (pkgs.callPackage ./lib.nix { })
|
||||
|
@ -34,6 +37,7 @@ let
|
|||
concatPaths
|
||||
sanitizeName
|
||||
duplicates
|
||||
parentsOf
|
||||
;
|
||||
|
||||
cfg = config.environment.persistence;
|
||||
|
@ -147,7 +151,10 @@ in
|
|||
The path to the file.
|
||||
'';
|
||||
};
|
||||
parentDirectory = dirPermsOpts;
|
||||
parentDirectory = dirPermsOpts // {
|
||||
# See comment in dirOpts below for an explanation.
|
||||
defaultPerms = mapAttrs (_: x: x // { internal = true; }) dirPermsOpts;
|
||||
};
|
||||
filePath = mkOption {
|
||||
type = path;
|
||||
internal = true;
|
||||
|
@ -162,6 +169,12 @@ in
|
|||
The path to the directory.
|
||||
'';
|
||||
};
|
||||
# Save the default permissions at the level the
|
||||
# directory resides. This used when creating its
|
||||
# parent directories, giving them reasonable
|
||||
# default permissions unaffected by the
|
||||
# directory's own.
|
||||
defaultPerms = mapAttrs (_: x: x // { internal = true; }) dirPermsOpts;
|
||||
dirPath = mkOption {
|
||||
type = path;
|
||||
internal = true;
|
||||
|
@ -172,7 +185,7 @@ in
|
|||
commonOpts
|
||||
fileOpts
|
||||
({ config, ... }: {
|
||||
parentDirectory = mkDefault defaultPerms;
|
||||
parentDirectory = mkDefault (defaultPerms // { inherit defaultPerms; });
|
||||
filePath = mkDefault config.file;
|
||||
})
|
||||
];
|
||||
|
@ -180,6 +193,7 @@ in
|
|||
commonOpts
|
||||
dirOpts
|
||||
({ config, ... }: {
|
||||
defaultPerms = mkDefault defaultPerms;
|
||||
dirPath = mkDefault config.directory;
|
||||
})
|
||||
] ++ (mapAttrsToList (n: v: { ${n} = mkDefault v; }) defaultPerms));
|
||||
|
@ -200,6 +214,7 @@ in
|
|||
fileConfig =
|
||||
{ config, ... }:
|
||||
{
|
||||
parentDirectory = mkDefault (userDefaultPerms // { defaultPerms = userDefaultPerms; });
|
||||
filePath =
|
||||
if config.home != null then
|
||||
concatPaths [ config.home config.file ]
|
||||
|
@ -209,13 +224,13 @@ in
|
|||
userFile = submodule [
|
||||
commonOpts
|
||||
fileOpts
|
||||
{ parentDirectory = mkDefault userDefaultPerms; }
|
||||
{ inherit (config) home; }
|
||||
fileConfig
|
||||
];
|
||||
dirConfig =
|
||||
{ config, ... }:
|
||||
{
|
||||
defaultPerms = mkDefault userDefaultPerms;
|
||||
dirPath =
|
||||
if config.home != null then
|
||||
concatPaths [ config.home config.directory ]
|
||||
|
@ -480,7 +495,8 @@ in
|
|||
# storage directories we want to bind mount.
|
||||
dirCreationScript =
|
||||
let
|
||||
fileDirectories = unique (map
|
||||
# The parent directories of files.
|
||||
fileDirs = unique (map
|
||||
(f:
|
||||
rec {
|
||||
directory = dirOf f.file;
|
||||
|
@ -492,11 +508,90 @@ in
|
|||
inherit (f) persistentStoragePath home;
|
||||
} // f.parentDirectory)
|
||||
files);
|
||||
|
||||
# All the directories actually listed by the user and the
|
||||
# parent directories of listed files.
|
||||
explicitDirs = directories ++ fileDirs;
|
||||
|
||||
# Home directories have to be handled specially, since
|
||||
# they're at the permissions boundary where they
|
||||
# themselves should be owned by the user and have stricter
|
||||
# permissions than regular directories, whereas its parent
|
||||
# should be owned by root and have regular permissions.
|
||||
#
|
||||
# This simply collects all the home directories and sets
|
||||
# the appropriate permissions and ownership.
|
||||
homeDirs =
|
||||
foldl'
|
||||
(state: dir:
|
||||
let
|
||||
defaultPerms = {
|
||||
mode = "0755";
|
||||
user = "root";
|
||||
group = "root";
|
||||
};
|
||||
homeDir = {
|
||||
directory = dir.home;
|
||||
dirPath = dir.home;
|
||||
home = null;
|
||||
mode = "0700";
|
||||
user = dir.user;
|
||||
group = users.${dir.user}.group;
|
||||
inherit defaultPerms;
|
||||
inherit (dir) persistentStoragePath;
|
||||
};
|
||||
in
|
||||
if dir.home != null then
|
||||
if !(elem homeDir state) then
|
||||
state ++ [ homeDir ]
|
||||
else
|
||||
state
|
||||
else
|
||||
state)
|
||||
[ ]
|
||||
explicitDirs;
|
||||
|
||||
# Generate entries for all parent directories of the
|
||||
# argument directories, listed in the order they need to
|
||||
# be created. The parent directories are assigned default
|
||||
# permissions.
|
||||
mkParentDirs = dirs:
|
||||
let
|
||||
# Create a new directory item from `dir`, the child
|
||||
# directory item to inherit properties from and
|
||||
# `path`, the parent directory path.
|
||||
mkParent = dir: path: {
|
||||
directory = path;
|
||||
dirPath =
|
||||
if dir.home != null then
|
||||
concatPaths [ dir.home path ]
|
||||
else
|
||||
path;
|
||||
inherit (dir) persistentStoragePath home;
|
||||
inherit (dir.defaultPerms) user group mode;
|
||||
};
|
||||
# Create new directory items for all parent
|
||||
# directories of a directory.
|
||||
mkParents = dir:
|
||||
map (mkParent dir) (parentsOf dir.directory);
|
||||
in
|
||||
unique (flatten (map mkParents dirs));
|
||||
|
||||
# Parent directories of home folders. This is usually only
|
||||
# /home, unless the user's home is in a non-standard
|
||||
# location.
|
||||
homeDirParents = mkParentDirs homeDirs;
|
||||
|
||||
# Parent directories of all explicitly listed directories.
|
||||
parentDirs = mkParentDirs explicitDirs;
|
||||
|
||||
# All directories in the order they should be created.
|
||||
allDirs = homeDirParents ++ homeDirs ++ parentDirs ++ explicitDirs;
|
||||
in
|
||||
pkgs.writeShellScript "impermanence-run-create-directories" ''
|
||||
_status=0
|
||||
trap "_status=1" ERR
|
||||
${concatMapStrings mkDirWithPerms (directories ++ fileDirectories)}
|
||||
${concatMapStrings mkDirWithPerms allDirs}
|
||||
exit $_status
|
||||
'';
|
||||
|
||||
|
|
Loading…
Reference in a new issue