...

Source file src/github.com/opencontainers/runc/libcontainer/configs/validate/validator_test.go

Documentation: github.com/opencontainers/runc/libcontainer/configs/validate

     1  package validate
     2  
     3  import (
     4  	"os"
     5  	"path/filepath"
     6  	"testing"
     7  
     8  	"github.com/opencontainers/runc/libcontainer/configs"
     9  	"golang.org/x/sys/unix"
    10  )
    11  
    12  func TestValidate(t *testing.T) {
    13  	config := &configs.Config{
    14  		Rootfs: "/var",
    15  	}
    16  
    17  	validator := New()
    18  	err := validator.Validate(config)
    19  	if err != nil {
    20  		t.Errorf("Expected error to not occur: %+v", err)
    21  	}
    22  }
    23  
    24  func TestValidateWithInvalidRootfs(t *testing.T) {
    25  	dir := "rootfs"
    26  	if err := os.Symlink("/var", dir); err != nil {
    27  		t.Fatal(err)
    28  	}
    29  	defer os.Remove(dir)
    30  
    31  	config := &configs.Config{
    32  		Rootfs: dir,
    33  	}
    34  
    35  	validator := New()
    36  	err := validator.Validate(config)
    37  	if err == nil {
    38  		t.Error("Expected error to occur but it was nil")
    39  	}
    40  }
    41  
    42  func TestValidateNetworkWithoutNETNamespace(t *testing.T) {
    43  	network := &configs.Network{Type: "loopback"}
    44  	config := &configs.Config{
    45  		Rootfs:     "/var",
    46  		Namespaces: []configs.Namespace{},
    47  		Networks:   []*configs.Network{network},
    48  	}
    49  
    50  	validator := New()
    51  	err := validator.Validate(config)
    52  	if err == nil {
    53  		t.Error("Expected error to occur but it was nil")
    54  	}
    55  }
    56  
    57  func TestValidateNetworkRoutesWithoutNETNamespace(t *testing.T) {
    58  	route := &configs.Route{Gateway: "255.255.255.0"}
    59  	config := &configs.Config{
    60  		Rootfs:     "/var",
    61  		Namespaces: []configs.Namespace{},
    62  		Routes:     []*configs.Route{route},
    63  	}
    64  
    65  	validator := New()
    66  	err := validator.Validate(config)
    67  	if err == nil {
    68  		t.Error("Expected error to occur but it was nil")
    69  	}
    70  }
    71  
    72  func TestValidateHostname(t *testing.T) {
    73  	config := &configs.Config{
    74  		Rootfs:   "/var",
    75  		Hostname: "runc",
    76  		Namespaces: configs.Namespaces(
    77  			[]configs.Namespace{
    78  				{Type: configs.NEWUTS},
    79  			},
    80  		),
    81  	}
    82  
    83  	validator := New()
    84  	err := validator.Validate(config)
    85  	if err != nil {
    86  		t.Errorf("Expected error to not occur: %+v", err)
    87  	}
    88  }
    89  
    90  func TestValidateHostnameWithoutUTSNamespace(t *testing.T) {
    91  	config := &configs.Config{
    92  		Rootfs:   "/var",
    93  		Hostname: "runc",
    94  	}
    95  
    96  	validator := New()
    97  	err := validator.Validate(config)
    98  	if err == nil {
    99  		t.Error("Expected error to occur but it was nil")
   100  	}
   101  }
   102  
   103  func TestValidateSecurityWithMaskPaths(t *testing.T) {
   104  	config := &configs.Config{
   105  		Rootfs:    "/var",
   106  		MaskPaths: []string{"/proc/kcore"},
   107  		Namespaces: configs.Namespaces(
   108  			[]configs.Namespace{
   109  				{Type: configs.NEWNS},
   110  			},
   111  		),
   112  	}
   113  
   114  	validator := New()
   115  	err := validator.Validate(config)
   116  	if err != nil {
   117  		t.Errorf("Expected error to not occur: %+v", err)
   118  	}
   119  }
   120  
   121  func TestValidateSecurityWithROPaths(t *testing.T) {
   122  	config := &configs.Config{
   123  		Rootfs:        "/var",
   124  		ReadonlyPaths: []string{"/proc/sys"},
   125  		Namespaces: configs.Namespaces(
   126  			[]configs.Namespace{
   127  				{Type: configs.NEWNS},
   128  			},
   129  		),
   130  	}
   131  
   132  	validator := New()
   133  	err := validator.Validate(config)
   134  	if err != nil {
   135  		t.Errorf("Expected error to not occur: %+v", err)
   136  	}
   137  }
   138  
   139  func TestValidateSecurityWithoutNEWNS(t *testing.T) {
   140  	config := &configs.Config{
   141  		Rootfs:        "/var",
   142  		MaskPaths:     []string{"/proc/kcore"},
   143  		ReadonlyPaths: []string{"/proc/sys"},
   144  	}
   145  
   146  	validator := New()
   147  	err := validator.Validate(config)
   148  	if err == nil {
   149  		t.Error("Expected error to occur but it was nil")
   150  	}
   151  }
   152  
   153  func TestValidateUserNamespace(t *testing.T) {
   154  	if _, err := os.Stat("/proc/self/ns/user"); os.IsNotExist(err) {
   155  		t.Skip("Test requires userns.")
   156  	}
   157  	config := &configs.Config{
   158  		Rootfs: "/var",
   159  		Namespaces: configs.Namespaces(
   160  			[]configs.Namespace{
   161  				{Type: configs.NEWUSER},
   162  			},
   163  		),
   164  		UidMappings: []configs.IDMap{{HostID: 0, ContainerID: 123, Size: 100}},
   165  		GidMappings: []configs.IDMap{{HostID: 0, ContainerID: 123, Size: 100}},
   166  	}
   167  
   168  	validator := New()
   169  	err := validator.Validate(config)
   170  	if err != nil {
   171  		t.Errorf("expected error to not occur %+v", err)
   172  	}
   173  }
   174  
   175  func TestValidateUsernsMappingWithoutNamespace(t *testing.T) {
   176  	config := &configs.Config{
   177  		Rootfs:      "/var",
   178  		UidMappings: []configs.IDMap{{HostID: 0, ContainerID: 123, Size: 100}},
   179  		GidMappings: []configs.IDMap{{HostID: 0, ContainerID: 123, Size: 100}},
   180  	}
   181  
   182  	validator := New()
   183  	err := validator.Validate(config)
   184  	if err == nil {
   185  		t.Error("Expected error to occur but it was nil")
   186  	}
   187  }
   188  
   189  // TestConvertSysctlVariableToDotsSeparator tests whether the sysctl variable
   190  // can be correctly converted to a dot as a separator.
   191  func TestConvertSysctlVariableToDotsSeparator(t *testing.T) {
   192  	type testCase struct {
   193  		in  string
   194  		out string
   195  	}
   196  	valid := []testCase{
   197  		{in: "kernel.shm_rmid_forced", out: "kernel.shm_rmid_forced"},
   198  		{in: "kernel/shm_rmid_forced", out: "kernel.shm_rmid_forced"},
   199  		{in: "net.ipv4.conf.eno2/100.rp_filter", out: "net.ipv4.conf.eno2/100.rp_filter"},
   200  		{in: "net/ipv4/conf/eno2.100/rp_filter", out: "net.ipv4.conf.eno2/100.rp_filter"},
   201  		{in: "net/ipv4/ip_local_port_range", out: "net.ipv4.ip_local_port_range"},
   202  		{in: "kernel/msgmax", out: "kernel.msgmax"},
   203  		{in: "kernel/sem", out: "kernel.sem"},
   204  	}
   205  
   206  	for _, test := range valid {
   207  		convertSysctlVal := convertSysctlVariableToDotsSeparator(test.in)
   208  		if convertSysctlVal != test.out {
   209  			t.Errorf("The sysctl variable was not converted correctly. got: %s, want: %s", convertSysctlVal, test.out)
   210  		}
   211  	}
   212  }
   213  
   214  func TestValidateSysctl(t *testing.T) {
   215  	sysctl := map[string]string{
   216  		"fs.mqueue.ctl":                    "ctl",
   217  		"fs/mqueue/ctl":                    "ctl",
   218  		"net.ctl":                          "ctl",
   219  		"net/ctl":                          "ctl",
   220  		"net.ipv4.conf.eno2/100.rp_filter": "ctl",
   221  		"kernel.ctl":                       "ctl",
   222  		"kernel/ctl":                       "ctl",
   223  	}
   224  
   225  	for k, v := range sysctl {
   226  		config := &configs.Config{
   227  			Rootfs: "/var",
   228  			Sysctl: map[string]string{k: v},
   229  		}
   230  
   231  		validator := New()
   232  		err := validator.Validate(config)
   233  		if err == nil {
   234  			t.Error("Expected error to occur but it was nil")
   235  		}
   236  	}
   237  }
   238  
   239  func TestValidateValidSysctl(t *testing.T) {
   240  	sysctl := map[string]string{
   241  		"fs.mqueue.ctl":                    "ctl",
   242  		"fs/mqueue/ctl":                    "ctl",
   243  		"net.ctl":                          "ctl",
   244  		"net/ctl":                          "ctl",
   245  		"net.ipv4.conf.eno2/100.rp_filter": "ctl",
   246  		"kernel.msgmax":                    "ctl",
   247  		"kernel/msgmax":                    "ctl",
   248  	}
   249  
   250  	for k, v := range sysctl {
   251  		config := &configs.Config{
   252  			Rootfs: "/var",
   253  			Sysctl: map[string]string{k: v},
   254  			Namespaces: []configs.Namespace{
   255  				{
   256  					Type: configs.NEWNET,
   257  				},
   258  				{
   259  					Type: configs.NEWIPC,
   260  				},
   261  			},
   262  		}
   263  
   264  		validator := New()
   265  		err := validator.Validate(config)
   266  		if err != nil {
   267  			t.Errorf("Expected error to not occur with {%s=%s} but got: %q", k, v, err)
   268  		}
   269  	}
   270  }
   271  
   272  func TestValidateSysctlWithSameNs(t *testing.T) {
   273  	config := &configs.Config{
   274  		Rootfs: "/var",
   275  		Sysctl: map[string]string{"net.ctl": "ctl"},
   276  		Namespaces: configs.Namespaces(
   277  			[]configs.Namespace{
   278  				{
   279  					Type: configs.NEWNET,
   280  					Path: "/proc/self/ns/net",
   281  				},
   282  			},
   283  		),
   284  	}
   285  
   286  	validator := New()
   287  	err := validator.Validate(config)
   288  	if err == nil {
   289  		t.Error("Expected error to occur but it was nil")
   290  	}
   291  }
   292  
   293  func TestValidateSysctlWithBindHostNetNS(t *testing.T) {
   294  	if os.Getuid() != 0 {
   295  		t.Skip("requires root")
   296  	}
   297  
   298  	const selfnet = "/proc/self/ns/net"
   299  
   300  	file := filepath.Join(t.TempDir(), "default")
   301  	fd, err := os.Create(file)
   302  	if err != nil {
   303  		t.Fatal(err)
   304  	}
   305  	defer os.Remove(file)
   306  	fd.Close()
   307  
   308  	if err := unix.Mount(selfnet, file, "bind", unix.MS_BIND, ""); err != nil {
   309  		t.Fatalf("can't bind-mount %s to %s: %s", selfnet, file, err)
   310  	}
   311  	defer func() {
   312  		_ = unix.Unmount(file, unix.MNT_DETACH)
   313  	}()
   314  
   315  	config := &configs.Config{
   316  		Rootfs: "/var",
   317  		Sysctl: map[string]string{"net.ctl": "ctl", "net.foo": "bar"},
   318  		Namespaces: configs.Namespaces(
   319  			[]configs.Namespace{
   320  				{
   321  					Type: configs.NEWNET,
   322  					Path: file,
   323  				},
   324  			},
   325  		),
   326  	}
   327  
   328  	validator := New()
   329  	if err := validator.Validate(config); err == nil {
   330  		t.Error("Expected error to occur but it was nil")
   331  	}
   332  }
   333  
   334  func TestValidateSysctlWithoutNETNamespace(t *testing.T) {
   335  	config := &configs.Config{
   336  		Rootfs:     "/var",
   337  		Sysctl:     map[string]string{"net.ctl": "ctl"},
   338  		Namespaces: []configs.Namespace{},
   339  	}
   340  
   341  	validator := New()
   342  	err := validator.Validate(config)
   343  	if err == nil {
   344  		t.Error("Expected error to occur but it was nil")
   345  	}
   346  }
   347  
   348  func TestValidateMounts(t *testing.T) {
   349  	testCases := []struct {
   350  		isErr bool
   351  		dest  string
   352  	}{
   353  		// TODO (runc v1.x.x): make these relative paths an error. See https://github.com/opencontainers/runc/pull/3004
   354  		{isErr: false, dest: "not/an/abs/path"},
   355  		{isErr: false, dest: "./rel/path"},
   356  		{isErr: false, dest: "./rel/path"},
   357  		{isErr: false, dest: "../../path"},
   358  
   359  		{isErr: false, dest: "/abs/path"},
   360  		{isErr: false, dest: "/abs/but/../unclean"},
   361  	}
   362  
   363  	validator := New()
   364  
   365  	for _, tc := range testCases {
   366  		config := &configs.Config{
   367  			Rootfs: "/var",
   368  			Mounts: []*configs.Mount{
   369  				{Destination: tc.dest},
   370  			},
   371  		}
   372  
   373  		err := validator.Validate(config)
   374  		if tc.isErr && err == nil {
   375  			t.Errorf("mount dest: %s, expected error, got nil", tc.dest)
   376  		}
   377  		if !tc.isErr && err != nil {
   378  			t.Errorf("mount dest: %s, expected nil, got error %v", tc.dest, err)
   379  		}
   380  	}
   381  }
   382  

View as plain text