Skip to content

Commit

Permalink
feat: add ravel jailer
Browse files Browse the repository at this point in the history
  • Loading branch information
lucas-jacques committed Dec 19, 2024
1 parent 3cf8f33 commit 10db894
Show file tree
Hide file tree
Showing 35 changed files with 1,495 additions and 330 deletions.
9 changes: 7 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
build-init:
CGO_ENABLED=0 go build -o bin/ravel-init -ldflags="-s -w" cmd/ravel-init/*.go

run-raveld:
sudo go run cmd/ravel/ravel.go daemon -c ravel.toml
run-api:
air
build-ravel:
CGO_ENABLED=0 go build -o bin/ravel cmd/ravel/ravel.go

build-jailer:
CGO_ENABLED=0 go build -o bin/jailer cmd/jailer/jailer.go

install-ravel: build-ravel
sudo cp ./bin/ravel /usr/bin/ravel
protoc:
Expand Down
12 changes: 6 additions & 6 deletions agent/machinerunner/destroy.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,7 @@ func (m *MachineRunner) Destroy(ctx context.Context, force bool) error {
status := m.state.Status()

if status == api.MachineStatusStopped {
if err := m.state.PushDestroyEvent(api.OriginUser, force, "requested by user"); err != nil {
return err
}
go m.destroyImpl(ctx)
go m.destroyImpl(ctx, force, "requested by user")
return nil
}

Expand All @@ -37,12 +34,15 @@ func (m *MachineRunner) Destroy(ctx context.Context, force bool) error {
return err
}

go m.destroyImpl(ctx)
go m.destroyImpl(ctx, force, "requested by user")
return nil

}

func (m *MachineRunner) destroyImpl(ctx context.Context) error {
func (m *MachineRunner) destroyImpl(ctx context.Context, force bool, reason string) error {
if err := m.state.PushDestroyEvent(api.OriginUser, force, reason); err != nil {
return err
}
err := m.runtime.DestroyInstance(ctx, m.state.InstanceId())
if err != nil && !errdefs.IsNotFound(err) {
slog.Error("failed to destroy instance", "instance", m.state.InstanceId(), "error", err)
Expand Down
2 changes: 1 addition & 1 deletion agent/machinerunner/prepare.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,5 @@ func (m *MachineRunner) onPrepareFailed(msg string) {
slog.Error("Failed to push PrepareFailed event", "error", err)
}

m.destroyImpl(context.Background())
m.destroyImpl(context.Background(), true, "prepare failed")
}
7 changes: 6 additions & 1 deletion agent/machinerunner/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"time"

"github.com/valyentdev/ravel/api"
"github.com/valyentdev/ravel/core/errdefs"
"github.com/valyentdev/ravel/core/instance"
)

Expand All @@ -21,6 +22,10 @@ func (m *MachineRunner) Run() {
ctx := context.Background()
updates, err := m.runtime.WatchInstanceState(ctx, m.state.InstanceId())
if err != nil {
if errdefs.IsNotFound(err) {
m.destroyImpl(ctx, true, "instance not found")
}

slog.Error("failed to watch instance state", "machine_id", m.state.Id(), "error", err)
return
}
Expand Down Expand Up @@ -52,7 +57,7 @@ func (m *MachineRunner) recover(ctx context.Context) bool {
return false

case api.MachineStatusDestroying:
err := m.destroyImpl(ctx)
err := m.destroyImpl(ctx, true, "recovering from destroy")
if err != nil {
slog.Error("failed to destroy machine", "machine_id", m.state.Id(), "error", err)
}
Expand Down
2 changes: 1 addition & 1 deletion client/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func NewDaemonClient(socket string) *DaemonClient {

func (a *DaemonClient) CreateInstance(ctx context.Context, options daemon.InstanceOptions) (*instance.Instance, error) {
var instance instance.Instance
err := a.client.Post(ctx, "/instances", options, httpclient.WithJSONBody(&options))
err := a.client.Post(ctx, "/instances", nil, httpclient.WithJSONBody(&options))
if err != nil {
return nil, err
}
Expand Down
15 changes: 15 additions & 0 deletions cmd/jailer/jailer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package main

import (
"log/slog"
"os"

"github.com/valyentdev/ravel/core/jailer"
)

func main() {
if err := jailer.Run(); err != nil {
slog.Error("jailer run failed", "error", err)
os.Exit(1)
}
}
40 changes: 39 additions & 1 deletion core/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package config

import (
"encoding/json"
"errors"
"fmt"
"os"
"strings"

Expand Down Expand Up @@ -33,6 +35,41 @@ type RavelConfig struct {
Registries registry.RegistriesConfig `json:"registries" toml:"registries"`
}

// never display data because it contains secrets
func fmtDecodeError(err *toml.DecodeError) error {
line, column := err.Position()

return fmt.Errorf("%s %s at %s", err.Error(), strings.Join(err.Key(), "."), fmt.Sprintf("line %d, column %d", line, column))

}

func joinErrors(errs ...error) error {
var errStr string

for _, err := range errs {
errStr += err.Error() + "\n"
}
return errors.New(errStr)
}

func buildTomlError(err error) error {
smeErr, ok := err.(*toml.StrictMissingError)
if ok {
var errs []error
for _, e := range smeErr.Errors {
errs = append(errs, fmtDecodeError(&e))
}
return joinErrors(errs...)
}

decodeErr, ok := err.(*toml.DecodeError)
if ok {
return joinErrors(fmtDecodeError(decodeErr))
}

return fmt.Errorf("toml error: %w", err)
}

func ReadFile(path string) (RavelConfig, error) {
var config RavelConfig

Expand All @@ -46,7 +83,8 @@ func ReadFile(path string) (RavelConfig, error) {
decoder = decoder.DisallowUnknownFields()
err = decoder.Decode(&config)
if err != nil {
return config, err
tomlErr := err.(*toml.StrictMissingError)
return config, buildTomlError(tomlErr)
}
} else {
err = json.Unmarshal(bytes, &config)
Expand Down
7 changes: 4 additions & 3 deletions core/config/runtime.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package config

type RuntimeConfig struct {
InitBinary string `json:"init_binary" toml:"init_binary"`
LinuxKernel string `json:"linux_kernel" toml:"linux_kernel"`
Snapshotter string `json:"snapshotter" toml:"snapshotter"`
CloudHypervisorBinary string `json:"cloud_hypervisor_binary" toml:"cloud_hypervisor_binary"`
JailerBinary string `json:"jailer_binary" toml:"jailer_binary"`
InitBinary string `json:"init_binary" toml:"init_binary"`
LinuxKernel string `json:"linux_kernel" toml:"linux_kernel"`
}
6 changes: 2 additions & 4 deletions core/instance/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"time"

"github.com/containerd/containerd/v2/client"
"github.com/valyentdev/ravel/api"
)

Expand Down Expand Up @@ -33,7 +32,7 @@ type Handle struct {
}

type VM interface {
Start(ctx context.Context) (Handle, error)
Start(ctx context.Context) error
Exec(ctx context.Context, cmd []string, timeout time.Duration) (*api.ExecResult, error)
Run() ExitResult
WaitExit(ctx context.Context) bool
Expand All @@ -43,9 +42,8 @@ type VM interface {
}

type Builder interface {
PrepareInstance(ctx context.Context, instance *Instance, image client.Image) error
BuildInstanceVM(ctx context.Context, instance *Instance) (VM, error)
RecoverInstanceVM(ctx context.Context, instance *Instance) (VM, Handle, error)
RecoverInstanceVM(ctx context.Context, instance *Instance) (VM, error)
CleanupInstanceVM(ctx context.Context, instance *Instance) error
CleanupInstance(ctx context.Context, instance *Instance) error
}
Expand Down
60 changes: 60 additions & 0 deletions core/jailer/chroot.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package jailer

import (
"fmt"
"path/filepath"
"syscall"

"golang.org/x/sys/unix"
)

func chroot(path string) error {
rootDir := "/"

err := unix.Mount("", rootDir, "", syscall.MS_SLAVE|syscall.MS_REC, "")
if err != nil {
return fmt.Errorf("failed to mount: %w", err)
}

err = unix.Mount(path, path, "", syscall.MS_BIND|syscall.MS_REC, "")
if err != nil {
return err
}

err = syscall.Chdir(path)
if err != nil {
return fmt.Errorf("failed to chdir: %w", err)
}

err = syscall.Mkdir("old_root", 0755)
if err != nil {
return fmt.Errorf("failed to create old_root: %w", err)
}

oldRootAbs, err := filepath.Abs("./old_root")
if err != nil {
return fmt.Errorf("failed to get absolute path of old root: %w", err)
}

err = syscall.PivotRoot(path, oldRootAbs)
if err != nil {
return fmt.Errorf("failed to pivot_root: %w", err)
}

err = syscall.Chdir("/")
if err != nil {
return fmt.Errorf("failed to chdir: %w", err)
}

err = syscall.Unmount("old_root", syscall.MNT_DETACH)
if err != nil {
return fmt.Errorf("failed to unmount old_root: %w", err)
}

err = syscall.Rmdir("old_root")
if err != nil {
return fmt.Errorf("failed to remove old_root: %w", err)
}

return nil
}
68 changes: 68 additions & 0 deletions core/jailer/flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package jailer

import (
"flag"
"os"
"os/exec"
"strconv"
)

func setupJailerFlags(config *JailerConfig) *flag.FlagSet {
jailerFlagSet := flag.NewFlagSet("", flag.ExitOnError)
jailerFlagSet.Bool("help h", false, "Show help")
jailerFlagSet.IntVar(&config.Uid, "uid", 0, "UID of the process")
jailerFlagSet.IntVar(&config.Gid, "gid", 0, "GID of the process")
jailerFlagSet.StringVar(&config.Netns, "netns", "", "Network namespace to join")
jailerFlagSet.StringVar(&config.NewRoot, "new-root", "", "New root directory")
jailerFlagSet.BoolVar(&config.NewPid, "new-pid", false, "Create new PID namespace")
jailerFlagSet.IntVar(&config.NoFiles, "rlimit-nofiles", 0, "Number of open files")
jailerFlagSet.IntVar(&config.Fsize, "rlimit-fsize", 0, "File size limit")
jailerFlagSet.BoolVar(&config.MountProc, "mount-proc", false, "Mount /proc inside the jail")
jailerFlagSet.StringVar(&config.Cgroup, "cgroup", "", "CGroup to join")
return jailerFlagSet
}

func makeCmd(jailer string, command []string, opts *options) *exec.Cmd {
args := []string{
jailer,
"exec",
"--uid", strconv.FormatInt(int64(opts.Uid), 10),
"--gid", strconv.FormatInt(int64(opts.Gid), 10),
"--new-root", opts.NewRoot,
}

if opts.netns != "" {
args = append(args, "--netns", opts.netns)
}

if opts.newPidNS {
args = append(args, "--new-pid")
}

if opts.mountProc {
args = append(args, "--mount-proc")
}

if opts.setRlimits {
args = append(args, "--rlimit-fsize", strconv.FormatInt(int64(opts.fsize), 10))
args = append(args, "--rlimit-nofiles", strconv.FormatInt(int64(opts.noFiles), 10))
}

if opts.cgroup != "" {
args = append(args, "--cgroup", opts.cgroup)
}

args = append(args, "--")
args = append(args, command...)

cmd := &exec.Cmd{
Path: jailer,
Args: args,
Env: []string{},
Stdin: os.Stdin,
Stdout: os.Stdout,
Stderr: os.Stderr,
}

return cmd
}
Loading

0 comments on commit 10db894

Please sign in to comment.