github.com/tri-adam/singularity@v3.1.1+incompatible/cmd/singularity/security_test.go (about)

     1  // Copyright (c) 2018, Sylabs Inc. All rights reserved.
     2  // This software is licensed under a 3-clause BSD license. Please consult the
     3  // LICENSE.md file distributed with the sources of this project regarding your
     4  // rights to use or distribute this software.
     5  
     6  // +build seccomp
     7  
     8  package main
     9  
    10  import (
    11  	"os"
    12  	"strings"
    13  	"testing"
    14  
    15  	"github.com/sylabs/singularity/internal/pkg/buildcfg"
    16  	"github.com/sylabs/singularity/internal/pkg/test"
    17  )
    18  
    19  // testSecurityUnpriv tests security flag fuctionality for singularity exec without elevated privileges
    20  func testSecurityUnpriv(t *testing.T) {
    21  	tests := []struct {
    22  		name   string
    23  		image  string
    24  		action string
    25  		argv   []string
    26  		opts
    27  		exit          int
    28  		expectSuccess bool
    29  	}{
    30  		// taget UID/GID
    31  		{"Set_uid", imagePath, "exec", []string{"id", "-u", "|", "grep", "99"}, opts{security: []string{"uid:99"}}, 1, false},
    32  		{"Set_gid", imagePath, "exec", []string{"id", "-g", "|", "grep", "99"}, opts{security: []string{"gid:99"}}, 1, false},
    33  		// seccomp from json file
    34  		{"SecComp_BlackList", imagePath, "exec", []string{"mkdir", "/tmp/foo"}, opts{security: []string{"seccomp:./testdata/seccomp-profile.json"}}, 1, false},
    35  		{"SecComp_true", imagePath, "exec", []string{"true"}, opts{security: []string{"seccomp:./testdata/seccomp-profile.json"}}, 0, true},
    36  		// capabilities
    37  		{"capabilities_keep_true", imagePath, "exec", []string{"ping", "-c", "1", "8.8.8.8"}, opts{keepPrivs: true}, 1, false},
    38  		{"capabilities_keep-false", imagePath, "exec", []string{"ping", "-c", "1", "8.8.8.8"}, opts{keepPrivs: false}, 1, false},
    39  		{"capabilities_drop", imagePath, "exec", []string{"ping", "-c", "1", "8.8.8.8"}, opts{dropCaps: "CAP_NET_RAW"}, 1, false},
    40  	}
    41  
    42  	for _, tt := range tests {
    43  		t.Run("unpriv "+tt.name, test.WithoutPrivilege(func(t *testing.T) {
    44  			stdout, stderr, exitCode, err := imageExec(t, tt.action, tt.opts, tt.image, tt.argv)
    45  			if tt.expectSuccess && (exitCode != 0) {
    46  				t.Log(stdout, stderr, exitCode)
    47  				t.Fatalf("unexpected failure running '%v': %v", strings.Join(tt.argv, " "), err)
    48  			} else if !tt.expectSuccess && (exitCode != 1) {
    49  				t.Log(stdout, stderr, exitCode)
    50  				t.Fatalf("unexpected success running '%v'", strings.Join(tt.argv, " "))
    51  			}
    52  		}))
    53  	}
    54  }
    55  
    56  // testSecurityPriv tests security flag fuctionality for singularity exec with elevated privileges
    57  func testSecurityPriv(t *testing.T) {
    58  	tests := []struct {
    59  		name   string
    60  		image  string
    61  		action string
    62  		argv   []string
    63  		opts
    64  		exit          int
    65  		expectSuccess bool
    66  	}{
    67  		// taget UID/GID
    68  		{"Set_uid", imagePath, "exec", []string{"id", "-u", "|", "grep", "99"}, opts{security: []string{"uid:99"}}, 1, false},
    69  		{"Set_gid", imagePath, "exec", []string{"id", "-g", "|", "grep", "99"}, opts{security: []string{"gid:99"}}, 1, false},
    70  		// seccomp from json file
    71  		{"SecComp_BlackList", imagePath, "exec", []string{"mkdir", "/tmp/foo"}, opts{security: []string{"seccomp:./testdata/seccomp-profile.json"}}, 1, false},
    72  		{"SecComp_true", imagePath, "exec", []string{"true"}, opts{security: []string{"seccomp:./testdata/seccomp-profile.json"}}, 0, true},
    73  		// capabilities
    74  		{"capabilities_keep", imagePath, "exec", []string{"ping", "-c", "1", "8.8.8.8"}, opts{keepPrivs: true}, 0, true},
    75  		{"capabilities_drop", imagePath, "exec", []string{"ping", "-c", "1", "8.8.8.8"}, opts{dropCaps: "CAP_NET_RAW"}, 1, false},
    76  	}
    77  
    78  	for _, tt := range tests {
    79  		t.Run("priv "+tt.name, test.WithPrivilege(func(t *testing.T) {
    80  			stdout, stderr, exitCode, err := imageExec(t, tt.action, tt.opts, tt.image, tt.argv)
    81  			if tt.expectSuccess && (exitCode != 0) {
    82  				t.Log(stdout, stderr, exitCode)
    83  				t.Fatalf("unexpected failure running '%v': %v", strings.Join(tt.argv, " "), err)
    84  			} else if !tt.expectSuccess && (exitCode != 1) {
    85  				t.Log(stdout, stderr, exitCode)
    86  				t.Fatalf("unexpected success running '%v'", strings.Join(tt.argv, " "))
    87  			}
    88  		}))
    89  	}
    90  }
    91  
    92  // testSecurityConfOwnership tests checks on config files ownerships
    93  func testSecurityConfOwnership(t *testing.T) {
    94  	configFile := buildcfg.SYSCONFDIR + "/singularity/singularity.conf"
    95  	// Change file ownership (do not try this at home)
    96  	err := os.Chown(configFile, 1001, 0)
    97  	if err != nil {
    98  		t.Fatal(err)
    99  	}
   100  
   101  	// try to run
   102  	t.Run("non_root_config", test.WithoutPrivilege(func(t *testing.T) {
   103  		_, stderr, exitCode, err := imageExec(t, "exec", opts{}, imagePath, []string{"/bin/true"})
   104  		if exitCode != 1 {
   105  			t.Log(stderr, err)
   106  			t.Fatalf("unexpected success running /bin/true")
   107  		}
   108  	}))
   109  
   110  	// return file ownership to normal
   111  	err = os.Chown(configFile, 0, 0)
   112  	if err != nil {
   113  		t.Fatal(err)
   114  	}
   115  }
   116  
   117  func TestSecurity(t *testing.T) {
   118  	test.EnsurePrivilege(t)
   119  	opts := buildOpts{
   120  		force:   true,
   121  		sandbox: false,
   122  	}
   123  	if b, err := imageBuild(opts, imagePath, "../../examples/busybox/Singularity"); err != nil {
   124  		t.Log(string(b))
   125  		t.Fatalf("unexpected failure: %v", err)
   126  	}
   127  	defer os.Remove(imagePath)
   128  
   129  	// Security
   130  	t.Run("Security_unpriv", testSecurityPriv)
   131  	t.Run("Security_priv", testSecurityUnpriv)
   132  	t.Run("Security_config_ownerships", testSecurityConfOwnership)
   133  
   134  }