mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
services: Add support for Credential Spec and SELinux
- Defined "normalized" type for Credential Spec and SELinux - Added --credential-spec to docker service create & update - SELinux is API only at the time Signed-off-by: Andrea Luzzardi <aluzzardi@gmail.com>
This commit is contained in:
parent
c4010e257f
commit
89a995a9d7
4 changed files with 163 additions and 0 deletions
|
@ -21,6 +21,28 @@ type DNSConfig struct {
|
||||||
Options []string `json:",omitempty"`
|
Options []string `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SELinuxContext contains the SELinux labels of the container.
|
||||||
|
type SELinuxContext struct {
|
||||||
|
Disable bool
|
||||||
|
|
||||||
|
User string
|
||||||
|
Role string
|
||||||
|
Type string
|
||||||
|
Level string
|
||||||
|
}
|
||||||
|
|
||||||
|
// CredentialSpec for managed service account (Windows only)
|
||||||
|
type CredentialSpec struct {
|
||||||
|
File string
|
||||||
|
Registry string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Privileges defines the security options for the container.
|
||||||
|
type Privileges struct {
|
||||||
|
CredentialSpec *CredentialSpec
|
||||||
|
SELinuxContext *SELinuxContext
|
||||||
|
}
|
||||||
|
|
||||||
// ContainerSpec represents the spec of a container.
|
// ContainerSpec represents the spec of a container.
|
||||||
type ContainerSpec struct {
|
type ContainerSpec struct {
|
||||||
Image string `json:",omitempty"`
|
Image string `json:",omitempty"`
|
||||||
|
@ -32,6 +54,7 @@ type ContainerSpec struct {
|
||||||
Dir string `json:",omitempty"`
|
Dir string `json:",omitempty"`
|
||||||
User string `json:",omitempty"`
|
User string `json:",omitempty"`
|
||||||
Groups []string `json:",omitempty"`
|
Groups []string `json:",omitempty"`
|
||||||
|
Privileges *Privileges `json:",omitempty"`
|
||||||
StopSignal string `json:",omitempty"`
|
StopSignal string `json:",omitempty"`
|
||||||
TTY bool `json:",omitempty"`
|
TTY bool `json:",omitempty"`
|
||||||
OpenStdin bool `json:",omitempty"`
|
OpenStdin bool `json:",omitempty"`
|
||||||
|
|
|
@ -236,6 +236,38 @@ func (r *restartPolicyOptions) ToRestartPolicy() *swarm.RestartPolicy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type credentialSpecOpt struct {
|
||||||
|
value *swarm.CredentialSpec
|
||||||
|
source string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *credentialSpecOpt) Set(value string) error {
|
||||||
|
c.source = value
|
||||||
|
c.value = &swarm.CredentialSpec{}
|
||||||
|
switch {
|
||||||
|
case strings.HasPrefix(value, "file://"):
|
||||||
|
c.value.File = strings.TrimPrefix(value, "file://")
|
||||||
|
case strings.HasPrefix(value, "registry://"):
|
||||||
|
c.value.Registry = strings.TrimPrefix(value, "registry://")
|
||||||
|
default:
|
||||||
|
return errors.New("Invalid credential spec - value must be prefixed file:// or registry:// followed by a value")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *credentialSpecOpt) Type() string {
|
||||||
|
return "credential-spec"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *credentialSpecOpt) String() string {
|
||||||
|
return c.source
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *credentialSpecOpt) Value() *swarm.CredentialSpec {
|
||||||
|
return c.value
|
||||||
|
}
|
||||||
|
|
||||||
func convertNetworks(networks []string) []swarm.NetworkAttachmentConfig {
|
func convertNetworks(networks []string) []swarm.NetworkAttachmentConfig {
|
||||||
nets := []swarm.NetworkAttachmentConfig{}
|
nets := []swarm.NetworkAttachmentConfig{}
|
||||||
for _, network := range networks {
|
for _, network := range networks {
|
||||||
|
@ -353,6 +385,7 @@ type serviceOptions struct {
|
||||||
workdir string
|
workdir string
|
||||||
user string
|
user string
|
||||||
groups opts.ListOpts
|
groups opts.ListOpts
|
||||||
|
credentialSpec credentialSpecOpt
|
||||||
stopSignal string
|
stopSignal string
|
||||||
tty bool
|
tty bool
|
||||||
readOnly bool
|
readOnly bool
|
||||||
|
@ -498,6 +531,12 @@ func (opts *serviceOptions) ToService() (swarm.ServiceSpec, error) {
|
||||||
EndpointSpec: opts.endpoint.ToEndpointSpec(),
|
EndpointSpec: opts.endpoint.ToEndpointSpec(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.credentialSpec.Value() != nil {
|
||||||
|
service.TaskTemplate.ContainerSpec.Privileges = &swarm.Privileges{
|
||||||
|
CredentialSpec: opts.credentialSpec.Value(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return service, nil
|
return service, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -509,6 +548,8 @@ func addServiceFlags(flags *pflag.FlagSet, opts *serviceOptions) {
|
||||||
|
|
||||||
flags.StringVarP(&opts.workdir, flagWorkdir, "w", "", "Working directory inside the container")
|
flags.StringVarP(&opts.workdir, flagWorkdir, "w", "", "Working directory inside the container")
|
||||||
flags.StringVarP(&opts.user, flagUser, "u", "", "Username or UID (format: <name|uid>[:<group|gid>])")
|
flags.StringVarP(&opts.user, flagUser, "u", "", "Username or UID (format: <name|uid>[:<group|gid>])")
|
||||||
|
flags.Var(&opts.credentialSpec, flagCredentialSpec, "Credential spec for managed service account (Windows only)")
|
||||||
|
flags.SetAnnotation(flagCredentialSpec, "version", []string{"1.29"})
|
||||||
flags.StringVar(&opts.hostname, flagHostname, "", "Container hostname")
|
flags.StringVar(&opts.hostname, flagHostname, "", "Container hostname")
|
||||||
flags.SetAnnotation(flagHostname, "version", []string{"1.25"})
|
flags.SetAnnotation(flagHostname, "version", []string{"1.25"})
|
||||||
flags.Var(&opts.entrypoint, flagEntrypoint, "Overwrite the default ENTRYPOINT of the image")
|
flags.Var(&opts.entrypoint, flagEntrypoint, "Overwrite the default ENTRYPOINT of the image")
|
||||||
|
@ -576,6 +617,7 @@ func addServiceFlags(flags *pflag.FlagSet, opts *serviceOptions) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
flagCredentialSpec = "credential-spec"
|
||||||
flagPlacementPref = "placement-pref"
|
flagPlacementPref = "placement-pref"
|
||||||
flagPlacementPrefAdd = "placement-pref-add"
|
flagPlacementPrefAdd = "placement-pref-add"
|
||||||
flagPlacementPrefRemove = "placement-pref-rm"
|
flagPlacementPrefRemove = "placement-pref-rm"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package convert
|
package convert
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -39,6 +40,31 @@ func containerSpecFromGRPC(c *swarmapi.ContainerSpec) types.ContainerSpec {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Privileges
|
||||||
|
if c.Privileges != nil {
|
||||||
|
containerSpec.Privileges = &types.Privileges{}
|
||||||
|
|
||||||
|
if c.Privileges.CredentialSpec != nil {
|
||||||
|
containerSpec.Privileges.CredentialSpec = &types.CredentialSpec{}
|
||||||
|
switch c.Privileges.CredentialSpec.Source.(type) {
|
||||||
|
case *swarmapi.Privileges_CredentialSpec_File:
|
||||||
|
containerSpec.Privileges.CredentialSpec.File = c.Privileges.CredentialSpec.GetFile()
|
||||||
|
case *swarmapi.Privileges_CredentialSpec_Registry:
|
||||||
|
containerSpec.Privileges.CredentialSpec.Registry = c.Privileges.CredentialSpec.GetRegistry()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Privileges.SELinuxContext != nil {
|
||||||
|
containerSpec.Privileges.SELinuxContext = &types.SELinuxContext{
|
||||||
|
Disable: c.Privileges.SELinuxContext.Disable,
|
||||||
|
User: c.Privileges.SELinuxContext.User,
|
||||||
|
Type: c.Privileges.SELinuxContext.Type,
|
||||||
|
Role: c.Privileges.SELinuxContext.Role,
|
||||||
|
Level: c.Privileges.SELinuxContext.Level,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Mounts
|
// Mounts
|
||||||
for _, m := range c.Mounts {
|
for _, m := range c.Mounts {
|
||||||
mount := mounttypes.Mount{
|
mount := mounttypes.Mount{
|
||||||
|
@ -166,6 +192,40 @@ func containerToGRPC(c types.ContainerSpec) (*swarmapi.ContainerSpec, error) {
|
||||||
containerSpec.StopGracePeriod = gogotypes.DurationProto(*c.StopGracePeriod)
|
containerSpec.StopGracePeriod = gogotypes.DurationProto(*c.StopGracePeriod)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Privileges
|
||||||
|
if c.Privileges != nil {
|
||||||
|
containerSpec.Privileges = &swarmapi.Privileges{}
|
||||||
|
|
||||||
|
if c.Privileges.CredentialSpec != nil {
|
||||||
|
containerSpec.Privileges.CredentialSpec = &swarmapi.Privileges_CredentialSpec{}
|
||||||
|
|
||||||
|
if c.Privileges.CredentialSpec.File != "" && c.Privileges.CredentialSpec.Registry != "" {
|
||||||
|
return nil, errors.New("cannot specify both \"file\" and \"registry\" credential specs")
|
||||||
|
}
|
||||||
|
if c.Privileges.CredentialSpec.File != "" {
|
||||||
|
containerSpec.Privileges.CredentialSpec.Source = &swarmapi.Privileges_CredentialSpec_File{
|
||||||
|
File: c.Privileges.CredentialSpec.File,
|
||||||
|
}
|
||||||
|
} else if c.Privileges.CredentialSpec.Registry != "" {
|
||||||
|
containerSpec.Privileges.CredentialSpec.Source = &swarmapi.Privileges_CredentialSpec_Registry{
|
||||||
|
Registry: c.Privileges.CredentialSpec.Registry,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("must either provide \"file\" or \"registry\" for credential spec")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Privileges.SELinuxContext != nil {
|
||||||
|
containerSpec.Privileges.SELinuxContext = &swarmapi.Privileges_SELinuxContext{
|
||||||
|
Disable: c.Privileges.SELinuxContext.Disable,
|
||||||
|
User: c.Privileges.SELinuxContext.User,
|
||||||
|
Type: c.Privileges.SELinuxContext.Type,
|
||||||
|
Role: c.Privileges.SELinuxContext.Role,
|
||||||
|
Level: c.Privileges.SELinuxContext.Level,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Mounts
|
// Mounts
|
||||||
for _, m := range c.Mounts {
|
for _, m := range c.Mounts {
|
||||||
mount := swarmapi.Mount{
|
mount := swarmapi.Mount{
|
||||||
|
|
|
@ -351,6 +351,8 @@ func (c *containerConfig) hostConfig() *enginecontainer.HostConfig {
|
||||||
hc.DNSOptions = c.spec().DNSConfig.Options
|
hc.DNSOptions = c.spec().DNSConfig.Options
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.applyPrivileges(hc)
|
||||||
|
|
||||||
// The format of extra hosts on swarmkit is specified in:
|
// The format of extra hosts on swarmkit is specified in:
|
||||||
// http://man7.org/linux/man-pages/man5/hosts.5.html
|
// http://man7.org/linux/man-pages/man5/hosts.5.html
|
||||||
// IP_address canonical_hostname [aliases...]
|
// IP_address canonical_hostname [aliases...]
|
||||||
|
@ -600,6 +602,42 @@ func (c *containerConfig) networkCreateRequest(name string) (clustertypes.Networ
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *containerConfig) applyPrivileges(hc *enginecontainer.HostConfig) {
|
||||||
|
privileges := c.spec().Privileges
|
||||||
|
if privileges == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
credentials := privileges.CredentialSpec
|
||||||
|
if credentials != nil {
|
||||||
|
switch credentials.Source.(type) {
|
||||||
|
case *api.Privileges_CredentialSpec_File:
|
||||||
|
hc.SecurityOpt = append(hc.SecurityOpt, "credentialspec=file://"+credentials.GetFile())
|
||||||
|
case *api.Privileges_CredentialSpec_Registry:
|
||||||
|
hc.SecurityOpt = append(hc.SecurityOpt, "credentialspec=registry://"+credentials.GetRegistry())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
selinux := privileges.SELinuxContext
|
||||||
|
if selinux != nil {
|
||||||
|
if selinux.Disable {
|
||||||
|
hc.SecurityOpt = append(hc.SecurityOpt, "label=disable")
|
||||||
|
}
|
||||||
|
if selinux.User != "" {
|
||||||
|
hc.SecurityOpt = append(hc.SecurityOpt, "label=user:"+selinux.User)
|
||||||
|
}
|
||||||
|
if selinux.Role != "" {
|
||||||
|
hc.SecurityOpt = append(hc.SecurityOpt, "label=role:"+selinux.Role)
|
||||||
|
}
|
||||||
|
if selinux.Level != "" {
|
||||||
|
hc.SecurityOpt = append(hc.SecurityOpt, "label=level:"+selinux.Level)
|
||||||
|
}
|
||||||
|
if selinux.Type != "" {
|
||||||
|
hc.SecurityOpt = append(hc.SecurityOpt, "label=type:"+selinux.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c containerConfig) eventFilter() filters.Args {
|
func (c containerConfig) eventFilter() filters.Args {
|
||||||
filter := filters.NewArgs()
|
filter := filters.NewArgs()
|
||||||
filter.Add("type", events.ContainerEventType)
|
filter.Add("type", events.ContainerEventType)
|
||||||
|
|
Loading…
Add table
Reference in a new issue