def test_09_add_user(self): """Test09 NixAuthentication().add_user().""" auth = NixAuthentication() with patch(BUILTINS + '.open', mock_open()): status = auth.add_user("root", "pw", 0, 0, "gecos", "/home", "/bin/bash") self.assertTrue(status)
def _setup_container_user_noroot(self, user): """ Setup user for engines without root support. Equivalent to _setup_container_user() for engines without root support. """ host_auth = NixAuthentication() (passwd, group) = self._select_auth_files() container_auth = NixAuthentication(passwd, group) if not user: user = HostInfo().username() (valid_user, user_id) = self._user_from_str(user, host_auth, container_auth) if not valid_user: Msg().err("Error: invalid syntax for user", user) return False if self.opt["user"] == "root": self.opt["user"] = HostInfo().username() self.opt["uid"] = str(HostInfo.uid) self.opt["gid"] = str(HostInfo.gid) if (self._is_mountpoint("/etc/passwd") or self._is_mountpoint("/etc/group")): self.opt["hostauth"] = self.opt["containerauth"] = False return True if self.opt["user"]: self.opt["uid"] = str(HostInfo.uid) self.opt["gid"] = str(HostInfo.gid) else: if self.opt["hostauth"] or self.opt["containerauth"]: Msg().err("Error: user not found on host") return False self.opt["user"] = user_id["user"] if "user" in user_id else user self.opt["uid"] = user_id["uid"] if "uid" in user_id else "" self.opt["gid"] = user_id["gid"] if "gid" in user_id else "" self._create_user(container_auth, host_auth) return True
def test_13_get_home(self, mock_user): """Test13 NixAuthentication().get_home().""" mock_user.return_value = ('root', '0', '0', 'root', '/root', '/bin/bash') auth = NixAuthentication() status = auth.get_home() self.assertEqual(status, '/root')
def test_01_init(self): """Test01 NixAuthentication() constructor.""" auth = NixAuthentication() self.assertEqual(auth.passwd_file, None) self.assertEqual(auth.group_file, None) auth = NixAuthentication("passwd", "group") self.assertEqual(auth.passwd_file, "passwd") self.assertEqual(auth.group_file, "group")
def test_04_user_in_subgid(self, mock_file, mock_host): """Test04 NixAuthentication().user_in_subgid.""" mock_host.return_value = ("user", "1000", "1000", "usr", "/home/user", "/bin/bash") auth = NixAuthentication() auth.subuid_file = "/etc/subgid" subgid_line = StringIO('user:100000:65536') with patch(BUILTINS + '.open') as mopen: mopen.return_value.__iter__ = ( lambda self: iter(subgid_line.readline, '')) listuser = auth.user_in_subgid("user") self.assertEqual(listuser, [("100000", "65536")]) self.assertTrue(mock_host.called) mock_file.return_value = ("user", "1000", "1000", "usr", "/home/user", "/bin/bash") auth = NixAuthentication() auth.passwd_file = "/etc/passwd" auth.subgid_file = "/etc/subgid" subgid_line = StringIO('user:100000:65536') with patch(BUILTINS + '.open') as mopen: mopen.return_value.__iter__ = ( lambda self: iter(subgid_line.readline, '')) listuser = auth.user_in_subgid("user") self.assertEqual(listuser, [("100000", "65536")]) self.assertTrue(mock_file.called)
def test_12_get_group(self, mock_file, mock_host): """Test12 NixAuthentication().get_group().""" auth = NixAuthentication() auth.group_file = "" auth.get_group("group") self.assertTrue(mock_host.called) self.assertFalse(mock_file.called) mock_host.reset_mock() mock_file.reset_mock() auth = NixAuthentication() auth.group_file = "group" auth.get_group("group") self.assertFalse(mock_host.called) self.assertTrue(mock_file.called)
def test_11_get_user(self, mock_file, mock_host): """Test11 NixAuthentication().get_user().""" auth = NixAuthentication() auth.passwd_file = "" auth.get_user("user") self.assertTrue(mock_host.called) self.assertFalse(mock_file.called) mock_host.reset_mock() mock_file.reset_mock() auth = NixAuthentication() auth.passwd_file = "passwd" auth.get_user("user") self.assertFalse(mock_host.called) self.assertTrue(mock_file.called)
def _get_volume_bindings(self): """Get the volume bindings string for singularity exec""" vol_list = [] home_dir = NixAuthentication().get_home() home_is_binded = False tmp_is_binded = False vartmp_is_binded = False for vol in self.opt["vol"]: (host_path, cont_path) = Uvolume(vol).split() if os.path.isdir(host_path): if host_path == home_dir and cont_path in ("", host_path): home_is_binded = True elif host_path == "/tmp" and cont_path in ("", "/tmp"): tmp_is_binded = True elif host_path == "/var/tmp" and cont_path in ("", "/var/tmp"): vartmp_is_binded = True vol_list.extend(["-B", "%s:%s" % (host_path, cont_path), ]) if not home_is_binded: vol_list.extend(["--home", "%s/root:%s" % (self.container_root, "/root"), ]) if not tmp_is_binded: vol_list.extend(["-B", "%s/tmp:/tmp" % (self.container_root), ]) if not vartmp_is_binded: vol_list.extend(["-B", "%s/var/tmp:/var/tmp" % (self.container_root), ]) return vol_list
def test_07__get_user_from_file(self): """Test07 NixAuthentication()._get_user_from_file().""" auth = NixAuthentication() auth.passwd_file = "passwd" passwd_line = StringIO('root:x:0:0:root:/root:/bin/bash') with patch(BUILTINS + '.open') as mopen: mopen.return_value.__iter__ = ( lambda self: iter(passwd_line.readline, '')) (name, uid, gid, gecos, _dir, shell) = auth._get_user_from_file("root") self.assertEqual(name, "root") self.assertEqual(uid, "0") self.assertEqual(gid, "0") self.assertEqual(gecos, "root") self.assertEqual(_dir, "/root") self.assertEqual(shell, "/bin/bash") passwd_line = StringIO('root:x:0:0:root:/root:/bin/bash') auth = NixAuthentication() with patch(BUILTINS + '.open') as mopen: mopen.return_value.__iter__ = ( lambda self: iter(passwd_line.readline, '')) (name, uid, gid, gecos, _dir, shell) = auth._get_user_from_file(0) self.assertEqual(name, "root") self.assertEqual(uid, "0") self.assertEqual(gid, "0") self.assertEqual(gecos, "root") self.assertEqual(_dir, "/root") self.assertEqual(shell, "/bin/bash")
def _setup_container_user(self, user): """Once we know which username to use inside the container we need to check if it exists in the passwd file that will be used inside the container. Since we can override the usage of the container /etc/passwd and /etc/group with files passed from the host, then we must check if this username is in the appropriate file so: 1. check if the passwd will be the one of the host system either because we passwd --hostauth or because we did --volume=/etc/passwd:/etc/passwd 2. else we are using the container original /etc/passwd In either case the user specified may not exist, in which case we copy the passwd file to a new file and we create the intended user. The file is then passwd/mapped into the container. """ host_auth = NixAuthentication() (passwd, group) = self._select_auth_files() container_auth = NixAuthentication(passwd, group) if not user: user = "******" (valid_user, user_id) = self._user_from_str(user, host_auth, container_auth) if not valid_user: Msg().err("Error: invalid syntax for user", user) return False if (self._is_mountpoint("/etc/passwd") or self._is_mountpoint("/etc/group")): self.opt["hostauth"] = self.opt["containerauth"] = False return True if self.opt["user"]: if self.opt["user"] != "root": self.opt["uid"] = str(HostInfo.uid) self.opt["gid"] = str(HostInfo.gid) else: if self.opt["hostauth"] or self.opt["containerauth"]: Msg().err("Error: user not found") return False self.opt["user"] = user_id["user"] if "user" in user_id else user self.opt["uid"] = user_id["uid"] if "uid" in user_id else "" self.opt["gid"] = user_id["gid"] if "gid" in user_id else "" self._create_user(container_auth, host_auth) return True
def namespace_exec(self, method, flags=CLONE_NEWUSER): """Execute command in namespace""" (pread1, pwrite1) = os.pipe() (pread2, pwrite2) = os.pipe() cpid = os.fork() if cpid: os.close(pwrite1) os.read(pread1, 1) # wait user = HostInfo().username() newidmap = ["newuidmap", str(cpid), "0", str(HostInfo.uid), "1"] for (subid, subcount) in NixAuthentication().user_in_subuid(user): newidmap.extend(["1", subid, subcount]) subprocess.call(newidmap) newidmap = ["newgidmap", str(cpid), "0", str(HostInfo.uid), "1"] for (subid, subcount) in NixAuthentication().user_in_subgid(user): newidmap.extend(["1", subid, subcount]) subprocess.call(newidmap) os.close(pwrite2) # notify (dummy, status) = os.waitpid(cpid, 0) if status % 256: Msg().err("Error: namespace exec action failed") return False return True self.unshare(flags) os.close(pwrite2) os.close(pwrite1) # notify os.read(pread2, 1) # wait try: os.setgid(0) os.setuid(0) os.setgroups([0, 0, ]) except OSError: Msg().err("Error: setting ids and groups") return False # pylint: disable=protected-access os._exit(int(method())) return True
def _run_as_root(self): """Set configure running as normal user or as root via --fakeroot """ username = HostInfo().username() if "user" in self.opt: if self.opt["user"] == username: return False if self.opt["user"] != "root" and self.opt["uid"] != '0': Msg().out("Warning: running as another user not supported") return False if self._has_option("--fakeroot", "exec"): if (NixAuthentication().user_in_subuid(username) and NixAuthentication().user_in_subgid(username)): Config.conf['singularity_options'].extend(["--fakeroot", ]) return True self.opt["user"] = username return False
def _create_user(self, container_auth, host_auth): """If we need to create a new user then we first copy /etc/passwd and /etc/group to new files and them we add the user account into these copied files which later are binding/mapped/passed to the container. So setup this binding as well via hostauth. """ if self.opt["containerauth"]: tmp_passwd = container_auth.passwd_file tmp_group = container_auth.group_file self.opt["hostauth"] = False else: FileUtil().umask(0o077) tmp_passwd = FileUtil("passwd").mktmp() tmp_group = FileUtil("group").mktmp() if self.opt["hostauth"]: FileUtil("/etc/passwd").copyto(tmp_passwd) FileUtil("/etc/group").copyto(tmp_group) else: FileUtil(container_auth.passwd_file).copyto(tmp_passwd) FileUtil(container_auth.group_file).copyto(tmp_group) FileUtil().umask() if not (self.opt["containerauth"] or self.opt["hostauth"]): Msg().out("Warning: non-existing user will be created", l=Msg.DBG) self._fill_user() new_auth = NixAuthentication(tmp_passwd, tmp_group) new_auth.add_user(self.opt["user"], 'x', self.opt["uid"], self.opt["gid"], self.opt["gecos"], self.opt["home"], self.opt["shell"]) (group, dummy, dummy) = host_auth.get_group(self.opt["gid"]) if not group: new_auth.add_group(self.opt["user"], self.opt["gid"]) for sup_gid in os.getgroups(): new_auth.add_group('G' + str(sup_gid), str(sup_gid), [self.opt["user"], ]) if not self.opt["containerauth"]: self.opt["hostauth"] = True self.hostauth_list = (tmp_passwd + ":/etc/passwd", tmp_group + ":/etc/group") return True
def _fill_user(self): """Fill in values for user to be used in the account creation. Provide default values in case the required fields are empty. """ if not self.opt["uid"]: self.opt["uid"] = str(HostInfo.uid) if not self.opt["gid"]: self.opt["gid"] = str(HostInfo.gid) if not self.opt["user"]: self.opt["user"] = "******" + self.opt["uid"][0:4] if self.opt["bindhome"]: self.opt["home"] = NixAuthentication().get_home() if not self.opt["home"]: self.opt["home"] = '/' if not self.opt["shell"]: self.opt["shell"] = "/bin/sh" if not self.opt["gecos"]: self.opt["gecos"] = "*UDOCKER*"
def test_06__get_group_from_host(self, mock_grgid, mock_grname): """Test06 NixAuthentication()._get_group_from_host().""" hgr = grp.struct_group(["root", "*", "0", str([])]) mock_grgid.return_value = hgr auth = NixAuthentication() (name, gid, mem) = auth._get_group_from_host(0) self.assertEqual(name, hgr.gr_name) self.assertEqual(gid, str(hgr.gr_gid)) self.assertEqual(mem, hgr.gr_mem) mock_grname.return_value = hgr auth = NixAuthentication() (name, gid, mem) = auth._get_group_from_host("root") self.assertEqual(name, hgr.gr_name) self.assertEqual(gid, str(hgr.gr_gid)) self.assertEqual(mem, hgr.gr_mem)
def test_08__get_group_from_file(self): """Test08 NixAuthentication()._get_group_from_file().""" auth = NixAuthentication() auth.passwd_file = "passwd" group_line = StringIO('root:x:0:a,b,c') with patch(BUILTINS + '.open') as mopen: mopen.return_value.__iter__ = ( lambda self: iter(group_line.readline, '')) (name, gid, mem) = auth._get_group_from_file("root") self.assertEqual(name, "root") self.assertEqual(gid, "0") self.assertEqual(mem, "a,b,c") group_line = StringIO('root:x:0:a,b,c') auth = NixAuthentication() with patch(BUILTINS + '.open') as mopen: mopen.return_value.__iter__ = ( lambda self: iter(group_line.readline, '')) (name, gid, mem) = auth._get_group_from_file(0) self.assertEqual(name, "root") self.assertEqual(gid, "0") self.assertEqual(mem, "a,b,c")
def test_05__get_user_from_host(self, mock_getpwuid, mock_getpwnam): """Test05 NixAuthentication()._get_user_from_host().""" usr = pwd.struct_passwd( ["root", "*", "0", "0", "root usr", "/root", "/bin/bash"]) mock_getpwuid.return_value = usr auth = NixAuthentication() (name, uid, gid, gecos, _dir, shell) = auth._get_user_from_host(0) self.assertEqual(name, usr.pw_name) self.assertEqual(uid, usr.pw_uid) self.assertEqual(gid, str(usr.pw_gid)) self.assertEqual(gecos, usr.pw_gecos) self.assertEqual(_dir, usr.pw_dir) self.assertEqual(shell, usr.pw_shell) mock_getpwnam.return_value = usr auth = NixAuthentication() (name, uid, gid, gecos, _dir, shell) = auth._get_user_from_host("root") self.assertEqual(name, usr.pw_name) self.assertEqual(uid, usr.pw_uid) self.assertEqual(gid, str(usr.pw_gid)) self.assertEqual(gecos, usr.pw_gecos) self.assertEqual(_dir, usr.pw_dir)
def _get_bindhome(self): """Binding of the host $HOME in to the container $HOME""" if self.opt["bindhome"]: return NixAuthentication().get_home() return ""
def test_10_add_group(self): """Test10 NixAuthentication().add_group().""" auth = NixAuthentication() with patch(BUILTINS + '.open', mock_open()): status = auth.add_group("root", 0) self.assertTrue(status)