diff --git a/README.md b/README.md index 032a01d..8969110 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,13 @@ DEBU[0001] run: executing command on 170.10.20.30 using ssh: [sudo df -i] ... ``` +To run crashd in a `restrictedMode`, use the `--restrictedMode` flag as shown: + +``` +$> crashd run --restrictedMode diagnostics.crsh +``` +Restricted mode is used to prevent the execution of potentially harmful commands. In restricted mode, the following commands are disabled: `run_local`, `capture_local`, `copy_to` + ## Compute Resource Providers Crashd utilizes the concept of a provider to enumerate compute resources. Each implementation of a provider is responsible for enumerating compute resources on which Crashd can execute commands using a transport (i.e. SSH). Crashd comes with several providers including diff --git a/cmd/run.go b/cmd/run.go index c26563f..af261b4 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -14,14 +14,16 @@ import ( ) type runFlags struct { - args map[string]string - argsFile string + args map[string]string + argsFile string + restrictedMode bool } func defaultRunFlags() *runFlags { return &runFlags{ - args: make(map[string]string), - argsFile: ArgsFile, + args: make(map[string]string), + argsFile: ArgsFile, + restrictedMode: false, } } @@ -40,6 +42,7 @@ func newRunCommand() *cobra.Command { } cmd.Flags().StringToStringVar(&flags.args, "args", flags.args, "comma-separated key=value pairs passed to the script (i.e. --args 'key0=val0,key1=val1')") cmd.Flags().StringVar(&flags.argsFile, "args-file", flags.argsFile, "path to a file containing key=value argument pairs that are passed to the script file") + cmd.Flags().BoolVar(&flags.restrictedMode, "restrictedMode", flags.restrictedMode, "run the script in a restricted mode that prevents usage of certain grammar functions") return cmd } @@ -55,7 +58,7 @@ func run(flags *runFlags, path string) error { return err } - if err := exec.ExecuteFile(file, scriptArgs); err != nil { + if err := exec.ExecuteFile(file, scriptArgs, flags.restrictedMode); err != nil { return fmt.Errorf("execution failed for %s: %w", file.Name(), err) } diff --git a/exec/executor.go b/exec/executor.go index 6a5e7a2..747612b 100644 --- a/exec/executor.go +++ b/exec/executor.go @@ -13,8 +13,8 @@ import ( type ArgMap map[string]string -func Execute(name string, source io.Reader, args ArgMap) error { - star, err := newExecutor(args) +func Execute(name string, source io.Reader, args ArgMap, restrictedMode bool) error { + star, err := newExecutor(args, restrictedMode) if err != nil { return err } @@ -22,8 +22,8 @@ func Execute(name string, source io.Reader, args ArgMap) error { return execute(star, name, source) } -func ExecuteFile(file *os.File, args ArgMap) error { - return Execute(file.Name(), file, args) +func ExecuteFile(file *os.File, args ArgMap, restrictedMode bool) error { + return Execute(file.Name(), file, args, restrictedMode) } type StarlarkModule struct { @@ -31,8 +31,8 @@ type StarlarkModule struct { Source io.Reader } -func ExecuteWithModules(name string, source io.Reader, args ArgMap, modules ...StarlarkModule) error { - star, err := newExecutor(args) +func ExecuteWithModules(name string, source io.Reader, args ArgMap, restrictedMode bool, modules ...StarlarkModule) error { + star, err := newExecutor(args, restrictedMode) if err != nil { return err } @@ -47,7 +47,7 @@ func ExecuteWithModules(name string, source io.Reader, args ArgMap, modules ...S return execute(star, name, source) } -func newExecutor(args ArgMap) (*starlark.Executor, error) { +func newExecutor(args ArgMap, restrictedMode bool) (*starlark.Executor, error) { star := starlark.New() if args != nil { diff --git a/exec/executor_test.go b/exec/executor_test.go index 37596a0..639c6dd 100644 --- a/exec/executor_test.go +++ b/exec/executor_test.go @@ -57,7 +57,7 @@ func TestExampleScripts(t *testing.T) { t.Fatal(err) } defer file.Close() - if err := ExecuteFile(file, test.args); err != nil { + if err := ExecuteFile(file, test.args, false); err != nil { t.Fatal(err) } }) @@ -74,7 +74,7 @@ func TestExecute(t *testing.T) { name: "execute single script", script: `result = run_local("echo 'Hello World!'")`, exec: func(t *testing.T, script string) { - if err := Execute("run_local", strings.NewReader(script), ArgMap{}); err != nil { + if err := Execute("run_local", strings.NewReader(script), ArgMap{}, false); err != nil { t.Fatal(err) } }, @@ -91,6 +91,7 @@ def multiply(x, y): "multiply", strings.NewReader(script), ArgMap{}, + false, StarlarkModule{Name: "lib", Source: strings.NewReader(mod)}); err != nil { t.Fatal(err) } diff --git a/starlark/starlark_exec.go b/starlark/starlark_exec.go index 2120ce9..76a2a1a 100644 --- a/starlark/starlark_exec.go +++ b/starlark/starlark_exec.go @@ -20,10 +20,10 @@ type Executor struct { result starlark.StringDict } -func New() *Executor { +func New(restrictedMode ...bool) *Executor { return &Executor{ thread: &starlark.Thread{Name: "crashd"}, - predecs: newPredeclareds(), + predecs: newPredeclareds(restrictedMode), } } @@ -117,8 +117,9 @@ func setupLocalDefaults(thread *starlark.Thread) error { // newPredeclareds creates string dictionary containing the // global built-ins values and functions available to the // running script. -func newPredeclareds() starlark.StringDict { - return starlark.StringDict{ +func newPredeclareds(restrictedMode []bool) starlark.StringDict { + + dict := starlark.StringDict{ identifiers.os: setupOSStruct(), identifiers.crashdCfg: starlark.NewBuiltin(identifiers.crashdCfg, crashdConfigFn), identifiers.sshCfg: starlark.NewBuiltin(identifiers.sshCfg, SshConfigFn), @@ -141,4 +142,13 @@ func newPredeclareds() starlark.StringDict { identifiers.setDefaults: starlark.NewBuiltin(identifiers.setDefaults, SetDefaultsFunc), identifiers.log: starlark.NewBuiltin(identifiers.log, logFunc), } + + if len(restrictedMode) > 0 && restrictedMode[0] { + logrus.Info("Running crashd in restricted mode. Some functions will be disabled from the grammar.") + delete(dict, identifiers.runLocal) + delete(dict, identifiers.captureLocal) + delete(dict, identifiers.copyTo) + } + + return dict }