Add support for apparmor/selinux
Set's an apparmor profile on the OCI spec if one is configred on the
worker.
Adds selinux labels to containers (only added if selinux is enabled on
the system).
This assumes that the specified apparmor profile is already loaded on
the system and does not try to load it or even check if it is loaded.
SELinux support requires the `selinux` build tag to be added.
Likewise, `runc` would require both the `apparmor` and `selinux` build
tags.
Signed-off-by: Brian Goff <cpuguy83@gmail.com>
Vendored go-selinux to v1.8.0
Fixed tests
Signed-off-by: Tibor Vass <tibor@docker.com>
(cherry picked from commit 68bb095353
)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
v0.9
parent
d5579348b8
commit
d9834f872c
|
@ -87,6 +87,10 @@ type OCIConfig struct {
|
|||
// Decoding this is delayed in order to remove the dependency from this
|
||||
// config pkg to stargz snapshotter's config pkg.
|
||||
StargzSnapshotterConfig toml.Primitive `toml:"stargzSnapshotter"`
|
||||
|
||||
// ApparmorProfile is the name of the apparmor profile that should be used to constrain build containers.
|
||||
// The profile should already be loaded (by a higher level system) before creating a worker.
|
||||
ApparmorProfile string `toml:"apparmor-profile"`
|
||||
}
|
||||
|
||||
type ContainerdConfig struct {
|
||||
|
@ -98,6 +102,10 @@ type ContainerdConfig struct {
|
|||
GCConfig
|
||||
NetworkConfig
|
||||
Snapshotter string `toml:"snapshotter"`
|
||||
|
||||
// ApparmorProfile is the name of the apparmor profile that should be used to constrain build containers.
|
||||
// The profile should already be loaded (by a higher level system) before creating a worker.
|
||||
ApparmorProfile string `toml:"apparmor-profile"`
|
||||
}
|
||||
|
||||
type GCPolicy struct {
|
||||
|
|
|
@ -91,6 +91,10 @@ func init() {
|
|||
Usage: "snapshotter name to use",
|
||||
Value: ctd.DefaultSnapshotter,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "containerd-worker-apparmor-profile",
|
||||
Usage: "set the name of the apparmor profile applied to containers",
|
||||
},
|
||||
}
|
||||
|
||||
if defaultConf.Workers.Containerd.GC == nil || *defaultConf.Workers.Containerd.GC {
|
||||
|
@ -192,6 +196,9 @@ func applyContainerdFlags(c *cli.Context, cfg *config.Config) error {
|
|||
if c.GlobalIsSet("containerd-worker-snapshotter") {
|
||||
cfg.Workers.Containerd.Snapshotter = c.GlobalString("containerd-worker-snapshotter")
|
||||
}
|
||||
if c.GlobalIsSet("containerd-worker-apparmor-profile") {
|
||||
cfg.Workers.Containerd.ApparmorProfile = c.GlobalString("containerd-worker-apparmor-profile")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -222,7 +229,7 @@ func containerdWorkerInitializer(c *cli.Context, common workerInitializerOpt) ([
|
|||
if cfg.Snapshotter != "" {
|
||||
snapshotter = cfg.Snapshotter
|
||||
}
|
||||
opt, err := containerd.NewWorkerOpt(common.config.Root, cfg.Address, snapshotter, cfg.Namespace, cfg.Labels, dns, nc, ctd.WithTimeout(60*time.Second))
|
||||
opt, err := containerd.NewWorkerOpt(common.config.Root, cfg.Address, snapshotter, cfg.Namespace, cfg.Labels, dns, nc, common.config.Workers.Containerd.ApparmorProfile, ctd.WithTimeout(60*time.Second))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -96,6 +96,10 @@ func init() {
|
|||
Usage: "name of specified oci worker binary",
|
||||
Value: defaultConf.Workers.OCI.Binary,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "oci-worker-apparmor-profile",
|
||||
Usage: "set the name of the apparmor profile applied to containers",
|
||||
},
|
||||
}
|
||||
n := "oci-worker-rootless"
|
||||
u := "enable rootless mode"
|
||||
|
@ -215,6 +219,9 @@ func applyOCIFlags(c *cli.Context, cfg *config.Config) error {
|
|||
if c.GlobalIsSet("oci-worker-proxy-snapshotter-path") {
|
||||
cfg.Workers.OCI.ProxySnapshotterPath = c.GlobalString("oci-worker-proxy-snapshotter-path")
|
||||
}
|
||||
if c.GlobalIsSet("oci-worker-apparmor-profile") {
|
||||
cfg.Workers.OCI.ApparmorProfile = c.GlobalString("oci-worker-apparmor-profile")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -268,7 +275,7 @@ func ociWorkerInitializer(c *cli.Context, common workerInitializerOpt) ([]worker
|
|||
},
|
||||
}
|
||||
|
||||
opt, err := runc.NewWorkerOpt(common.config.Root, snFactory, cfg.Rootless, processMode, cfg.Labels, idmapping, nc, dns, cfg.Binary)
|
||||
opt, err := runc.NewWorkerOpt(common.config.Root, snFactory, cfg.Rootless, processMode, cfg.Labels, idmapping, nc, dns, cfg.Binary, cfg.ApparmorProfile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -37,10 +37,11 @@ type containerdExecutor struct {
|
|||
dnsConfig *oci.DNSConfig
|
||||
running map[string]chan error
|
||||
mu sync.Mutex
|
||||
apparmorProfile string
|
||||
}
|
||||
|
||||
// New creates a new executor backed by connection to containerd API
|
||||
func New(client *containerd.Client, root, cgroup string, networkProviders map[pb.NetMode]network.Provider, dnsConfig *oci.DNSConfig) executor.Executor {
|
||||
func New(client *containerd.Client, root, cgroup string, networkProviders map[pb.NetMode]network.Provider, dnsConfig *oci.DNSConfig, apparmorProfile string) executor.Executor {
|
||||
// clean up old hosts/resolv.conf file. ignore errors
|
||||
os.RemoveAll(filepath.Join(root, "hosts"))
|
||||
os.RemoveAll(filepath.Join(root, "resolv.conf"))
|
||||
|
@ -52,6 +53,7 @@ func New(client *containerd.Client, root, cgroup string, networkProviders map[pb
|
|||
cgroupParent: cgroup,
|
||||
dnsConfig: dnsConfig,
|
||||
running: make(map[string]chan error),
|
||||
apparmorProfile: apparmorProfile,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -168,7 +170,7 @@ func (w *containerdExecutor) Run(ctx context.Context, id string, root executor.M
|
|||
opts = append(opts, containerdoci.WithCgroup(cgroupsPath))
|
||||
}
|
||||
processMode := oci.ProcessSandbox // FIXME(AkihiroSuda)
|
||||
spec, cleanup, err := oci.GenerateSpec(ctx, meta, mounts, id, resolvConf, hostsFile, namespace, processMode, nil, opts...)
|
||||
spec, cleanup, err := oci.GenerateSpec(ctx, meta, mounts, id, resolvConf, hostsFile, namespace, processMode, nil, w.apparmorProfile, opts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import (
|
|||
"github.com/moby/buildkit/snapshot"
|
||||
"github.com/moby/buildkit/util/network"
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/opencontainers/selinux/go-selinux"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
|
@ -35,7 +36,7 @@ const (
|
|||
|
||||
// GenerateSpec generates spec using containerd functionality.
|
||||
// opts are ignored for s.Process, s.Hostname, and s.Mounts .
|
||||
func GenerateSpec(ctx context.Context, meta executor.Meta, mounts []executor.Mount, id, resolvConf, hostsFile string, namespace network.Namespace, processMode ProcessMode, idmap *idtools.IdentityMapping, opts ...oci.SpecOpts) (*specs.Spec, func(), error) {
|
||||
func GenerateSpec(ctx context.Context, meta executor.Meta, mounts []executor.Mount, id, resolvConf, hostsFile string, namespace network.Namespace, processMode ProcessMode, idmap *idtools.IdentityMapping, apparmorProfile string, opts ...oci.SpecOpts) (*specs.Spec, func(), error) {
|
||||
c := &containers.Container{
|
||||
ID: id,
|
||||
}
|
||||
|
@ -52,7 +53,7 @@ func GenerateSpec(ctx context.Context, meta executor.Meta, mounts []executor.Mou
|
|||
return nil, nil, err
|
||||
}
|
||||
|
||||
if securityOpts, err := generateSecurityOpts(meta.SecurityMode); err == nil {
|
||||
if securityOpts, err := generateSecurityOpts(meta.SecurityMode, apparmorProfile); err == nil {
|
||||
opts = append(opts, securityOpts...)
|
||||
} else {
|
||||
return nil, nil, err
|
||||
|
@ -103,6 +104,9 @@ func GenerateSpec(ctx context.Context, meta executor.Meta, mounts []executor.Mou
|
|||
for _, f := range releasers {
|
||||
f()
|
||||
}
|
||||
if s.Process.SelinuxLabel != "" {
|
||||
selinux.ReleaseLabel(s.Process.SelinuxLabel)
|
||||
}
|
||||
}
|
||||
|
||||
for _, m := range mounts {
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/moby/buildkit/util/entitlements/security"
|
||||
"github.com/moby/buildkit/util/system"
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/opencontainers/selinux/go-selinux/label"
|
||||
)
|
||||
|
||||
func generateMountOpts(resolvConf, hostsFile string) ([]oci.SpecOpts, error) {
|
||||
|
@ -26,15 +27,32 @@ func generateMountOpts(resolvConf, hostsFile string) ([]oci.SpecOpts, error) {
|
|||
}
|
||||
|
||||
// generateSecurityOpts may affect mounts, so must be called after generateMountOpts
|
||||
func generateSecurityOpts(mode pb.SecurityMode) ([]oci.SpecOpts, error) {
|
||||
if mode == pb.SecurityMode_INSECURE {
|
||||
func generateSecurityOpts(mode pb.SecurityMode, apparmorProfile string) (opts []oci.SpecOpts, _ error) {
|
||||
switch mode {
|
||||
case pb.SecurityMode_INSECURE:
|
||||
return []oci.SpecOpts{
|
||||
security.WithInsecureSpec(),
|
||||
oci.WithWriteableCgroupfs,
|
||||
oci.WithWriteableSysfs,
|
||||
func(_ context.Context, _ oci.Client, _ *containers.Container, s *oci.Spec) error {
|
||||
var err error
|
||||
s.Process.SelinuxLabel, s.Linux.MountLabel, err = label.InitLabels([]string{"disable"})
|
||||
return err
|
||||
},
|
||||
}, nil
|
||||
} else if system.SeccompSupported() && mode == pb.SecurityMode_SANDBOX {
|
||||
return []oci.SpecOpts{withDefaultProfile()}, nil
|
||||
case pb.SecurityMode_SANDBOX:
|
||||
if system.SeccompSupported() {
|
||||
opts = append(opts, withDefaultProfile())
|
||||
}
|
||||
if apparmorProfile != "" {
|
||||
opts = append(opts, oci.WithApparmorProfile(apparmorProfile))
|
||||
}
|
||||
opts = append(opts, func(_ context.Context, _ oci.Client, _ *containers.Container, s *oci.Spec) error {
|
||||
var err error
|
||||
s.Process.SelinuxLabel, s.Linux.MountLabel, err = label.InitLabels(nil)
|
||||
return err
|
||||
})
|
||||
return opts, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ func generateMountOpts(resolvConf, hostsFile string) ([]oci.SpecOpts, error) {
|
|||
}
|
||||
|
||||
// generateSecurityOpts may affect mounts, so must be called after generateMountOpts
|
||||
func generateSecurityOpts(mode pb.SecurityMode) ([]oci.SpecOpts, error) {
|
||||
func generateSecurityOpts(mode pb.SecurityMode, apparmorProfile string) ([]oci.SpecOpts, error) {
|
||||
if mode == pb.SecurityMode_INSECURE {
|
||||
return nil, errors.New("no support for running in insecure mode on Windows")
|
||||
}
|
||||
|
|
|
@ -42,9 +42,10 @@ type Opt struct {
|
|||
ProcessMode oci.ProcessMode
|
||||
IdentityMapping *idtools.IdentityMapping
|
||||
// runc run --no-pivot (unrecommended)
|
||||
NoPivot bool
|
||||
DNS *oci.DNSConfig
|
||||
OOMScoreAdj *int
|
||||
NoPivot bool
|
||||
DNS *oci.DNSConfig
|
||||
OOMScoreAdj *int
|
||||
ApparmorProfile string
|
||||
}
|
||||
|
||||
var defaultCommandCandidates = []string{"buildkit-runc", "runc"}
|
||||
|
@ -62,6 +63,7 @@ type runcExecutor struct {
|
|||
oomScoreAdj *int
|
||||
running map[string]chan error
|
||||
mu sync.Mutex
|
||||
apparmorProfile string
|
||||
}
|
||||
|
||||
func New(opt Opt, networkProviders map[pb.NetMode]network.Provider) (executor.Executor, error) {
|
||||
|
@ -124,6 +126,7 @@ func New(opt Opt, networkProviders map[pb.NetMode]network.Provider) (executor.Ex
|
|||
dns: opt.DNS,
|
||||
oomScoreAdj: opt.OOMScoreAdj,
|
||||
running: make(map[string]chan error),
|
||||
apparmorProfile: opt.ApparmorProfile,
|
||||
}
|
||||
return w, nil
|
||||
}
|
||||
|
@ -253,7 +256,7 @@ func (w *runcExecutor) Run(ctx context.Context, id string, root executor.Mount,
|
|||
}
|
||||
opts = append(opts, containerdoci.WithCgroup(cgroupsPath))
|
||||
}
|
||||
spec, cleanup, err := oci.GenerateSpec(ctx, meta, mounts, id, resolvConf, hostsFile, namespace, w.processMode, w.idmap, opts...)
|
||||
spec, cleanup, err := oci.GenerateSpec(ctx, meta, mounts, id, resolvConf, hostsFile, namespace, w.processMode, w.idmap, w.apparmorProfile, opts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
1
go.mod
1
go.mod
|
@ -46,6 +46,7 @@ require (
|
|||
github.com/opencontainers/image-spec v1.0.1
|
||||
github.com/opencontainers/runc v1.0.0-rc92
|
||||
github.com/opencontainers/runtime-spec v1.0.3-0.20200728170252-4d89ac9fbff6
|
||||
github.com/opencontainers/selinux v1.8.0
|
||||
github.com/opentracing-contrib/go-stdlib v1.0.0
|
||||
github.com/opentracing/opentracing-go v1.2.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
|
|
|
@ -23,16 +23,16 @@ import (
|
|||
)
|
||||
|
||||
// NewWorkerOpt creates a WorkerOpt.
|
||||
func NewWorkerOpt(root string, address, snapshotterName, ns string, labels map[string]string, dns *oci.DNSConfig, nopt netproviders.Opt, opts ...containerd.ClientOpt) (base.WorkerOpt, error) {
|
||||
func NewWorkerOpt(root string, address, snapshotterName, ns string, labels map[string]string, dns *oci.DNSConfig, nopt netproviders.Opt, apparmorProfile string, opts ...containerd.ClientOpt) (base.WorkerOpt, error) {
|
||||
opts = append(opts, containerd.WithDefaultNamespace(ns))
|
||||
client, err := containerd.New(address, opts...)
|
||||
if err != nil {
|
||||
return base.WorkerOpt{}, errors.Wrapf(err, "failed to connect client to %q . make sure containerd is running", address)
|
||||
}
|
||||
return newContainerd(root, client, snapshotterName, ns, labels, dns, nopt)
|
||||
return newContainerd(root, client, snapshotterName, ns, labels, dns, nopt, apparmorProfile)
|
||||
}
|
||||
|
||||
func newContainerd(root string, client *containerd.Client, snapshotterName, ns string, labels map[string]string, dns *oci.DNSConfig, nopt netproviders.Opt) (base.WorkerOpt, error) {
|
||||
func newContainerd(root string, client *containerd.Client, snapshotterName, ns string, labels map[string]string, dns *oci.DNSConfig, nopt netproviders.Opt, apparmorProfile string) (base.WorkerOpt, error) {
|
||||
if strings.Contains(snapshotterName, "/") {
|
||||
return base.WorkerOpt{}, errors.Errorf("bad snapshotter name: %q", snapshotterName)
|
||||
}
|
||||
|
@ -104,7 +104,7 @@ func newContainerd(root string, client *containerd.Client, snapshotterName, ns s
|
|||
ID: id,
|
||||
Labels: xlabels,
|
||||
MetadataStore: md,
|
||||
Executor: containerdexecutor.New(client, root, "", np, dns),
|
||||
Executor: containerdexecutor.New(client, root, "", np, dns, apparmorProfile),
|
||||
Snapshotter: snap,
|
||||
ContentStore: cs,
|
||||
Applier: winlayers.NewFileSystemApplierWithWindows(cs, df),
|
||||
|
|
|
@ -30,7 +30,7 @@ func newWorkerOpt(t *testing.T, addr string) (base.WorkerOpt, func()) {
|
|||
tmpdir, err := ioutil.TempDir("", "workertest")
|
||||
require.NoError(t, err)
|
||||
cleanup := func() { os.RemoveAll(tmpdir) }
|
||||
workerOpt, err := NewWorkerOpt(tmpdir, addr, "overlayfs", "buildkit-test", nil, nil, netproviders.Opt{Mode: "host"})
|
||||
workerOpt, err := NewWorkerOpt(tmpdir, addr, "overlayfs", "buildkit-test", nil, nil, netproviders.Opt{Mode: "host"}, "")
|
||||
require.NoError(t, err)
|
||||
return workerOpt, cleanup
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ type SnapshotterFactory struct {
|
|||
}
|
||||
|
||||
// NewWorkerOpt creates a WorkerOpt.
|
||||
func NewWorkerOpt(root string, snFactory SnapshotterFactory, rootless bool, processMode oci.ProcessMode, labels map[string]string, idmap *idtools.IdentityMapping, nopt netproviders.Opt, dns *oci.DNSConfig, binary string) (base.WorkerOpt, error) {
|
||||
func NewWorkerOpt(root string, snFactory SnapshotterFactory, rootless bool, processMode oci.ProcessMode, labels map[string]string, idmap *idtools.IdentityMapping, nopt netproviders.Opt, dns *oci.DNSConfig, binary, apparmorProfile string) (base.WorkerOpt, error) {
|
||||
var opt base.WorkerOpt
|
||||
name := "runc-" + snFactory.Name
|
||||
root = filepath.Join(root, name)
|
||||
|
@ -62,6 +62,7 @@ func NewWorkerOpt(root string, snFactory SnapshotterFactory, rootless bool, proc
|
|||
ProcessMode: processMode,
|
||||
IdentityMapping: idmap,
|
||||
DNS: dns,
|
||||
ApparmorProfile: apparmorProfile,
|
||||
}, np)
|
||||
if err != nil {
|
||||
return opt, err
|
||||
|
|
|
@ -40,7 +40,7 @@ func newWorkerOpt(t *testing.T, processMode oci.ProcessMode) (base.WorkerOpt, fu
|
|||
},
|
||||
}
|
||||
rootless := false
|
||||
workerOpt, err := NewWorkerOpt(tmpdir, snFactory, rootless, processMode, nil, nil, netproviders.Opt{Mode: "host"}, nil, "")
|
||||
workerOpt, err := NewWorkerOpt(tmpdir, snFactory, rootless, processMode, nil, nil, netproviders.Opt{Mode: "host"}, nil, "", "")
|
||||
require.NoError(t, err)
|
||||
|
||||
return workerOpt, cleanup
|
||||
|
|
Loading…
Reference in New Issue