diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..f3d39f7a --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,25 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/go +{ + "name": "Go", + // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile + "image": "mcr.microsoft.com/devcontainers/go:1-1.21-bullseye", + "features": { + "ghcr.io/devcontainers/features/docker-in-docker:2": {} + } + + // Features to add to the dev container. More info: https://containers.dev/features. + // "features": {}, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "go version", + + // Configure tool-specific properties. + // "customizations": {}, + + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" +} diff --git a/Dockerfile b/Dockerfile index 12796a31..7c60c164 100644 --- a/Dockerfile +++ b/Dockerfile @@ -121,7 +121,7 @@ RUN --mount=from=binaries \ --mount=type=bind,from=buildx-version,source=/buildx-version,target=/buildx-version < 0 { fmt.Fprintf(w, "Driver Options:\t%s\n", strings.Join(driverOpts, " ")) } + var securityOpts []string + for k, v := range n.SecurityOpts { + securityOpts = append(securityOpts, fmt.Sprintf("%s=%q", k, v)) + } + if len(securityOpts) > 0 { + fmt.Fprintf(w, "Security Options:\t%s\n", strings.Join(driverOpts, " ")) + } if err := n.Err; err != nil { fmt.Fprintf(w, "Error:\t%s\n", err.Error()) diff --git a/driver/docker-container/driver.go b/driver/docker-container/driver.go index d42d233a..1e93cb08 100644 --- a/driver/docker-container/driver.go +++ b/driver/docker-container/driver.go @@ -8,6 +8,7 @@ import ( "os" "path" "path/filepath" + "strconv" "strings" "sync/atomic" "time" @@ -41,6 +42,7 @@ type Driver struct { netMode string image string cgroupParent string + securityOpts map[string]string env []string } @@ -109,7 +111,6 @@ func (d *Driver) create(ctx context.Context, l progress.SubLogger) error { if d.InitConfig.BuildkitFlags != nil { cfg.Cmd = d.InitConfig.BuildkitFlags } - useInit := true // let it cleanup exited processes created by BuildKit's container API if err := l.Wrap("creating container "+d.Name, func() error { hc := &container.HostConfig{ @@ -127,6 +128,13 @@ func (d *Driver) create(ctx context.Context, l progress.SubLogger) error { hc.NetworkMode = container.NetworkMode(d.netMode) } if info, err := d.DockerAPI.Info(ctx); err == nil { + secOpts, err := dockertypes.DecodeSecurityOptions(info.SecurityOptions) + l.Wrap("driverOpts"+info.CgroupDriver, func() error { + return nil + }) + if err != nil { + return err + } if info.CgroupDriver == "cgroupfs" { // Place all buildkit containers inside this cgroup by default so limits can be attached // to all build activity on the host. @@ -135,18 +143,27 @@ func (d *Driver) create(ctx context.Context, l progress.SubLogger) error { hc.CgroupParent = d.cgroupParent } } - - secOpts, err := dockertypes.DecodeSecurityOptions(info.SecurityOptions) - if err != nil { - return err - } for _, f := range secOpts { if f.Name == "userns" { hc.UsernsMode = "host" break } } - + for i, k := range d.securityOpts { + switch { + case i == "systempaths": + hc.MaskedPaths = []string{} + hc.ReadonlyPaths = []string{} + case i == "privileged": + val, err := strconv.ParseBool(k) + if err != nil { + return errors.Errorf("invalid value privleged security option, options are true/false") + } + hc.Privileged = val + default: + hc.SecurityOpt = append(hc.SecurityOpt, i+"="+k) + } + } } _, err := d.DockerAPI.ContainerCreate(ctx, cfg, hc, &network.NetworkingConfig{}, nil, d.Name) if err != nil && !errdefs.IsConflict(err) { diff --git a/driver/docker-container/factory.go b/driver/docker-container/factory.go index 118d9c5a..11ebc0f0 100644 --- a/driver/docker-container/factory.go +++ b/driver/docker-container/factory.go @@ -40,6 +40,7 @@ func (f *factory) New(ctx context.Context, cfg driver.InitConfig) (driver.Driver return nil, errors.Errorf("%s driver requires docker API access", f.Name()) } d := &Driver{factory: f, InitConfig: cfg} + d.securityOpts = make(map[string]string) for k, v := range cfg.DriverOpts { switch { case k == "network": @@ -57,11 +58,32 @@ func (f *factory) New(ctx context.Context, cfg driver.InitConfig) (driver.Driver return nil, errors.Errorf("invalid env option %q, expecting env.FOO=bar", k) } d.env = append(d.env, fmt.Sprintf("%s=%s", envName, v)) + case k == "seccomp": + d.securityOpts[k] = v + case k == "apparmor": + d.securityOpts[k] = v + case k == "systempaths": + d.securityOpts[k] = v + case k == "privileged": + d.securityOpts[k] = v default: return nil, errors.Errorf("invalid driver option %s for docker-container driver", k) } } - + for i, _ := range cfg.SecurityOpts { + switch { + case i == "seccomp": + continue + case i == "apparmor": + continue + case i == "systempaths": + continue + case i == "privileged": + continue + default: + return nil, errors.Errorf("invalid Security option %s for docker-container driver", i) + } + } return d, nil } diff --git a/driver/driver.go b/driver/driver.go index 8642a543..1310333c 100644 --- a/driver/driver.go +++ b/driver/driver.go @@ -80,7 +80,6 @@ func Boot(ctx, clientContext context.Context, d *DriverHandle, pw progress.Write return nil, err } } - c, err := d.Client(clientContext) if err != nil { if errors.Cause(err) == ErrNotRunning && try <= 2 { diff --git a/driver/manager.go b/driver/manager.go index b4ec318f..e91c99dc 100644 --- a/driver/manager.go +++ b/driver/manager.go @@ -56,6 +56,7 @@ type InitConfig struct { BuildkitFlags []string Files map[string][]byte DriverOpts map[string]string + SecurityOpts map[string]string Auth Auth Platforms []specs.Platform // ContextPathHash can be used for determining pods in the driver instance @@ -104,7 +105,7 @@ func GetFactory(name string, instanceRequired bool) (Factory, error) { return nil, errors.Errorf("failed to find driver %q", name) } -func GetDriver(ctx context.Context, name string, f Factory, endpointAddr string, api dockerclient.APIClient, auth Auth, kcc KubeClientConfig, flags []string, files map[string][]byte, do map[string]string, platforms []specs.Platform, contextPathHash string) (*DriverHandle, error) { +func GetDriver(ctx context.Context, name string, f Factory, endpointAddr string, api dockerclient.APIClient, auth Auth, kcc KubeClientConfig, flags []string, files map[string][]byte, do map[string]string, so map[string]string, platforms []specs.Platform, contextPathHash string) (*DriverHandle, error) { ic := InitConfig{ EndpointAddr: endpointAddr, DockerAPI: api, @@ -112,6 +113,7 @@ func GetDriver(ctx context.Context, name string, f Factory, endpointAddr string, Name: name, BuildkitFlags: flags, DriverOpts: do, + SecurityOpts: so, Auth: auth, Platforms: platforms, ContextPathHash: contextPathHash, diff --git a/out/buildx b/out/buildx new file mode 100755 index 00000000..277a72b8 Binary files /dev/null and b/out/buildx differ diff --git a/store/nodegroup.go b/store/nodegroup.go index 823470dd..4d56daa5 100644 --- a/store/nodegroup.go +++ b/store/nodegroup.go @@ -24,11 +24,12 @@ type NodeGroup struct { } type Node struct { - Name string - Endpoint string - Platforms []specs.Platform - Flags []string - DriverOpts map[string]string + Name string + Endpoint string + Platforms []specs.Platform + Flags []string + DriverOpts map[string]string + SecurityOpts map[string]string Files map[string][]byte } @@ -48,7 +49,7 @@ func (ng *NodeGroup) Leave(name string) error { return nil } -func (ng *NodeGroup) Update(name, endpoint string, platforms []string, endpointsSet bool, actionAppend bool, flags []string, configFile string, do map[string]string) error { +func (ng *NodeGroup) Update(name, endpoint string, platforms []string, endpointsSet bool, actionAppend bool, flags []string, configFile string, do map[string]string, so map[string]string) error { if ng.Dynamic { return errors.New("dynamic node group does not support Update") } @@ -91,6 +92,10 @@ func (ng *NodeGroup) Update(name, endpoint string, platforms []string, endpoints n.DriverOpts = do needsRestart = true } + if so != nil { + n.SecurityOpts = so + needsRestart = true + } if configFile != "" { for k, v := range files { n.Files[k] = v @@ -118,12 +123,13 @@ func (ng *NodeGroup) Update(name, endpoint string, platforms []string, endpoints } n := Node{ - Name: name, - Endpoint: endpoint, - Platforms: pp, - Flags: flags, - DriverOpts: do, - Files: files, + Name: name, + Endpoint: endpoint, + Platforms: pp, + Flags: flags, + DriverOpts: do, + SecurityOpts: so, + Files: files, } ng.Nodes = append(ng.Nodes, n) @@ -156,6 +162,10 @@ func (n *Node) Copy() *Node { for k, v := range n.DriverOpts { driverOpts[k] = v } + securityOpts := map[string]string{} + for k, v := range n.SecurityOpts { + securityOpts[k] = v + } files := map[string][]byte{} for k, v := range n.Files { vv := []byte{} @@ -163,12 +173,13 @@ func (n *Node) Copy() *Node { files[k] = vv } return &Node{ - Name: n.Name, - Endpoint: n.Endpoint, - Platforms: platforms, - Flags: flags, - DriverOpts: driverOpts, - Files: files, + Name: n.Name, + Endpoint: n.Endpoint, + Platforms: platforms, + Flags: flags, + DriverOpts: driverOpts, + SecurityOpts: securityOpts, + Files: files, } }