
This allows filesystem-based ACLs for configuring access to the socket of a shim. Ported from Michael Crosby's similar patch for v2 shims. Signed-off-by: Samuel Karp <skarp@amazon.com>
200 lines
5.7 KiB
Go
200 lines
5.7 KiB
Go
// +build linux
|
|
|
|
/*
|
|
Copyright The containerd Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package linux
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha256"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/containerd/containerd/events/exchange"
|
|
"github.com/containerd/containerd/runtime/linux/runctypes"
|
|
"github.com/containerd/containerd/runtime/v1/shim"
|
|
"github.com/containerd/containerd/runtime/v1/shim/client"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// loadBundle loads an existing bundle from disk
|
|
func loadBundle(id, path, workdir string) *bundle {
|
|
return &bundle{
|
|
id: id,
|
|
path: path,
|
|
workDir: workdir,
|
|
}
|
|
}
|
|
|
|
// newBundle creates a new bundle on disk at the provided path for the given id
|
|
func newBundle(id, path, workDir string, spec []byte) (b *bundle, err error) {
|
|
if err := os.MkdirAll(path, 0711); err != nil {
|
|
return nil, err
|
|
}
|
|
path = filepath.Join(path, id)
|
|
if err := os.Mkdir(path, 0711); err != nil {
|
|
return nil, err
|
|
}
|
|
defer func() {
|
|
if err != nil {
|
|
os.RemoveAll(path)
|
|
}
|
|
}()
|
|
workDir = filepath.Join(workDir, id)
|
|
if err := os.MkdirAll(workDir, 0711); err != nil {
|
|
return nil, err
|
|
}
|
|
defer func() {
|
|
if err != nil {
|
|
os.RemoveAll(workDir)
|
|
}
|
|
}()
|
|
rootfs := filepath.Join(path, "rootfs")
|
|
if err := os.MkdirAll(rootfs, 0711); err != nil {
|
|
return nil, err
|
|
}
|
|
err = ioutil.WriteFile(filepath.Join(path, configFilename), spec, 0666)
|
|
return &bundle{
|
|
id: id,
|
|
path: path,
|
|
workDir: workDir,
|
|
}, err
|
|
}
|
|
|
|
type bundle struct {
|
|
id string
|
|
path string
|
|
workDir string
|
|
}
|
|
|
|
// ShimOpt specifies shim options for initialization and connection
|
|
type ShimOpt func(*bundle, string, *runctypes.RuncOptions) (shim.Config, client.Opt)
|
|
|
|
// ShimRemote is a ShimOpt for connecting and starting a remote shim
|
|
func ShimRemote(c *Config, daemonAddress, cgroup string, exitHandler func()) ShimOpt {
|
|
return func(b *bundle, ns string, ropts *runctypes.RuncOptions) (shim.Config, client.Opt) {
|
|
config := b.shimConfig(ns, c, ropts)
|
|
return config,
|
|
client.WithStart(c.Shim, b.shimAddress(ns, daemonAddress), daemonAddress, cgroup, c.ShimDebug, exitHandler)
|
|
}
|
|
}
|
|
|
|
// ShimLocal is a ShimOpt for using an in process shim implementation
|
|
func ShimLocal(c *Config, exchange *exchange.Exchange) ShimOpt {
|
|
return func(b *bundle, ns string, ropts *runctypes.RuncOptions) (shim.Config, client.Opt) {
|
|
return b.shimConfig(ns, c, ropts), client.WithLocal(exchange)
|
|
}
|
|
}
|
|
|
|
// ShimConnect is a ShimOpt for connecting to an existing remote shim
|
|
func ShimConnect(c *Config, onClose func()) ShimOpt {
|
|
return func(b *bundle, ns string, ropts *runctypes.RuncOptions) (shim.Config, client.Opt) {
|
|
return b.shimConfig(ns, c, ropts), client.WithConnect(b.decideShimAddress(ns), onClose)
|
|
}
|
|
}
|
|
|
|
// NewShimClient connects to the shim managing the bundle and tasks creating it if needed
|
|
func (b *bundle) NewShimClient(ctx context.Context, namespace string, getClientOpts ShimOpt, runcOpts *runctypes.RuncOptions) (*client.Client, error) {
|
|
cfg, opt := getClientOpts(b, namespace, runcOpts)
|
|
return client.New(ctx, cfg, opt)
|
|
}
|
|
|
|
// Delete deletes the bundle from disk
|
|
func (b *bundle) Delete() error {
|
|
address, _ := b.loadAddress()
|
|
if address != "" {
|
|
// we don't care about errors here
|
|
client.RemoveSocket(address)
|
|
}
|
|
err := atomicDelete(b.path)
|
|
if err == nil {
|
|
return atomicDelete(b.workDir)
|
|
}
|
|
// error removing the bundle path; still attempt removing work dir
|
|
err2 := atomicDelete(b.workDir)
|
|
if err2 == nil {
|
|
return err
|
|
}
|
|
return errors.Wrapf(err, "Failed to remove both bundle and workdir locations: %v", err2)
|
|
}
|
|
|
|
func (b *bundle) legacyShimAddress(namespace string) string {
|
|
return filepath.Join(string(filepath.Separator), "containerd-shim", namespace, b.id, "shim.sock")
|
|
}
|
|
|
|
const socketRoot = "/run/containerd"
|
|
|
|
func (b *bundle) shimAddress(namespace, socketPath string) string {
|
|
d := sha256.Sum256([]byte(filepath.Join(socketPath, namespace, b.id)))
|
|
return fmt.Sprintf("unix://%s/%x", filepath.Join(socketRoot, "s"), d)
|
|
}
|
|
|
|
func (b *bundle) loadAddress() (string, error) {
|
|
addressPath := filepath.Join(b.path, "address")
|
|
data, err := ioutil.ReadFile(addressPath)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(data), nil
|
|
}
|
|
|
|
func (b *bundle) decideShimAddress(namespace string) string {
|
|
address, err := b.loadAddress()
|
|
if err != nil {
|
|
return b.legacyShimAddress(namespace)
|
|
}
|
|
return address
|
|
}
|
|
|
|
func (b *bundle) shimConfig(namespace string, c *Config, runcOptions *runctypes.RuncOptions) shim.Config {
|
|
var (
|
|
criuPath string
|
|
runtimeRoot = c.RuntimeRoot
|
|
systemdCgroup bool
|
|
)
|
|
if runcOptions != nil {
|
|
criuPath = runcOptions.CriuPath
|
|
systemdCgroup = runcOptions.SystemdCgroup
|
|
if runcOptions.RuntimeRoot != "" {
|
|
runtimeRoot = runcOptions.RuntimeRoot
|
|
}
|
|
}
|
|
return shim.Config{
|
|
Path: b.path,
|
|
WorkDir: b.workDir,
|
|
Namespace: namespace,
|
|
Criu: criuPath,
|
|
RuntimeRoot: runtimeRoot,
|
|
SystemdCgroup: systemdCgroup,
|
|
}
|
|
}
|
|
|
|
// atomicDelete renames the path to a hidden file before removal
|
|
func atomicDelete(path string) error {
|
|
// create a hidden dir for an atomic removal
|
|
atomicPath := filepath.Join(filepath.Dir(path), fmt.Sprintf(".%s", filepath.Base(path)))
|
|
if err := os.Rename(path, atomicPath); err != nil {
|
|
if os.IsNotExist(err) {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
return os.RemoveAll(atomicPath)
|
|
}
|