github.com/rootless-containers/rootlesskit/v2@v2.3.4/pkg/parent/warn.go (about)

     1  package parent
     2  
     3  import (
     4  	"errors"
     5  	"os"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"github.com/moby/sys/mountinfo"
    10  	"github.com/sirupsen/logrus"
    11  	"golang.org/x/sys/unix"
    12  )
    13  
    14  func warnPropagation(propagation string) {
    15  	mounts, err := mountinfo.GetMounts(mountinfo.SingleEntryFilter("/"))
    16  	if err != nil || len(mounts) < 1 {
    17  		logrus.WithError(err).Warn("Failed to parse mountinfo")
    18  		return
    19  	}
    20  	root := mounts[0]
    21  	// 1. When running on a "sane" host,   root.Optional is like "shared:1".   ("shared" in findmnt(8) output)
    22  	// 2. When running inside a container, root.Optional is like "master:363". ("private, slave" in findmnt(8) output)
    23  	//
    24  	// Setting non-private propagation is supported for 1, unsupported for 2.
    25  	if !strings.Contains(propagation, "private") && !strings.Contains(root.Optional, "shared") {
    26  		logrus.Warnf("The host root filesystem is mounted as %q. Setting child propagation to %q is not supported.",
    27  			root.Optional, propagation)
    28  	}
    29  }
    30  
    31  // warnSysctl verifies /proc/sys/kernel/unprivileged_userns_clone and /proc/sys/user/max_user_namespaces
    32  func warnSysctl() {
    33  	uuc, err := os.ReadFile("/proc/sys/kernel/unprivileged_userns_clone")
    34  	// The file exists only on distros with the "add sysctl to disallow unprivileged CLONE_NEWUSER by default" patch.
    35  	// (e.g. Debian and Arch)
    36  	if err == nil {
    37  		s := strings.TrimSpace(string(uuc))
    38  		i, err := strconv.ParseInt(s, 10, 64)
    39  		if err != nil {
    40  			logrus.WithError(err).Warnf("Failed to parse /proc/sys/kernel/unprivileged_userns_clone (%q)", s)
    41  		} else if i == 0 {
    42  			logrus.Warn("/proc/sys/kernel/unprivileged_userns_clone needs to be set to 1.")
    43  		}
    44  	}
    45  
    46  	mun, err := os.ReadFile("/proc/sys/user/max_user_namespaces")
    47  	if err == nil {
    48  		s := strings.TrimSpace(string(mun))
    49  		i, err := strconv.ParseInt(strings.TrimSpace(string(mun)), 10, 64)
    50  		if err != nil {
    51  			logrus.WithError(err).Warnf("Failed to parse /proc/sys/user/max_user_namespaces (%q)", s)
    52  		} else if i == 0 {
    53  			logrus.Warn("/proc/sys/user/max_user_namespaces needs to be set to non-zero.")
    54  		} else {
    55  			threshold := int64(1024)
    56  			if i < threshold {
    57  				logrus.Warnf("/proc/sys/user/max_user_namespaces=%d may be low. Consider setting to >= %d.", i, threshold)
    58  			}
    59  		}
    60  	}
    61  }
    62  
    63  func warnOnChildStartFailure(childStartErr error) {
    64  	if errors.Is(childStartErr, unix.EACCES) {
    65  		// apparmor_restrict_unprivileged_userns is available since Ubuntu 23.10.
    66  		// Enabled by default since Ubuntu 24.04.
    67  		// https://github.com/containerd/nerdctl/issues/2847
    68  		b, err := os.ReadFile("/proc/sys/kernel/apparmor_restrict_unprivileged_userns")
    69  		if err == nil {
    70  			s := strings.TrimSpace(string(b))
    71  			i, err := strconv.ParseInt(s, 10, 64)
    72  			if err != nil {
    73  				logrus.WithError(err).Warnf("Failed to parse /proc/sys/kernel/apparmor_restrict_unprivileged_userns (%q)", s)
    74  			} else if i == 1 {
    75  				logrus.WithError(childStartErr).Warnf("This error might have happened because /proc/sys/kernel/apparmor_restrict_unprivileged_userns is set to 1")
    76  				selfExe, err := os.Executable()
    77  				if err != nil {
    78  					selfExe = "/usr/local/bin/rootlesskit"
    79  					logrus.WithError(err).Warnf("Failed to detect the path of the rootlesskit binary, assuming it to be %q", selfExe)
    80  				}
    81  				profileName := strings.ReplaceAll(strings.TrimPrefix(selfExe, "/"), "/", ".")
    82  				const tmpl = `
    83  
    84  ########## BEGIN ##########
    85  cat <<EOT | sudo tee "/etc/apparmor.d/%s"
    86  # ref: https://ubuntu.com/blog/ubuntu-23-10-restricted-unprivileged-user-namespaces
    87  abi <abi/4.0>,
    88  include <tunables/global>
    89  
    90  %s flags=(unconfined) {
    91    userns,
    92  
    93    # Site-specific additions and overrides. See local/README for details.
    94    include if exists <local/%s>
    95  }
    96  EOT
    97  sudo systemctl restart apparmor.service
    98  ########## END ##########
    99  `
   100  				logrus.Warnf("Hint: try running the following commands:\n"+tmpl+"\n", profileName, selfExe, profileName)
   101  			}
   102  		}
   103  	}
   104  }