github.com/rkt/rkt@v1.30.1-0.20200224141603-171c416fac02/tests/rkt_caps_test.go (about)

     1  // Copyright 2015 The rkt Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // +build host coreos src kvm
    16  
    17  package main
    18  
    19  import (
    20  	"fmt"
    21  	"os"
    22  	"strings"
    23  	"testing"
    24  
    25  	"github.com/rkt/rkt/common"
    26  	"github.com/rkt/rkt/tests/testutils"
    27  	"github.com/syndtr/gocapability/capability"
    28  )
    29  
    30  var appCapsTests = []struct {
    31  	// constants
    32  	testName     string // name of the image
    33  	capRetainSet string // only use value if != "x"
    34  	capRemoveSet string // only use value if != "x"
    35  	expected     string // caps bounding set as printed by gocapability
    36  	useOldFlag   bool   // use --cap-retain or --cap-remove instead of --caps-*; backwards compatibility testing
    37  
    38  	// set during the test
    39  	imageFile string
    40  }{
    41  	// Testing without isolators
    42  	{
    43  		testName:     "image-none",
    44  		capRetainSet: "x",
    45  		capRemoveSet: "x",
    46  		expected: strings.Join([]string{
    47  			"chown",
    48  			"dac_override",
    49  			"fowner",
    50  			"fsetid",
    51  			"kill",
    52  			"setgid",
    53  			"setuid",
    54  			"setpcap",
    55  			"net_bind_service",
    56  			"net_raw",
    57  			"sys_chroot",
    58  			"mknod",
    59  			"audit_write",
    60  			"setfcap",
    61  		}, ", "),
    62  	},
    63  	// Testing retain set
    64  	{
    65  		testName:     "image-only-one-cap",
    66  		capRetainSet: "CAP_NET_ADMIN",
    67  		capRemoveSet: "x",
    68  		expected:     "net_admin",
    69  	},
    70  	{
    71  		testName:     "image-only-one-cap-old",
    72  		capRetainSet: "CAP_NET_ADMIN",
    73  		capRemoveSet: "x",
    74  		expected:     "net_admin",
    75  		useOldFlag:   true,
    76  	},
    77  	{
    78  		testName:     "image-only-one-cap-from-default",
    79  		capRetainSet: "CAP_CHOWN",
    80  		capRemoveSet: "x",
    81  		expected:     "chown",
    82  	},
    83  	{
    84  		testName: "image-some-caps",
    85  		capRetainSet: strings.Join([]string{
    86  			"CAP_CHOWN",
    87  			"CAP_FOWNER",
    88  			"CAP_SYS_ADMIN",
    89  			"CAP_NET_ADMIN",
    90  		}, ","),
    91  		capRemoveSet: "x",
    92  		expected: strings.Join([]string{
    93  			"chown",
    94  			"fowner",
    95  			"net_admin",
    96  			"sys_admin",
    97  		}, ", "),
    98  	},
    99  	{
   100  		testName: "image-caps-from-nspawn-default",
   101  		capRetainSet: strings.Join([]string{
   102  			"CAP_CHOWN",
   103  			"CAP_DAC_OVERRIDE",
   104  			"CAP_DAC_READ_SEARCH",
   105  			"CAP_FOWNER",
   106  			"CAP_FSETID",
   107  			"CAP_IPC_OWNER",
   108  			"CAP_KILL",
   109  			"CAP_LEASE",
   110  			"CAP_LINUX_IMMUTABLE",
   111  			"CAP_NET_BIND_SERVICE",
   112  			"CAP_NET_BROADCAST",
   113  			"CAP_NET_RAW",
   114  			"CAP_SETGID",
   115  			"CAP_SETFCAP",
   116  			"CAP_SETPCAP",
   117  			"CAP_SETUID",
   118  			"CAP_SYS_ADMIN",
   119  			"CAP_SYS_CHROOT",
   120  			"CAP_SYS_NICE",
   121  			"CAP_SYS_PTRACE",
   122  			"CAP_SYS_TTY_CONFIG",
   123  			"CAP_SYS_RESOURCE",
   124  			"CAP_SYS_BOOT",
   125  			"CAP_AUDIT_WRITE",
   126  			"CAP_AUDIT_CONTROL",
   127  		}, ","),
   128  		capRemoveSet: "x",
   129  		expected: strings.Join([]string{
   130  			"chown",
   131  			"dac_override",
   132  			"dac_read_search",
   133  			"fowner",
   134  			"fsetid",
   135  			"kill",
   136  			"setgid",
   137  			"setuid",
   138  			"setpcap",
   139  			"linux_immutable",
   140  			"net_bind_service",
   141  			"net_broadcast",
   142  			"net_raw",
   143  			"ipc_owner",
   144  			"sys_chroot",
   145  			"sys_ptrace",
   146  			"sys_admin",
   147  			"sys_boot",
   148  			"sys_nice",
   149  			"sys_resource",
   150  			"sys_tty_config",
   151  			"lease",
   152  			"audit_write",
   153  			"audit_control",
   154  			"setfcap",
   155  		}, ", "),
   156  	},
   157  	// Testing remove set
   158  	{
   159  		testName:     "image-remove-one-from-default",
   160  		capRetainSet: "x",
   161  		capRemoveSet: "CAP_CHOWN",
   162  		expected: strings.Join([]string{
   163  			"dac_override, fowner, fsetid, kill, setgid, setuid, setpcap, net_bind_service, net_raw, sys_chroot, mknod, audit_write, setfcap",
   164  		}, ", "),
   165  	},
   166  	{
   167  		testName:     "image-remove-one-from-default-old",
   168  		capRetainSet: "x",
   169  		capRemoveSet: "CAP_CHOWN",
   170  		useOldFlag:   true,
   171  		expected: strings.Join([]string{
   172  			"dac_override, fowner, fsetid, kill, setgid, setuid, setpcap, net_bind_service, net_raw, sys_chroot, mknod, audit_write, setfcap",
   173  		}, ", "),
   174  	},
   175  	{
   176  		testName:     "image-remove-one-already-removed",
   177  		capRetainSet: "x",
   178  		capRemoveSet: "CAP_SYS_ADMIN",
   179  		expected: strings.Join([]string{
   180  			"chown",
   181  			"dac_override",
   182  			"fowner",
   183  			"fsetid",
   184  			"kill",
   185  			"setgid",
   186  			"setuid",
   187  			"setpcap",
   188  			"net_bind_service",
   189  			"net_raw",
   190  			"sys_chroot",
   191  			"mknod",
   192  			"audit_write",
   193  			"setfcap",
   194  		}, ", "),
   195  	},
   196  	{
   197  		testName:     "image-remove-two",
   198  		capRetainSet: "x",
   199  		capRemoveSet: "CAP_CHOWN,CAP_SYS_ADMIN",
   200  		expected: strings.Join([]string{
   201  			"dac_override",
   202  			"fowner",
   203  			"fsetid",
   204  			"kill",
   205  			"setgid",
   206  			"setuid",
   207  			"setpcap",
   208  			"net_bind_service",
   209  			"net_raw",
   210  			"sys_chroot",
   211  			"mknod",
   212  			"audit_write",
   213  			"setfcap",
   214  		}, ", "),
   215  	},
   216  	{
   217  		testName:     "image-remove-all",
   218  		capRetainSet: "x",
   219  		capRemoveSet: strings.Join([]string{
   220  			"CAP_AUDIT_WRITE",
   221  			"CAP_CHOWN",
   222  			"CAP_DAC_OVERRIDE",
   223  			"CAP_FSETID",
   224  			"CAP_FOWNER",
   225  			"CAP_KILL",
   226  			"CAP_MKNOD",
   227  			"CAP_NET_RAW",
   228  			"CAP_NET_BIND_SERVICE",
   229  			"CAP_SETUID",
   230  			"CAP_SETGID",
   231  			"CAP_SETPCAP",
   232  			"CAP_SETFCAP",
   233  			"CAP_SYS_CHROOT",
   234  		}, ","),
   235  		expected: "",
   236  	},
   237  	{
   238  		testName:     "image-remove-all-but-one",
   239  		capRetainSet: "x",
   240  		capRemoveSet: strings.Join([]string{
   241  			"CAP_AUDIT_WRITE",
   242  			"CAP_CHOWN",
   243  			"CAP_DAC_OVERRIDE",
   244  			"CAP_FSETID",
   245  			"CAP_FOWNER",
   246  			"CAP_KILL",
   247  			"CAP_MKNOD",
   248  			"CAP_NET_RAW",
   249  			"CAP_NET_BIND_SERVICE",
   250  			"CAP_SETUID",
   251  			"CAP_SETGID",
   252  			"CAP_SETPCAP",
   253  			"CAP_SETFCAP",
   254  		}, ","),
   255  		expected: "sys_chroot",
   256  	},
   257  	{
   258  		testName:     "image-remove-all-plus-one",
   259  		capRetainSet: "x",
   260  		capRemoveSet: strings.Join([]string{
   261  			"CAP_AUDIT_WRITE",
   262  			"CAP_CHOWN",
   263  			"CAP_DAC_OVERRIDE",
   264  			"CAP_FSETID",
   265  			"CAP_FOWNER",
   266  			"CAP_KILL",
   267  			"CAP_MKNOD",
   268  			"CAP_NET_RAW",
   269  			"CAP_NET_BIND_SERVICE",
   270  			"CAP_SETUID",
   271  			"CAP_SETGID",
   272  			"CAP_SETPCAP",
   273  			"CAP_SETFCAP",
   274  			"CAP_SYS_CHROOT",
   275  			"CAP_SYS_ADMIN",
   276  		}, ","),
   277  		expected: "",
   278  	},
   279  	// Testing with an empty retain set or an empty remove set
   280  	// TODO(alban): "actool patch-manifest" cannot generate those images for now
   281  	//{
   282  	//	testName:     "image-retain-set-empty",
   283  	//	capRetainSet: "",
   284  	//	capRemoveSet: "x",
   285  	//	expected:     "",
   286  	//},
   287  	//{
   288  	//	testName:     "image-remove-none",
   289  	//	capRetainSet: "x",
   290  	//	capRemoveSet: "",
   291  	//	expected:     "TODO(alban)",
   292  	//},
   293  }
   294  
   295  func capsSeveralAppsRunAndCheckOutput(t *testing.T, ctx *testutils.RktRunCtx, cmd string) {
   296  	// Ideally, the test would run the pod only one time, but all
   297  	// apps' output is mixed together without ordering guarantees, so
   298  	// it makes it impossible to call all the expectWithOutput() in
   299  	// the correct order.
   300  	for _, tt := range appCapsTests {
   301  		t.Logf("Checking caps for %q", tt.testName)
   302  		child := spawnOrFail(t, cmd)
   303  
   304  		expected := fmt.Sprintf("Capability set: bounding: %s (%s)",
   305  			tt.expected, tt.testName)
   306  		if err := expectWithOutput(child, expected); err != nil {
   307  			t.Fatalf("Expected %q but not found: %v", expected, err)
   308  		}
   309  
   310  		waitOrFail(t, child, 0)
   311  
   312  		ctx.RunGC()
   313  	}
   314  }
   315  
   316  func TestCapsSeveralAppWithPatches(t *testing.T) {
   317  	if err := common.SupportsOverlay(); err != nil {
   318  		t.Skipf("Skipping storage-intensive test, overlayfs not supported: %v", err)
   319  	}
   320  
   321  	// All the following images are launched together in the same pod
   322  
   323  	ctx := testutils.NewRktRunCtx()
   324  	defer ctx.Cleanup()
   325  
   326  	for i, tt := range appCapsTests {
   327  		patches := []string{
   328  			fmt.Sprintf("--name=%s", tt.testName),
   329  			fmt.Sprintf("--exec=/inspect --print-caps-pid=0 --suffix-msg=%s", tt.testName),
   330  		}
   331  		if tt.capRetainSet != "x" {
   332  			patches = append(patches, "--capability="+tt.capRetainSet)
   333  		}
   334  		if tt.capRemoveSet != "x" {
   335  			patches = append(patches, "--revoke-capability="+tt.capRemoveSet)
   336  		}
   337  		imageFile := patchTestACI(tt.testName+".aci", patches...)
   338  		defer os.Remove(imageFile)
   339  		appCapsTests[i].imageFile = imageFile
   340  		t.Logf("Built image %q", imageFile)
   341  	}
   342  
   343  	// Generate the rkt arguments to launch all the apps in the same pod
   344  	rktArgs := ""
   345  	for _, tt := range appCapsTests {
   346  		rktArgs += " " + tt.imageFile
   347  	}
   348  	cmd := fmt.Sprintf("%s --insecure-options=image run %s", ctx.Cmd(), rktArgs)
   349  
   350  	capsSeveralAppsRunAndCheckOutput(t, ctx, cmd)
   351  }
   352  
   353  func TestCapsSeveralAppWithFlags(t *testing.T) {
   354  	if err := common.SupportsOverlay(); err != nil {
   355  		t.Skipf("Skipping storage-intensive test, overlayfs not supported: %v", err)
   356  	}
   357  
   358  	// All the following images are launched together in the same pod
   359  	ctx := testutils.NewRktRunCtx()
   360  	defer ctx.Cleanup()
   361  
   362  	for i, tt := range appCapsTests {
   363  		imageFile := patchTestACI(tt.testName+".aci", fmt.Sprintf("--name=%s", tt.testName),
   364  			fmt.Sprintf("--exec=/inspect --print-caps-pid=0 --suffix-msg=%s", tt.testName))
   365  		defer os.Remove(imageFile)
   366  		appCapsTests[i].imageFile = imageFile
   367  		t.Logf("Built image %q", imageFile)
   368  	}
   369  
   370  	// Generate the rkt arguments to launch all the apps in the same pod
   371  	rktArgs := ""
   372  	for _, tt := range appCapsTests {
   373  		rktArgs += " " + tt.imageFile
   374  		if tt.capRetainSet != "x" {
   375  			capFlag := " --caps-retain"
   376  			if tt.useOldFlag {
   377  				capFlag = " --cap-retain"
   378  			}
   379  			rktArgs += fmt.Sprintf("%s=%s", capFlag, tt.capRetainSet)
   380  		}
   381  		if tt.capRemoveSet != "x" {
   382  			capFlag := " --caps-remove"
   383  			if tt.useOldFlag {
   384  				capFlag = " --cap-remove"
   385  			}
   386  			rktArgs += fmt.Sprintf("%s=%s", capFlag, tt.capRemoveSet)
   387  		}
   388  	}
   389  	cmd := fmt.Sprintf("%s --insecure-options=image run %s", ctx.Cmd(), rktArgs)
   390  
   391  	capsSeveralAppsRunAndCheckOutput(t, ctx, cmd)
   392  }
   393  
   394  // Tests that flags on the command line override the isolator in the ACI
   395  func TestCapsOverride(t *testing.T) {
   396  	if err := common.SupportsOverlay(); err != nil {
   397  		t.Skipf("Skipping storage-intensive test, overlayfs not supported: %v", err)
   398  	}
   399  
   400  	ctx := testutils.NewRktRunCtx()
   401  	defer ctx.Cleanup()
   402  
   403  	var appCapsOverride = []struct {
   404  		testName        string // name of the image
   405  		capRetainSetImg string // only use value if != "x"
   406  		capRemoveSetImg string // only use value if != "x"
   407  		capRetainSetFlg string // only use value if != "x"
   408  		capRemoveSetFlg string // only use value if != "x"
   409  		expected        string // caps bounding set as printed by gocapability
   410  	}{
   411  		{
   412  			testName:        "retain-override-retain",
   413  			capRetainSetImg: "CAP_MKNOD",
   414  			capRemoveSetImg: "x",
   415  			capRetainSetFlg: "CAP_SYS_ADMIN",
   416  			capRemoveSetFlg: "x",
   417  			expected: strings.Join([]string{
   418  				"sys_admin",
   419  			}, ", "),
   420  		},
   421  		{
   422  			testName:        "retain-override-remove",
   423  			capRetainSetImg: "x",
   424  			capRemoveSetImg: "CAP_CHOWN",
   425  			capRetainSetFlg: "CAP_SYS_ADMIN",
   426  			capRemoveSetFlg: "x",
   427  			expected: strings.Join([]string{
   428  				"sys_admin",
   429  			}, ", "),
   430  		},
   431  		{
   432  			testName:        "remove-override-remove",
   433  			capRetainSetImg: "x",
   434  			capRemoveSetImg: "CAP_CHOWN",
   435  			capRetainSetFlg: "x",
   436  			capRemoveSetFlg: "CAP_KILL",
   437  			expected: strings.Join([]string{
   438  				"chown",
   439  				"dac_override",
   440  				"fowner",
   441  				"fsetid",
   442  				"setgid",
   443  				"setuid",
   444  				"setpcap",
   445  				"net_bind_service",
   446  				"net_raw",
   447  				"sys_chroot",
   448  				"mknod",
   449  				"audit_write",
   450  				"setfcap",
   451  			}, ", "),
   452  		},
   453  		{
   454  			testName:        "remove-override-retain",
   455  			capRetainSetImg: "CAP_MKNOD",
   456  			capRemoveSetImg: "x",
   457  			capRetainSetFlg: "x",
   458  			capRemoveSetFlg: "CAP_KILL",
   459  			expected: strings.Join([]string{
   460  				"chown",
   461  				"dac_override",
   462  				"fowner",
   463  				"fsetid",
   464  				"setgid",
   465  				"setuid",
   466  				"setpcap",
   467  				"net_bind_service",
   468  				"net_raw",
   469  				"sys_chroot",
   470  				"mknod",
   471  				"audit_write",
   472  				"setfcap",
   473  			}, ", "),
   474  		},
   475  	}
   476  
   477  	for _, tt := range appCapsOverride {
   478  		patches := []string{
   479  			fmt.Sprintf("--name=%s", tt.testName),
   480  			fmt.Sprintf("--exec=/inspect --print-caps-pid=0 --suffix-msg=%s", tt.testName),
   481  		}
   482  		if tt.capRetainSetImg != "x" {
   483  			patches = append(patches, "--capability="+tt.capRetainSetImg)
   484  		}
   485  		if tt.capRemoveSetImg != "x" {
   486  			patches = append(patches, "--revoke-capability="+tt.capRemoveSetImg)
   487  		}
   488  		imageFile := patchTestACI(tt.testName+".aci", patches...)
   489  		defer os.Remove(imageFile)
   490  		t.Logf("Built image %q", imageFile)
   491  
   492  		cmd := fmt.Sprintf("%s --insecure-options=image run %s", ctx.Cmd(), imageFile)
   493  		if tt.capRetainSetFlg != "x" {
   494  			cmd += " --cap-retain=" + tt.capRetainSetFlg
   495  		}
   496  		if tt.capRemoveSetFlg != "x" {
   497  			cmd += " --cap-remove=" + tt.capRemoveSetFlg
   498  		}
   499  
   500  		t.Logf("Checking caps for %q", tt.testName)
   501  		child := spawnOrFail(t, cmd)
   502  
   503  		expected := fmt.Sprintf("Capability set: bounding: %s (%s)",
   504  			tt.expected, tt.testName)
   505  		if err := expectWithOutput(child, expected); err != nil {
   506  			t.Fatalf("Expected %q but not found: %v", expected, err)
   507  		}
   508  
   509  		waitOrFail(t, child, 0)
   510  
   511  		ctx.RunGC()
   512  	}
   513  }
   514  
   515  // Tests that --insecure-options=capabilities allows everything
   516  func TestCapsInsecureOption(t *testing.T) {
   517  	if err := common.SupportsOverlay(); err != nil {
   518  		t.Skipf("Skipping storage-intensive test, overlayfs not supported: %v", err)
   519  	}
   520  
   521  	ctx := testutils.NewRktRunCtx()
   522  	defer ctx.Cleanup()
   523  
   524  	imageFile := patchTestACI("insecure-options-caps.aci", "--exec=/inspect --print-caps-pid=0")
   525  	defer os.Remove(imageFile)
   526  
   527  	defaultCaps := strings.Join([]string{
   528  		"chown",
   529  		"dac_override",
   530  		"fowner",
   531  		"fsetid",
   532  		"kill",
   533  		"setgid",
   534  		"setuid",
   535  		"setpcap",
   536  		"net_bind_service",
   537  		"net_raw",
   538  		"sys_chroot",
   539  		"mknod",
   540  		"audit_write",
   541  		"setfcap",
   542  	}, ", ")
   543  
   544  	// All capabilities defined in Linux, in the same order as their definition:
   545  	// https://github.com/torvalds/linux/blob/v4.7/include/uapi/linux/capability.h#L90
   546  	allCaps := strings.Join([]string{
   547  		"chown",
   548  		"dac_override",
   549  		"dac_read_search",
   550  		"fowner",
   551  		"fsetid",
   552  		"kill",
   553  		"setgid",
   554  		"setuid",
   555  		"setpcap",
   556  		"linux_immutable",
   557  		"net_bind_service",
   558  		"net_broadcast",
   559  		"net_admin",
   560  		"net_raw",
   561  		"ipc_lock",
   562  		"ipc_owner",
   563  		"sys_module",
   564  		"sys_rawio",
   565  		"sys_chroot",
   566  		"sys_ptrace",
   567  		"sys_pacct",
   568  		"sys_admin",
   569  		"sys_boot",
   570  		"sys_nice",
   571  		"sys_resource",
   572  		"sys_time",
   573  		"sys_tty_config",
   574  		"mknod",
   575  		"lease",
   576  		"audit_write",
   577  		"audit_control",
   578  		"setfcap",
   579  		"mac_override",
   580  		"mac_admin",
   581  		"syslog",
   582  		"wake_alarm",
   583  		"block_suspend",
   584  		// do not check "audit_read" to support tests in older kernels
   585  		// "audit_read", // since Linux 3.16
   586  	}, ", ")
   587  
   588  	// Without --insecure-options=capabilities
   589  	expectedDefault := fmt.Sprintf("Capability set: bounding: %s", defaultCaps)
   590  	for _, insecureOption := range []string{"image", "image,ondisk,paths", "all-fetch"} {
   591  		// run
   592  		t.Logf("run: check caps with --insecure-options=%s\n", insecureOption)
   593  		cmd := fmt.Sprintf(`%s --debug --insecure-options=%s run %s`, ctx.Cmd(), insecureOption, imageFile)
   594  		runRktAndCheckOutput(t, cmd, expectedDefault, false)
   595  		// run-prepared
   596  		t.Logf("run-prepared: check caps with --insecure-options=%s\n", insecureOption)
   597  		cmd = fmt.Sprintf(`%s --insecure-options=%s prepare %s`, ctx.Cmd(), insecureOption, imageFile)
   598  		uuid := runRktAndGetUUID(t, cmd)
   599  		cmd = fmt.Sprintf("%s --insecure-options=%s --debug run-prepared %s", ctx.Cmd(), insecureOption, uuid)
   600  		runRktAndCheckOutput(t, cmd, expectedDefault, false)
   601  	}
   602  
   603  	// With --insecure-options=capabilities
   604  	expectedAll := fmt.Sprintf("Capability set: bounding: %s", allCaps)
   605  	for _, insecureOption := range []string{"image,capabilities", "image,ondisk,paths,capabilities", "all-fetch,capabilities", "all"} {
   606  		// run
   607  		t.Logf("run: check caps with --insecure-options=%s\n", insecureOption)
   608  		cmd := fmt.Sprintf(`%s --debug --insecure-options=%s run %s`, ctx.Cmd(), insecureOption, imageFile)
   609  		runRktAndCheckOutput(t, cmd, expectedAll, false)
   610  		// run-prepared
   611  		t.Logf("run-prepared: check caps with --insecure-options=%s\n", insecureOption)
   612  		cmd = fmt.Sprintf(`%s --insecure-options=%s prepare %s`, ctx.Cmd(), insecureOption, imageFile)
   613  		uuid := runRktAndGetUUID(t, cmd)
   614  		cmd = fmt.Sprintf("%s --insecure-options=%s --debug run-prepared %s", ctx.Cmd(), insecureOption, uuid)
   615  		runRktAndCheckOutput(t, cmd, expectedAll, false)
   616  	}
   617  }
   618  
   619  var capsTests = []struct {
   620  	testName            string
   621  	capIsolator         string
   622  	capa                capability.Cap
   623  	capInStage1Expected bool
   624  	capInStage2Expected bool
   625  	nonrootCapExpected  bool
   626  }{
   627  	{
   628  		testName:            "Check we don't have CAP_NET_ADMIN without isolator",
   629  		capIsolator:         "",
   630  		capa:                capability.CAP_NET_ADMIN,
   631  		capInStage1Expected: false,
   632  		capInStage2Expected: false,
   633  		nonrootCapExpected:  false,
   634  	},
   635  	{
   636  		testName:            "Check we have CAP_MKNOD without isolator",
   637  		capIsolator:         "",
   638  		capa:                capability.CAP_MKNOD,
   639  		capInStage1Expected: true,
   640  		capInStage2Expected: true,
   641  		nonrootCapExpected:  true,
   642  	},
   643  	{
   644  		testName:            "Check we have CAP_NET_ADMIN with an isolator",
   645  		capIsolator:         "CAP_NET_ADMIN,CAP_NET_BIND_SERVICE",
   646  		capa:                capability.CAP_NET_ADMIN,
   647  		capInStage1Expected: true,
   648  		capInStage2Expected: true,
   649  		nonrootCapExpected:  true,
   650  	},
   651  	{
   652  		testName:            "Check we have CAP_NET_BIND_SERVICE with an isolator",
   653  		capIsolator:         "CAP_NET_ADMIN,CAP_NET_BIND_SERVICE",
   654  		capa:                capability.CAP_NET_BIND_SERVICE,
   655  		capInStage1Expected: true,
   656  		capInStage2Expected: true,
   657  		nonrootCapExpected:  true,
   658  	},
   659  	{
   660  		testName:            "Check we don't have CAP_NET_ADMIN with an isolator setting CAP_NET_BIND_SERVICE",
   661  		capIsolator:         "CAP_NET_BIND_SERVICE",
   662  		capa:                capability.CAP_NET_ADMIN,
   663  		capInStage1Expected: false,
   664  		capInStage2Expected: false,
   665  		nonrootCapExpected:  false,
   666  	},
   667  }
   668  
   669  func NewCapsTest(hasStage1FullCaps bool) testutils.Test {
   670  	return testutils.TestFunc(func(t *testing.T) {
   671  		ctx := testutils.NewRktRunCtx()
   672  		defer ctx.Cleanup()
   673  
   674  		for i, tt := range capsTests {
   675  			stage1Args := []string{"--exec=/inspect --print-caps-pid=1 --print-user"}
   676  			stage2Args := []string{"--exec=/inspect --print-caps-pid=0 --print-user"}
   677  			if tt.capIsolator != "" {
   678  				stage1Args = append(stage1Args, "--capability="+tt.capIsolator)
   679  				stage2Args = append(stage2Args, "--capability="+tt.capIsolator)
   680  			}
   681  			stage1FileName := patchTestACI("rkt-inspect-print-caps-stage1.aci", stage1Args...)
   682  			defer os.Remove(stage1FileName)
   683  			stage2FileName := patchTestACI("rkt-inspect-print-caps-stage2.aci", stage2Args...)
   684  			defer os.Remove(stage2FileName)
   685  			stageFileNames := []string{stage1FileName, stage2FileName}
   686  
   687  			for _, stage := range []int{1, 2} {
   688  				t.Logf("Running test #%v: %v [stage%v]", i, tt.testName, stage)
   689  
   690  				cmd := fmt.Sprintf("%s --debug --insecure-options=image run --mds-register=false --set-env=CAPABILITY=%d %s", ctx.Cmd(), int(tt.capa), stageFileNames[stage-1])
   691  				child := spawnOrFail(t, cmd)
   692  
   693  				expectedLine := tt.capa.String()
   694  
   695  				capInStage1Expected := tt.capInStage1Expected || hasStage1FullCaps
   696  
   697  				if (stage == 1 && capInStage1Expected) || (stage == 2 && tt.capInStage2Expected) {
   698  					expectedLine += "=enabled"
   699  				} else {
   700  					expectedLine += "=disabled"
   701  				}
   702  
   703  				if err := expectWithOutput(child, expectedLine); err != nil {
   704  					t.Fatalf("Expected %q but not found: %v", expectedLine, err)
   705  				}
   706  
   707  				if err := expectWithOutput(child, "User: uid=0 euid=0 gid=0 egid=0"); err != nil {
   708  					t.Fatalf("Expected user 0 but not found: %v", err)
   709  				}
   710  
   711  				waitOrFail(t, child, 0)
   712  			}
   713  			ctx.Reset()
   714  		}
   715  	})
   716  }
   717  
   718  func TestCapsNonRoot(t *testing.T) {
   719  	if err := common.SupportsOverlay(); err != nil {
   720  		t.Skipf("Skipping storage-intensive test, overlayfs not supported: %v", err)
   721  	}
   722  
   723  	ctx := testutils.NewRktRunCtx()
   724  	defer ctx.Cleanup()
   725  
   726  	for i, tt := range capsTests {
   727  		args := []string{"--exec=/inspect --print-caps-pid=0 --print-user", "--user=9000", "--group=9000"}
   728  		if tt.capIsolator != "" {
   729  			args = append(args, "--capability="+tt.capIsolator)
   730  		}
   731  		fileName := patchTestACI("rkt-inspect-print-caps-nonroot.aci", args...)
   732  		defer os.Remove(fileName)
   733  
   734  		t.Logf("Running test #%v: %v [non-root]", i, tt.testName)
   735  
   736  		cmd := fmt.Sprintf("%s --debug --insecure-options=image run --mds-register=false --set-env=CAPABILITY=%d %s", ctx.Cmd(), int(tt.capa), fileName)
   737  		child := spawnOrFail(t, cmd)
   738  
   739  		expectedLine := tt.capa.String()
   740  		if tt.nonrootCapExpected {
   741  			expectedLine += "=enabled"
   742  		} else {
   743  			expectedLine += "=disabled"
   744  		}
   745  		if err := expectWithOutput(child, expectedLine); err != nil {
   746  			t.Fatalf("Expected %q but not found: %v", expectedLine, err)
   747  		}
   748  
   749  		if err := expectWithOutput(child, "User: uid=9000 euid=9000 gid=9000 egid=9000"); err != nil {
   750  			t.Fatalf("Expected user 9000 but not found: %v", err)
   751  		}
   752  
   753  		waitOrFail(t, child, 0)
   754  		ctx.Reset()
   755  	}
   756  }