diff --git a/.github/SUPPORT.md b/.github/SUPPORT.md index be284dfc61..6c04b4e18d 100644 --- a/.github/SUPPORT.md +++ b/.github/SUPPORT.md @@ -14,6 +14,27 @@ For issues with code (and especially if you need to share debug output) we recom - [Singularity Registry Issues](https://github.com/singularityhub/sregistry/issues): is the board for issues relevant to Singularity Registry - [Documentation Issues](https://github.com/singularityware/singularityware.github.io/issues): documentation questions, feedback, and suggestions should go here. Feel free to create an issue on a board and additionally request updated content here. +### How do I ask for help? +After you identify a bug, you should search the respective issue board for similar problems reported by other users. Another user may be facing the same issue, and you can add a +1 (in message or icon) to indicate to the maintainers that the issue is pressing for you as well. The squeaky wheel gets the grease! + +### How is time allocated to addressing issues? +While we wish we could address every issue, there are only so many hours in the day. We rank issues based on the following questions: + + 1. How many users are affected? + 2. Is there a proposed work-around? + 2. In how many instances does the proposed work-around fail? + +With these simple questions, we can ensure that work is directed and has the maximum impact! However, if your issue doesn't seem to be getting attention you can still move it along using some of the strategies discussed below. + +### What if my issue goes stale? +Issues can go stale for a number of reasons. In the bullets below, we will review some of these reasons, along with strategies for managing them: + + 1. *The issue needs a gentle reminder*. Try targeting a few people with a "`ping @username any thoughts about this?`" in the case that it was forgotten. + 2. *Was your issue properly explained*? You are much more likely to get help when you give clear instructions for reproducing the issue, and show effort on your part to think about what the problem might be. If possible, try to come up with a way to reproduce the issue that does not involve a special environment or exotic hardware. + 3. *Is there broad need*? It could be that your issue isn't having a big enough impact for other users to warrant the time for the small development team. In this case, you might try implementing a suggested fix, and then asking for help with the details. + 4. *Is your issue scattered?* When many issues pile up on boards, it sometimes is the case that issues are duplicated. It's important to find these duplicates and merge them into one, because in finding the duplicate you find another user to talk to about the issue. + 5. *Does your issue need to have scope?* The idea of scoping an issue means framing it with respect to other components of the software. For example, if you have a feature request to see metadata about an object, you might frame that in the context of container introspection, and suggest an addition to the software that fits with the "inspect" command. A very powerful thing to do would be to open up an issue that (not only discusses your specific addition) but also opens up discussion to the general community for "How we can do introspection" better. Then create a set of issues and add them to a [Github milestone](https://help.github.com/articles/about-milestones/). This kind of contribution is much more powerful than simply asking for something. + ## Google Group You can reach the community quickly by way of joining our [Google Group](https://groups.google.com/a/lbl.gov/forum/#!forum/singularity). diff --git a/.gitignore b/.gitignore index ab4fbd5756..18406d28c9 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,7 @@ missing singularity.spec src/util/config_defaults.h test.sh +secbuildimg.sh src/action src/action-suid @@ -51,9 +52,8 @@ src/verify src/stamp-h1 src/include/stamp-h1 src/.dirstamp -src/sinit -src/start -src/start-suid +src/wrapper +src/wrapper-suid src/.deps/ src/.libs/ diff --git a/.travis.yml b/.travis.yml index 6335bd6bdd..3ad1ff77c7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,7 @@ addons: - squashfs-tools - uuid-dev - libssl-dev + - libarchive-dev env: global: @@ -24,9 +25,6 @@ matrix: - os: linux env: OS_TYPE=centos OS_VERSION=7 -services: - - docker - before_install: - .travis/before_install diff --git a/.travis/before_install b/.travis/before_install index f4d80175dd..a0625cb590 100755 --- a/.travis/before_install +++ b/.travis/before_install @@ -13,12 +13,8 @@ if [ -z "$OS_TYPE" ]; then exit fi -# run docker as shown at -# https://djw8605.github.io/2016/05/03/building-centos-packages-on-travisci/ - sudo apt-get update -echo 'DOCKER_OPTS="-H tcp://127.0.0.1:2375 -H unix:///var/run/docker.sock -s devicemapper"' | \ - sudo tee /etc/default/docker > /dev/null -sudo service docker restart -sleep 5 -sudo docker pull ${OS_TYPE}:${OS_VERSION} + +./autogen.sh +./configure --prefix=/usr/local +sudo make install diff --git a/.travis/centos_run b/.travis/centos_run index b894dd266e..9487ee9eea 100755 --- a/.travis/centos_run +++ b/.travis/centos_run @@ -1,25 +1,21 @@ #!/bin/bash -ex -# This script starts docker and (on el7) systemd and runs a test +DIST="${OS_TYPE}${OS_VERSION}" -if [ "$OS_VERSION" = "6" ]; then - sudo docker run --privileged --rm=true -v `pwd`:/build:rw centos:${OS_VERSION} /bin/bash -exc "cd /build;.travis/rpmbuild_test $OS_VERSION" - exit -fi +mkdir /tmp/$DIST -# Mount /var/run/docker.sock and set --network=host so we can call docker from inside -# cause some tests need it. Cannot mount to /var/run/docker.sock inside cause CentOS -# /usr/sbin/init mounts another overlayfs on top of it -docker run --privileged -d -ti -e "container=docker" -v /var/run/docker.sock:/docker.sock --network=host \ - -v /sys/fs/cgroup:/sys/fs/cgroup -v `pwd`:/build:rw centos:${OS_VERSION} /usr/sbin/init -DOCKER_CONTAINER_ID=$(docker ps | grep centos | awk '{print $1}') -docker logs $DOCKER_CONTAINER_ID -docker exec -ti $DOCKER_CONTAINER_ID /bin/bash -exc " - export DOCKER_HOST=unix:///docker.sock - chmod o+rw /docker.sock && - cd /build && - .travis/rpmbuild_test $OS_VERSION -" -docker ps -a -docker stop $DOCKER_CONTAINER_ID -docker rm -v $DOCKER_CONTAINER_ID +cp -a . /tmp/$DIST/singularity +cat > "/tmp/$DIST/script" << SCRIPT +#!/bin/bash + +echo "%_var /var" > /root/.rpmmacros +echo "%_dbpath %{_var}/lib/rpm" >> /root/.rpmmacros + +.travis/rpmbuild_test $OS_VERSION +SCRIPT + +cd /tmp/$DIST/singularity + +# mounting /run for /dev/shm (python multiprocessing) +sudo singularity exec -w -B /run --keep-privs --allow-setuid docker://${OS_TYPE}:${OS_VERSION} /bin/bash -ex /tmp/$DIST/script +sudo rm -rf /tmp/$DIST diff --git a/.travis/install_test b/.travis/install_test index 1eb0de86be..ec2da10400 100755 --- a/.travis/install_test +++ b/.travis/install_test @@ -3,4 +3,5 @@ ./autogen.sh ./configure --prefix=/usr/local sudo make install +sudo make secbuildimg make test diff --git a/.travis/rpmbuild_test b/.travis/rpmbuild_test index 59626eb8d9..05c84cf843 100755 --- a/.travis/rpmbuild_test +++ b/.travis/rpmbuild_test @@ -1,12 +1,13 @@ #!/bin/bash -ex -# this script runs as root under docker +# this script runs as root under docker OS_VERSION="$1" # build and install -yum install -y libtool rpm-build make squashfs-tools openssl-devel libuuid-devel +yum install -y libtool rpm-build make yum-utils squashfs-tools ./autogen.sh +yum-builddep -y singularity.spec ./configure make dist rpmbuild -ta *.tar.gz @@ -30,4 +31,11 @@ sed -i 's,^localstatedir=.*,localstatedir=/var,' test.sh useradd testuser echo "Defaults:testuser env_keep=DOCKER_HOST" >>/etc/sudoers echo "testuser ALL=(ALL) NOPASSWD: ALL" >>/etc/sudoers + +# build image for isolated builds +sed -i 's,^prefix=.*,prefix=/usr,' secbuildimg.sh +sed -i 's,^sysconfdir=.*,sysconfdir=/etc,' secbuildimg.sh +sed -i 's,^localstatedir=.*,localstatedir=/var,' secbuildimg.sh +make secbuildimg + su testuser -c "make test" diff --git a/CHANGELOG.md b/CHANGELOG.md index 10c9cf1be5..7984a93c1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,37 @@ and changes prior to that are (unfortunately) done retrospectively. Critical ite - changed behaviour (recipe sections work differently) -## [v2.4.1](https://github.com/singularityware/singularity/tree/development) (development) +## [v3.0](https://github.com/singularityware/singularity/tree/development) + +### Implemented enhancements + - Feature sif sign #1143 + - Boot/start instance #1032 + - --nv option will use [nvidia-container-cli](https://github.com/NVIDIA/libnvidia-container) if installed + - [nvliblist.conf](https://github.com/singularityware/singularity/blob/master/etc/nvliblist.conf) now has a section for binaries + - --nv can be made default with all action commands in singularity.conf + - --nv can be controlled by env vars `$SINGULARITY_NV` and `$SINGULARITY_NV_OFF` + - Adjustments to SCIF (Scientific Filesystem) integration for broader use + +### Security related fixes + - Add capability support and secure build #934 + - Put /usr/local/{bin,sbin} in front of the default PATH + - Add capability to support all tar compression formats #1155 + - Handle docker layer aufs whiteout files correctly (requires libarchive). + +### Bug Fixes + - Put /usr/local/{bin,sbin} in front of the default PATH + - Fixed bug that did not export environment variables for apps with "-" in name + +## [v2.4.2](https://github.com/singularityware/singularity/tree/release-2.4) + + - This fixed an issue for support of older distributions and kernels with regards to `setns()` + functionality. + - Fixed autofs bug path (lost during merge) + +## [v2.4.1](https://github.com/singularityware/singularity/tree/release-2.4) (2017-11-22) + +### apprun script backslash removal fix + - Fixed the unwanted removal of backslashes in apprun scripts ### Security related fixes - Fixed container path and owner limitations (original merge was lost) @@ -25,7 +55,6 @@ and changes prior to that are (unfortunately) done retrospectively. Critical ite - Exposing labels for SCI-F in environment ### Bug Fixes - - Tar creation uses GNU tar-1.28 to not add a timestamp to the header for more reproducible builds - Adjusting environment parsing regular expression for Docker to allow for "=" sign in variable - Try overlayFS now default option - Confirm that localstate directories were properly packaged @@ -36,9 +65,13 @@ and changes prior to that are (unfortunately) done retrospectively. Critical ite - Fix for empty docker namespaces in private repositories - Fix Docker environment parsing - Revert lolcow easter egg - - - + - Fix "Duplicate bootstrap definition key" triggered by comments and blank spaces + - Fix for docker permission error when downloading multiple layers + - Fix parsing of registry (including port), namespace, tags, and version + - Add "$@" to any CMD/ENTRYPOINT found when building from Docker + - Added sqaushfs-tools as a dependency for building deb files + - Fix terminal echo problem when using PID namespace and killing shell + - Fix SuSE squashFS package name in RPM spec ## [v2.4](https://github.com/singularityware/singularity/tree/v2.4) (2017-10-02) [Full Changelog](https://github.com/singularityware/singularity/compare/2.3.2...2.4) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index b517b77d67..f57588c85a 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -7,6 +7,7 @@ - Cedric Clerget - Dave Dykstra - Dave Godlove + - Divya Cote - Krishna Muriki - Michael Bauer - Vanessa Sochat @@ -14,6 +15,7 @@ #Contributors: + - Afif Elghraoui - Amanda Duffy - Ángel Bejarano - Bernard Li @@ -25,6 +27,7 @@ - George Hartzell - Jarrod Johnson - Jason Stover + - Jeff Kriske - Maciej Sieczka - Mark Egan-Fuller - Nathan Lin @@ -36,4 +39,7 @@ - Yaroslav Halchenko - Josef Hrabal - Daniele Tamino - \ No newline at end of file + - Thomas Hamel + - David Trudgian + - Tarcisio Fedrizzi + diff --git a/ChangeLog b/ChangeLog deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/Makefile.am b/Makefile.am index fe63111152..9afec37690 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,7 +1,7 @@ SUBDIRS = bin etc libexec man src -MAINTAINERCLEANFILES = Makefile.in aclocal.m4 configure ltmain.sh depcomp install-sh missing config.* *.m4 singularity-*.tar.gz singularity-*.rpm m4/* test.sh -DISTCLEANFILES = Makefile test.sh +MAINTAINERCLEANFILES = Makefile.in aclocal.m4 configure ltmain.sh compile depcomp install-sh missing config.* *.m4 singularity-*.tar.gz singularity-*.rpm m4/* test.sh secbuildimg.sh +DISTCLEANFILES = Makefile test.sh secbuildimg.sh CLEANFILES = EXTRA_DIST = singularity.spec autogen.sh examples debian CONTRIBUTORS.md CONTRIBUTING.md COPYRIGHT.md INSTALL.md LICENSE-LBNL.md LICENSE.md README.md tests @@ -13,11 +13,21 @@ distclean-local: maintainer-clean-local test: test.sh sh ./test.sh +secbuildimg: secbuildimg.sh + sh ./secbuildimg.sh + install-perms: @echo @echo "install-perms is no longer required" @echo +all-local: + @echo + @echo "******" + @echo " mksquashfs from squash-tools is required for full functionality" + @echo "******" + @echo + install-data-hook: install -d -m 0755 $(DESTDIR)$(CONTAINER_MOUNTDIR) install -d -m 0755 $(DESTDIR)$(CONTAINER_FINALDIR) @@ -25,6 +35,10 @@ install-data-hook: install -d -m 0755 $(DESTDIR)$(SESSIONDIR) test -f $(DESTDIR)$(libexecdir)/singularity/sexec-suid && rm -f $(DESTDIR)$(libexecdir)/singularity/sexec-suid || : test -f $(DESTDIR)$(libexecdir)/singularity/bin/copy-suid && rm -f $(DESTDIR)$(libexecdir)/singularity/bin/copy-suid || : - + test -f $(DESTDIR)$(libexecdir)/singularity/bin/action-suid && rm -f $(DESTDIR)$(libexecdir)/singularity/bin/action-suid || : + test -f $(DESTDIR)$(libexecdir)/singularity/bin/start-suid && rm -f $(DESTDIR)$(libexecdir)/singularity/bin/start-suid || : + test -f $(DESTDIR)$(libexecdir)/singularity/bin/mount-suid && rm -f $(DESTDIR)$(libexecdir)/singularity/bin/mount-suid || : + test -f $(DESTDIR)$(libexecdir)/singularity/bin/import-suid && rm -f $(DESTDIR)$(libexecdir)/singularity/bin/import-suid || : + test -f $(DESTDIR)$(libexecdir)/singularity/bin/export-suid && rm -f $(DESTDIR)$(libexecdir)/singularity/bin/export-suid || : ACLOCAL_AMFLAGS = -I m4 diff --git a/bin/singularity.in b/bin/singularity.in index e0b8df1724..ce31ab2dab 100644 --- a/bin/singularity.in +++ b/bin/singularity.in @@ -83,6 +83,16 @@ while true; do message 4 "Enabling shell debugging\n" shift ;; + -c|--conf) + shift + if [ $(id -ru) != 0 ]; then + message ERROR "Only root can set a custom configuration file\n" + exit 1 + else + export SINGULARITY_CONFIG_FILE="${1:-}" + fi + shift + ;; -v|--verbose) SINGULARITY_MESSAGELEVEL=`expr $SINGULARITY_MESSAGELEVEL + 1` message 2 "Increasing verbosity level ($SINGULARITY_MESSAGELEVEL)\n" @@ -137,3 +147,4 @@ fi # We should never get here... exit 255 + diff --git a/configure.ac b/configure.ac index 6a6b8768b0..c0a50c65d0 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ(2.59) -AC_INIT([singularity],[2.4],[gmkurtzer@lbl.gov]) +AC_INIT([singularity],[2.4.1],[gmkurtzer@lbl.gov]) if test -z "$prefix" -o "$prefix" = "NONE" ; then prefix=${ac_default_prefix} @@ -27,7 +27,7 @@ AC_SUBST(SINGULARITY_ARCH) # https://www.mail-archive.com/debian-bugs-dist@lists.debian.org/msg1232579.html #AM_INIT_AUTOMAKE([foreign subdir-objects]) -AM_INIT_AUTOMAKE([foreign subdir-objects]) ## SEE ABOVE BEFORE CHANGING +AM_INIT_AUTOMAKE([foreign subdir-objects tar-pax]) ## SEE ABOVE BEFORE CHANGING AC_CONFIG_SRCDIR([src]) AC_CONFIG_HEADERS([src/config.h]) AC_CONFIG_MACRO_DIR([m4]) @@ -42,7 +42,7 @@ AC_PROG_LIBTOOL(libtool) # Setting rpath if necessary if test "$libdir" = "\${exec_prefix}/lib"; then - LDFLAGS="$LDFLAGS -Wl,-rpath -Wl,\$(libdir)" + LDFLAGS="$LDFLAGS -Wl,-rpath -Wl,\$(libdir) -Wl,-z,relro,-z,now" fi AC_MSG_CHECKING([for namespace: CLONE_NEWPID]) @@ -149,6 +149,19 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#define _GNU_SOURCE ] ) +AC_MSG_CHECKING([for namespace: CLONE_NEWUTS]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#define _GNU_SOURCE + #include + ]], + [[unshare(CLONE_NEWUTS);]])], + [ + AC_MSG_RESULT([yes]) + SINGULARITY_DEFINES="$SINGULARITY_DEFINES -DNS_CLONE_NEWUTS" + ], [ + AC_MSG_RESULT([no]) + ] + ) + AC_MSG_CHECKING([for feature: NO_NEW_PRIVS]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include @@ -223,6 +236,30 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ ] ) +AC_MSG_CHECKING([for user capabilities]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #include + ]], + [[#ifndef PR_CAP_AMBIENT + #error failed + #endif + ]])], + [ + AC_MSG_RESULT([yes]) + SINGULARITY_DEFINES="$SINGULARITY_DEFINES -DUSER_CAPABILITIES" + ], [ + AC_MSG_RESULT([no]) + ] + ) + +AC_CHECK_HEADER([linux/securebits.h], [ + SINGULARITY_DEFINES="$SINGULARITY_DEFINES -DSINGULARITY_SECUREBITS" + ]) + + +AC_CHECK_HEADER([linux/capability.h], [], [ + AC_MSG_ERROR([linux/capability.h header not found, requires kernel headers installation.]) + ]) AC_ARG_WITH([userns], @@ -237,23 +274,21 @@ AC_SUBST(USER_NS) AC_CHECK_FUNCS(setns, [ ], [ - NO_SETNS="-DNO_SETNS" + SINGULARITY_DEFINES="$SINGULARITY_DEFINES -DSINGULARITY_NO_SETNS" + AC_MSG_CHECKING([for setns kernel support]) + if test -d "/proc/self/ns"; then + SINGULARITY_DEFINES="$SINGULARITY_DEFINES -DSINGULARITY_SETNS_SYSCALL" + AC_MSG_RESULT([yes]) + else + WARN_SETNS=1 + AC_MSG_RESULT([no]) + fi ] ) -AC_SUBST(NO_SETNS) -AC_MSG_CHECKING([for setns syscall support]) -if test -d "/proc/self/ns"; then - SINGULARITY_DEFINES="$SINGULARITY_DEFINES -DSINGULARITY_SETNS_SYSCALL" - SETNS_SYSCALL=1 - AC_MSG_RESULT([yes]) -else - SETNS_SYSCALL=0 - AC_MSG_RESULT([no]) -fi -AC_SUBST(SETNS_SYSCALL) +AC_SUBST(SINGULARITY_DEFINES) # --------------------------------------------------------------------- # PYTHON @@ -280,8 +315,6 @@ else fi -AC_SUBST(SINGULARITY_DEFINES) - AC_MSG_CHECKING([--with-slurm]) AC_ARG_WITH([slurm], AS_HELP_STRING([--with-slurm], [This feature will no longer be part of Singularity proper]), @@ -306,7 +339,7 @@ AC_ARG_ENABLE([suid], AS_IF([test "x$enable_suid" != "xno"], [ AC_MSG_RESULT([yes]) - BUILD_SUID="action-suid start-suid mount-suid" + BUILD_SUID="wrapper-suid" ], [ AC_MSG_RESULT([no]) BUILD_SUID="" @@ -342,26 +375,36 @@ AC_SUBST(SESSIONDIR, "$localstatedir/singularity/mnt/session") AC_CHECK_HEADERS([uuid/uuid.h], [uuid_header_found=yes; break;]) AS_IF([test "x$uuid_header_found" != "xyes"], - [AC_MSG_ERROR([Unable to find the uuid headers, need package libuuid-devel])]) + [AC_MSG_ERROR([Unable to find the uuid headers, need package libuuid-devel (uuid-dev on Debian/Ubuntu)])]) AC_SEARCH_LIBS([uuid_generate], [uuid], [], [ - AC_MSG_ERROR([unable to find the uuid_generate(), need package libuuid-devel ]) + AC_MSG_ERROR([unable to find the uuid_generate(), need package libuuid-devel (uuid-dev on Debian/Ubuntu)]) ]) # check for openssl needed by signing AC_CHECK_HEADERS([openssl/sha.h], [ssl_header_found=yes; break;]) AS_IF([test "x$ssl_header_found" != "xyes"], - [AC_MSG_ERROR([Unable to find the ssl headers, need package openssl-devel])]) + [AC_MSG_ERROR([Unable to find the ssl headers, need package openssl-devel (libssl-dev on Debian/Ubuntu)])]) AC_SEARCH_LIBS([SHA1], [crypto], [], [ - AC_MSG_ERROR([unable to find the SHA1(), need package openssl-devel]) + AC_MSG_ERROR([unable to find the SHA1(), need package openssl-devel (libssl-dev on Debian/Ubuntu)]) ]) +# check for libarchive needed by docker-extract +AC_CHECK_HEADERS([archive.h], +[archive_header_found=yes; break;]) +AS_IF([test "x$archive_header_found" != "xyes"], +[AC_MSG_ERROR([Unable to find the libarchive headers, need package libarchive-devel (libarchive-dev on Debian/Ubuntu)])]) +AC_SEARCH_LIBS([archive_read_new], [archive], [], [ +AC_MSG_ERROR([unable to find libarchive, need package libarchive-devel (libarchive-de on Debian/Ubuntu)]) +]) +CFLAGS="$CFLAGS -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -fstack-protector --param ssp-buffer-size=4" AC_CONFIG_FILES([ Makefile singularity.spec test.sh + secbuildimg.sh src/Makefile src/lib/Makefile src/lib/runtime/Makefile @@ -370,11 +413,14 @@ AC_CONFIG_FILES([ src/lib/runtime/ns/mnt/Makefile src/lib/runtime/ns/net/Makefile src/lib/runtime/ns/pid/Makefile + src/lib/runtime/ns/uts/Makefile + src/lib/runtime/ns/user/Makefile src/lib/runtime/files/Makefile src/lib/runtime/files/group/Makefile src/lib/runtime/files/passwd/Makefile src/lib/runtime/files/resolvconf/Makefile src/lib/runtime/files/libs/Makefile + src/lib/runtime/files/hostname/Makefile src/lib/runtime/mounts/Makefile src/lib/runtime/mounts/cwd/Makefile src/lib/runtime/mounts/dev/Makefile @@ -423,7 +469,7 @@ AC_CONFIG_FILES([ AC_OUTPUT -if test "$SETNS_SYSCALL" = "0"; then +if test -n "$WARN_SETNS"; then echo echo "WARNING: instance feature is disabled due to lack of kernel support" echo diff --git a/debian/changelog b/debian/changelog index 8a0065cec4..51f660d295 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,11 @@ +singularity-container (2.4.1-1) unstable; urgency=high + + * This is a bug fix point release to the 2.4 feature branch, and includes a + number of fixes including some security related points that deserve + immediate attention + + -- Gregory M. Kurtzer Wed, 22 Nov 2017 12:20:27 -0700 + singularity-container (2.4-1) unstable; urgency=low * Building an image is now more intuitive with the introduction of our build diff --git a/debian/control b/debian/control index 9260d8ee1a..ac75547ff4 100644 --- a/debian/control +++ b/debian/control @@ -11,7 +11,10 @@ Build-Depends: debhelper (>= 9), dh-autoreconf, help2man, + libarchive-dev, + libssl-dev, python, + uuid-dev, Standards-Version: 3.9.8 Homepage: http://gmkurtzer.github.io/singularity Vcs-Git: https://github.com/singularityware/singularity.git diff --git a/etc/Makefile.am b/etc/Makefile.am index c5222877ad..ca8611f79b 100644 --- a/etc/Makefile.am +++ b/etc/Makefile.am @@ -1,5 +1,6 @@ confdir = $(sysconfdir)/singularity/ completiondir = $(sysconfdir)/bash_completion.d/ +capabilitiesdir = $(sysconfdir)/singularity/capabilities/ all: singularity.conf @@ -8,6 +9,7 @@ singularity.conf: singularity.conf.in ../src/util/config_defaults.h dist_conf_DATA = default-nsswitch.conf singularity.conf init nvliblist.conf dist_completion_DATA = bash_completion.d/singularity +dist_capabilities_DATA = capabilities/user.root CLEANFILES = singularity.conf MAINTAINERCLEANFILES = Makefile.in diff --git a/etc/bash_completion.d/singularity b/etc/bash_completion.d/singularity index 537b7b89fe..0c42e784bc 100644 --- a/etc/bash_completion.d/singularity +++ b/etc/bash_completion.d/singularity @@ -44,7 +44,9 @@ _singularity() { # TODO: This can be dynamically generated by the following: # find /opt/singularity/libexec/singularity/cli -maxdepth 1 -mindepth 1 -name '*.exec' -type f -exec basename {} \; | sed -e 's|.exec||' | sort -u # but we then need to auto-generate this file with autoconf. - local -r cmds="help selftest exec run shell test apps bootstrap build check inspect mount pull instance.start instance.list instance.stop image image.create image.import image.export image.expand sign verify" + local -r cmds="help selftest exec run shell test apps bootstrap build check inspect mount pull instance.start instance.list instance.stop image image.create image.import image.export image.expand sign verify capability.add capability.drop capability.list" + + local -r capabilities="cap_chown cap_dac_override cap_dac_read_search cap_fowner cap_fsetid cap_kill cap_setgid cap_setuid cap_setpcap cap_linux_immutable cap_net_bind_service cap_net_broadcast cap_net_admin cap_net_raw cap_ipc_lock cap_ipc_owner cap_sys_module cap_sys_rawio cap_sys_chroot cap_sys_ptrace cap_sys_pacct cap_sys_admin cap_sys_boot cap_sys_nice cap_sys_resource cap_sys_time cap_sys_tty_config cap_mknod cap_lease cap_audit_write cap_audit_control cap_setfcap cap_mac_override cap_mac_admin cap_syslog cap_wake_alarm cap_block_suspend cap_audit_read" # Find the first command (skipping any global options) cmd_idx=( $(_singularity_next_nonopt 1) ) @@ -292,6 +294,31 @@ _singularity() { return 0 ;; + capability.add|capability.drop) + if [[ ${cur} == -* ]] ; then + COMPREPLY=( $(compgen -W "--user --group --help" -- ${cur}) ) + elif [[ $container_idx -lt $COMP_CWORD ]]; then + realcur="${cur##*,}" + prefix="${cur%$realcur}" + compopt -o nospace + COMPREPLY=( $(compgen -P "${prefix}" -W "${capabilities}" -- ${realcur}) ) + else + _usergroup + fi + return 0 + ;; + + capability.list) + if [[ ${cur} == -* ]] ; then + COMPREPLY=( $(compgen -W "--user --group --defined --help" -- ${cur}) ) + elif [ $container_idx -lt $COMP_CWORD ]; then + return 0 + else + _usergroup + fi + return 0 + ;; + instance) # In this case, no sub-command is present. if [ $container_idx -eq $COMP_CWORD ]; then diff --git a/etc/capabilities/user.root b/etc/capabilities/user.root new file mode 100644 index 0000000000..573541ac97 --- /dev/null +++ b/etc/capabilities/user.root @@ -0,0 +1 @@ +0 diff --git a/etc/init b/etc/init index bc4c6f22c4..0d7b84483d 100644 --- a/etc/init +++ b/etc/init @@ -12,9 +12,9 @@ unset BASH_ENV # Provide a sane path within the container if [ -z ${SINGULARITYENV_PATH+x} ]; then - SINGULARITYENV_PATH="/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin" + SINGULARITYENV_PATH="/usr/local/bin:/usr/local/sbin:/bin:/sbin:/usr/bin:/usr/sbin" else - SINGULARITYENV_PATH="$SINGULARITYENV_PATH:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin" + SINGULARITYENV_PATH="$SINGULARITYENV_PATH:/usr/local/bin:/usr/local/sbin:/bin:/sbin:/usr/bin:/usr/sbin" fi # Don't save the shell's HISTFILE diff --git a/etc/nvliblist.conf b/etc/nvliblist.conf index a9fcef0916..907fbe0045 100644 --- a/etc/nvliblist.conf +++ b/etc/nvliblist.conf @@ -1,8 +1,19 @@ # NVLIBLIST.CONF # This configuration file determines which NVIDIA libraries to search for on # the host system when the --nv option is invoked. You can edit it if you have -# different libraries on your host system. +# different libraries on your host system. You can also add binaries and they +# will be mounted into the container when the --nv option is passed. +# put binaries here +# In shared environments you should ensure that permissions on these files +# exclude writing by non-privileged users. +/usr/bin/nvidia-smi +/usr/bin/nvidia-debugdump +/usr/bin/nvidia-persistenced +/usr/bin/nvidia-cuda-mps-control +/usr/bin/nvidia-cuda-mps-server + +# put libs here (must end in .so) libcuda.so libEGL_installertest.so libEGL_nvidia.so diff --git a/etc/singularity.conf.in b/etc/singularity.conf.in index 5769dccffb..1df9c8ccc3 100644 --- a/etc/singularity.conf.in +++ b/etc/singularity.conf.in @@ -161,6 +161,15 @@ bind path = /etc/hosts #@LIMIT_CONTAINER_OWNERS@ = gmk, singularity, nobody +# LIMIT CONTAINER GROUPS: [STRING] +# DEFAULT: @LIMIT_CONTAINER_GROUPS_DEFAULT@ +# Only allow containers to be used that are owned by a given group. If this +# configuration is undefined (commented or set to NULL), all containers are +# allowed to be used. This feature only applies when Singularity is running in +# SUID mode and the user is non-root. +#@LIMIT_CONTAINER_GROUPS@ = group1, singularity, nobody + + # LIMIT CONTAINER PATHS: [STRING] # DEFAULT: @LIMIT_CONTAINER_PATHS_DEFAULT@ # Only allow containers to be used that are located within an allowed path @@ -186,3 +195,39 @@ bind path = /etc/hosts # errors when accessed from container (typically bind mounts) #autofs bug path = /nfs #autofs bug path = /cifs-share + + +# ALWAYS USE NV ${TYPE}: [BOOL] +# DEFAULT: no +# This feature allows an administrator to determine that every action command +# should be executed implicitely with the --nv option (useful for GPU only +# environments). +@ALWAYS_USE_NV@ = @ALWAYS_USE_NV_DEFAULT@ + + +# ROOT DEFAULT CAPABILITIES: [full/file/default/no] +# DEFAULT: no +# Define default root capability set kept during runtime +# - full: keep all capabilities (same as --keep-privs) +# - file: keep capabilities configured in ${prefix}/etc/singularity/capabilities/user.root +# - default: keep capabilities required by singularity binary +# - no: no capabilities (same as --no-privs) +@ROOT_DEFAULT_CAPABILITIES@ = @ROOT_DEFAULT_CAPABILITIES_DEFAULT@ + + +# ALLOW_ROOT CAPABILITIES: [BOOL] +# DEFAULT: yes +# This allows root to gain/drop capabilities other than those defined +# by root default capabilities. +# Example: +# If root default capabilities = file and allow root capabilities = no, +# only capabilities defined in file ${prefix}/etc/singularity/capabilities/user.root +# could be obtained by root +@ALLOW_ROOT_CAPABILITIES@ = @ALLOW_ROOT_CAPABILITIES_DEFAULT@ + + +# ALLOW USER CAPABILITIES: [BOOL] +# DEFAULT: no +# This allows user to gain capabilities based on whitelist managed by administrator +# (requires recent kernel >= 4.3) +@ALLOW_USER_CAPABILITIES@ = @ALLOW_USER_CAPABILITIES_DEFAULT@ diff --git a/examples/apps/README.md b/examples/apps/README.md index a92c135c0a..cf65da30ce 100644 --- a/examples/apps/README.md +++ b/examples/apps/README.md @@ -1,15 +1,21 @@ # Singularity SCI-F Apps +The Scientific Filesystem is well suited for Singularity containers to allow you +to build a container that has multiple entrypoints, along with modular environments, +libraries, and executables. Here we will review the basic building and using of a +Singularity container that implements SCIF. For more quick start tutorials, see +the [official documentation for SCIF](https://vsoch.github.io/scif/). + Build your image ``` -sudo singularity build cowsay.img Singularity.cowsay +sudo singularity build cowsay.simg Singularity.cowsay ``` What apps are installed? ``` -singularity apps cowsay.img +singularity apps cowsay.simg cowsay fortune lolcat @@ -18,14 +24,17 @@ lolcat Ask for help for a specific app! ``` -singularity help --app fortune cowsay.img +singularity help --app fortune cowsay.simg fortune is the best app ``` Run a particular app ``` -singularity run --app fortune cowsay.img +singularity run --app fortune cowsay.simg +When I reflect upon the number of disagreeable people who I know who have gone +to a better world, I am moved to lead a different life. + -- Mark Twain, "Pudd'nhead Wilson's Calendar" ``` Inspect an app @@ -33,8 +42,7 @@ Inspect an app ``` singularity inspect --app fortune cowsay.img { - "SINGULARITY_APP_NAME": "fortune", - "SINGULARITY_APP_SIZE": "1MB" + "SCIF_APPNAME": "fortune", + "SCIF_APPSIZE": "1MB" } ``` - diff --git a/examples/debian/Singularity b/examples/debian/Singularity index e27a26c4b9..a0f9e709c9 100644 --- a/examples/debian/Singularity +++ b/examples/debian/Singularity @@ -8,4 +8,5 @@ MirrorURL: http://ftp.us.debian.org/debian/ %post echo "Hello from inside the container" apt-get update - apt-get -y --force-yes install vim + apt-get -y install vim + apt-get clean diff --git a/examples/legacy/2.2/contrib/debian85-tensorflow-0.10.def b/examples/legacy/2.2/contrib/debian85-tensorflow-0.10.def index c0936910c1..aeb73b5f18 100644 --- a/examples/legacy/2.2/contrib/debian85-tensorflow-0.10.def +++ b/examples/legacy/2.2/contrib/debian85-tensorflow-0.10.def @@ -21,6 +21,7 @@ MirrorURL: http://ftp.us.debian.org/debian/ %post apt-get update apt-get -y install vim python-pip python-dev + apt-get clean pip install --upgrade https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow-0.10.0-cp27-none-linux_x86_64.whl diff --git a/examples/legacy/2.2/contrib/ubuntu-bio.def b/examples/legacy/2.2/contrib/ubuntu-bio.def index 46ca99b392..84be8d90f7 100644 --- a/examples/legacy/2.2/contrib/ubuntu-bio.def +++ b/examples/legacy/2.2/contrib/ubuntu-bio.def @@ -19,6 +19,7 @@ Include: bash sed -i 's/main/main restricted universe/g' /etc/apt/sources.list apt-get update apt-get -y install wget bzip2 build-essential zlib1g-dev software-properties-common libncurses5-dev + apt-get clean # Install BWA @@ -44,6 +45,7 @@ Include: bash apt-get update echo 'oracle-java8-installer shared/accepted-oracle-license-v1-1 select true' | debconf-set-selections apt-get -y install oracle-java8-installer + apt-get clean # This is now ready to have GATK (Genomic Analysis Took Kit) installed diff --git a/examples/legacy/2.2/contrib/ubuntu-openfoam.def b/examples/legacy/2.2/contrib/ubuntu-openfoam.def index e95fcbf701..5393a4a2ce 100644 --- a/examples/legacy/2.2/contrib/ubuntu-openfoam.def +++ b/examples/legacy/2.2/contrib/ubuntu-openfoam.def @@ -18,5 +18,6 @@ Include: bash wget -O - http://dl.openfoam.org/gpg.key | apt-key add - apt-get update apt-get -y install openfoam4 + apt-get clean echo ". /opt/openfoam4/etc/bashrc" >> /environment diff --git a/examples/ubuntu/Singularity b/examples/ubuntu/Singularity index b90cf5e8f6..e46920a50c 100644 --- a/examples/ubuntu/Singularity +++ b/examples/ubuntu/Singularity @@ -10,5 +10,7 @@ MirrorURL: http://us.archive.ubuntu.com/ubuntu/ %post echo "Hello from inside the container" sed -i 's/$/ universe/' /etc/apt/sources.list - apt-get -y --force-yes install vim + apt-get update + apt-get -y install vim + apt-get clean diff --git a/libexec/Makefile.am b/libexec/Makefile.am index 6d07ce5713..c4eda0f86e 100644 --- a/libexec/Makefile.am +++ b/libexec/Makefile.am @@ -2,7 +2,7 @@ SUBDIRS = bootstrap-scripts cli helpers handlers python scriptlibexecdir = $(libexecdir)/singularity -dist_scriptlibexec_SCRIPTS = functions image-handler.sh +dist_scriptlibexec_SCRIPTS = functions image-handler.sh capabilities MAINTAINERCLEANFILES = Makefile.in diff --git a/libexec/bootstrap-scripts/Makefile.am b/libexec/bootstrap-scripts/Makefile.am index ed238424af..0861f87cb5 100644 --- a/libexec/bootstrap-scripts/Makefile.am +++ b/libexec/bootstrap-scripts/Makefile.am @@ -17,9 +17,11 @@ dist_mods_SCRIPTS = functions checks.sh \ environment.sh \ post.sh \ pre.sh \ - main-deffile.sh \ + main-deffile-stage1.sh \ + main-deffile-stage2.sh \ main-dockerhub.sh \ - main-null.sh + main-null.sh \ + secbuild.sh MAINTAINERCLEANFILES = Makefile.in diff --git a/libexec/bootstrap-scripts/deffile-driver-busybox.sh b/libexec/bootstrap-scripts/deffile-driver-busybox.sh index e5a4ba2f10..692af955a2 100644 --- a/libexec/bootstrap-scripts/deffile-driver-busybox.sh +++ b/libexec/bootstrap-scripts/deffile-driver-busybox.sh @@ -39,6 +39,7 @@ fi ########## BEGIN BOOTSTRAP SCRIPT ########## +MIRRORURL="${SINGULARITY_DEFFILE_MIRRORURL:-}" if [ -z "${MIRRORURL:-}" ]; then MIRRORURL="https://www.busybox.net/downloads/binaries/1.26.1-defconfig-multiarch/busybox-x86_64" diff --git a/libexec/bootstrap-scripts/deffile-driver-debootstrap.sh b/libexec/bootstrap-scripts/deffile-driver-debootstrap.sh index 237b40d398..26700466da 100644 --- a/libexec/bootstrap-scripts/deffile-driver-debootstrap.sh +++ b/libexec/bootstrap-scripts/deffile-driver-debootstrap.sh @@ -46,6 +46,7 @@ if ! DEBOOTSTRAP_PATH=`singularity_which debootstrap`; then exit 1 fi +ARCH="${SINGULARITY_DEFFILE_ARCH:-}" if [ -n "${ARCH:-}" ]; then ARCH=`echo ${ARCH:-} | sed -e 's/\s//g'` else @@ -64,23 +65,26 @@ else fi fi - +MIRRORURL="${SINGULARITY_DEFFILE_MIRRORURL:-}" if [ -z "${MIRRORURL:-}" ]; then message ERROR "No 'MirrorURL' defined in bootstrap definition\n" ABORT 1 fi +OSVERSION="${SINGULARITY_DEFFILE_OSVERSION:-}" if [ -z "${OSVERSION:-}" ]; then message ERROR "No 'OSVersion' defined in bootstrap definition\n" ABORT 1 fi -REQUIRES=`echo "${INCLUDE:-}" | sed -e 's/\s/,/g'` +REQUIRES=`echo "${SINGULARITY_DEFFILE_INCLUDE:-}" | sed -e 's/\s/,/g'` # The excludes save 25M or so with jessie. (Excluding udev avoids # systemd, for instance.) There are a few more we could exclude # to save a few MB. I see 182M cleaned with this, v. 241M with # the default debootstrap. -if ! eval "$DEBOOTSTRAP_PATH --variant=minbase --exclude=openssl,udev,debconf-i18n,e2fsprogs --include=apt,$REQUIRES --arch=$ARCH '$OSVERSION' '$SINGULARITY_ROOTFS' '$MIRRORURL'"; then + +$DEBOOTSTRAP_PATH --variant=minbase --exclude=openssl,udev,debconf-i18n,e2fsprogs --include=apt,$REQUIRES --arch=$ARCH "$OSVERSION" "$SINGULARITY_ROOTFS" "$MIRRORURL" +if [ $? != 0 ]; then ABORT 255 fi diff --git a/libexec/bootstrap-scripts/deffile-driver-docker.sh b/libexec/bootstrap-scripts/deffile-driver-docker.sh index d75f5c5442..c4e879d0b3 100644 --- a/libexec/bootstrap-scripts/deffile-driver-docker.sh +++ b/libexec/bootstrap-scripts/deffile-driver-docker.sh @@ -37,6 +37,7 @@ if [ -z "${SINGULARITY_ROOTFS:-}" ]; then exit 1 fi +FROM="${SINGULARITY_DEFFILE_FROM:-}" if [ -z "${FROM:-}" ]; then message ERROR "Required Definition tag 'From:' not defined.\n" exit 1 @@ -46,19 +47,21 @@ fi # Docker Customizations ################################################################################ -if [ ! -z "${REGISTRY:-}" ]; then - message DEBUG "Custom Docker Registry 'Registry:' ${REGISTRY}.\n" +if [ ! -z "${SINGULARITY_DEFFILE_REGISTRY:-}" ]; then + message DEBUG "Custom Docker Registry 'Registry:' ${SINGULARITY_DEFFILE_REGISTRY}.\n" + REGISTRY="${SINGULARITY_DEFFILE_REGISTRY}" export REGISTRY fi # Note: NAMESPACE can be set to an empty string, and that's a valid namespace # for Docker (not so for shub://) -if [ -n "${NAMESPACE+set}" ]; then - message DEBUG "Custom Docker Namespace 'Namespace:' ${NAMESPACE}.\n" +if [ ! -z "${SINGULARITY_DEFFILE_NAMESPACE+set}" ]; then + message DEBUG "Custom Docker Namespace 'Namespace:' ${SINGULARITY_DEFFILE_NAMESPACE}.\n" + NAMESPACE="${SINGULARITY_DEFFILE_NAMESPACE}" export NAMESPACE fi -if [ -z "${INCLUDECMD:-}" ]; then +if [ -z "${SINGULARITY_DEFFILE_INCLUDECMD:-}" ]; then export SINGULARITY_INCLUDECMD="yes" fi @@ -78,8 +81,8 @@ eval_abort "$SINGULARITY_libexecdir/singularity/python/import.py" umask 0002 for i in `cat "$SINGULARITY_CONTENTS"`; do name=`basename "$i"` - message 1 "Exploding layer: $name\n" - zcat "$i" | (cd "$SINGULARITY_ROOTFS"; tar --exclude=dev/* -xf -) || exit $? + message 2 "Exploding layer: $name\n" + $SINGULARITY_libexecdir/singularity/bin/docker-extract "$i" done rm -f "$SINGULARITY_CONTENTS" diff --git a/libexec/bootstrap-scripts/deffile-driver-localimage.sh b/libexec/bootstrap-scripts/deffile-driver-localimage.sh index c3a318e5ca..ad6d0f075e 100644 --- a/libexec/bootstrap-scripts/deffile-driver-localimage.sh +++ b/libexec/bootstrap-scripts/deffile-driver-localimage.sh @@ -32,6 +32,7 @@ if [ -z "${SINGULARITY_ROOTFS:-}" ]; then exit 1 fi +FROM="${SINGULARITY_DEFFILE_FROM:-}" if [ -z "${FROM:-}" ]; then message ERROR "Required Definition tag 'From:' not defined.\n" exit 1 @@ -46,7 +47,9 @@ fi umask 0002 message 1 "Exporting contents of ${FROM} to ${SINGULARITY_IMAGE}\n" -if ! eval "${SINGULARITY_bindir}"/singularity image.export "${FROM}" | tar xBf - -C "${SINGULARITY_ROOTFS}"; then + +${SINGULARITY_bindir}/singularity image.export "${FROM}" | tar xBf - -C "${SINGULARITY_ROOTFS}" +if [ $? != 0 ]; then message ERROR "Failed to export contents of ${FROM} to ${SINGULARITY_ROOTFS}\n" ABORT 255 fi diff --git a/libexec/bootstrap-scripts/deffile-driver-self.sh b/libexec/bootstrap-scripts/deffile-driver-self.sh index 7be19a59db..c55524ce29 100644 --- a/libexec/bootstrap-scripts/deffile-driver-self.sh +++ b/libexec/bootstrap-scripts/deffile-driver-self.sh @@ -45,6 +45,7 @@ fi # By default, we clone from root unless specified otherwise +FROM="${SINGULARITY_DEFFILE_FROM:-}" if [ -z "${FROM:-}" ]; then FROM='/' @@ -57,10 +58,11 @@ export SINGULARITY_DUMP # The user can specify custom exclusions -if [ -z "${EXCLUDE:-}" ]; then +if [ -z "${SINGULARITY_DEFFILE_EXCLUDE:-}" ]; then EXCLUDE='' else - message 1 "Custom exclusions: $EXCLUDE\n" + message 1 "Custom exclusions: $SINGULARITY_DEFFILE_EXCLUDE\n" + EXCLUDE="${SINGULARITY_DEFFILE_EXCLUDE:-}" fi CUSTOM_EXCLUSIONS=$(echo "$EXCLUDE" | sed 's/[^ ]* */--exclude &/g') diff --git a/libexec/bootstrap-scripts/deffile-driver-shub.sh b/libexec/bootstrap-scripts/deffile-driver-shub.sh index 354829c1e2..c728e13119 100644 --- a/libexec/bootstrap-scripts/deffile-driver-shub.sh +++ b/libexec/bootstrap-scripts/deffile-driver-shub.sh @@ -32,6 +32,7 @@ if [ -z "${SINGULARITY_ROOTFS:-}" ]; then exit 1 fi +FROM="${SINGULARITY_DEFFILE_FROM:-}" if [ -z "${FROM:-}" ]; then message ERROR "Required Definition tag 'From:' not defined.\n" exit 1 @@ -42,13 +43,15 @@ fi # Singularity Hub/Registry Customizations ################################################################################ -if [ ! -z "${REGISTRY:-}" ]; then - message DEBUG "Custom Singularity Registry 'Registry:' ${REGISTRY}.\n" +if [ ! -z "${SINGULARITY_DEFFILE_REGISTRY:-}" ]; then + message DEBUG "Custom Singularity Registry 'Registry:' ${SINGULARITY_DEFFILE_REGISTRY}.\n" + REGISTRY="${SINGULARITY_DEFFILE_REGISTRY:-}" export REGISTRY fi -if [ ! -z "${NAMESPACE:-}" ]; then - message DEBUG "Custom Singularity Registry Namespace 'Namespace:' ${NAMESPACE}.\n" +if [ ! -z "${SINGULARITY_DEFFILE_NAMESPACE:-}" ]; then + message DEBUG "Custom Singularity Registry Namespace 'Namespace:' ${SINGULARITY_DEFFILE_NAMESPACE}.\n" + NAMESPACE="${SINGULARITY_DEFFILE_NAMESPACE:-}" export NAMESPACE fi @@ -83,8 +86,8 @@ message 1 "Exporting contents of ${SINGULARITY_CONTAINER} to ${SINGULARITY_IMAGE SINGULARITY_CONTAINER=`cat $SINGULARITY_CONTENTS` rm -r $SINGULARITY_CONTENTS -#if ! eval "${SINGULARITY_bindir}"/singularity image.export "${SINGULARITY_CONTAINER}" | (cd "${SINGULARITY_ROOTFS}" && tar xBf -); then -if ! eval "${SINGULARITY_bindir}"/singularity image.export "${SINGULARITY_CONTAINER}" | tar xBf - -C "${SINGULARITY_ROOTFS}"; then +${SINGULARITY_bindir}/singularity image.export "${SINGULARITY_CONTAINER}" | tar xBf - -C "${SINGULARITY_ROOTFS}" +if [ $? != 0 ]; then message ERROR "Failed to export contents of ${SINGULARITY_CONTAINER} to ${SINGULARITY_ROOTFS}\n" rm $SINGULARITY_CONTAINER ABORT 255 diff --git a/libexec/bootstrap-scripts/deffile-driver-yum.sh b/libexec/bootstrap-scripts/deffile-driver-yum.sh index ec79dbc04b..75dae3b17a 100644 --- a/libexec/bootstrap-scripts/deffile-driver-yum.sh +++ b/libexec/bootstrap-scripts/deffile-driver-yum.sh @@ -81,6 +81,7 @@ if [ "$RPM_DBPATH" != '%{_var}/lib/rpm' ]; then ABORT 1 fi +OSVERSION="${SINGULARITY_DEFFILE_OSVERSION:-}" if [ -z "${OSVERSION:-}" ]; then if [ -f "/etc/redhat-release" ]; then OSVERSION=`rpm -qf --qf '%{VERSION}' /etc/redhat-release` @@ -89,15 +90,15 @@ if [ -z "${OSVERSION:-}" ]; then fi fi -MIRROR=`echo "${MIRRORURL:-}" | sed -r "s/%\{?OSVERSION\}?/$OSVERSION/gi"` -MIRROR_META=`echo "${METALINK:-}" | sed -r "s/%\{?OSVERSION\}?/$OSVERSION/gi"` +MIRROR=`echo "${SINGULARITY_DEFFILE_MIRRORURL:-}" | sed -r "s/%\{?OSVERSION\}?/$OSVERSION/gi"` +MIRROR_META=`echo "${SINGULARITY_DEFFILE_METALINK:-}" | sed -r "s/%\{?OSVERSION\}?/$OSVERSION/gi"` if [ -z "${MIRROR:-}" ] && [ -z "${MIRROR_META:-}" ]; then message ERROR "No 'MirrorURL' or 'MetaLink' defined in bootstrap definition\n" ABORT 1 fi -MIRROR_UPDATES=`echo "${UPDATEURL:-}" | sed -r "s/%\{?OSVERSION\}?/$OSVERSION/gi"` -MIRROR_UPDATES_META=`echo "${UPDATEMETALINK:-}" | sed -r "s/%\{?OSVERSION\}?/$OSVERSION/gi"` +MIRROR_UPDATES=`echo "${SINGULARITY_DEFFILE_UPDATEURL:-}" | sed -r "s/%\{?OSVERSION\}?/$OSVERSION/gi"` +MIRROR_UPDATES_META=`echo "${SINGULARITY_DEFFILE_UPDATEMETALINK:-}" | sed -r "s/%\{?OSVERSION\}?/$OSVERSION/gi"` if [ -n "${MIRROR_UPDATES:-}" ] || [ -n "${MIRROR_UPDATES_META:-}" ]; then message 1 "'UpdateURL' or 'UpdateMetaLink' defined in bootstrap definition\n" fi @@ -143,6 +144,8 @@ if [ -n "${MIRROR_META:-}" ]; then echo "metalink=$MIRROR_META" >> "$SINGULARITY_ROOTFS/$YUM_CONF" fi echo "enabled=1" >> "$SINGULARITY_ROOTFS/$YUM_CONF" + +GPG="${SINGULARITY_DEFFILE_GPG:-}" if [ -n "${GPG:-}" ]; then echo "gpgcheck=1" >> "$SINGULARITY_ROOTFS/$YUM_CONF" else @@ -205,7 +208,10 @@ else fi # Do the install! -if ! eval "$INSTALL_CMD --noplugins -c $SINGULARITY_ROOTFS/$YUM_CONF --installroot $SINGULARITY_ROOTFS --releasever=${OSVERSION} -y install /etc/redhat-release coreutils ${INCLUDE:-}"; then +INCLUDE="${SINGULARITY_DEFFILE_INCLUDE:-}" +$INSTALL_CMD --noplugins -c "$SINGULARITY_ROOTFS/$YUM_CONF" --installroot $SINGULARITY_ROOTFS --releasever="${OSVERSION}" -y install /etc/redhat-release coreutils ${INCLUDE:-} + +if [ $? != 0 ]; then message ERROR "Bootstrap failed... exiting\n" ABORT 255 fi diff --git a/libexec/bootstrap-scripts/deffile-driver-zypper.sh b/libexec/bootstrap-scripts/deffile-driver-zypper.sh index 190905bcec..966440d730 100644 --- a/libexec/bootstrap-scripts/deffile-driver-zypper.sh +++ b/libexec/bootstrap-scripts/deffile-driver-zypper.sh @@ -78,6 +78,7 @@ if [ "$RPM_DBPATH" != '%{_var}/lib/rpm' ]; then ABORT 1 fi +OSVERSION="${SINGULARITY_DEFFILE_OSVERSION:-}" if [ -z "${OSVERSION:-}" ]; then if [ -f "/etc/os-release" ]; then OSVERSION=`rpm -qf --qf '%{VERSION}' /etc/os-release` @@ -86,15 +87,15 @@ if [ -z "${OSVERSION:-}" ]; then fi fi -MIRROR=`echo "${MIRRORURL:-}" | sed -r "s/%\{?OSVERSION\}?/$OSVERSION/gi"` -MIRROR_META=`echo "${METALINK:-}" | sed -r "s/%\{?OSVERSION\}?/$OSVERSION/gi"` +MIRROR=`echo "${SINGULARITY_DEFFILE_MIRRORURL:-}" | sed -r "s/%\{?OSVERSION\}?/$OSVERSION/gi"` +MIRROR_META=`echo "${SINGULARITY_DEFFILE_METALINK:-}" | sed -r "s/%\{?OSVERSION\}?/$OSVERSION/gi"` if [ -z "${MIRROR:-}" ] && [ -z "${MIRROR_META:-}" ]; then message ERROR "No 'MirrorURL' or 'MetaLink' defined in bootstrap definition\n" ABORT 1 fi -MIRROR_UPDATES=`echo "${UPDATEURL:-}" | sed -r "s/%\{?OSVERSION\}?/$OSVERSION/gi"` -MIRROR_UPDATES_META=`echo "${UPDATEMETALINK:-}" | sed -r "s/%\{?OSVERSION\}?/$OSVERSION/gi"` +MIRROR_UPDATES=`echo "${SINGULARITY_DEFFILE_UPDATEURL:-}" | sed -r "s/%\{?OSVERSION\}?/$OSVERSION/gi"` +MIRROR_UPDATES_META=`echo "${SINGULARITY_DEFFILE_UPDATEMETALINK:-}" | sed -r "s/%\{?OSVERSION\}?/$OSVERSION/gi"` if [ -n "${MIRROR_UPDATES:-}" ] || [ -n "${MIRROR_UPDATES_META:-}" ]; then message 1 "'UpdateURL' or 'UpdateMetaLink' defined in bootstrap definition\n" fi @@ -118,7 +119,9 @@ $INSTALL_CMD --root $SINGULARITY_ROOTFS ar $MIRROR repo-oss $INSTALL_CMD --root $SINGULARITY_ROOTFS --gpg-auto-import-keys refresh # Do the install! -if ! eval "$INSTALL_CMD -c $SINGULARITY_ROOTFS/$ZYPP_CONF --root $SINGULARITY_ROOTFS --releasever=${OSVERSION} -n install --auto-agree-with-licenses aaa_base ${INCLUDE:-}"; then +INCLUDE="${SINGULARITY_DEFFILE_INCLUDE:-}" +$INSTALL_CMD -c "$SINGULARITY_ROOTFS/$ZYPP_CONF" --root $SINGULARITY_ROOTFS --releasever="${OSVERSION}" -n install --auto-agree-with-licenses aaa_base ${INCLUDE:-} +if [ $? != 0 ]; then message ERROR "Bootstrap failed... exiting\n" ABORT 255 fi diff --git a/libexec/bootstrap-scripts/deffile-sections.sh b/libexec/bootstrap-scripts/deffile-sections.sh index a5c5b3167b..4e3caa7ae6 100644 --- a/libexec/bootstrap-scripts/deffile-sections.sh +++ b/libexec/bootstrap-scripts/deffile-sections.sh @@ -53,8 +53,6 @@ if [ ! -f "${SINGULARITY_BUILDDEF:-}" ]; then fi -umask 0002 - # First priority goes to runscript defined in build file runscript_command=$(singularity_section_get "runscript" "$SINGULARITY_BUILDDEF") @@ -64,19 +62,6 @@ if [ ! -z "$runscript_command" ]; then echo "$runscript_command" > "$SINGULARITY_ROOTFS/singularity" fi -test -d "$SINGULARITY_ROOTFS/proc" || install -d -m 755 "$SINGULARITY_ROOTFS/proc" -test -d "$SINGULARITY_ROOTFS/sys" || install -d -m 755 "$SINGULARITY_ROOTFS/sys" -test -d "$SINGULARITY_ROOTFS/tmp" || install -d -m 755 "$SINGULARITY_ROOTFS/tmp" -test -d "$SINGULARITY_ROOTFS/dev" || install -d -m 755 "$SINGULARITY_ROOTFS/dev" - -mount --no-mtab -t proc proc "$SINGULARITY_ROOTFS/proc" -mount --no-mtab -t sysfs sysfs "$SINGULARITY_ROOTFS/sys" -mount --no-mtab --rbind "/tmp" "$SINGULARITY_ROOTFS/tmp" -mount --no-mtab --rbind "/dev" "$SINGULARITY_ROOTFS/dev" - -cp /etc/hosts "$SINGULARITY_ROOTFS/etc/hosts" -cp /etc/resolv.conf "$SINGULARITY_ROOTFS/etc/resolv.conf" - ### EXPORT ENVARS DEBIAN_FRONTEND=noninteractive SINGULARITY_ENVIRONMENT="/.singularity.d/env/91-environment.sh" @@ -317,8 +302,8 @@ if [ -z "${SINGULARITY_BUILDSECTION:-}" -o "${SINGULARITY_BUILDSECTION:-}" == "a # Make sure we have metadata APPBASE="$SINGULARITY_ROOTFS/scif/apps/${APPNAME}" APPFOLDER_SIZE=$(singularity_calculate_size "${APPBASE}") - $ADD_LABEL --key "SINGULARITY_APP_SIZE" --value "${APPFOLDER_SIZE}MB" --file "$APPBASE/scif/labels.json" - $ADD_LABEL --key "SINGULARITY_APP_NAME" --value "${APPNAME}" --file "${APPBASE}/scif/labels.json" + $ADD_LABEL --key "SCIF_APPSIZE" --value "${APPFOLDER_SIZE}MB" --file "$APPBASE/scif/labels.json" + $ADD_LABEL --key "SCIF_APPNAME" --value "${APPNAME}" --file "${APPBASE}/scif/labels.json" done fi @@ -340,7 +325,7 @@ fi ### APPLABELS -if [ -z "${SINGULARITY_BUILDSECTION:-}" -o "${SINGULARITY_BUILDSECTION:-}" == "appfiles" ]; then +if [ -z "${SINGULARITY_BUILDSECTION:-}" -o "${SINGULARITY_BUILDSECTION:-}" == "applabels" ]; then if singularity_section_exists "applabels" "$SINGULARITY_BUILDDEF"; then APPNAMES=(`singularity_section_args "applabels" "$SINGULARITY_BUILDDEF"`) @@ -364,15 +349,15 @@ if [ -z "${SINGULARITY_BUILDSECTION:-}" -o "${SINGULARITY_BUILDSECTION:-}" == "a for APPNAME in "${APPNAMES[@]}"; do message 1 "Installing ${APPNAME}\n" APPBASE="$SINGULARITY_ROOTFS/scif/apps/${APPNAME}" - SINGULARITY_APPROOT="/scif/apps/${APPNAME}" - export SINGULARITY_APPROOT + SCIF_APPROOT="/scif/apps/${APPNAME}" + export SCIF_APPROOT singularity_app_init "${APPNAME}" "${SINGULARITY_ROOTFS}" singularity_app_save "${APPNAME}" "$SINGULARITY_BUILDDEF" "${APPBASE}/scif/Singularity" singularity_app_install_get "${APPNAME}" "$SINGULARITY_BUILDDEF" | chroot "$SINGULARITY_ROOTFS" /bin/sh -xe || ABORT 255 APPFOLDER_SIZE=$(singularity_calculate_size "${APPBASE}") - $ADD_LABEL --key "SINGULARITY_APP_SIZE" --value "${APPFOLDER_SIZE}MB" --file "$APPBASE/scif/labels.json" --quiet -f - $ADD_LABEL --key "SINGULARITY_APP_NAME" --value "${APPNAME}" --file "${APPBASE}/scif/labels.json" --quiet -f + $ADD_LABEL --key "SCIF_APPSIZE" --value "${APPFOLDER_SIZE}MB" --file "$APPBASE/scif/labels.json" --quiet -f + $ADD_LABEL --key "SCIF_APPNAME" --value "${APPNAME}" --file "${APPBASE}/scif/labels.json" --quiet -f done fi @@ -388,34 +373,34 @@ for app in ${SINGULARITY_ROOTFS}/scif/apps/*; do if [ -d "$app" ]; then app="${app##*/}" - app=(`echo $app | sed -e "s/-/_/g"`) + appvar=(`echo $app | sed -e "s/-/_/g"`) appbase="${SINGULARITY_ROOTFS}/scif/apps/$app" appmeta="${appbase}/scif" # Export data, root, metadata, labels, environment - echo "APPDATA_$app=/scif/data/$app" >> "${APPGLOBAL}" - echo "APPMETA_$app=/scif/apps/$app/scif" >> "${APPGLOBAL}" - echo "APPROOT_$app=/scif/apps/$app" >> "${APPGLOBAL}" - echo "APPBIN_$app=/scif/apps/$app/bin" >> "${APPGLOBAL}" - echo "APPLIB_$app=/scif/apps/$app/lib" >> "${APPGLOBAL}" - echo "export APPDATA_$app APPROOT_$app APPMETA_$app APPBIN_$app APPLIB_$app" >> "${APPGLOBAL}" + echo "SCIF_APPDATA_$appvar=/scif/data/$app" >> "${APPGLOBAL}" + echo "SCIF_APPMETA_$appvar=/scif/apps/$app/scif" >> "${APPGLOBAL}" + echo "SCIF_APPROOT_$appvar=/scif/apps/$app" >> "${APPGLOBAL}" + echo "SCIF_APPBIN_$appvar=/scif/apps/$app/bin" >> "${APPGLOBAL}" + echo "SCIF_APPLIB_$appvar=/scif/apps/$app/lib" >> "${APPGLOBAL}" + echo "export SCIF_APPDATA_$appvar SCIF_APPROOT_$appvar SCIF_APPMETA_$appvar SCIF_APPBIN_$appvar SCIF_APPLIB_$appvar" >> "${APPGLOBAL}" # Environment if [ -e "${appmeta}/env/90-environment.sh" ]; then - echo "APPENV_${app}=/scif/apps/$app/scif/env/90-environment.sh" >> "${APPGLOBAL}" - echo "export APPENV_${app}" >> "${APPGLOBAL}" + echo "SCIF_APPENV_${appvar}=/scif/apps/$app/scif/env/90-environment.sh" >> "${APPGLOBAL}" + echo "export SCIF_APPENV_${appvar}" >> "${APPGLOBAL}" fi # Labels if [ -e "${appmeta}/labels.json" ]; then - echo "APPLABELS_${app}=/scif/apps/$app/scif/labels.json" >> "${APPGLOBAL}" - echo "export APPLABELS_${app}" >> "${APPGLOBAL}" + echo "SCIF_APPLABELS_${appvar}=/scif/apps/$app/scif/labels.json" >> "${APPGLOBAL}" + echo "export SCIF_APPLABELS_${appvar}" >> "${APPGLOBAL}" fi # Runscript if [ -e "${appmeta}/runscript" ]; then - echo "APPRUN_${app}=/scif/apps/$app/scif/runscript" >> "${APPGLOBAL}" - echo "export APPRUN_${app}" >> "${APPGLOBAL}" + echo "SCIF_APPRUN_${appvar}=/scif/apps/$app/scif/runscript" >> "${APPGLOBAL}" + echo "export SCIF_APPRUN_${appvar}" >> "${APPGLOBAL}" fi fi done diff --git a/libexec/bootstrap-scripts/environment/95-apps.sh b/libexec/bootstrap-scripts/environment/95-apps.sh index 492ca43fe1..d20b119f08 100644 --- a/libexec/bootstrap-scripts/environment/95-apps.sh +++ b/libexec/bootstrap-scripts/environment/95-apps.sh @@ -18,9 +18,9 @@ if test -n "${SINGULARITY_APPNAME:-}"; then export SINGULARITY_APPNAME if test -d "/scif/apps/${SINGULARITY_APPNAME:-}/"; then - SINGULARITY_APPS="/scif/apps" - SINGULARITY_APPROOT="/scif/apps/${SINGULARITY_APPNAME:-}" - export SINGULARITY_APPROOT SINGULARITY_APPS + SCIF_APPS="/scif/apps" + SCIF_APPROOT="/scif/apps/${SINGULARITY_APPNAME:-}" + export SCIF_APPROOT SCIF_APPS PATH="/scif/apps/${SINGULARITY_APPNAME:-}:$PATH" # Automatically add application bin to path diff --git a/libexec/bootstrap-scripts/functions b/libexec/bootstrap-scripts/functions index 63810c60fd..9da77416f5 100644 --- a/libexec/bootstrap-scripts/functions +++ b/libexec/bootstrap-scripts/functions @@ -74,7 +74,7 @@ get_section() { RECIPE="$2" FOUND="no" - while read line + while read -r line do @@ -198,14 +198,14 @@ singularity_app_init() { # App specific variables should be exposed at run, shell, exec echo " - SINGULARITY_APPNAME=$APPNAME - SINGULARITY_APPROOT=\"/scif/apps/${APPNAME}\" - SINGULARITY_APPMETA=\"/scif/apps/${APPNAME}/scif\" - SINGULARITY_DATA=\"/scif/data\" - SINGULARITY_APPDATA=\"/scif/data/${APPNAME}\" - SINGULARITY_APPINPUT=\"/scif/data/${APPNAME}/input\" - SINGULARITY_APPOUTPUT=\"/scif/data/${APPNAME}/output\" - export SINGULARITY_APPDATA SINGULARITY_APPNAME SINGULARITY_APPROOT SINGULARITY_APPMETA SINGULARITY_APPINPUT SINGULARITY_APPOUTPUT SINGULARITY_DATA + SCIF_APPNAME=$APPNAME + SCIF_APPROOT=\"/scif/apps/${APPNAME}\" + SCIF_APPMETA=\"/scif/apps/${APPNAME}/scif\" + SCIF_DATA=\"/scif/data\" + SCIF_APPDATA=\"/scif/data/${APPNAME}\" + SCIF_APPINPUT=\"/scif/data/${APPNAME}/input\" + SCIF_APPOUTPUT=\"/scif/data/${APPNAME}/output\" + export SCIF_APPDATA SCIF_APPNAME SCIF_APPROOT SCIF_APPMETA SCIF_APPINPUT SCIF_APPOUTPUT SCIF_DATA " > "${APPMETA}/env/01-base.sh" } diff --git a/libexec/bootstrap-scripts/main-deffile.sh b/libexec/bootstrap-scripts/main-deffile-stage1.sh similarity index 97% rename from libexec/bootstrap-scripts/main-deffile.sh rename to libexec/bootstrap-scripts/main-deffile-stage1.sh index a20f2ee891..995982520f 100644 --- a/libexec/bootstrap-scripts/main-deffile.sh +++ b/libexec/bootstrap-scripts/main-deffile-stage1.sh @@ -40,6 +40,8 @@ fi eval_abort "$SINGULARITY_libexecdir/singularity/bootstrap-scripts/pre.sh" eval_abort "$SINGULARITY_libexecdir/singularity/bootstrap-scripts/environment.sh" +BOOTSTRAP="${SINGULARITY_DEFFILE_BOOTSTRAP:-}" + if [ -n "${BOOTSTRAP:-}" -a -z "${SINGULARITY_BUILDNOBASE:-}" ]; then if [ -x "$SINGULARITY_libexecdir/singularity/bootstrap-scripts/deffile-driver-$BOOTSTRAP.sh" ]; then if [ ! -f "${SINGULARITY_ROOTFS}/.coredone" ]; then @@ -59,7 +61,7 @@ if [ "${BOOTSTRAP:-}" = "localimage" -o "${BOOTSTRAP:-}" = "shub" ]; then export SINGULARITY_STARTING_ENVIRONMENT SINGULARITY_STARTING_ENVSHA1 fi -eval_abort "$SINGULARITY_libexecdir/singularity/bootstrap-scripts/deffile-sections.sh" +eval_abort "$SINGULARITY_libexecdir/singularity/bootstrap-scripts/main-deffile-stage2.sh" eval_abort "$SINGULARITY_libexecdir/singularity/bootstrap-scripts/post.sh" # take another snapshot and compare to see what changed diff --git a/libexec/bootstrap-scripts/main-deffile-stage2.sh b/libexec/bootstrap-scripts/main-deffile-stage2.sh new file mode 100644 index 0000000000..616ad40303 --- /dev/null +++ b/libexec/bootstrap-scripts/main-deffile-stage2.sh @@ -0,0 +1,66 @@ +#!/bin/bash +# +# Copyright (c) 2017, SingularityWare, LLC. All rights reserved. +# +# See the COPYRIGHT.md file at the top-level directory of this distribution and at +# https://github.com/singularityware/singularity/blob/master/COPYRIGHT.md. +# +# This file is part of the Singularity Linux container project. It is subject to the license +# terms in the LICENSE.md file found in the top-level directory of this distribution and +# at https://github.com/singularityware/singularity/blob/master/LICENSE.md. No part +# of Singularity, including this file, may be copied, modified, propagated, or distributed +# except according to the terms contained in the LICENSE.md file. +# +# + +## Basic sanity +if [ -z "$SINGULARITY_libexecdir" ]; then + echo "Could not identify the Singularity libexecdir." + exit 1 +fi + +## Load functions +if [ -f "$SINGULARITY_libexecdir/singularity/functions" ]; then + . "$SINGULARITY_libexecdir/singularity/functions" +else + echo "Error loading functions: $SINGULARITY_libexecdir/singularity/functions" + exit 1 +fi +if [ -f "$SINGULARITY_libexecdir/singularity/bootstrap-scripts/functions" ]; then + . "$SINGULARITY_libexecdir/singularity/bootstrap-scripts/functions" +else + echo "Error loading functions: $SINGULARITY_libexecdir/singularity/bootstrap-scripts/functions" + exit 1 +fi + +if [ -z "${SINGULARITY_ROOTFS:-}" ]; then + message ERROR "Singularity root file system not defined\n" + exit 1 +fi + +if [ -z "${SINGULARITY_BUILDDEF:-}" ]; then + message ERROR "Singularity bootstrap definition file not defined!\n" + exit 1 +fi + +if [ ! -f "${SINGULARITY_BUILDDEF:-}" ]; then + message ERROR "Singularity bootstrap definition file not found!\n" + exit 1 +fi + +umask 0002 + +test -d "$SINGULARITY_ROOTFS/proc" || install -d -m 755 "$SINGULARITY_ROOTFS/proc" +test -d "$SINGULARITY_ROOTFS/sys" || install -d -m 755 "$SINGULARITY_ROOTFS/sys" +test -d "$SINGULARITY_ROOTFS/tmp" || install -d -m 755 "$SINGULARITY_ROOTFS/tmp" +test -d "$SINGULARITY_ROOTFS/dev" || install -d -m 755 "$SINGULARITY_ROOTFS/dev" + +mount -r --no-mtab -t proc proc "$SINGULARITY_ROOTFS/proc" +mount -r --no-mtab -t sysfs sysfs "$SINGULARITY_ROOTFS/sys" +mount --no-mtab --rbind "/tmp" "$SINGULARITY_ROOTFS/tmp" +mount --no-mtab --rbind "/dev" "$SINGULARITY_ROOTFS/dev" + +cp /etc/hosts "$SINGULARITY_ROOTFS/etc/hosts" +cp /etc/resolv.conf "$SINGULARITY_ROOTFS/etc/resolv.conf" + +eval_abort "SINGULARITY_STAGE2=1 ${SINGULARITY_libexecdir}/singularity/bin/builddef" diff --git a/libexec/bootstrap-scripts/main-dockerhub.sh b/libexec/bootstrap-scripts/main-dockerhub.sh index a67a177c5e..515c6ff0b7 100644 --- a/libexec/bootstrap-scripts/main-dockerhub.sh +++ b/libexec/bootstrap-scripts/main-dockerhub.sh @@ -50,8 +50,8 @@ eval_abort "$SINGULARITY_libexecdir/singularity/python/import.py" for i in `cat "$SINGULARITY_CONTENTS"`; do name=`basename "$i"` - message 1 "Exploding layer: $name\n" - zcat "$i" | (cd "$SINGULARITY_ROOTFS"; tar --exclude=dev/* -xf -) || exit $? + message 2 "Exploding layer: $name\n" + $SINGULARITY_libexecdir/singularity/bin/docker-extract "$i" done rm -f "$SINGULARITY_CONTENTS" diff --git a/libexec/bootstrap-scripts/pre.sh b/libexec/bootstrap-scripts/pre.sh index 8733b01a1b..3c49c57a8f 100644 --- a/libexec/bootstrap-scripts/pre.sh +++ b/libexec/bootstrap-scripts/pre.sh @@ -44,11 +44,4 @@ install -d -m 0755 "$SINGULARITY_ROOTFS" install -d -m 0755 "$SINGULARITY_ROOTFS/.singularity.d" install -d -m 0755 "$SINGULARITY_ROOTFS/.singularity.d/env" -if [ -f "$SINGULARITY_BUILDDEF" ]; then - ARGS=`singularity_section_args "pre" "$SINGULARITY_BUILDDEF"` - singularity_section_get "pre" "$SINGULARITY_BUILDDEF" | /bin/sh -e -x $ARGS || ABORT 255 -fi - - - exit 0 diff --git a/libexec/bootstrap-scripts/secbuild.sh b/libexec/bootstrap-scripts/secbuild.sh new file mode 100755 index 0000000000..23c9eda47c --- /dev/null +++ b/libexec/bootstrap-scripts/secbuild.sh @@ -0,0 +1,172 @@ +#!/bin/bash +# +# Copyright (c) 2017, SingularityWare, LLC. All rights reserved. +# +# See the COPYRIGHT.md file at the top-level directory of this distribution and at +# https://github.com/singularityware/singularity/blob/master/COPYRIGHT.md. +# +# This file is part of the Singularity Linux container project. It is subject to the license +# terms in the LICENSE.md file found in the top-level directory of this distribution and +# at https://github.com/singularityware/singularity/blob/master/LICENSE.md. No part +# of Singularity, including this file, may be copied, modified, propagated, or distributed +# except according to the terms contained in the LICENSE.md file. +# +# + +## Basic sanity +if [ -z "$SINGULARITY_libexecdir" ]; then + echo "Could not identify the Singularity libexecdir." + exit 1 +fi + +SECBUILD_IMAGE="$SINGULARITY_libexecdir/singularity/bootstrap-scripts/secbuild.img" + +if [ ! -d "${SECBUILD_IMAGE:-}" ]; then + echo + echo "$SECBUILD_IMAGE is missing, build it as root by typing:" + echo + echo "make secbuildimg" + echo + exit 1 +fi + +## Load functions +if [ -f "$SINGULARITY_libexecdir/singularity/functions" ]; then + . "$SINGULARITY_libexecdir/singularity/functions" +else + echo "Error loading functions: $SINGULARITY_libexecdir/singularity/functions" + exit 1 +fi +if [ -f "$SINGULARITY_libexecdir/singularity/bootstrap-scripts/functions" ]; then + . "$SINGULARITY_libexecdir/singularity/bootstrap-scripts/functions" +else + echo "Error loading functions: $SINGULARITY_libexecdir/singularity/bootstrap-scripts/functions" + exit 1 +fi + +if [ -z "${SINGULARITY_ROOTFS:-}" ]; then + message ERROR "Singularity root file system not defined\n" + exit 1 +fi + +if [ -z "${SINGULARITY_BUILDDEF:-}" ]; then + message ERROR "Singularity bootstrap definition file not defined!\n" + exit 1 +fi + +if [ ! -f "${SINGULARITY_BUILDDEF:-}" ]; then + message ERROR "Singularity bootstrap definition file not found!\n" + exit 1 +fi + +BUILDDEF_DIR_NAME=$(dirname ${SINGULARITY_BUILDDEF:-}) +BUILDDEF_DIR=$(readlink -f ${BUILDDEF_DIR_NAME:-}) + +if [ -z "${BUILDDEF_DIR:-}" ]; then + message ERROR "Can't find parent directory of $SINGULARITY_BUILDDEF\n" + exit 1 +fi + +BUILDDEF=$(basename ${SINGULARITY_BUILDDEF:-}) + +# create a temporary dir per build instance +export SINGULARITY_WORKDIR=$(mktemp -d) + +# create /tmp and /var/tmp into WORKDIR +mkdir -p $SINGULARITY_WORKDIR/tmp $SINGULARITY_WORKDIR/var_tmp + +# set sticky bit for these directories +chmod 1777 $SINGULARITY_WORKDIR/tmp +chmod 1777 $SINGULARITY_WORKDIR/var_tmp + +# setup a fake root directory +cp -a /etc/skel $SINGULARITY_WORKDIR/root + +cat > "$SINGULARITY_WORKDIR/root/.rpmmacros" << RPMMAC +%_var /var +%_dbpath %{_var}/lib/rpm +RPMMAC + +REPO_DIR="/root/repo" +STAGED_BUILD_IMAGE="/root/build" + +mkdir ${SINGULARITY_WORKDIR}${REPO_DIR} +mkdir ${SINGULARITY_WORKDIR}${STAGED_BUILD_IMAGE} + +BUILD_SCRIPT="$SINGULARITY_WORKDIR/tmp/build-script" +TMP_CONF_FILE="$SINGULARITY_WORKDIR/tmp.conf" +FSTAB_FILE="$SINGULARITY_WORKDIR/fstab" +RESOLV_CONF="$SINGULARITY_WORKDIR/resolv.conf" + +cp /etc/resolv.conf $RESOLV_CONF + +cat > "$FSTAB_FILE" << FSTAB +none $STAGED_BUILD_IMAGE bind dev 0 0 +FSTAB + +cat > "$TMP_CONF_FILE" << CONF +config passwd = no +config group = no +config resolv_conf = no +mount proc = no +mount sys = no +mount home = no +mount dev = minimal +mount devpts = no +mount tmp = no +enable overlay = no +user bind control = no +bind path = $SINGULARITY_WORKDIR/root:/root +bind path = $SINGULARITY_WORKDIR/tmp:/tmp +bind path = $SINGULARITY_WORKDIR/var_tmp:/var/tmp +bind path = $SINGULARITY_ROOTFS:$STAGED_BUILD_IMAGE +bind path = $BUILDDEF_DIR:$REPO_DIR +bind path = $FSTAB_FILE:/etc/fstab +bind path = $RESOLV_CONF:/etc/resolv.conf +root default capabilities = default +allow user capabilities = no +CONF + +# here build pre-stage +cat > "$BUILD_SCRIPT" << SCRIPT +#!/bin/sh + +mount -r --no-mtab -t proc proc /proc +if [ \$? != 0 ]; then + echo "Can't mount /proc directory" + exit 1 +fi + +mount -r --no-mtab -t sysfs sysfs /sys +if [ \$? != 0 ]; then + echo "Can't mount /sys directory" + exit 1 +fi + +mount -o remount,dev $STAGED_BUILD_IMAGE +if [ \$? != 0 ]; then + echo "Can't remount $STAGED_BUILD_IMAGE" + exit 1 +fi + +cd $REPO_DIR + +singularity build --sandbox $STAGED_BUILD_IMAGE $BUILDDEF +exit \$? +SCRIPT + +chmod +x $BUILD_SCRIPT + +unset SINGULARITY_IMAGE +unset SINGULARITY_NO_PRIVS +unset SINGULARITY_KEEP_PRIVS +unset SINGULARITY_ADD_CAPS +unset SINGULARITY_DROP_CAPS + +${SINGULARITY_bindir}/singularity -c $TMP_CONF_FILE exec -e -i -p $SECBUILD_IMAGE /tmp/build-script +if [ $? != 0 ]; then + rm -rf $SINGULARITY_WORKDIR + exit 1 +fi + +rm -rf $SINGULARITY_WORKDIR diff --git a/libexec/capabilities b/libexec/capabilities new file mode 100644 index 0000000000..991752dbe8 --- /dev/null +++ b/libexec/capabilities @@ -0,0 +1,179 @@ +#!/bin/bash + +declare -A SINGULARITY_CAPS + +SINGULARITY_CAPS[CAP_CHOWN]=0 +SINGULARITY_CAPS[CAP_DAC_OVERRIDE]=1 +SINGULARITY_CAPS[CAP_DAC_READ_SEARCH]=2 +SINGULARITY_CAPS[CAP_FOWNER]=3 +SINGULARITY_CAPS[CAP_FSETID]=4 +SINGULARITY_CAPS[CAP_KILL]=5 +SINGULARITY_CAPS[CAP_SETGID]=6 +SINGULARITY_CAPS[CAP_SETUID]=7 +SINGULARITY_CAPS[CAP_SETPCAP]=8 +SINGULARITY_CAPS[CAP_LINUX_IMMUTABLE]=9 +SINGULARITY_CAPS[CAP_NET_BIND_SERVICE]=10 +SINGULARITY_CAPS[CAP_NET_BROADCAST]=11 +SINGULARITY_CAPS[CAP_NET_ADMIN]=12 +SINGULARITY_CAPS[CAP_NET_RAW]=13 +SINGULARITY_CAPS[CAP_IPC_LOCK]=14 +SINGULARITY_CAPS[CAP_IPC_OWNER]=15 +SINGULARITY_CAPS[CAP_SYS_MODULE]=16 +SINGULARITY_CAPS[CAP_SYS_RAWIO]=17 +SINGULARITY_CAPS[CAP_SYS_CHROOT]=18 +SINGULARITY_CAPS[CAP_SYS_PTRACE]=19 +SINGULARITY_CAPS[CAP_SYS_PACCT]=20 +SINGULARITY_CAPS[CAP_SYS_ADMIN]=21 +SINGULARITY_CAPS[CAP_SYS_BOOT]=22 +SINGULARITY_CAPS[CAP_SYS_NICE]=23 +SINGULARITY_CAPS[CAP_SYS_RESOURCE]=24 +SINGULARITY_CAPS[CAP_SYS_TIME]=25 +SINGULARITY_CAPS[CAP_SYS_TTY_CONFIG]=26 +SINGULARITY_CAPS[CAP_MKNOD]=27 +SINGULARITY_CAPS[CAP_LEASE]=28 +SINGULARITY_CAPS[CAP_AUDIT_WRITE]=29 +SINGULARITY_CAPS[CAP_AUDIT_CONTROL]=30 +SINGULARITY_CAPS[CAP_SETFCAP]=31 +SINGULARITY_CAPS[CAP_MAC_OVERRIDE]=32 +SINGULARITY_CAPS[CAP_MAC_ADMIN]=33 +SINGULARITY_CAPS[CAP_SYSLOG]=34 +SINGULARITY_CAPS[CAP_WAKE_ALARM]=35 +SINGULARITY_CAPS[CAP_BLOCK_SUSPEND]=36 +SINGULARITY_CAPS[CAP_AUDIT_READ]=37 + +parse_capabilities() { + ncaps=0 + parsed="" + caps=$(echo "${1:-}" | tr '[a-z]' '[A-Z]') + last=$(echo "${1##*,}" | tr '[a-z]' '[A-Z]') + + while true; do + pcaps="${caps%%,*}" + + if [[ ! "$pcaps" =~ ^CAP_ ]]; then + pcaps="CAP_$pcaps" + fi + + if [ -z "${SINGULARITY_CAPS[$pcaps]:-}" ]; then + echo "Unknown or not supported capability $pcaps, skipped" 1>&2 + if [ "$caps" == "$last" ]; then + break + fi + caps=${caps#*,} + continue + fi + + ncaps=$(( $(( 1 << ${SINGULARITY_CAPS[$pcaps]} )) | $ncaps )) + + if [ "$caps" == "$last" ]; then + break + fi + + caps=${caps#*,} + done + + echo -en "$ncaps" +} + +singularity_add_capabilities() { + if [ "$(singularity_config_value 'allow user capabilities')" != "yes" ]; then + if [ "$(id -ru)" != 0 ]; then + message ERROR "User capabilities are disabled by configuration\n" + exit 1 + fi + fi + + if [ "$(singularity_config_value 'allow root capabilities')" != "yes" ]; then + if [ "$(id -ru)" = 0 ]; then + message ERROR "Root capabilities are disabled by configuration\n" + exit 1 + fi + fi + + if [ -z "${1:-}" ]; then + message ERROR "No capability specified\n" + exit 1 + fi + + if [ ! -z "${SINGULARITY_ADD_CAPS:-}" ]; then + OLD_CAPS=${SINGULARITY_ADD_CAPS:-} + NEW_CAPS=$(parse_capabilities ${1:-}) + export SINGULARITY_ADD_CAPS=$(( $OLD_CAPS | $NEW_CAPS )) + else + export SINGULARITY_ADD_CAPS=$(parse_capabilities ${1:-}) + fi +} + +singularity_drop_capabilities() { + if [ "$(singularity_config_value 'allow user capabilities')" != "yes" ]; then + if [ "$(id -ru)" != 0 ]; then + message ERROR "User capabilities are disabled by configuration\n" + exit 1 + fi + fi + + if [ "$(singularity_config_value 'allow root capabilities')" != "yes" ]; then + if [ "$(id -ru)" = 0 ]; then + message ERROR "Root capabilities are disabled by configuration\n" + exit 1 + fi + fi + + if [ -z "${1:-}" ]; then + message ERROR "No capability specified\n" + exit 1 + fi + + if [ ! -z "${SINGULARITY_DROP_CAPS:-}" ]; then + OLD_CAPS=${SINGULARITY_DROP_CAPS:-} + NEW_CAPS=$(parse_capabilities ${1:-}) + export SINGULARITY_DROP_CAPS=$(( $OLD_CAPS & ~$NEW_CAPS )) + else + export SINGULARITY_DROP_CAPS=$(parse_capabilities ${1:-}) + fi +} + +singularity_get_env_capabilities() { + if [ ! -z "${SINGULARITY_ADD_CAPS:-}" -o ! -z "${SINGULARITY_DROP_CAPS:-}" ]; then + if [ "$(singularity_config_value 'allow user capabilities')" != "yes" ]; then + if [ "$(id -ru)" != 0 ]; then + message ERROR "User capabilities are disabled by configuration\n" + exit 1 + fi + fi + + if [ "$(singularity_config_value 'allow root capabilities')" != "yes" ]; then + if [ "$(id -ru)" = 0 ]; then + message ERROR "Root capabilities are disabled by configuration\n" + exit 1 + fi + fi + fi + + if [ ! -z "${SINGULARITY_ADD_CAPS:-}" ]; then + export SINGULARITY_ADD_CAPS=$(parse_capabilities ${SINGULARITY_ADD_CAPS:-}) + fi + if [ ! -z "${SINGULARITY_DROP_CAPS:-}" ]; then + export SINGULARITY_DROP_CAPS=$(parse_capabilities ${SINGULARITY_DROP_CAPS:-}) + fi +} + +singularity_print_capabilities() { + if [ -z "${1:-}" ]; then + message ERROR "No capability to print\n" + fi + + PCAPS="" + + for capname in "${!SINGULARITY_CAPS[@]}"; do + if [ $(( ${1:-0} & $(( 1 << ${SINGULARITY_CAPS[$capname]} )) )) != 0 ]; then + if [ -z "$PCAPS" ]; then + PCAPS="$capname" + else + PCAPS="$PCAPS,$capname" + fi + fi + done + + echo "$PCAPS" +} diff --git a/libexec/cli/Makefile.am b/libexec/cli/Makefile.am index 637cb06bd5..54ad8c877d 100644 --- a/libexec/cli/Makefile.am +++ b/libexec/cli/Makefile.am @@ -4,6 +4,10 @@ dist_cliexec_SCRIPTS = action_argparser.sh \ apps.exec \ bootstrap.exec \ build.exec \ + capability.exec \ + capability.add.exec \ + capability.drop.exec \ + capability.list.exec \ check.exec \ create.exec \ exec.exec \ @@ -31,6 +35,10 @@ dist_cliexec_SCRIPTS = action_argparser.sh \ dist_cliexec_DATA = apps.info \ bootstrap.info \ build.info \ + capability.info \ + capability.add.info \ + capability.drop.info \ + capability.list.info \ check.info \ create.info \ exec.info \ diff --git a/libexec/cli/action_argparser.sh b/libexec/cli/action_argparser.sh index 1f2c84ee95..b633341c1a 100644 --- a/libexec/cli/action_argparser.sh +++ b/libexec/cli/action_argparser.sh @@ -16,6 +16,14 @@ # file found in the top-level directory of this distribution and at # https://github.com/singularityware/singularity/blob/master/LICENSE-LBNL.md. +## Load capabilities +if [ -f "$SINGULARITY_libexecdir/singularity/capabilities" ]; then + . "$SINGULARITY_libexecdir/singularity/capabilities" + singularity_get_env_capabilities +else + echo "Error loading capabilities: $SINGULARITY_libexecdir/singularity/capabilities" + exit 1 +fi message 2 "Evaluating args: '$*'\n" @@ -114,6 +122,18 @@ while true; do SINGULARITY_UNSHARE_NET=1 export SINGULARITY_UNSHARE_NET ;; + --uts) + shift + SINGULARITY_UNSHARE_UTS=1 + export SINGULARITY_UNSHARE_UTS + ;; + --hostname) + shift + SINGULARITY_UNSHARE_UTS=1 + SINGULARITY_HOSTNAME="$1" + export SINGULARITY_UNSHARE_UTS SINGULARITY_HOSTNAME + shift + ;; --pwd) shift SINGULARITY_TARGET_PWD="$1" @@ -122,34 +142,59 @@ while true; do ;; --nv) shift - SINGULARITY_NVLIBLIST=`mktemp ${TMPDIR:-/tmp}/.singularity-nvliblist.XXXXXXXX` - cat $SINGULARITY_sysconfdir"/singularity/nvliblist.conf" | grep -Ev "^#|^\s*$" > $SINGULARITY_NVLIBLIST - for i in $(ldconfig -p | grep -f "${SINGULARITY_NVLIBLIST}"); do - if [ -f "$i" ]; then - message 2 "Found NV library: $i\n" - if [ -z "${SINGULARITY_CONTAINLIBS:-}" ]; then - SINGULARITY_CONTAINLIBS="$i" - else - SINGULARITY_CONTAINLIBS="$SINGULARITY_CONTAINLIBS,$i" - fi + SINGULARITY_NV=1 + ;; + -f|--fakeroot) + shift + SINGULARITY_NOSUID=1 + SINGULARITY_USERNS_UID=0 + SINGULARITY_USERNS_GID=0 + export SINGULARITY_USERNS_UID SINGULARITY_USERNS_GID SINGULARITY_NOSUID + ;; + --keep-privs) + if [ "$(id -ru)" = "0" ]; then + if [ "$(singularity_config_value 'allow root capabilities')" != "yes" ]; then + message ERROR "keep-privs is disabled when allow root capabilities directive is set to no\n" + exit 1 fi - done - rm $SINGULARITY_NVLIBLIST - if [ -z "${SINGULARITY_CONTAINLIBS:-}" ]; then - message WARN "Could not find any Nvidia libraries on this host!\n"; + SINGULARITY_KEEP_PRIVS=1 + export SINGULARITY_KEEP_PRIVS + message 4 "Requesting keep privileges\n" else - export SINGULARITY_CONTAINLIBS + message WARNING "Keeping privileges is for root only\n" fi - if NVIDIA_SMI=$(which nvidia-smi); then - if [ -n "${SINGULARITY_BINDPATH:-}" ]; then - SINGULARITY_BINDPATH="${SINGULARITY_BINDPATH},${NVIDIA_SMI}" - else - SINGULARITY_BINDPATH="${NVIDIA_SMI}" - fi - export SINGULARITY_BINDPATH - else - message WARN "Could not find the Nvidia SMI binary to bind into container\n" + shift + ;; + --no-privs) + if [ "$(id -ru)" = "0" ]; then + SINGULARITY_NO_PRIVS=1 + export SINGULARITY_NO_PRIVS + message 4 "Requesting no privileges\n" + fi + shift + ;; + --add-caps) + shift + singularity_add_capabilities "$1" + shift + ;; + --drop-caps) + shift + singularity_drop_capabilities "$1" + shift + ;; + --allow-setuid) + shift + if [ "$(id -ru)" != "0" ]; then + message ERROR "allow-setuid is for root only\n" + exit 1 + fi + if [ "$(singularity_config_value 'allow root capabilities')" != "yes" ]; then + message ERROR "allow-setuid is disabled when allow root capabilities directive is set to no\n" + exit 1 fi + SINGULARITY_ALLOW_SETUID=1 + export SINGULARITY_ALLOW_SETUID ;; -*) message ERROR "Unknown option: ${1:-}\n" @@ -160,3 +205,17 @@ while true; do ;; esac done + +if [ -z "${SINGULARITY_NV_OFF:-}" ]; then # this is a "kill switch" provided for user + # if singularity.conf specifies --nv + if [ `$SINGULARITY_libexecdir/singularity/bin/get-configvals "always use nv"` == "yes" ]; then + message 2 "'always use nv = yes' found in singularity.conf\n" + message 2 "binding nvidia files into container\n" + bind_nvidia_files + # or if the user asked for --nv + elif [ -n "${SINGULARITY_NV:-}" ]; then + bind_nvidia_files + fi +fi + + diff --git a/libexec/cli/apps.exec b/libexec/cli/apps.exec index 9f95de38df..089abd3084 100644 --- a/libexec/cli/apps.exec +++ b/libexec/cli/apps.exec @@ -65,8 +65,8 @@ fi export SINGULARITY_IMAGE SINGULARITY_MOUNTPOINT shift -if [ -z "${SINGULARITY_NOSUID:-}" -a -u "$SINGULARITY_libexecdir/singularity/bin/mount-suid" ]; then - eval "$SINGULARITY_libexecdir/singularity/bin/mount-suid" /bin/bash "$SINGULARITY_libexecdir/singularity/helpers/apps/list.sh" +if [ -z "${SINGULARITY_NOSUID:-}" -a -u "$SINGULARITY_libexecdir/singularity/bin/wrapper-suid" ]; then + eval "$SINGULARITY_libexecdir/singularity/bin/wrapper-suid" /bin/bash "$SINGULARITY_libexecdir/singularity/helpers/apps/list.sh" RETVAL=$? elif [ -x "$SINGULARITY_libexecdir/singularity/bin/mount" ]; then eval "$SINGULARITY_libexecdir/singularity/bin/mount" /bin/bash "$SINGULARITY_libexecdir/singularity/helpers/apps/list.sh" diff --git a/libexec/cli/apps.info b/libexec/cli/apps.info index f1e0bff393..28593ba1e8 100644 --- a/libexec/cli/apps.info +++ b/libexec/cli/apps.info @@ -24,20 +24,20 @@ for all apps regardless of the active: ACTIVE APP ENVIRONMENT: - SINGULARITY_APPNAME the name of the application - SINGULARITY_APPROOT the application base (/scif/apps/) - SINGULARITY_APPMETA the application metadata folder - SINGULARITY_APPDATA the data base folder for active app - SINGULARITY_APPINPUT expected input folder within data base folder - SINGULARITY_APPOUTPUT the output data folder within data base folder + SCIF_APPNAME the name of the application + SCIF_APPROOT the application base (/scif/apps/) + SCIF_APPMETA the application metadata folder + SCIF_APPDATA the data base folder for active app + SCIF_APPINPUT expected input folder within data base folder + SCIF_APPOUTPUT the output data folder within data base folder GLOBAL APP ENVIRONMENT: - SINGULARITY_DATA scif defined data base for all apps (/scif/data) - SINGULARITY_APPS scif defined install bases for all apps (/scif/apps) - APPROOT_ root for application - APPDATA_ data root for application + SCIF_DATA scif defined data base for all apps (/scif/data) + SCIF_APPS scif defined install bases for all apps (/scif/apps) + SCIF_APPROOT_ root for application + SCIF_APPDATA_ data root for application For additional help, please visit our public documentation pages which are diff --git a/libexec/cli/build.exec b/libexec/cli/build.exec index d53e2ac2a7..985e6929eb 100644 --- a/libexec/cli/build.exec +++ b/libexec/cli/build.exec @@ -1,25 +1,25 @@ #!/bin/bash -# +# # Copyright (c) 2017, SingularityWare, LLC. All rights reserved. # # Copyright (c) 2015-2017, Gregory M. Kurtzer. All rights reserved. -# +# # Copyright (c) 2016-2017, The Regents of the University of California, # through Lawrence Berkeley National Laboratory (subject to receipt of any # required approvals from the U.S. Dept. of Energy). All rights reserved. -# +# # This software is licensed under a customized 3-clause BSD license. Please # consult LICENSE file distributed with the sources of this project regarding # your rights to use or distribute this software. -# +# # NOTICE. This Software was developed under funding from the U.S. Department of # Energy and the U.S. Government consequently retains certain rights. As such, # the U.S. Government has been granted for itself and others acting on its # behalf a paid-up, nonexclusive, irrevocable, worldwide license in the Software # to reproduce, distribute copies to the public, prepare derivative works, and -# perform publicly and display publicly, and to permit other to do so. -# -# +# perform publicly and display publicly, and to permit other to do so. +# +# set -e ## Basic sanity @@ -123,6 +123,10 @@ while true; do SINGULARITY_CHECKTAGS="${1:-}" shift; ;; + -i|--isolated) + SINGULARITY_ISOLATED_BUILD=1 + shift + ;; -*) message ERROR "Unknown option: ${1:-}\n" exit 1 @@ -177,7 +181,7 @@ if [ "$USERID" != "0" ]; then if [ -n "${SINGULARITY_SANDBOX:-}" ]; then # Sandbox with deffile is no go. - if eval is_deffile "${SINGULARITY_BUILDDEF}"; then + if eval is_deffile "${SINGULARITY_BUILDDEF}"; then message ERROR "You must be the root user to sandbox from a Singularity recipe file\n" exit 1 @@ -196,12 +200,12 @@ fi # if the container exists, build into it (or delete it if -F was passed) if [ -e "$SINGULARITY_CONTAINER_OUTPUT" ]; then if eval is_image "${SINGULARITY_CONTAINER_OUTPUT}"; then - if [ -z "${SINGULARITY_FORCE:-}" ]; then + if [ -z "${SINGULARITY_FORCE:-}" ]; then message 1 "Building into existing container: $SINGULARITY_CONTAINER_OUTPUT\n" SINGULARITY_BTSTRP_2EXISTING=1 elif [ -n "${SINGULARITY_FORCE:-}" ]; then - # this may be a directory, so rm -rf - rm -rf "$SINGULARITY_CONTAINER_OUTPUT" + # this may be a directory, so rm -rf + rm -rf "$SINGULARITY_CONTAINER_OUTPUT" 2> /dev/null fi else message ERROR "$SINGULARITY_CONTAINER_OUTPUT is not an FS image file\n" @@ -229,7 +233,7 @@ else fi SINGULARITY_IMAGE="$SINGULARITY_ROOTFS" - # create a temporary placeholder location to build file system + # create a temporary placeholder location to build file system if ! SINGULARITY_TEMP_FS=`mktemp "${SINGULARITY_TMPDIR:-/tmp}"/.singularity-temp-fs.XXXXXX`; then message ERROR "Failed to create temporary file system\n" ABORT 255 @@ -259,8 +263,9 @@ case $SINGULARITY_BUILDDEF in zcat "$SINGULARITY_libexecdir/singularity/bootstrap-scripts/environment.tar" | tar xBf - -C "${SINGULARITY_ROOTFS}" || exit $? for i in `cat "$SINGULARITY_CONTENTS"`; do - message 1 "Importing: $i\n" - zcat "$i" | (cd "$SINGULARITY_ROOTFS"; tar --exclude=dev/* -xf - ) || exit $? + name=`basename "$i"` + message 2 "Exploding layer: $name\n" + $SINGULARITY_libexecdir/singularity/bin/docker-extract "$i" done SINGULARITY_BUILDDEF="$SINGULARITY_ROOTFS" @@ -284,7 +289,7 @@ case $SINGULARITY_BUILDDEF in # relying on pull.py for error checking here ${SINGULARITY_libexecdir}/singularity/python/pull.py - # switch $SINGULARITY_CONTAINER from remote to local + # switch $SINGULARITY_CONTAINER from remote to local SINGULARITY_BUILDDEF=`cat $SINGULARITY_CONTENTS` SINGULARITY_CLEANUP=1 ;; @@ -309,7 +314,7 @@ if [ -f "${SINGULARITY_BUILDDEF:-}" ]; then ABORT 255 fi - elif eval is_deffile "${SINGULARITY_BUILDDEF}"; then + elif eval is_deffile "${SINGULARITY_BUILDDEF}"; then message 1 "Using container recipe deffile: $SINGULARITY_BUILDDEF\n" if [ "$USERID" != "0" ]; then message ERROR "You must be the root user to build from a Singularity recipe file\n" @@ -322,7 +327,11 @@ if [ -f "${SINGULARITY_BUILDDEF:-}" ]; then fi fi export SINGULARITY_BUILDDEF - eval_abort "$SINGULARITY_libexecdir/singularity/bin/builddef" + if [ -n "${SINGULARITY_ISOLATED_BUILD:-}" ]; then + eval_abort "$SINGULARITY_libexecdir/singularity/bootstrap-scripts/secbuild.sh" + else + eval_abort "$SINGULARITY_libexecdir/singularity/bin/builddef" + fi else message ERROR "Unsupported file type: $SINGULARITY_BUILDDEF\n" @@ -354,7 +363,7 @@ if [ -z "${SINGULARITY_SANDBOX:-}" ]; then if [ -z "${SINGULARITY_BTSTRP_2EXISTING:-}" ]; then CONTAINER_SIZE=`du -sm ${SINGULARITY_ROOTFS} | cut -f 1` - # using the let builtin instead of bc which is not guaranteed + # using the let builtin instead of bc which is not guaranteed let "IMAGE_SIZE=(${CONTAINER_SIZE:-768} * 125)/100" if [ "$IMAGE_SIZE" -lt 10 ]; then IMAGE_SIZE=10 diff --git a/libexec/cli/build.info b/libexec/cli/build.info index 6df5809dbf..3b9966c881 100644 --- a/libexec/cli/build.info +++ b/libexec/cli/build.info @@ -1,7 +1,7 @@ NAME="build" SECTION="management" SUMMARY="Build a new Singularity container" -USAGE="singularity [...] build [build options...] " +USAGE="singularity [...] build [build options...] " print_help() { cat</dev/null > $file +if [ $? != 0 ]; then + message ERROR "You don't have permission to add/drop capabilities\n" + exit 1 +fi + +echo "Authorized capabilities after changes for ${ctype:-} ${usergroup:-}" 1>&2 +singularity_print_capabilities $caps diff --git a/libexec/cli/capability.add.info b/libexec/cli/capability.add.info new file mode 100644 index 0000000000..e2657b873c --- /dev/null +++ b/libexec/cli/capability.add.info @@ -0,0 +1,68 @@ +NAME="capability.add" +GROUP="capability" +SUMMARY="Add capabilities for user/group" +USAGE="singularity [...] capability.add [add options...] " + +print_help() { +cat</dev/null > $file +if [ $? != 0 ]; then + message ERROR "You don't have permission to add/drop capabilities\n" + exit 1 +fi + +echo "Authorized capabilities after changes for ${ctype:-} ${usergroup:-}" 1>&2 +singularity_print_capabilities $caps diff --git a/libexec/cli/capability.drop.info b/libexec/cli/capability.drop.info new file mode 100644 index 0000000000..941fa39dde --- /dev/null +++ b/libexec/cli/capability.drop.info @@ -0,0 +1,68 @@ +NAME="capability.drop" +GROUP="capability" +SUMMARY="Drop capabilities for user/group" +USAGE="singularity [...] capability.drop [drop options...] " + +print_help() { +cat<&2 + for file in $SINGULARITY_sysconfdir/singularity/capabilities/{user,group}.*; do + if [ -f "$file" ]; then + caps=$(cat $file) + file=$(basename $file) + type=${file%%.*} + id=${file##*.} + if [ "$caps" != "0" ]; then + printf "%.16s\t[%s]\t" "$id" "$type" + singularity_print_capabilities $caps + fi + fi + done + exit 0 +fi + +if [ -z "${CAP_USERGROUP:-}" ]; then + message ERROR "No user or group specified\n" + exit 1 +fi + +ctype="${SWITCH:-}" +usergroup="${CAP_USERGROUP:-}" + +if [ -f "$SINGULARITY_sysconfdir/singularity/capabilities/${ctype:-}.${usergroup:-}" ]; then + caps=$(cat "$SINGULARITY_sysconfdir/singularity/capabilities/${ctype:-}.${usergroup:-}") +else + message ERROR "No capabilities defined for ${ctype:-} ${usergroup:-}\n" + exit 1 +fi + +if [ "$caps" = "0" ]; then + message 1 "No capabilities defined for ${ctype:-} ${usergroup:-}\n" + exit 0 +else + echo "Capabilities for $ctype $usergroup" 1>&2 +fi + +singularity_print_capabilities $caps diff --git a/libexec/cli/capability.list.info b/libexec/cli/capability.list.info new file mode 100644 index 0000000000..4cd4cc164c --- /dev/null +++ b/libexec/cli/capability.list.info @@ -0,0 +1,25 @@ +NAME="capability.list" +GROUP="capability" +SUMMARY="List authorized capabilities for the given user/group" +USAGE="singularity [...] capability.list [list options...]" + +print_help() { +cat< A home directory specification. spec can either be a src path or src:dest pair. src is the source path @@ -32,6 +33,7 @@ EXEC OPTIONS: --nv Enable experimental Nvidia support -o|--overlay Use a persistent overlayFS via a writable image -p|--pid Run container in a new PID namespace + --uts Run container in a new UTS namespace --pwd Initial working directory for payload process inside the container -S|--scratch Include a scratch directory within the container that @@ -44,6 +46,19 @@ EXEC OPTIONS: -w|--writable By default all Singularity containers are available as read only. This option makes the file system accessible as read/write. + --hostname Set container hostname + -f|--fakeroot Run container in a new user namespace as root uid/gid + --keep-privs Lets root user to keep all privileges (root only) + --no-privs Drop all privileges, for root user only as privilieges + are always dropped for users + --add-caps A comma separated capability list to add + Accepted formats: + - CAP_CHOWN,CAP_SETUID + - CHOWN,SETUID + - cap_chown,cap_setuid + - chown,setuid + --drop-caps A comma separated capability list to drop + --allow-setuid Allow setuid binaries in container (root only) CONTAINER FORMATS SUPPORTED: @@ -53,8 +68,8 @@ CONTAINER FORMATS SUPPORTED: *.tar* Tar archives are exploded to a temporary directory and run within that directory (and cleaned up after). The contents of the archive is a root file system with root - being in the current directory. Compression suffixes as - '.gz' and '.bz2' are supported. + being in the current directory. All compression + suffixes are supported. directory/ Container directories that contain a valid root file system. instance://* A local running instance of a container. (See the diff --git a/libexec/cli/help.exec b/libexec/cli/help.exec index 8158d1ab6c..34eb40cc0b 100644 --- a/libexec/cli/help.exec +++ b/libexec/cli/help.exec @@ -131,8 +131,11 @@ elif [ -e "${HELP_ASK}" ]; then export SINGULARITY_IMAGE SINGULARITY_MOUNTPOINT shift - if [ -z "${SINGULARITY_NOSUID:-}" -a -u "$SINGULARITY_libexecdir/singularity/bin/mount-suid" ]; then - eval "$SINGULARITY_libexecdir/singularity/bin/mount-suid" /bin/bash "$SINGULARITY_libexecdir/singularity/helpers/help.sh" + if [ -z "${SINGULARITY_NOSUID:-}" -a -u "$SINGULARITY_libexecdir/singularity/bin/wrapper-suid" ]; then + if [ -z "${SINGULARITY_COMMAND:-}" ]; then + export SINGULARITY_COMMAND="mount" + fi + eval "$SINGULARITY_libexecdir/singularity/bin/wrapper-suid" /bin/bash "$SINGULARITY_libexecdir/singularity/helpers/help.sh" RETVAL=$? elif [ -x "$SINGULARITY_libexecdir/singularity/bin/mount" ]; then eval "$SINGULARITY_libexecdir/singularity/bin/mount" /bin/bash "$SINGULARITY_libexecdir/singularity/helpers/help.sh" diff --git a/libexec/cli/image.export.exec b/libexec/cli/image.export.exec index c906372b8c..dc0414e802 100644 --- a/libexec/cli/image.export.exec +++ b/libexec/cli/image.export.exec @@ -82,8 +82,8 @@ if [ -n "${1:-}" ]; then fi -if [ -z "${SINGULARITY_NOSUID:-}" -a -u "$SINGULARITY_libexecdir/singularity/bin/mount-suid" ]; then - exec "$SINGULARITY_libexecdir/singularity/bin/mount-suid" /bin/bash "$SINGULARITY_libexecdir/singularity/helpers/image.sh" +if [ -z "${SINGULARITY_NOSUID:-}" -a -u "$SINGULARITY_libexecdir/singularity/bin/wrapper-suid" ]; then + exec "$SINGULARITY_libexecdir/singularity/bin/wrapper-suid" /bin/bash "$SINGULARITY_libexecdir/singularity/helpers/image.sh" elif [ -x "$SINGULARITY_libexecdir/singularity/bin/mount" ]; then exec "$SINGULARITY_libexecdir/singularity/bin/mount" /bin/bash "$SINGULARITY_libexecdir/singularity/helpers/image.sh" else diff --git a/libexec/cli/image.import.exec b/libexec/cli/image.import.exec index 43cf750e4b..76d602cfaa 100644 --- a/libexec/cli/image.import.exec +++ b/libexec/cli/image.import.exec @@ -82,8 +82,8 @@ if [ -n "${1:-}" ]; then fi -if [ -z "${SINGULARITY_NOSUID:-}" -a -u "$SINGULARITY_libexecdir/singularity/bin/mount-suid" ]; then - exec "$SINGULARITY_libexecdir/singularity/bin/mount-suid" /bin/bash "$SINGULARITY_libexecdir/singularity/helpers/image.sh" "$@" +if [ -z "${SINGULARITY_NOSUID:-}" -a -u "$SINGULARITY_libexecdir/singularity/bin/wrapper-suid" ]; then + exec "$SINGULARITY_libexecdir/singularity/bin/wrapper-suid" /bin/bash "$SINGULARITY_libexecdir/singularity/helpers/image.sh" "$@" elif [ -x "$SINGULARITY_libexecdir/singularity/bin/mount" ]; then exec "$SINGULARITY_libexecdir/singularity/bin/mount" /bin/bash "$SINGULARITY_libexecdir/singularity/helpers/image.sh" "$@" diff --git a/libexec/cli/inspect.exec b/libexec/cli/inspect.exec index e9d09900f5..c45687132d 100644 --- a/libexec/cli/inspect.exec +++ b/libexec/cli/inspect.exec @@ -103,8 +103,8 @@ fi export SINGULARITY_IMAGE SINGULARITY_MOUNTPOINT shift -if [ -z "${SINGULARITY_NOSUID:-}" -a -u "$SINGULARITY_libexecdir/singularity/bin/mount-suid" ]; then - eval "$SINGULARITY_libexecdir/singularity/bin/mount-suid" /bin/bash "$SINGULARITY_libexecdir/singularity/helpers/inspect.sh" +if [ -z "${SINGULARITY_NOSUID:-}" -a -u "$SINGULARITY_libexecdir/singularity/bin/wrapper-suid" ]; then + eval "$SINGULARITY_libexecdir/singularity/bin/wrapper-suid" /bin/bash "$SINGULARITY_libexecdir/singularity/helpers/inspect.sh" RETVAL=$? elif [ -x "$SINGULARITY_libexecdir/singularity/bin/mount" ]; then eval "$SINGULARITY_libexecdir/singularity/bin/mount" /bin/bash "$SINGULARITY_libexecdir/singularity/helpers/inspect.sh" diff --git a/libexec/cli/instance.list.info b/libexec/cli/instance.list.info index 2c5b712806..542c541b7b 100644 --- a/libexec/cli/instance.list.info +++ b/libexec/cli/instance.list.info @@ -14,19 +14,13 @@ LIST OPTIONS: EXAMPLES: $ singularity instance.list - /home/mibauer/singularity/sinstance/test.img [2056.132716] Instances: - Instance PID: 11963 - Instance Name: test + DAEMON NAME PID CONTAINER IMAGE + test 11963 /home/mibauer/singularity/sinstance/test.img $ sudo singularity instance.list -u mibauer - /home/mibauer/singularity/sinstance/test.img [2056.132716] Instances: - Instance PID: 16219 - Instance Name: test - - Instance PID: 11963 - Instance Name: test - - + DAEMON NAME PID CONTAINER IMAGE + test 11963 /home/mibauer/singularity/sinstance/test.img + test2 16219 /home/mibauer/singularity/sinstance/test.img For additional help, please visit our public documentation pages which are found at: diff --git a/libexec/cli/instance.start.exec b/libexec/cli/instance.start.exec index 5ba9580b02..96c09d4bc7 100644 --- a/libexec/cli/instance.start.exec +++ b/libexec/cli/instance.start.exec @@ -23,6 +23,15 @@ else exit 1 fi +## Load capabilities +if [ -f "$SINGULARITY_libexecdir/singularity/capabilities" ]; then + . "$SINGULARITY_libexecdir/singularity/capabilities" + singularity_get_env_capabilities +else + echo "Error loading capabilities: $SINGULARITY_libexecdir/singularity/capabilities" + exit 1 +fi + if [ -f "${HOME:-}/.singularity-init" ]; then . "${HOME:-}/.singularity-init" fi @@ -84,7 +93,7 @@ while true; do ABORT 255 fi ;; - -n|--nv) + --nv) shift for i in `ldconfig -p | grep -E "/libnv|/libcuda|/libEGL|/libGL|/libnvcu|/libvdpau|/libOpenCL|/libOpenGL"`; do if [ -f "$i" ]; then @@ -117,6 +126,90 @@ while true; do SINGULARITY_UNSHARE_NET=1 export SINGULARITY_UNSHARE_NET ;; + --uts) + shift + SINGULARITY_UNSHARE_UTS=1 + export SINGULARITY_UNSHARE_UTS + ;; + --hostname) + shift + SINGULARITY_UNSHARE_UTS=1 + SINGULARITY_HOSTNAME="$1" + export SINGULARITY_UNSHARE_UTS SINGULARITY_HOSTNAME + shift + ;; + --boot) + shift + if [ "${USERID}" = "0" ]; then + SINGULARITY_INSTANCE_BOOT=1 + SINGULARITY_UNSHARE_NET=1 + SINGULARITY_UNSHARE_UTS=1 + SINGULARITY_KEEP_PRIVS=1 + singularity_drop_capabilities "CAP_SYS_BOOT,CAP_SYS_RAWIO" + export SINGULARITY_INSTANCE_BOOT SINGULARITY_UNSHARE_NET SINGULARITY_KEEP_PRIVS SINGULARITY_UNSHARE_UTS + else + message ERROR "boot option requires root privileges\n" + exit 1 + fi + ;; + -f|--fakeroot) + shift + SINGULARITY_NOSUID=1 + SINGULARITY_USERNS_UID=0 + SINGULARITY_USERNS_GID=0 + export SINGULARITY_USERNS_UID SINGULARITY_USERNS_GID SINGULARITY_NOSUID + ;; + -u|--user|--userns) + shift + SINGULARITY_NOSUID=1 + export SINGULARITY_NOSUID + ;; + --keep-privs) + if [ "$(id -ru)" = "0" ]; then + if [ "$(singularity_config_value 'allow root capabilities')" != "yes" ]; then + message ERROR "keep-privs is disabled when allow root capabilities directive is set to no\n" + exit 1 + fi + + SINGULARITY_KEEP_PRIVS=1 + export SINGULARITY_KEEP_PRIVS + message 4 "Requesting keep privileges\n" + else + message WARNING "Keeping privileges is for root only\n" + fi + shift + ;; + --no-privs) + if [ "$(id -ru)" = "0" ]; then + SINGULARITY_NO_PRIVS=1 + export SINGULARITY_NO_PRIVS + message 4 "Requesting no privileges\n" + fi + shift + ;; + --add-caps) + shift + singularity_add_capabilities "$1" + shift + ;; + --drop-caps) + shift + singularity_drop_capabilities "$1" + shift + ;; + --allow-setuid) + shift + if [ "$(id -ru)" != "0" ]; then + message ERROR "allow-setuid is for root only\n" + exit 1 + fi + if [ "$(singularity_config_value 'allow root capabilities')" != "yes" ]; then + message ERROR "allow-setuid is disabled when allow root capabilities directive is set to no\n" + exit 1 + fi + SINGULARITY_ALLOW_SETUID=1 + export SINGULARITY_ALLOW_SETUID + ;; -*) message ERROR "Unknown option: ${1:-}\n" exit 1 @@ -134,7 +227,6 @@ else ABORT 255 fi - if [ -z "${SINGULARITY_IMAGE:-}" ]; then SINGULARITY_IMAGE="${1:-}" export SINGULARITY_IMAGE @@ -212,8 +304,9 @@ if [ -x "$SINGULARITY_libexecdir/singularity/image-handler.sh" ]; then . "$SINGULARITY_libexecdir/singularity/image-handler.sh" fi -if [ -z "${SINGULARITY_NOSUID:-}" -a -u "$SINGULARITY_libexecdir/singularity/bin/start-suid" ]; then - exec -a "$INSTANCE_PROC_NAME" "$SINGULARITY_libexecdir/singularity/bin/start-suid" "$@" <&0 + +if [ -z "${SINGULARITY_NOSUID:-}" -a -u "$SINGULARITY_libexecdir/singularity/bin/wrapper-suid" ]; then + exec -a "$INSTANCE_PROC_NAME" "$SINGULARITY_libexecdir/singularity/bin/wrapper-suid" "$@" <&0 elif [ -x "$SINGULARITY_libexecdir/singularity/bin/start" ]; then exec -a "$INSTANCE_PROC_NAME" "$SINGULARITY_libexecdir/singularity/bin/start" "$@" <&0 else diff --git a/libexec/cli/instance.start.info b/libexec/cli/instance.start.info index 3900f4079e..3ab3e62d65 100644 --- a/libexec/cli/instance.start.info +++ b/libexec/cli/instance.start.info @@ -24,6 +24,7 @@ START OPTIONS: overrides the home directory within the container -n|--net Run container in a new network namespace (loopback is only network device active) + --uts Run container in a new UTS namespace --nv Enable experimental Nvidia support -o|--overlay Use a persistent overlayFS via a writable image -S|--scratch Include a scratch directory within the container that @@ -33,6 +34,24 @@ START OPTIONS: -w|--writable By default all Singularity containers are available as read only. This option makes the file system accessible as read/write. + -u|--userns Run container in a new user namespace (this allows + Singularity to run completely unprivileged on recent + kernels and doesn't support all features) + --hostname Set container hostname + --boot By default instance execute startscript, this option + turn off this behaviour and execute /sbin/init (root only) + -f|--fakeroot Run container in a new user namespace as root uid/gid + --keep-privs Lets root user to keep all privileges (root only) + --no-privs Drop all privileges, for root user only as privilieges + are always dropped for users + --add-caps A comma separated capability list to add + Accepted formats: + - CAP_CHOWN,CAP_SETUID + - CHOWN,SETUID + - cap_chown,cap_setuid + - chown,setuid + --drop-caps A comma separated capability list to drop + --allow-setuid Allow setuid binaries in container (root only) CONTAINER FORMATS SUPPORTED: @@ -42,8 +61,8 @@ CONTAINER FORMATS SUPPORTED: *.tar* Tar archives are exploded to a temporary directory and run within that directory (and cleaned up after). The contents of the archive is a root file system with root - being in the current directory. Compression suffixes as - '.gz' and '.bz2' are supported. + being in the current directory. All compression + suffixes are supported. directory/ Container directories that contain a valid root file system. diff --git a/libexec/cli/instance.stop.exec b/libexec/cli/instance.stop.exec index 48b72173aa..0ddeb7dffc 100644 --- a/libexec/cli/instance.stop.exec +++ b/libexec/cli/instance.stop.exec @@ -34,7 +34,8 @@ if ! USERID=`id -ru`; then exit 255 fi -KILL_VAL="15" +# kill instances with SIGINT by default, for init systems it's equal to Ctrl+Alt+Del +KILL_VAL="SIGINT" KILL_WAIT="10" while true; do diff --git a/libexec/cli/mount.exec b/libexec/cli/mount.exec index 21b6eaf964..f293727ed2 100644 --- a/libexec/cli/mount.exec +++ b/libexec/cli/mount.exec @@ -85,8 +85,8 @@ SINGULARITY_IMAGE="${1:-}" export SINGULARITY_IMAGE shift -if [ -z "${SINGULARITY_NOSUID:-}" -a -u "$SINGULARITY_libexecdir/singularity/bin/mount-suid" ]; then - exec "$SINGULARITY_libexecdir/singularity/bin/mount-suid" "$@" +if [ -z "${SINGULARITY_NOSUID:-}" -a -u "$SINGULARITY_libexecdir/singularity/bin/wrapper-suid" ]; then + exec "$SINGULARITY_libexecdir/singularity/bin/wrapper-suid" "$@" elif [ -x "$SINGULARITY_libexecdir/singularity/bin/mount" ]; then exec "$SINGULARITY_libexecdir/singularity/bin/mount" "$@" else diff --git a/libexec/cli/run.exec b/libexec/cli/run.exec index d7b2896184..b8f185a7dc 100644 --- a/libexec/cli/run.exec +++ b/libexec/cli/run.exec @@ -75,8 +75,8 @@ if [ -x "$SINGULARITY_libexecdir/singularity/image-handler.sh" ]; then fi -if [ -z "${SINGULARITY_NOSUID:-}" -a -u "$SINGULARITY_libexecdir/singularity/bin/action-suid" ]; then - exec "$SINGULARITY_libexecdir/singularity/bin/action-suid" "$@" <&0 +if [ -z "${SINGULARITY_NOSUID:-}" -a -u "$SINGULARITY_libexecdir/singularity/bin/wrapper-suid" ]; then + exec "$SINGULARITY_libexecdir/singularity/bin/wrapper-suid" "$@" <&0 elif [ -x "$SINGULARITY_libexecdir/singularity/bin/action" ]; then exec "$SINGULARITY_libexecdir/singularity/bin/action" "$@" <&0 else diff --git a/libexec/cli/run.info b/libexec/cli/run.info index 4112e5dda4..4d9f343345 100644 --- a/libexec/cli/run.info +++ b/libexec/cli/run.info @@ -25,7 +25,8 @@ RUN OPTIONS: default). This option can be called multiple times. -c|--contain Use minimal /dev and empty other directories (e.g. /tmp and $HOME) instead of sharing filesystems on your host - -C|--containall Contain not only file systems, but also PID and IPC + -C|--containall Contain not only file systems, but also PID, IPC, and + environment -e|--cleanenv Clean environment before running container -H|--home A home directory specification. spec can either be a src path or src:dest pair. src is the source path @@ -37,6 +38,7 @@ RUN OPTIONS: --nv Enable experimental Nvidia support -o|--overlay Use a persistent overlayFS via a writable image -p|--pid Run container in a new PID namespace + --uts Run container in a new UTS namespace --pwd Initial working directory for payload process inside the container -S|--scratch Include a scratch directory within the container that @@ -49,6 +51,20 @@ RUN OPTIONS: -w|--writable By default all Singularity containers are available as read only. This option makes the file system accessible as read/write. + --hostname Set container hostname + -f|--fakeroot Run container in a new user namespace as root uid/gid + --keep-privs Lets root user to keep all privileges (root only) + --no-privs Drop all privileges, for root user only as privilieges + are always dropped for users + --add-caps A comma separated capability list to add + Accepted formats: + - CAP_CHOWN,CAP_SETUID + - CHOWN,SETUID + - cap_chown,cap_setuid + - chown,setuid + --drop-caps A comma separated capability list to drop + --allow-setuid Allow setuid binaries in container (root only) + CONTAINER FORMATS SUPPORTED: *.sqsh SquashFS format. Native to Singularity 2.4+ @@ -57,8 +73,8 @@ CONTAINER FORMATS SUPPORTED: *.tar* Tar archives are exploded to a temporary directory and run within that directory (and cleaned up after). The contents of the archive is a root file system with root - being in the current directory. Compression suffixes as - '.gz' and '.bz2' are supported. + being in the current directory. All compression + suffixes are supported. directory/ Container directories that contain a valid root file system. instance://* A local running instance of a container. (See the diff --git a/libexec/cli/selftest.exec b/libexec/cli/selftest.exec index 009befd3b3..fefd451255 100644 --- a/libexec/cli/selftest.exec +++ b/libexec/cli/selftest.exec @@ -52,9 +52,7 @@ SINGULARITY_MESSAGELEVEL=5 SUID_FILES=" - $SINGULARITY_libexecdir/singularity/bin/action-suid - $SINGULARITY_libexecdir/singularity/bin/mount-suid - $SINGULARITY_libexecdir/singularity/bin/start-suid + $SINGULARITY_libexecdir/singularity/bin/wrapper-suid " stest 0 sh -c "test -f $SINGULARITY_sysconfdir/singularity/singularity.conf" diff --git a/libexec/cli/shell.exec b/libexec/cli/shell.exec index 3309c35c18..d4078a88e7 100644 --- a/libexec/cli/shell.exec +++ b/libexec/cli/shell.exec @@ -74,8 +74,8 @@ if [ -x "$SINGULARITY_libexecdir/singularity/image-handler.sh" ]; then fi -if [ -z "${SINGULARITY_NOSUID:-}" -a -u "$SINGULARITY_libexecdir/singularity/bin/action-suid" ]; then - exec "$SINGULARITY_libexecdir/singularity/bin/action-suid" "$@" <&0 +if [ -z "${SINGULARITY_NOSUID:-}" -a -u "$SINGULARITY_libexecdir/singularity/bin/wrapper-suid" ]; then + exec "$SINGULARITY_libexecdir/singularity/bin/wrapper-suid" "$@" <&0 elif [ -x "$SINGULARITY_libexecdir/singularity/bin/action" ]; then exec "$SINGULARITY_libexecdir/singularity/bin/action" "$@" <&0 else diff --git a/libexec/cli/shell.info b/libexec/cli/shell.info index dfb5373172..27091d6202 100644 --- a/libexec/cli/shell.info +++ b/libexec/cli/shell.info @@ -19,7 +19,8 @@ SHELL OPTIONS: default). This option can be called multiple times. -c|--contain Use minimal /dev and empty other directories (e.g. /tmp and $HOME) instead of sharing filesystems on your host - -C|--containall Contain not only file systems, but also PID and IPC + -C|--containall Contain not only file systems, but also PID, IPC, and + environment -e|--cleanenv Clean environment before running container -H|--home A home directory specification. spec can either be a src path or src:dest pair. src is the source path @@ -33,6 +34,7 @@ SHELL OPTIONS: --nv Enable experimental Nvidia support -o|--overlay Use a persistent overlayFS via a writable image -p|--pid Run container in a new PID namespace (creates child) + --uts Run container in a new UTS namespace --pwd Initial working directory for payload process inside the container -S|--scratch Include a scratch directory within the container that @@ -46,6 +48,19 @@ SHELL OPTIONS: -w|--writable By default all Singularity containers are available as read only. This option makes the file system accessible as read/write. + --hostname Set container hostname + -f|--fakeroot Run container in a new user namespace as root uid/gid + --keep-privs Lets root user to keep all privileges (root only) + --no-privs Drop all privileges, for root user only as privilieges + are always dropped for users + --add-caps A comma separated capability list to add + Accepted formats: + - CAP_CHOWN,CAP_SETUID + - CHOWN,SETUID + - cap_chown,cap_setuid + - chown,setuid + --drop-caps A comma separated capability list to drop + --allow-setuid Allow setuid binaries in container (root only) CONTAINER FORMATS SUPPORTED: @@ -55,8 +70,8 @@ CONTAINER FORMATS SUPPORTED: *.tar* Tar archives are exploded to a temporary directory and run within that directory (and cleaned up after). The contents of the archive is a root file system with root - being in the current directory. Compression suffixes as - '.gz' and '.bz2' are supported. + being in the current directory. All compression + suffixes are supported. directory/ Container directories that contain a valid root file system. instance://* A local running instance of a container. (See the diff --git a/libexec/cli/test.exec b/libexec/cli/test.exec index 926c5ccd4b..a762176fe7 100644 --- a/libexec/cli/test.exec +++ b/libexec/cli/test.exec @@ -86,8 +86,8 @@ if [ -x "$SINGULARITY_libexecdir/singularity/image-handler.sh" ]; then fi -if [ -z "${SINGULARITY_NOSUID:-}" -a -u "$SINGULARITY_libexecdir/singularity/bin/action-suid" ]; then - exec "$SINGULARITY_libexecdir/singularity/bin/action-suid" "$@" <&0 +if [ -z "${SINGULARITY_NOSUID:-}" -a -u "$SINGULARITY_libexecdir/singularity/bin/wrapper-suid" ]; then + exec "$SINGULARITY_libexecdir/singularity/bin/wrapper-suid" "$@" <&0 elif [ -x "$SINGULARITY_libexecdir/singularity/bin/action" ]; then exec "$SINGULARITY_libexecdir/singularity/bin/action" "$@" <&0 else diff --git a/libexec/functions b/libexec/functions index 3a929b1e5a..f6bf19ed7a 100644 --- a/libexec/functions +++ b/libexec/functions @@ -24,6 +24,11 @@ if [ -z "${SINGULARITY_libexecdir:-}" ]; then exit 2 fi +if [ -z "${SINGULARITY_sysconfdir:-}" ]; then + echo "ERROR: SINGULARITY_sysconfdir not defined in environment" + exit 2 +fi + if [ -z "${SINGULARITY_MESSAGELEVEL:-}" ]; then echo "Warning: SINGULARITY_MESSAGELEVEL is undefined, temporarily setting to '5' (all messages)" SINGULARITY_MESSAGELEVEL=5 @@ -193,6 +198,24 @@ singularity_calculate_size() { return 0 } +singularity_add_caps() { + if [ -n "${SINGULARITY_ADD_CAPS:-}" ]; then + SINGULARITY_ADD_CAPS="$SINGULARITY_ADD_CAPS,$1" + else + SINGULARITY_ADD_CAPS="$1" + fi + export SINGULARITY_ADD_CAPS +} + +singularity_drop_caps() { + if [ -n "${SINGULARITY_DROP_CAPS:-}" ]; then + SINGULARITY_DROP_CAPS="$SINGULARITY_DROP_CAPS,$1" + else + SINGULARITY_DROP_CAPS="$1" + fi + export SINGULARITY_DROP_CAPS +} + ABORT() { RETVAL="${1:-}" if [ -z "$RETVAL" ]; then @@ -345,12 +368,12 @@ compare_envs() { if [ -n "${REMOVED:-}" -o -n "${ADDED:-}" ]; then message 1 "Environment variables were added, removed, and/or changed during bootstrap.\n" elif [ "${SINGULARITY_STARTING_ENVSHA1:-}" != "${SINGULARITY_ENDING_ENVSHA1:-}" ]; then - message 1 "The environment of ${SINGULARITY_IMAGE} may differ from the environment of ${FROM}\n" - message 1 "${FROM} sha1 sum of environment is ${SINGULARITY_STARTING_ENVSHA1}\n" + message 1 "The environment of ${SINGULARITY_IMAGE} may differ from the environment of ${SINGULARITY_DEFFILE_FROM}\n" + message 1 "${SINGULARITY_DEFFILE_FROM} sha1 sum of environment is ${SINGULARITY_STARTING_ENVSHA1}\n" message 1 "${SINGULARITY_IMAGE} sha1 sum of environment is ${SINGULARITY_ENDING_ENVSHA1}\n" fi if [ -n "${REMOVED:-}" ]; then - message 1 "Variables unique to original image (${FROM})\n" + message 1 "Variables unique to original image (${SINGULARITY_DEFFILE_FROM})\n" message 1 "$REMOVED\n" fi if [ -n "${ADDED:-}" ]; then @@ -451,6 +474,81 @@ is_tar() { } +bind_nvidia_files() { +# either use nvidia-container-cli or nvliblist.conf to find nvidia libs and +# binaries for binding at runtime with --nv + if ! SINGULARITY_NVLIBLIST=`mktemp "${TMPDIR:-/tmp}"/.singularity-nvliblist.XXXXXXXX`; then + message ERROR "Failed to create temporary nvliblist file.\n" + ABORT 255 + fi + + # check to see if nvidia-container-cli is producing plausible output + if ! singularity_which nvidia-container-cli > /dev/null 2>&1 || ! SINGULARITY_NV_FILES=`nvidia-container-cli list -cguv`; then + message 2 "nvidia-container-cli not installed or not working\n" + SINGULARITY_USE_NVLIBLIST_CONF=1 + else + for i in $SINGULARITY_NV_FILES; do + if [ ! -f $i ]; then + message 2 "nvidia-container-cli is returning unusable output\n" + SINGULARITY_USE_NVLIBLIST_CONF=1 + fi + done + fi + + # first choice use nvidia-container-cli + if [ -z ${SINGULARITY_USE_NVLIBLIST_CONF:-} ]; then + message 2 "Using nvidia-container-cli to locate nvidia libraries and binaries.\n" + # bins should have full paths + SINGULARITY_NVBINLIST=`echo "$SINGULARITY_NV_FILES" | grep -Ev "\.so"` + # libs should just be basenames ending in ".so" + for i in `echo "$SINGULARITY_NV_FILES" | grep -E "\.so"`; do + basename "$i" | awk -F "\.so" '{print $1 ".so"}' >> "$SINGULARITY_NVLIBLIST" + done + + # if it's absent, or broken fall back to nvliblist.conf + else [ -n ${USE_NVLIBLIST_CONF:-} ] + message 2 "Falling back to $SINGULARITY_sysconfdir/singularity/nvliblist.conf\n" + # strip comments and spaces and bins do NOT end in .so + SINGULARITY_NVBINLIST=`cat $SINGULARITY_sysconfdir"/singularity/nvliblist.conf" | grep -Ev "^#|^\s*$" | grep -vE "\.so$"` + # strip comments and spaces and libs DO end in .so + cat "${SINGULARITY_sysconfdir}/singularity/nvliblist.conf" | grep -Ev "^#|^\s*$" | grep -E "\.so$" > $SINGULARITY_NVLIBLIST + fi + + # first deal with libs (pattern match from ld.so cache) + for i in $(ldconfig -p | grep -f "${SINGULARITY_NVLIBLIST}"); do + if [ -f "$i" ]; then + message 2 "Found NV library: $i\n" + SINGULARITY_CONTAINLIBS="${SINGULARITY_CONTAINLIBS:-},$i" + fi + done + rm $SINGULARITY_NVLIBLIST + + # then deal with bins (should have full path specified) + for SINGULARITY_NVBIN in $SINGULARITY_NVBINLIST; do + if [ -f "$SINGULARITY_NVBIN" ]; then + message 2 "Found NV binary: $SINGULARITY_NVBIN\n" + SINGULARITY_BIN_FOUND=1 + SINGULARITY_BINDPATH="${SINGULARITY_BINDPATH:-},${SINGULARITY_NVBIN}" + fi + done + + # in case we couldn't find files, warn the user. otherwise export variables + if [ -z "${SINGULARITY_CONTAINLIBS:-}" ]; then + message WARNING "Could not find any NVIDIA libraries on this host!\n"; + message WARNING "You may need to edit ${SINGULARITY_sysconfdir}/singularity/nvliblist.conf\n" + else + export SINGULARITY_CONTAINLIBS + fi + + if [ -z "${SINGULARITY_BIN_FOUND:-}" ]; then + message WARNING "Could not find any NVIDIA binaries on this host!\n"; + message WARNING "You may need to edit ${SINGULARITY_sysconfdir}/singularity/nvliblist.conf\n" + else + export SINGULARITY_BINDPATH + fi +} + + ############################################################################## # CHECKS ############################################################################## @@ -540,6 +638,18 @@ atexit() { trap "$1" EXIT } +singularity_config_value() { + if [ ! -z "${SINGULARITY_CONFIG_FILE:-}" ]; then + config="${SINGULARITY_CONFIG_FILE:-}" + else + config="${SINGULARITY_sysconfdir:-}/singularity/singularity.conf" + fi + + value=$(sed -n "s/^\s*$1[ \t]*=[ \t]*\(.*\)/\1/p" $config) + echo $value +} + if [ -n "${SHELL_DEBUG:-}" ]; then set -x fi + diff --git a/libexec/handlers/archive-tar.sh b/libexec/handlers/archive-tar.sh index 498cbde06b..826c0d76dd 100644 --- a/libexec/handlers/archive-tar.sh +++ b/libexec/handlers/archive-tar.sh @@ -24,35 +24,10 @@ if ! mkdir -p "$CONTAINER_DIR"; then ABORT 255 fi - -case "$SINGULARITY_IMAGE" in - - *.tar) - - message 1 "Opening tar archive, stand by...\n" - # this almost always gives permission errors, so ignore them when - # running as a user. - tar -C "$CONTAINER_DIR" -xf "$SINGULARITY_IMAGE" 2>/dev/null - - ;; - *.tgz|*.tar.gz) - - message 1 "Opening gzip compressed archive, stand by...\n" - - # this almost always gives permission errors, so ignore them when - # running as a user. - tar -C "$CONTAINER_DIR" -xzf "$SINGULARITY_IMAGE" 2>/dev/null - - ;; - *.tbz|*.tar.bz) - - message 1 "Opening bzip compressed archive, stand by...\n" - # this almost always gives permission errors, so ignore them when - # running as a user. - tar -C "$CONTAINER_DIR" -xjf "$SINGULARITY_IMAGE" 2>/dev/null - - ;; -esac +message 1 "Opening tar archive, stand by...\n" +# this almost always gives permission errors, so ignore them when +# running as a user. +tar -C "$CONTAINER_DIR" -xf "$SINGULARITY_IMAGE" 2>/dev/null chmod -R +w "$CONTAINER_DIR" diff --git a/libexec/handlers/image-docker.sh b/libexec/handlers/image-docker.sh index 88dda37283..ea0246f7a4 100644 --- a/libexec/handlers/image-docker.sh +++ b/libexec/handlers/image-docker.sh @@ -40,18 +40,7 @@ zcat $SINGULARITY_libexecdir/singularity/bootstrap-scripts/environment.tar | (cd for i in `cat "$SINGULARITY_CONTENTS"`; do name=`basename "$i"` message 2 "Exploding layer: $name\n" - # Settings of file privileges must be buffered - files=$( zcat "$i" | (cd "$SINGULARITY_ROOTFS"; tar --overwrite --exclude=dev/* -xvf -)) || exit $? - for file in $files; do - if [ -L "$SINGULARITY_ROOTFS/$file" ]; then - # Skipping symlinks - true - elif [ -f "$SINGULARITY_ROOTFS/$file" ]; then - chmod u+rw "$SINGULARITY_ROOTFS/$file" >/dev/null 2>&1 - elif [ -d "$SINGULARITY_ROOTFS/$file" ]; then - chmod u+rwx "$SINGULARITY_ROOTFS/${file%/}" >/dev/null 2>&1 - fi - done + $SINGULARITY_libexecdir/singularity/bin/docker-extract "$i" done rm -f "$SINGULARITY_CONTENTS" diff --git a/libexec/handlers/image-instance.sh b/libexec/handlers/image-instance.sh index 1907826f49..a5e2ef817f 100644 --- a/libexec/handlers/image-instance.sh +++ b/libexec/handlers/image-instance.sh @@ -26,6 +26,39 @@ if [ ! -f "${DAEMON_IMAGE}" -a ! -d "${DAEMON_IMAGE}" ]; then ABORT 255 fi +if [ -z "${DAEMON_PID}" ]; then + message ERROR "No PID found for daemon process\n" + ABORT 255 +fi + +# determine is we need to enter user namespace +self_inode=$(stat -L -c "%i" /proc/self/ns/user 2>/dev/null) +daemon_inode=$(stat -L -c "%i" /proc/$DAEMON_PID/ns/user 2>/dev/null) +if [ $? = 0 ]; then + if [ ! -z "$self_inode" -a ! -z "$daemon_inode" ]; then + if [ "$self_inode" != "$daemon_inode" ]; then + SINGULARITY_NOSUID=1 + export SINGULARITY_NOSUID + fi + fi +fi + +if [ ! -z "${ADD_CAPS:-}" ]; then + export SINGULARITY_ADD_CAPS="${ADD_CAPS}" +fi + +if [ ! -z "${DROP_CAPS:-}" ]; then + export SINGULARITY_DROP_CAPS="${DROP_CAPS}" +fi + +if [ ! -z "${KEEP_PRIVS:-}" ]; then + export SINGULARITY_KEEP_PRIVS="1" +fi + +if [ ! -z "${NO_PRIVS:-}" ]; then + export SINGULARITY_NO_PRIVS="1" +fi + SINGULARITY_IMAGE="${DAEMON_IMAGE}" SINGULARITY_DAEMON_JOIN=1 export SINGULARITY_DAEMON_JOIN SINGULARITY_IMAGE diff --git a/libexec/image-handler.sh b/libexec/image-handler.sh index f9cf21f9c7..e4ecb162ea 100644 --- a/libexec/image-handler.sh +++ b/libexec/image-handler.sh @@ -67,7 +67,8 @@ case "$SINGULARITY_IMAGE" in ;; - *.tar|*.tgz|*.tar.gz|*.tbz|*.tar.bz) + *.tar|*.tgz|*.tar.gz|*.tbz|*.tbz2|*.tb2|*.tz2|*.tar.bz|*.tar.bz2|*.txz|*.tar.xz|\ + *.tar.lz|*.tlz|*.tar.lzma|*.tar.Z|*.tZ|*.lzo) . "$SINGULARITY_libexecdir/singularity/handlers/archive-tar.sh" diff --git a/libexec/python/Makefile.am b/libexec/python/Makefile.am index ebfd32fac9..a1e9f2ffab 100644 --- a/libexec/python/Makefile.am +++ b/libexec/python/Makefile.am @@ -6,4 +6,7 @@ scriptlibexecdir = $(libexecdir)/singularity/python dist_scriptlibexec_DATA = defaults.py __init__.py message.py shell.py sutils.py base.py templates.py dist_scriptlibexec_SCRIPTS = import.py pull.py size.py -MAINTAINERCLEANFILES = Makefile.in +MAINTAINERCLEANFILES = Makefile.in *.pyc +DISTCLEANFILES = Makefile *.pyc +CLEANFILES = *.pyc + diff --git a/man/Makefile.am b/man/Makefile.am index def20c3f4a..7a2b8050eb 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -1 +1,3 @@ +MAINTAINERCLEANFILES = Makefile.in + dist_man_MANS = singularity.1 diff --git a/secbuildimg.sh.in b/secbuildimg.sh.in new file mode 100755 index 0000000000..6631e57108 --- /dev/null +++ b/secbuildimg.sh.in @@ -0,0 +1,82 @@ +#!/bin/bash +# +# Copyright (c) 2017, SingularityWare, LLC. All rights reserved. +# +# See the COPYRIGHT.md file at the top-level directory of this distribution and at +# https://github.com/singularityware/singularity/blob/master/COPYRIGHT.md. +# +# This file is part of the Singularity Linux container project. It is subject to the license +# terms in the LICENSE.md file found in the top-level directory of this distribution and +# at https://github.com/singularityware/singularity/blob/master/LICENSE.md. No part +# of Singularity, including this file, may be copied, modified, propagated, or distributed +# except according to the terms contained in the LICENSE.md file. +# +# + + +prefix="@prefix@" +exec_prefix="@exec_prefix@" +libexecdir="@libexecdir@" +sysconfdir="@sysconfdir@" +localstatedir="@localstatedir@" +bindir="@bindir@" + +SINGULARITY_libexecdir="$libexecdir" +SINGULARITY_PATH="$bindir" + +if [ ! -x "$SINGULARITY_PATH/singularity" ]; then + /bin/echo "ERROR: Could not locate singularity program at: $SINGULARITY_PATH/singularity" + exit 1 +fi + +SECBUILD_IMAGE="$SINGULARITY_libexecdir/singularity/bootstrap-scripts/secbuild.img" + +if [ $(id -ru) != 0 ]; then + echo + echo "You must be root to install" + echo + exit 1 +fi + +if [ -e "$SECBUILD_IMAGE" ]; then + rm -rf $SECBUILD_IMAGE +fi + +BUILDTMP=$(mktemp -d) +SECBUILD_SINGULARITY="$BUILDTMP/singularity" +SECBUILD_DEFFILE="$BUILDTMP/secbuild.def" + +cat > "$SECBUILD_DEFFILE" << DEFFILE +Bootstrap: docker +From: ubuntu:latest + +%setup + cp -a . $SECBUILD_SINGULARITY + +%post + export LC_LANG=C + apt-get update + apt-get install -y git gcc python make automake autoconf libtool + apt-get install -y squashfs-tools curl uuid-dev libssl-dev libarchive-dev + apt-get install -y debootstrap yum zypper + apt-get clean + apt-get autoclean + rm -rf /usr/local/libexec/singularity + rm -rf /usr/local/lib/singularity + cd $SECBUILD_SINGULARITY + ./configure --disable-suid + make clean + make -j4 + make install +DEFFILE + +$SINGULARITY_PATH/singularity build --sandbox $SECBUILD_IMAGE $SECBUILD_DEFFILE +if [ $? != 0 ]; then + echo + echo "Secbuild image failed" + rm -rf $BUILDTMP + exit 1 +else + echo + echo "Secbuild image completed" +fi diff --git a/singularity.spec.in b/singularity.spec.in index 9cc85b0fa2..c4f55b549b 100644 --- a/singularity.spec.in +++ b/singularity.spec.in @@ -35,7 +35,14 @@ Source: %{name}-%{version}.tar.gz ExclusiveOS: linux BuildRoot: %{?_tmppath}%{!?_tmppath:/var/tmp}/%{name}-%{version}-%{release}-root BuildRequires: python +BuildRequires: libuuid-devel +BuildRequires: libarchive-devel +BuildRequires: openssl-devel +%if "%{_target_vendor}" == "suse" +Requires: squashfs +%else Requires: squashfs-tools +%endif Requires: %{name}-runtime = %{version}-%{release} @@ -85,6 +92,8 @@ rm -rf $RPM_BUILD_ROOT %doc examples CONTRIBUTORS.md CONTRIBUTING.md COPYRIGHT.md INSTALL.md LICENSE-LBNL.md LICENSE.md README.md %attr(0755, root, root) %dir %{_sysconfdir}/singularity %attr(0644, root, root) %config(noreplace) %{_sysconfdir}/singularity/* +%attr(0755, root, root) %dir %{_sysconfdir}/singularity/capabilities +%attr(0644, root, root) %config(noreplace) %{_sysconfdir}/singularity/capabilities/* %{_libexecdir}/singularity/cli/apps.* %{_libexecdir}/singularity/cli/bootstrap.* @@ -96,25 +105,22 @@ rm -rf $RPM_BUILD_ROOT %{_libexecdir}/singularity/cli/mount.* %{_libexecdir}/singularity/cli/pull.* %{_libexecdir}/singularity/cli/selftest.* -%{_libexecdir}/singularity/handlers %{_libexecdir}/singularity/helpers -%{_libexecdir}/singularity/image-handler.sh %{_libexecdir}/singularity/python # Binaries %{_libexecdir}/singularity/bin/builddef %{_libexecdir}/singularity/bin/cleanupd +%{_libexecdir}/singularity/bin/get-configvals %{_libexecdir}/singularity/bin/get-section %{_libexecdir}/singularity/bin/mount %{_libexecdir}/singularity/bin/image-type %{_libexecdir}/singularity/bin/sif +%{_libexecdir}/singularity/bin/docker-extract # Directories %{_libexecdir}/singularity/bootstrap-scripts -#SUID programs -%attr(4755, root, root) %{_libexecdir}/singularity/bin/mount-suid - %files runtime %dir %{_libexecdir}/singularity %dir %{_localstatedir}/singularity @@ -133,21 +139,29 @@ rm -rf $RPM_BUILD_ROOT %{_libexecdir}/singularity/cli/run.* %{_libexecdir}/singularity/cli/shell.* %{_libexecdir}/singularity/cli/test.* +%{_libexecdir}/singularity/cli/capability.* %{_libexecdir}/singularity/cli/sign.* %{_libexecdir}/singularity/cli/verify.* %{_libexecdir}/singularity/cli/siflist.* %{_libexecdir}/singularity/bin/action %{_libexecdir}/singularity/bin/start +%{_libexecdir}/singularity/bin/image-type +%{_libexecdir}/singularity/bin/wrapper +%{_libexecdir}/singularity/bin/docker-extract %{_libexecdir}/singularity/functions +%{_libexecdir}/singularity/capabilities +%{_libexecdir}/singularity/handlers +%{_libexecdir}/singularity/image-handler.sh %dir %{_sysconfdir}/singularity %config(noreplace) %{_sysconfdir}/singularity/* +%dir %{_sysconfdir}/singularity/capabilities +%config(noreplace) %{_sysconfdir}/singularity/capabilities/* %{_mandir}/man1/singularity.1* %dir %{_sysconfdir}/bash_completion.d %{_sysconfdir}/bash_completion.d/singularity #SUID programs -%attr(4755, root, root) %{_libexecdir}/singularity/bin/action-suid -%attr(4755, root, root) %{_libexecdir}/singularity/bin/start-suid +%attr(4755, root, root) %{_libexecdir}/singularity/bin/wrapper-suid %files devel %defattr(-, root, root) diff --git a/src/Makefile.am b/src/Makefile.am index 0aaab7c002..3a469bc156 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -5,30 +5,35 @@ DISTCLEANFILES = Makefile CLEANFILES = core.* *~ *.la AM_CFLAGS = -Wall -fpie -fPIC AM_LDFLAGS = -pie -AM_CPPFLAGS = -DBINDIR=\"$(bindir)\" -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) +AM_CPPFLAGS = -DBINDIR=\"$(bindir)\" -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) lexecdir = $(libexecdir)/singularity/bin -lexec_PROGRAMS = action builddef cleanupd get-section image-type mount start sif $(BUILD_SUID) -EXTRA_PROGRAMS = action-suid mount-suid start-suid +lexec_PROGRAMS = action builddef cleanupd docker-extract get-section get-configvals image-type mount start sif wrapper $(BUILD_SUID) +EXTRA_PROGRAMS = wrapper-suid -cleanupd_SOURCES = cleanupd.c util/util.c util/file.c util/message.c util/privilege.c util/config_parser.c util/registry.c +cleanupd_SOURCES = cleanupd.c util/util.c util/file.c util/message.c util/privilege.c util/config_parser.c util/registry.c util/capability.c util/suid.c cleanupd_CPPFLAGS = $(AM_CPPFLAGS) -action_SOURCES = action.c util/util.c util/file.c util/registry.c util/privilege.c util/sessiondir.c util/suid.c util/cleanupd.c util/daemon.c util/mount.c +action_SOURCES = action.c util/util.c util/file.c util/registry.c util/privilege.c util/sessiondir.c util/suid.c util/cleanupd.c util/daemon.c util/capability.c util/mount.c action_LDADD = lib/image/libsingularity-image.la lib/runtime/libsingularity-runtime.la lib/sif/libsingularity-sif.la action-lib/libinternal.la -luuid action_CPPFLAGS = $(AM_CPPFLAGS) -builddef_SOURCES = builddef.c util/util.c util/file.c util/registry.c util/sessiondir.c +builddef_SOURCES = builddef.c util/util.c util/file.c util/registry.c util/sessiondir.c util/capability.c builddef_LDADD = lib/image/libsingularity-image.la lib/runtime/libsingularity-runtime.la lib/sif/libsingularity-sif.la bootstrap-lib/libinternal.la -luuid builddef_CPPFLAGS = $(AM_CPPFLAGS) builddef_LDFLAGS = -static -start_SOURCES = start.c util/util.c util/file.c util/registry.c util/privilege.c util/sessiondir.c util/suid.c util/cleanupd.c util/fork.c util/daemon.c util/signal.c util/mount.c +docker_extract_SOURCES = docker-extract.c util/util.c util/file.c util/message.c util/privilege.c util/config_parser.c util/registry.c util/capability.c util/suid.c +docker_extract_LDADD = -larchive +docker_extract_CPPFLAGS = $(AM_CPPFLAGS) +docker_extract_LDFLAGS = -static + +start_SOURCES = start.c util/util.c util/file.c util/registry.c util/privilege.c util/sessiondir.c util/suid.c util/cleanupd.c util/fork.c util/daemon.c util/signal.c util/capability.c util/mount.c start_LDADD = lib/image/libsingularity-image.la lib/runtime/libsingularity-runtime.la lib/sif/libsingularity-sif.la action-lib/libinternal.la -luuid start_CPPFLAGS = $(AM_CPPFLAGS) -mount_SOURCES = mount.c util/util.c util/file.c util/registry.c util/suid.c util/privilege.c util/mount.c +mount_SOURCES = mount.c util/util.c util/file.c util/registry.c util/suid.c util/privilege.c util/capability.c util/mount.c mount_LDADD = lib/image/libsingularity-image.la lib/runtime/libsingularity-runtime.la lib/sif/libsingularity-sif.la -luuid mount_CPPFLAGS = $(AM_CPPFLAGS) @@ -36,28 +41,24 @@ sif_SOURCES = sif.c util/util.c sif_LDADD = lib/image/libsingularity-image.la lib/signing/libsingularity-signing.la lib/sif/libsingularity-sif.la -luuid -lcrypto sif_CPPFLAGS = $(AM_CPPFLAGS) -get_section_SOURCES = get-section.c util/util.c util/file.c util/message.c util/privilege.c util/config_parser.c util/registry.c +get_section_SOURCES = get-section.c util/util.c util/file.c util/message.c util/privilege.c util/config_parser.c util/registry.c util/capability.c util/suid.c get_section_CPPFLAGS = $(AM_CPPFLAGS) +get_configvals_SOURCES = get-configvals.c util/util.c util/config_parser.c util/message.c util/registry.c util/file.c util/privilege.c util/suid.c util/capability.c +get_configvals_CPPFLAGS = $(AM_CPPFLAGS) + image_type_SOURCES = image-type.c util/util.c util/message.c util/config_parser.c util/file.c image_type_LDADD = lib/image/libsingularity-image.la lib/sif/libsingularity-sif.la -luuid image_type_CPPFLAGS = $(AM_CPPFLAGS) -action_suid_SOURCES = $(action_SOURCES) -action_suid_LDADD = $(action_LDADD) -action_suid_LDFLAGS = -static -action_suid_CPPFLAGS = -DSINGULARITY_SUID $(AM_CPPFLAGS) - -start_suid_SOURCES = $(start_SOURCES) -start_suid_LDADD = $(start_LDADD) -start_suid_LDFLAGS = -static -start_suid_CPPFLAGS = -DSINGULARITY_SUID $(AM_CPPFLAGS) - -mount_suid_SOURCES = $(mount_SOURCES) -mount_suid_LDADD = $(mount_LDADD) -mount_suid_LDFLAGS = -static -mount_suid_CPPFLAGS = -DSINGULARITY_SUID $(AM_CPPFLAGS) +wrapper_SOURCES = wrapper.c util/util.c util/file.c util/registry.c util/suid.c util/privilege.c util/capability.c +wrapper_LDADD = lib/image/libsingularity-image.la lib/runtime/libsingularity-runtime.la lib/sif/libsingularity-sif.la -luuid +wrapper_CPPFLAGS = $(AM_CPPFLAGS) +wrapper_suid_SOURCES = $(wrapper_SOURCES) +wrapper_suid_LDADD = $(wrapper_LDADD) +wrapper_suid_LDFLAGS = -static +wrapper_suid_CPPFLAGS = -DSINGULARITY_SUID $(AM_CPPFLAGS) install-data-hook: make_suid diff --git a/src/action-lib/Makefile.am b/src/action-lib/Makefile.am index b22d0ae8ce..723a8624cd 100644 --- a/src/action-lib/Makefile.am +++ b/src/action-lib/Makefile.am @@ -4,7 +4,7 @@ CLEANFILES = core.* *~ *.la AM_CFLAGS = -Wall -fpie AM_LDFLAGS = -pie -AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) noinst_LTLIBRARIES = libinternal.la libinternal_la_SOURCES = exec.c test.c ready.c run.c shell.c diff --git a/src/action.c b/src/action.c index b9e864f0f6..a66bb2b7e8 100644 --- a/src/action.c +++ b/src/action.c @@ -55,16 +55,17 @@ int main(int argc, char **argv) { char *target_pwd = NULL; char *command = NULL; - singularity_config_init(joinpath(SYSCONFDIR, "/singularity/singularity.conf")); + singularity_config_init(); + singularity_suid_init(); singularity_priv_init(); - singularity_suid_init(argv); singularity_registry_init(); - singularity_priv_userns(); singularity_priv_drop(); + singularity_runtime_autofs(); + singularity_daemon_init(); if ( singularity_registry_get("WRITABLE") != NULL ) { @@ -99,7 +100,13 @@ int main(int argc, char **argv) { singularity_priv_drop_perm(); - if ( singularity_registry_get("CONTAIN") != NULL ) { + if ( ( target_pwd = singularity_registry_get("TARGET_PWD") ) != NULL ) { + singularity_message(DEBUG, "Attempting to chdir to TARGET_PWD: %s\n", target_pwd); + if ( chdir(target_pwd) != 0 ) { + singularity_message(ERROR, "Could not change directory to: %s\n", target_pwd); + ABORT(255); + } + } else if ( singularity_registry_get("CONTAIN") != NULL ) { singularity_message(DEBUG, "Attempting to chdir to home: %s\n", singularity_priv_home()); if ( chdir(singularity_priv_home()) != 0 ) { singularity_message(WARNING, "Could not chdir to home: %s\n", singularity_priv_home()); @@ -108,12 +115,6 @@ int main(int argc, char **argv) { ABORT(255); } } - } else if ( ( target_pwd = singularity_registry_get("TARGET_PWD") ) != NULL ) { - singularity_message(DEBUG, "Attempting to chdir to TARGET_PWD: %s\n", target_pwd); - if ( chdir(target_pwd) != 0 ) { - singularity_message(ERROR, "Could not change directory to: %s\n", target_pwd); - ABORT(255); - } } else if ( pwd != NULL ) { singularity_message(DEBUG, "Attempting to chdir to CWD: %s\n", pwd); if ( chdir(pwd) != 0 ) { @@ -151,8 +152,6 @@ int main(int argc, char **argv) { action_shell(argc, argv); } else if ( strcmp(command, "exec") == 0 ) { action_exec(argc, argv); - } else if ( strcmp(command, "inspect") == 0 ) { - action_exec(argc, argv); } else if ( strcmp(command, "run") == 0 ) { action_run(argc, argv); } else if ( strcmp(command, "test") == 0 ) { diff --git a/src/bootstrap-lib/Makefile.am b/src/bootstrap-lib/Makefile.am index f9c7cc0069..fcdf0dbd0d 100644 --- a/src/bootstrap-lib/Makefile.am +++ b/src/bootstrap-lib/Makefile.am @@ -4,7 +4,7 @@ CLEANFILES = core.* *~ *.la AM_CFLAGS = -Wall -fpie AM_LDFLAGS = -pie -AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) noinst_LTLIBRARIES = libinternal.la libinternal_la_SOURCES = init.c keyval.c diff --git a/src/builddef.c b/src/builddef.c index 297d3dd486..6a8aa52764 100644 --- a/src/builddef.c +++ b/src/builddef.c @@ -36,6 +36,7 @@ #include "lib/image/image.h" #include "lib/runtime/runtime.h" #include "util/config_parser.h" +#include "util/capability.h" #include "util/privilege.h" #include "util/sessiondir.h" @@ -60,10 +61,20 @@ int main(int argc, char **argv) { char *line; char *builddef; - singularity_config_init(joinpath(SYSCONFDIR, "/singularity/singularity.conf")); + singularity_config_init(); singularity_registry_init(); singularity_priv_init(); + if ( singularity_registry_get("STAGE2") != NULL ) { + char *bootstrap = joinpath(LIBEXECDIR, "/singularity/bootstrap-scripts/deffile-sections.sh"); + singularity_capability_init_minimal(); + execl(bootstrap, bootstrap, NULL); //Flawfinder: ignore (Yes, yes, we know, and this is required) + singularity_message(ERROR, "Exec of bootstrap stage2 failed\n"); + ABORT(255); + } + + singularity_capability_init_default(); + singularity_message(INFO, "Sanitizing environment\n"); if ( envclean() != 0 ) { singularity_message(ERROR, "Failed sanitizing the environment\n"); @@ -142,7 +153,6 @@ int main(int argc, char **argv) { // Cool little feature, every key defined in def file is transposed // to environment - envar_set(uppercase(bootdef_key), bootdef_value, 1); envar_set(strjoin("SINGULARITY_DEFFILE_", uppercase(bootdef_key)), bootdef_value, 1); } } @@ -153,6 +163,7 @@ int main(int argc, char **argv) { envar_set("PATH", "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin", 1); envar_set("SINGULARITY_ROOTFS", CONTAINER_MOUNTDIR, 1); envar_set("SINGULARITY_libexecdir", LIBEXECDIR, 1); + envar_set("SINGULARITY_sysconfdir", SYSCONFDIR, 1); envar_set("SINGULARITY_bindir", BINDIR, 1); envar_set("SINGULARITY_IMAGE", singularity_registry_get("IMAGE"), 1); envar_set("SINGULARITY_BUILDDEF", singularity_registry_get("BUILDDEF"), 1); @@ -171,11 +182,11 @@ int main(int argc, char **argv) { envar_set("HOME", singularity_priv_home(), 1); envar_set("LANG", "C", 1); - char *bootstrap = joinpath(LIBEXECDIR, "/singularity/bootstrap-scripts/main-deffile.sh"); + char *bootstrap = joinpath(LIBEXECDIR, "/singularity/bootstrap-scripts/main-deffile-stage1.sh"); execl(bootstrap, bootstrap, NULL); //Flawfinder: ignore (Yes, yes, we know, and this is required) - singularity_message(ERROR, "Exec of bootstrap code failed: %s\n", strerror(errno)); + singularity_message(ERROR, "Exec of bootstrap stage1 failed: %s\n", strerror(errno)); ABORT(255); return(0); diff --git a/src/docker-extract.c b/src/docker-extract.c new file mode 100644 index 0000000000..e1f180e6b6 --- /dev/null +++ b/src/docker-extract.c @@ -0,0 +1,336 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "util/file.h" +#include "util/message.h" +#include "util/registry.h" +#include "util/util.h" + +/* apply_opaque + * Given opq_marker as a path to a whiteout opaque marker + * e.g. usr/share/doc/test/.wh..wh..opq + * Make the directory containing the make opaque for this layer by removing it + * if it exists under rootfs_dir + */ +int apply_opaque(const char *opq_marker, char *rootfs_dir) { + int retval = 0; + char *token; + static char buf[MAXPATHLEN]; + + token = strrchr(opq_marker, '/'); + if (token == NULL) { + singularity_message(ERROR, "Error getting dirname for opaque marker\n"); + ABORT(255); + } + + retval = snprintf(buf, sizeof(buf), "%s/%s", rootfs_dir, opq_marker); + if (retval == -1 || retval >= sizeof(buf)) { + singularity_message(ERROR, "Error with pathname too long\n"); + ABORT(255); + } + + if (is_dir(buf) == 0) { + s_rmdir(buf); + } + + return retval; +} + +/* apply_whiteout + * Given wh_marker as a path to a whiteout marker + * e.g. usr/share/doc/test/.wh.deletedfile + * Whiteout the referenced file for this layer by removing it if it exists + * under rootfs_dir + */ +int apply_whiteout(const char *wh_marker, char *rootfs_dir) { + int retval = 0; + char* token; + size_t token_pos, l = 0; + char buf[MAXPATHLEN]; + + token = strstr(wh_marker, ".wh."); + if (token == NULL) { + singularity_message(ERROR, "Error getting filename for whiteout marker\n"); + ABORT(255); + } + + // Start with ROOTFS + retval = snprintf(buf, sizeof(buf), "%s/", rootfs_dir); + if (retval == -1 || retval >= sizeof(buf)) { + singularity_message(ERROR, "Error with pathname too long\n"); + ABORT(255); + } + l = strlen(buf); + // Add whiteout path up to .wh. + token_pos = strlen(wh_marker) - strlen(token); + retval = snprintf(buf + l, token_pos + 1, "%s", wh_marker); + if (retval == -1 || retval >= sizeof(buf) - l) { + singularity_message(ERROR, "Error with pathname too long\n"); + ABORT(255); + } + l = strlen(buf); + // Concatenate suffix after .wh + retval = snprintf(buf + l, sizeof(buf) - l, "%s", token + 4); + if (retval == -1 || retval >= sizeof(buf) - l) { + singularity_message(ERROR, "Error with pathname too long\n"); + ABORT(255); + } + + if (is_dir(buf) == 0) { + retval = s_rmdir(buf); + } else if (is_file(buf) == 0) { + singularity_message(DEBUG, "Removing whiteout-ed file: %s\n", + buf); + retval = unlink(buf); + } + + return retval; +} + +/* apply_whiteouts + * Process tarfile and apply any aufs opaque/whiteouts on rootfs_dir + */ +int apply_whiteouts(char *tarfile, char *rootfs_dir) { + int retval = 0; + int errcode = 0; + + struct archive *a; + struct archive_entry *entry; + + a = archive_read_new(); +#if ARCHIVE_VERSION_NUMBER <= 3000000 + archive_read_support_compression_all(a); +#else + archive_read_support_filter_all(a); +#endif + archive_read_support_format_all(a); + retval = archive_read_open_filename(a, tarfile, 10240); + if (retval != ARCHIVE_OK) { + return (1); + } + + while (archive_read_next_header(a, &entry) == ARCHIVE_OK) { + + const char *pathname = archive_entry_pathname(entry); + + if (strstr(pathname, "/.wh..wh..opq")) { + singularity_message(DEBUG, "Opaque Marker %s\n", pathname); + errcode = apply_opaque(pathname, rootfs_dir); + if (errcode != 0) { + break; + } + } else if (strstr(pathname, "/.wh.")) { + singularity_message(DEBUG, "Whiteout Marker %s\n", pathname); + errcode = apply_whiteout(pathname, rootfs_dir); + if (errcode != 0) { + break; + } + } + } +#if ARCHIVE_VERSION_NUMBER <= 3000000 + retval = archive_read_finish(a); +#else + retval = archive_read_free(a); +#endif + if (retval != ARCHIVE_OK){ + singularity_message(ERROR, "Error freeing archive\n"); + ABORT(255); + } + + return errcode; +} + +/* See https://github.com/libarchive/libarchive/wiki/Examples#A_Complete_Extractor */ +static int copy_data(struct archive *ar, struct archive *aw) { + int r; + const void *buff; + size_t size; + int64_t offset; + + for (;;) { + r = archive_read_data_block(ar, &buff, &size, &offset); + if (r == ARCHIVE_EOF) { + return (ARCHIVE_OK); + } + if (r < ARCHIVE_OK) { + return (r); + } + r = archive_write_data_block(aw, buff, size, offset); + if (r < ARCHIVE_OK) { + singularity_message(ERROR, "tar extraction error: %s\n", archive_error_string(aw)); + return (r); + } + } +} + +/* extract_tar + * Extract a tar file to rootfs_dir using libarchive. Handles compression. + * Exclude any .wh. whiteout files, and device/pipe/fifo entries + * + * See https://github.com/libarchive/libarchive/wiki/Examples#A_Complete_Extractor + */ +int extract_tar(const char *tarfile, const char *rootfs_dir) { + int retval = 0; + struct archive *a; + struct archive *ext; + struct archive_entry *entry; + int flags; + int r; + char *orig_dir; + const char *pathname; + int pathtype; + + orig_dir = get_current_dir_name(); + + /* Select which attributes we want to restore. */ + flags = ARCHIVE_EXTRACT_TIME; + flags |= ARCHIVE_EXTRACT_PERM; + flags |= ARCHIVE_EXTRACT_ACL; + flags |= ARCHIVE_EXTRACT_FFLAGS; + + a = archive_read_new(); + archive_read_support_format_all(a); +#if ARCHIVE_VERSION_NUMBER <= 3000000 + archive_read_support_compression_all(a); +#else + archive_read_support_filter_all(a); +#endif + ext = archive_write_disk_new(); + archive_write_disk_set_options(ext, flags); + archive_write_disk_set_standard_lookup(ext); + if ((r = archive_read_open_filename(a, tarfile, 10240))){ + singularity_message(ERROR, "Error opening tar file %s\n", tarfile); + ABORT(255); + } + + // Extract into the SINGULARITY_ROOTFS + r = chdir(rootfs_dir); + if (r < 0 ){ + singularity_message(ERROR, "Could not chdir to SINGULARITY_ROOTFS %s\n", rootfs_dir); + ABORT(255); + } + + for (;;) { + r = archive_read_next_header(a, &entry); + + if (r == ARCHIVE_EOF) { + break; + } + + if (r < ARCHIVE_OK) { + singularity_message(WARNING, "Warning reading tar header: %s\n", archive_error_string(a)); + } + if (r < ARCHIVE_WARN) { + singularity_message(ERROR, "Error reading tar header: %s\n", archive_error_string(a)); + ABORT(255); + } + + pathname = archive_entry_pathname(entry); + pathtype = archive_entry_filetype(entry); + + // Do not extract whiteout markers (handled in apply_whiteouts) + // Do not extract sockers, chr/blk devices, pipes + if (strstr(pathname, "/.wh.") || pathtype == AE_IFSOCK || + pathtype == AE_IFCHR || pathtype == AE_IFBLK || pathtype == AE_IFIFO) { + continue; + } + + r = archive_write_header(ext, entry); + if (r < ARCHIVE_OK) { + singularity_message(WARNING, "Warning handling tar header: %s\n", archive_error_string(ext)); + }else if (archive_entry_size(entry) > 0) { + r = copy_data(a, ext); + if (r < ARCHIVE_OK) { + singularity_message(WARNING, "Warning handling tar header: %s\n", archive_error_string(ext)); + } + if (r < ARCHIVE_WARN) { + singularity_message(ERROR, "Error handling tar header: %s\n", archive_error_string(ext)); + ABORT(255); + } + } + r = archive_write_finish_entry(ext); + if (r < ARCHIVE_OK) { + singularity_message(WARNING, "Warning freeing archive entry: %s\n", archive_error_string(ext)); + } + if (r < ARCHIVE_WARN) { + singularity_message(ERROR, "Error freeing archive entry: %s\n", archive_error_string(ext)); + ABORT(255); + } + } + archive_read_close(a); +#if ARCHIVE_VERSION_NUMBER <= 3000000 + archive_read_finish(a); + archive_write_close(ext); + archive_write_finish(ext); +#else + archive_read_free(a); + archive_write_close(ext); + archive_write_free(ext); +#endif + r = chdir(orig_dir); + if (r < 0 ){ + singularity_message(ERROR, "Could not chdir back to %s\n", orig_dir); + ABORT(255); + } + + free(orig_dir); + + return (retval); +} + +int main(int argc, char **argv) { + int retval = 0; + char *rootfs_dir = singularity_registry_get("ROOTFS"); + char *tarfile = NULL; + + if (argc != 2) { + singularity_message(ERROR, "Provide a single docker tar file to extract\n"); + ABORT(255); + } + + if (rootfs_dir == NULL) { + singularity_message(ERROR, "Environment is not properly setup\n"); + ABORT(255); + } + + if (is_dir(rootfs_dir) < 0) { + singularity_message(ERROR, "SINGULARITY_ROOTFS does not exist\n"); + ABORT(255); + } + + tarfile = argv[1]; + + if (is_file(tarfile) < 0) { + singularity_message(ERROR, "tar file does not exist: %s\n", tarfile); + ABORT(255); + } + + singularity_message(DEBUG, "Applying whiteouts for tar file %s\n", tarfile); + retval = apply_whiteouts(tarfile, rootfs_dir); + + if (retval != 0) { + singularity_message(ERROR, "Error applying layer whiteouts\n"); + ABORT(255); + } + + singularity_message(DEBUG, "Extracting docker tar file %s\n", tarfile); + retval = extract_tar(tarfile, rootfs_dir); + + if (retval != 0) { + singularity_message(ERROR, "Error extracting tar file\n"); + ABORT(255); + } + + return (retval); +} diff --git a/src/get-configvals.c b/src/get-configvals.c new file mode 100644 index 0000000000..ad69cce757 --- /dev/null +++ b/src/get-configvals.c @@ -0,0 +1,40 @@ +/* +* Copyright (c) 2017, SingularityWare, LLC., Inc. All rights reserved. +* +* This software is licensed under a 3-clause BSD license. Please +* consult LICENSE file distributed with the sources of this project regarding +* your rights to use or distribute this software. +* +*/ + + +#include +#include + +#include "util/util.h" +#include "util/config_parser.h" + +#ifndef SYSCONFDIR +#error SYSCONFDIR not defined +#endif + +int main(int argc, char **argv) { + + if ( argc < 2 ) { + printf("USAGE: %s [key]\n", argv[0]); + exit(0); + } + + singularity_config_init(); + + /* + If the key does not exist in the singularity.conf file, this is just + going to return "NULL". The function was originally designed to return the + default value if the value does not exist in the conf file, but I don't + know how to do that using only strings, and the key needs to be based on + user input, not hardcoded. + */ + printf("%s\n", _singularity_config_get_value_impl(argv[1], "NULL")); + + return(0); +} diff --git a/src/image-type.c b/src/image-type.c index cc946a5f28..e91e5b3838 100644 --- a/src/image-type.c +++ b/src/image-type.c @@ -76,7 +76,7 @@ int main(int argc, char **argv) { return(0); } - singularity_config_init(joinpath(SYSCONFDIR, "/singularity/singularity.conf")); + singularity_config_init(); singularity_message(VERBOSE3, "Instantiating read only container image object\n"); image = singularity_image_init(argv[1], O_RDONLY); diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index ebcbe25181..abfab18f19 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -5,7 +5,7 @@ DISTCLEANFILES = Makefile CLEANFILES = core.* *~ *.la AM_CFLAGS = -Wall -fpie -fPIC AM_LDFLAGS = -pie -AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) distlibdir = $(libdir)/singularity distincludedir = $(includedir)/singularity diff --git a/src/lib/image/Makefile.am b/src/lib/image/Makefile.am index 41bc11fa13..7d1379467e 100644 --- a/src/lib/image/Makefile.am +++ b/src/lib/image/Makefile.am @@ -5,13 +5,13 @@ DISTCLEANFILES = Makefile CLEANFILES = core.* *~ *.la AM_CFLAGS = -Wall -fpie -fPIC AM_LDFLAGS = -pie -AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) distlibdir = $(libdir)/singularity distincludedir = $(includedir)/singularity noinst_LTLIBRARIES = libimage.la libimage_la_LIBADD = ext3/libinternal.la dir/libinternal.la squashfs/libinternal.la sif/libinternal.la -libimage_la_SOURCES = image.c bind.c ../../util/registry.c ../../util/message.c ../../util/config_parser.c ../../util/privilege.c ../../util/util.c ../../util/file.c ../../util/suid.c ../../util/mount.c +libimage_la_SOURCES = image.c bind.c ../../util/registry.c ../../util/message.c ../../util/config_parser.c ../../util/privilege.c ../../util/util.c ../../util/file.c ../../util/suid.c ../../util/capability.c ../../util/mount.c libimage_la_CFLAGS = $(AM_CFLAGS) # This fixes duplicate sources in library and progs distinclude_HEADERS = image.h bind.h @@ -26,10 +26,12 @@ libsingularity_image_la_CFLAGS = $(AM_CFLAGS) # These are kludges so they don't remove the $(DEPDIR) in ../../util/ otherwise # the clean will fail when other Makefile tries to remove those directories distclean: distclean-recursive - -rm ./$(DEPDIR) - -rm -f Makefile + -rm -rf ./$(DEPDIR) + -rm -rf ./.libs + -rm -f $(DISTCLEANFILES) $(CLEANFILES) maintainer-clean: maintainer-clean-recursive - -rm ./$(DEPDIR) - -rm -f Makefile + -rm -rf ./$(DEPDIR) + -rm -rf ./.libs + -rm -f $(MAINTAINERCLEANFILES) $(DISTCLEANFILES) $(CLEANFILES) diff --git a/src/lib/image/dir/Makefile.am b/src/lib/image/dir/Makefile.am index 12e2046337..3aec57f6c4 100644 --- a/src/lib/image/dir/Makefile.am +++ b/src/lib/image/dir/Makefile.am @@ -4,7 +4,7 @@ CLEANFILES = core.* *~ *.la AM_CFLAGS = -Wall -fpie AM_LDFLAGS = -pie -AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) noinst_LTLIBRARIES = libinternal.la libinternal_la_SOURCES = init.c mount.c diff --git a/src/lib/image/dir/mount.c b/src/lib/image/dir/mount.c index 462543d9a6..018ee4a8dd 100644 --- a/src/lib/image/dir/mount.c +++ b/src/lib/image/dir/mount.c @@ -36,26 +36,51 @@ #include "util/message.h" #include "util/config_parser.h" #include "util/privilege.h" +#include "util/suid.h" +#include "util/registry.h" #include "util/mount.h" #include "../image.h" int _singularity_image_dir_mount(struct image_object *image, char *mount_point) { + int mntflags = MS_BIND | MS_NOSUID | MS_REC | MS_NODEV; if ( strcmp(image->path, "/") == 0 ) { singularity_message(ERROR, "Naughty naughty naughty...\n"); ABORT(255); } + if ( singularity_allow_container_setuid() ) { + singularity_message(DEBUG, "allow-setuid option set, removing MS_NOSUID mount flags\n"); + mntflags &= ~MS_NOSUID; + } + + if ( singularity_priv_getuid() == 0 ) { + singularity_message(DEBUG, "run as root, removing MS_NODEV mount flags\n"); + mntflags &= ~MS_NODEV; + } + singularity_priv_escalate(); singularity_message(DEBUG, "Mounting container directory %s->%s\n", image->path, mount_point); - if ( singularity_mount(image->path, mount_point, NULL, MS_BIND|MS_NOSUID|MS_REC|MS_NODEV, NULL) < 0 ) { + if ( singularity_mount(image->path, mount_point, NULL, mntflags, NULL) < 0 ) { singularity_message(ERROR, "Could not mount container directory %s->%s: %s\n", image->path, mount_point, strerror(errno)); return 1; } singularity_priv_drop(); + if ( singularity_priv_userns_enabled() != 1 ) { + if ( image->writable == 0 ) { + mntflags |= MS_RDONLY; + } + singularity_priv_escalate(); + if ( singularity_mount(NULL, mount_point, NULL, MS_REMOUNT | mntflags, NULL) < 0 ) { + singularity_message(ERROR, "Could not mount container directory %s->%s: %s\n", image->path, mount_point, strerror(errno)); + return 1; + } + singularity_priv_drop(); + } + return(0); } diff --git a/src/lib/image/ext3/Makefile.am b/src/lib/image/ext3/Makefile.am index 12e2046337..3aec57f6c4 100644 --- a/src/lib/image/ext3/Makefile.am +++ b/src/lib/image/ext3/Makefile.am @@ -4,7 +4,7 @@ CLEANFILES = core.* *~ *.la AM_CFLAGS = -Wall -fpie AM_LDFLAGS = -pie -AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) noinst_LTLIBRARIES = libinternal.la libinternal_la_SOURCES = init.c mount.c diff --git a/src/lib/image/ext3/mount.c b/src/lib/image/ext3/mount.c index 281c718482..2118da4f3e 100644 --- a/src/lib/image/ext3/mount.c +++ b/src/lib/image/ext3/mount.c @@ -37,6 +37,7 @@ #include "util/config_parser.h" #include "util/privilege.h" #include "util/registry.h" +#include "util/suid.h" #include "util/mount.h" #include "../image.h" @@ -44,7 +45,7 @@ int _singularity_image_ext3_mount(struct image_object *image, char *mount_point) { - int opts = MS_NOSUID; + int mntflags = MS_NOSUID | MS_NODEV; char *loop_dev; if ( ( loop_dev = singularity_image_bind(image) ) == NULL ) { @@ -52,20 +53,25 @@ int _singularity_image_ext3_mount(struct image_object *image, char *mount_point) ABORT(255); } - if ( getuid() != 0 ) { + if ( singularity_priv_getuid() != 0 ) { singularity_message(DEBUG, "Adding MS_NODEV to mount options\n"); - opts |= MS_NODEV; + mntflags |= MS_NODEV; + } + + if ( singularity_allow_container_setuid() ) { + singularity_message(DEBUG, "allow-setuid option set, removing MS_NOSUID mount flags\n"); + mntflags &= ~MS_NOSUID; } if ( image->writable <= 0 ) { singularity_message(DEBUG, "Adding MS_RDONLY to mount options\n"); - opts |= MS_RDONLY; + mntflags |= MS_RDONLY; } singularity_priv_escalate(); singularity_message(VERBOSE, "Mounting '%s' to: '%s'\n", loop_dev, mount_point); - if ( singularity_mount(loop_dev, mount_point, "ext3", opts, "errors=remount-ro") < 0 ) { + if ( singularity_mount(loop_dev, mount_point, "ext3", mntflags, "errors=remount-ro") < 0 ) { singularity_message(ERROR, "Failed to mount ext3 image: %s\n", strerror(errno)); ABORT(255); } diff --git a/src/lib/image/image.c b/src/lib/image/image.c index e2c86c19ef..d50e815547 100644 --- a/src/lib/image/image.c +++ b/src/lib/image/image.c @@ -123,6 +123,7 @@ struct image_object singularity_image_init(char *path, int open_flags) { if ( ( singularity_suid_enabled() >= 0 ) && ( singularity_priv_getuid() != 0 ) ) { singularity_limit_container_paths(&image); singularity_limit_container_owners(&image); + singularity_limit_container_groups(&image); } return(image); @@ -222,6 +223,50 @@ void singularity_limit_container_owners(struct image_object *image) { } } +void singularity_limit_container_groups(struct image_object *image) { + const char *limit_container_groups = singularity_config_get_value(LIMIT_CONTAINER_GROUPS); + + if ( strcmp(limit_container_groups, "NULL") != 0 ) { + struct stat image_stat; + char *group_token = NULL; + char *current = strtok_r(strdup(limit_container_groups), ",", &group_token); + + chomp(current); + + singularity_message(DEBUG, "Limiting container access to allowed groups\n"); + + if ( fstat(image->fd, &image_stat) != 0 ) { + singularity_message(ERROR, "Could not fstat() image file descriptor (%d): %s\n", image->fd, strerror(errno)); + ABORT(255); + } + + while (1) { + struct group *group; + + if ( current[0] == '\0' ) { + singularity_message(DEBUG, "Skipping blank group limit entry\n"); + } else { + singularity_message(DEBUG, "Checking group: '%s'\n", current); + + if ( ( group = getgrnam(current) ) != NULL ) { + if ( group->gr_gid == image_stat.st_gid ) { + singularity_message(DEBUG, "Singularity image is owned by required group: %s\n", current); + break; + } + } + } + + current = strtok_r(NULL, ",", &group_token); + chomp(current); + + if ( current == NULL ) { + singularity_message(ERROR, "Singularity image is not owned by required group(s)\n"); + ABORT(255); + } + } + } +} + void singularity_limit_container_paths(struct image_object *image) { const char *limit_container_paths = singularity_config_get_value(LIMIT_CONTAINER_PATHS); @@ -243,8 +288,16 @@ void singularity_limit_container_paths(struct image_object *image) { if ( readlink(fd_path, image_path, PATH_MAX-1) > 0 ) { // Flawfinder: ignore (TOCTOU not an issue within /proc) char *current = strtok_r(strdup(limit_container_paths), ",", &path_token); + char *current_path = NULL; chomp(current); + + current_path = realpath(current, NULL); // Flawfinder: ignore + if ( current_path == NULL ) { + singularity_message(WARNING, "Configuration limit container path contains an invalid path %s\n", current); + ABORT(255); + } + while (1) { if ( current[0] == '\0' ) { @@ -253,8 +306,8 @@ void singularity_limit_container_paths(struct image_object *image) { } else { singularity_message(DEBUG, "Checking image path: '%s'\n", current); - if ( strncmp(image_path, current, strlength(current, PATH_MAX)) == 0 ) { - singularity_message(VERBOSE, "Singularity image is in an allowed path: %s\n", current); + if ( strncmp(image_path, current_path, strlength(current_path, PATH_MAX)) == 0 ) { + singularity_message(VERBOSE, "Singularity image is in an allowed path: %s\n", current_path); break; } @@ -265,9 +318,17 @@ void singularity_limit_container_paths(struct image_object *image) { singularity_message(ERROR, "Singularity image is not in an allowed configured path\n"); ABORT(255); } + + if ( current_path ) free(current_path); + + current_path = realpath(current, NULL); // Flawfinder: ignore + if ( current_path == NULL ) { + singularity_message(WARNING, "Configuration limit container path contains an invalid path %s\n", current); + ABORT(255); + } } } - + free(current_path); } else { singularity_message(ERROR, "Could not obtain the full system path of the image file: %s\n", strerror(errno)); ABORT(255); diff --git a/src/lib/image/image.h b/src/lib/image/image.h index 001e630ecd..0c261f0b49 100644 --- a/src/lib/image/image.h +++ b/src/lib/image/image.h @@ -55,5 +55,6 @@ int singularity_image_type(struct image_object *object); extern int singularity_image_mount(struct image_object *image, char *mount_point); void singularity_limit_container_paths(struct image_object *object); void singularity_limit_container_owners(struct image_object *object); +void singularity_limit_container_groups(struct image_object *object); #endif diff --git a/src/lib/image/sif/Makefile.am b/src/lib/image/sif/Makefile.am index e8c1f1b294..30a03b7796 100644 --- a/src/lib/image/sif/Makefile.am +++ b/src/lib/image/sif/Makefile.am @@ -4,7 +4,7 @@ CLEANFILES = core.* *~ *.la AM_CFLAGS = -Wall -fpie AM_LDFLAGS = -pie -AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) noinst_LTLIBRARIES = libinternal.la libinternal_la_SOURCES = init.c diff --git a/src/lib/image/squashfs/Makefile.am b/src/lib/image/squashfs/Makefile.am index 12e2046337..3aec57f6c4 100644 --- a/src/lib/image/squashfs/Makefile.am +++ b/src/lib/image/squashfs/Makefile.am @@ -4,7 +4,7 @@ CLEANFILES = core.* *~ *.la AM_CFLAGS = -Wall -fpie AM_LDFLAGS = -pie -AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) noinst_LTLIBRARIES = libinternal.la libinternal_la_SOURCES = init.c mount.c diff --git a/src/lib/image/squashfs/mount.c b/src/lib/image/squashfs/mount.c index 986384bb44..a666dd5838 100644 --- a/src/lib/image/squashfs/mount.c +++ b/src/lib/image/squashfs/mount.c @@ -35,6 +35,7 @@ #include "util/util.h" #include "util/message.h" #include "util/config_parser.h" +#include "util/suid.h" #include "util/privilege.h" #include "util/mount.h" @@ -43,6 +44,7 @@ int _singularity_image_squashfs_mount(struct image_object *image, char *mount_point) { + int mntflags = MS_NOSUID | MS_RDONLY | MS_NODEV; char *loop_dev = NULL; if ( ( loop_dev = singularity_image_bind(image) ) == NULL ) { @@ -50,9 +52,14 @@ int _singularity_image_squashfs_mount(struct image_object *image, char *mount_po ABORT(255); } + if ( singularity_allow_container_setuid() ) { + singularity_message(DEBUG, "allow-setuid option set, removing MS_NOSUID mount flags\n"); + mntflags &= ~MS_NOSUID; + } + singularity_priv_escalate(); singularity_message(VERBOSE, "Mounting squashfs image: %s -> %s\n", loop_dev, mount_point); - if ( singularity_mount(loop_dev, mount_point, "squashfs", MS_NOSUID|MS_RDONLY|MS_NODEV, "errors=remount-ro") < 0 ) { + if ( singularity_mount(loop_dev, mount_point, "squashfs", mntflags, "errors=remount-ro") < 0 ) { singularity_message(ERROR, "Failed to mount squashfs image in (read only): %s\n", strerror(errno)); ABORT(255); } diff --git a/src/lib/runtime/Makefile.am b/src/lib/runtime/Makefile.am index b9ed1a3a65..e64eede443 100644 --- a/src/lib/runtime/Makefile.am +++ b/src/lib/runtime/Makefile.am @@ -5,13 +5,13 @@ DISTCLEANFILES = Makefile CLEANFILES = core.* *~ *.la AM_CFLAGS = -Wall -fpie -fPIC AM_LDFLAGS = -pie -AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) distlibdir = $(libdir)/singularity distincludedir = $(includedir)/singularity noinst_LTLIBRARIES = libinternal.la libinternal_la_LIBADD = ns/libinternal.la mounts/libinternal.la files/libinternal.la enter/libinternal.la overlayfs/libinternal.la environment/libinternal.la autofs/libinternal.la -libinternal_la_SOURCES = runtime.c ../../util/fork.c ../../util/registry.c ../../util/message.c ../../util/config_parser.c ../../util/privilege.c ../../util/util.c ../../util/file.c ../../util/setns.c ../../util/mount.c +libinternal_la_SOURCES = runtime.c ../../util/fork.c ../../util/registry.c ../../util/message.c ../../util/config_parser.c ../../util/privilege.c ../../util/util.c ../../util/file.c ../../util/setns.c ../../util/capability.c ../../util/suid.c ../../util/mount.c libinternal_la_CFLAGS = $(AM_CFLAGS) # This fixes duplicate sources in library and progs distinclude_HEADERS = runtime.h @@ -28,10 +28,12 @@ EXTRA_DIST = # These are kludges so they don't remove the $(DEPDIR) in ../../util/ otherwise # the clean will fail when other Makefile tries to remove those directories distclean: distclean-recursive - -rm ./$(DEPDIR) - -rm -f Makefile + -rm -rf ./$(DEPDIR) + -rm -rf ./.libs + -rm -f $(DISTCLEANFILES) $(CLEANFILES) maintainer-clean: maintainer-clean-recursive - -rm ./$(DEPDIR) - -rm -f Makefile + -rm -rf ./$(DEPDIR) + -rm -rf ./.libs + -rm -f $(MAINTAINERCLEANFILES) $(DISTCLEANFILES) $(CLEANFILES) diff --git a/src/lib/runtime/autofs/Makefile.am b/src/lib/runtime/autofs/Makefile.am index 2874806e68..8f853c38a6 100644 --- a/src/lib/runtime/autofs/Makefile.am +++ b/src/lib/runtime/autofs/Makefile.am @@ -4,8 +4,8 @@ CLEANFILES = core.* *~ *.la AM_CFLAGS = -Wall -fpie AM_LDFLAGS = -pie -libns_pid_a_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) -AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) +libns_pid_a_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) noinst_LTLIBRARIES = libinternal.la diff --git a/src/lib/runtime/enter/Makefile.am b/src/lib/runtime/enter/Makefile.am index 2115b8fedc..24f059c696 100644 --- a/src/lib/runtime/enter/Makefile.am +++ b/src/lib/runtime/enter/Makefile.am @@ -6,8 +6,8 @@ CLEANFILES = core.* *~ *.la AM_CFLAGS = -Wall -fpie AM_LDFLAGS = -pie -libns_pid_a_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) -AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) +libns_pid_a_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) noinst_LTLIBRARIES = libinternal.la diff --git a/src/lib/runtime/enter/chroot/Makefile.am b/src/lib/runtime/enter/chroot/Makefile.am index 8a1dadf30f..24a884f742 100644 --- a/src/lib/runtime/enter/chroot/Makefile.am +++ b/src/lib/runtime/enter/chroot/Makefile.am @@ -4,7 +4,7 @@ CLEANFILES = core.* *~ *.la AM_CFLAGS = -Wall -fpie AM_LDFLAGS = -pie -AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) noinst_LTLIBRARIES = libinternal.la libinternal_la_SOURCES = chroot.c diff --git a/src/lib/runtime/enter/chroot/chroot.c b/src/lib/runtime/enter/chroot/chroot.c index ba8e84f256..ea1d803fe6 100644 --- a/src/lib/runtime/enter/chroot/chroot.c +++ b/src/lib/runtime/enter/chroot/chroot.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -35,21 +36,40 @@ #include "util/util.h" #include "util/message.h" #include "util/privilege.h" +#include "util/registry.h" #include "util/config_parser.h" #include "../../runtime.h" +int pivot_root(const char *new_root, const char *put_old) { + return syscall(__NR_pivot_root, new_root, put_old); +} int _singularity_runtime_enter_chroot(void) { char *container_dir = CONTAINER_FINALDIR; - singularity_priv_escalate(); singularity_message(VERBOSE, "Entering container file system root: %s\n", container_dir); - if ( chroot(container_dir) < 0 ) { // Flawfinder: ignore (yep, yep, yep... we know!) - singularity_message(ERROR, "failed chroot to container at: %s\n", container_dir); - ABORT(255); + + if ( singularity_registry_get("DAEMON_JOIN") == NULL ) { + singularity_priv_escalate(); + if ( chdir(container_dir) < 0 ) { + singularity_message(ERROR, "Could not chdir to file system root %s: %s\n", container_dir, strerror(errno)); + ABORT(1); + } + if ( pivot_root(".", "etc") < 0 ) { + singularity_message(ERROR, "Changing root filesystem failed\n"); + ABORT(255); + } + if ( chroot(".") < 0 ) { // Flawfinder: ignore (yep, yep, yep... we know!) + singularity_message(ERROR, "failed chroot to container at: %s\n", container_dir); + ABORT(255); + } + if ( umount2("etc", MNT_DETACH) < 0 ) { + singularity_message(ERROR, "Changing root filesystem failed\n"); + ABORT(255); + } + singularity_priv_drop(); } - singularity_priv_drop(); singularity_message(DEBUG, "Changing dir to '/' within the new root\n"); if ( chdir("/") < 0 ) { diff --git a/src/lib/runtime/environment/Makefile.am b/src/lib/runtime/environment/Makefile.am index 9191d31873..6aacbad4e1 100644 --- a/src/lib/runtime/environment/Makefile.am +++ b/src/lib/runtime/environment/Makefile.am @@ -4,8 +4,8 @@ CLEANFILES = core.* *~ *.la AM_CFLAGS = -Wall -fpie AM_LDFLAGS = -pie -libns_pid_a_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) -AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) +libns_pid_a_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) noinst_LTLIBRARIES = libinternal.la diff --git a/src/lib/runtime/files/Makefile.am b/src/lib/runtime/files/Makefile.am index 393f8c04d2..3407756af2 100644 --- a/src/lib/runtime/files/Makefile.am +++ b/src/lib/runtime/files/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = passwd group resolvconf libs +SUBDIRS = passwd group resolvconf libs hostname MAINTAINERCLEANFILES = Makefile.in DISTCLEANFILES = Makefile @@ -6,12 +6,12 @@ CLEANFILES = core.* *~ *.la AM_CFLAGS = -Wall -fpie AM_LDFLAGS = -pie -libns_pid_a_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) -AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) +libns_pid_a_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) noinst_LTLIBRARIES = libinternal.la -libinternal_la_LIBADD = passwd/libinternal.la group/libinternal.la resolvconf/libinternal.la libs/libinternal.la +libinternal_la_LIBADD = passwd/libinternal.la group/libinternal.la resolvconf/libinternal.la libs/libinternal.la hostname/libinternal.la libinternal_la_SOURCES = files.c file-bind.c EXTRA_DIST = file-bind.h files.h diff --git a/src/lib/runtime/files/files.c b/src/lib/runtime/files/files.c index 6c35e788ba..85d40c85fb 100644 --- a/src/lib/runtime/files/files.c +++ b/src/lib/runtime/files/files.c @@ -36,7 +36,7 @@ #include "./group/group.h" #include "./resolvconf/resolvconf.h" #include "./libs/libs.h" - +#include "./hostname/hostname.h" int _singularity_runtime_files(void) { int retval = 0; @@ -46,6 +46,7 @@ int _singularity_runtime_files(void) { retval += _singularity_runtime_files_group(); retval += _singularity_runtime_files_resolvconf(); retval += _singularity_runtime_files_libs(); + retval += _singularity_runtime_files_hostname(); return(retval); } diff --git a/src/lib/runtime/files/group/Makefile.am b/src/lib/runtime/files/group/Makefile.am index 894bdbd7b9..007323f081 100644 --- a/src/lib/runtime/files/group/Makefile.am +++ b/src/lib/runtime/files/group/Makefile.am @@ -4,7 +4,7 @@ CLEANFILES = core.* *~ *.la AM_CFLAGS = -Wall -fpie AM_LDFLAGS = -pie -AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) noinst_LTLIBRARIES = libinternal.la libinternal_la_SOURCES = group.c diff --git a/src/lib/runtime/files/hostname/Makefile.am b/src/lib/runtime/files/hostname/Makefile.am new file mode 100644 index 0000000000..7d284e32e8 --- /dev/null +++ b/src/lib/runtime/files/hostname/Makefile.am @@ -0,0 +1,12 @@ +MAINTAINERCLEANFILES = Makefile.in +DISTCLEANFILES = Makefile +CLEANFILES = core.* *~ *.la + +AM_CFLAGS = -Wall -fpie +AM_LDFLAGS = -pie +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) + +noinst_LTLIBRARIES = libinternal.la +libinternal_la_SOURCES = hostname.c + +EXTRA_DIST = hostname.h diff --git a/src/lib/runtime/files/hostname/hostname.c b/src/lib/runtime/files/hostname/hostname.c new file mode 100644 index 0000000000..e9cdb73517 --- /dev/null +++ b/src/lib/runtime/files/hostname/hostname.c @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2017, SingularityWare, LLC. All rights reserved. + * + * This software is licensed under a 3-clause BSD license. Please + * consult LICENSE file distributed with the sources of this project regarding + * your rights to use or distribute this software. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "util/file.h" +#include "util/util.h" +#include "util/registry.h" +#include "util/config_parser.h" +#include "util/message.h" +#include "util/privilege.h" + +#include "../file-bind.h" +#include "../../runtime.h" + + +int _singularity_runtime_files_hostname(void) { + FILE *hostname_fd; + char *tmpdir = singularity_registry_get("SESSIONDIR"); + char *hostname_file = "/etc/hostname"; + char *containerdir = CONTAINER_FINALDIR; + char *source = joinpath(tmpdir, "/hostname"); + char *target = joinpath(containerdir, hostname_file); + char *hostname = singularity_registry_get("HOSTNAME"); + + if ( hostname == NULL ) { + singularity_message(DEBUG, "Setting container hostname not requested by user\n"); + return(0); + } + + singularity_message(DEBUG, "Check if /etc/hostname is present in container\n"); + if ( is_file(target) < 0 ) { + singularity_message(VERBOSE, "/etc/hostname doesn't exists, skipping\n"); + return(0); + } + + hostname_fd = fopen(source, "w+"); + if ( hostname_fd == NULL ) { + singularity_message(ERROR, "Couldn't create hostname session file\n"); + ABORT(255); + } + + if ( strlen(hostname) > HOST_NAME_MAX ) { + hostname[HOST_NAME_MAX] = '\0'; + } + + fprintf(hostname_fd, "%s\n", hostname); + fclose(hostname_fd); + + container_file_bind(source, hostname_file); + + return(0); +} diff --git a/src/lib/runtime/files/hostname/hostname.h b/src/lib/runtime/files/hostname/hostname.h new file mode 100644 index 0000000000..ee24d6678c --- /dev/null +++ b/src/lib/runtime/files/hostname/hostname.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2017, SingularityWare, LLC. All rights reserved. + * + * This software is licensed under a 3-clause BSD license. Please + * consult LICENSE file distributed with the sources of this project regarding + * your rights to use or distribute this software. + * + */ + +#ifndef __SINGULARITY_RUNTIME_FILES_HOSTNAME_H_ +#define __SINGULARITY_RUNTIME_FILES_HOSTNAME_H_ + +extern int _singularity_runtime_files_hostname(void); + +#endif /* __SINGULARITY_RUNTIME_FILES_HOSTNAME_H */ + diff --git a/src/lib/runtime/files/libs/Makefile.am b/src/lib/runtime/files/libs/Makefile.am index 299dc39026..f54f5047d5 100644 --- a/src/lib/runtime/files/libs/Makefile.am +++ b/src/lib/runtime/files/libs/Makefile.am @@ -4,7 +4,7 @@ CLEANFILES = core.* *~ *.la AM_CFLAGS = -Wall -fpie AM_LDFLAGS = -pie -AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) noinst_LTLIBRARIES = libinternal.la libinternal_la_SOURCES = libs.c diff --git a/src/lib/runtime/files/passwd/Makefile.am b/src/lib/runtime/files/passwd/Makefile.am index 7fc5a85c5b..867ae4a60a 100644 --- a/src/lib/runtime/files/passwd/Makefile.am +++ b/src/lib/runtime/files/passwd/Makefile.am @@ -4,7 +4,7 @@ CLEANFILES = core.* *~ *.la AM_CFLAGS = -Wall -fpie AM_LDFLAGS = -pie -AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) noinst_LTLIBRARIES = libinternal.la libinternal_la_SOURCES = passwd.c diff --git a/src/lib/runtime/files/resolvconf/Makefile.am b/src/lib/runtime/files/resolvconf/Makefile.am index 02469a7d3e..180781a9d9 100644 --- a/src/lib/runtime/files/resolvconf/Makefile.am +++ b/src/lib/runtime/files/resolvconf/Makefile.am @@ -4,7 +4,7 @@ CLEANFILES = core.* *~ *.la AM_CFLAGS = -Wall -fpie AM_LDFLAGS = -pie -AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) noinst_LTLIBRARIES = libinternal.la libinternal_la_SOURCES = resolvconf.c diff --git a/src/lib/runtime/mounts/Makefile.am b/src/lib/runtime/mounts/Makefile.am index 1b297487cf..f945dc2aa2 100644 --- a/src/lib/runtime/mounts/Makefile.am +++ b/src/lib/runtime/mounts/Makefile.am @@ -6,8 +6,8 @@ CLEANFILES = core.* *~ *.la AM_CFLAGS = -Wall -fpie AM_LDFLAGS = -pie -libns_pid_a_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) -AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) +libns_pid_a_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) noinst_LTLIBRARIES = libinternal.la diff --git a/src/lib/runtime/mounts/binds/Makefile.am b/src/lib/runtime/mounts/binds/Makefile.am index ed9fae5183..086a25d9b9 100644 --- a/src/lib/runtime/mounts/binds/Makefile.am +++ b/src/lib/runtime/mounts/binds/Makefile.am @@ -4,7 +4,7 @@ CLEANFILES = core.* *~ *.la AM_CFLAGS = -Wall -fpie AM_LDFLAGS = -pie -AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) noinst_LTLIBRARIES = libinternal.la libinternal_la_SOURCES = binds.c diff --git a/src/lib/runtime/mounts/cwd/Makefile.am b/src/lib/runtime/mounts/cwd/Makefile.am index 1167843541..dcab08162c 100644 --- a/src/lib/runtime/mounts/cwd/Makefile.am +++ b/src/lib/runtime/mounts/cwd/Makefile.am @@ -4,7 +4,7 @@ CLEANFILES = core.* *~ *.la AM_CFLAGS = -Wall -fpie AM_LDFLAGS = -pie -AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) noinst_LTLIBRARIES = libinternal.la libinternal_la_SOURCES = cwd.c diff --git a/src/lib/runtime/mounts/dev/Makefile.am b/src/lib/runtime/mounts/dev/Makefile.am index 8beb4aeee5..1aa54b1038 100644 --- a/src/lib/runtime/mounts/dev/Makefile.am +++ b/src/lib/runtime/mounts/dev/Makefile.am @@ -4,7 +4,7 @@ CLEANFILES = core.* *~ *.la AM_CFLAGS = -Wall -fpie AM_LDFLAGS = -pie -AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) noinst_LTLIBRARIES = libinternal.la libinternal_la_SOURCES = dev.c diff --git a/src/lib/runtime/mounts/home/Makefile.am b/src/lib/runtime/mounts/home/Makefile.am index add3f79946..1cea531099 100644 --- a/src/lib/runtime/mounts/home/Makefile.am +++ b/src/lib/runtime/mounts/home/Makefile.am @@ -4,7 +4,7 @@ CLEANFILES = core.* *~ *.la AM_CFLAGS = -Wall -fpie AM_LDFLAGS = -pie -AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) noinst_LTLIBRARIES = libinternal.la libinternal_la_SOURCES = home.c diff --git a/src/lib/runtime/mounts/hostfs/Makefile.am b/src/lib/runtime/mounts/hostfs/Makefile.am index 712f09985a..fe5e1a981a 100644 --- a/src/lib/runtime/mounts/hostfs/Makefile.am +++ b/src/lib/runtime/mounts/hostfs/Makefile.am @@ -4,7 +4,7 @@ CLEANFILES = core.* *~ *.la AM_CFLAGS = -Wall -fpie AM_LDFLAGS = -pie -AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) noinst_LTLIBRARIES = libinternal.la libinternal_la_SOURCES = hostfs.c diff --git a/src/lib/runtime/mounts/kernelfs/Makefile.am b/src/lib/runtime/mounts/kernelfs/Makefile.am index 4f3f0f7661..fe0c357dad 100644 --- a/src/lib/runtime/mounts/kernelfs/Makefile.am +++ b/src/lib/runtime/mounts/kernelfs/Makefile.am @@ -4,7 +4,7 @@ CLEANFILES = core.* *~ *.la AM_CFLAGS = -Wall -fpie AM_LDFLAGS = -pie -AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) noinst_LTLIBRARIES = libinternal.la libinternal_la_SOURCES = kernelfs.c diff --git a/src/lib/runtime/mounts/kernelfs/kernelfs.c b/src/lib/runtime/mounts/kernelfs/kernelfs.c index 926f7cd403..dd07ffde0c 100644 --- a/src/lib/runtime/mounts/kernelfs/kernelfs.c +++ b/src/lib/runtime/mounts/kernelfs/kernelfs.c @@ -64,6 +64,22 @@ int _singularity_runtime_mount_kernelfs(void) { singularity_message(ERROR, "Could not mount new procfs into container: %s\n", strerror(errno)); ABORT(255); } + if ( singularity_mount(NULL, "/proc", NULL, MS_UNBINDABLE | MS_REC, NULL) < 0 ) { + singularity_message(ERROR, "Could not propagate /proc as unbindable: %s\n", strerror(errno)); + ABORT(255); + } + singularity_priv_drop(); + } + if ( singularity_priv_userns_enabled() != 1 ) { + singularity_priv_escalate(); + if ( singularity_mount(joinpath(container_dir, "/proc/sys"), joinpath(container_dir, "/proc/sys"), NULL, MS_BIND, NULL) < 0) { + singularity_message(ERROR, "Could not bind-mount /proc/sys into container: %s\n", strerror(errno)); + ABORT(255); + } + if ( singularity_mount(NULL, joinpath(container_dir, "/proc/sys"), NULL, MS_BIND | MS_REMOUNT | MS_NOSUID | MS_RDONLY, NULL) < 0) { + singularity_message(ERROR, "Could not remount /proc into container: %s\n", strerror(errno)); + ABORT(255); + } singularity_priv_drop(); } } else { @@ -93,6 +109,10 @@ int _singularity_runtime_mount_kernelfs(void) { singularity_message(ERROR, "Could not mount /sys into container: %s\n", strerror(errno)); ABORT(255); } + if ( singularity_mount(NULL, joinpath(container_dir, "/sys"), NULL, MS_BIND | MS_REMOUNT | MS_NOSUID | MS_RDONLY, NULL) < 0 ) { + singularity_message(ERROR, "Could not mount /sys into container: %s\n", strerror(errno)); + ABORT(255); + } singularity_priv_drop(); } } else { diff --git a/src/lib/runtime/mounts/scratch/Makefile.am b/src/lib/runtime/mounts/scratch/Makefile.am index 4e50491ffe..b7f9451601 100644 --- a/src/lib/runtime/mounts/scratch/Makefile.am +++ b/src/lib/runtime/mounts/scratch/Makefile.am @@ -4,7 +4,7 @@ CLEANFILES = core.* *~ *.la AM_CFLAGS = -Wall -fpie AM_LDFLAGS = -pie -AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) noinst_LTLIBRARIES = libinternal.la libinternal_la_SOURCES = scratch.c diff --git a/src/lib/runtime/mounts/tmp/Makefile.am b/src/lib/runtime/mounts/tmp/Makefile.am index 723c0d25d2..1655b63a00 100644 --- a/src/lib/runtime/mounts/tmp/Makefile.am +++ b/src/lib/runtime/mounts/tmp/Makefile.am @@ -4,7 +4,7 @@ CLEANFILES = core.* *~ *.la AM_CFLAGS = -Wall -fpie AM_LDFLAGS = -pie -AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) noinst_LTLIBRARIES = libinternal.la libinternal_la_SOURCES = tmp.c diff --git a/src/lib/runtime/mounts/userbinds/Makefile.am b/src/lib/runtime/mounts/userbinds/Makefile.am index 97ec1145d7..9a8882e9c7 100644 --- a/src/lib/runtime/mounts/userbinds/Makefile.am +++ b/src/lib/runtime/mounts/userbinds/Makefile.am @@ -4,7 +4,7 @@ CLEANFILES = core.* *~ *.la AM_CFLAGS = -Wall -fpie AM_LDFLAGS = -pie -AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) noinst_LTLIBRARIES = libinternal.la libinternal_la_SOURCES = userbinds.c diff --git a/src/lib/runtime/ns/Makefile.am b/src/lib/runtime/ns/Makefile.am index de4656c898..aeac127735 100644 --- a/src/lib/runtime/ns/Makefile.am +++ b/src/lib/runtime/ns/Makefile.am @@ -1,17 +1,30 @@ -SUBDIRS = mnt pid ipc net +SUBDIRS = mnt pid ipc net uts user -MAINTAINERCLEANFILES = Makefile.in -DISTCLEANFILES = Makefile CLEANFILES = core.* *~ *.la +DISTCLEANFILES = Makefile +MAINTAINERCLEANFILES = Makefile.in AM_CFLAGS = -Wall -fpie AM_LDFLAGS = -pie -libns_pid_a_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) -AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) +libns_pid_a_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) noinst_LTLIBRARIES = libinternal.la -libinternal_la_LIBADD = mnt/libinternal.la pid/libinternal.la ipc/libinternal.la net/libinternal.la -libinternal_la_SOURCES = ns.c +libinternal_la_LIBADD = mnt/libinternal.la pid/libinternal.la ipc/libinternal.la net/libinternal.la uts/libinternal.la user/libinternal.la +libinternal_la_SOURCES = ns.c ../../../util/daemon.c EXTRA_DIST = ns.h + +# These are kludges so they don't remove the $(DEPDIR) in ../../util/ otherwise +# the clean will fail when other Makefile tries to remove those directories +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -rf ./.libs + -rm -f $(DISTCLEANFILES) $(CLEANFILES) + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -rf ./.libs + -rm -f $(MAINTAINERCLEANFILES) $(DISTCLEANFILES) $(CLEANFILES) + diff --git a/src/lib/runtime/ns/ipc/Makefile.am b/src/lib/runtime/ns/ipc/Makefile.am index d523b25b61..c8c65a6863 100644 --- a/src/lib/runtime/ns/ipc/Makefile.am +++ b/src/lib/runtime/ns/ipc/Makefile.am @@ -4,7 +4,7 @@ CLEANFILES = core.* *~ *.la AM_CFLAGS = -Wall -fpie AM_LDFLAGS = -pie -AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) noinst_LTLIBRARIES = libinternal.la libinternal_la_SOURCES = ipc.c diff --git a/src/lib/runtime/ns/ipc/ipc.c b/src/lib/runtime/ns/ipc/ipc.c index 5dc5c81870..7aab84c59c 100644 --- a/src/lib/runtime/ns/ipc/ipc.c +++ b/src/lib/runtime/ns/ipc/ipc.c @@ -40,6 +40,7 @@ #include "util/privilege.h" #include "util/fork.h" #include "util/registry.h" +#include "util/daemon.h" #include "util/setns.h" @@ -75,12 +76,16 @@ int _singularity_runtime_ns_ipc(void) { return(0); } -int _singularity_runtime_ns_ipc_join(void) { - int ns_fd = atoi(singularity_registry_get("DAEMON_NS_FD")); +int _singularity_runtime_ns_ipc_join(int ns_fd) { int ipc_fd; /* Attempt to open /proc/[PID]/ns/pid */ singularity_priv_escalate(); + if ( ! singularity_daemon_own_namespace("ipc") ) { + singularity_priv_drop(); + return(0); + } + ipc_fd = openat(ns_fd, "ipc", O_RDONLY); if( ipc_fd == -1 ) { diff --git a/src/lib/runtime/ns/ipc/ipc.h b/src/lib/runtime/ns/ipc/ipc.h index e301b1ced6..12bfcc5887 100644 --- a/src/lib/runtime/ns/ipc/ipc.h +++ b/src/lib/runtime/ns/ipc/ipc.h @@ -24,7 +24,7 @@ #define __SINGULARITY_RUNTIME_NS_IPC_H_ extern int _singularity_runtime_ns_ipc(void); -extern int _singularity_runtime_ns_ipc_join(void); +extern int _singularity_runtime_ns_ipc_join(int ns_fd); #endif /* __SINGULARITY_RUNTIME_NS_IPC_H */ diff --git a/src/lib/runtime/ns/mnt/Makefile.am b/src/lib/runtime/ns/mnt/Makefile.am index 1804253bcd..2fb5d2b65a 100644 --- a/src/lib/runtime/ns/mnt/Makefile.am +++ b/src/lib/runtime/ns/mnt/Makefile.am @@ -4,7 +4,7 @@ CLEANFILES = core.* *~ *.la AM_CFLAGS = -Wall -fpie AM_LDFLAGS = -pie -AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) noinst_LTLIBRARIES = libinternal.la libinternal_la_SOURCES = mnt.c diff --git a/src/lib/runtime/ns/mnt/mnt.c b/src/lib/runtime/ns/mnt/mnt.c index 677a56c0d0..8a043aed43 100644 --- a/src/lib/runtime/ns/mnt/mnt.c +++ b/src/lib/runtime/ns/mnt/mnt.c @@ -38,6 +38,7 @@ #include "util/message.h" #include "util/config_parser.h" #include "util/privilege.h" +#include "util/daemon.h" #include "util/setns.h" #include "util/mount.h" @@ -92,12 +93,16 @@ int _singularity_runtime_ns_mnt(void) { return(0); } -int _singularity_runtime_ns_mnt_join(void) { - int ns_fd = atoi(singularity_registry_get("DAEMON_NS_FD")); +int _singularity_runtime_ns_mnt_join(int ns_fd) { int mnt_fd; /* Attempt to open /proc/[MNT]/ns/mnt */ singularity_priv_escalate(); + if ( ! singularity_daemon_own_namespace("mnt") ) { + singularity_priv_drop(); + return(0); + } + mnt_fd = openat(ns_fd, "mnt", O_RDONLY); if( mnt_fd == -1 ) { @@ -113,7 +118,7 @@ int _singularity_runtime_ns_mnt_join(void) { singularity_priv_drop(); singularity_message(DEBUG, "Successfully joined mount namespace\n"); - close(ns_fd); + close(mnt_fd); return(0); } diff --git a/src/lib/runtime/ns/mnt/mnt.h b/src/lib/runtime/ns/mnt/mnt.h index 13097b8f5b..3a5a086276 100644 --- a/src/lib/runtime/ns/mnt/mnt.h +++ b/src/lib/runtime/ns/mnt/mnt.h @@ -24,7 +24,7 @@ #define __SINGULARITY_RUNTIME_NS_MNT_H_ extern int _singularity_runtime_ns_mnt(void); -extern int _singularity_runtime_ns_mnt_join(void); +extern int _singularity_runtime_ns_mnt_join(int ns_fd); #endif /* __SINGULARITY_RUNTIME_NS_MNT_H */ diff --git a/src/lib/runtime/ns/net/Makefile.am b/src/lib/runtime/ns/net/Makefile.am index 467f9fff3f..51c5998139 100644 --- a/src/lib/runtime/ns/net/Makefile.am +++ b/src/lib/runtime/ns/net/Makefile.am @@ -4,7 +4,7 @@ CLEANFILES = core.* *~ *.la AM_CFLAGS = -Wall -fpie AM_LDFLAGS = -pie -AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) noinst_LTLIBRARIES = libinternal.la libinternal_la_SOURCES = net.c diff --git a/src/lib/runtime/ns/net/net.c b/src/lib/runtime/ns/net/net.c index a4d3e831af..545e5b0b9b 100644 --- a/src/lib/runtime/ns/net/net.c +++ b/src/lib/runtime/ns/net/net.c @@ -29,6 +29,7 @@ #include "util/privilege.h" #include "util/fork.h" #include "util/registry.h" +#include "util/daemon.h" #include "util/setns.h" @@ -82,12 +83,16 @@ int _singularity_runtime_ns_net(void) { return(0); } -int _singularity_runtime_ns_net_join(void) { - int ns_fd = atoi(singularity_registry_get("DAEMON_NS_FD")); +int _singularity_runtime_ns_net_join(int ns_fd) { int net_fd; /* Attempt to open /proc/[PID]/ns/net */ singularity_priv_escalate(); + if ( ! singularity_daemon_own_namespace("net") ) { + singularity_priv_drop(); + return(0); + } + net_fd = openat(ns_fd, "net", O_RDONLY); if( net_fd == -1 ) { diff --git a/src/lib/runtime/ns/net/net.h b/src/lib/runtime/ns/net/net.h index 7a62986b2e..d9da0fe635 100644 --- a/src/lib/runtime/ns/net/net.h +++ b/src/lib/runtime/ns/net/net.h @@ -11,7 +11,7 @@ #define __SINGULARITY_RUNTIME_NS_NET_H_ extern int _singularity_runtime_ns_net(void); -extern int _singularity_runtime_ns_net_join(void); +extern int _singularity_runtime_ns_net_join(int ns_fd); #endif /* __SINGULARITY_RUNTIME_NS_NET_H */ diff --git a/src/lib/runtime/ns/ns.c b/src/lib/runtime/ns/ns.c index f28def907a..f54d86f032 100644 --- a/src/lib/runtime/ns/ns.c +++ b/src/lib/runtime/ns/ns.c @@ -36,18 +36,25 @@ #include "util/file.h" #include "util/util.h" #include "util/message.h" +#include "util/registry.h" #include "util/config_parser.h" #include "./ipc/ipc.h" #include "./mnt/mnt.h" #include "./pid/pid.h" #include "./net/net.h" +#include "./uts/uts.h" +#include "./user/user.h" #include "../runtime.h" int _singularity_runtime_ns(unsigned int flags) { int retval = 0; + if ( flags & SR_NS_USER ) { + singularity_message(DEBUG, "Calling: _singularity_runtime_ns_user()\n"); + retval += _singularity_runtime_ns_user(); + } if ( flags & SR_NS_IPC ) { singularity_message(DEBUG, "Calling: _singularity_runtime_ns_ipc()\n"); retval += _singularity_runtime_ns_ipc(); @@ -60,34 +67,48 @@ int _singularity_runtime_ns(unsigned int flags) { singularity_message(DEBUG, "Calling: _singularity_runtime_ns_net()\n"); retval += _singularity_runtime_ns_net(); } + if ( flags & SR_NS_UTS ) { + singularity_message(DEBUG, "Calling: _singularity_runtime_ns_uts()\n"); + retval += _singularity_runtime_ns_uts(); + } if ( flags & SR_NS_MNT ) { singularity_message(DEBUG, "Calling: _singularity_runtime_ns_mnt()\n"); retval += _singularity_runtime_ns_mnt(); } - return(retval); } int _singularity_runtime_ns_join(unsigned int flags) { int retval = 0; + int ns_fd = atoi(singularity_registry_get("DAEMON_NS_FD")); + if ( flags & SR_NS_USER ) { + singularity_message(DEBUG, "Calling: _singularity_runtime_ns_user_join()\n"); + retval += _singularity_runtime_ns_user_join(ns_fd); + } if ( flags & SR_NS_IPC ) { singularity_message(DEBUG, "Calling: _singularity_runtime_ns_ipc_join()\n"); - retval += _singularity_runtime_ns_ipc_join(); + retval += _singularity_runtime_ns_ipc_join(ns_fd); } if ( flags & SR_NS_PID ) { singularity_message(DEBUG, "Calling: _singularity_runtime_ns_pid_join()\n"); - retval += _singularity_runtime_ns_pid_join(); + retval += _singularity_runtime_ns_pid_join(ns_fd); } if ( flags & SR_NS_NET ) { singularity_message(DEBUG, "Calling: _singularity_runtime_ns_net_join()\n"); - retval += _singularity_runtime_ns_net_join(); + retval += _singularity_runtime_ns_net_join(ns_fd); + } + if ( flags & SR_NS_UTS ) { + singularity_message(DEBUG, "Calling: _singularity_runtime_ns_uts_join()\n"); + retval += _singularity_runtime_ns_uts_join(ns_fd); } if ( flags & SR_NS_MNT ) { singularity_message(DEBUG, "Calling: _singularity_runtime_ns_mnt_join()\n"); - retval += _singularity_runtime_ns_mnt_join(); + retval += _singularity_runtime_ns_mnt_join(ns_fd); } + close(ns_fd); + return(retval); } diff --git a/src/lib/runtime/ns/pid/Makefile.am b/src/lib/runtime/ns/pid/Makefile.am index 83969ae59e..c4d0b25202 100644 --- a/src/lib/runtime/ns/pid/Makefile.am +++ b/src/lib/runtime/ns/pid/Makefile.am @@ -4,7 +4,7 @@ CLEANFILES = core.* *~ *.la AM_CFLAGS = -Wall -fpie AM_LDFLAGS = -pie -AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) noinst_LTLIBRARIES = libinternal.la libinternal_la_SOURCES = pid.c diff --git a/src/lib/runtime/ns/pid/pid.c b/src/lib/runtime/ns/pid/pid.c index 8caf130f2a..308bdbbb10 100644 --- a/src/lib/runtime/ns/pid/pid.c +++ b/src/lib/runtime/ns/pid/pid.c @@ -40,6 +40,7 @@ #include "util/privilege.h" #include "util/fork.h" #include "util/registry.h" +#include "util/daemon.h" #include "util/setns.h" @@ -79,12 +80,16 @@ int _singularity_runtime_ns_pid(void) { return(0); } -int _singularity_runtime_ns_pid_join(void) { - int ns_fd = atoi(singularity_registry_get("DAEMON_NS_FD")); +int _singularity_runtime_ns_pid_join(int ns_fd) { int pid_fd; /* Attempt to open /proc/[PID]/ns/pid */ singularity_priv_escalate(); + if ( ! singularity_daemon_own_namespace("pid") ) { + singularity_priv_drop(); + return(0); + } + pid_fd = openat(ns_fd, "pid", O_RDONLY); if( pid_fd == -1 ) { diff --git a/src/lib/runtime/ns/pid/pid.h b/src/lib/runtime/ns/pid/pid.h index 83fef51425..ef98ac2fc7 100644 --- a/src/lib/runtime/ns/pid/pid.h +++ b/src/lib/runtime/ns/pid/pid.h @@ -24,7 +24,7 @@ #define __SINGULARITY_RUNTIME_NS_PID_H_ extern int _singularity_runtime_ns_pid(void); -extern int _singularity_runtime_ns_pid_join(void); +extern int _singularity_runtime_ns_pid_join(int ns_fd); #endif /* __SINGULARITY_RUNTIME_NS_PID_H */ diff --git a/src/lib/runtime/ns/user/Makefile.am b/src/lib/runtime/ns/user/Makefile.am new file mode 100644 index 0000000000..1ab3ee0a5b --- /dev/null +++ b/src/lib/runtime/ns/user/Makefile.am @@ -0,0 +1,12 @@ +MAINTAINERCLEANFILES = Makefile.in +DISTCLEANFILES = Makefile +CLEANFILES = core.* *~ *.la + +AM_CFLAGS = -Wall -fpie +AM_LDFLAGS = -pie +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) + +noinst_LTLIBRARIES = libinternal.la +libinternal_la_SOURCES = user.c + +EXTRA_DIST = user.h diff --git a/src/lib/runtime/ns/user/user.c b/src/lib/runtime/ns/user/user.c new file mode 100644 index 0000000000..a64d47754b --- /dev/null +++ b/src/lib/runtime/ns/user/user.c @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2017, SingularityWare, LLC. All rights reserved. + * + * This software is licensed under a 3-clause BSD license. Please + * consult LICENSE file distributed with the sources of this project regarding + * your rights to use or distribute this software. + * + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util/file.h" +#include "util/util.h" +#include "util/message.h" +#include "util/config_parser.h" +#include "util/privilege.h" +#include "util/fork.h" +#include "util/registry.h" +#include "util/daemon.h" +#include "util/setns.h" + + +int _singularity_runtime_ns_user(void) { + + if ( singularity_priv_userns_enabled() ) { + uid_t uid = singularity_priv_getuid(); + gid_t gid = singularity_priv_getgid(); + char *target_uid_str = singularity_registry_get("USERNS_UID"); + char *target_gid_str = singularity_registry_get("USERNS_GID"); + long int target_uid = uid, target_gid = gid; + + singularity_message(VERBOSE, "Invoking the user namespace\n"); + + if ( target_uid_str != NULL ) { + if ( str2int(target_uid_str, &target_uid) < 0 ) { + singularity_message(ERROR, "Unable to convert target UID (%s) to integer: %s\n", target_uid_str, strerror(errno)); + ABORT(255); + } + } + if ( target_gid_str != NULL ) { + if ( str2int(target_gid_str, &target_gid) < 0 ) { + singularity_message(ERROR, "Unable to convert target GID (%s) to integer: %s\n", target_gid_str, strerror(errno)); + ABORT(255); + } + } + + singularity_message(DEBUG, "Attempting to virtualize the USER namespace\n"); + if ( unshare(CLONE_NEWUSER) != 0 ) { + singularity_message(ERROR, "Failed invoking the NEWUSER namespace runtime: %s\n", strerror(errno)); + ABORT(255); // If we are configured to use CLONE_NEWUSER, we should abort if that fails + } + + singularity_message(DEBUG, "Enabled user namespaces\n"); + + { + singularity_message(DEBUG, "Setting setgroups to: 'deny'\n"); + char *map_file = (char *) malloc(PATH_MAX); + snprintf(map_file, PATH_MAX-1, "/proc/%d/setgroups", getpid()); // Flawfinder: ignore + FILE *map_fp = fopen(map_file, "w+"); // Flawfinder: ignore + if ( map_fp != NULL ) { + singularity_message(DEBUG, "Updating setgroups: %s\n", map_file); + fprintf(map_fp, "deny\n"); + if ( fclose(map_fp) < 0 ) { + singularity_message(ERROR, "Failed to write deny to setgroup file %s: %s\n", map_file, strerror(errno)); + ABORT(255); + } + } else { + singularity_message(ERROR, "Could not write info to setgroups: %s\n", strerror(errno)); + ABORT(255); + } + free(map_file); + } + { + singularity_message(DEBUG, "Setting GID map to: '%i %i 1'\n", (gid_t)target_gid, gid); + char *map_file = (char *) malloc(PATH_MAX); + snprintf(map_file, PATH_MAX-1, "/proc/%d/gid_map", getpid()); // Flawfinder: ignore + FILE *map_fp = fopen(map_file, "w+"); // Flawfinder: ignore + if ( map_fp != NULL ) { + singularity_message(DEBUG, "Updating the parent gid_map: %s\n", map_file); + fprintf(map_fp, "%i %i 1\n", (gid_t)target_gid, gid); + if ( fclose(map_fp) < 0 ) { + singularity_message(ERROR, "Failed to write to GID map %s: %s\n", map_file, strerror(errno)); + ABORT(255); + } + } else { + singularity_message(ERROR, "Could not write parent info to gid_map: %s\n", strerror(errno)); + ABORT(255); + } + free(map_file); + } + { + singularity_message(DEBUG, "Setting UID map to: '%i %i 1'\n", (uid_t)target_uid, uid); + char *map_file = (char *) malloc(PATH_MAX); + snprintf(map_file, PATH_MAX-1, "/proc/%d/uid_map", getpid()); // Flawfinder: ignore + FILE *map_fp = fopen(map_file, "w+"); // Flawfinder: ignore + if ( map_fp != NULL ) { + singularity_message(DEBUG, "Updating the parent uid_map: %s\n", map_file); + fprintf(map_fp, "%i %i 1\n", (uid_t)target_uid, uid); + if ( fclose(map_fp) < 0 ) { + singularity_message(ERROR, "Failed to write to UID map %s: %s\n", map_file, strerror(errno)); + ABORT(255); + } + } else { + singularity_message(ERROR, "Could not write parent info to uid_map: %s\n", strerror(errno)); + ABORT(255); + } + free(map_file); + } + } + + return(0); +} + +int _singularity_runtime_ns_user_join(int ns_fd) { + int user_fd; + + if ( singularity_priv_userns_enabled() ) { + if ( ! singularity_daemon_own_namespace("user") ) { + return(0); + } + + /* Attempt to open /proc/[PID]/ns/user */ + user_fd = openat(ns_fd, "user", O_RDONLY); + + if( user_fd == -1 ) { + singularity_message(ERROR, "Could not open USER NS fd: %s\n", strerror(errno)); + ABORT(255); + } + + singularity_message(DEBUG, "Attempting to join USER namespace\n"); + if ( setns(user_fd, CLONE_NEWUSER) < 0 ) { + singularity_message(ERROR, "Could not join USER namespace: %s\n", strerror(errno)); + ABORT(255); + } + singularity_message(DEBUG, "Successfully joined USER namespace\n"); + + close(user_fd); + } + + return(0); +} diff --git a/src/lib/runtime/ns/user/user.h b/src/lib/runtime/ns/user/user.h new file mode 100644 index 0000000000..2142d3428f --- /dev/null +++ b/src/lib/runtime/ns/user/user.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2017, SingularityWare, LLC. All rights reserved. + * + * This software is licensed under a 3-clause BSD license. Please + * consult LICENSE file distributed with the sources of this project regarding + * your rights to use or distribute this software. + * + */ + +#ifndef __SINGULARITY_RUNTIME_NS_USER_H_ +#define __SINGULARITY_RUNTIME_NS_USER_H_ + +extern int _singularity_runtime_ns_user(void); +extern int _singularity_runtime_ns_user_join(int ns_fd); + +#endif /* __SINGULARITY_RUNTIME_NS_USER_H */ + diff --git a/src/lib/runtime/ns/uts/Makefile.am b/src/lib/runtime/ns/uts/Makefile.am new file mode 100644 index 0000000000..f9da0e2019 --- /dev/null +++ b/src/lib/runtime/ns/uts/Makefile.am @@ -0,0 +1,12 @@ +MAINTAINERCLEANFILES = Makefile.in +DISTCLEANFILES = Makefile +CLEANFILES = core.* *~ *.la + +AM_CFLAGS = -Wall -fpie +AM_LDFLAGS = -pie +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) + +noinst_LTLIBRARIES = libinternal.la +libinternal_la_SOURCES = uts.c + +EXTRA_DIST = uts.h diff --git a/src/lib/runtime/ns/uts/uts.c b/src/lib/runtime/ns/uts/uts.c new file mode 100644 index 0000000000..f6c9d26066 --- /dev/null +++ b/src/lib/runtime/ns/uts/uts.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2017, SingularityWare, LLC. All rights reserved. + * + * This software is licensed under a 3-clause BSD license. Please + * consult LICENSE file distributed with the sources of this project regarding + * your rights to use or distribute this software. + * + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util/file.h" +#include "util/util.h" +#include "util/message.h" +#include "util/config_parser.h" +#include "util/privilege.h" +#include "util/fork.h" +#include "util/registry.h" +#include "util/daemon.h" +#include "util/setns.h" + + +int _singularity_runtime_ns_uts(void) { + char *hostname = singularity_registry_get("HOSTNAME"); + size_t hostname_len; + + if ( hostname ) { + hostname_len = strlen(hostname); + if ( hostname_len > HOST_NAME_MAX ) { + singularity_message(WARNING, "Hostname too long, truncated to %d bytes length\n", HOST_NAME_MAX); + hostname_len = HOST_NAME_MAX; + } + } + + if ( singularity_registry_get("UNSHARE_UTS") == NULL ) { + /* UTS namespace is enforced for root user */ + if ( singularity_priv_getuid() != 0 ) { + singularity_message(VERBOSE2, "Not virtualizing UTS namespace on user request\n"); + return(0); + } + } + +#ifdef NS_CLONE_NEWUTS + singularity_message(DEBUG, "Using UTS namespace: CLONE_NEWUTS\n"); + singularity_priv_escalate(); + singularity_message(DEBUG, "Virtualizing UTS namespace\n"); + if ( unshare(CLONE_NEWUTS) < 0 ) { + singularity_message(ERROR, "Could not virtualize UTS namespace: %s\n", strerror(errno)); + ABORT(255); + } + if ( hostname && sethostname(hostname, hostname_len) < 0 ) { + singularity_message(ERROR, "Could not set hostname %s: %s\n", hostname, strerror(errno)); + ABORT(255); + } + singularity_priv_drop(); + +#else + singularity_message(WARNING, "Skipping UTS namespace creation, support not available on host\n"); + return(0); +#endif + + return(0); +} + +int _singularity_runtime_ns_uts_join(int ns_fd) { + int uts_fd; + + /* Attempt to open /proc/[PID]/ns/pid */ + singularity_priv_escalate(); + if ( ! singularity_daemon_own_namespace("uts") ) { + singularity_priv_drop(); + return(0); + } + uts_fd = openat(ns_fd, "uts", O_RDONLY); + + if( uts_fd == -1 ) { + /* If no IPC file exists, continue without IPC NS */ + singularity_message(WARNING, "Skipping UTS namespace creation, support not available on host\n"); + return(0); + } + + singularity_message(DEBUG, "Attempting to join UTS namespace\n"); + if ( setns(uts_fd, CLONE_NEWUTS) < 0 ) { + singularity_message(ERROR, "Could not join UTS namespace: %s\n", strerror(errno)); + ABORT(255); + } + singularity_priv_drop(); + singularity_message(DEBUG, "Successfully joined UTS namespace\n"); + + close(uts_fd); + return(0); +} diff --git a/src/lib/runtime/ns/uts/uts.h b/src/lib/runtime/ns/uts/uts.h new file mode 100644 index 0000000000..8c383bc5eb --- /dev/null +++ b/src/lib/runtime/ns/uts/uts.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2017, SingularityWare, LLC. All rights reserved. + * + * This software is licensed under a 3-clause BSD license. Please + * consult LICENSE file distributed with the sources of this project regarding + * your rights to use or distribute this software. + * + */ + +#ifndef __SINGULARITY_RUNTIME_NS_UTS_H_ +#define __SINGULARITY_RUNTIME_NS_UTS_H_ + +extern int _singularity_runtime_ns_uts(void); +extern int _singularity_runtime_ns_uts_join(int ns_fd); + +#endif /* __SINGULARITY_RUNTIME_NS_UTS_H */ + diff --git a/src/lib/runtime/overlayfs/Makefile.am b/src/lib/runtime/overlayfs/Makefile.am index 8c4ad2ad4c..bd4a7891b3 100644 --- a/src/lib/runtime/overlayfs/Makefile.am +++ b/src/lib/runtime/overlayfs/Makefile.am @@ -4,7 +4,7 @@ CLEANFILES = core.* *~ *.la AM_CFLAGS = -Wall -fpie AM_LDFLAGS = -pie -AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) noinst_LTLIBRARIES = libinternal.la libinternal_la_SOURCES = overlayfs.c diff --git a/src/lib/runtime/overlayfs/overlayfs.c b/src/lib/runtime/overlayfs/overlayfs.c index 07b1b30897..8553fff6e9 100644 --- a/src/lib/runtime/overlayfs/overlayfs.c +++ b/src/lib/runtime/overlayfs/overlayfs.c @@ -39,6 +39,7 @@ #include "util/message.h" #include "util/config_parser.h" #include "util/privilege.h" +#include "util/suid.h" #include "util/mount.h" #include "lib/image/image.h" @@ -47,6 +48,11 @@ int _singularity_runtime_overlayfs(void) { + int secure_flags = MS_NOSUID | MS_NODEV; + + if ( singularity_allow_container_setuid() ) { + secure_flags &= ~MS_NOSUID; + } singularity_priv_escalate(); singularity_message(DEBUG, "Creating overlay_final directory: %s\n", CONTAINER_FINALDIR); @@ -123,7 +129,7 @@ int _singularity_runtime_overlayfs(void) { singularity_priv_escalate(); singularity_message(DEBUG, "Mounting overlay tmpfs: %s\n", overlay_mount); - if ( singularity_mount("tmpfs", overlay_mount, "tmpfs", MS_NOSUID | MS_NODEV, size) < 0 ){ + if ( singularity_mount("tmpfs", overlay_mount, "tmpfs", secure_flags, size) < 0 ){ singularity_message(ERROR, "Failed to mount overlay tmpfs %s: %s\n", overlay_mount, strerror(errno)); ABORT(255); } @@ -156,7 +162,7 @@ int _singularity_runtime_overlayfs(void) { } singularity_message(VERBOSE, "Mounting overlay with options: %s\n", overlay_options); - int result = singularity_mount("OverlayFS", overlay_final, "overlay", MS_NOSUID | MS_NODEV, overlay_options); + int result = singularity_mount("OverlayFS", overlay_final, "overlay", secure_flags, overlay_options); if (result < 0) { if ( (errno == EPERM) || ( try_overlay && ( errno == ENODEV ) ) ) { singularity_message(VERBOSE, "Singularity overlay mount did not work (%s), continuing without it\n", strerror(errno)); @@ -183,7 +189,7 @@ int _singularity_runtime_overlayfs(void) { // If we got here, assume we are not overlaying, so we must bind to final directory singularity_priv_escalate(); singularity_message(DEBUG, "Binding container directory to final home %s->%s\n", CONTAINER_MOUNTDIR, CONTAINER_FINALDIR); - if ( singularity_mount(CONTAINER_MOUNTDIR, CONTAINER_FINALDIR, NULL, MS_BIND|MS_NOSUID|MS_REC|MS_NODEV, NULL) < 0 ) { + if ( singularity_mount(CONTAINER_MOUNTDIR, CONTAINER_FINALDIR, NULL, MS_BIND|MS_REC|secure_flags, NULL) < 0 ) { singularity_message(ERROR, "Could not bind mount container to final home %s->%s: %s\n", CONTAINER_MOUNTDIR, CONTAINER_FINALDIR, strerror(errno)); return 1; } diff --git a/src/lib/runtime/runtime.h b/src/lib/runtime/runtime.h index 6dd237c22f..1b1fb6fb69 100644 --- a/src/lib/runtime/runtime.h +++ b/src/lib/runtime/runtime.h @@ -31,6 +31,8 @@ extern int singularity_runtime_ns(unsigned int flags); #define SR_NS_IPC 2 #define SR_NS_MNT 4 #define SR_NS_NET 8 +#define SR_NS_UTS 16 +#define SR_NS_USER 32 #define SR_NS_ALL 255 // Setup/initialize the overlayFS diff --git a/src/lib/sif/Makefile.am b/src/lib/sif/Makefile.am index 5616472e31..af5061cd6e 100644 --- a/src/lib/sif/Makefile.am +++ b/src/lib/sif/Makefile.am @@ -3,7 +3,7 @@ DISTCLEANFILES = Makefile CLEANFILES = core.* *~ *.la AM_CFLAGS = -Wall -fpie -fPIC AM_LDFLAGS = -pie -AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) distlibdir = $(libdir)/singularity distincludedir = $(includedir)/singularity @@ -18,13 +18,3 @@ libsingularity_sif_la_SOURCES = libsingularity_sif_la_LIBADD = $(noinst_LTLIBRARIES) libsingularity_sif_la_LDFLAGS = -version-info 1:0:0 libsingularity_sif_la_CFLAGS = $(AM_CFLAGS) - -# These are kludges so they don't remove the $(DEPDIR) in ../../util/ otherwise -# the clean will fail when other Makefile tries to remove those directories -distclean: distclean-recursive - -rm ./$(DEPDIR) - -rm -f Makefile - -maintainer-clean: maintainer-clean-recursive - -rm ./$(DEPDIR) - -rm -f Makefile diff --git a/src/lib/sif/list.c b/src/lib/sif/list.c index fc85494cb1..16587d2ed4 100644 --- a/src/lib/sif/list.c +++ b/src/lib/sif/list.c @@ -92,13 +92,13 @@ listdelete(Node *head, void *elem, Searchfn fn) } int -listforall(Node *head, Actionfn fn) +listforall(Node *head, Actionfn fn, void *data) { int ret; Node *e; for(e = head->next; e != NULL; e = e->next){ - ret = fn(e->elem); + ret = fn(e->elem, data); if(ret < 0) return ret; } diff --git a/src/lib/sif/list.h b/src/lib/sif/list.h index 0d9abe7265..c8de84ecc2 100644 --- a/src/lib/sif/list.h +++ b/src/lib/sif/list.h @@ -20,11 +20,11 @@ struct Node{ }; typedef int (*Searchfn)(void *cur, void *elem); -typedef int (*Actionfn)(void *elem); +typedef int (*Actionfn)(void *elem, void *data); Node *listcreate(void *elem); void listaddfront(Node *head, Node *new); void listaddtail(Node *head, Node *new); Node *listfind(Node *head, void *elem, Searchfn fn); Node *listdelete(Node *head, void *elem, Searchfn fn); -int listforall(Node *head, Actionfn fn); +int listforall(Node *head, Actionfn fn, void *data); diff --git a/src/lib/sif/sif.c b/src/lib/sif/sif.c index 170efde6bc..5f1b12679c 100644 --- a/src/lib/sif/sif.c +++ b/src/lib/sif/sif.c @@ -12,9 +12,7 @@ * except according to the terms contained in the LICENSE.md file. */ -#ifndef _XOPEN_SOURCE -#define _XOPEN_SOURCE 700 -#endif +#define _GNU_SOURCE #include #include @@ -31,21 +29,12 @@ #include "list.h" #include "sif.h" +#include "sifaccess.h" Siferrno siferrno; -/* count the next descriptor id to generate when adding a new descriptor */ -static int desccounter = 1; - -typedef struct Siflayout Siflayout; -static struct Siflayout{ - Sifinfo *info; - char *descptr; - char *dataptr; -} siflayout; - enum{ - REGION_GROWSIZE = 4096 /* Initial and grow size of data object descriptors */ + REGION_GROWSIZE = sizeof(Sifdescriptor)*32 /* descr. area grow size */ }; @@ -69,7 +58,7 @@ sif_strerror(Siferrno siferrno) case SIF_EUARCH: return "unknown host architecture while validating image"; case SIF_ESIFVER: return "unsupported SIF version while validating image"; case SIF_ERARCH: return "architecture mismatch while validating image"; - case SIF_ENODESC: return "cannot find data object descriptors while validating image"; + case SIF_ENODESC: return "cannot find data object descriptor(s)"; case SIF_ENODEF: return "cannot find definition file descriptor"; case SIF_ENOENV: return "cannot find envvar descriptor"; case SIF_ENOLAB: return "cannot find jason label descriptor"; @@ -156,9 +145,8 @@ sif_load(char *filename, Sifinfo *info, int rdonly) int i; int oflags, mprot, mflags; struct stat st; - char *p; + Sifdescriptor *desc; - siflayout.info = info; memset(info, 0, sizeof(Sifinfo)); if(filename == NULL){ @@ -198,43 +186,25 @@ sif_load(char *filename, Sifinfo *info, int rdonly) if(sif_validate(info) < 0) goto bail_unmap; - /* init the descriptor counter for next id to use */ - desccounter = info->header.ndesc + 1; - - /* set output file write pointers (descriptors and data) */ - siflayout.descptr = info->mapstart + info->header.descoff+info->header.desclen; - siflayout.dataptr = info->mapstart + info->header.dataoff+info->header.datalen; + /* point to the first descriptor in SIF file */ + desc = (Sifdescriptor *)(info->mapstart + sizeof(Sifheader)); + info->nextid = desc->cm.id; /* build up the list of SIF data object descriptors */ - for(i = 0, p = info->mapstart+sizeof(Sifheader); i < info->header.ndesc; i++){ + for(i = 0; i < info->header.ndesc; i++){ Node *n; - Sifcommon *cm = (Sifcommon *)p; - n = listcreate(cm); + if(desc->cm.id > info->nextid) + info->nextid = desc->cm.id; + + n = listcreate(desc++); if(n == NULL){ siferrno = SIF_ELNOMEM; goto bail_unmap; } listaddtail(&info->deschead, n); - - switch(cm->datatype){ - case DATA_DEFFILE: - p += sizeof(Sifdeffile); - break; - case DATA_ENVVAR: - p += sizeof(Sifenvvar); - break; - case DATA_LABELS: - p += sizeof(Siflabels); - break; - case DATA_PARTITION: - p += sizeof(Sifpartition); - break; - case DATA_SIGNATURE: - p += sizeof(Sifsignature); - break; - } } + info->nextid++; return 0; @@ -273,15 +243,15 @@ grow_descregion(Sifheader *header) } static int -update_headeroffsets(Sifheader *header, size_t descsize, size_t datasize) +update_headeroffsets(Sifheader *header, size_t datasize) { header->ndesc++; - if((header->descoff + header->desclen + descsize) >= header->dataoff){ + if((header->descoff + header->desclen + sizeof(Sifdescriptor)) >= header->dataoff){ siferrno = SIF_EDNOMEM; return -1; } - header->desclen += descsize; + header->desclen += sizeof(Sifdescriptor); header->datalen += datasize; return 0; @@ -290,10 +260,7 @@ update_headeroffsets(Sifheader *header, size_t descsize, size_t datasize) static int prepddesc(void *elem) { - Ddesc *d = elem; - - if(update_headeroffsets(&siflayout.info->header, sizeof(Sifdeffile), d->len)) - return -1; + Defdesc *d = elem; /* prep input file (definition file) */ d->fd = open(d->fname, O_RDONLY); @@ -302,7 +269,7 @@ prepddesc(void *elem) return -1; } /* map input definition file into memory for SIF creation coming up after */ - d->mapstart = mmap(NULL, d->len, PROT_READ, MAP_PRIVATE, d->fd, 0); + d->mapstart = mmap(NULL, d->cm.len, PROT_READ, MAP_PRIVATE, d->fd, 0); if(d->mapstart == MAP_FAILED){ siferrno = SIF_EMAPDEF; close(d->fd); @@ -315,20 +282,13 @@ prepddesc(void *elem) static int prepedesc(void *elem) { - Edesc *e = elem; - - if(update_headeroffsets(&siflayout.info->header, sizeof(Sifenvvar), e->len) < 0) - return -1; return 0; } static int prepldesc(void *elem) { - Ldesc *l = elem; - - if(update_headeroffsets(&siflayout.info->header, sizeof(Siflabels), l->len) < 0) - return -1; + Labeldesc *l = elem; /* prep input file (JSON-label file) */ l->fd = open(l->fname, O_RDONLY); @@ -337,7 +297,7 @@ prepldesc(void *elem) return -1; } /* map input JSON-label file into memory for SIF creation coming up after */ - l->mapstart = mmap(NULL, l->len, PROT_READ, MAP_PRIVATE, l->fd, 0); + l->mapstart = mmap(NULL, l->cm.len, PROT_READ, MAP_PRIVATE, l->fd, 0); if(l->mapstart == MAP_FAILED){ siferrno = SIF_EMAPLAB; close(l->fd); @@ -349,10 +309,7 @@ prepldesc(void *elem) static int preppdesc(void *elem) { - Pdesc *p = elem; - - if(update_headeroffsets(&siflayout.info->header, sizeof(Sifpartition), p->len) < 0) - return -1; + Partdesc *p = elem; /* prep input file (partition file) */ p->fd = open(p->fname, O_RDONLY); @@ -361,7 +318,7 @@ preppdesc(void *elem) return -1; } /* map input partition file into memory for SIF creation coming up after */ - p->mapstart = mmap(NULL, p->len, PROT_READ, MAP_PRIVATE, p->fd, 0); + p->mapstart = mmap(NULL, p->cm.len, PROT_READ, MAP_PRIVATE, p->fd, 0); if(p->mapstart == MAP_FAILED){ siferrno = SIF_EMAPPAR; close(p->fd); @@ -373,28 +330,31 @@ preppdesc(void *elem) static int prepsdesc(void *elem) { - Sdesc *s = elem; - - if(update_headeroffsets(&siflayout.info->header, sizeof(Sifsignature), s->len) < 0) - return -1; return 0; } static int -prepdesc(void *elem) +prepdesc(void *elem, void *data) { - Sifdatatype *dt = (Sifdatatype *)elem; - switch(*dt){ + Eleminfo *e = elem; + + /* for each eleminfo node to prepare, set the info ptr */ + e->info = data; + + if(update_headeroffsets(&e->info->header, e->cm.len)) + return -1; + + switch(e->cm.datatype){ case DATA_DEFFILE: - return prepddesc(elem); + return prepddesc(&e->defdesc); case DATA_ENVVAR: - return prepedesc(elem); + return prepedesc(&e->envdesc); case DATA_LABELS: - return prepldesc(elem); + return prepldesc(&e->labeldesc); case DATA_PARTITION: - return preppdesc(elem); + return preppdesc(&e->partdesc); case DATA_SIGNATURE: - return prepsdesc(elem); + return prepsdesc(&e->sigdesc); default: siferrno = SIF_EUDESC; return -1; @@ -405,23 +365,25 @@ prepdesc(void *elem) static int putddesc(void *elem) { - Ddesc *d = elem; - Sifdeffile *desc = (Sifdeffile *)siflayout.descptr; + Eleminfo *e = elem; + + e->desc = (Sifdescriptor *)(e->info->mapstart + e->info->header.descoff + e->info->header.desclen); /* write data object descriptor */ - desc->cm.datatype = DATA_DEFFILE; - desc->cm.id = desccounter++; - desc->cm.groupid = d->groupid; - desc->cm.link = d->link; - desc->cm.fileoff = siflayout.dataptr - siflayout.info->mapstart; - desc->cm.filelen = d->len; + e->desc->cm.datatype = DATA_DEFFILE; + e->desc->cm.id = e->info->nextid++; + e->info->header.ndesc++; + e->desc->cm.groupid = e->cm.groupid; + e->desc->cm.link = e->cm.link; + e->desc->cm.fileoff = e->info->header.dataoff + e->info->header.datalen; + e->desc->cm.filelen = e->cm.len; /* write data object */ - memcpy(siflayout.dataptr, d->mapstart, desc->cm.filelen); + memcpy(e->info->mapstart + e->desc->cm.fileoff, e->defdesc.mapstart, e->desc->cm.filelen); - /* increment file map pointers */ - siflayout.descptr += sizeof(Sifdeffile); - siflayout.dataptr += desc->cm.filelen; + /* increment header desclen and datalen */ + e->info->header.desclen += sizeof(Sifdescriptor); + e->info->header.datalen += e->desc->cm.filelen; return 0; } @@ -429,23 +391,25 @@ putddesc(void *elem) static int putedesc(void *elem) { - Edesc *e = elem; - Sifenvvar *desc = (Sifenvvar *)siflayout.descptr; + Eleminfo *e = elem; + + e->desc = (Sifdescriptor *)(e->info->mapstart + e->info->header.descoff + e->info->header.desclen); /* write data object descriptor */ - desc->cm.datatype = DATA_ENVVAR; - desc->cm.id = desccounter++; - desc->cm.groupid = e->groupid; - desc->cm.link = e->link; - desc->cm.fileoff = siflayout.dataptr - siflayout.info->mapstart; - desc->cm.filelen = e->len; + e->desc->cm.datatype = DATA_ENVVAR; + e->desc->cm.id = e->info->nextid++; + e->info->header.ndesc++; + e->desc->cm.groupid = e->cm.groupid; + e->desc->cm.link = e->cm.link; + e->desc->cm.fileoff = e->info->header.dataoff + e->info->header.datalen; + e->desc->cm.filelen = e->cm.len; /* write data object */ - memcpy(siflayout.dataptr, e->vars, desc->cm.filelen); + memcpy(e->info->mapstart + e->desc->cm.fileoff, e->envdesc.vars, e->desc->cm.filelen); - /* increment file map pointers */ - siflayout.descptr += sizeof(Sifenvvar); - siflayout.dataptr += desc->cm.filelen; + /* increment header desclen and datalen */ + e->info->header.desclen += sizeof(Sifdescriptor); + e->info->header.datalen += e->desc->cm.filelen; return 0; } @@ -453,23 +417,25 @@ putedesc(void *elem) static int putldesc(void *elem) { - Ldesc *l = elem; - Siflabels *desc = (Siflabels *)siflayout.descptr; + Eleminfo *e = elem; + + e->desc = (Sifdescriptor *)(e->info->mapstart + e->info->header.descoff + e->info->header.desclen); /* write data object descriptor */ - desc->cm.datatype = DATA_LABELS; - desc->cm.id = desccounter++; - desc->cm.groupid = l->groupid; - desc->cm.link = l->link; - desc->cm.fileoff = siflayout.dataptr - siflayout.info->mapstart; - desc->cm.filelen = l->len; + e->desc->cm.datatype = DATA_LABELS; + e->desc->cm.id = e->info->nextid++; + e->info->header.ndesc++; + e->desc->cm.groupid = e->cm.groupid; + e->desc->cm.link = e->cm.link; + e->desc->cm.fileoff = e->info->header.dataoff + e->info->header.datalen; + e->desc->cm.filelen = e->cm.len; /* write data object */ - memcpy(siflayout.dataptr, l->mapstart, desc->cm.filelen); + memcpy(e->info->mapstart + e->desc->cm.fileoff, e->labeldesc.mapstart, e->desc->cm.filelen); - /* increment file map pointers */ - siflayout.descptr += sizeof(Siflabels); - siflayout.dataptr += desc->cm.filelen; + /* increment header desclen and datalen */ + e->info->header.desclen += sizeof(Sifdescriptor); + e->info->header.datalen += e->desc->cm.filelen; return 0; } @@ -477,26 +443,28 @@ putldesc(void *elem) static int putpdesc(void *elem) { - Pdesc *p = elem; - Sifpartition *desc = (Sifpartition *)siflayout.descptr; + Eleminfo *e = elem; + + e->desc = (Sifdescriptor *)(e->info->mapstart + e->info->header.descoff + e->info->header.desclen); /* write data object descriptor */ - desc->cm.datatype = DATA_PARTITION; - desc->cm.id = desccounter++; - desc->cm.groupid = p->groupid; - desc->cm.link = p->link; - desc->cm.fileoff = siflayout.dataptr - siflayout.info->mapstart; - desc->cm.filelen = p->len; - desc->fstype = p->fstype; - desc->parttype = p->parttype; - strncpy(desc->content, p->content, sizeof(desc->content)-1); + e->desc->cm.datatype = DATA_PARTITION; + e->desc->cm.id = e->info->nextid++; + e->info->header.ndesc++; + e->desc->cm.groupid = e->cm.groupid; + e->desc->cm.link = e->cm.link; + e->desc->cm.fileoff = e->info->header.dataoff + e->info->header.datalen; + e->desc->cm.filelen = e->cm.len; + e->desc->part.fstype = e->partdesc.fstype; + e->desc->part.parttype = e->partdesc.parttype; + strncpy(e->desc->part.content, e->partdesc.content, sizeof(e->desc->part.content)-1); /* write data object */ - memcpy(siflayout.dataptr, p->mapstart, desc->cm.filelen); + memcpy(e->info->mapstart + e->desc->cm.fileoff, e->partdesc.mapstart, e->desc->cm.filelen); - /* increment file map pointers */ - siflayout.descptr += sizeof(Sifpartition); - siflayout.dataptr += desc->cm.filelen; + /* increment header desclen and datalen */ + e->info->header.desclen += sizeof(Sifdescriptor); + e->info->header.datalen += e->desc->cm.filelen; return 0; } @@ -504,34 +472,37 @@ putpdesc(void *elem) static int putsdesc(void *elem) { - Sdesc *s = elem; - Sifsignature *desc = (Sifsignature *)siflayout.descptr; + Eleminfo *e = elem; + + e->desc = (Sifdescriptor *)(e->info->mapstart + e->info->header.descoff + e->info->header.desclen); /* write data object descriptor */ - desc->cm.datatype = DATA_SIGNATURE; - desc->cm.id = desccounter++; - desc->cm.groupid = s->groupid; - desc->cm.link = s->link; - desc->cm.fileoff = siflayout.dataptr - siflayout.info->mapstart; - desc->cm.filelen = s->len; - desc->hashtype = s->hashtype; - strncpy(desc->entity, s->entity, sizeof(desc->entity)-1); + e->desc->cm.datatype = DATA_SIGNATURE; + e->desc->cm.id = e->info->nextid++; + e->info->header.ndesc++; + e->desc->cm.groupid = e->cm.groupid; + e->desc->cm.link = e->cm.link; + e->desc->cm.fileoff = e->info->header.dataoff + e->info->header.datalen; + e->desc->cm.filelen = e->cm.len; + e->desc->sig.hashtype = e->sigdesc.hashtype; + strncpy(e->desc->sig.entity, e->sigdesc.entity, sizeof(e->desc->sig.entity)-1); /* write data object */ - memcpy(siflayout.dataptr, s->signature, desc->cm.filelen); + memcpy(e->info->mapstart + e->desc->cm.fileoff, e->sigdesc.signature, e->desc->cm.filelen); - /* increment file map pointers */ - siflayout.descptr += sizeof(Sifsignature); - siflayout.dataptr += desc->cm.filelen; + /* increment header desclen and datalen */ + e->info->header.desclen += sizeof(Sifdescriptor); + e->info->header.datalen += e->desc->cm.filelen; return 0; } static int -putdesc(void *elem) +putdesc(void *elem, void *data) { - Sifdatatype *dt = (Sifdatatype *)elem; - switch(*dt){ + Eleminfo *e = elem; + + switch(e->cm.datatype){ case DATA_DEFFILE: return putddesc(elem); case DATA_ENVVAR: @@ -552,9 +523,9 @@ putdesc(void *elem) static int cleanupddesc(void *elem) { - Ddesc *d = elem; + Defdesc *d = elem; - munmap(d->mapstart, d->len); + munmap(d->mapstart, d->cm.len); close(d->fd); return 0; @@ -571,9 +542,9 @@ cleanupedesc(void *elem) static int cleanuppdesc(void *elem) { - Pdesc *p = elem; + Partdesc *p = elem; - munmap(p->mapstart, p->len); + munmap(p->mapstart, p->cm.len); close(p->fd); return 0; @@ -582,9 +553,9 @@ cleanuppdesc(void *elem) static int cleanupldesc(void *elem) { - Ldesc *l = elem; + Labeldesc *l = elem; - munmap(l->mapstart, l->len); + munmap(l->mapstart, l->cm.len); close(l->fd); return 0; @@ -599,10 +570,10 @@ cleanupsdesc(void *elem) } static int -cleanupdesc(void *elem) +cleanupdesc(void *elem, void *data) { - Sifdatatype *dt = (Sifdatatype *)elem; - switch(*dt){ + Cmdesc *desc = (Cmdesc *)elem; + switch(desc->datatype){ case DATA_DEFFILE: return cleanupddesc(elem); case DATA_ENVVAR: @@ -621,39 +592,82 @@ cleanupdesc(void *elem) } int -sif_putdataobj(Sifinfo *info, Sifdatatype *datatype) +sif_putdataobj(Eleminfo *e, Sifinfo *info) { - if(prepdesc(datatype) < 0) - return -1; + size_t oldsize; + void *oldmap; + int oldndesc = info->header.ndesc; + size_t olddesclen = info->header.desclen; + size_t olddatalen = info->header.datalen; - if(munmap(info->mapstart, info->filesize) < 0){ - siferrno = SIF_EOUNMAP; + if(prepdesc(e, info) < 0) return -1; - } + + oldmap = info->mapstart; + oldsize = info->filesize; + info->filesize = info->header.dataoff + info->header.datalen; if(posix_fallocate(info->fd, 0, info->filesize) != 0){ siferrno = SIF_EFALLOC; return -1; } - info->mapstart = mmap(NULL, info->filesize, PROT_WRITE, MAP_SHARED, info->fd, 0); + + info->mapstart = mremap(oldmap, oldsize, info->filesize, MREMAP_MAYMOVE); if(info->mapstart == MAP_FAILED){ siferrno = SIF_EOMAP; return -1; } + /* reset values modified by update_headeroffsets() */ + info->header.ndesc = oldndesc; + info->header.desclen = olddesclen; + info->header.datalen = olddatalen; + + putdesc(e, NULL); + cleanupdesc(&e->cm, NULL); + /* write down the modified header */ info->header.mtime = time(NULL); memcpy(info->mapstart, &info->header, sizeof(Sifheader)); - putdesc(datatype); - cleanupdesc(datatype); - return 0; } int -sif_deldataobj(Sifinfo *info, int id) +sif_deldataobj(Sifinfo *info, int id, int flags) { + Sifdescriptor *desc; + + desc = sif_getdescid(info, id); + if(desc == NULL){ + siferrno = SIF_ENODESC; + return -1; + } + + /* zero out or remove data portion */ + switch(flags){ + case DEL_ZERO: + memset(info->mapstart + desc->cm.fileoff, 0, desc->cm.filelen); + break; + case DEL_COMPACT: + siferrno = SIF_ENOSUPP; + return -1; + } + + /* remove and shuffle descriptors */ + if(info->header.ndesc > 1){ + memmove(desc, (char *)desc + sizeof(Sifdescriptor), sizeof(Sifdescriptor)); + }else{ + memset(desc, 0, sizeof(Sifdescriptor)); + } + + /* write down the modified header */ + info->header.ndesc--; + info->header.mtime = time(NULL); + info->header.desclen -= sizeof(Sifdescriptor); + + memcpy(info->mapstart, &info->header, sizeof(Sifheader)); + return 0; } @@ -662,7 +676,6 @@ sif_create(Sifcreateinfo *cinfo) { Sifinfo info; - siflayout.info = &info; memset(&info, 0, sizeof(Sifinfo)); /* assemble the SIF global header from options (cinfo) */ @@ -671,13 +684,14 @@ sif_create(Sifcreateinfo *cinfo) memcpy(info.header.version, cinfo->sifversion, SIF_ARCH_LEN); memcpy(info.header.arch, cinfo->arch, SIF_ARCH_LEN); uuid_copy(info.header.uuid, cinfo->uuid); + info.nextid = 1; info.header.ctime = time(NULL); info.header.mtime = time(NULL); info.header.descoff = sizeof(Sifheader); info.header.dataoff = grow_descregion(&info.header); /* check the number of data object descriptors and sizes */ - if(listforall(&cinfo->deschead, prepdesc) < 0) + if(listforall(&cinfo->deschead, prepdesc, &info) < 0) return -1; if(info.header.ndesc == 0){ @@ -707,17 +721,17 @@ sif_create(Sifcreateinfo *cinfo) return -1; } - /* set output file write pointers (descriptors and data) */ - siflayout.descptr = info.mapstart; - siflayout.dataptr = info.mapstart + info.header.dataoff; + /* reset values modified by update_headeroffsets() */ + info.header.ndesc = 0; + info.header.desclen = 0; + info.header.datalen = 0; /* build SIF: header, data object descriptors, data objects */ - memcpy(siflayout.descptr, &info.header, sizeof(Sifheader)); - siflayout.descptr += sizeof(Sifheader); - listforall(&cinfo->deschead, putdesc); + listforall(&cinfo->deschead, putdesc, NULL); + memcpy(info.mapstart, &info.header, sizeof(Sifheader)); /* cleanup opened and mmap'ed input files (deffile, labels, partition) */ - listforall(&cinfo->deschead, cleanupdesc); + listforall(&cinfo->deschead, cleanupdesc, NULL); /* unmap and close resulting output file */ if(munmap(info.mapstart, info.header.dataoff + info.header.datalen) < 0){ @@ -732,4 +746,3 @@ sif_create(Sifcreateinfo *cinfo) return 0; } - diff --git a/src/lib/sif/sif.h b/src/lib/sif/sif.h index 5613f11171..463aa5c9c6 100644 --- a/src/lib/sif/sif.h +++ b/src/lib/sif/sif.h @@ -21,84 +21,68 @@ /* * Layout of a SIF file (example) * - * .================================================================. - * | GLOBAL HEADER: Sifheader | - * | - launch: "#!/usr/bin/env..." | - * | - magic: "SIF_MAGIC" | - * | - version: "1" | - * | - arch: "4" | - * | - uuid: b2659d4e-bd50-4ea5-bd17-eec5e54f918e | - * | - ctime: 1504657553 | - * | - ndesc: 4 | - * | - descoff: 88 | --. - * | - dataoff: 280 | | - * | - datalen: 42111314 | | - * |----------------------------------------------------------------| | - * | DESCR[0]: Siflabels | <-' - * | - Sifcommon | - * | - datatype: DATA_LABELS | - * | - groupid: inter-object relation | - * | - fileoff: #bytes from start | --. - * | - filelen: #bytes used | | - * |----------------------------------------------------------------| | - * | DESCR[1]: Sifdeffile | | - * | - Sifcommon | | - * | - datatype: DATA_LABELS | | - * | - groupid: inter-object relation | | - * | - fileoff: #bytes from start | ----. - * | - filelen: #bytes used | | | - * |----------------------------------------------------------------| | | - * | DESCR[2]: Sifenvvar | | | - * | - Sifcommon | | | - * | - datatype: DATA_LABELS | | | - * | - groupid: inter-object relation | | | - * | - fileoff: #bytes from start | ------. - * | - filelen: #bytes used | | | | - * |----------------------------------------------------------------| | | | - * | DESCR[3]: Sifsignature | | | | - * | - Sifcommon | | | | - * | - datatype: DATA_LABELS | | | | - * | - groupid: inter-object relation | | | | - * | - fileoff: #bytes from start | --------. - * | - filelen: #bytes used | | | | | - * | - hashtype: HASH_SHA384 | | | | | - * | - entity: "Joe Bloe ..." */ - SIF_CONTENT_LEN = 256, /* "RHEL 7.4 / kernel 3.10.0-693 / ..." */ + SIF_CONTENT_LEN = 64, /* "RHEL 7.4 / kernel 3.10.0-693 / ..." */ SIF_GROUP_MASK = 0xf0000000, /* groups start at that offset */ SIF_UNUSED_GROUP = SIF_GROUP_MASK,/* descriptor without a group */ @@ -161,6 +145,11 @@ typedef enum{ HASH_BLAKE2B } Sifhashtype; +enum{ + DEL_ZERO = 1, + DEL_COMPACT +}; + /* SIF data object descriptor info common to all object type */ typedef struct Sifcommon Sifcommon; struct Sifcommon{ @@ -207,6 +196,16 @@ struct Sifsignature{ char entity[SIF_ENTITY_LEN]; }; +typedef union Sifdescriptor Sifdescriptor; +union Sifdescriptor{ + Sifcommon cm; + Sifdeffile def; + Siflabels label; + Sifenvvar env; + Sifpartition part; + Sifsignature sig; +}; + /* Singularity image format (SIF) global header */ typedef struct Sifheader Sifheader; struct Sifheader{ @@ -233,6 +232,7 @@ struct Sifheader{ typedef struct Sifinfo Sifinfo; struct Sifinfo{ Sifheader header; /* the loaded SIF global header */ + int nextid; /* The next id to use for new descriptors */ int fd; /* file descriptor of opened SIF file */ size_t filesize; /* file size of the opened SIF file */ char *mapstart; /* memory map of opened SIF file */ @@ -245,67 +245,76 @@ struct Sifinfo{ * a new SIF file. Transient data not found in the final SIF file. */ -/* information needed to create an definition-file data object descriptor */ -typedef struct Ddesc Ddesc; -struct Ddesc{ +/* common information needed to create a data object descriptor */ +typedef struct Cmdesc Cmdesc; +struct Cmdesc{ Sifdatatype datatype; int groupid; int link; + size_t len; +}; + +/* information needed to create an definition-file data object descriptor */ +typedef struct Defdesc Defdesc; +struct Defdesc{ + Cmdesc cm; char *fname; int fd; unsigned char *mapstart; - size_t len; }; /* information needed to create an envvar data object descriptor */ -typedef struct Edesc Edesc; -struct Edesc{ - Sifdatatype datatype; - int groupid; - int link; +typedef struct Envdesc Envdesc; +struct Envdesc{ + Cmdesc cm; char *vars; - size_t len; }; /* information needed to create an JSON-labels data object descriptor */ -typedef struct Ldesc Ldesc; -struct Ldesc{ - Sifdatatype datatype; - int groupid; - int link; +typedef struct Labeldesc Labeldesc; +struct Labeldesc{ + Cmdesc cm; char *fname; int fd; unsigned char *mapstart; - size_t len; }; /* information needed to create an partition data object descriptor */ -typedef struct Pdesc Pdesc; -struct Pdesc{ - Sifdatatype datatype; - int groupid; - int link; +typedef struct Partdesc Partdesc; +struct Partdesc{ + Cmdesc cm; char *fname; int fd; unsigned char *mapstart; - size_t len; Siffstype fstype; Sifparttype parttype; char content[SIF_CONTENT_LEN]; }; /* information needed to create an signature data object descriptor */ -typedef struct Sdesc Sdesc; -struct Sdesc{ - Sifdatatype datatype; - int groupid; - int link; +typedef struct Sigdesc Sigdesc; +struct Sigdesc{ + Cmdesc cm; char *signature; - size_t len; Sifhashtype hashtype; char entity[SIF_ENTITY_LEN]; }; +/* Most SIF manipulations require Sifinfo and *desc */ +typedef struct Eleminfo Eleminfo; +struct Eleminfo{ + Sifinfo *info; + Sifdescriptor *desc; + union{ + Cmdesc cm; + Defdesc defdesc; + Envdesc envdesc; + Labeldesc labeldesc; + Partdesc partdesc; + Sigdesc sigdesc; + }; +}; + /* all creation info needed wrapped into a struct */ typedef struct Sifcreateinfo Sifcreateinfo; struct Sifcreateinfo{ @@ -335,7 +344,7 @@ typedef enum{ SIF_EUARCH, /* unknown host architecture while validating image */ SIF_ESIFVER, /* unsupported SIF version while validating image */ SIF_ERARCH, /* architecture mismatch while validating image */ - SIF_ENODESC, /* cannot find data object descriptors while validating image */ + SIF_ENODESC, /* cannot find data object descriptor(s) */ SIF_ENODEF, /* cannot find definition file descriptor */ SIF_ENOENV, /* cannot find envvar descriptor */ SIF_ENOLAB, /* cannot find jason label descriptor */ @@ -356,7 +365,8 @@ typedef enum{ SIF_EOMAP, /* cannot mmap SIF output file */ SIF_EOUNMAP, /* cannot unmmap SIF output file */ SIF_EOCLOSE, /* closing SIF file failed, file corrupted, don't use */ - SIF_EDNOMEM /* no more space to add new descriptors */ + SIF_EDNOMEM, /* no more space to add new descriptors */ + SIF_ENOSUPP /* operation not implemented/supported */ } Siferrno; @@ -372,7 +382,7 @@ int sif_load(char *filename, Sifinfo *info, int rdonly); int sif_unload(Sifinfo *info); int sif_create(Sifcreateinfo *cinfo); -int sif_putdataobj(Sifinfo *info, Sifdatatype *datatype); -int sif_deldataobj(Sifinfo *info, int id); +int sif_putdataobj(Eleminfo *e, Sifinfo *info); +int sif_deldataobj(Sifinfo *info, int id, int flags); #endif /* __SINGULARITY_SIF_H_ */ diff --git a/src/lib/sif/sifaccess.c b/src/lib/sif/sifaccess.c index 01dfd8ad89..5bc81f697f 100644 --- a/src/lib/sif/sifaccess.c +++ b/src/lib/sif/sifaccess.c @@ -84,37 +84,66 @@ sif_fsstr(Siffstype ftype) return "Unknown fstype"; } +char * +sif_hreadable(size_t value) +{ + static char conversion[32]; + int divs = 0; + + memset(conversion, 0, 32); + + for(; value; value>>=10) { + if(value < 1024) + break; + divs++; + } + + switch(divs) { + case 0: snprintf(conversion, 31, "%ld", value); + break; + case 1: snprintf(conversion, 31, "%ldKB", value); + break; + case 2: snprintf(conversion, 31, "%ldMB", value); + break; + case 3: snprintf(conversion, 31, "%ldGB", value); + break; + case 4: snprintf(conversion, 31, "%ldTB", value); + break; + } + + return conversion; +} + int -sif_printrow(void *elem) +sif_printrow(void *elem, void *data) { static char fposbuf[26]; - Sifcommon *cm = (Sifcommon *)elem; - Sifpartition *p = (Sifpartition *)elem; - Sifsignature *s = (Sifsignature *)elem; + Sifdescriptor *desc = elem; - printf("%-4d ", cm->id); - if(cm->groupid == SIF_UNUSED_GROUP) + printf("%-4d ", desc->cm.id); + if(desc->cm.groupid == SIF_UNUSED_GROUP) printf("|%-7s ", "NONE"); else - printf("|%-7d ", cm->groupid & ~SIF_GROUP_MASK); - if(cm->link == SIF_UNUSED_LINK) + printf("|%-7d ", desc->cm.groupid & ~SIF_GROUP_MASK); + if(desc->cm.link == SIF_UNUSED_LINK) printf("|%-7s ", "NONE"); else - printf("|%-7d ", cm->link); - sprintf(fposbuf, "|%ld-%ld ", cm->fileoff, cm->fileoff+cm->filelen-1); + printf("|%-7d ", desc->cm.link); + sprintf(fposbuf, "|%ld-%ld ", desc->cm.fileoff, + desc->cm.fileoff+desc->cm.filelen-1); printf("%-26s ", fposbuf); - switch(cm->datatype){ + switch(desc->cm.datatype){ case DATA_PARTITION: - printf("|%s (%s/%s)", sif_datastr(cm->datatype), - sif_fsstr(p->fstype), sif_partstr(p->parttype)); + printf("|%s (%s/%s)", sif_datastr(desc->cm.datatype), + sif_fsstr(desc->part.fstype), sif_partstr(desc->part.parttype)); break; case DATA_SIGNATURE: - printf("|%s (%s)", sif_datastr(cm->datatype), - sif_hashstr(s->hashtype)); + printf("|%s (%s)", sif_datastr(desc->cm.datatype), + sif_hashstr(desc->sig.hashtype)); break; default: - printf("|%s", sif_datastr(cm->datatype)); + printf("|%s", sif_datastr(desc->cm.datatype)); break; } printf("\n"); @@ -140,38 +169,36 @@ sif_printlist(Sifinfo *info) printf("%-4s %-8s %-8s %-26s %s\n", "ID", "|GROUP", "|LINK", "|SIF POSITION (start-end)", "|TYPE"); printf("------------------------------------------------------------------------------\n"); - listforall(&info->deschead, sif_printrow); + listforall(&info->deschead, sif_printrow, NULL); } int -sif_printdesc(void *elem) +sif_printdesc(void *elem, void *data) { - Sifcommon *cm = (Sifcommon *)elem; - Sifpartition *p = (Sifpartition *)elem; - Sifsignature *s = (Sifsignature *)elem; + Sifdescriptor *desc = elem; - printf("desc type: %s\n", sif_datastr(cm->datatype)); - printf("desc id: %d\n", cm->id); - if(cm->groupid == SIF_UNUSED_GROUP) + printf("desc type: %s\n", sif_datastr(desc->cm.datatype)); + printf("desc id: %d\n", desc->cm.id); + if(desc->cm.groupid == SIF_UNUSED_GROUP) printf("group id: NONE\n"); else - printf("group id: %d\n", cm->groupid & ~SIF_GROUP_MASK); - if(cm->link == SIF_UNUSED_LINK) + printf("group id: %d\n", desc->cm.groupid & ~SIF_GROUP_MASK); + if(desc->cm.link == SIF_UNUSED_LINK) printf("link: NONE\n"); else - printf("link: %d\n", cm->link); - printf("fileoff: %ld\n", cm->fileoff); - printf("filelen: %ld\n", cm->filelen); + printf("link: %d\n", desc->cm.link); + printf("fileoff: %ld\n", desc->cm.fileoff); + printf("filelen: %ld\n", desc->cm.filelen); - switch(cm->datatype){ + switch(desc->cm.datatype){ case DATA_PARTITION: - printf("fstype: %s\n", sif_fsstr(p->fstype)); - printf("parttype: %s\n", sif_partstr(p->parttype)); - printf("content: %s\n", p->content); + printf("fstype: %s\n", sif_fsstr(desc->part.fstype)); + printf("parttype: %s\n", sif_partstr(desc->part.parttype)); + printf("content: %s\n", desc->part.content); break; case DATA_SIGNATURE: - printf("hashtype: %s\n", sif_hashstr(s->hashtype)); - printf("entity: %s\n", s->entity); + printf("hashtype: %s\n", sif_hashstr(desc->sig.hashtype)); + printf("entity: %s\n", desc->sig.entity); break; default: break; @@ -200,12 +227,10 @@ sif_printheader(Sifinfo *info) printf("number of descriptors: %d\n", info->header.ndesc); printf("start of descriptors in file: %ld\n", info->header.descoff); - printf("length of descriptors in file: %ld\n", info->header.desclen); + printf("length of descriptors in file: %s\n", sif_hreadable(info->header.desclen)); printf("start of data in file: %ld\n", info->header.dataoff); - printf("length of data in file: %ld\n", info->header.datalen); + printf("length of data in file: %s\n", sif_hreadable(info->header.datalen)); printf("============================================\n"); - - listforall(&info->deschead, sif_printdesc); } /* Get the SIF header structure */ @@ -218,21 +243,21 @@ sif_getheader(Sifinfo *info) static int sameid(void *cur, void *elem) { - Sifcommon *c = (Sifcommon *)cur; - Sifcommon *e = (Sifcommon *)elem; + Sifdescriptor *c = cur; + Sifdescriptor *e = elem; - if(c->id == e->id) + if(c->cm.id == e->cm.id) return 1; return 0; } -Sifcommon * +Sifdescriptor * sif_getdescid(Sifinfo *info, int id) { - Sifcommon lookfor; + Sifdescriptor lookfor; Node *n; - lookfor.id = id; + lookfor.cm.id = id; n = listfind(&info->deschead, &lookfor, sameid); if(n == NULL){ @@ -246,7 +271,7 @@ static int isdeffile(void *cur, void *elem) { Sifdeffile *c = (Sifdeffile *)cur; - Sifdeffile *e = (Sifdeffile *)elem; + Sifdeffile *e = (Sifdeffile *)elem; if(c->cm.datatype == DATA_DEFFILE && c->cm.groupid == e->cm.groupid) return 1; @@ -275,7 +300,7 @@ static int islabels(void *cur, void *elem) { Siflabels *c = (Siflabels *)cur; - Siflabels *e = (Siflabels *)elem; + Siflabels *e = (Siflabels *)elem; if(c->cm.datatype == DATA_LABELS && c->cm.groupid == e->cm.groupid) return 1; @@ -303,7 +328,7 @@ static int isenvvar(void *cur, void *elem) { Sifenvvar *c = (Sifenvvar *)cur; - Sifenvvar *e = (Sifenvvar *)elem; + Sifenvvar *e = (Sifenvvar *)elem; if(c->cm.datatype == DATA_ENVVAR && c->cm.groupid == e->cm.groupid) return 1; @@ -331,7 +356,7 @@ static int ispartition(void *cur, void *elem) { Sifpartition *c = (Sifpartition *)cur; - Sifpartition *e = (Sifpartition *)elem; + Sifpartition *e = (Sifpartition *)elem; if(c->cm.datatype == DATA_PARTITION && c->cm.groupid == e->cm.groupid) return 1; @@ -359,7 +384,7 @@ static int issignature(void *cur, void *elem) { Sifsignature *c = (Sifsignature *)cur; - Sifsignature *e = (Sifsignature *)elem; + Sifsignature *e = (Sifsignature *)elem; if(c->cm.datatype == DATA_SIGNATURE && c->cm.groupid == e->cm.groupid) return 1; @@ -386,21 +411,21 @@ sif_getsignature(Sifinfo *info, int groupid) static int linkmatches(void *cur, void *elem) { - Sifcommon *c = (Sifcommon *)cur; - Sifcommon *e = (Sifcommon *)elem; + Sifdescriptor *c = cur; + Sifdescriptor *e = elem; - if(c->link == e->id) + if(c->cm.link == e->cm.id) return 1; return 0; } -Sifcommon * +Sifdescriptor * sif_getlinkeddesc(Sifinfo *info, int id) { - Sifcommon lookfor; + Sifdescriptor lookfor; Node *n; - lookfor.id = id; + lookfor.cm.id = id; n = listfind(&info->deschead, &lookfor, linkmatches); if(n == NULL){ diff --git a/src/lib/sif/sifaccess.h b/src/lib/sif/sifaccess.h index 6f01e1eb0d..08ff39c6b3 100644 --- a/src/lib/sif/sifaccess.h +++ b/src/lib/sif/sifaccess.h @@ -21,13 +21,13 @@ char *sif_partstr(Sifparttype ptype); char *sif_datastr(Sifdatatype dtype); char *sif_fsstr(Siffstype ftype); -int sif_printrow(void *elem); -int sif_printdesc(void *elem); +int sif_printrow(void *elem, void *data); +int sif_printdesc(void *elem, void *data); void sif_printlist(Sifinfo *info); void sif_printheader(Sifinfo *info); -Sifcommon *sif_getdescid(Sifinfo *info, int id); -Sifcommon *sif_getlinkeddesc(Sifinfo *info, int id); +Sifdescriptor *sif_getdescid(Sifinfo *info, int id); +Sifdescriptor *sif_getlinkeddesc(Sifinfo *info, int id); Sifheader *sif_getheader(Sifinfo *info); Sifdeffile *sif_getdeffile(Sifinfo *info, int groupid); diff --git a/src/lib/signing/Makefile.am b/src/lib/signing/Makefile.am index 0b1310989c..d8a58b2fbe 100644 --- a/src/lib/signing/Makefile.am +++ b/src/lib/signing/Makefile.am @@ -1,9 +1,9 @@ MAINTAINERCLEANFILES = Makefile.in config.h config.h.in DISTCLEANFILES = Makefile -CLEANFILES = core.* *~ *.la +CLEANFILES = core.* *~ *.la *.o *.lo AM_CFLAGS = -Wall -fpie -fPIC AM_LDFLAGS = -pie -AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) distlibdir = $(libdir)/singularity distincludedir = $(includedir)/singularity @@ -21,10 +21,12 @@ libsingularity_signing_la_CFLAGS = $(AM_CFLAGS) # These are kludges so they don't remove the $(DEPDIR) in ../../util/ otherwise # the clean will fail when other Makefile tries to remove those directories -distclean: distclean-recursive - -rm ./$(DEPDIR) - -rm -f Makefile +distclean: + -rm -rf ./$(DEPDIR) + -rm -rf ./.libs + -rm -f $(DISTCLEANFILES) $(CLEANFILES) -maintainer-clean: maintainer-clean-recursive - -rm ./$(DEPDIR) - -rm -f Makefile +maintainer-clean: + -rm -rf ./$(DEPDIR) + -rm -rf ./.libs + -rm -f $(MAINTAINERCLEANFILES) $(DISTCLEANFILES) $(CLEANFILES) diff --git a/src/mount.c b/src/mount.c index 07bdac1c34..0c417f20fb 100644 --- a/src/mount.c +++ b/src/mount.c @@ -48,14 +48,16 @@ int main(int argc, char **argv) { struct image_object image; - singularity_config_init(joinpath(SYSCONFDIR, "/singularity/singularity.conf")); + singularity_config_init(); + singularity_suid_init(); singularity_priv_init(); - singularity_suid_init(argv); singularity_registry_init(); singularity_priv_drop(); + singularity_runtime_autofs(); + if ( singularity_registry_get("WRITABLE") != NULL ) { singularity_message(VERBOSE3, "Instantiating writable container image object\n"); image = singularity_image_init(singularity_registry_get("IMAGE"), O_RDWR); diff --git a/src/sif.c b/src/sif.c index 44ab4e52c4..382caf55a9 100644 --- a/src/sif.c +++ b/src/sif.c @@ -45,9 +45,11 @@ usage() fprintf(stderr, "usage: %s COMMAND OPTION FILE\n", progname); fprintf(stderr, "\n\n"); fprintf(stderr, "create -- Create a new sif file with input data objects\n"); + fprintf(stderr, "del id Delete a specified set of descriptor+object\n"); fprintf(stderr, "dump id Display data object content\n"); - fprintf(stderr, "list -- List SIF data descriptors from an input SIF file\n"); + fprintf(stderr, "header -- Display SIF header\n"); fprintf(stderr, "info id Print data object descriptor info\n"); + fprintf(stderr, "list -- List SIF data descriptors from an input SIF file\n"); fprintf(stderr, "sign id Cryptographically sign a data object from an input SIF file\n"); fprintf(stderr, "verify id Verify the signature of a data object from an input SIF file\n"); fprintf(stderr, "\n\n"); @@ -65,32 +67,32 @@ usage() Node * ddescadd(Node *head, char *fname) { - Ddesc *e; + Eleminfo *e; Node *n; struct stat st; - e = malloc(sizeof(Ddesc)); + e = malloc(sizeof(Eleminfo)); if(e == NULL){ - fprintf(stderr, "Error allocating memory for Ddesc\n"); + fprintf(stderr, "Error allocating memory for Eleminfo\n"); return NULL; } - e->datatype = DATA_DEFFILE; - e->groupid = SIF_DEFAULT_GROUP; - e->link = SIF_UNUSED_LINK; - e->fname = strdup(fname); - if(e->fname == NULL){ - fprintf(stderr, "Error allocating memory for e->fname\n"); + e->cm.datatype = DATA_DEFFILE; + e->cm.groupid = SIF_DEFAULT_GROUP; + e->cm.link = SIF_UNUSED_LINK; + e->defdesc.fname = strdup(fname); + if(e->defdesc.fname == NULL){ + fprintf(stderr, "Error allocating memory for e->defdesc.fname\n"); return NULL; } - if(stat(e->fname, &st) < 0){ + if(stat(e->defdesc.fname, &st) < 0){ perror("Error calling stat"); free(e); return NULL; } - e->len = st.st_size; + e->cm.len = st.st_size; n = listcreate(e); if(n == NULL){ - fprintf(stderr, "Error allocating Ddesc node\n"); + fprintf(stderr, "Error allocating Eleminfo node\n"); free(e); return NULL; } @@ -103,23 +105,23 @@ static char testenvs[] = "VAR0=VALUE0\nVAR1=VALUE1\nVAR2=VALUE2"; Node * edescadd(Node *head) { - Edesc *e; + Eleminfo *e; Node *n; - e = malloc(sizeof(Edesc)); + e = malloc(sizeof(Eleminfo)); if(e == NULL){ - fprintf(stderr, "Error allocating memory for Edesc\n"); + fprintf(stderr, "Error allocating memory for Eleminfo\n"); return NULL; } - e->datatype = DATA_ENVVAR; - e->groupid = SIF_DEFAULT_GROUP; - e->link = SIF_UNUSED_LINK; - e->vars = testenvs; - e->len = sizeof(testenvs); + e->cm.datatype = DATA_ENVVAR; + e->cm.groupid = SIF_DEFAULT_GROUP; + e->cm.link = SIF_UNUSED_LINK; + e->cm.len = sizeof(testenvs); + e->envdesc.vars = testenvs; n = listcreate(e); if(n == NULL){ - fprintf(stderr, "Error allocating Edesc node\n"); + fprintf(stderr, "Error allocating Eleminfo node\n"); free(e); return NULL; } @@ -131,32 +133,32 @@ edescadd(Node *head) Node * ldescadd(Node *head, char *fname) { - Ldesc *e; + Eleminfo *e; Node *n; struct stat st; - e = malloc(sizeof(Ldesc)); + e = malloc(sizeof(Eleminfo)); if(e == NULL){ - fprintf(stderr, "Error allocating memory for Ldesc\n"); + fprintf(stderr, "Error allocating memory for Eleminfo\n"); return NULL; } - e->datatype = DATA_LABELS; - e->groupid = SIF_DEFAULT_GROUP; - e->link = SIF_UNUSED_LINK; - e->fname = strdup(fname); - if(e->fname == NULL){ - fprintf(stderr, "Error allocating memory for e->fname\n"); + e->cm.datatype = DATA_LABELS; + e->cm.groupid = SIF_DEFAULT_GROUP; + e->cm.link = SIF_UNUSED_LINK; + e->labeldesc.fname = strdup(fname); + if(e->labeldesc.fname == NULL){ + fprintf(stderr, "Error allocating memory for e->labeldesc.fname\n"); return NULL; } - if(stat(e->fname, &st) < 0){ + if(stat(e->labeldesc.fname, &st) < 0){ perror("Error calling stat"); free(e); return NULL; } - e->len = st.st_size; + e->cm.len = st.st_size; n = listcreate(e); if(n == NULL){ - fprintf(stderr, "Error allocating Ldesc node\n"); + fprintf(stderr, "Error allocating Eleminfo node\n"); free(e); return NULL; } @@ -168,26 +170,26 @@ ldescadd(Node *head, char *fname) Node * sdescadd(Node *head, char *signedhash, Sifhashtype hashtype) { - Sdesc *e; + Eleminfo *e; Node *n; char entity[SIF_ENTITY_LEN] = { }; - e = malloc(sizeof(Sdesc)); + e = malloc(sizeof(Eleminfo)); if(e == NULL){ - fprintf(stderr, "Error allocating memory for Sdesc\n"); + fprintf(stderr, "Error allocating memory for Eleminfo\n"); return NULL; } - e->datatype = DATA_SIGNATURE; - e->groupid = SIF_DEFAULT_GROUP; - e->link = SIF_UNUSED_LINK; - e->signature = strdup(signedhash); - e->len = strlen(signedhash)+1; - e->hashtype = hashtype; - strcpy(e->entity, entity); /* Flawfinder: ignore */ + e->cm.datatype = DATA_SIGNATURE; + e->cm.groupid = SIF_DEFAULT_GROUP; + e->cm.link = SIF_UNUSED_LINK; + e->cm.len = strlen(signedhash)+1; + e->sigdesc.signature = strdup(signedhash); + e->sigdesc.hashtype = hashtype; + strcpy(e->sigdesc.entity, entity); /* Flawfinder: ignore */ n = listcreate(e); if(n == NULL){ - fprintf(stderr, "Error allocating Sdesc node\n"); + fprintf(stderr, "Error allocating Eleminfo node\n"); free(e); return NULL; } @@ -199,7 +201,7 @@ sdescadd(Node *head, char *signedhash, Sifhashtype hashtype) Node * pdescadd(Node *head, char *fname, int argc, char *argv[]) { - Pdesc *e; + Eleminfo *e; Node *n; int opt; char content[SIF_CONTENT_LEN] = { }; @@ -256,32 +258,32 @@ pdescadd(Node *head, char *fname, int argc, char *argv[]) return NULL; } - e = malloc(sizeof(Pdesc)); + e = malloc(sizeof(Eleminfo)); if(e == NULL){ - fprintf(stderr, "Error allocating memory for Pdesc\n"); + fprintf(stderr, "Error allocating memory for Eleminfo\n"); return NULL; } - e->datatype = DATA_PARTITION; - e->groupid = SIF_DEFAULT_GROUP; - e->link = SIF_UNUSED_LINK; - e->fname = strdup(fname); - if(e->fname == NULL){ - fprintf(stderr, "Error allocating memory for e->fname\n"); + e->cm.datatype = DATA_PARTITION; + e->cm.groupid = SIF_DEFAULT_GROUP; + e->cm.link = SIF_UNUSED_LINK; + e->partdesc.fname = strdup(fname); + if(e->partdesc.fname == NULL){ + fprintf(stderr, "Error allocating memory for e->partdesc.fname\n"); return NULL; } - if(stat(e->fname, &st) < 0){ + if(stat(e->partdesc.fname, &st) < 0){ perror("Error calling stat"); free(e); return NULL; } - e->len = st.st_size; - e->fstype = fstype; - e->parttype = parttype; - strcpy(e->content, content); /* Flawfinder: ignore */ + e->cm.len = st.st_size; + e->partdesc.fstype = fstype; + e->partdesc.parttype = parttype; + strcpy(e->partdesc.content, content); /* Flawfinder: ignore */ n = listcreate(e); if(n == NULL){ - fprintf(stderr, "Error allocating Pdesc node\n"); + fprintf(stderr, "Error allocating Eleminfo node\n"); free(e); return NULL; } @@ -419,8 +421,8 @@ int cmd_sign(int argc, char *argv[]) { Sifinfo sif; - Sifcommon *cm; - Sdesc s; + Eleminfo e; + Sifdescriptor *desc; int id; static char signedhash[SGN_MAXLEN]; static char hash[SGN_HASHLEN]; @@ -439,15 +441,15 @@ cmd_sign(int argc, char *argv[]) return(-1); } - cm = sif_getdescid(&sif, id); - if(cm == NULL){ + desc = sif_getdescid(&sif, id); + if(desc == NULL){ fprintf(stderr, "Cannot find descriptor %d from SIF file: %s\n", id, sif_strerror(siferrno)); sif_unload(&sif); return -1; } - if(sgn_hashbuffer(sif.mapstart+cm->fileoff, cm->filelen, hash) == NULL){ + if(sgn_hashbuffer(sif.mapstart+desc->cm.fileoff, desc->cm.filelen, hash) == NULL){ fprintf(stderr, "Error with computing hash: %s\n", sgn_strerror(sgnerrno)); sif_unload(&sif); @@ -463,30 +465,28 @@ cmd_sign(int argc, char *argv[]) return -1; }; - s.datatype = DATA_SIGNATURE; - s.groupid = SIF_UNUSED_GROUP; - s.link = id; - s.signature = strdup(signedhash); - s.len = strlen(signedhash)+1; - s.hashtype = SNG_DEFAULT_HASH; + e.cm.datatype = DATA_SIGNATURE; + e.cm.groupid = SIF_UNUSED_GROUP; + e.cm.link = id; + e.cm.len = strlen(signedhash)+1; + e.sigdesc.signature = strdup(signedhash); + e.sigdesc.hashtype = SNG_DEFAULT_HASH; - if(sif_putdataobj(&sif, (Sifdatatype *)&s) < 0){ + if(sif_putdataobj(&e, &sif) < 0){ fprintf(stderr, "Error adding new data object: %s\n", sif_strerror(siferrno)); - free(s.signature); + free(e.sigdesc.signature); sif_unload(&sif); return -1; } + free(e.sigdesc.signature); if(sif_unload(&sif) < 0){ fprintf(stderr, "Error releasing SIF file gracefully: %s\n", sif_strerror(siferrno)); - free(s.signature); - sif_unload(&sif); return -1; } - sif_unload(&sif); return 0; } @@ -494,8 +494,8 @@ int cmd_verify(int argc, char *argv[]) { Sifinfo sif; - Sifcommon *cm; - Sifcommon *link; + Sifdescriptor *desc; + Sifdescriptor *link; int id; static char hash[SGN_HASHLEN]; static char hashstr[SGN_HASHLEN*2+1]; @@ -513,34 +513,34 @@ cmd_verify(int argc, char *argv[]) return(-1); } - cm = sif_getdescid(&sif, id); - if(cm == NULL){ + desc = sif_getdescid(&sif, id); + if(desc == NULL){ fprintf(stderr, "Cannot find descriptor %d from SIF file: %s\n", id, sif_strerror(siferrno)); sif_unload(&sif); return -1; } - link = sif_getlinkeddesc(&sif, cm->id); + link = sif_getlinkeddesc(&sif, desc->cm.id); if(link == NULL){ - fprintf(stderr, "Cannot find signature for id %d: %s\n", cm->id, - sif_strerror(siferrno)); + fprintf(stderr, "Cannot find signature for id %d: %s\n", + desc->cm.id, sif_strerror(siferrno)); sif_unload(&sif); return -1; } - if(sgn_verifyhash(sif.mapstart+link->fileoff) < 0){ + if(sgn_verifyhash(sif.mapstart+link->cm.fileoff) < 0){ fprintf(stderr, "Signature verification failed: %s\n", sgn_strerror(sgnerrno)); sif_unload(&sif); return -1; } - if(sgn_getsignedhash(sif.mapstart+link->fileoff, hashstr) < 0){ + if(sgn_getsignedhash(sif.mapstart+link->cm.fileoff, hashstr) < 0){ fprintf(stderr, "Could not find SIFHASH inside signature: %s\n", sgn_strerror(sgnerrno)); sif_unload(&sif); return -1; } - if(sgn_hashbuffer(sif.mapstart+cm->fileoff, cm->filelen, hash) == NULL){ + if(sgn_hashbuffer(sif.mapstart+desc->cm.fileoff, desc->cm.filelen, hash) == NULL){ fprintf(stderr, "Error with computing hash: %s\n", sgn_strerror(sgnerrno)); sif_unload(&sif); @@ -563,7 +563,7 @@ cmd_info(int argc, char *argv[]) { int id; Sifinfo sif; - Sifcommon *cm; + Sifdescriptor *desc; if(argc < 4){ usage(); @@ -577,8 +577,8 @@ cmd_info(int argc, char *argv[]) return(-1); } - cm = sif_getdescid(&sif, id); - if(cm == NULL){ + desc = sif_getdescid(&sif, id); + if(desc == NULL){ fprintf(stderr, "Cannot find descriptor %d from SIF file: %s\n", id, sif_strerror(siferrno)); sif_unload(&sif); @@ -587,7 +587,7 @@ cmd_info(int argc, char *argv[]) printf("Descriptor info:\n"); printf("---------------------------\n"); - sif_printdesc(cm); + sif_printdesc(desc, NULL); sif_unload(&sif); @@ -599,7 +599,7 @@ cmd_dump(int argc, char *argv[]) { int id; Sifinfo sif; - Sifcommon *cm; + Sifdescriptor *desc; char *c; if(argc < 4){ @@ -614,15 +614,17 @@ cmd_dump(int argc, char *argv[]) return(-1); } - cm = sif_getdescid(&sif, id); - if(cm == NULL){ + desc = sif_getdescid(&sif, id); + if(desc == NULL){ fprintf(stderr, "Cannot find descriptor %d from SIF file: %s\n", id, sif_strerror(siferrno)); sif_unload(&sif); return -1; } - for(c = sif.mapstart+cm->fileoff; c < sif.mapstart+cm->fileoff+cm->filelen; c++) + for(c = sif.mapstart+desc->cm.fileoff; + c < sif.mapstart+desc->cm.fileoff+desc->cm.filelen; + c++) printf("%c", *c); sif_unload(&sif); @@ -630,6 +632,60 @@ cmd_dump(int argc, char *argv[]) return 0; } +int +cmd_del(int argc, char *argv[]) +{ + int ret; + int id; + Sifinfo sif; + + if(argc < 4){ + usage(); + return -1; + } + + id = atoi(argv[2]); + + if(sif_load(argv[3], &sif, 0) < 0){ + fprintf(stderr, "Cannot load SIF image: %s\n", sif_strerror(siferrno)); + return(-1); + } + + ret = sif_deldataobj(&sif, id, DEL_ZERO); + if(ret < 0){ + fprintf(stderr, "Cannot delete object with id %d from SIF file: %s\n", id, + sif_strerror(siferrno)); + sif_unload(&sif); + return -1; + } + + sif_unload(&sif); + + return 0; +} + +int +cmd_header(int argc, char *argv[]) +{ + Sifinfo sif; + + if(argc < 3) { + usage(); + return -1; + } + + if(sif_load(argv[2], &sif, 1) < 0) { + fprintf(stderr, "Cannot load SIF image: %s\n", sif_strerror(siferrno)); + return -1; + } + + sif_printheader(&sif); + + sif_unload(&sif); + + return 0; +} + int main(int argc, char *argv[]) { @@ -651,6 +707,10 @@ main(int argc, char *argv[]) return cmd_info(argc, argv); if(strncmp(argv[1], "dump", 4) == 0) return cmd_dump(argc, argv); + if(strncmp(argv[1], "del", 3) == 0) + return cmd_del(argc, argv); + if(strncmp(argv[1], "header", 6) == 0) + return cmd_header(argc, argv); usage(); return -1; diff --git a/src/slurm/Makefile.am b/src/slurm/Makefile.am index 17c90805d5..885f010253 100644 --- a/src/slurm/Makefile.am +++ b/src/slurm/Makefile.am @@ -1,4 +1,4 @@ -AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) plugindir = $(libdir)/slurm diff --git a/src/slurm/singularity.c b/src/slurm/singularity.c index a99e8180ce..3ffbae85ee 100644 --- a/src/slurm/singularity.c +++ b/src/slurm/singularity.c @@ -178,7 +178,7 @@ static int setup_container(spank_t spank) singularity_message(VERBOSE, "Running SLURM/Singularity integration " "plugin\n"); - if ((rc = singularity_config_init(joinpath(SYSCONFDIR, "/singularity/singularity.conf"))) != 0) { + if ((rc = singularity_config_init()) != 0) { return rc; } diff --git a/src/start.c b/src/start.c index 61fa12db0c..5493bcfd6d 100644 --- a/src/start.c +++ b/src/start.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -50,25 +51,30 @@ int started = 0; int main(int argc, char **argv) { - int i, daemon_fd, cleanupd_fd; + int i, cleanupd_fd; struct tempfile *stdout_log, *stderr_log, *singularity_debug; struct image_object image; pid_t child; siginfo_t siginfo; struct stat filestat; - singularity_config_init(joinpath(SYSCONFDIR, "/singularity/singularity.conf")); + singularity_config_init(); + singularity_suid_init(); singularity_priv_init(); - singularity_suid_init(argv); singularity_registry_init(); - singularity_priv_userns(); singularity_priv_drop(); + singularity_runtime_autofs(); + singularity_registry_set("UNSHARE_PID", "1"); singularity_registry_set("UNSHARE_IPC", "1"); + if ( singularity_registry_get("INSTANCE_BOOT") != NULL ) { + singularity_registry_set("CONTAIN", "1"); + } + singularity_cleanupd(); if ( singularity_registry_get("WRITABLE") != NULL ) { @@ -96,8 +102,8 @@ int main(int argc, char **argv) { singularity_registry_set("ROOTFS", CONTAINER_FINALDIR); singularity_daemon_init(); - singularity_message(DEBUG, "We are ready to recieve jobs, sending signal_go_ahead to parent\n"); - + singularity_message(DEBUG, "Entering chroot environment\n"); + singularity_runtime_enter(); singularity_priv_drop_perm(); @@ -106,15 +112,65 @@ int main(int argc, char **argv) { ABORT(255); } - singularity_install_signal_handler(); + if ( chdir("/") < 0 ) { + singularity_message(ERROR, "Can't change directory to /\n"); + } + setsid(); + umask(0); - daemon_fd = atoi(singularity_registry_get("DAEMON_FD")); cleanupd_fd = atoi(singularity_registry_get("CLEANUPD_FD")); - + + if ( singularity_registry_get("INSTANCE_BOOT") != NULL ) { + int pipes[2]; + + if ( pipe2(pipes, O_CLOEXEC) < 0 ) { + singularity_signal_go_ahead(255); + return(0); + } + + if ( fork() == 0 ) { + /* wait a broken pipe which mean exec success */ + struct pollfd pfd; + pfd.fd = pipes[0]; + pfd.events = POLLRDHUP; + + close(pipes[1]); + while( poll(&pfd, 1, 1000) >= 0 ) { + if ( pfd.revents == POLLHUP ) break; + } + singularity_signal_go_ahead(0); + + /* wait /sbin/init install signal handler */ + usleep(20000); + return(0); + } else { + close(pipes[0]); + if ( is_exec("/sbin/init") == 0 ) { + argv[1] = NULL; + if ( execv("/sbin/init", argv) < 0 ) { // Flawfinder: ignore + singularity_message(ERROR, "Exec of /sbin/init failed\n"); + } + } else { + singularity_message(ERROR, "/sbin/init not present in container\n"); + } + /* send exit status and implicitly kill polling child */ + singularity_signal_go_ahead(255); + return(0); + } + } + + /* set program name */ + if ( prctl(PR_SET_NAME, "sinit", 0, 0, 0) < 0 ) { + singularity_message(ERROR, "Failed to set program name\n"); + ABORT(255); + } + + singularity_install_signal_handler(); + /* Close all open fd's that may be present besides daemon info file fd */ singularity_message(DEBUG, "Closing open fd's\n"); for( i = sysconf(_SC_OPEN_MAX); i > 2; i-- ) { - if ( i != daemon_fd && i != cleanupd_fd ) { + if ( i != cleanupd_fd ) { if ( fstat(i, &filestat) == 0 ) { if ( S_ISFIFO(filestat.st_mode) != 0 ) { continue; diff --git a/src/util/Makefile.am b/src/util/Makefile.am index 502210596c..fe60f83dc9 100644 --- a/src/util/Makefile.am +++ b/src/util/Makefile.am @@ -3,10 +3,12 @@ DISTCLEANFILES = Makefile CLEANFILES = core.* *~ *.la *.o AM_CFLAGS = -Wall -fpie -fPIC AM_LDFLAGS = -pie -AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) $(NO_SETNS) +AM_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DLIBEXECDIR=\"$(libexecdir)\" $(SINGULARITY_DEFINES) -EXTRA_DIST = cleanupd.c \ +EXTRA_DIST = capability.c \ + capability.h \ + cleanupd.c \ cleanupd.h \ config_parser.c \ config_parser.h \ @@ -24,6 +26,7 @@ EXTRA_DIST = cleanupd.c \ privilege.h \ registry.c \ registry.h \ + securebits.h \ sessiondir.c \ sessiondir.h \ suid.c \ diff --git a/src/util/capability.c b/src/util/capability.c new file mode 100644 index 0000000000..895afd6ee5 --- /dev/null +++ b/src/util/capability.c @@ -0,0 +1,565 @@ +/* + * Copyright (c) 2017, SingularityWare, LLC. All rights reserved. + * + * This software is licensed under a 3-clause BSD license. Please + * consult LICENSE file distributed with the sources of this project regarding + * your rights to use or distribute this software. + * + */ + + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef SINGULARITY_SECUREBITS +# include +#else +# include "util/securebits.h" +#endif /* SINGULARITY_SECUREBITS */ + +#include "config.h" + +#include "util/file.h" +#include "util/util.h" +#include "util/registry.h" +#include "util/privilege.h" +#include "util/message.h" +#include "util/config_parser.h" + +#ifndef SYSCONFDIR +#error SYSCONFDIR not defined +#endif + +#define NO_CAP 100 +#define CAPSET_MAX 40 + +/* Support only 64 bits sets, since kernel 2.6.25 */ +#ifdef _LINUX_CAPABILITY_VERSION_3 +# define LINUX_CAPABILITY_VERSION _LINUX_CAPABILITY_VERSION_3 +#elif defined(_LINUX_CAPABILITY_VERSION_2) +# define LINUX_CAPABILITY_VERSION _LINUX_CAPABILITY_VERSION_2 +#else +# error Linux 64 bits capability set not supported +#endif /* _LINUX_CAPABILITY_VERSION_3 */ + +/* + if uid != 0 -> no capabilities + if uid = 0 -> root default capabilities + if uid = 0 and keep_privs -> all capabilities + if uid = 0 and no_privs -> no capabilities + if uid = 0 and build stage 2 -> minimal capabilities +*/ + +static __u32 default_capabilities[] = { + CAP_SETUID, + CAP_SETGID, + CAP_SETPCAP, + CAP_SETFCAP, + CAP_SYS_ADMIN, + CAP_NET_ADMIN, + CAP_MKNOD, + CAP_CHOWN, + CAP_FOWNER, + CAP_SYS_CHROOT, + CAP_SYS_PTRACE, + CAP_DAC_READ_SEARCH, + CAP_DAC_OVERRIDE, + CAP_AUDIT_WRITE, + NO_CAP +}; + +static __u32 minimal_capabilities[] = { + CAP_SETUID, + CAP_SETGID, + CAP_SETFCAP, + CAP_CHOWN, + CAP_FOWNER, + CAP_SYS_CHROOT, + CAP_DAC_READ_SEARCH, + CAP_DAC_OVERRIDE, + CAP_AUDIT_WRITE, + NO_CAP +}; + +enum { + ROOT_DEFCAPS_FULL, + ROOT_DEFCAPS_FILE, + ROOT_DEFCAPS_DEFAULT, + ROOT_DEFCAPS_NO, + ROOT_DEFCAPS_ERROR +}; + +static int get_root_default_capabilities(void) { + char *value = strdup(singularity_config_get_value(ROOT_DEFAULT_CAPABILITIES)); + + if ( value == NULL ) { + return(ROOT_DEFCAPS_ERROR); + } + + chomp(value); + + if ( strcmp(value, "full") == 0 ) { + return(ROOT_DEFCAPS_FULL); + } else if ( strcmp(value, "file") == 0 ) { + return(ROOT_DEFCAPS_FILE); + } else if ( strcmp(value, "default") == 0 ) { + return(ROOT_DEFCAPS_DEFAULT); + } else if ( strcmp(value, "no") == 0 ) { + return(ROOT_DEFCAPS_NO); + } + + return(ROOT_DEFCAPS_ERROR); +} + +static unsigned long long get_current_capabilities(void) { + int i; + unsigned long long caps = 0; + + for ( i = CAPSET_MAX - 1; i >= 0; i-- ) { + if ( prctl(PR_CAPBSET_READ, i) > 0 ) { + caps |= (1ULL << i); + } + } + + return(caps); +} + +static char *cap2str(unsigned long long cap) { + char *str = (char *)calloc(24, sizeof(char *)); + + if ( str == NULL ) { + singularity_message(ERROR, "Failed to allocate 24 memory bytes\n"); + ABORT(255); + } + + snprintf(str, 23, "%llu", cap); + + return(str); +} + +static unsigned long long str2cap(char *value) { + unsigned long long caps = 0; + + if ( value == NULL ) return(caps); + + errno = 0; + caps = strtoull(value, NULL, 10); + if ( errno != 0 ) { + singularity_message(WARNING, "Can't convert string %s to unsigned long long\n", value); + caps = 0; + } + + return(caps); +} + +static unsigned long long array2cap(__u32 *capabilities) { + unsigned long long caps = 0; + __u32 i; + + for ( i = 0; capabilities[i] != NO_CAP; i++ ) { + caps |= (1ULL << capabilities[i]); + } + + return(caps); +} + +static int capget(cap_user_header_t hdrp, cap_user_data_t datap) { + return syscall(__NR_capget, hdrp, datap); +} + +static int capset(cap_user_header_t hdrp, const cap_user_data_t datap) { + return syscall(__NR_capset, hdrp, datap); +} + +static int singularity_capability_keep_privs(void) { + if ( singularity_priv_getuid() == 0 && singularity_registry_get("KEEP_PRIVS") != NULL ) { + return(1); + } + return(0); +} + +static int singularity_capability_no_privs(void) { + if ( singularity_priv_getuid() == 0 && singularity_registry_get("NO_PRIVS") != NULL ) { + return(1); + } + return(0); +} + +static void singularity_capability_set_securebits(int bits) { + int current_bits = prctl(PR_GET_SECUREBITS); + + if ( current_bits < 0 ) { + singularity_message(ERROR, "Failed to read securebits\n"); + ABORT(255); + } + + if ( ! (current_bits & SECBIT_NO_SETUID_FIXUP_LOCKED) ) { + if ( singularity_capability_keep_privs() == 1 ) { + return; + } + + if ( singularity_priv_getuid() == 0 ) { + bits &= ~(SECBIT_NOROOT|SECBIT_NOROOT_LOCKED); + } + + if ( prctl(PR_SET_SECUREBITS, bits) < 0 ) { + singularity_message(ERROR, "Failed to set securebits\n"); + ABORT(255); + } + } +} + +void singularity_capability_keep(void) { + singularity_capability_set_securebits(SECBIT_NO_SETUID_FIXUP); +} + +void singularity_capability_set_effective(void) { + struct __user_cap_header_struct header; + struct __user_cap_data_struct data[2]; + + singularity_message(DEBUG, "Set effective/permitted capabilities for current processus\n"); + + header.version = LINUX_CAPABILITY_VERSION; + header.pid = getpid(); + + if ( capget(&header, data) < 0 ) { + singularity_message(ERROR, "Failed to get processus capabilities\n"); + ABORT(255); + } + +#ifdef USER_CAPABILITIES + data[1].permitted = data[1].inheritable; + data[0].permitted = data[0].inheritable; + + data[1].effective = 0; + data[0].effective = 0; +#else + data[1].permitted = data[1].effective = data[1].inheritable; + data[0].permitted = data[0].effective = data[0].inheritable; +#endif + + if ( capset(&header, data) < 0 ) { + singularity_message(ERROR, "Failed to set processus capabilities\n"); + ABORT(255); + } +} + +static void singularity_capability_set(unsigned long long capabilities) { + __u32 caps_index; + __u32 last_cap; + struct __user_cap_header_struct header; + struct __user_cap_data_struct data[2]; + + singularity_message(DEBUG, "Entering in a restricted capability set\n"); + + header.version = LINUX_CAPABILITY_VERSION; + header.pid = getpid(); + + if ( capget(&header, data) < 0 ) { + singularity_message(ERROR, "Failed to get processus capabilities\n"); + ABORT(255); + } + + // We can't rely on CAP_LAST_CAP if singularity is compiled in a container by + // example, host is ubuntu with a recent kernel and container is a centos 6 + // container, so CAP_LAST_CAP could be less than CAP_LAST_CAP host and we could + // forget to drop some capabilities + + singularity_message(DEBUG, "Determining highest capability of the running process\n"); + + /* read bounding set */ + for ( last_cap = CAPSET_MAX; ; last_cap-- ) { + if ( prctl(PR_CAPBSET_READ, last_cap) > 0 || last_cap == 0 ) { + break; + } + } + + singularity_message(DEBUG, "Dropping capabilities in bounding set\n"); + for ( caps_index = 0; caps_index <= last_cap; caps_index++ ) { + if ( !(capabilities & (1ULL << caps_index)) ) { + if ( prctl(PR_CAPBSET_DROP, caps_index) < 0 ) { + singularity_message(ERROR, "Failed to drop bounding capabilities set\n"); + ABORT(255); + } + } + } + + data[1].inheritable = (__u32)(capabilities >> 32); + data[0].inheritable = (__u32)(capabilities & 0xFFFFFFFF); + + if ( capset(&header, data) < 0 ) { + singularity_message(ERROR, "Failed to set processus capabilities\n"); + ABORT(255); + } + +#ifdef USER_CAPABILITIES + // set ambient capabilities if supported + if ( singularity_config_get_bool(ALLOW_USER_CAPABILITIES) ) { + int i; + for (i = 0; i <= CAPSET_MAX; i++ ) { + if ( (capabilities & (1ULL << i)) ) { + singularity_message(DEBUG, "Set ambient cap %d\n", i); + if ( prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, i, 0, 0) < 0 ) { + singularity_message(ERROR, "Failed to set ambient capability\n"); + ABORT(255); + } + } + } + } +#endif /* USER_CAPABILITIES */ +} + +static unsigned long long get_capabilities_from_file(char *ftype, char *id) { + unsigned long long caps = 0; + FILE *file = NULL; + char strcap[24]; + char path[PATH_MAX]; + + singularity_message(DEBUG, "Get capabilities from file for %s %s\n", ftype, id); + + memset(strcap, 0, 24); + memset(path, 0, PATH_MAX); + + snprintf(path, PATH_MAX-1, SYSCONFDIR "/singularity/capabilities/%s.%s", ftype, id); // Flawfinder: ignore + + file = fopen(path, "r"); + if ( file == NULL ) { + singularity_message(DEBUG, "Fail to open %s: %s\n", path, strerror(errno)); + return(caps); + } + + if ( fgets(strcap, 23, file) == NULL ) { + singularity_message(DEBUG, "Fail to read %s content: %s\n", path, strerror(errno)); + return(caps); + } + + caps = str2cap(strcap); + return(caps); +} + +static unsigned long long get_user_file_capabilities(void) { + unsigned long long caps = 0; + uid_t uid = singularity_priv_getuid(); + struct passwd *pw; + + pw = getpwuid(uid); + if ( pw == NULL ) { + singularity_message(ERROR, "Failed to retrieve password file entry for uid %d\n", uid); + ABORT(255); + } + + caps = get_capabilities_from_file("user", pw->pw_name); + return(caps); +} + +static unsigned long long get_group_file_capabilities(void) { + unsigned long long caps = 0; + gid_t *gids = NULL; + int i, count = getgroups(0, NULL); + + if ( count > NGROUPS_MAX ) { + singularity_message(ERROR, "Number of user group\n"); + ABORT(255); + } + + gids = (gid_t *)calloc(count, sizeof(gid_t)); + if ( gids == NULL ) { + singularity_message(ERROR, "Failed to allocate memory for user groups\n"); + ABORT(255); + } + + if ( getgroups(count, gids) < 0 ) { + singularity_message(ERROR, "Failed to retrieve user group\n"); + ABORT(255); + } + + for ( i = count - 1; i >= 0; i-- ) { + struct group *gr = getgrgid(gids[i]); + + if ( gr == NULL ) { + singularity_message(ERROR, "Failed to retrieve group file entry for gid %d\n", gids[i]); + ABORT(255); + } + caps |= get_capabilities_from_file("group", gr->gr_name); + } + + free(gids); + + return(caps); +} + +static unsigned long long setup_user_capabilities(void) { + unsigned long long caps = 0; + +#ifdef USER_CAPABILITIES + if ( singularity_config_get_bool(ALLOW_USER_CAPABILITIES) ) { + unsigned long long tcaps = str2cap(singularity_registry_get("ADD_CAPS")); + caps |= get_user_file_capabilities(); + caps |= get_group_file_capabilities(); + caps = tcaps & caps; + singularity_registry_set("ADD_CAPS", cap2str(caps)); + envar_set("SINGULARITY_ADD_CAPS", singularity_registry_get("ADD_CAPS"), 1); + } else { + envar_set("SINGULARITY_ADD_CAPS", "0", 1); + } +#else + if ( singularity_config_get_bool(ALLOW_USER_CAPABILITIES) ) { + singularity_message(WARNING, "User capabilities are not supported by your kernel\n"); + envar_set("SINGULARITY_ADD_CAPS", "0", 1); + } +#endif + + return(caps); +} + +static int setup_capabilities(void) { + int root_default_caps = get_root_default_capabilities(); + + if ( singularity_priv_getuid() == 0 ) { + if ( singularity_config_get_bool(ALLOW_ROOT_CAPABILITIES) <= 0 ) { + singularity_registry_set("ADD_CAPS", NULL); + unsetenv("SINGULARITY_ADD_CAPS"); + unsetenv("SINGULARITY_DROP_CAPS"); + singularity_registry_set("DROP_CAPS", NULL); + unsetenv("SINGULARITY_NO_PRIVS"); + singularity_registry_set("NO_PRIVS", NULL); + unsetenv("SINGULARITY_KEEP_PRIVS"); + singularity_registry_set("KEEP_PRIVS", NULL); + } + + if ( root_default_caps == ROOT_DEFCAPS_ERROR ) { + singularity_message(WARNING, "root default capabilities value in configuration is unknown, set to no\n"); + singularity_registry_set("NO_PRIVS", "1"); + singularity_registry_set("KEEP_PRIVS", NULL); + + unsetenv("SINGULARITY_KEEP_PRIVS"); + envar_set("SINGULARITY_NO_PRIVS", "1", 1); + } else if ( root_default_caps == ROOT_DEFCAPS_FULL || singularity_capability_keep_privs() ) { + unsigned long long caps = get_current_capabilities(); + if ( singularity_registry_get("NO_PRIVS") == NULL ) { + singularity_registry_set("KEEP_PRIVS", "1"); + envar_set("SINGULARITY_KEEP_PRIVS", "1", 1); + singularity_registry_set("ADD_CAPS", cap2str(caps)); + envar_set("SINGULARITY_ADD_CAPS", singularity_registry_get("ADD_CAPS"), 1); + } else { + envar_set("SINGULARITY_NO_PRIVS", "1", 1); + unsetenv("SINGULARITY_KEEP_PRIVS"); + envar_set("SINGULARITY_ADD_CAPS", singularity_registry_get("ADD_CAPS"), 1); + } + } else if ( root_default_caps == ROOT_DEFCAPS_FILE ) { + unsigned long long filecap = get_user_file_capabilities(); + + if ( singularity_registry_get("ADD_CAPS") == NULL ) { + if ( ! singularity_capability_no_privs() ) { + singularity_registry_set("ADD_CAPS", cap2str(filecap)); + } + } else { + unsigned long long envcap = str2cap(singularity_registry_get("ADD_CAPS")); + if ( ! singularity_capability_no_privs() ) { + singularity_registry_set("ADD_CAPS", cap2str(envcap | filecap)); + } + } + envar_set("SINGULARITY_ADD_CAPS", singularity_registry_get("ADD_CAPS"), 1); + + if ( ! singularity_capability_keep_privs() ) { + singularity_registry_set("NO_PRIVS", "1"); + envar_set("SINGULARITY_NO_PRIVS", "1", 1); + } + } else if ( root_default_caps == ROOT_DEFCAPS_NO ) { + if ( ! singularity_capability_keep_privs() ) { + singularity_registry_set("NO_PRIVS", "1"); + envar_set("SINGULARITY_NO_PRIVS", "1", 1); + } + } + } else { + setup_user_capabilities(); + } + + envar_set("SINGULARITY_ROOT_DEFAULT_CAPS", int2str(root_default_caps), 1); + return(root_default_caps); +} + +void singularity_capability_init(void) { + setup_capabilities(); + + if ( ! singularity_capability_keep_privs() ) { + if ( singularity_registry_get("ADD_CAPS") ) { + unsigned long long capabilities = str2cap(singularity_registry_get("ADD_CAPS")); + unsigned long long final = array2cap(default_capabilities) | capabilities; + + singularity_capability_set(final); + } else { + singularity_capability_set(array2cap(default_capabilities)); + } + } +} + +/* for mount command */ +void singularity_capability_init_default(void) { + singularity_capability_set(array2cap(default_capabilities)); + envar_set("SINGULARITY_ROOT_DEFAULT_CAPS", int2str(ROOT_DEFCAPS_DEFAULT), 1); + unsetenv("SINGULARITY_ADD_CAPS"); + unsetenv("SINGULARITY_DROP_CAPS"); + unsetenv("SINGULARITY_NO_PRIVS"); + unsetenv("SINGULARITY_KEEP_PRIVS"); +} + +/* for build stage 2 */ +void singularity_capability_init_minimal(void) { + singularity_capability_set(array2cap(minimal_capabilities)); + unsetenv("SINGULARITY_ADD_CAPS"); + unsetenv("SINGULARITY_DROP_CAPS"); + unsetenv("SINGULARITY_NO_PRIVS"); + unsetenv("SINGULARITY_KEEP_PRIVS"); +} + +void singularity_capability_drop(void) { + long int root_default_caps; + int root_user = (singularity_priv_getuid() == 0) ? 1 : 0; + + if ( singularity_registry_get("ROOT_DEFAULT_CAPS") == NULL ) { + root_default_caps = setup_capabilities(); + } else { + if ( str2int(singularity_registry_get("ROOT_DEFAULT_CAPS"), &root_default_caps) == -1 ) { + singularity_message(ERROR, "Failed to get root default capabilities via environment variable\n"); + ABORT(255); + } + } + + if ( singularity_capability_keep_privs() ) { + unsigned long long capabilities = str2cap(singularity_registry_get("ADD_CAPS")); + singularity_capability_set(capabilities); + } + + if ( singularity_capability_no_privs() || ( ! singularity_capability_keep_privs() && ! root_user ) ) { + singularity_message(DEBUG, "Set capabilities\n"); + unsigned long long capabilities = str2cap(singularity_registry_get("ADD_CAPS")); + singularity_capability_set(capabilities); + } + if ( singularity_registry_get("DROP_CAPS") ) { + singularity_message(DEBUG, "Drop capabilities requested by user\n"); + unsigned long long capabilities = str2cap(singularity_registry_get("DROP_CAPS")); + unsigned long long current = get_current_capabilities(); + + current &= ~capabilities; + + singularity_capability_set(current); + } + singularity_capability_set_securebits(SECURE_ALL_BITS|SECURE_ALL_LOCKS); + singularity_capability_set_effective(); +} diff --git a/src/util/capability.h b/src/util/capability.h new file mode 100644 index 0000000000..66886ee42f --- /dev/null +++ b/src/util/capability.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2017, SingularityWare, LLC. All rights reserved. + * + * This software is licensed under a 3-clause BSD license. Please + * consult LICENSE file distributed with the sources of this project regarding + * your rights to use or distribute this software. + * + */ + + +#ifndef __CAPABILITY_H_ +#define __CAPABILITY_H_ + +void singularity_capability_init(void); +void singularity_capability_init_minimal(void); +void singularity_capability_init_default(void); + +// Drop all capabilities +void singularity_capability_drop(void); +void singularity_capability_keep(void); + +#endif /* __CAPABILITY_H_ */ diff --git a/src/util/config_defaults.h.in b/src/util/config_defaults.h.in index 2213adc24a..d4009b40d5 100644 --- a/src/util/config_defaults.h.in +++ b/src/util/config_defaults.h.in @@ -85,6 +85,9 @@ #define LIMIT_CONTAINER_OWNERS "limit container owners" #define LIMIT_CONTAINER_OWNERS_DEFAULT "NULL" +#define LIMIT_CONTAINER_GROUPS "limit container groups" +#define LIMIT_CONTAINER_GROUPS_DEFAULT "NULL" + #define LIMIT_CONTAINER_PATHS "limit container paths" #define LIMIT_CONTAINER_PATHS_DEFAULT "NULL" @@ -106,4 +109,16 @@ #define ALLOW_CONTAINER_SQUASHFS "allow container squashfs" #define ALLOW_CONTAINER_SQUASHFS_DEFAULT 1 +#define ALWAYS_USE_NV "always use nv" +#define ALWAYS_USE_NV_DEFAULT "no" + +#define ROOT_DEFAULT_CAPABILITIES "root default capabilities" +#define ROOT_DEFAULT_CAPABILITIES_DEFAULT "no" + +#define ALLOW_ROOT_CAPABILITIES "allow root capabilities" +#define ALLOW_ROOT_CAPABILITIES_DEFAULT 1 + +#define ALLOW_USER_CAPABILITIES "allow user capabilities" +#define ALLOW_USER_CAPABILITIES_DEFAULT 0 + #endif // __SINGULARITY_CONFIG_DEFAULTS_H_ diff --git a/src/util/config_parser.c b/src/util/config_parser.c index 655498eccb..02eb808844 100644 --- a/src/util/config_parser.c +++ b/src/util/config_parser.c @@ -38,6 +38,7 @@ #include "util/util.h" #include "util/message.h" #include "util/file.h" +#include "util/registry.h" #include "config_parser.h" #define MAX_LINE_LEN (PATH_MAX + 128) @@ -119,6 +120,11 @@ int singularity_config_parse(char *config_path) { singularity_message(VERBOSE, "Initialize configuration file: %s\n", config_path); if ( is_file(config_path) != 0 ) { singularity_message(ERROR, "Specified configuration file %s does not appear to be a normal file.\n", config_path); + return -1; + } + if ( is_owner(config_path, 0) != 0 ) { + singularity_message(ERROR, "Specified configuration file %s is not owned by root\n", config_path); + return -1; } FILE *config_fp = fopen(config_path, "r"); if ( config_fp == NULL ) { // Flawfinder: ignore (we have to open the file...) @@ -201,12 +207,20 @@ int singularity_config_parse(char *config_path) { * * Returns non-zero on error. */ -int singularity_config_init(char *config_path) { +int singularity_config_init(void) { + char *config_path; + if (config_initialized) { return 0; } config_initialized = 1; + if ( getuid() == 0 && singularity_registry_get("CONFIG_FILE") != NULL ) { + config_path = singularity_registry_get("CONFIG_FILE"); + } else { + config_path = joinpath(SYSCONFDIR, "/singularity/singularity.conf"); + } + hcreate_r(60, &config_table); int retval = singularity_config_parse(config_path); if (retval) { // Error case. diff --git a/src/util/config_parser.h b/src/util/config_parser.h index b2eebde4b3..4941f5d25c 100644 --- a/src/util/config_parser.h +++ b/src/util/config_parser.h @@ -59,6 +59,6 @@ int _singularity_config_get_bool_char_impl(const char *key, const char *value); // Initialize the configuration table // -int singularity_config_init(char *config_path); +int singularity_config_init(void); #endif /* __SINGULARITY_CONFIG_H_ */ diff --git a/src/util/daemon.c b/src/util/daemon.c index 2df1d57fae..909e10fe9f 100644 --- a/src/util/daemon.c +++ b/src/util/daemon.c @@ -11,26 +11,60 @@ #define _GNU_SOURCE #include #include +#include #include #include #include #include +#include +#include #include "config.h" #include "util/file.h" #include "util/util.h" #include "util/daemon.h" #include "util/registry.h" -#include "util/message.c" +#include "util/message.h" #include "lib/image/image.h" #include "lib/runtime/runtime.h" #include "util/privilege.h" +void *xmalloc(size_t size) { + void *mem = malloc(size); + if ( mem == NULL ) { + singularity_message(ERROR, "Failed to allocate %lu memory bytes\n", size); + ABORT(255); + } + memset(mem, 0, size); + return (void *)mem; +} + +int xsnprintf(char **buf, size_t size, char *fmt, ...) { + int ret; + va_list ap; + if ( *buf == NULL ) { + *buf = (char *)xmalloc(size); + } + + va_start(ap, fmt); + ret = vsnprintf(*buf, size - 1, fmt, ap); // Flawfinder: ignore + va_end(ap); + + return ret; +} + void daemon_file_parse(void) { - singularity_message(DEBUG, "reached file parse\n"); char *key, *val; - char *line = (char *)malloc(2048 * sizeof(char *)); + char *line = (char *)xmalloc(2048); FILE *file = fopen(singularity_registry_get("DAEMON_FILE"), "r"); + char *daemon_name = singularity_registry_get("DAEMON_NAME"); + + singularity_message(DEBUG, "reached file parse for daemon %s\n", daemon_name); + + if ( file == NULL ) { + singularity_message(ERROR, "%s daemon does not exist\n", daemon_name); + ABORT(255); + } while( fgets(line, 2048, file) ) { key = strtok(line, "=\n"); @@ -38,6 +72,7 @@ void daemon_file_parse(void) { singularity_message(DEBUG, "Read key-val pair %s=%s\n", key, val); singularity_registry_set(key, val); } + free(line); } void daemon_file_write(int fd, char *key, char *val) { @@ -56,11 +91,43 @@ void daemon_file_write(int fd, char *key, char *val) { } } +int daemon_is_running(char *pid_path) { + int retval = 0; + char *daemon_name = singularity_registry_get("DAEMON_NAME"); + char *daemon_cmdline = NULL; + char *daemon_procname = NULL; + FILE *file_cmdline; + + xsnprintf(&daemon_procname, 2048, "singularity-instance: %s [%s]", singularity_priv_getuser(), daemon_name); + + file_cmdline = fopen(joinpath(pid_path, "/cmdline"), "r"); + if ( file_cmdline == NULL ) { + singularity_message(ERROR, "Can't open process command line, is instance %s running ?\n", daemon_name); + ABORT(255); + } + + daemon_cmdline = (char *)xmalloc(2048); + if ( fgets(daemon_cmdline, 2048, file_cmdline) == NULL ) { + singularity_message(ERROR, "Can't read command line, is instance %s running ?\n", daemon_name); + ABORT(255); + } + + if ( strcmp(daemon_procname, daemon_cmdline) == 0 ) { + retval = 1; + } + + free(daemon_cmdline); + free(daemon_procname); + fclose(file_cmdline); + + return(retval); +} + int daemon_is_owner(char *pid_path) { int retval = 0; char *proc_status = joinpath(pid_path, "/status"); - char *uid_check = (char *)malloc(2048); - char *line = (char *)malloc(2048); + char *uid_check = (char *)xmalloc(2048); + char *line = (char *)xmalloc(2048); FILE *status = fopen(proc_status, "r"); pid_t uid = singularity_priv_getuid(); @@ -88,53 +155,61 @@ int daemon_is_owner(char *pid_path) { void daemon_init_join(void) { char *ns_path, *ns_fd_str; - char *pid_path; - int lock_result, ns_fd; - int *lock_fd = malloc(sizeof(int)); + int ns_fd; + char *pid_path = NULL; char *daemon_file = singularity_registry_get("DAEMON_FILE"); char *daemon_name = singularity_registry_get("DAEMON_NAME"); - - /* Check if there is a lock on daemon file */ - singularity_message(DEBUG, "Checking for lock on %s\n", daemon_file); - lock_result = filelock(daemon_file, lock_fd); - if ( lock_result == 0 ) { - /* Successfully obtained lock, no daemon controls this file. */ - singularity_message(ERROR, "Unable to join daemon: %s daemon does not exist\n", daemon_name); - unlink(daemon_file); - close(*lock_fd); + if ( daemon_name == NULL ) { + singularity_message(ERROR, "No instance name specified\n"); ABORT(255); - return; - } else if ( lock_result == EALREADY ) { - /* EALREADY is set when another process has a lock on the file. */ - singularity_message(DEBUG, "Another process has lock on daemon file\n"); + } - daemon_file_parse(); - - pid_path = (char *)malloc(2048 * sizeof(char *)); - sprintf(pid_path, "/proc/%s", singularity_registry_get("DAEMON_PID")); //Flawfinder: ignore + if ( daemon_file == NULL ) { + singularity_message(ERROR, "No instance file found for instance %s\n", daemon_name); + ABORT(255); + } - if ( daemon_is_owner(pid_path) == 0 ) { - singularity_message(ERROR, "Unable to join instance: you are not the owner\n"); - ABORT(255); - } + singularity_message(DEBUG, "Check if instance %s is running\n", daemon_name); + + daemon_file_parse(); + if ( singularity_registry_get("DAEMON_PID") == NULL ) { + singularity_message(ERROR, "%s seems corrupted or bad formatted", daemon_file); + ABORT(255); + } + + xsnprintf(&pid_path, 2048, "/proc/%s", singularity_registry_get("DAEMON_PID")); + + if ( daemon_is_owner(pid_path) == 0 ) { + singularity_message(ERROR, "Unable to join instance: you are not the owner\n"); + ABORT(255); + } + + if ( daemon_is_running(pid_path) ) { ns_path = joinpath(pid_path, "/ns"); /* Open FD to /proc/[PID]/ns directory to call openat() for ns files */ singularity_priv_escalate(); - if ( ( ns_fd = open(ns_path, O_RDONLY | O_CLOEXEC) ) == -1 ) { + if ( ( ns_fd = open(ns_path, O_RDONLY) ) == -1 ) { singularity_message(ERROR, "Unable to open ns directory of PID in daemon file: %s\n", strerror(errno)); ABORT(255); } singularity_priv_drop(); + if ( fcntl(ns_fd, F_SETFD, FD_CLOEXEC) != 0 ) { + singularity_message(ERROR, "Unable to set CLOEXEC on file descriptor\n"); + ABORT(255); + } + ns_fd_str = int2str(ns_fd); /* Set DAEMON_NS_FD to /proc/[PID]/ns FD in registry */ singularity_registry_set("DAEMON_NS_FD", ns_fd_str); + + free(pid_path); } else { - singularity_message(ERROR, "Unable to join daemon: %s daemon does not exist\n", daemon_name); + singularity_message(ERROR, "No instance named %s found\n", daemon_name); ABORT(255); } } @@ -143,70 +218,155 @@ void daemon_init_start(void) { char *daemon_file = singularity_registry_get("DAEMON_FILE"); char *daemon_name = singularity_registry_get("DAEMON_NAME"); char *daemon_file_dir = strdup(daemon_file); - char *daemon_pid = (char *)malloc(256 * sizeof(char)); + char *daemon_pid = (char *)xmalloc(256); char *daemon_image; int daemon_fd; - int lock; /* Check if /var/tmp/.singularity-daemon-[UID]/ directory exists, if not create it */ if ( is_dir(dirname(daemon_file_dir)) == -1 ) { s_mkpath(daemon_file_dir, 0755); } - - /* Attempt to open lock on daemon file */ - lock = filelock(daemon_file, &daemon_fd); - if( lock == 0 ) { - singularity_message(DEBUG, "Successfully obtained excluse lock on %s\n", daemon_file); + if ( access(daemon_file, F_OK) == 0 ) { // Flawfinder: ignore + char *pid_path = NULL; + + /* check if it's a singularity daemon */ + daemon_file_parse(); - /* Calling readlink on /proc/self returns the PID of the thread in the host PID NS */ - memset(daemon_pid, 0, 256); - if ( readlink("/proc/self", daemon_pid, 256) == -1 ) { //Flawfinder: ignore - singularity_message(ERROR, "Unable to open /proc/self: %s\n", strerror(errno)); + if ( singularity_registry_get("DAEMON_PID") == NULL ) { + singularity_message(ERROR, "%s seems corrupted or bad formatted", daemon_file); ABORT(255); - } else { - singularity_message(DEBUG, "PID in host namespace: %s\n", daemon_pid); } - if ( !(daemon_image = realpath(singularity_registry_get("IMAGE"), NULL)) ) { //Flawfinder: ignore - singularity_message(DEBUG, "ERROR: %s\n", strerror(errno)); - } - - /* Successfully obtained lock, write to daemon fd */ - lseek(daemon_fd, 0, SEEK_SET); - if ( ftruncate(daemon_fd, 0) == -1 ) { - singularity_message(ERROR, "Unable to truncate %d: %s\n", daemon_fd, strerror(errno)); + xsnprintf(&pid_path, 2048, "/proc/%s", singularity_registry_get("DAEMON_PID")); + + if ( daemon_is_running(pid_path) ) { + singularity_message(ERROR, "An instance named %s is already running\n", daemon_name); + ABORT(255); } + unlink(daemon_file); + free(pid_path); + } - daemon_file_write(daemon_fd, "DAEMON_PID", daemon_pid); - daemon_file_write(daemon_fd, "DAEMON_IMAGE", daemon_image); - daemon_file_write(daemon_fd, "DAEMON_ROOTFS", singularity_registry_get("ROOTFS")); + /* file don't exists assume no daemon is running */ + if ( ( daemon_fd = open(daemon_file, O_RDWR | O_CREAT | O_SYNC, 0644) ) < 0 ) { + singularity_message(ERROR, "Unable to create daemon file %s\n", daemon_file); + ABORT(255); + } - singularity_registry_set("DAEMON_FD", int2str(daemon_fd)); - } else if( lock == EALREADY ) { - /* Another daemon controls this file already */ - singularity_message(ERROR, "Daemon %s already exists: %s\n", daemon_name, strerror(errno)); + /* Calling readlink on /proc/self returns the PID of the thread in the host PID NS */ + if ( readlink("/proc/self", daemon_pid, 255) == -1 ) { //Flawfinder: ignore + singularity_message(ERROR, "Unable to open /proc/self: %s\n", strerror(errno)); ABORT(255); } else { - singularity_message(ERROR, "Cannot lock %s: %s\n", daemon_file, strerror(errno)); + singularity_message(DEBUG, "PID in host namespace: %s\n", daemon_pid); + } + + if ( !(daemon_image = realpath(singularity_registry_get("IMAGE"), NULL)) ) { //Flawfinder: ignore + singularity_message(DEBUG, "ERROR: %s\n", strerror(errno)); + } + + /* Successfully obtained lock, write to daemon fd */ + lseek(daemon_fd, 0, SEEK_SET); + if ( ftruncate(daemon_fd, 0) == -1 ) { + singularity_message(ERROR, "Unable to truncate %d: %s\n", daemon_fd, strerror(errno)); + } + + daemon_file_write(daemon_fd, "DAEMON_PID", daemon_pid); + daemon_file_write(daemon_fd, "DAEMON_IMAGE", daemon_image); + daemon_file_write(daemon_fd, "DAEMON_ROOTFS", singularity_registry_get("ROOTFS")); + + if ( singularity_registry_get("ADD_CAPS") ) { + daemon_file_write(daemon_fd, "ADD_CAPS", singularity_registry_get("ADD_CAPS")); + } + if ( singularity_registry_get("DROP_CAPS") ) { + daemon_file_write(daemon_fd, "DROP_CAPS", singularity_registry_get("DROP_CAPS")); + } + if ( singularity_registry_get("NO_PRIVS") ) { + daemon_file_write(daemon_fd, "NO_PRIVS", singularity_registry_get("NO_PRIVS")); + } + if ( singularity_registry_get("KEEP_PRIVS") ) { + daemon_file_write(daemon_fd, "KEEP_PRIVS", singularity_registry_get("KEEP_PRIVS")); + } + + close(daemon_fd); + + free(daemon_pid); + free(daemon_image); + free(daemon_file_dir); +} + +int singularity_daemon_own_namespace(char *namespace) { + int retval = 0; + char *self_ns_path = NULL; + char *target_ns_path = (char *)xmalloc(PATH_MAX); + struct stat self_ns; + struct stat target_ns; + char *target_pid = singularity_registry_get("DAEMON_PID"); + + if ( target_pid == NULL ) { + singularity_message(ERROR, "DAEMON_PID is not set\n"); + ABORT(255); + } + + if ( target_ns_path == NULL ) { + singularity_message(ERROR, "Can't allocate %d memory bytes buffer\n", PATH_MAX); ABORT(255); } + + if ( namespace == NULL ) { + singularity_message(ERROR, "No namespace specified\n"); + ABORT(255); + } + + if ( xsnprintf(&target_ns_path, PATH_MAX, "/proc/%s/ns/%s", target_pid, namespace) >= PATH_MAX ) { + singularity_message(ERROR, "Path too long\n"); + ABORT(255); + } + + self_ns_path = joinpath("/proc/self/ns/", namespace); + + if ( stat(self_ns_path, &self_ns) < 0 ) { + singularity_message(ERROR, "Stat failed on link %s\n", self_ns_path); + ABORT(255); + } + + if ( stat(target_ns_path, &target_ns) < 0 ) { + singularity_message(ERROR, "Stat failed on link %s\n", target_ns_path); + ABORT(255); + } + + if ( self_ns.st_ino != target_ns.st_ino ) { + retval = 1; + } + + free(target_ns_path); + free(self_ns_path); + + return(retval); } void singularity_daemon_init(void) { -#if defined (NO_SETNS) && !defined (SINGULARITY_SETNS_SYSCALL) - singularity_message(ERROR, "Instance feature is disabled, your kernel is too old\n"); - ABORT(255); -#else if ( singularity_registry_get("DAEMON_START") ) { + +#if defined (SINGULARITY_NO_SETNS) && !defined (SINGULARITY_SETNS_SYSCALL) + singularity_message(ERROR, "Instance feature is disabled, your kernel is too old\n"); + ABORT(255); +#endif + daemon_init_start(); return; } else if ( singularity_registry_get("DAEMON_JOIN") ) { + +#if defined (SINGULARITY_NO_SETNS) && !defined (SINGULARITY_SETNS_SYSCALL) + singularity_message(ERROR, "Instance feature is disabled, your kernel is too old\n"); + ABORT(255); +#endif + daemon_init_join(); return; } else { singularity_message(DEBUG, "Not joining a daemon, daemon join not set\n"); return; } -#endif } diff --git a/src/util/daemon.h b/src/util/daemon.h index 097cbcebfa..19be078eef 100644 --- a/src/util/daemon.h +++ b/src/util/daemon.h @@ -12,5 +12,6 @@ #define __SINGULARITY_DAEMON_H_ void singularity_daemon_init(void); - + int singularity_daemon_own_namespace(char *namespace); + #endif diff --git a/src/util/fork.c b/src/util/fork.c index e091b7b17c..8906264e9d 100644 --- a/src/util/fork.c +++ b/src/util/fork.c @@ -38,6 +38,7 @@ #include "util/privilege.h" #include "util/message.h" #include "util/util.h" +#include "util/suid.h" int generic_signal_rpipe = -1; int generic_signal_wpipe = -1; @@ -213,12 +214,14 @@ static int wait_child() { } } while( child_ok ); - /* Catch the exit status of the child process */ - retval = 0; + /* Catch the exit status or kill signal of the child process */ waitpid(child_pid, &tmpstatus, 0); - retval = WEXITSTATUS(tmpstatus); - - return(retval); + if (WIFEXITED(tmpstatus)) { + return(WEXITSTATUS(tmpstatus)); + } else if (WIFSIGNALED(tmpstatus)) { + kill(getpid(), WTERMSIG(tmpstatus)); + } + return(-1); } /* */ @@ -379,7 +382,7 @@ pid_t singularity_fork(unsigned int flags) { fds[1].revents = 0; /* Drop privs if we're SUID */ - if ( singularity_priv_is_suid() == 0 ) { + if ( singularity_suid_enabled() ) { singularity_message(DEBUG, "Dropping permissions\n"); singularity_priv_drop(); } diff --git a/src/util/privilege.c b/src/util/privilege.c index 163157925e..95c809cc7b 100644 --- a/src/util/privilege.c +++ b/src/util/privilege.c @@ -42,7 +42,9 @@ #include "util/util.h" #include "util/registry.h" #include "util/privilege.h" +#include "util/capability.h" #include "util/message.h" +#include "util/suid.h" #include "util/config_parser.h" @@ -52,7 +54,7 @@ static struct PRIV_INFO { gid_t gid; gid_t *gids; size_t gids_count; - int userns_ready; + int userns; uid_t orig_uid; uid_t orig_gid; pid_t orig_pid; @@ -189,94 +191,25 @@ void singularity_priv_init(void) { uinfo.home = strdup("/"); uinfo.homedir = uinfo.home; } - - return; -} - -void singularity_priv_userns(void) { - - singularity_message(VERBOSE, "Invoking the user namespace\n"); + uinfo.userns = 0; if ( singularity_config_get_bool(ALLOW_USER_NS) <= 0 ) { singularity_message(VERBOSE, "Not virtualizing USER namespace by configuration: 'allow user ns' = no\n"); } else if ( getuid() == 0 ) { singularity_message(VERBOSE, "Not virtualizing USER namespace: running as root\n"); - } else if ( singularity_priv_is_suid() == 0 ) { + } else if ( singularity_suid_enabled() ) { singularity_message(VERBOSE, "Not virtualizing USER namespace: running as SUID\n"); + } else if ( singularity_registry_get("NOSUID") == NULL ) { + singularity_message(VERBOSE, "Not virtualizing USER namespace: not requested by user\n"); } else { - uid_t uid = singularity_priv_getuid(); - gid_t gid = singularity_priv_getgid(); - - singularity_message(DEBUG, "Attempting to virtualize the USER namespace\n"); - if ( unshare(CLONE_NEWUSER) != 0 ) { - singularity_message(ERROR, "Failed invoking the NEWUSER namespace runtime: %s\n", strerror(errno)); - ABORT(255); // If we are configured to use CLONE_NEWUSER, we should abort if that fails - } - - singularity_message(DEBUG, "Enabled user namespaces\n"); - - { - singularity_message(DEBUG, "Setting setgroups to: 'deny'\n"); - char *map_file = (char *) malloc(PATH_MAX); - snprintf(map_file, PATH_MAX-1, "/proc/%d/setgroups", getpid()); // Flawfinder: ignore - FILE *map_fp = fopen(map_file, "w+"); // Flawfinder: ignore - if ( map_fp != NULL ) { - singularity_message(DEBUG, "Updating setgroups: %s\n", map_file); - fprintf(map_fp, "deny\n"); - if ( fclose(map_fp) < 0 ) { - singularity_message(ERROR, "Failed to write deny to setgroup file %s: %s\n", map_file, strerror(errno)); - ABORT(255); - } - } else { - singularity_message(ERROR, "Could not write info to setgroups: %s\n", strerror(errno)); - ABORT(255); - } - free(map_file); - } - { - singularity_message(DEBUG, "Setting GID map to: '%i %i 1'\n", gid, gid); - char *map_file = (char *) malloc(PATH_MAX); - snprintf(map_file, PATH_MAX-1, "/proc/%d/gid_map", getpid()); // Flawfinder: ignore - FILE *map_fp = fopen(map_file, "w+"); // Flawfinder: ignore - if ( map_fp != NULL ) { - singularity_message(DEBUG, "Updating the parent gid_map: %s\n", map_file); - fprintf(map_fp, "%i %i 1\n", gid, gid); - if ( fclose(map_fp) < 0 ) { - singularity_message(ERROR, "Failed to write to GID map %s: %s\n", map_file, strerror(errno)); - ABORT(255); - } - } else { - singularity_message(ERROR, "Could not write parent info to gid_map: %s\n", strerror(errno)); - ABORT(255); - } - free(map_file); - } - { - singularity_message(DEBUG, "Setting UID map to: '%i %i 1'\n", uid, uid); - char *map_file = (char *) malloc(PATH_MAX); - snprintf(map_file, PATH_MAX-1, "/proc/%d/uid_map", getpid()); // Flawfinder: ignore - FILE *map_fp = fopen(map_file, "w+"); // Flawfinder: ignore - if ( map_fp != NULL ) { - singularity_message(DEBUG, "Updating the parent uid_map: %s\n", map_file); - fprintf(map_fp, "%i %i 1\n", uid, uid); - if ( fclose(map_fp) < 0 ) { - singularity_message(ERROR, "Failed to write to UID map %s: %s\n", map_file, strerror(errno)); - ABORT(255); - } - } else { - singularity_message(ERROR, "Could not write parent info to uid_map: %s\n", strerror(errno)); - ABORT(255); - } - free(map_file); - } - - uinfo.userns_ready = 1; + uinfo.userns = 1; } - singularity_message(DEBUG, "Returning singularity_priv_init(void)\n"); + return; } + void singularity_priv_escalate(void) { if ( uinfo.ready != 1 ) { @@ -284,7 +217,7 @@ void singularity_priv_escalate(void) { ABORT(255); } - if ( uinfo.userns_ready == 1 ) { + if ( uinfo.userns == 1 ) { singularity_message(DEBUG, "Not escalating privileges, user namespace enabled\n"); return; } @@ -319,7 +252,7 @@ void singularity_priv_drop(void) { ABORT(255); } - if ( uinfo.userns_ready == 1 ) { + if ( uinfo.userns == 1 ) { singularity_message(DEBUG, "Not dropping privileges, user namespace enabled\n"); return; } @@ -385,12 +318,13 @@ void singularity_priv_drop_perm(void) { ABORT(255); } - if ( uinfo.userns_ready == 1 ) { + if ( uinfo.userns == 1 ) { singularity_message(VERBOSE2, "User namespace called, no privilges to drop\n"); return; } if ( uinfo.uid == 0 ) { + singularity_capability_drop(); singularity_message(VERBOSE2, "Calling user is root, no privileges to drop\n"); return; } @@ -398,6 +332,8 @@ void singularity_priv_drop_perm(void) { singularity_message(DEBUG, "Escalating permissison so we can properly drop permission\n"); singularity_priv_escalate(); + singularity_capability_keep(); + singularity_message(DEBUG, "Resetting supplementary groups\n"); if ( setgroups(uinfo.gids_count, uinfo.gids) < 0 ) { singularity_message(ERROR, "Could not reset supplementary group list (perm): %s\n", strerror(errno)); @@ -445,22 +381,14 @@ void singularity_priv_drop_perm(void) { singularity_message(VERBOSE2, "Not enabling NO_NEW_PRIVS flag due to lack of compile-time support.\n"); #endif + singularity_capability_drop(); singularity_message(DEBUG, "Finished dropping privileges\n"); } int singularity_priv_userns_enabled(void) { - return uinfo.userns_ready; -} - -/* Return 0 if program is SUID, -1 if not SUID */ -int singularity_priv_is_suid(void) { - if ( ( is_suid("/proc/self/exe") == 0 ) && ( is_owner("/proc/self/exe", 0) == 0) ) { - return(0); - } else { - return(-1); - } + return uinfo.userns; } char *singularity_priv_home(void) { @@ -492,7 +420,7 @@ uid_t singularity_priv_getuid(void) { singularity_message(ERROR, "Invoked before privilege info initialized!\n"); ABORT(255); } - return uinfo.uid; + return singularity_priv_userns_enabled() ? getuid() : uinfo.uid; } gid_t singularity_priv_getgid(void) { @@ -500,7 +428,7 @@ gid_t singularity_priv_getgid(void) { singularity_message(ERROR, "Invoked before privilege info initialized!\n"); ABORT(255); } - return uinfo.gid; + return singularity_priv_userns_enabled() ? getgid() : uinfo.gid; } const gid_t *singularity_priv_getgids(void) { diff --git a/src/util/privilege.h b/src/util/privilege.h index 22e5b6dbb5..0fcab0f2fc 100644 --- a/src/util/privilege.h +++ b/src/util/privilege.h @@ -25,7 +25,6 @@ #define __PRIVILEGE_H_ void singularity_priv_init(void); - void singularity_priv_userns(void); void singularity_priv_escalate(void); void singularity_priv_drop(void); void singularity_priv_drop_perm(void); @@ -34,7 +33,6 @@ const gid_t *singularity_priv_getgids(); int singularity_priv_getgidcount(void); int singularity_priv_userns_enabled(void); - int singularity_priv_is_suid(void); char *singularity_priv_home(void); char *singularity_priv_homedir(void); char *singularity_priv_getuser(void); diff --git a/src/util/securebits.h b/src/util/securebits.h new file mode 100644 index 0000000000..c3a43ad9d9 --- /dev/null +++ b/src/util/securebits.h @@ -0,0 +1,71 @@ +#ifndef _LINUX_SECUREBITS_H +#define _LINUX_SECUREBITS_H + +/* Each securesetting is implemented using two bits. One bit specifies + whether the setting is on or off. The other bit specify whether the + setting is locked or not. A setting which is locked cannot be + changed from user-level. */ +#define issecure_mask(X) (1 << (X)) + +#define SECUREBITS_DEFAULT 0x00000000 + +/* When set UID 0 has no special privileges. When unset, we support + inheritance of root-permissions and suid-root executable under + compatibility mode. We raise the effective and inheritable bitmasks + *of the executable file* if the effective uid of the new process is + 0. If the real uid is 0, we raise the effective (legacy) bit of the + executable file. */ +#define SECURE_NOROOT 0 +#define SECURE_NOROOT_LOCKED 1 /* make bit-0 immutable */ + +#define SECBIT_NOROOT (issecure_mask(SECURE_NOROOT)) +#define SECBIT_NOROOT_LOCKED (issecure_mask(SECURE_NOROOT_LOCKED)) + +/* When set, setuid to/from uid 0 does not trigger capability-"fixup". + When unset, to provide compatiblility with old programs relying on + set*uid to gain/lose privilege, transitions to/from uid 0 cause + capabilities to be gained/lost. */ +#define SECURE_NO_SETUID_FIXUP 2 +#define SECURE_NO_SETUID_FIXUP_LOCKED 3 /* make bit-2 immutable */ + +#define SECBIT_NO_SETUID_FIXUP (issecure_mask(SECURE_NO_SETUID_FIXUP)) +#define SECBIT_NO_SETUID_FIXUP_LOCKED \ + (issecure_mask(SECURE_NO_SETUID_FIXUP_LOCKED)) + +/* When set, a process can retain its capabilities even after + transitioning to a non-root user (the set-uid fixup suppressed by + bit 2). Bit-4 is cleared when a process calls exec(); setting both + bit 4 and 5 will create a barrier through exec that no exec()'d + child can use this feature again. */ +#define SECURE_KEEP_CAPS 4 +#define SECURE_KEEP_CAPS_LOCKED 5 /* make bit-4 immutable */ + +#define SECBIT_KEEP_CAPS (issecure_mask(SECURE_KEEP_CAPS)) +#define SECBIT_KEEP_CAPS_LOCKED (issecure_mask(SECURE_KEEP_CAPS_LOCKED)) + +/* When set, a process cannot add new capabilities to its ambient set. */ +#define SECURE_NO_CAP_AMBIENT_RAISE 6 +#define SECURE_NO_CAP_AMBIENT_RAISE_LOCKED 7 /* make bit-6 immutable */ + +#define SECBIT_NO_CAP_AMBIENT_RAISE (issecure_mask(SECURE_NO_CAP_AMBIENT_RAISE)) +#define SECBIT_NO_CAP_AMBIENT_RAISE_LOCKED \ + (issecure_mask(SECURE_NO_CAP_AMBIENT_RAISE_LOCKED)) + +#ifdef USER_CAPABILITIES + +# define SECURE_ALL_BITS (issecure_mask(SECURE_NOROOT) | \ + issecure_mask(SECURE_NO_SETUID_FIXUP) | \ + issecure_mask(SECURE_KEEP_CAPS) | \ + issecure_mask(SECURE_NO_CAP_AMBIENT_RAISE)) + +#else + +# define SECURE_ALL_BITS (issecure_mask(SECURE_NOROOT) | \ + issecure_mask(SECURE_NO_SETUID_FIXUP) | \ + issecure_mask(SECURE_KEEP_CAPS)) + +#endif /* USER_CAPABILITIES */ + +# define SECURE_ALL_LOCKS (SECURE_ALL_BITS << 1) + +#endif /* _LINUX_SECUREBITS_H */ diff --git a/src/util/setns.c b/src/util/setns.c index 1b50f72ca2..67f926ec72 100644 --- a/src/util/setns.c +++ b/src/util/setns.c @@ -19,19 +19,23 @@ #include #include -#if defined (NO_SETNS) && defined (SINGULARITY_SETNS_SYSCALL) +#include "util/message.h" + +#if defined (SINGULARITY_NO_SETNS) && defined (SINGULARITY_SETNS_SYSCALL) #include "util/setns.h" int setns(int fd, int nstype) { + singularity_message(DEBUG, "Using syscall() wrapped __NR_setns\n"); return syscall(__NR_setns, fd, nstype); } -#elif defined (NO_SETNS) && !defined (SINGULARITY_SETNS_SYSCALL) +#elif defined (SINGULARITY_NO_SETNS) && !defined (SINGULARITY_SETNS_SYSCALL) int setns(int fd, int nstype) { + singularity_message(VERBOSE, "setns() not supported at compile time by kernel at time of building\n"); errno = ENOSYS; return -1; } -#endif /* NO_SETNS && SINGULARITY_SETNS_SYCALL */ +#endif /* SINGULARITY_NO_SETNS && SINGULARITY_SETNS_SYSCALL */ diff --git a/src/util/suid.c b/src/util/suid.c index 9b6ba51509..d529fea1a3 100644 --- a/src/util/suid.c +++ b/src/util/suid.c @@ -25,13 +25,17 @@ #include #include #include +#include +#include +#include +#include #include "config.h" #include "util/util.h" -#include "util/message.h" #include "util/file.h" #include "util/registry.h" #include "util/config_parser.h" +#include "util/message.h" #include "util/privilege.h" #ifndef SYSCONFDIR @@ -43,80 +47,59 @@ #endif -int singularity_suid_init(char **argv) { - -#ifdef SINGULARITY_SUID - singularity_message(VERBOSE2, "Running SUID program workflow\n"); +int is_enabled = -1; - singularity_message(VERBOSE2, "Checking program has appropriate permissions\n"); - if ( ( is_owner("/proc/self/exe", 0) < 0 ) || ( is_suid("/proc/self/exe") < 0 ) ) { - char *path = (char*) malloc(PATH_MAX); - int len = readlink("/proc/self/exe", path, PATH_MAX - 1); // Flawfinder: ignore (TOCTOU not an issue here) - if ( len <= 0 ) { - singularity_abort(255, "Could not obtain link target of self\n"); - } - if ( len == PATH_MAX - 1 ) { - singularity_abort(255, "Link length error!\n"); - } - path[len] = '\0'; +int singularity_suid_init(void) { + ElfW(auxv_t) *auxv; + char *progname = NULL; + char *buffer = (char *)malloc(4096); + int proc_auxv = open("/proc/self/auxv", O_RDONLY); - singularity_message(ERROR, "Installation error, run the following commands as root to fix:\n"); - singularity_message(ERROR, " sudo chown root:root %s\n", path); - singularity_message(ERROR, " sudo chmod 4755 %s\n", path); - if ( getuid() == 0 ) { - singularity_message(INFO, "\n"); - } else { - ABORT(255); - } + if ( proc_auxv < 0 ) { + singularity_message(ERROR, "Can't open /proc/self/auxv: %s\n", strerror(errno)); + ABORT(255); } - singularity_message(VERBOSE2, "Checking configuration file is properly owned by root\n"); - if ( is_owner(joinpath(SYSCONFDIR, "/singularity/singularity.conf"), 0 ) < 0 ) { - singularity_abort(255, "Running in privileged mode, root must own the Singularity configuration file: %s\n", joinpath(SYSCONFDIR, "/singularity/singularity.conf")); + /* use auxiliary vectors to determine if running privileged */ + memset(buffer, 0, 4096); + if ( read(proc_auxv, buffer, 4092) < 0 ) { + singularity_message(ERROR, "Can't read auxiliary vectors: %s\n", strerror(errno)); + ABORT(255); } + auxv = (ElfW(auxv_t) *)buffer; - singularity_message(VERBOSE2, "Checking if singularity.conf allows us to run as suid\n"); - if ( ( singularity_config_get_bool(ALLOW_SETUID) <= 0 ) || ( singularity_registry_get("NOSUID") != NULL ) ) { - char *self; - char *self_tail; - - self = (char *) malloc(PATH_MAX); - - if ( readlink("/proc/self/exe", self, PATH_MAX) <= 0 ) { // Flawfinder: ignore (TOCTOU not an issue here) - singularity_message(ERROR, "Could not dereference our own program name\n"); - ABORT(255); + for (; auxv->a_type != AT_NULL; auxv++) { + if ( auxv->a_type == AT_SECURE ) { + is_enabled = (int)auxv->a_un.a_val; } - - if ( ( self_tail = strstr(self, "-suid") ) == NULL ) { - singularity_message(ERROR, "Could not identify non-SUID operation path: %s\n", self); - ABORT(255); + if ( auxv->a_type == AT_EXECFN ) { + progname = (char *)auxv->a_un.a_val; } + } - *self_tail = '\0'; - - if ( is_exec(self) == 0 ) { - singularity_message(VERBOSE, "Invoking non-SUID program flow: %s\n", self); - argv[0] = strdup(self); - - singularity_priv_drop_perm(); - - execv(argv[0], argv); // Flawfinder: ignore (all covered with sand) + free(buffer); + close(proc_auxv); - singularity_message(ERROR, "Failed exec'ing non-SUID program flow: %s\n", strerror(errno)); - ABORT(255); - } else { - singularity_message(ERROR, "Could not locate non-SUID program flow: %s\n", self); - ABORT(255); - } + if ( is_enabled < 0 ) { + singularity_message(ERROR, "Failed to determine if program run with SUID or not\n"); + ABORT(255); + } - singularity_message(ERROR, "We never should have gotten here...\n"); + if ( progname == NULL ) { + singularity_message(ERROR, "Failed to retrieve program name\n"); ABORT(255); } - if ( geteuid() != 0 ) { - singularity_message(ERROR, "Singularity is not running with appropriate privileges!\n"); - singularity_message(ERROR, "Check installation path is not mounted with 'nosuid', and/or consult manual.\n"); +#ifdef SINGULARITY_SUID + + singularity_message(VERBOSE2, "Running SUID program workflow\n"); + + singularity_message(VERBOSE2, "Checking program has appropriate permissions\n"); + if ( is_enabled == 0 && getuid() != 0 ) { + singularity_message(ERROR, "Installation error, run the following commands as root to fix:\n"); + singularity_message(ERROR, " sudo chown root:root %s\n", progname); + singularity_message(ERROR, " sudo chmod 4755 %s\n", progname); ABORT(255); } @@ -124,26 +107,35 @@ int singularity_suid_init(char **argv) { singularity_message(VERBOSE, "Running NON-SUID program workflow\n"); singularity_message(DEBUG, "Checking program has appropriate permissions\n"); - if ( is_suid("/proc/self/exe") >= 0 ) { + if ( is_suid(progname) == 0 ) { singularity_message(ERROR, "This program must **NOT** be SUID\n"); ABORT(255); } - #endif /* SINGULARITY_SUID */ return(0); } int singularity_suid_enabled(void) { - if ( is_owner("/proc/self/exe", 0) < 0 ) { - singularity_message(DEBUG, "Executable is not root owned\n"); - return(-1); - } + return(is_enabled); +} - if ( is_suid("/proc/self/exe") < 0 ) { - singularity_message(DEBUG, "Executable is not SUID\n"); - return(-1); +int singularity_suid_allowed(void) { + singularity_message(VERBOSE2, "Checking if singularity.conf allows us to run as suid\n"); + if ( ( singularity_config_get_bool(ALLOW_SETUID) <= 0 ) || ( singularity_registry_get("NOSUID") != NULL ) ) { + envar_set("SINGULARITY_NOSUID", "1", 1); + singularity_registry_set("NOSUID", "1"); + return(0); } - return(1); } + +int singularity_allow_container_setuid(void) { + int ret = 0; + if ( singularity_config_get_bool(ALLOW_ROOT_CAPABILITIES) ) { + if ( singularity_registry_get("ALLOW_SETUID") && singularity_priv_getuid() == 0 ) { + return(1); + } + } + return(ret); +} diff --git a/src/util/suid.h b/src/util/suid.h index 9e0b45725c..a6ca2d5491 100644 --- a/src/util/suid.h +++ b/src/util/suid.h @@ -24,7 +24,9 @@ #ifndef __SINGULARITY_SUID_H_ #define __SINGULARITY_SUID_H_ -extern int singularity_suid_init(char **argv); +extern int singularity_suid_init(void); extern int singularity_suid_enabled(void); +extern int singularity_allow_container_setuid(void); +extern int singularity_suid_allowed(void); #endif /* __SINGULARITY_SUID_H_ */ diff --git a/src/util/util.c b/src/util/util.c index 7096b3bd04..7b77e375ff 100644 --- a/src/util/util.c +++ b/src/util/util.c @@ -345,7 +345,11 @@ int envclean(void) { key = strtok_r(envclone[i], "=", &tok); - if ( strcmp(key, "http_proxy") == 0 ) { + if ( (strcasecmp(key, "http_proxy") == 0) || + (strcasecmp(key, "https_proxy") == 0) || + (strcasecmp(key, "no_proxy") == 0) || + (strcasecmp(key, "all_proxy") == 0) + ) { singularity_message(DEBUG, "Leaving environment variable set: %s\n", key); } else { singularity_message(DEBUG, "Unsetting environment variable: %s\n", key); diff --git a/src/wrapper.c b/src/wrapper.c new file mode 100644 index 0000000000..5a0075c200 --- /dev/null +++ b/src/wrapper.c @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2017, SingularityWare, LLC. All rights reserved. + * + * This software is licensed under a 3-clause BSD license. Please + * consult LICENSE file distributed with the sources of this project regarding + * your rights to use or distribute this software. + * + */ + + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "util/file.h" +#include "util/util.h" +#include "util/registry.h" +#include "util/config_parser.h" +#include "util/capability.h" +#include "util/privilege.h" +#include "util/suid.h" +#include "lib/image/image.h" +#include "lib/runtime/runtime.h" + +#ifndef SYSCONFDIR +#error SYSCONFDIR not defined +#endif + +#define MOUNT_BINARY "mount" +#define START_BINARY "start" +#define ACTION_BINARY "action" + +extern char **environ; + +struct cmd_wrapper { + char *command; + char *binary; + void (*capinit)(void); +}; + +struct cmd_wrapper cmd_wrapper[] = { + { .command = "shell", .binary = ACTION_BINARY, .capinit = singularity_capability_init }, + { .command = "exec", .binary = ACTION_BINARY, .capinit = singularity_capability_init }, + { .command = "run", .binary = ACTION_BINARY, .capinit = singularity_capability_init }, + { .command = "test", .binary = ACTION_BINARY, .capinit = singularity_capability_init }, + { .command = "mount", .binary = MOUNT_BINARY, .capinit = singularity_capability_init_default }, + { .command = "help", .binary = MOUNT_BINARY, .capinit = singularity_capability_init_default }, + { .command = "apps", .binary = MOUNT_BINARY, .capinit = singularity_capability_init_default }, + { .command = "inspect", .binary = MOUNT_BINARY, .capinit = singularity_capability_init_default }, + { .command = "check", .binary = MOUNT_BINARY, .capinit = singularity_capability_init_default }, + { .command = "image.import", .binary = MOUNT_BINARY, .capinit = singularity_capability_init_default }, + { .command = "image.export", .binary = MOUNT_BINARY, .capinit = singularity_capability_init_default }, + { .command = "instance.start", .binary = START_BINARY, .capinit = singularity_capability_init }, + { .command = NULL, .binary = NULL, .capinit = NULL } +}; + +int main(int argc, char **argv) { + int index; + char *command; + char *binary; + char *libexec_bin = joinpath(LIBEXECDIR, "/singularity/bin/"); + + singularity_registry_init(); + singularity_config_init(); + singularity_suid_init(); + + command = singularity_registry_get("COMMAND"); + if ( command == NULL ) { + singularity_message(ERROR, "no command passed\n"); + ABORT(255); + } + + for ( index = 0; cmd_wrapper[index].command != NULL; index++) { + if ( strcmp(command, cmd_wrapper[index].command) == 0 ) { + break; + } + } + + if ( cmd_wrapper[index].command == NULL ) { + singularity_message(ERROR, "unknown command %s\n", command); + ABORT(255); + } + + /* if allow setuid is no or nosuid requested fallback to non suid command */ + if ( singularity_suid_allowed() == 0 ) { + singularity_priv_init(); + singularity_priv_drop_perm(); + } else { + singularity_priv_init(); + cmd_wrapper[index].capinit(); + } + + binary = strjoin(libexec_bin, cmd_wrapper[index].binary); + execve(binary, argv, environ); // Flawfinder: ignore + + singularity_message(ERROR, "Failed to execute %s binary\n", cmd_wrapper[index].binary); + ABORT(255); + + return(0); +} diff --git a/tests/20-build.sh b/tests/20-build.sh index fb3815b475..5f4eb2bcc6 100755 --- a/tests/20-build.sh +++ b/tests/20-build.sh @@ -126,12 +126,45 @@ stest 0 sudo rm "$CONTAINER" stest 0 sudo singularity build "$CONTAINER" "${CONTAINER2}.tar" container_check -# from tar.gx to squashfs +# from tar.gz to squashfs stest 0 sh -c "singularity image.export '$CONTAINER' | gzip -9 > '${CONTAINER2}.tar.gz'" sudo rm "$CONTAINER" stest 0 sudo singularity build "$CONTAINER" "${CONTAINER2}.tar.gz" container_check +# isolated: from shub to squashfs (via def file) +sudo rm "$CONTAINER" +stest 0 sudo singularity build --isolated "$CONTAINER" "../examples/shub/Singularity" +container_check + +# isolated: from docker to squashfs (via def file) +sudo rm "$CONTAINER" +stest 0 sudo singularity build --isolated "$CONTAINER" "../examples/docker/Singularity" +container_check + +# isolated: from definition file to squashfs +sudo rm "$CONTAINER" +stest 0 sudo singularity build --isolated "$CONTAINER" "../examples/busybox/Singularity" +container_check + +# when isolated, ${SINGULARITY_TESTDIR} is not accessible, localimage need to be in the same +# directory as definition file and "From" need to be a relative path +cat >"${SINGULARITY_TESTDIR}/Singularity" </dev/null 2>&1; then # make sure local test does not exist, ignore errors sudo docker kill registry >/dev/null 2>&1 diff --git a/tests/29-instance.sh b/tests/29-instance.sh index fb20b54eed..f3687810a1 100755 --- a/tests/29-instance.sh +++ b/tests/29-instance.sh @@ -27,7 +27,6 @@ CONTAINER="$SINGULARITY_TESTDIR/container" stest 0 sudo singularity build "$CONTAINER" "../examples/busybox/Singularity" stest 0 singularity -x -d instance.start "$CONTAINER" service1 -stest 0 sleep 5 stest 0 singularity -x exec instance://service1 true stest 1 singularity -x exec instance://service1 false diff --git a/tests/31-action_uris.sh b/tests/31-action_uris.sh index 9fdbee2941..8a2b86eca9 100755 --- a/tests/31-action_uris.sh +++ b/tests/31-action_uris.sh @@ -29,9 +29,35 @@ test_init "Action URI tests" CONTAINER="$SINGULARITY_TESTDIR/container.img" +NO_XZ=false +if [ ! $(which xz) ]; then + NO_XZ=true + echo "Not testing with xz, not installed\n" +fi + +# Testing Docker URI stest 0 singularity exec docker://busybox true stest 1 singularity exec docker://busybox false +# Creating a new container +stest 0 sudo singularity build "$CONTAINER" "../examples/busybox/Singularity" + +# Creating tarball archives +stest 0 sh -c "singularity image.export "$CONTAINER" | gzip -c - > \"$CONTAINER.tar.gz\"" +stest 0 sh -c "singularity image.export "$CONTAINER" | bzip2 -c - > \"$CONTAINER.tar.bz2\"" + +$NO_XZ || stest 0 sh -c "singularity image.export "$CONTAINER" | xz -c - > \"$CONTAINER.tar.xz\"" + +# Testing tarball archives +stest 0 singularity exec "$CONTAINER.tar.gz" true +stest 0 singularity exec "$CONTAINER.tar.bz2" true + +$NO_XZ || stest 0 singularity exec "$CONTAINER.tar.xz" true + +# Testing automatic algorithm detection +stest 0 mv "$CONTAINER.tar.gz" "$CONTAINER.tar.bz2" +stest 0 singularity exec "$CONTAINER.tar.bz2" true + test_cleanup diff --git a/tests/40-privblock.sh b/tests/40-privblock.sh index 627691602d..67ab7c3af5 100755 --- a/tests/40-privblock.sh +++ b/tests/40-privblock.sh @@ -29,14 +29,30 @@ test_init "Checking escalation block" CONTAINER="$SINGULARITY_TESTDIR/container.img" -stest 0 sudo singularity build "$CONTAINER" docker://centos:7 +stest 0 sudo singularity build --sandbox "$CONTAINER" docker://centos:7 +stest 0 sudo singularity exec -w "$CONTAINER" chmod +s /bin/ping + stest 0 singularity exec "$CONTAINER" true stest 1 singularity exec "$CONTAINER" false # Checking no new privs with capabilities -stest 0 sudo singularity exec "$CONTAINER" chsh -s /bin/sh -stest 1 singularity exec "$CONTAINER" chsh -s /bin/sh +stest 1 sudo singularity exec "$CONTAINER" ping -c 1 127.0.0.1 +stest 1 singularity exec "$CONTAINER" ping -c 1 127.0.0.1 + +stest 1 sudo singularity exec --keep-privs "$CONTAINER" su -s /bin/sh - bin -c "ping -c 1 127.0.0.1" +stest 0 sudo singularity exec --keep-privs --allow-setuid "$CONTAINER" su -s /bin/sh - bin -c "ping -c 1 127.0.0.1" + +stest 1 sudo singularity exec "$CONTAINER" mount -B /etc /mnt +stest 1 sudo singularity exec --no-privs "$CONTAINER" mount -B /etc /mnt +stest 0 sudo singularity exec --add-caps sys_admin "$CONTAINER" mount -B /etc /mnt + +stest 0 sudo singularity exec --no-privs --add-caps sys_admin "$CONTAINER" mount -B /etc /mnt +stest 1 sudo singularity exec --keep-privs --drop-caps sys_admin "$CONTAINER" mount -B /etc /mnt + +stest 1 sudo singularity exec "$CONTAINER" dd if=/dev/mem of=/dev/null bs=1 count=1 +stest 0 sudo singularity exec --keep-privs "$CONTAINER" dd if=/dev/mem of=/dev/null bs=1 count=1 +stest 0 sudo rm -rf "$CONTAINER" test_cleanup