Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support new options in terminal #6805

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changes/unreleased/Added-20240304-224513.yaml
@@ -0,0 +1,6 @@
kind: Added
body: Support privileges and nesting in default terminal command
time: 2024-03-04T22:45:13.122651+01:00
custom:
Author: TomChv
PR: "6805"
14 changes: 13 additions & 1 deletion core/container.go
Expand Up @@ -41,6 +41,18 @@ import (

var ErrContainerNoExec = errors.New("no command has been executed")

type DefaultTerminalCmdOpts struct {
Args []string

// Provide dagger access to the executed command
// Do not use this option unless you trust the command being executed.
// The command being executed WILL BE GRANTED FULL ACCESS TO YOUR HOST FILESYSTEM
ExperimentalPrivilegedNesting bool `default:"false"`

// Grant the process all root capabilities
InsecureRootCapabilities bool `default:"false"`
}

// Container is a content-addressed container.
type Container struct {
Query *Query
Expand Down Expand Up @@ -83,7 +95,7 @@ type Container struct {
Focused bool `json:"focused"`

// The args to invoke when using the terminal api on this container.
DefaultTerminalCmd []string `json:"defaultTerminalCmd,omitempty"`
DefaultTerminalCmd *DefaultTerminalCmdOpts `json:"defaultTerminalCmd,omitempty"`
}

func (*Container) Type() *ast.Type {
Expand Down
68 changes: 49 additions & 19 deletions core/schema/container.go
Expand Up @@ -294,7 +294,7 @@ func (s *containerSchema) Install() {
`Redirect the command's standard error to a file in the container (e.g.,
"/tmp/stderr").`).
ArgDoc("experimentalPrivilegedNesting",
`Provides dagger access to the executed command.`,
`Provides Dagger access to the executed command.`,
`Do not use this option unless you trust the command being executed;
the command being executed WILL BE GRANTED FULL ACCESS TO YOUR HOST
FILESYSTEM.`).
Expand Down Expand Up @@ -441,11 +441,33 @@ func (s *containerSchema) Install() {

dagql.Func("withDefaultTerminalCmd", s.withDefaultTerminalCmd).
Doc(`Set the default command to invoke for the container's terminal API.`).
ArgDoc("args", `The args of the command.`),
ArgDoc("args", `The args of the command.`).
ArgDoc("experimentalPrivilegedNesting",
`Provides Dagger access to the executed command.`,
`Do not use this option unless you trust the command being executed;
the command being executed WILL BE GRANTED FULL ACCESS TO YOUR HOST
FILESYSTEM.`).
ArgDoc("insecureRootCapabilities",
`Execute the command with all root capabilities. This is similar to
running a command with "sudo" or executing "docker run" with the
"--privileged" flag. Containerization does not provide any security
guarantees when using this option. It should only be used when
absolutely necessary and only with trusted commands.`),

dagql.NodeFunc("terminal", s.terminal).
Doc(`Return an interactive terminal for this container using its configured default terminal command if not overridden by args (or sh as a fallback default).`).
ArgDoc("cmd", `If set, override the container's default terminal command and invoke these command arguments instead.`),
ArgDoc("cmd", `If set, override the container's default terminal command and invoke these command arguments instead.`).
ArgDoc("experimentalPrivilegedNesting",
`Provides Dagger access to the executed command.`,
`Do not use this option unless you trust the command being executed;
the command being executed WILL BE GRANTED FULL ACCESS TO YOUR HOST
FILESYSTEM.`).
ArgDoc("insecureRootCapabilities",
`Execute the command with all root capabilities. This is similar to
running a command with "sudo" or executing "docker run" with the
"--privileged" flag. Containerization does not provide any security
guarantees when using this option. It should only be used when
absolutely necessary and only with trusted commands.`),

dagql.Func("experimentalWithGPU", s.withGPU).
Doc(`EXPERIMENTAL API! Subject to change/removal at any time.`,
Expand Down Expand Up @@ -1317,39 +1339,47 @@ func (s *containerSchema) withoutFocus(ctx context.Context, parent *core.Contain
return child, nil
}

type containerWithDefaultTerminalCmdArgs struct {
core.DefaultTerminalCmdOpts
}

func (s *containerSchema) withDefaultTerminalCmd(
ctx context.Context,
ctr *core.Container,
args struct {
Args []string
},
args containerWithDefaultTerminalCmdArgs,
) (*core.Container, error) {
ctr = ctr.Clone()
ctr.DefaultTerminalCmd = args.Args
ctr.DefaultTerminalCmd = &args.DefaultTerminalCmdOpts
return ctr, nil
}

type containerTerminalArgs struct {
core.TerminalArgs
}

func (s *containerSchema) terminal(
ctx context.Context,
ctr dagql.Instance[*core.Container],
args struct {
Cmd *[]string
},
args containerTerminalArgs,
) (*core.Terminal, error) {
var shellArgs []string
if args.Cmd != nil {
shellArgs = *args.Cmd
} else {
// if no override args specified, use default shell
shellArgs = ctr.Self.DefaultTerminalCmd
if args.Cmd == nil || len(args.Cmd) == 0 {
args.Cmd = ctr.Self.DefaultTerminalCmd.Args
}
Comment on lines +1365 to +1367
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just hit this today - this line can panic if ctr.Self.DefaultTerminalCmd isn't set.

goroutine 457496 [running]:
runtime/debug.Stack()
	/usr/local/go/src/runtime/debug/stack.go:24 +0x5e
github.com/dagger/dagger/dagql.(*Server).resolvePath.func1()
	/app/dagql/server.go:642 +0x74
panic({0x1bd65a0?, 0x3139130?})
	/usr/local/go/src/runtime/panic.go:914 +0x21f
github.com/dagger/dagger/core/schema.(*containerSchema).terminal(0x1d3a040?, {0x2299038, 0xc00330eea0}, {0xc005579280, 0xc00305f340, {0x0, 0x1, 0xc0033daf60, 0xc00394bed8}, 0x0}, ...)
	/app/core/schema/container.go:1366 +0xc9
github.com/dagger/dagger/dagql.NodeFunc[...].func1({0xc005579280, 0xc00305f340, {0x0, 0x1, 0xc0033daf60, 0xc00394bed8}, 0x0}, 0xc00330ef00)
	/app/dagql/objects.go:355 +0x184
github.com/dagger/dagger/dagql.Class[...].Call(0x22c3500?, {0x2299038?, 0xc00330eea0?}, {0xc005579280, 0xc00305f340, {0x0, 0x1, 0xc0033daf60, 0xc00394bed8}, 0x0}, ...)
	/app/dagql/objects.go:202 +0x15d
github.com/dagger/dagger/dagql.Instance[...].Select(0x22b3a20, {0x2299038, 0xc00330eea0}, {{0xc000cd2508, 0x8}, {0x319c680, 0x0, 0x0}, 0x0})
	/app/dagql/objects.go:271 +0x2fe
github.com/dagger/dagger/dagql.(*Server).cachedSelect.func1({0x2299038?, 0xc00330eea0?})
	/app/dagql/server.go:583 +0x49
github.com/dagger/dagger/tracing.AroundFunc.ProgrockAroundFunc.func1({0x2299038, 0xc00330ed80})
	/app/tracing/graphql.go:102 +0x5c4
github.com/dagger/dagger/tracing.AroundFunc.SpanAroundFunc.func2({0x2299038, 0xc003e691a0})
	/app/tracing/graphql.go:33 +0xa6
github.com/dagger/dagger/dagql.(*cacheMap[...]).GetOrInitializeOnHit(0x22bf680, {0x2299038, 0xc003e68ea0}, {0xc003775130, 0x47}, 0xc004ccb788, 0xc0031f9b08)
	/app/dagql/cachemap.go:85 +0x272
github.com/dagger/dagger/dagql.(*cacheMap[...]).GetOrInitialize(0xc0031f9b48?, {0x2299038?, 0xc003e68ea0?}, {0xc003775130?, 0x10c0f25?}, 0x229c200?)
	/app/dagql/cachemap.go:60 +0x45
github.com/dagger/dagger/dagql.(*Server).cachedSelect(0xc003e1a4b0, {0x2299038, 0xc004a82630}, {0x229c200, 0xc002c9ac40}, {{0xc000cd2508, 0x8}, {0x319c680, 0x0, 0x0}, ...})
	/app/dagql/server.go:592 +0x203
github.com/dagger/dagger/dagql.(*Server).resolvePath(0x1052c6b?, {0x2299038, 0xc004a82630}, {0x229c200?, 0xc002c9ac40?}, {{0xc000cd2508, 0x8}, {{0xc000cd2508, 0x8}, {0x319c680, ...}, ...}, ...})
	/app/dagql/server.go:651 +0x13e
github.com/dagger/dagger/dagql.(*Server).Resolve.func1()
	/app/dagql/server.go:405 +0x7f
github.com/dagger/dagger/dagql.(*Server).Resolve.(*ErrorPool).Go.func3()
	/go/pkg/mod/github.com/sourcegraph/conc@v0.3.0/pool/error_pool.go:30 +0x22
github.com/sourcegraph/conc/pool.(*Pool).worker(0xc001825858?)
	/go/pkg/mod/github.com/sourcegraph/conc@v0.3.0/pool/pool.go:154 +0x6f
github.com/sourcegraph/conc/panics.(*Catcher).Try(0x445edc?, 0x6?)
	/go/pkg/mod/github.com/sourcegraph/conc@v0.3.0/panics/panics.go:23 +0x48
github.com/sourcegraph/conc.(*WaitGroup).Go.func1()
	/go/pkg/mod/github.com/sourcegraph/conc@v0.3.0/waitgroup.go:32 +0x56
created by github.com/sourcegraph/conc.(*WaitGroup).Go in goroutine 372437
	/go/pkg/mod/github.com/sourcegraph/conc@v0.3.0/waitgroup.go:30 +0x73


if args.ExperimentalPrivilegedNesting == nil {
args.ExperimentalPrivilegedNesting = &ctr.Self.DefaultTerminalCmd.ExperimentalPrivilegedNesting
}

if args.InsecureRootCapabilities == nil {
args.InsecureRootCapabilities = &ctr.Self.DefaultTerminalCmd.InsecureRootCapabilities
}

// if still no args, default to sh
if len(shellArgs) == 0 {
shellArgs = []string{"sh"}
if len(args.Cmd) == 0 {
args.Cmd = []string{"sh"}
}

term, handler, err := ctr.Self.Terminal(ctr.ID(), shellArgs)
term, handler, err := ctr.Self.Terminal(ctr.ID(), &args.TerminalArgs)
if err != nil {
return nil, err
}
Expand Down
22 changes: 18 additions & 4 deletions core/terminal.go
Expand Up @@ -39,7 +39,19 @@ func (term *Terminal) WebsocketURL() string {
return fmt.Sprintf("ws://dagger/%s", term.Endpoint)
}

func (container *Container) Terminal(svcID *idproto.ID, args []string) (*Terminal, http.Handler, error) {
type TerminalArgs struct {
Cmd []string `default:"[]"`

// Provide dagger access to the executed command
// Do not use this option unless you trust the command being executed.
// The command being executed WILL BE GRANTED FULL ACCESS TO YOUR HOST FILESYSTEM
ExperimentalPrivilegedNesting *bool `default:"false"`

// Grant the process all root capabilities
InsecureRootCapabilities *bool `default:"false"`
Comment on lines +48 to +51
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah fun, I guess these need to be pointers so we can tell if you meant to override back to false.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly, I was confused at first but then I realised it was the only solution because we're using boolean :/

}

func (container *Container) Terminal(svcID *idproto.ID, args *TerminalArgs) (*Terminal, http.Handler, error) {
termID, err := svcID.Digest()
if err != nil {
return nil, nil, err
Expand Down Expand Up @@ -79,13 +91,15 @@ func (container *Container) runTerminal(
svcID *idproto.ID,
conn *websocket.Conn,
clientMetadata *engine.ClientMetadata,
args []string,
args *TerminalArgs,
) error {
container = container.Clone()

container, err := container.WithExec(ctx, ContainerExecOpts{
Args: args,
SkipEntrypoint: true,
Args: args.Cmd,
SkipEntrypoint: true,
ExperimentalPrivilegedNesting: *args.ExperimentalPrivilegedNesting,
InsecureRootCapabilities: *args.InsecureRootCapabilities,
})
if err != nil {
return fmt.Errorf("failed to create container for interactive terminal: %w", err)
Expand Down
38 changes: 36 additions & 2 deletions docs/docs-graphql/schema.graphqls
Expand Up @@ -348,7 +348,24 @@ type Container {
"""
If set, override the container's default terminal command and invoke these command arguments instead.
"""
cmd: [String!]
cmd: [String!] = []

"""
Provides Dagger access to the executed command.

Do not use this option unless you trust the command being executed; the
command being executed WILL BE GRANTED FULL ACCESS TO YOUR HOST FILESYSTEM.
"""
experimentalPrivilegedNesting: Boolean = false

"""
Execute the command with all root capabilities. This is similar to running a
command with "sudo" or executing "docker run" with the "--privileged" flag.
Containerization does not provide any security guarantees when using this
option. It should only be used when absolutely necessary and only with
trusted commands.
"""
insecureRootCapabilities: Boolean = false
): Terminal!

"""Retrieves the user to be set for all commands."""
Expand All @@ -366,6 +383,23 @@ type Container {
withDefaultTerminalCmd(
"""The args of the command."""
args: [String!]!

"""
Provides Dagger access to the executed command.

Do not use this option unless you trust the command being executed; the
command being executed WILL BE GRANTED FULL ACCESS TO YOUR HOST FILESYSTEM.
"""
experimentalPrivilegedNesting: Boolean = false

"""
Execute the command with all root capabilities. This is similar to running a
command with "sudo" or executing "docker run" with the "--privileged" flag.
Containerization does not provide any security guarantees when using this
option. It should only be used when absolutely necessary and only with
trusted commands.
"""
insecureRootCapabilities: Boolean = false
): Container!

"""Retrieves this container plus a directory written at the given path."""
Expand Down Expand Up @@ -432,7 +466,7 @@ type Container {
args: [String!]!

"""
Provides dagger access to the executed command.
Provides Dagger access to the executed command.

Do not use this option unless you trust the command being executed; the
command being executed WILL BE GRANTED FULL ACCESS TO YOUR HOST FILESYSTEM.
Expand Down
47 changes: 42 additions & 5 deletions sdk/elixir/lib/dagger/gen/container.ex

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 36 additions & 2 deletions sdk/go/dagger.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.