mirror of
				https://github.com/moby/moby.git
				synced 2022-11-09 12:21:53 -05:00 
			
		
		
		
	inital seccomp support
Signed-off-by: Jessica Frazelle <acidburn@docker.com>
This commit is contained in:
		
							parent
							
								
									255004ef33
								
							
						
					
					
						commit
						6707f4b9b6
					
				
					 8 changed files with 173 additions and 2 deletions
				
			
		| 
						 | 
				
			
			@ -51,6 +51,7 @@ type Container struct {
 | 
			
		|||
	ShmPath         string
 | 
			
		||||
	MqueuePath      string
 | 
			
		||||
	ResolvConfPath  string
 | 
			
		||||
	SeccompProfile  string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateDaemonEnvironment returns the list of all environment variables given the list of
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -236,6 +236,7 @@ func (daemon *Daemon) populateCommand(c *container.Container, env []string) erro
 | 
			
		|||
		Pid:                pid,
 | 
			
		||||
		ReadonlyRootfs:     c.HostConfig.ReadonlyRootfs,
 | 
			
		||||
		RemappedRoot:       remappedRoot,
 | 
			
		||||
		SeccompProfile:     c.SeccompProfile,
 | 
			
		||||
		UIDMapping:         uidMap,
 | 
			
		||||
		UTS:                uts,
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -152,6 +152,16 @@ func TestParseSecurityOpt(t *testing.T) {
 | 
			
		|||
		t.Fatalf("Unexpected AppArmorProfile, expected: \"test_profile\", got %q", container.AppArmorProfile)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// test seccomp
 | 
			
		||||
	sp := "/path/to/seccomp_test.json"
 | 
			
		||||
	config.SecurityOpt = []string{"seccomp:" + sp}
 | 
			
		||||
	if err := parseSecurityOpt(container, config); err != nil {
 | 
			
		||||
		t.Fatalf("Unexpected parseSecurityOpt error: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if container.SeccompProfile != sp {
 | 
			
		||||
		t.Fatalf("Unexpected AppArmorProfile, expected: %q, got %q", sp, container.SeccompProfile)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// test valid label
 | 
			
		||||
	config.SecurityOpt = []string{"label:user:USER"}
 | 
			
		||||
	if err := parseSecurityOpt(container, config); err != nil {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										2
									
								
								daemon/daemon_unix.go
									
										
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										2
									
								
								daemon/daemon_unix.go
									
										
									
									
									
										
										
										Normal file → Executable file
									
								
							| 
						 | 
				
			
			@ -74,6 +74,8 @@ func parseSecurityOpt(container *container.Container, config *runconfig.HostConf
 | 
			
		|||
			labelOpts = append(labelOpts, con[1])
 | 
			
		||||
		case "apparmor":
 | 
			
		||||
			container.AppArmorProfile = con[1]
 | 
			
		||||
		case "seccomp":
 | 
			
		||||
			container.SeccompProfile = con[1]
 | 
			
		||||
		default:
 | 
			
		||||
			return fmt.Errorf("Invalid --security-opt: %q", opt)
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -118,6 +118,7 @@ type Command struct {
 | 
			
		|||
	Pid                *Pid              `json:"pid"`
 | 
			
		||||
	ReadonlyRootfs     bool              `json:"readonly_rootfs"`
 | 
			
		||||
	RemappedRoot       *User             `json:"remap_root"`
 | 
			
		||||
	SeccompProfile     string            `json:"seccomp_profile"`
 | 
			
		||||
	UIDMapping         []idtools.IDMap   `json:"uidmapping"`
 | 
			
		||||
	UTS                *UTS              `json:"uts"`
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,8 +19,8 @@ import (
 | 
			
		|||
 | 
			
		||||
// createContainer populates and configures the container type with the
 | 
			
		||||
// data provided by the execdriver.Command
 | 
			
		||||
func (d *Driver) createContainer(c *execdriver.Command, hooks execdriver.Hooks) (*configs.Config, error) {
 | 
			
		||||
	container := execdriver.InitContainer(c)
 | 
			
		||||
func (d *Driver) createContainer(c *execdriver.Command, hooks execdriver.Hooks) (container *configs.Config, err error) {
 | 
			
		||||
	container = execdriver.InitContainer(c)
 | 
			
		||||
 | 
			
		||||
	if err := d.createIpc(container, c); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
| 
						 | 
				
			
			@ -82,6 +82,12 @@ func (d *Driver) createContainer(c *execdriver.Command, hooks execdriver.Hooks)
 | 
			
		|||
		container.AppArmorProfile = c.AppArmorProfile
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.SeccompProfile != "" {
 | 
			
		||||
		container.Seccomp, err = loadSeccompProfile(c.SeccompProfile)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if err := execdriver.SetupCgroups(container, c); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										94
									
								
								daemon/execdriver/native/seccomp.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								daemon/execdriver/native/seccomp.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,94 @@
 | 
			
		|||
// +build linux
 | 
			
		||||
 | 
			
		||||
package native
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
 | 
			
		||||
	"github.com/opencontainers/runc/libcontainer/configs"
 | 
			
		||||
	"github.com/opencontainers/runc/libcontainer/seccomp"
 | 
			
		||||
	"github.com/opencontainers/specs"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func loadSeccompProfile(path string) (*configs.Seccomp, error) {
 | 
			
		||||
	f, err := ioutil.ReadFile(path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("Opening seccomp profile failed: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var config specs.Seccomp
 | 
			
		||||
	if err := json.Unmarshal(f, &config); err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("Decoding seccomp profile failed: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return setupSeccomp(&config)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func setupSeccomp(config *specs.Seccomp) (newConfig *configs.Seccomp, err error) {
 | 
			
		||||
	if config == nil {
 | 
			
		||||
		return nil, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// No default action specified, no syscalls listed, assume seccomp disabled
 | 
			
		||||
	if config.DefaultAction == "" && len(config.Syscalls) == 0 {
 | 
			
		||||
		return nil, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	newConfig = new(configs.Seccomp)
 | 
			
		||||
	newConfig.Syscalls = []*configs.Syscall{}
 | 
			
		||||
 | 
			
		||||
	// if config.Architectures == 0 then libseccomp will figure out the architecture to use
 | 
			
		||||
	if len(config.Architectures) > 0 {
 | 
			
		||||
		newConfig.Architectures = []string{}
 | 
			
		||||
		for _, arch := range config.Architectures {
 | 
			
		||||
			newArch, err := seccomp.ConvertStringToArch(string(arch))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
			newConfig.Architectures = append(newConfig.Architectures, newArch)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Convert default action from string representation
 | 
			
		||||
	newConfig.DefaultAction, err = seccomp.ConvertStringToAction(string(config.DefaultAction))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Loop through all syscall blocks and convert them to libcontainer format
 | 
			
		||||
	for _, call := range config.Syscalls {
 | 
			
		||||
		newAction, err := seccomp.ConvertStringToAction(string(call.Action))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		newCall := configs.Syscall{
 | 
			
		||||
			Name:   call.Name,
 | 
			
		||||
			Action: newAction,
 | 
			
		||||
			Args:   []*configs.Arg{},
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Loop through all the arguments of the syscall and convert them
 | 
			
		||||
		for _, arg := range call.Args {
 | 
			
		||||
			newOp, err := seccomp.ConvertStringToOperator(string(arg.Op))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			newArg := configs.Arg{
 | 
			
		||||
				Index:    arg.Index,
 | 
			
		||||
				Value:    arg.Value,
 | 
			
		||||
				ValueTwo: arg.ValueTwo,
 | 
			
		||||
				Op:       newOp,
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			newCall.Args = append(newCall.Args, &newArg)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		newConfig.Syscalls = append(newConfig.Syscalls, &newCall)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return newConfig, nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -3789,3 +3789,59 @@ func (s *DockerSuite) TestRunWithOomScoreAdjInvalidRange(c *check.C) {
 | 
			
		|||
		c.Fatalf("Expected output to contain %q, got %q instead", expected, out)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TestRunSeccompProfileDenyUnshare checks that 'docker run --security-opt seccomp:/tmp/profile.json jess/unshare unshare' exits with operation not permitted.
 | 
			
		||||
func (s *DockerSuite) TestRunSeccompProfileDenyUnshare(c *check.C) {
 | 
			
		||||
	testRequires(c, SameHostDaemon)
 | 
			
		||||
	jsonData := `{
 | 
			
		||||
	"defaultAction": "SCMP_ACT_ALLOW",
 | 
			
		||||
	"syscalls": [
 | 
			
		||||
		{
 | 
			
		||||
			"name": "unshare",
 | 
			
		||||
			"action": "SCMP_ACT_ERRNO"
 | 
			
		||||
		}
 | 
			
		||||
	]
 | 
			
		||||
}`
 | 
			
		||||
	tmpFile, err := ioutil.TempFile("", "profile.json")
 | 
			
		||||
	defer tmpFile.Close()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, err := tmpFile.Write([]byte(jsonData)); err != nil {
 | 
			
		||||
		c.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	runCmd := exec.Command(dockerBinary, "run", "--security-opt", "seccomp:"+tmpFile.Name(), "jess/unshare", "unshare", "-p", "-m", "-f", "-r", "mount", "-t", "proc", "none", "/proc")
 | 
			
		||||
	out, _, _ := runCommandWithOutput(runCmd)
 | 
			
		||||
	if !strings.Contains(out, "Operation not permitted") {
 | 
			
		||||
		c.Fatalf("expected unshare with seccomp profile denied to fail, got %s", out)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TestRunSeccompProfileDenyChmod checks that 'docker run --security-opt seccomp:/tmp/profile.json busybox chmod 400 /etc/hostname' exits with operation not permitted.
 | 
			
		||||
func (s *DockerSuite) TestRunSeccompProfileDenyChmod(c *check.C) {
 | 
			
		||||
	testRequires(c, SameHostDaemon)
 | 
			
		||||
	jsonData := `{
 | 
			
		||||
	"defaultAction": "SCMP_ACT_ALLOW",
 | 
			
		||||
	"syscalls": [
 | 
			
		||||
		{
 | 
			
		||||
			"name": "chmod",
 | 
			
		||||
			"action": "SCMP_ACT_ERRNO"
 | 
			
		||||
		}
 | 
			
		||||
	]
 | 
			
		||||
}`
 | 
			
		||||
	tmpFile, err := ioutil.TempFile("", "profile.json")
 | 
			
		||||
	defer tmpFile.Close()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, err := tmpFile.Write([]byte(jsonData)); err != nil {
 | 
			
		||||
		c.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	runCmd := exec.Command(dockerBinary, "run", "--security-opt", "seccomp:"+tmpFile.Name(), "busybox", "chmod", "400", "/etc/hostname")
 | 
			
		||||
	out, _, _ := runCommandWithOutput(runCmd)
 | 
			
		||||
	if !strings.Contains(out, "Operation not permitted") {
 | 
			
		||||
		c.Fatalf("expected chmod with seccomp profile denied to fail, got %s", out)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue