Ver Fonte

Merge pull request #46386 from dperny/add-swarm-seccomp-apparmor

Add support for swarm seccomp and apparmor
Sebastiaan van Stijn há 1 ano atrás
pai
commit
b94e88c1e2

+ 9 - 0
api/server/router/swarm/helpers.go

@@ -118,4 +118,13 @@ func adjustForAPIVersion(cliVersion string, service *swarm.ServiceSpec) {
 		service.Mode.ReplicatedJob = nil
 		service.Mode.ReplicatedJob = nil
 		service.Mode.GlobalJob = nil
 		service.Mode.GlobalJob = nil
 	}
 	}
+
+	if versions.LessThan(cliVersion, "1.44") {
+		// seccomp, apparmor, and no_new_privs were added in 1.44.
+		if service.TaskTemplate.ContainerSpec != nil && service.TaskTemplate.ContainerSpec.Privileges != nil {
+			service.TaskTemplate.ContainerSpec.Privileges.Seccomp = nil
+			service.TaskTemplate.ContainerSpec.Privileges.AppArmor = nil
+			service.TaskTemplate.ContainerSpec.Privileges.NoNewPrivileges = false
+		}
+	}
 }
 }

+ 26 - 0
api/swagger.yaml

@@ -3553,6 +3553,32 @@ definitions:
                   Level:
                   Level:
                     type: "string"
                     type: "string"
                     description: "SELinux level label"
                     description: "SELinux level label"
+              Seccomp:
+                type: "object"
+                description: "Options for configuring seccomp on the container"
+                properties:
+                  Mode:
+                    type: "string"
+                    enum:
+                      - "default"
+                      - "unconfined"
+                      - "custom"
+                  Profile:
+                    description: "The custom seccomp profile as a json object"
+                    type: "string"
+              AppArmor:
+                type: "object"
+                description: "Options for configuring AppArmor on the container"
+                properties:
+                  Mode:
+                    type: "string"
+                    enum:
+                      - "default"
+                      - "disabled"
+              NoNewPrivileges:
+                type: "boolean"
+                description: "Configuration of the no_new_privs bit in the container"
+
           TTY:
           TTY:
             description: "Whether a pseudo-TTY should be allocated."
             description: "Whether a pseudo-TTY should be allocated."
             type: "boolean"
             type: "boolean"

+ 41 - 2
api/types/swarm/container.go

@@ -32,6 +32,42 @@ type SELinuxContext struct {
 	Level string
 	Level string
 }
 }
 
 
+// SeccompMode is the type used for the enumeration of possible seccomp modes
+// in SeccompOpts
+type SeccompMode string
+
+const (
+	SeccompModeDefault    SeccompMode = "default"
+	SeccompModeUnconfined SeccompMode = "unconfined"
+	SeccompModeCustom     SeccompMode = "custom"
+)
+
+// SeccompOpts defines the options for configuring seccomp on a swarm-managed
+// container.
+type SeccompOpts struct {
+	// Mode is the SeccompMode used for the container.
+	Mode SeccompMode `json:",omitempty"`
+	// Profile is the custom seccomp profile as a json object to be used with
+	// the container. Mode should be set to SeccompModeCustom when using a
+	// custom profile in this manner.
+	Profile []byte `json:",omitempty"`
+}
+
+// AppArmorMode is type used for the enumeration of possible AppArmor modes in
+// AppArmorOpts
+type AppArmorMode string
+
+const (
+	AppArmorModeDefault  AppArmorMode = "default"
+	AppArmorModeDisabled AppArmorMode = "disabled"
+)
+
+// AppArmorOpts defines the options for configuring AppArmor on a swarm-managed
+// container.  Currently, custom AppArmor profiles are not supported.
+type AppArmorOpts struct {
+	Mode AppArmorMode `json:",omitempty"`
+}
+
 // CredentialSpec for managed service account (Windows only)
 // CredentialSpec for managed service account (Windows only)
 type CredentialSpec struct {
 type CredentialSpec struct {
 	Config   string
 	Config   string
@@ -41,8 +77,11 @@ type CredentialSpec struct {
 
 
 // Privileges defines the security options for the container.
 // Privileges defines the security options for the container.
 type Privileges struct {
 type Privileges struct {
-	CredentialSpec *CredentialSpec
-	SELinuxContext *SELinuxContext
+	CredentialSpec  *CredentialSpec
+	SELinuxContext  *SELinuxContext
+	Seccomp         *SeccompOpts  `json:",omitempty"`
+	AppArmor        *AppArmorOpts `json:",omitempty"`
+	NoNewPrivileges bool
 }
 }
 
 
 // ContainerSpec represents the spec of a container.
 // ContainerSpec represents the spec of a container.

+ 56 - 0
daemon/cluster/convert/container.go

@@ -69,6 +69,34 @@ func containerSpecFromGRPC(c *swarmapi.ContainerSpec) *types.ContainerSpec {
 				Level:   c.Privileges.SELinuxContext.Level,
 				Level:   c.Privileges.SELinuxContext.Level,
 			}
 			}
 		}
 		}
+
+		if c.Privileges.Seccomp != nil {
+			containerSpec.Privileges.Seccomp = &types.SeccompOpts{
+				Profile: c.Privileges.Seccomp.Profile,
+			}
+
+			switch c.Privileges.Seccomp.Mode {
+			case swarmapi.Privileges_SeccompOpts_DEFAULT:
+				containerSpec.Privileges.Seccomp.Mode = types.SeccompModeDefault
+			case swarmapi.Privileges_SeccompOpts_UNCONFINED:
+				containerSpec.Privileges.Seccomp.Mode = types.SeccompModeUnconfined
+			case swarmapi.Privileges_SeccompOpts_CUSTOM:
+				containerSpec.Privileges.Seccomp.Mode = types.SeccompModeCustom
+			}
+		}
+
+		if c.Privileges.Apparmor != nil {
+			containerSpec.Privileges.AppArmor = &types.AppArmorOpts{}
+
+			switch c.Privileges.Apparmor.Mode {
+			case swarmapi.Privileges_AppArmorOpts_DEFAULT:
+				containerSpec.Privileges.AppArmor.Mode = types.AppArmorModeDefault
+			case swarmapi.Privileges_AppArmorOpts_DISABLED:
+				containerSpec.Privileges.AppArmor.Mode = types.AppArmorModeDisabled
+			}
+		}
+
+		containerSpec.Privileges.NoNewPrivileges = c.Privileges.NoNewPrivileges
 	}
 	}
 
 
 	// Mounts
 	// Mounts
@@ -308,6 +336,34 @@ func containerToGRPC(c *types.ContainerSpec) (*swarmapi.ContainerSpec, error) {
 				Level:   c.Privileges.SELinuxContext.Level,
 				Level:   c.Privileges.SELinuxContext.Level,
 			}
 			}
 		}
 		}
+
+		if c.Privileges.Seccomp != nil {
+			containerSpec.Privileges.Seccomp = &swarmapi.Privileges_SeccompOpts{
+				Profile: c.Privileges.Seccomp.Profile,
+			}
+
+			switch c.Privileges.Seccomp.Mode {
+			case types.SeccompModeDefault:
+				containerSpec.Privileges.Seccomp.Mode = swarmapi.Privileges_SeccompOpts_DEFAULT
+			case types.SeccompModeUnconfined:
+				containerSpec.Privileges.Seccomp.Mode = swarmapi.Privileges_SeccompOpts_UNCONFINED
+			case types.SeccompModeCustom:
+				containerSpec.Privileges.Seccomp.Mode = swarmapi.Privileges_SeccompOpts_CUSTOM
+			}
+		}
+
+		if c.Privileges.AppArmor != nil {
+			containerSpec.Privileges.Apparmor = &swarmapi.Privileges_AppArmorOpts{}
+
+			switch c.Privileges.AppArmor.Mode {
+			case types.AppArmorModeDefault:
+				containerSpec.Privileges.Apparmor.Mode = swarmapi.Privileges_AppArmorOpts_DEFAULT
+			case types.AppArmorModeDisabled:
+				containerSpec.Privileges.Apparmor.Mode = swarmapi.Privileges_AppArmorOpts_DISABLED
+			}
+		}
+
+		containerSpec.Privileges.NoNewPrivileges = c.Privileges.NoNewPrivileges
 	}
 	}
 
 
 	if c.Configs != nil {
 	if c.Configs != nil {

+ 26 - 0
daemon/cluster/executor/container/container.go

@@ -698,6 +698,32 @@ func (c *containerConfig) applyPrivileges(hc *enginecontainer.HostConfig) {
 			hc.SecurityOpt = append(hc.SecurityOpt, "label=type:"+selinux.Type)
 			hc.SecurityOpt = append(hc.SecurityOpt, "label=type:"+selinux.Type)
 		}
 		}
 	}
 	}
+
+	// variable to make the lines shorter and easier to read
+	if seccomp := privileges.Seccomp; seccomp != nil {
+		switch seccomp.Mode {
+		// case api.Privileges_SeccompOpts_DEFAULT:
+		//   if the setting is default, nothing needs to be set here. we leave
+		//   the option empty.
+		case api.Privileges_SeccompOpts_UNCONFINED:
+			hc.SecurityOpt = append(hc.SecurityOpt, "seccomp=unconfined")
+		case api.Privileges_SeccompOpts_CUSTOM:
+			// Profile is bytes, but those bytes are actually a string. This is
+			// basically verbatim what happens in the cli after a file is read.
+			hc.SecurityOpt = append(hc.SecurityOpt, fmt.Sprintf("seccomp=%s", seccomp.Profile))
+		}
+	}
+
+	// if the setting is DEFAULT, then nothing to be done. If it's DISABLED,
+	// we set that. Custom not supported yet. When custom *is* supported, make
+	// it look like the above.
+	if apparmor := privileges.Apparmor; apparmor != nil && apparmor.Mode == api.Privileges_AppArmorOpts_DISABLED {
+		hc.SecurityOpt = append(hc.SecurityOpt, "apparmor=unconfined")
+	}
+
+	if privileges.NoNewPrivileges {
+		hc.SecurityOpt = append(hc.SecurityOpt, "no-new-privileges=true")
+	}
 }
 }
 
 
 func (c *containerConfig) eventFilter() filters.Args {
 func (c *containerConfig) eventFilter() filters.Args {

+ 3 - 0
docs/api/version-history.md

@@ -52,6 +52,9 @@ keywords: "API, Docker, rcli, REST, documentation"
   These endpoints will also return the full set of validation errors they find,
   These endpoints will also return the full set of validation errors they find,
   instead of returning only the first one.
   instead of returning only the first one.
   Note that this change is _unversioned_ and applies to all API versions.
   Note that this change is _unversioned_ and applies to all API versions.
+* `POST /services/create` and `POST /services/{id}/update` now accept `Seccomp`
+  and `AppArmor` fields in the `ContainerSpec.Privileges` object. This allows
+  some configuration of Seccomp and AppArmor in Swarm services.
 
 
 ## v1.43 API changes
 ## v1.43 API changes
 
 

+ 1 - 1
vendor.mod

@@ -66,7 +66,7 @@ require (
 	github.com/moby/locker v1.0.1
 	github.com/moby/locker v1.0.1
 	github.com/moby/patternmatcher v0.6.0
 	github.com/moby/patternmatcher v0.6.0
 	github.com/moby/pubsub v1.0.0
 	github.com/moby/pubsub v1.0.0
-	github.com/moby/swarmkit/v2 v2.0.0-20230815220644-3f2e40b3ed51
+	github.com/moby/swarmkit/v2 v2.0.0-20230823155524-12f0c246fed0
 	github.com/moby/sys/mount v0.3.3
 	github.com/moby/sys/mount v0.3.3
 	github.com/moby/sys/mountinfo v0.6.2
 	github.com/moby/sys/mountinfo v0.6.2
 	github.com/moby/sys/sequential v0.5.0
 	github.com/moby/sys/sequential v0.5.0

+ 2 - 2
vendor.sum

@@ -917,8 +917,8 @@ github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkV
 github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
 github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
 github.com/moby/pubsub v1.0.0 h1:jkp/imWsmJz2f6LyFsk7EkVeN2HxR/HTTOY8kHrsxfA=
 github.com/moby/pubsub v1.0.0 h1:jkp/imWsmJz2f6LyFsk7EkVeN2HxR/HTTOY8kHrsxfA=
 github.com/moby/pubsub v1.0.0/go.mod h1:bXSO+3h5MNXXCaEG+6/NlAIk7MMZbySZlnB+cUQhKKc=
 github.com/moby/pubsub v1.0.0/go.mod h1:bXSO+3h5MNXXCaEG+6/NlAIk7MMZbySZlnB+cUQhKKc=
-github.com/moby/swarmkit/v2 v2.0.0-20230815220644-3f2e40b3ed51 h1:Am1RXolTCcQT5zaEUBGczaHZaV069crCKpLHckp9isM=
-github.com/moby/swarmkit/v2 v2.0.0-20230815220644-3f2e40b3ed51/go.mod h1:dHTjRFlamMrutFg1Vi0AEZKtVx2qZV/7A/imviPXQiE=
+github.com/moby/swarmkit/v2 v2.0.0-20230823155524-12f0c246fed0 h1:DBx1xD69N0nQjoMQskoTQQTb0hpZbo+8Rk9ug0wUMKs=
+github.com/moby/swarmkit/v2 v2.0.0-20230823155524-12f0c246fed0/go.mod h1:dHTjRFlamMrutFg1Vi0AEZKtVx2qZV/7A/imviPXQiE=
 github.com/moby/sys/mount v0.1.0/go.mod h1:FVQFLDRWwyBjDTBNQXDlWnSFREqOo3OKX9aqhmeoo74=
 github.com/moby/sys/mount v0.1.0/go.mod h1:FVQFLDRWwyBjDTBNQXDlWnSFREqOo3OKX9aqhmeoo74=
 github.com/moby/sys/mount v0.1.1/go.mod h1:FVQFLDRWwyBjDTBNQXDlWnSFREqOo3OKX9aqhmeoo74=
 github.com/moby/sys/mount v0.1.1/go.mod h1:FVQFLDRWwyBjDTBNQXDlWnSFREqOo3OKX9aqhmeoo74=
 github.com/moby/sys/mount v0.3.3 h1:fX1SVkXFJ47XWDoeFW4Sq7PdQJnV2QIDZAqjNqgEjUs=
 github.com/moby/sys/mount v0.3.3 h1:fX1SVkXFJ47XWDoeFW4Sq7PdQJnV2QIDZAqjNqgEjUs=

+ 78 - 0
vendor/github.com/moby/swarmkit/v2/api/api.pb.txt

@@ -4324,6 +4324,29 @@ file {
       }
       }
       json_name: "selinuxContext"
       json_name: "selinuxContext"
     }
     }
+    field {
+      name: "seccomp"
+      number: 3
+      label: LABEL_OPTIONAL
+      type: TYPE_MESSAGE
+      type_name: ".docker.swarmkit.v1.Privileges.SeccompOpts"
+      json_name: "seccomp"
+    }
+    field {
+      name: "apparmor"
+      number: 4
+      label: LABEL_OPTIONAL
+      type: TYPE_MESSAGE
+      type_name: ".docker.swarmkit.v1.Privileges.AppArmorOpts"
+      json_name: "apparmor"
+    }
+    field {
+      name: "no_new_privileges"
+      number: 5
+      label: LABEL_OPTIONAL
+      type: TYPE_BOOL
+      json_name: "noNewPrivileges"
+    }
     nested_type {
     nested_type {
       name: "CredentialSpec"
       name: "CredentialSpec"
       field {
       field {
@@ -4392,6 +4415,61 @@ file {
         json_name: "level"
         json_name: "level"
       }
       }
     }
     }
+    nested_type {
+      name: "SeccompOpts"
+      field {
+        name: "mode"
+        number: 1
+        label: LABEL_OPTIONAL
+        type: TYPE_ENUM
+        type_name: ".docker.swarmkit.v1.Privileges.SeccompOpts.SeccompMode"
+        json_name: "mode"
+      }
+      field {
+        name: "profile"
+        number: 2
+        label: LABEL_OPTIONAL
+        type: TYPE_BYTES
+        json_name: "profile"
+      }
+      enum_type {
+        name: "SeccompMode"
+        value {
+          name: "DEFAULT"
+          number: 0
+        }
+        value {
+          name: "UNCONFINED"
+          number: 1
+        }
+        value {
+          name: "CUSTOM"
+          number: 2
+        }
+      }
+    }
+    nested_type {
+      name: "AppArmorOpts"
+      field {
+        name: "mode"
+        number: 1
+        label: LABEL_OPTIONAL
+        type: TYPE_ENUM
+        type_name: ".docker.swarmkit.v1.Privileges.AppArmorOpts.AppArmorMode"
+        json_name: "mode"
+      }
+      enum_type {
+        name: "AppArmorMode"
+        value {
+          name: "DEFAULT"
+          number: 0
+        }
+        value {
+          name: "DISABLED"
+          number: 1
+        }
+      }
+    }
   }
   }
   message_type {
   message_type {
     name: "JobStatus"
     name: "JobStatus"

Diff do ficheiro suprimidas por serem muito extensas
+ 564 - 402
vendor/github.com/moby/swarmkit/v2/api/types.pb.go


+ 33 - 0
vendor/github.com/moby/swarmkit/v2/api/types.proto

@@ -1156,6 +1156,39 @@ message Privileges {
 		string level = 5;
 		string level = 5;
 	}
 	}
 	SELinuxContext selinux_context = 2 [(gogoproto.customname) = "SELinuxContext"];
 	SELinuxContext selinux_context = 2 [(gogoproto.customname) = "SELinuxContext"];
+
+	// SeccompOpts contains options for configuring seccomp profiles on the
+	// container. See https://docs.docker.com/engine/security/seccomp/ for more
+	// information.
+	message SeccompOpts {
+		enum SeccompMode {
+			DEFAULT = 0;
+			UNCONFINED = 1;
+			CUSTOM =  2;
+		}
+		SeccompMode mode = 1;
+		// Profile contains the json definition of the seccomp profile to use,
+		// if Mode is set to custom.
+		bytes profile = 2;
+	}
+	SeccompOpts seccomp = 3;
+
+	// AppArmorOpts contains options for configuring AppArmor profiles on the
+	// container. Currently, custom profiles are not supported. See
+	// https://docs.docker.com/engine/security/apparmor/ for more information.
+	message AppArmorOpts {
+		enum AppArmorMode {
+			DEFAULT = 0;
+			DISABLED = 1;
+		}
+		AppArmorMode mode = 1;
+	}
+	AppArmorOpts apparmor = 4;
+
+	// NoNewPrivileges, if set to true, disables the container from gaining new
+	// privileges. See https://docs.kernel.org/userspace-api/no_new_privs.html
+	// for details.
+	bool no_new_privileges = 5;
 }
 }
 
 
 // JobStatus indicates the status of a Service that is in one of the Job modes.
 // JobStatus indicates the status of a Service that is in one of the Job modes.

+ 1 - 1
vendor/modules.txt

@@ -829,7 +829,7 @@ github.com/moby/patternmatcher/ignorefile
 # github.com/moby/pubsub v1.0.0
 # github.com/moby/pubsub v1.0.0
 ## explicit; go 1.19
 ## explicit; go 1.19
 github.com/moby/pubsub
 github.com/moby/pubsub
-# github.com/moby/swarmkit/v2 v2.0.0-20230815220644-3f2e40b3ed51
+# github.com/moby/swarmkit/v2 v2.0.0-20230823155524-12f0c246fed0
 ## explicit; go 1.18
 ## explicit; go 1.18
 github.com/moby/swarmkit/v2/agent
 github.com/moby/swarmkit/v2/agent
 github.com/moby/swarmkit/v2/agent/configs
 github.com/moby/swarmkit/v2/agent/configs

Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff