11477Szelenkov@nginx.comimport grp 21596Szelenkov@nginx.comimport os 31293St.nateldemoura@f5.comimport pwd 4*1635Szelenkov@nginx.com 51596Szelenkov@nginx.comimport pytest 61477Szelenkov@nginx.com 71182St.nateldemoura@f5.comfrom unit.applications.lang.go import TestApplicationGo 81182St.nateldemoura@f5.comfrom unit.feature.isolation import TestFeatureIsolation 91182St.nateldemoura@f5.com 101182St.nateldemoura@f5.comclass TestGoIsolation(TestApplicationGo): 111467Szelenkov@nginx.com prerequisites = {'modules': {'go': 'any'}, 'features': ['isolation']} 121182St.nateldemoura@f5.com 131182St.nateldemoura@f5.com isolation = TestFeatureIsolation() 141182St.nateldemoura@f5.com 151182St.nateldemoura@f5.com @classmethod 161596Szelenkov@nginx.com def setup_class(cls, complete_check=True): 171596Szelenkov@nginx.com unit = super().setup_class(complete_check=False) 181182St.nateldemoura@f5.com 191596Szelenkov@nginx.com TestFeatureIsolation().check(cls.available, unit.temp_dir) 201182St.nateldemoura@f5.com 211182St.nateldemoura@f5.com return unit if not complete_check else unit.complete() 221182St.nateldemoura@f5.com 231307St.nateldemoura@f5.com def unpriv_creds(self): 241307St.nateldemoura@f5.com nobody_uid = pwd.getpwnam('nobody').pw_uid 251307St.nateldemoura@f5.com 261307St.nateldemoura@f5.com try: 271307St.nateldemoura@f5.com nogroup_gid = grp.getgrnam('nogroup').gr_gid 281307St.nateldemoura@f5.com nogroup = 'nogroup' 291307St.nateldemoura@f5.com except: 301307St.nateldemoura@f5.com nogroup_gid = grp.getgrnam('nobody').gr_gid 311307St.nateldemoura@f5.com nogroup = 'nobody' 321307St.nateldemoura@f5.com 331307St.nateldemoura@f5.com return (nobody_uid, nogroup_gid, nogroup) 341307St.nateldemoura@f5.com 351182St.nateldemoura@f5.com def isolation_key(self, key): 361182St.nateldemoura@f5.com return key in self.available['features']['isolation'].keys() 371182St.nateldemoura@f5.com 381182St.nateldemoura@f5.com def test_isolation_values(self): 391182St.nateldemoura@f5.com self.load('ns_inspect') 401182St.nateldemoura@f5.com 411296St.nateldemoura@f5.com obj = self.getjson()['body'] 421182St.nateldemoura@f5.com 431182St.nateldemoura@f5.com for ns, ns_value in self.available['features']['isolation'].items(): 441182St.nateldemoura@f5.com if ns.upper() in obj['NS']: 451596Szelenkov@nginx.com assert obj['NS'][ns.upper()] == ns_value, '%s match' % ns 461182St.nateldemoura@f5.com 471596Szelenkov@nginx.com def test_isolation_unpriv_user(self, is_su): 481182St.nateldemoura@f5.com if not self.isolation_key('unprivileged_userns_clone'): 491596Szelenkov@nginx.com pytest.skip('unprivileged clone is not available') 501182St.nateldemoura@f5.com 511596Szelenkov@nginx.com if is_su: 521596Szelenkov@nginx.com pytest.skip('privileged tests, skip this') 531307St.nateldemoura@f5.com 541307St.nateldemoura@f5.com self.load('ns_inspect') 551307St.nateldemoura@f5.com obj = self.getjson()['body'] 561307St.nateldemoura@f5.com 571596Szelenkov@nginx.com assert obj['UID'] == os.geteuid(), 'uid match' 581596Szelenkov@nginx.com assert obj['GID'] == os.getegid(), 'gid match' 591307St.nateldemoura@f5.com 601307St.nateldemoura@f5.com self.load('ns_inspect', isolation={'namespaces': {'credential': True}}) 611307St.nateldemoura@f5.com 621307St.nateldemoura@f5.com obj = self.getjson()['body'] 631307St.nateldemoura@f5.com 641307St.nateldemoura@f5.com nobody_uid, nogroup_gid, nogroup = self.unpriv_creds() 651307St.nateldemoura@f5.com 661307St.nateldemoura@f5.com # unprivileged unit map itself to nobody in the container by default 671596Szelenkov@nginx.com assert obj['UID'] == nobody_uid, 'uid of nobody' 681596Szelenkov@nginx.com assert obj['GID'] == nogroup_gid, 'gid of %s' % nogroup 691307St.nateldemoura@f5.com 701307St.nateldemoura@f5.com self.load( 711307St.nateldemoura@f5.com 'ns_inspect', 721307St.nateldemoura@f5.com user='root', 731307St.nateldemoura@f5.com isolation={'namespaces': {'credential': True}}, 741307St.nateldemoura@f5.com ) 751307St.nateldemoura@f5.com 761307St.nateldemoura@f5.com obj = self.getjson()['body'] 771307St.nateldemoura@f5.com 781596Szelenkov@nginx.com assert obj['UID'] == 0, 'uid match user=root' 791596Szelenkov@nginx.com assert obj['GID'] == 0, 'gid match user=root' 801307St.nateldemoura@f5.com 811307St.nateldemoura@f5.com self.load( 821307St.nateldemoura@f5.com 'ns_inspect', 831307St.nateldemoura@f5.com user='root', 841307St.nateldemoura@f5.com group=nogroup, 851307St.nateldemoura@f5.com isolation={'namespaces': {'credential': True}}, 861307St.nateldemoura@f5.com ) 871307St.nateldemoura@f5.com 881307St.nateldemoura@f5.com obj = self.getjson()['body'] 891307St.nateldemoura@f5.com 901596Szelenkov@nginx.com assert obj['UID'] == 0, 'uid match user=root group=nogroup' 911596Szelenkov@nginx.com assert obj['GID'] == nogroup_gid, 'gid match user=root group=nogroup' 921307St.nateldemoura@f5.com 931307St.nateldemoura@f5.com self.load( 941307St.nateldemoura@f5.com 'ns_inspect', 951307St.nateldemoura@f5.com user='root', 961307St.nateldemoura@f5.com group='root', 971307St.nateldemoura@f5.com isolation={ 981307St.nateldemoura@f5.com 'namespaces': {'credential': True}, 991596Szelenkov@nginx.com 'uidmap': [{'container': 0, 'host': os.geteuid(), 'size': 1}], 1001596Szelenkov@nginx.com 'gidmap': [{'container': 0, 'host': os.getegid(), 'size': 1}], 1011307St.nateldemoura@f5.com }, 1021307St.nateldemoura@f5.com ) 1031307St.nateldemoura@f5.com 1041307St.nateldemoura@f5.com obj = self.getjson()['body'] 1051307St.nateldemoura@f5.com 1061596Szelenkov@nginx.com assert obj['UID'] == 0, 'uid match uidmap' 1071596Szelenkov@nginx.com assert obj['GID'] == 0, 'gid match gidmap' 1081307St.nateldemoura@f5.com 1091596Szelenkov@nginx.com def test_isolation_priv_user(self, is_su): 1101596Szelenkov@nginx.com if not is_su: 1111596Szelenkov@nginx.com pytest.skip('unprivileged tests, skip this') 1121307St.nateldemoura@f5.com 1131182St.nateldemoura@f5.com self.load('ns_inspect') 1141293St.nateldemoura@f5.com 1151307St.nateldemoura@f5.com nobody_uid, nogroup_gid, nogroup = self.unpriv_creds() 1161307St.nateldemoura@f5.com 1171307St.nateldemoura@f5.com obj = self.getjson()['body'] 1181293St.nateldemoura@f5.com 1191596Szelenkov@nginx.com assert obj['UID'] == nobody_uid, 'uid match' 1201596Szelenkov@nginx.com assert obj['GID'] == nogroup_gid, 'gid match' 1211307St.nateldemoura@f5.com 1221307St.nateldemoura@f5.com self.load('ns_inspect', isolation={'namespaces': {'credential': True}}) 1231293St.nateldemoura@f5.com 1241296St.nateldemoura@f5.com obj = self.getjson()['body'] 1251182St.nateldemoura@f5.com 1261307St.nateldemoura@f5.com # privileged unit map app creds in the container by default 1271596Szelenkov@nginx.com assert obj['UID'] == nobody_uid, 'uid nobody' 1281596Szelenkov@nginx.com assert obj['GID'] == nogroup_gid, 'gid nobody' 1291293St.nateldemoura@f5.com 1301307St.nateldemoura@f5.com self.load( 1311307St.nateldemoura@f5.com 'ns_inspect', 1321307St.nateldemoura@f5.com user='root', 1331307St.nateldemoura@f5.com isolation={'namespaces': {'credential': True}}, 1341307St.nateldemoura@f5.com ) 1351182St.nateldemoura@f5.com 1361296St.nateldemoura@f5.com obj = self.getjson()['body'] 1371182St.nateldemoura@f5.com 1381596Szelenkov@nginx.com assert obj['UID'] == 0, 'uid nobody user=root' 1391596Szelenkov@nginx.com assert obj['GID'] == 0, 'gid nobody user=root' 1401182St.nateldemoura@f5.com 1411307St.nateldemoura@f5.com self.load( 1421307St.nateldemoura@f5.com 'ns_inspect', 1431307St.nateldemoura@f5.com user='root', 1441307St.nateldemoura@f5.com group=nogroup, 1451307St.nateldemoura@f5.com isolation={'namespaces': {'credential': True}}, 1461182St.nateldemoura@f5.com ) 1471182St.nateldemoura@f5.com 1481296St.nateldemoura@f5.com obj = self.getjson()['body'] 1491182St.nateldemoura@f5.com 1501596Szelenkov@nginx.com assert obj['UID'] == 0, 'uid match user=root group=nogroup' 1511596Szelenkov@nginx.com assert obj['GID'] == nogroup_gid, 'gid match user=root group=nogroup' 1521307St.nateldemoura@f5.com 1531307St.nateldemoura@f5.com self.load( 1541307St.nateldemoura@f5.com 'ns_inspect', 1551307St.nateldemoura@f5.com user='root', 1561307St.nateldemoura@f5.com group='root', 1571307St.nateldemoura@f5.com isolation={ 1581307St.nateldemoura@f5.com 'namespaces': {'credential': True}, 1591307St.nateldemoura@f5.com 'uidmap': [{'container': 0, 'host': 0, 'size': 1}], 1601307St.nateldemoura@f5.com 'gidmap': [{'container': 0, 'host': 0, 'size': 1}], 1611307St.nateldemoura@f5.com }, 1621307St.nateldemoura@f5.com ) 1631307St.nateldemoura@f5.com 1641307St.nateldemoura@f5.com obj = self.getjson()['body'] 1651307St.nateldemoura@f5.com 1661596Szelenkov@nginx.com assert obj['UID'] == 0, 'uid match uidmap user=root' 1671596Szelenkov@nginx.com assert obj['GID'] == 0, 'gid match gidmap user=root' 1681307St.nateldemoura@f5.com 1691307St.nateldemoura@f5.com # map 65535 uids 1701307St.nateldemoura@f5.com self.load( 1711307St.nateldemoura@f5.com 'ns_inspect', 1721307St.nateldemoura@f5.com user='nobody', 1731307St.nateldemoura@f5.com isolation={ 1741307St.nateldemoura@f5.com 'namespaces': {'credential': True}, 1751307St.nateldemoura@f5.com 'uidmap': [ 1761307St.nateldemoura@f5.com {'container': 0, 'host': 0, 'size': nobody_uid + 1} 1771307St.nateldemoura@f5.com ], 1781307St.nateldemoura@f5.com }, 1791307St.nateldemoura@f5.com ) 1801307St.nateldemoura@f5.com 1811307St.nateldemoura@f5.com obj = self.getjson()['body'] 1821307St.nateldemoura@f5.com 1831596Szelenkov@nginx.com assert obj['UID'] == nobody_uid, 'uid match uidmap user=nobody' 1841596Szelenkov@nginx.com assert obj['GID'] == nogroup_gid, 'gid match uidmap user=nobody' 1851182St.nateldemoura@f5.com 1861182St.nateldemoura@f5.com def test_isolation_mnt(self): 1871182St.nateldemoura@f5.com if not self.isolation_key('mnt'): 1881596Szelenkov@nginx.com pytest.skip('mnt namespace is not supported') 1891182St.nateldemoura@f5.com 1901182St.nateldemoura@f5.com if not self.isolation_key('unprivileged_userns_clone'): 1911596Szelenkov@nginx.com pytest.skip('unprivileged clone is not available') 1921182St.nateldemoura@f5.com 1931307St.nateldemoura@f5.com self.load( 1941307St.nateldemoura@f5.com 'ns_inspect', 1951307St.nateldemoura@f5.com isolation={'namespaces': {'mount': True, 'credential': True}}, 1961182St.nateldemoura@f5.com ) 1971182St.nateldemoura@f5.com 1981296St.nateldemoura@f5.com obj = self.getjson()['body'] 1991182St.nateldemoura@f5.com 2001182St.nateldemoura@f5.com # all but user and mnt 2011182St.nateldemoura@f5.com allns = list(self.available['features']['isolation'].keys()) 2021182St.nateldemoura@f5.com allns.remove('user') 2031182St.nateldemoura@f5.com allns.remove('mnt') 2041182St.nateldemoura@f5.com 2051182St.nateldemoura@f5.com for ns in allns: 2061182St.nateldemoura@f5.com if ns.upper() in obj['NS']: 2071596Szelenkov@nginx.com assert ( 2081596Szelenkov@nginx.com obj['NS'][ns.upper()] 2091596Szelenkov@nginx.com == self.available['features']['isolation'][ns] 2101596Szelenkov@nginx.com ), ('%s match' % ns) 2111182St.nateldemoura@f5.com 2121596Szelenkov@nginx.com assert obj['NS']['MNT'] != self.isolation.getns('mnt'), 'mnt set' 2131596Szelenkov@nginx.com assert obj['NS']['USER'] != self.isolation.getns('user'), 'user set' 2141182St.nateldemoura@f5.com 2151596Szelenkov@nginx.com def test_isolation_pid(self, is_su): 2161182St.nateldemoura@f5.com if not self.isolation_key('pid'): 2171596Szelenkov@nginx.com pytest.skip('pid namespace is not supported') 2181182St.nateldemoura@f5.com 2191596Szelenkov@nginx.com if not (is_su or self.isolation_key('unprivileged_userns_clone')): 2201596Szelenkov@nginx.com pytest.skip('requires root or unprivileged_userns_clone') 2211182St.nateldemoura@f5.com 2221307St.nateldemoura@f5.com self.load( 2231307St.nateldemoura@f5.com 'ns_inspect', 2241307St.nateldemoura@f5.com isolation={'namespaces': {'pid': True, 'credential': True}}, 2251307St.nateldemoura@f5.com ) 2261182St.nateldemoura@f5.com 2271296St.nateldemoura@f5.com obj = self.getjson()['body'] 2281182St.nateldemoura@f5.com 2291596Szelenkov@nginx.com assert obj['PID'] == 1, 'pid of container is 1' 2301182St.nateldemoura@f5.com 2311236St.nateldemoura@f5.com def test_isolation_namespace_false(self): 2321236St.nateldemoura@f5.com self.load('ns_inspect') 2331236St.nateldemoura@f5.com allns = list(self.available['features']['isolation'].keys()) 2341236St.nateldemoura@f5.com 2351236St.nateldemoura@f5.com remove_list = ['unprivileged_userns_clone', 'ipc', 'cgroup'] 2361236St.nateldemoura@f5.com allns = [ns for ns in allns if ns not in remove_list] 2371236St.nateldemoura@f5.com 2381236St.nateldemoura@f5.com namespaces = {} 2391236St.nateldemoura@f5.com for ns in allns: 2401236St.nateldemoura@f5.com if ns == 'user': 2411236St.nateldemoura@f5.com namespaces['credential'] = False 2421236St.nateldemoura@f5.com elif ns == 'mnt': 2431236St.nateldemoura@f5.com namespaces['mount'] = False 2441236St.nateldemoura@f5.com elif ns == 'net': 2451236St.nateldemoura@f5.com namespaces['network'] = False 2461236St.nateldemoura@f5.com elif ns == 'uts': 2471236St.nateldemoura@f5.com namespaces['uname'] = False 2481236St.nateldemoura@f5.com else: 2491236St.nateldemoura@f5.com namespaces[ns] = False 2501236St.nateldemoura@f5.com 2511307St.nateldemoura@f5.com self.load('ns_inspect', isolation={'namespaces': namespaces}) 2521236St.nateldemoura@f5.com 2531296St.nateldemoura@f5.com obj = self.getjson()['body'] 2541236St.nateldemoura@f5.com 2551236St.nateldemoura@f5.com for ns in allns: 2561236St.nateldemoura@f5.com if ns.upper() in obj['NS']: 2571596Szelenkov@nginx.com assert ( 2581596Szelenkov@nginx.com obj['NS'][ns.upper()] 2591596Szelenkov@nginx.com == self.available['features']['isolation'][ns] 2601596Szelenkov@nginx.com ), ('%s match' % ns) 2611236St.nateldemoura@f5.com 2621490St.nateldemoura@f5.com def test_go_isolation_rootfs_container(self): 2631490St.nateldemoura@f5.com if not self.isolation_key('unprivileged_userns_clone'): 2641596Szelenkov@nginx.com pytest.skip('unprivileged clone is not available') 2651490St.nateldemoura@f5.com 2661490St.nateldemoura@f5.com if not self.isolation_key('mnt'): 2671596Szelenkov@nginx.com pytest.skip('mnt namespace is not supported') 2681490St.nateldemoura@f5.com 2691490St.nateldemoura@f5.com isolation = { 2701490St.nateldemoura@f5.com 'namespaces': {'mount': True, 'credential': True}, 2711596Szelenkov@nginx.com 'rootfs': self.temp_dir, 2721490St.nateldemoura@f5.com } 2731490St.nateldemoura@f5.com 2741490St.nateldemoura@f5.com self.load('ns_inspect', isolation=isolation) 2751490St.nateldemoura@f5.com 2761490St.nateldemoura@f5.com obj = self.getjson(url='/?file=/go/app')['body'] 2771490St.nateldemoura@f5.com 2781596Szelenkov@nginx.com assert obj['FileExists'] == True, 'app relative to rootfs' 2791490St.nateldemoura@f5.com 2801490St.nateldemoura@f5.com obj = self.getjson(url='/?file=/bin/sh')['body'] 2811596Szelenkov@nginx.com assert obj['FileExists'] == False, 'file should not exists' 2821490St.nateldemoura@f5.com 2831596Szelenkov@nginx.com def test_go_isolation_rootfs_container_priv(self, is_su): 2841596Szelenkov@nginx.com if not is_su: 2851596Szelenkov@nginx.com pytest.skip('requires root') 2861490St.nateldemoura@f5.com 2871490St.nateldemoura@f5.com if not self.isolation_key('mnt'): 2881596Szelenkov@nginx.com pytest.skip('mnt namespace is not supported') 2891490St.nateldemoura@f5.com 2901490St.nateldemoura@f5.com isolation = { 2911490St.nateldemoura@f5.com 'namespaces': {'mount': True}, 2921596Szelenkov@nginx.com 'rootfs': self.temp_dir, 2931490St.nateldemoura@f5.com } 2941490St.nateldemoura@f5.com 2951490St.nateldemoura@f5.com self.load('ns_inspect', isolation=isolation) 2961490St.nateldemoura@f5.com 2971490St.nateldemoura@f5.com obj = self.getjson(url='/?file=/go/app')['body'] 2981490St.nateldemoura@f5.com 2991596Szelenkov@nginx.com assert obj['FileExists'] == True, 'app relative to rootfs' 3001490St.nateldemoura@f5.com 3011490St.nateldemoura@f5.com obj = self.getjson(url='/?file=/bin/sh')['body'] 3021596Szelenkov@nginx.com assert obj['FileExists'] == False, 'file should not exists' 3031490St.nateldemoura@f5.com 3041581St.nateldemoura@f5.com def test_go_isolation_rootfs_default_tmpfs(self): 3051581St.nateldemoura@f5.com if not self.isolation_key('unprivileged_userns_clone'): 3061596Szelenkov@nginx.com pytest.skip('unprivileged clone is not available') 3071581St.nateldemoura@f5.com 3081581St.nateldemoura@f5.com if not self.isolation_key('mnt'): 3091596Szelenkov@nginx.com pytest.skip('mnt namespace is not supported') 3101581St.nateldemoura@f5.com 3111581St.nateldemoura@f5.com isolation = { 3121581St.nateldemoura@f5.com 'namespaces': {'mount': True, 'credential': True}, 3131596Szelenkov@nginx.com 'rootfs': self.temp_dir, 3141581St.nateldemoura@f5.com } 3151581St.nateldemoura@f5.com 3161581St.nateldemoura@f5.com self.load('ns_inspect', isolation=isolation) 3171581St.nateldemoura@f5.com 3181581St.nateldemoura@f5.com obj = self.getjson(url='/?file=/tmp')['body'] 3191581St.nateldemoura@f5.com 3201596Szelenkov@nginx.com assert obj['FileExists'] == True, 'app has /tmp' 321