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
 | 
						ShmPath         string
 | 
				
			||||||
	MqueuePath      string
 | 
						MqueuePath      string
 | 
				
			||||||
	ResolvConfPath  string
 | 
						ResolvConfPath  string
 | 
				
			||||||
 | 
						SeccompProfile  string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CreateDaemonEnvironment returns the list of all environment variables given the list of
 | 
					// 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,
 | 
							Pid:                pid,
 | 
				
			||||||
		ReadonlyRootfs:     c.HostConfig.ReadonlyRootfs,
 | 
							ReadonlyRootfs:     c.HostConfig.ReadonlyRootfs,
 | 
				
			||||||
		RemappedRoot:       remappedRoot,
 | 
							RemappedRoot:       remappedRoot,
 | 
				
			||||||
 | 
							SeccompProfile:     c.SeccompProfile,
 | 
				
			||||||
		UIDMapping:         uidMap,
 | 
							UIDMapping:         uidMap,
 | 
				
			||||||
		UTS:                uts,
 | 
							UTS:                uts,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -152,6 +152,16 @@ func TestParseSecurityOpt(t *testing.T) {
 | 
				
			||||||
		t.Fatalf("Unexpected AppArmorProfile, expected: \"test_profile\", got %q", container.AppArmorProfile)
 | 
							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
 | 
						// test valid label
 | 
				
			||||||
	config.SecurityOpt = []string{"label:user:USER"}
 | 
						config.SecurityOpt = []string{"label:user:USER"}
 | 
				
			||||||
	if err := parseSecurityOpt(container, config); err != nil {
 | 
						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])
 | 
								labelOpts = append(labelOpts, con[1])
 | 
				
			||||||
		case "apparmor":
 | 
							case "apparmor":
 | 
				
			||||||
			container.AppArmorProfile = con[1]
 | 
								container.AppArmorProfile = con[1]
 | 
				
			||||||
 | 
							case "seccomp":
 | 
				
			||||||
 | 
								container.SeccompProfile = con[1]
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			return fmt.Errorf("Invalid --security-opt: %q", opt)
 | 
								return fmt.Errorf("Invalid --security-opt: %q", opt)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -118,6 +118,7 @@ type Command struct {
 | 
				
			||||||
	Pid                *Pid              `json:"pid"`
 | 
						Pid                *Pid              `json:"pid"`
 | 
				
			||||||
	ReadonlyRootfs     bool              `json:"readonly_rootfs"`
 | 
						ReadonlyRootfs     bool              `json:"readonly_rootfs"`
 | 
				
			||||||
	RemappedRoot       *User             `json:"remap_root"`
 | 
						RemappedRoot       *User             `json:"remap_root"`
 | 
				
			||||||
 | 
						SeccompProfile     string            `json:"seccomp_profile"`
 | 
				
			||||||
	UIDMapping         []idtools.IDMap   `json:"uidmapping"`
 | 
						UIDMapping         []idtools.IDMap   `json:"uidmapping"`
 | 
				
			||||||
	UTS                *UTS              `json:"uts"`
 | 
						UTS                *UTS              `json:"uts"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,8 +19,8 @@ import (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// createContainer populates and configures the container type with the
 | 
					// createContainer populates and configures the container type with the
 | 
				
			||||||
// data provided by the execdriver.Command
 | 
					// data provided by the execdriver.Command
 | 
				
			||||||
func (d *Driver) createContainer(c *execdriver.Command, hooks execdriver.Hooks) (*configs.Config, error) {
 | 
					func (d *Driver) createContainer(c *execdriver.Command, hooks execdriver.Hooks) (container *configs.Config, err error) {
 | 
				
			||||||
	container := execdriver.InitContainer(c)
 | 
						container = execdriver.InitContainer(c)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := d.createIpc(container, c); err != nil {
 | 
						if err := d.createIpc(container, c); err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
| 
						 | 
					@ -82,6 +82,12 @@ func (d *Driver) createContainer(c *execdriver.Command, hooks execdriver.Hooks)
 | 
				
			||||||
		container.AppArmorProfile = c.AppArmorProfile
 | 
							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 {
 | 
						if err := execdriver.SetupCgroups(container, c); err != nil {
 | 
				
			||||||
		return nil, err
 | 
							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)
 | 
							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