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>v0.8
parent
8142d66b5e
commit
68bb095353
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ type Opt struct {
|
|||
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
|
||||
|
|
4
go.sum
4
go.sum
|
@ -724,6 +724,8 @@ github.com/opencontainers/runtime-spec v1.0.3-0.20200728170252-4d89ac9fbff6/go.m
|
|||
github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
|
||||
github.com/opencontainers/selinux v1.6.0 h1:+bIAS/Za3q5FTwWym4fTB0vObnfCf3G/NC7K6Jx62mY=
|
||||
github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE=
|
||||
github.com/opencontainers/selinux v1.8.0 h1:+77ba4ar4jsCbL1GLbFL8fFM57w6suPfSS9PDLDY7KM=
|
||||
github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo=
|
||||
github.com/opentracing-contrib/go-stdlib v1.0.0 h1:TBS7YuVotp8myLon4Pv7BtCBzOTo1DeZCld0Z63mW2w=
|
||||
github.com/opentracing-contrib/go-stdlib v1.0.0/go.mod h1:qtI1ogk+2JhVPIXVc6q+NHziSmy2W5GbdQZFUHADCBU=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
|
@ -919,6 +921,8 @@ github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17
|
|||
github.com/vmware/govmomi v0.20.3/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU=
|
||||
github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243 h1:R43TdZy32XXSXjJn7M/HhALJ9imq6ztLnChfYJpVDnM=
|
||||
github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
||||
github.com/willf/bitset v1.1.11 h1:N7Z7E9UvjW+sGsEl7k/SJrvY2reP1A07MrGuCjIOjRE=
|
||||
github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI=
|
||||
github.com/xanzy/go-gitlab v0.31.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug=
|
||||
github.com/xanzy/go-gitlab v0.32.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
|
|
|
@ -5,9 +5,6 @@ This package uses a selinux build tag to enable the selinux functionality. This
|
|||
allows non-linux and linux users who do not have selinux support to still use
|
||||
tools that rely on this library.
|
||||
|
||||
To compile with full selinux support use the -tags=selinux option in your build
|
||||
and test commands.
|
||||
|
||||
Usage:
|
||||
|
||||
import "github.com/opencontainers/selinux/go-selinux"
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
// +build selinux,linux
|
||||
|
||||
package label
|
||||
|
||||
import (
|
||||
|
@ -27,14 +25,14 @@ var ErrIncompatibleLabel = errors.New("Bad SELinux option z and Z can not be use
|
|||
// the container. A list of options can be passed into this function to alter
|
||||
// the labels. The labels returned will include a random MCS String, that is
|
||||
// guaranteed to be unique.
|
||||
func InitLabels(options []string) (plabel string, mlabel string, Err error) {
|
||||
func InitLabels(options []string) (plabel string, mlabel string, retErr error) {
|
||||
if !selinux.GetEnabled() {
|
||||
return "", "", nil
|
||||
}
|
||||
processLabel, mountLabel := selinux.ContainerLabels()
|
||||
if processLabel != "" {
|
||||
defer func() {
|
||||
if Err != nil {
|
||||
if retErr != nil {
|
||||
selinux.ReleaseLabel(mountLabel)
|
||||
}
|
||||
}()
|
||||
|
@ -57,7 +55,6 @@ func InitLabels(options []string) (plabel string, mlabel string, Err error) {
|
|||
con := strings.SplitN(opt, ":", 2)
|
||||
if !validOptions[con[0]] {
|
||||
return "", "", errors.Errorf("Bad label option %q, valid options 'disable, user, role, level, type, filetype'", con[0])
|
||||
|
||||
}
|
||||
if con[0] == "filetype" {
|
||||
mcon["type"] = con[1]
|
|
@ -1,4 +1,4 @@
|
|||
// +build !selinux !linux
|
||||
// +build !linux
|
||||
|
||||
package label
|
||||
|
||||
|
|
|
@ -30,6 +30,11 @@ var (
|
|||
// ErrLevelSyntax is returned when a sensitivity or category do not have correct syntax in a level
|
||||
ErrLevelSyntax = errors.New("invalid level syntax")
|
||||
|
||||
// ErrContextMissing is returned if a requested context is not found in a file.
|
||||
ErrContextMissing = errors.New("context does not have a match")
|
||||
// ErrVerifierNil is returned when a context verifier function is nil.
|
||||
ErrVerifierNil = errors.New("verifier function is nil")
|
||||
|
||||
// CategoryRange allows the upper bound on the category range to be adjusted
|
||||
CategoryRange = DefaultCategoryRange
|
||||
)
|
||||
|
@ -63,8 +68,12 @@ func FileLabel(fpath string) (string, error) {
|
|||
return fileLabel(fpath)
|
||||
}
|
||||
|
||||
// SetFSCreateLabel tells kernel the label to create all file system objects
|
||||
// created by this task. Setting label="" to return to default.
|
||||
// SetFSCreateLabel tells the kernel what label to use for all file system objects
|
||||
// created by this task.
|
||||
// Set the label to an empty string to return to the default label. Calls to SetFSCreateLabel
|
||||
// should be wrapped in runtime.LockOSThread()/runtime.UnlockOSThread() until file system
|
||||
// objects created by this task are finished to guarantee another goroutine does not migrate
|
||||
// to the current thread before execution is complete.
|
||||
func SetFSCreateLabel(label string) error {
|
||||
return setFSCreateLabel(label)
|
||||
}
|
||||
|
@ -113,19 +122,27 @@ func CalculateGlbLub(sourceRange, targetRange string) (string, error) {
|
|||
}
|
||||
|
||||
// SetExecLabel sets the SELinux label that the kernel will use for any programs
|
||||
// that are executed by the current process thread, or an error.
|
||||
// that are executed by the current process thread, or an error. Calls to SetExecLabel
|
||||
// should be wrapped in runtime.LockOSThread()/runtime.UnlockOSThread() until execution
|
||||
// of the program is finished to guarantee another goroutine does not migrate to the current
|
||||
// thread before execution is complete.
|
||||
func SetExecLabel(label string) error {
|
||||
return setExecLabel(label)
|
||||
}
|
||||
|
||||
// SetTaskLabel sets the SELinux label for the current thread, or an error.
|
||||
// This requires the dyntransition permission.
|
||||
// This requires the dyntransition permission. Calls to SetTaskLabel should
|
||||
// be wrapped in runtime.LockOSThread()/runtime.UnlockOSThread() to guarantee
|
||||
// the current thread does not run in a new mislabeled thread.
|
||||
func SetTaskLabel(label string) error {
|
||||
return setTaskLabel(label)
|
||||
}
|
||||
|
||||
// SetSocketLabel takes a process label and tells the kernel to assign the
|
||||
// label to the next socket that gets created
|
||||
// label to the next socket that gets created. Calls to SetSocketLabel
|
||||
// should be wrapped in runtime.LockOSThread()/runtime.UnlockOSThread() until
|
||||
// the the socket is created to guarantee another goroutine does not migrate
|
||||
// to the current thread before execution is complete.
|
||||
func SetSocketLabel(label string) error {
|
||||
return setSocketLabel(label)
|
||||
}
|
||||
|
@ -141,7 +158,10 @@ func PeerLabel(fd uintptr) (string, error) {
|
|||
}
|
||||
|
||||
// SetKeyLabel takes a process label and tells the kernel to assign the
|
||||
// label to the next kernel keyring that gets created
|
||||
// label to the next kernel keyring that gets created. Calls to SetKeyLabel
|
||||
// should be wrapped in runtime.LockOSThread()/runtime.UnlockOSThread() until
|
||||
// the kernel keyring is created to guarantee another goroutine does not migrate
|
||||
// to the current thread before execution is complete.
|
||||
func SetKeyLabel(label string) error {
|
||||
return setKeyLabel(label)
|
||||
}
|
||||
|
@ -247,3 +267,12 @@ func DupSecOpt(src string) ([]string, error) {
|
|||
func DisableSecOpt() []string {
|
||||
return disableSecOpt()
|
||||
}
|
||||
|
||||
// GetDefaultContextWithLevel gets a single context for the specified SELinux user
|
||||
// identity that is reachable from the specified scon context. The context is based
|
||||
// on the per-user /etc/selinux/{SELINUXTYPE}/contexts/users/<username> if it exists,
|
||||
// and falls back to the global /etc/selinux/{SELINUXTYPE}/contexts/default_contexts
|
||||
// file.
|
||||
func GetDefaultContextWithLevel(user, level, scon string) (string, error) {
|
||||
return getDefaultContextWithLevel(user, level, scon)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
// +build selinux,linux
|
||||
|
||||
package selinux
|
||||
|
||||
import (
|
||||
|
@ -28,6 +26,8 @@ const (
|
|||
minSensLen = 2
|
||||
contextFile = "/usr/share/containers/selinux/contexts"
|
||||
selinuxDir = "/etc/selinux/"
|
||||
selinuxUsersDir = "contexts/users"
|
||||
defaultContexts = "contexts/default_contexts"
|
||||
selinuxConfig = selinuxDir + "config"
|
||||
selinuxfsMount = "/sys/fs/selinux"
|
||||
selinuxTypeTag = "SELINUXTYPE"
|
||||
|
@ -35,6 +35,8 @@ const (
|
|||
xattrNameSelinux = "security.selinux"
|
||||
)
|
||||
|
||||
var policyRoot = filepath.Join(selinuxDir, readConfig(selinuxTypeTag))
|
||||
|
||||
type selinuxState struct {
|
||||
enabledSet bool
|
||||
enabled bool
|
||||
|
@ -54,6 +56,13 @@ type mlsRange struct {
|
|||
high *level
|
||||
}
|
||||
|
||||
type defaultSECtx struct {
|
||||
user, level, scon string
|
||||
userRdr, defaultRdr io.Reader
|
||||
|
||||
verifier func(string) error
|
||||
}
|
||||
|
||||
type levelItem byte
|
||||
|
||||
const (
|
||||
|
@ -111,7 +120,7 @@ func verifySELinuxfsMount(mnt string) bool {
|
|||
if err == nil {
|
||||
break
|
||||
}
|
||||
if err == unix.EAGAIN {
|
||||
if err == unix.EAGAIN || err == unix.EINTR {
|
||||
continue
|
||||
}
|
||||
return false
|
||||
|
@ -205,28 +214,16 @@ func getEnabled() bool {
|
|||
}
|
||||
|
||||
func readConfig(target string) string {
|
||||
var (
|
||||
val, key string
|
||||
bufin *bufio.Reader
|
||||
)
|
||||
|
||||
in, err := os.Open(selinuxConfig)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
defer in.Close()
|
||||
|
||||
bufin = bufio.NewReader(in)
|
||||
scanner := bufio.NewScanner(in)
|
||||
|
||||
for done := false; !done; {
|
||||
var line string
|
||||
if line, err = bufin.ReadString('\n'); err != nil {
|
||||
if err != io.EOF {
|
||||
return ""
|
||||
}
|
||||
done = true
|
||||
}
|
||||
line = strings.TrimSpace(line)
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
if len(line) == 0 {
|
||||
// Skip blank lines
|
||||
continue
|
||||
|
@ -236,7 +233,7 @@ func readConfig(target string) string {
|
|||
continue
|
||||
}
|
||||
if groups := assignRegex.FindStringSubmatch(line); groups != nil {
|
||||
key, val = strings.TrimSpace(groups[1]), strings.TrimSpace(groups[2])
|
||||
key, val := strings.TrimSpace(groups[1]), strings.TrimSpace(groups[2])
|
||||
if key == target {
|
||||
return strings.Trim(val, "\"")
|
||||
}
|
||||
|
@ -245,16 +242,18 @@ func readConfig(target string) string {
|
|||
return ""
|
||||
}
|
||||
|
||||
func getSELinuxPolicyRoot() string {
|
||||
return filepath.Join(selinuxDir, readConfig(selinuxTypeTag))
|
||||
}
|
||||
|
||||
func isProcHandle(fh *os.File) error {
|
||||
var buf unix.Statfs_t
|
||||
|
||||
for {
|
||||
err := unix.Fstatfs(int(fh.Fd()), &buf)
|
||||
if err != nil {
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
if err != unix.EINTR {
|
||||
return errors.Wrapf(err, "statfs(%q) failed", fh.Name())
|
||||
}
|
||||
}
|
||||
if buf.Type != unix.PROC_SUPER_MAGIC {
|
||||
return errors.Errorf("file %q is not on procfs", fh.Name())
|
||||
}
|
||||
|
@ -307,9 +306,16 @@ func setFileLabel(fpath string, label string) error {
|
|||
if fpath == "" {
|
||||
return ErrEmptyPath
|
||||
}
|
||||
if err := unix.Lsetxattr(fpath, xattrNameSelinux, []byte(label), 0); err != nil {
|
||||
for {
|
||||
err := unix.Lsetxattr(fpath, xattrNameSelinux, []byte(label), 0)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
if err != unix.EINTR {
|
||||
return errors.Wrapf(err, "failed to set file label on %s", fpath)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -751,7 +757,7 @@ func reserveLabel(label string) {
|
|||
if len(label) != 0 {
|
||||
con := strings.SplitN(label, ":", 4)
|
||||
if len(con) > 3 {
|
||||
mcsAdd(con[3])
|
||||
_ = mcsAdd(con[3])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -828,11 +834,11 @@ func intToMcs(id int, catRange uint32) string {
|
|||
}
|
||||
|
||||
for ORD > TIER {
|
||||
ORD = ORD - TIER
|
||||
ORD -= TIER
|
||||
TIER--
|
||||
}
|
||||
TIER = SETSIZE - TIER
|
||||
ORD = ORD + TIER
|
||||
ORD += TIER
|
||||
return fmt.Sprintf("s0:c%d,c%d", TIER, ORD)
|
||||
}
|
||||
|
||||
|
@ -844,17 +850,15 @@ func uniqMcs(catRange uint32) string {
|
|||
)
|
||||
|
||||
for {
|
||||
binary.Read(rand.Reader, binary.LittleEndian, &n)
|
||||
_ = binary.Read(rand.Reader, binary.LittleEndian, &n)
|
||||
c1 = n % catRange
|
||||
binary.Read(rand.Reader, binary.LittleEndian, &n)
|
||||
_ = binary.Read(rand.Reader, binary.LittleEndian, &n)
|
||||
c2 = n % catRange
|
||||
if c1 == c2 {
|
||||
continue
|
||||
} else {
|
||||
if c1 > c2 {
|
||||
} else if c1 > c2 {
|
||||
c1, c2 = c2, c1
|
||||
}
|
||||
}
|
||||
mcs = fmt.Sprintf("s0:c%d,c%d", c1, c2)
|
||||
if err := mcsAdd(mcs); err != nil {
|
||||
continue
|
||||
|
@ -884,18 +888,13 @@ func openContextFile() (*os.File, error) {
|
|||
if f, err := os.Open(contextFile); err == nil {
|
||||
return f, nil
|
||||
}
|
||||
lxcPath := filepath.Join(getSELinuxPolicyRoot(), "/contexts/lxc_contexts")
|
||||
lxcPath := filepath.Join(policyRoot, "/contexts/lxc_contexts")
|
||||
return os.Open(lxcPath)
|
||||
}
|
||||
|
||||
var labels = loadLabels()
|
||||
|
||||
func loadLabels() map[string]string {
|
||||
var (
|
||||
val, key string
|
||||
bufin *bufio.Reader
|
||||
)
|
||||
|
||||
labels := make(map[string]string)
|
||||
in, err := openContextFile()
|
||||
if err != nil {
|
||||
|
@ -903,18 +902,10 @@ func loadLabels() map[string]string {
|
|||
}
|
||||
defer in.Close()
|
||||
|
||||
bufin = bufio.NewReader(in)
|
||||
scanner := bufio.NewScanner(in)
|
||||
|
||||
for done := false; !done; {
|
||||
var line string
|
||||
if line, err = bufin.ReadString('\n'); err != nil {
|
||||
if err == io.EOF {
|
||||
done = true
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
line = strings.TrimSpace(line)
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
if len(line) == 0 {
|
||||
// Skip blank lines
|
||||
continue
|
||||
|
@ -924,7 +915,7 @@ func loadLabels() map[string]string {
|
|||
continue
|
||||
}
|
||||
if groups := assignRegex.FindStringSubmatch(line); groups != nil {
|
||||
key, val = strings.TrimSpace(groups[1]), strings.TrimSpace(groups[2])
|
||||
key, val := strings.TrimSpace(groups[1]), strings.TrimSpace(groups[2])
|
||||
labels[key] = strings.Trim(val, "\"")
|
||||
}
|
||||
}
|
||||
|
@ -1015,7 +1006,7 @@ func copyLevel(src, dest string) (string, error) {
|
|||
return "", err
|
||||
}
|
||||
mcsDelete(tcon["level"])
|
||||
mcsAdd(scon["level"])
|
||||
_ = mcsAdd(scon["level"])
|
||||
tcon["level"] = scon["level"]
|
||||
return tcon.Get(), nil
|
||||
}
|
||||
|
@ -1095,3 +1086,124 @@ func dupSecOpt(src string) ([]string, error) {
|
|||
func disableSecOpt() []string {
|
||||
return []string{"disable"}
|
||||
}
|
||||
|
||||
// findUserInContext scans the reader for a valid SELinux context
|
||||
// match that is verified with the verifier. Invalid contexts are
|
||||
// skipped. It returns a matched context or an empty string if no
|
||||
// match is found. If a scanner error occurs, it is returned.
|
||||
func findUserInContext(context Context, r io.Reader, verifier func(string) error) (string, error) {
|
||||
fromRole := context["role"]
|
||||
fromType := context["type"]
|
||||
scanner := bufio.NewScanner(r)
|
||||
|
||||
for scanner.Scan() {
|
||||
fromConns := strings.Fields(scanner.Text())
|
||||
if len(fromConns) == 0 {
|
||||
// Skip blank lines
|
||||
continue
|
||||
}
|
||||
|
||||
line := fromConns[0]
|
||||
|
||||
if line[0] == ';' || line[0] == '#' {
|
||||
// Skip comments
|
||||
continue
|
||||
}
|
||||
|
||||
// user context files contexts are formatted as
|
||||
// role_r:type_t:s0 where the user is missing.
|
||||
lineArr := strings.SplitN(line, ":", 4)
|
||||
// skip context with typo, or role and type do not match
|
||||
if len(lineArr) != 3 ||
|
||||
lineArr[0] != fromRole ||
|
||||
lineArr[1] != fromType {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, cc := range fromConns[1:] {
|
||||
toConns := strings.SplitN(cc, ":", 4)
|
||||
if len(toConns) != 3 {
|
||||
continue
|
||||
}
|
||||
|
||||
context["role"] = toConns[0]
|
||||
context["type"] = toConns[1]
|
||||
|
||||
outConn := context.get()
|
||||
if err := verifier(outConn); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
return outConn, nil
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return "", errors.Wrap(err, "failed to scan for context")
|
||||
}
|
||||
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func getDefaultContextFromReaders(c *defaultSECtx) (string, error) {
|
||||
if c.verifier == nil {
|
||||
return "", ErrVerifierNil
|
||||
}
|
||||
|
||||
context, err := newContext(c.scon)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "failed to create label for %s", c.scon)
|
||||
}
|
||||
|
||||
// set so the verifier validates the matched context with the provided user and level.
|
||||
context["user"] = c.user
|
||||
context["level"] = c.level
|
||||
|
||||
conn, err := findUserInContext(context, c.userRdr, c.verifier)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if conn != "" {
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
conn, err = findUserInContext(context, c.defaultRdr, c.verifier)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if conn != "" {
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
return "", errors.Wrapf(ErrContextMissing, "context not found: %q", c.scon)
|
||||
}
|
||||
|
||||
func getDefaultContextWithLevel(user, level, scon string) (string, error) {
|
||||
userPath := filepath.Join(policyRoot, selinuxUsersDir, user)
|
||||
defaultPath := filepath.Join(policyRoot, defaultContexts)
|
||||
|
||||
fu, err := os.Open(userPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer fu.Close()
|
||||
|
||||
fd, err := os.Open(defaultPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer fd.Close()
|
||||
|
||||
c := defaultSECtx{
|
||||
user: user,
|
||||
level: level,
|
||||
scon: scon,
|
||||
userRdr: fu,
|
||||
defaultRdr: fd,
|
||||
verifier: securityCheckContext,
|
||||
}
|
||||
|
||||
return getDefaultContextFromReaders(&c)
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// +build !selinux !linux
|
||||
// +build !linux
|
||||
|
||||
package selinux
|
||||
|
||||
|
@ -146,3 +146,7 @@ func dupSecOpt(src string) ([]string, error) {
|
|||
func disableSecOpt() []string {
|
||||
return []string{"disable"}
|
||||
}
|
||||
|
||||
func getDefaultContextWithLevel(user, level, scon string) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
// +build selinux,linux
|
||||
|
||||
package selinux
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// Returns a []byte slice if the xattr is set and nil otherwise
|
||||
// Requires path and its attribute as arguments
|
||||
func lgetxattr(path string, attr string) ([]byte, error) {
|
||||
// Start with a 128 length byte array
|
||||
dest := make([]byte, 128)
|
||||
sz, errno := unix.Lgetxattr(path, attr, dest)
|
||||
for errno == unix.ERANGE {
|
||||
// Buffer too small, use zero-sized buffer to get the actual size
|
||||
sz, errno = unix.Lgetxattr(path, attr, []byte{})
|
||||
if errno != nil {
|
||||
return nil, errno
|
||||
}
|
||||
|
||||
dest = make([]byte, sz)
|
||||
sz, errno = unix.Lgetxattr(path, attr, dest)
|
||||
}
|
||||
if errno != nil {
|
||||
return nil, errno
|
||||
}
|
||||
|
||||
return dest[:sz], nil
|
||||
}
|
38
vendor/github.com/opencontainers/selinux/go-selinux/xattrs_linux.go
generated
vendored
Normal file
38
vendor/github.com/opencontainers/selinux/go-selinux/xattrs_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
package selinux
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// lgetxattr returns a []byte slice containing the value of
|
||||
// an extended attribute attr set for path.
|
||||
func lgetxattr(path, attr string) ([]byte, error) {
|
||||
// Start with a 128 length byte array
|
||||
dest := make([]byte, 128)
|
||||
sz, errno := doLgetxattr(path, attr, dest)
|
||||
for errno == unix.ERANGE {
|
||||
// Buffer too small, use zero-sized buffer to get the actual size
|
||||
sz, errno = doLgetxattr(path, attr, []byte{})
|
||||
if errno != nil {
|
||||
return nil, errno
|
||||
}
|
||||
|
||||
dest = make([]byte, sz)
|
||||
sz, errno = doLgetxattr(path, attr, dest)
|
||||
}
|
||||
if errno != nil {
|
||||
return nil, errno
|
||||
}
|
||||
|
||||
return dest[:sz], nil
|
||||
}
|
||||
|
||||
// doLgetxattr is a wrapper that retries on EINTR
|
||||
func doLgetxattr(path, attr string, dest []byte) (int, error) {
|
||||
for {
|
||||
sz, err := unix.Lgetxattr(path, attr, dest)
|
||||
if err != unix.EINTR {
|
||||
return sz, err
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,17 +20,16 @@ type WalkFunc = filepath.WalkFunc
|
|||
//
|
||||
// Note that this implementation only supports primitive error handling:
|
||||
//
|
||||
// * no errors are ever passed to WalkFn
|
||||
// - no errors are ever passed to WalkFn;
|
||||
//
|
||||
// * once a walkFn returns any error, all further processing stops
|
||||
// - once a walkFn returns any error, all further processing stops
|
||||
// and the error is returned to the caller of Walk;
|
||||
//
|
||||
// * filepath.SkipDir is not supported;
|
||||
// - filepath.SkipDir is not supported;
|
||||
//
|
||||
// * if more than one walkFn instance will return an error, only one
|
||||
// - if more than one walkFn instance will return an error, only one
|
||||
// of such errors will be propagated and returned by Walk, others
|
||||
// will be silently discarded.
|
||||
//
|
||||
func Walk(root string, walkFn WalkFunc) error {
|
||||
return WalkN(root, walkFn, runtime.NumCPU()*2)
|
||||
}
|
||||
|
@ -38,6 +37,8 @@ func Walk(root string, walkFn WalkFunc) error {
|
|||
// WalkN is a wrapper for filepath.Walk which can call multiple walkFn
|
||||
// in parallel, allowing to handle each item concurrently. A maximum of
|
||||
// num walkFn will be called at any one time.
|
||||
//
|
||||
// Please see Walk documentation for caveats of using this function.
|
||||
func WalkN(root string, walkFn WalkFunc, num int) error {
|
||||
// make sure limit is sensible
|
||||
if num < 1 {
|
||||
|
|
|
@ -1,191 +0,0 @@
|
|||
# MAKEFILE
|
||||
#
|
||||
# @author Nicola Asuni <info@tecnick.com>
|
||||
# @link https://github.com/willf/bitset
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# List special make targets that are not associated with files
|
||||
.PHONY: help all test format fmtcheck vet lint coverage cyclo ineffassign misspell structcheck varcheck errcheck gosimple astscan qa deps clean nuke
|
||||
|
||||
# Use bash as shell (Note: Ubuntu now uses dash which doesn't support PIPESTATUS).
|
||||
SHELL=/bin/bash
|
||||
|
||||
# CVS path (path to the parent dir containing the project)
|
||||
CVSPATH=github.com/willf
|
||||
|
||||
# Project owner
|
||||
OWNER=willf
|
||||
|
||||
# Project vendor
|
||||
VENDOR=willf
|
||||
|
||||
# Project name
|
||||
PROJECT=bitset
|
||||
|
||||
# Project version
|
||||
VERSION=$(shell cat VERSION)
|
||||
|
||||
# Name of RPM or DEB package
|
||||
PKGNAME=${VENDOR}-${PROJECT}
|
||||
|
||||
# Current directory
|
||||
CURRENTDIR=$(shell pwd)
|
||||
|
||||
# GO lang path
|
||||
ifneq ($(GOPATH),)
|
||||
ifeq ($(findstring $(GOPATH),$(CURRENTDIR)),)
|
||||
# the defined GOPATH is not valid
|
||||
GOPATH=
|
||||
endif
|
||||
endif
|
||||
ifeq ($(GOPATH),)
|
||||
# extract the GOPATH
|
||||
GOPATH=$(firstword $(subst /src/, ,$(CURRENTDIR)))
|
||||
endif
|
||||
|
||||
# --- MAKE TARGETS ---
|
||||
|
||||
# Display general help about this command
|
||||
help:
|
||||
@echo ""
|
||||
@echo "$(PROJECT) Makefile."
|
||||
@echo "GOPATH=$(GOPATH)"
|
||||
@echo "The following commands are available:"
|
||||
@echo ""
|
||||
@echo " make qa : Run all the tests"
|
||||
@echo " make test : Run the unit tests"
|
||||
@echo ""
|
||||
@echo " make format : Format the source code"
|
||||
@echo " make fmtcheck : Check if the source code has been formatted"
|
||||
@echo " make vet : Check for suspicious constructs"
|
||||
@echo " make lint : Check for style errors"
|
||||
@echo " make coverage : Generate the coverage report"
|
||||
@echo " make cyclo : Generate the cyclomatic complexity report"
|
||||
@echo " make ineffassign : Detect ineffectual assignments"
|
||||
@echo " make misspell : Detect commonly misspelled words in source files"
|
||||
@echo " make structcheck : Find unused struct fields"
|
||||
@echo " make varcheck : Find unused global variables and constants"
|
||||
@echo " make errcheck : Check that error return values are used"
|
||||
@echo " make gosimple : Suggest code simplifications"
|
||||
@echo " make astscan : GO AST scanner"
|
||||
@echo ""
|
||||
@echo " make docs : Generate source code documentation"
|
||||
@echo ""
|
||||
@echo " make deps : Get the dependencies"
|
||||
@echo " make clean : Remove any build artifact"
|
||||
@echo " make nuke : Deletes any intermediate file"
|
||||
@echo ""
|
||||
|
||||
# Alias for help target
|
||||
all: help
|
||||
|
||||
# Run the unit tests
|
||||
test:
|
||||
@mkdir -p target/test
|
||||
@mkdir -p target/report
|
||||
GOPATH=$(GOPATH) \
|
||||
go test \
|
||||
-covermode=atomic \
|
||||
-bench=. \
|
||||
-race \
|
||||
-cpuprofile=target/report/cpu.out \
|
||||
-memprofile=target/report/mem.out \
|
||||
-mutexprofile=target/report/mutex.out \
|
||||
-coverprofile=target/report/coverage.out \
|
||||
-v ./... | \
|
||||
tee >(PATH=$(GOPATH)/bin:$(PATH) go-junit-report > target/test/report.xml); \
|
||||
test $${PIPESTATUS[0]} -eq 0
|
||||
|
||||
# Format the source code
|
||||
format:
|
||||
@find . -type f -name "*.go" -exec gofmt -s -w {} \;
|
||||
|
||||
# Check if the source code has been formatted
|
||||
fmtcheck:
|
||||
@mkdir -p target
|
||||
@find . -type f -name "*.go" -exec gofmt -s -d {} \; | tee target/format.diff
|
||||
@test ! -s target/format.diff || { echo "ERROR: the source code has not been formatted - please use 'make format' or 'gofmt'"; exit 1; }
|
||||
|
||||
# Check for syntax errors
|
||||
vet:
|
||||
GOPATH=$(GOPATH) go vet .
|
||||
|
||||
# Check for style errors
|
||||
lint:
|
||||
GOPATH=$(GOPATH) PATH=$(GOPATH)/bin:$(PATH) golint .
|
||||
|
||||
# Generate the coverage report
|
||||
coverage:
|
||||
@mkdir -p target/report
|
||||
GOPATH=$(GOPATH) \
|
||||
go tool cover -html=target/report/coverage.out -o target/report/coverage.html
|
||||
|
||||
# Report cyclomatic complexity
|
||||
cyclo:
|
||||
@mkdir -p target/report
|
||||
GOPATH=$(GOPATH) gocyclo -avg ./ | tee target/report/cyclo.txt ; test $${PIPESTATUS[0]} -eq 0
|
||||
|
||||
# Detect ineffectual assignments
|
||||
ineffassign:
|
||||
@mkdir -p target/report
|
||||
GOPATH=$(GOPATH) ineffassign ./ | tee target/report/ineffassign.txt ; test $${PIPESTATUS[0]} -eq 0
|
||||
|
||||
# Detect commonly misspelled words in source files
|
||||
misspell:
|
||||
@mkdir -p target/report
|
||||
GOPATH=$(GOPATH) misspell -error ./ | tee target/report/misspell.txt ; test $${PIPESTATUS[0]} -eq 0
|
||||
|
||||
# Find unused struct fields
|
||||
structcheck:
|
||||
@mkdir -p target/report
|
||||
GOPATH=$(GOPATH) structcheck -a ./ | tee target/report/structcheck.txt
|
||||
|
||||
# Find unused global variables and constants
|
||||
varcheck:
|
||||
@mkdir -p target/report
|
||||
GOPATH=$(GOPATH) varcheck -e ./ | tee target/report/varcheck.txt
|
||||
|
||||
# Check that error return values are used
|
||||
errcheck:
|
||||
@mkdir -p target/report
|
||||
GOPATH=$(GOPATH) errcheck ./ | tee target/report/errcheck.txt
|
||||
|
||||
# AST scanner
|
||||
astscan:
|
||||
@mkdir -p target/report
|
||||
GOPATH=$(GOPATH) gosec . | tee target/report/astscan.txt ; test $${PIPESTATUS[0]} -eq 0 || true
|
||||
|
||||
# Generate source docs
|
||||
docs:
|
||||
@mkdir -p target/docs
|
||||
nohup sh -c 'GOPATH=$(GOPATH) godoc -http=127.0.0.1:6060' > target/godoc_server.log 2>&1 &
|
||||
wget --directory-prefix=target/docs/ --execute robots=off --retry-connrefused --recursive --no-parent --adjust-extension --page-requisites --convert-links http://127.0.0.1:6060/pkg/github.com/${VENDOR}/${PROJECT}/ ; kill -9 `lsof -ti :6060`
|
||||
@echo '<html><head><meta http-equiv="refresh" content="0;./127.0.0.1:6060/pkg/'${CVSPATH}'/'${PROJECT}'/index.html"/></head><a href="./127.0.0.1:6060/pkg/'${CVSPATH}'/'${PROJECT}'/index.html">'${PKGNAME}' Documentation ...</a></html>' > target/docs/index.html
|
||||
|
||||
# Alias to run all quality-assurance checks
|
||||
qa: fmtcheck test vet lint coverage cyclo ineffassign misspell structcheck varcheck errcheck gosimple astscan
|
||||
|
||||
# --- INSTALL ---
|
||||
|
||||
# Get the dependencies
|
||||
deps:
|
||||
GOPATH=$(GOPATH) go get ./...
|
||||
GOPATH=$(GOPATH) go get golang.org/x/lint/golint
|
||||
GOPATH=$(GOPATH) go get github.com/jstemmer/go-junit-report
|
||||
GOPATH=$(GOPATH) go get github.com/axw/gocov/gocov
|
||||
GOPATH=$(GOPATH) go get github.com/fzipp/gocyclo
|
||||
GOPATH=$(GOPATH) go get github.com/gordonklaus/ineffassign
|
||||
GOPATH=$(GOPATH) go get github.com/client9/misspell/cmd/misspell
|
||||
GOPATH=$(GOPATH) go get github.com/opennota/check/cmd/structcheck
|
||||
GOPATH=$(GOPATH) go get github.com/opennota/check/cmd/varcheck
|
||||
GOPATH=$(GOPATH) go get github.com/kisielk/errcheck
|
||||
GOPATH=$(GOPATH) go get github.com/securego/gosec/cmd/gosec/...
|
||||
|
||||
# Remove any build artifact
|
||||
clean:
|
||||
GOPATH=$(GOPATH) go clean ./...
|
||||
|
||||
# Deletes any intermediate file
|
||||
nuke:
|
||||
rm -rf ./target
|
||||
GOPATH=$(GOPATH) go clean -i ./...
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
*Go language library to map between non-negative integers and boolean values*
|
||||
|
||||
[![Master Build Status](https://secure.travis-ci.org/willf/bitset.png?branch=master)](https://travis-ci.org/willf/bitset?branch=master)
|
||||
[![Test](https://github.com/willf/bitset/workflows/Test/badge.svg)](https://github.com/willf/bitset/actions?query=workflow%3ATest)
|
||||
[![Master Coverage Status](https://coveralls.io/repos/willf/bitset/badge.svg?branch=master&service=github)](https://coveralls.io/github/willf/bitset?branch=master)
|
||||
[![Go Report Card](https://goreportcard.com/badge/github.com/willf/bitset)](https://goreportcard.com/report/github.com/willf/bitset)
|
||||
[![GoDoc](https://godoc.org/github.com/willf/bitset?status.svg)](http://godoc.org/github.com/willf/bitset)
|
||||
[![PkgGoDev](https://pkg.go.dev/badge/github.com/willf/bitset?tab=doc)](https://pkg.go.dev/github.com/willf/bitset?tab=doc)
|
||||
|
||||
|
||||
## Description
|
||||
|
@ -63,8 +63,11 @@ func main() {
|
|||
|
||||
As an alternative to BitSets, one should check out the 'big' package, which provides a (less set-theoretical) view of bitsets.
|
||||
|
||||
Godoc documentation is at: https://godoc.org/github.com/willf/bitset
|
||||
Package documentation is at: https://pkg.go.dev/github.com/willf/bitset?tab=doc
|
||||
|
||||
## Memory Usage
|
||||
|
||||
The memory usage of a bitset using N bits is at least N/8 bytes. The number of bits in a bitset is at least as large as one plus the greatest bit index you have accessed. Thus it is possible to run out of memory while using a bitset. If you have lots of bits, you might prefer compressed bitsets, like the [Roaring bitmaps](http://roaringbitmap.org) and its [Go implementation](https://github.com/RoaringBitmap/roaring).
|
||||
|
||||
## Implementation Note
|
||||
|
||||
|
@ -82,15 +85,10 @@ go get github.com/willf/bitset
|
|||
|
||||
If you wish to contribute to this project, please branch and issue a pull request against master ("[GitHub Flow](https://guides.github.com/introduction/flow/)")
|
||||
|
||||
This project include a Makefile that allows you to test and build the project with simple commands.
|
||||
To see all available options:
|
||||
```bash
|
||||
make help
|
||||
```
|
||||
|
||||
## Running all tests
|
||||
|
||||
Before committing the code, please check if it passes all tests using (note: this will install some dependencies):
|
||||
Before committing the code, please check if it passes tests, has adequate coverage, etc.
|
||||
```bash
|
||||
make qa
|
||||
go test
|
||||
go test -cover
|
||||
```
|
||||
|
|
|
@ -138,6 +138,9 @@ func (b *BitSet) Len() uint {
|
|||
// extendSetMaybe adds additional words to incorporate new bits if needed
|
||||
func (b *BitSet) extendSetMaybe(i uint) {
|
||||
if i >= b.length { // if we need more bits, make 'em
|
||||
if i >= Cap() {
|
||||
panic("You are exceeding the capacity")
|
||||
}
|
||||
nsize := wordsNeeded(i + 1)
|
||||
if b.set == nil {
|
||||
b.set = make([]uint64, nsize)
|
||||
|
@ -160,7 +163,12 @@ func (b *BitSet) Test(i uint) bool {
|
|||
return b.set[i>>log2WordSize]&(1<<(i&(wordSize-1))) != 0
|
||||
}
|
||||
|
||||
// Set bit i to 1
|
||||
// Set bit i to 1, the capacity of the bitset is automatically
|
||||
// increased accordingly.
|
||||
// If i>= Cap(), this function will panic.
|
||||
// Warning: using a very large value for 'i'
|
||||
// may lead to a memory shortage and a panic: the caller is responsible
|
||||
// for providing sensible parameters in line with their memory capacity.
|
||||
func (b *BitSet) Set(i uint) *BitSet {
|
||||
b.extendSetMaybe(i)
|
||||
b.set[i>>log2WordSize] |= 1 << (i & (wordSize - 1))
|
||||
|
@ -176,7 +184,11 @@ func (b *BitSet) Clear(i uint) *BitSet {
|
|||
return b
|
||||
}
|
||||
|
||||
// SetTo sets bit i to value
|
||||
// SetTo sets bit i to value.
|
||||
// If i>= Cap(), this function will panic.
|
||||
// Warning: using a very large value for 'i'
|
||||
// may lead to a memory shortage and a panic: the caller is responsible
|
||||
// for providing sensible parameters in line with their memory capacity.
|
||||
func (b *BitSet) SetTo(i uint, value bool) *BitSet {
|
||||
if value {
|
||||
return b.Set(i)
|
||||
|
@ -184,7 +196,11 @@ func (b *BitSet) SetTo(i uint, value bool) *BitSet {
|
|||
return b.Clear(i)
|
||||
}
|
||||
|
||||
// Flip bit at i
|
||||
// Flip bit at i.
|
||||
// If i>= Cap(), this function will panic.
|
||||
// Warning: using a very large value for 'i'
|
||||
// may lead to a memory shortage and a panic: the caller is responsible
|
||||
// for providing sensible parameters in line with their memory capacity.
|
||||
func (b *BitSet) Flip(i uint) *BitSet {
|
||||
if i >= b.length {
|
||||
return b.Set(i)
|
||||
|
@ -193,26 +209,51 @@ func (b *BitSet) Flip(i uint) *BitSet {
|
|||
return b
|
||||
}
|
||||
|
||||
// Shrink shrinks BitSet to desired length in bits. It clears all bits > length
|
||||
// and reduces the size and length of the set.
|
||||
// Shrink shrinks BitSet so that the provided value is the last possible
|
||||
// set value. It clears all bits > the provided index and reduces the size
|
||||
// and length of the set.
|
||||
//
|
||||
// Note that the parameter value is not the new length in bits: it is the
|
||||
// maximal value that can be stored in the bitset after the function call.
|
||||
// The new length in bits is the parameter value + 1. Thus it is not possible
|
||||
// to use this function to set the length to 0, the minimal value of the length
|
||||
// after this function call is 1.
|
||||
//
|
||||
// A new slice is allocated to store the new bits, so you may see an increase in
|
||||
// memory usage until the GC runs. Normally this should not be a problem, but if you
|
||||
// have an extremely large BitSet its important to understand that the old BitSet will
|
||||
// remain in memory until the GC frees it.
|
||||
func (b *BitSet) Shrink(length uint) *BitSet {
|
||||
idx := wordsNeeded(length + 1)
|
||||
func (b *BitSet) Shrink(lastbitindex uint) *BitSet {
|
||||
length := lastbitindex + 1
|
||||
idx := wordsNeeded(length)
|
||||
if idx > len(b.set) {
|
||||
return b
|
||||
}
|
||||
shrunk := make([]uint64, idx)
|
||||
copy(shrunk, b.set[:idx])
|
||||
b.set = shrunk
|
||||
b.length = length + 1
|
||||
b.set[idx-1] &= (allBits >> (uint64(64) - uint64(length&(wordSize-1)) - 1))
|
||||
b.length = length
|
||||
b.set[idx-1] &= (allBits >> (uint64(64) - uint64(length&(wordSize-1))))
|
||||
return b
|
||||
}
|
||||
|
||||
// Compact shrinks BitSet to so that we preserve all set bits, while minimizing
|
||||
// memory usage. Compact calls Shrink.
|
||||
func (b *BitSet) Compact() *BitSet {
|
||||
idx := len(b.set) - 1
|
||||
for ; idx >= 0 && b.set[idx] == 0; idx-- {
|
||||
}
|
||||
newlength := uint((idx + 1) << log2WordSize)
|
||||
if newlength >= b.length {
|
||||
return b // nothing to do
|
||||
}
|
||||
if newlength > 0 {
|
||||
return b.Shrink(newlength - 1)
|
||||
}
|
||||
// We preserve one word
|
||||
return b.Shrink(63)
|
||||
}
|
||||
|
||||
// InsertAt takes an index which indicates where a bit should be
|
||||
// inserted. Then it shifts all the bits in the set to the left by 1, starting
|
||||
// from the given index position, and sets the index position to 0.
|
||||
|
@ -323,6 +364,9 @@ func (b *BitSet) DeleteAt(i uint) *BitSet {
|
|||
// including possibly the current index
|
||||
// along with an error code (true = valid, false = no set bit found)
|
||||
// for i,e := v.NextSet(0); e; i,e = v.NextSet(i + 1) {...}
|
||||
//
|
||||
// Users concerned with performance may want to use NextSetMany to
|
||||
// retrieve several values at once.
|
||||
func (b *BitSet) NextSet(i uint) (uint, bool) {
|
||||
x := int(i >> log2WordSize)
|
||||
if x >= len(b.set) {
|
||||
|
@ -358,6 +402,14 @@ func (b *BitSet) NextSet(i uint) (uint, bool) {
|
|||
// j += 1
|
||||
// }
|
||||
//
|
||||
//
|
||||
// It is possible to retrieve all set bits as follow:
|
||||
//
|
||||
// indices := make([]uint, bitmap.Count())
|
||||
// bitmap.NextSetMany(0, indices)
|
||||
//
|
||||
// However if bitmap.Count() is large, it might be preferable to
|
||||
// use several calls to NextSetMany, for performance reasons.
|
||||
func (b *BitSet) NextSetMany(i uint, buffer []uint) (uint, []uint) {
|
||||
myanswer := buffer
|
||||
capacity := cap(buffer)
|
||||
|
@ -809,7 +861,7 @@ func (b *BitSet) ReadFrom(stream io.Reader) (int64, error) {
|
|||
newset := New(uint(length))
|
||||
|
||||
if uint64(newset.length) != length {
|
||||
return 0, errors.New("Unmarshalling error: type mismatch")
|
||||
return 0, errors.New("unmarshalling error: type mismatch")
|
||||
}
|
||||
|
||||
// Read remaining bytes as set
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
module github.com/willf/bitset
|
||||
|
||||
go 1.14
|
|
@ -284,7 +284,7 @@ github.com/opencontainers/image-spec/specs-go/v1
|
|||
github.com/opencontainers/runc/libcontainer/user
|
||||
# github.com/opencontainers/runtime-spec v1.0.3-0.20200728170252-4d89ac9fbff6
|
||||
github.com/opencontainers/runtime-spec/specs-go
|
||||
# github.com/opencontainers/selinux v1.6.0
|
||||
# github.com/opencontainers/selinux v1.8.0
|
||||
github.com/opencontainers/selinux/go-selinux
|
||||
github.com/opencontainers/selinux/go-selinux/label
|
||||
github.com/opencontainers/selinux/pkg/pwalk
|
||||
|
@ -336,7 +336,7 @@ github.com/uber/jaeger-client-go/utils
|
|||
github.com/uber/jaeger-lib/metrics
|
||||
# github.com/urfave/cli v1.22.2
|
||||
github.com/urfave/cli
|
||||
# github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243
|
||||
# github.com/willf/bitset v1.1.11
|
||||
github.com/willf/bitset
|
||||
# go.etcd.io/bbolt v1.3.5
|
||||
go.etcd.io/bbolt
|
||||
|
|
|
@ -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