mirror of
https://github.com/NixOS/nixpkgs.git
synced 2024-11-16 23:03:40 +01:00
buildFHSEnv: refactor and simplify, drop buildFHSChrootEnv
This takes another approach at binding FHS directory structure. We now bind-mount all the root filesystem to directory "/host" in the target tree. From that we symlink all the directories into the tree if they do not already exist in FHS structure. This probably makes `CHROOTENV_EXTRA_BINDS` unnecessary -- its main usecase was to add bound directories from the host to the sandbox, and we not just symlink all of them. I plan to get some feedback on its usage and maybe deprecate it. This also drops old `buildFHSChrootEnv` infrastructure. The main problem with it is it's very difficult to unmount a recursive-bound directory when mount is not sandboxed. This problem is a bug even without these changes -- if you have for example `/home/alice` mounted to somewhere, you wouldn't see it in `buildFHSChrootEnv` now. With the new directory structure, it's impossible to use regular bind at all. After some tackling with this I realized that the fix would be brittle and dangerous (if you don't unmount everything clearly and proceed to removing the temporary directory, bye-bye fs!). It also probably doesn't worth it because I haven't heard that someone actually uses it for a long time, and `buildFHSUserEnv` should cover most cases while being much more maintainable and safe for the end-user.
This commit is contained in:
parent
38ba568634
commit
74107a7867
10 changed files with 58 additions and 253 deletions
|
@ -1,48 +0,0 @@
|
|||
{ stdenv } : { env, extraInstallCommands ? "" } :
|
||||
|
||||
let
|
||||
# References to shell scripts that set up or tear down the environment
|
||||
initSh = ./init.sh.in;
|
||||
mountSh = ./mount.sh.in;
|
||||
loadSh = ./load.sh.in;
|
||||
umountSh = ./umount.sh.in;
|
||||
destroySh = ./destroy.sh.in;
|
||||
|
||||
name = env.pname;
|
||||
|
||||
in stdenv.mkDerivation {
|
||||
name = "${name}-chrootenv";
|
||||
preferLocalBuild = true;
|
||||
buildCommand = ''
|
||||
mkdir -p $out/bin
|
||||
cd $out/bin
|
||||
|
||||
sed -e "s|@chrootEnv@|${env}|g" \
|
||||
-e "s|@name@|${name}|g" \
|
||||
-e "s|@shell@|${stdenv.shell}|g" \
|
||||
${initSh} > init-${name}-chrootenv
|
||||
chmod +x init-${name}-chrootenv
|
||||
|
||||
sed -e "s|@shell@|${stdenv.shell}|g" \
|
||||
-e "s|@name@|${name}|g" \
|
||||
${mountSh} > mount-${name}-chrootenv
|
||||
chmod +x mount-${name}-chrootenv
|
||||
|
||||
sed -e "s|@shell@|${stdenv.shell}|g" \
|
||||
-e "s|@name@|${name}|g" \
|
||||
${loadSh} > load-${name}-chrootenv
|
||||
chmod +x load-${name}-chrootenv
|
||||
|
||||
sed -e "s|@shell@|${stdenv.shell}|g" \
|
||||
-e "s|@name@|${name}|g" \
|
||||
${umountSh} > umount-${name}-chrootenv
|
||||
chmod +x umount-${name}-chrootenv
|
||||
|
||||
sed -e "s|@chrootEnv@|${env}|g" \
|
||||
-e "s|@shell@|${stdenv.shell}|g" \
|
||||
-e "s|@name@|${name}|g" \
|
||||
${destroySh} > destroy-${name}-chrootenv
|
||||
chmod +x destroy-${name}-chrootenv
|
||||
${extraInstallCommands}
|
||||
'';
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
#! @shell@ -e
|
||||
|
||||
chrootenvDest=/run/chrootenv/@name@
|
||||
|
||||
# Remove bind mount points
|
||||
rmdir $chrootenvDest/{dev,nix/store,nix,proc,sys,host-etc,home,var,run,tmp}
|
||||
|
||||
# Remove symlinks to the software that should be part of the chroot system profile
|
||||
for i in @chrootEnv@/*
|
||||
do
|
||||
if [ "$i" != "@chrootEnv@/etc" ] && [ "$i" != "@chrootEnv@/var" ]
|
||||
then
|
||||
rm $chrootenvDest/$(basename $i)
|
||||
fi
|
||||
done
|
||||
|
||||
# Remove the remaining folders
|
||||
rm -Rf $chrootenvDest/{etc,root}
|
||||
rm -Rf /tmp/chrootenv-@name@
|
||||
|
||||
# Remove the chroot environment folder
|
||||
rmdir $chrootenvDest
|
|
@ -1,22 +0,0 @@
|
|||
#! @shell@ -e
|
||||
|
||||
chrootenvDest=/run/chrootenv/@name@
|
||||
|
||||
# Create some mount points for stuff that must be bind mounted
|
||||
mkdir -p $chrootenvDest/{nix/store,dev,proc,sys,host-etc,host-tmp,home,var,run}
|
||||
|
||||
# Symlink the software that should be part of the chroot system profile
|
||||
for i in @chrootEnv@/*
|
||||
do
|
||||
if [ "$i" != "@chrootEnv@/var" ]
|
||||
then
|
||||
ln -s "$i" "$chrootenvDest"
|
||||
fi
|
||||
done
|
||||
|
||||
# Create root folder
|
||||
mkdir $chrootenvDest/root
|
||||
|
||||
# Create tmp folder
|
||||
mkdir -m1777 $chrootenvDest/tmp
|
||||
mkdir -m1777 -p /tmp/chrootenv-@name@
|
|
@ -1,13 +0,0 @@
|
|||
#! @shell@ -e
|
||||
|
||||
chrootenvDest=/run/chrootenv/@name@
|
||||
|
||||
# Enter the LFS chroot environment
|
||||
sudo chroot --userspec "$USER:${GROUPS[0]}" --groups "${GROUPS[0]}" $chrootenvDest /usr/bin/env -i \
|
||||
TERM="$TERM" \
|
||||
DISPLAY="$DISPLAY" \
|
||||
HOME="$HOME" \
|
||||
XDG_RUNTIME_DIR="$XDG_RUNTIME_DIR" \
|
||||
LANG="$LANG" \
|
||||
SSL_CERT_FILE="$SSL_CERT_FILE" \
|
||||
/bin/bash --login
|
|
@ -1,34 +0,0 @@
|
|||
#! @shell@ -e
|
||||
|
||||
chrootenvDest=/run/chrootenv/@name@
|
||||
|
||||
# Bind mount the Nix store
|
||||
mount --bind /nix/store $chrootenvDest/nix/store
|
||||
|
||||
# Bind mount some kernel related stuff
|
||||
mount --bind /dev $chrootenvDest/dev
|
||||
mount --bind /dev/pts $chrootenvDest/dev/pts
|
||||
mount --bind /dev/shm $chrootenvDest/dev/shm
|
||||
mount --bind /proc $chrootenvDest/proc
|
||||
mount --bind /sys $chrootenvDest/sys
|
||||
|
||||
# Bind mount home directories
|
||||
mount --bind /home $chrootenvDest/home
|
||||
|
||||
# Bind mount state directories
|
||||
mount --bind /var $chrootenvDest/var
|
||||
mount --rbind /run $chrootenvDest/run
|
||||
|
||||
# Bind mount the host system's /etc
|
||||
mount --bind /etc $chrootenvDest/host-etc
|
||||
|
||||
# Bind mount the host system's /tmp
|
||||
mount --bind /tmp $chrootenvDest/host-tmp
|
||||
|
||||
# Bind mount /tmp
|
||||
mount --bind /tmp/chrootenv-@name@ $chrootenvDest/tmp
|
||||
|
||||
# Expose sockets in /tmp
|
||||
for i in /tmp/.*-unix; do
|
||||
ln -s "/host-tmp/$(basename "$i")" "$chrootenvDest/$i"
|
||||
done
|
|
@ -1,6 +0,0 @@
|
|||
#! @shell@ -e
|
||||
|
||||
chrootenvDest=/run/chrootenv/@name@
|
||||
|
||||
# Unmount all (r)bind mounts
|
||||
umount -l $chrootenvDest/{dev/pts,dev/shm,dev,nix/store,proc,sys,host-etc,host-tmp,home,var,tmp,run}
|
|
@ -2,16 +2,15 @@
|
|||
|
||||
# Bind mounts hierarchy: from => to (relative)
|
||||
# If 'to' is nil, path will be the same
|
||||
mounts = { '/nix/store' => nil,
|
||||
'/dev' => nil,
|
||||
mounts = { '/' => 'host',
|
||||
'/proc' => nil,
|
||||
'/sys' => nil,
|
||||
'/etc' => 'host-etc',
|
||||
'/tmp' => 'host-tmp',
|
||||
'/home' => nil,
|
||||
'/nix' => nil,
|
||||
'/tmp' => nil,
|
||||
'/var' => nil,
|
||||
'/run' => nil,
|
||||
'/root' => nil,
|
||||
'/dev' => nil,
|
||||
'/home' => nil,
|
||||
}
|
||||
|
||||
# Propagate environment variables
|
||||
|
@ -62,9 +61,8 @@ $mount = make_fcall 'mount', [Fiddle::TYPE_VOIDP,
|
|||
Fiddle::TYPE_INT
|
||||
|
||||
# Read command line args
|
||||
abort "Usage: chrootenv swdir program args..." unless ARGV.length >= 2
|
||||
swdir = Pathname.new ARGV[0]
|
||||
execp = ARGV.drop 1
|
||||
abort "Usage: chrootenv program args..." unless ARGV.length >= 1
|
||||
execp = ARGV
|
||||
|
||||
# Populate extra mounts
|
||||
if not ENV["CHROOTENV_EXTRA_BINDS"].nil?
|
||||
|
@ -132,24 +130,6 @@ if $cpid == 0
|
|||
Dir.chroot root
|
||||
Dir.chdir '/'
|
||||
|
||||
# Symlink swdir hierarchy
|
||||
mount_dirs = Set.new mounts.map { |_, v| Pathname.new v }
|
||||
link_swdir = lambda do |swdir, prefix|
|
||||
swdir.find do |path|
|
||||
rel = prefix.join path.relative_path_from(swdir)
|
||||
# Don't symlink anything in binded or symlinked directories
|
||||
Find.prune if mount_dirs.include? rel or rel.symlink?
|
||||
if not rel.directory?
|
||||
# File does not exist; make a symlink and bail out
|
||||
rel.make_symlink path
|
||||
Find.prune
|
||||
end
|
||||
# Recursively follow symlinks
|
||||
link_swdir.call path.readlink, rel if path.symlink?
|
||||
end
|
||||
end
|
||||
link_swdir.call swdir, Pathname.new('')
|
||||
|
||||
# New environment
|
||||
new_env = Hash[ envvars.map { |x| [x, ENV[x]] } ]
|
||||
|
||||
|
|
|
@ -1,28 +1,29 @@
|
|||
{ runCommand, lib, writeText, writeScriptBin, stdenv, ruby } :
|
||||
{ env, runScript ? "bash", extraBindMounts ? [], extraInstallCommands ? "", meta ? {}, passthru ? {} } :
|
||||
{ callPackage, runCommand, lib, writeScript, stdenv, coreutils, ruby }:
|
||||
|
||||
let buildFHSEnv = callPackage ./env.nix { }; in
|
||||
|
||||
args@{ name, runScript ? "bash", extraBindMounts ? [], extraInstallCommands ? "", meta ? {}, passthru ? {}, ... }:
|
||||
|
||||
let
|
||||
name = env.pname;
|
||||
env = buildFHSEnv (removeAttrs args [ "runScript" "extraBindMounts" "extraInstallCommands" "meta" "passthru" ]);
|
||||
|
||||
# Sandboxing script
|
||||
chroot-user = writeScriptBin "chroot-user" ''
|
||||
chroot-user = writeScript "chroot-user" ''
|
||||
#! ${ruby}/bin/ruby
|
||||
${builtins.readFile ./chroot-user.rb}
|
||||
'';
|
||||
|
||||
init = run: writeText "${name}-init" ''
|
||||
source /etc/profile
|
||||
|
||||
# Make /tmp directory
|
||||
mkdir -m 1777 /tmp
|
||||
|
||||
# Expose sockets in /tmp
|
||||
for i in /host-tmp/.*-unix; do
|
||||
ln -s "$i" "/tmp/$(basename "$i")"
|
||||
init = run: writeScript "${name}-init" ''
|
||||
#! ${stdenv.shell}
|
||||
for i in ${env}/* /host/*; do
|
||||
path="/''${i##*/}"
|
||||
[ -e "$path" ] || ${coreutils}/bin/ln -s "$i" "$path"
|
||||
done
|
||||
|
||||
[ -d "$1" ] && [ -r "$1" ] && cd "$1"
|
||||
shift
|
||||
|
||||
source /etc/profile
|
||||
exec ${run} "$@"
|
||||
'';
|
||||
|
||||
|
@ -32,7 +33,7 @@ in runCommand name {
|
|||
env = runCommand "${name}-shell-env" {
|
||||
shellHook = ''
|
||||
export CHROOTENV_EXTRA_BINDS="${lib.concatStringsSep ":" extraBindMounts}:$CHROOTENV_EXTRA_BINDS"
|
||||
exec ${chroot-user}/bin/chroot-user ${env} bash ${init "bash"} "$(pwd)"
|
||||
exec ${chroot-user} ${init "bash"} "$(pwd)"
|
||||
'';
|
||||
} ''
|
||||
echo >&2 ""
|
||||
|
@ -46,7 +47,7 @@ in runCommand name {
|
|||
cat <<EOF >$out/bin/${name}
|
||||
#! ${stdenv.shell}
|
||||
export CHROOTENV_EXTRA_BINDS="${lib.concatStringsSep ":" extraBindMounts}:\$CHROOTENV_EXTRA_BINDS"
|
||||
exec ${chroot-user}/bin/chroot-user ${env} bash ${init runScript} "\$(pwd)" "\$@"
|
||||
exec ${chroot-user} ${init runScript} "\$(pwd)" "\$@"
|
||||
EOF
|
||||
chmod +x $out/bin/${name}
|
||||
${extraInstallCommands}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{ nixpkgs, nixpkgs_i686, system
|
||||
} :
|
||||
{ stdenv, buildEnv, writeText, pkgs, pkgsi686Linux, system }:
|
||||
|
||||
{ name, profile ? ""
|
||||
, pkgs ? null, targetPkgs ? pkgs: [], multiPkgs ? pkgs: []
|
||||
, targetPkgs ? pkgs: [], multiPkgs ? pkgs: []
|
||||
, extraBuildCommands ? "", extraBuildCommandsMulti ? ""
|
||||
, extraOutputsToInstall ? []
|
||||
}:
|
||||
|
@ -22,37 +22,32 @@
|
|||
# /lib will link to /lib32
|
||||
|
||||
let
|
||||
isMultiBuild = pkgs == null && multiPkgs != null && system == "x86_64-linux";
|
||||
is64Bit = system == "x86_64-linux";
|
||||
isMultiBuild = multiPkgs != null && is64Bit;
|
||||
isTargetBuild = !isMultiBuild;
|
||||
|
||||
# support deprecated "pkgs" option.
|
||||
targetPkgs' =
|
||||
if pkgs != null
|
||||
then builtins.trace "buildFHSEnv: 'pkgs' option is deprecated, use 'targetPkgs'" (pkgs': pkgs)
|
||||
else targetPkgs;
|
||||
|
||||
# list of packages (usually programs) which are only be installed for the
|
||||
# host's architecture
|
||||
targetPaths = targetPkgs' nixpkgs ++ (if multiPkgs == null then [] else multiPkgs nixpkgs);
|
||||
targetPaths = targetPkgs pkgs ++ (if multiPkgs == null then [] else multiPkgs pkgs);
|
||||
|
||||
# list of packages which are installed for both x86 and x86_64 on x86_64
|
||||
# systems
|
||||
multiPaths = multiPkgs nixpkgs_i686;
|
||||
multiPaths = multiPkgs pkgsi686Linux;
|
||||
|
||||
# base packages of the chroot
|
||||
# these match the host's architecture, glibc_multi is used for multilib
|
||||
# builds.
|
||||
basePkgs = with nixpkgs;
|
||||
basePkgs = with pkgs;
|
||||
[ (if isMultiBuild then glibc_multi else glibc)
|
||||
gcc.cc.lib bashInteractive coreutils less shadow su
|
||||
gawk diffutils findutils gnused gnugrep
|
||||
gnutar gzip bzip2 xz glibcLocales
|
||||
];
|
||||
baseMultiPkgs = with nixpkgs_i686;
|
||||
baseMultiPkgs = with pkgsi686Linux;
|
||||
[ gcc.cc.lib
|
||||
];
|
||||
|
||||
etcProfile = nixpkgs.writeText "profile" ''
|
||||
etcProfile = writeText "profile" ''
|
||||
export PS1='${name}-chrootenv:\u@\h:\w\$ '
|
||||
export LOCALE_ARCHIVE='/usr/lib/locale/locale-archive'
|
||||
export LD_LIBRARY_PATH='/run/opengl-driver/lib:/run/opengl-driver-32/lib:/usr/lib:/usr/lib32'
|
||||
|
@ -67,7 +62,7 @@ let
|
|||
'';
|
||||
|
||||
# Compose /etc for the chroot environment
|
||||
etcPkg = nixpkgs.stdenv.mkDerivation {
|
||||
etcPkg = stdenv.mkDerivation {
|
||||
name = "${name}-chrootenv-etc";
|
||||
buildCommand = ''
|
||||
mkdir -p $out/etc
|
||||
|
@ -77,38 +72,38 @@ let
|
|||
ln -s ${etcProfile} profile
|
||||
|
||||
# compatibility with NixOS
|
||||
ln -s /host-etc/static static
|
||||
ln -s /host/etc/static static
|
||||
|
||||
# symlink some NSS stuff
|
||||
ln -s /host-etc/passwd passwd
|
||||
ln -s /host-etc/group group
|
||||
ln -s /host-etc/shadow shadow
|
||||
ln -s /host-etc/hosts hosts
|
||||
ln -s /host-etc/resolv.conf resolv.conf
|
||||
ln -s /host-etc/nsswitch.conf nsswitch.conf
|
||||
ln -s /host/etc/passwd passwd
|
||||
ln -s /host/etc/group group
|
||||
ln -s /host/etc/shadow shadow
|
||||
ln -s /host/etc/hosts hosts
|
||||
ln -s /host/etc/resolv.conf resolv.conf
|
||||
ln -s /host/etc/nsswitch.conf nsswitch.conf
|
||||
|
||||
# symlink sudo and su stuff
|
||||
ln -s /host-etc/login.defs login.defs
|
||||
ln -s /host-etc/sudoers sudoers
|
||||
ln -s /host-etc/sudoers.d sudoers.d
|
||||
ln -s /host/etc/login.defs login.defs
|
||||
ln -s /host/etc/sudoers sudoers
|
||||
ln -s /host/etc/sudoers.d sudoers.d
|
||||
|
||||
# symlink other core stuff
|
||||
ln -s /host-etc/localtime localtime
|
||||
ln -s /host-etc/machine-id machine-id
|
||||
ln -s /host-etc/os-release os-release
|
||||
ln -s /host/etc/localtime localtime
|
||||
ln -s /host/etc/machine-id machine-id
|
||||
ln -s /host/etc/os-release os-release
|
||||
|
||||
# symlink PAM stuff
|
||||
ln -s /host-etc/pam.d pam.d
|
||||
ln -s /host/etc/pam.d pam.d
|
||||
|
||||
# symlink fonts stuff
|
||||
ln -s /host-etc/fonts fonts
|
||||
ln -s /host/etc/fonts fonts
|
||||
|
||||
# symlink ALSA stuff
|
||||
ln -s /host-etc/asound.conf asound.conf
|
||||
ln -s /host/etc/asound.conf asound.conf
|
||||
|
||||
# symlink SSL certs
|
||||
mkdir -p ssl
|
||||
ln -s /host-etc/ssl/certs ssl/certs
|
||||
ln -s /host/etc/ssl/certs ssl/certs
|
||||
|
||||
# symlink /etc/mtab -> /proc/mounts (compat for old userspace progs)
|
||||
ln -s /proc/mounts mtab
|
||||
|
@ -116,14 +111,14 @@ let
|
|||
};
|
||||
|
||||
# Composes a /usr-like directory structure
|
||||
staticUsrProfileTarget = nixpkgs.buildEnv {
|
||||
staticUsrProfileTarget = buildEnv {
|
||||
name = "${name}-usr-target";
|
||||
paths = [ etcPkg ] ++ basePkgs ++ targetPaths;
|
||||
extraOutputsToInstall = [ "lib" "out" ] ++ extraOutputsToInstall;
|
||||
ignoreCollisions = true;
|
||||
};
|
||||
|
||||
staticUsrProfileMulti = nixpkgs.buildEnv {
|
||||
staticUsrProfileMulti = buildEnv {
|
||||
name = "${name}-usr-multi";
|
||||
paths = baseMultiPkgs ++ multiPaths;
|
||||
extraOutputsToInstall = [ "lib" "out" ] ++ extraOutputsToInstall;
|
||||
|
@ -132,10 +127,9 @@ let
|
|||
|
||||
# setup library paths only for the targeted architecture
|
||||
setupLibDirs_target = ''
|
||||
mkdir -m0755 lib
|
||||
|
||||
# copy content of targetPaths
|
||||
cp -rsHf ${staticUsrProfileTarget}/lib/* lib/
|
||||
# link content of targetPaths
|
||||
cp -rsHf ${staticUsrProfileTarget}/lib lib
|
||||
ln -s lib lib${if is64Bit then "64" else "32"}
|
||||
'';
|
||||
|
||||
# setup /lib, /lib32 and /lib64
|
||||
|
@ -184,7 +178,7 @@ let
|
|||
done
|
||||
'';
|
||||
|
||||
in nixpkgs.stdenv.mkDerivation {
|
||||
in stdenv.mkDerivation {
|
||||
name = "${name}-fhs";
|
||||
buildCommand = ''
|
||||
mkdir -p $out
|
||||
|
@ -196,7 +190,4 @@ in nixpkgs.stdenv.mkDerivation {
|
|||
${if isMultiBuild then extraBuildCommandsMulti else ""}
|
||||
'';
|
||||
preferLocalBuild = true;
|
||||
passthru = {
|
||||
pname = name;
|
||||
};
|
||||
}
|
|
@ -118,29 +118,7 @@ in
|
|||
|
||||
buildEnv = callPackage ../build-support/buildenv { }; # not actually a package
|
||||
|
||||
buildFHSEnv = callPackage ../build-support/build-fhs-chrootenv/env.nix {
|
||||
nixpkgs = pkgs;
|
||||
nixpkgs_i686 = pkgsi686Linux;
|
||||
};
|
||||
|
||||
chrootFHSEnv = callPackage ../build-support/build-fhs-chrootenv { };
|
||||
userFHSEnv = callPackage ../build-support/build-fhs-userenv {
|
||||
ruby = ruby_2_1;
|
||||
};
|
||||
|
||||
buildFHSChrootEnv = args: chrootFHSEnv {
|
||||
env = buildFHSEnv (removeAttrs args [ "extraInstallCommands" ]);
|
||||
extraInstallCommands = args.extraInstallCommands or "";
|
||||
};
|
||||
|
||||
buildFHSUserEnv = args: userFHSEnv {
|
||||
env = buildFHSEnv (removeAttrs args [ "runScript" "extraBindMounts" "extraInstallCommands" "meta" "passthru" ]);
|
||||
runScript = args.runScript or "bash";
|
||||
extraBindMounts = args.extraBindMounts or [];
|
||||
extraInstallCommands = args.extraInstallCommands or "";
|
||||
meta = args.meta or {};
|
||||
passthru = args.passthru or {};
|
||||
};
|
||||
buildFHSUserEnv = callPackage ../build-support/build-fhs-userenv { };
|
||||
|
||||
buildMaven = callPackage ../build-support/build-maven.nix {};
|
||||
|
||||
|
|
Loading…
Reference in a new issue