diff --git a/cmd/cli/commands/install-runner.go b/cmd/cli/commands/install-runner.go index 782672e78..ef9a1363b 100644 --- a/cmd/cli/commands/install-runner.go +++ b/cmd/cli/commands/install-runner.go @@ -138,7 +138,7 @@ func ensureStandaloneRunnerAvailable(ctx context.Context, printer standalone.Sta port = standalone.DefaultControllerPortCloud environment = "cloud" } - if err := standalone.CreateControllerContainer(ctx, dockerClient, port, host, environment, false, gpu, "", modelStorageVolume, printer, engineKind, debug, false); err != nil { + if err := standalone.CreateControllerContainer(ctx, dockerClient, port, host, environment, false, gpu, "", modelStorageVolume, printer, engineKind, debug, false, ""); err != nil { return nil, fmt.Errorf("unable to initialize standalone model runner container: %w", err) } @@ -172,6 +172,7 @@ type runnerOptions struct { doNotTrack bool pullImage bool pruneContainers bool + proxyCert string } // runInstallOrStart is shared logic for install-runner and start-runner commands @@ -285,7 +286,7 @@ func runInstallOrStart(cmd *cobra.Command, opts runnerOptions, debug bool) error return fmt.Errorf("unable to initialize standalone model storage: %w", err) } // Create the model runner container. - if err := standalone.CreateControllerContainer(cmd.Context(), dockerClient, port, opts.host, environment, opts.doNotTrack, gpu, opts.backend, modelStorageVolume, asPrinter(cmd), engineKind, debug, vllmOnWSL); err != nil { + if err := standalone.CreateControllerContainer(cmd.Context(), dockerClient, port, opts.host, environment, opts.doNotTrack, gpu, opts.backend, modelStorageVolume, asPrinter(cmd), engineKind, debug, vllmOnWSL, opts.proxyCert); err != nil { return fmt.Errorf("unable to initialize standalone model runner container: %w", err) } @@ -300,6 +301,7 @@ func newInstallRunner() *cobra.Command { var backend string var doNotTrack bool var debug bool + var proxyCert string c := &cobra.Command{ Use: "install-runner", Short: "Install Docker Model Runner (Docker Engine only)", @@ -312,6 +314,7 @@ func newInstallRunner() *cobra.Command { doNotTrack: doNotTrack, pullImage: true, pruneContainers: false, + proxyCert: proxyCert, }, debug) }, ValidArgsFunction: completion.NoComplete, @@ -323,6 +326,7 @@ func newInstallRunner() *cobra.Command { Backend: &backend, DoNotTrack: &doNotTrack, Debug: &debug, + ProxyCert: &proxyCert, }) return c } diff --git a/cmd/cli/commands/reinstall-runner.go b/cmd/cli/commands/reinstall-runner.go index df7968db3..01798f5a0 100644 --- a/cmd/cli/commands/reinstall-runner.go +++ b/cmd/cli/commands/reinstall-runner.go @@ -12,6 +12,7 @@ func newReinstallRunner() *cobra.Command { var backend string var doNotTrack bool var debug bool + var proxyCert string c := &cobra.Command{ Use: "reinstall-runner", Short: "Reinstall Docker Model Runner (Docker Engine only)", @@ -24,6 +25,7 @@ func newReinstallRunner() *cobra.Command { doNotTrack: doNotTrack, pullImage: true, pruneContainers: true, + proxyCert: proxyCert, }, debug) }, ValidArgsFunction: completion.NoComplete, @@ -35,6 +37,7 @@ func newReinstallRunner() *cobra.Command { Backend: &backend, DoNotTrack: &doNotTrack, Debug: &debug, + ProxyCert: &proxyCert, }) return c } diff --git a/cmd/cli/commands/restart-runner.go b/cmd/cli/commands/restart-runner.go index 0de8cdb0f..85b80013b 100644 --- a/cmd/cli/commands/restart-runner.go +++ b/cmd/cli/commands/restart-runner.go @@ -11,6 +11,7 @@ func newRestartRunner() *cobra.Command { var gpuMode string var doNotTrack bool var debug bool + var proxyCert string c := &cobra.Command{ Use: "restart-runner", Short: "Restart Docker Model Runner (Docker Engine only)", @@ -30,6 +31,7 @@ func newRestartRunner() *cobra.Command { gpuMode: gpuMode, doNotTrack: doNotTrack, pullImage: false, + proxyCert: proxyCert, }, debug) }, ValidArgsFunction: completion.NoComplete, @@ -40,6 +42,7 @@ func newRestartRunner() *cobra.Command { GpuMode: &gpuMode, DoNotTrack: &doNotTrack, Debug: &debug, + ProxyCert: &proxyCert, }) return c } diff --git a/cmd/cli/commands/start-runner.go b/cmd/cli/commands/start-runner.go index b31b6a24c..8cab97493 100644 --- a/cmd/cli/commands/start-runner.go +++ b/cmd/cli/commands/start-runner.go @@ -11,6 +11,7 @@ func newStartRunner() *cobra.Command { var backend string var doNotTrack bool var debug bool + var proxyCert string c := &cobra.Command{ Use: "start-runner", Short: "Start Docker Model Runner (Docker Engine only)", @@ -21,6 +22,7 @@ func newStartRunner() *cobra.Command { backend: backend, doNotTrack: doNotTrack, pullImage: false, + proxyCert: proxyCert, }, debug) }, ValidArgsFunction: completion.NoComplete, @@ -31,6 +33,7 @@ func newStartRunner() *cobra.Command { Backend: &backend, DoNotTrack: &doNotTrack, Debug: &debug, + ProxyCert: &proxyCert, }) return c } diff --git a/cmd/cli/commands/utils.go b/cmd/cli/commands/utils.go index ee39f427f..33ba0ee42 100644 --- a/cmd/cli/commands/utils.go +++ b/cmd/cli/commands/utils.go @@ -182,7 +182,7 @@ func requireMinArgs(n int, cmdName string, usageArgs string) cobra.PositionalArg } } -// runnerOptions holds common runner configuration options +// runnerFlagOptions holds common runner configuration options type runnerFlagOptions struct { Port *uint16 Host *string @@ -190,6 +190,7 @@ type runnerFlagOptions struct { Backend *string DoNotTrack *bool Debug *bool + ProxyCert *string } // addRunnerFlags adds common runner flags to a command @@ -213,4 +214,7 @@ func addRunnerFlags(cmd *cobra.Command, opts runnerFlagOptions) { if opts.Debug != nil { cmd.Flags().BoolVar(opts.Debug, "debug", false, "Enable debug logging") } + if opts.ProxyCert != nil { + cmd.Flags().StringVar(opts.ProxyCert, "proxy-cert", "", "Path to a CA certificate file for proxy SSL inspection") + } } diff --git a/cmd/cli/docs/reference/docker_model_install-runner.yaml b/cmd/cli/docs/reference/docker_model_install-runner.yaml index 6ba6175fe..0da2321c4 100644 --- a/cmd/cli/docs/reference/docker_model_install-runner.yaml +++ b/cmd/cli/docs/reference/docker_model_install-runner.yaml @@ -66,6 +66,15 @@ options: experimentalcli: false kubernetes: false swarm: false + - option: proxy-cert + value_type: string + description: Path to a CA certificate file for proxy SSL inspection + deprecated: false + hidden: false + experimental: false + experimentalcli: false + kubernetes: false + swarm: false deprecated: false hidden: false experimental: false diff --git a/cmd/cli/docs/reference/docker_model_reinstall-runner.yaml b/cmd/cli/docs/reference/docker_model_reinstall-runner.yaml index 07349e6a5..213bf27a9 100644 --- a/cmd/cli/docs/reference/docker_model_reinstall-runner.yaml +++ b/cmd/cli/docs/reference/docker_model_reinstall-runner.yaml @@ -66,6 +66,15 @@ options: experimentalcli: false kubernetes: false swarm: false + - option: proxy-cert + value_type: string + description: Path to a CA certificate file for proxy SSL inspection + deprecated: false + hidden: false + experimental: false + experimentalcli: false + kubernetes: false + swarm: false deprecated: false hidden: false experimental: false diff --git a/cmd/cli/docs/reference/docker_model_restart-runner.yaml b/cmd/cli/docs/reference/docker_model_restart-runner.yaml index 99d8c4eab..652fb64b0 100644 --- a/cmd/cli/docs/reference/docker_model_restart-runner.yaml +++ b/cmd/cli/docs/reference/docker_model_restart-runner.yaml @@ -59,6 +59,15 @@ options: experimentalcli: false kubernetes: false swarm: false + - option: proxy-cert + value_type: string + description: Path to a CA certificate file for proxy SSL inspection + deprecated: false + hidden: false + experimental: false + experimentalcli: false + kubernetes: false + swarm: false deprecated: false hidden: false experimental: false diff --git a/cmd/cli/docs/reference/docker_model_start-runner.yaml b/cmd/cli/docs/reference/docker_model_start-runner.yaml index aec39c8d5..646e055fe 100644 --- a/cmd/cli/docs/reference/docker_model_start-runner.yaml +++ b/cmd/cli/docs/reference/docker_model_start-runner.yaml @@ -58,6 +58,15 @@ options: experimentalcli: false kubernetes: false swarm: false + - option: proxy-cert + value_type: string + description: Path to a CA certificate file for proxy SSL inspection + deprecated: false + hidden: false + experimental: false + experimentalcli: false + kubernetes: false + swarm: false deprecated: false hidden: false experimental: false diff --git a/cmd/cli/docs/reference/model_install-runner.md b/cmd/cli/docs/reference/model_install-runner.md index a0e7a84bd..eb4ee1b63 100644 --- a/cmd/cli/docs/reference/model_install-runner.md +++ b/cmd/cli/docs/reference/model_install-runner.md @@ -13,6 +13,7 @@ Install Docker Model Runner (Docker Engine only) | `--gpu` | `string` | `auto` | Specify GPU support (none\|auto\|cuda\|rocm\|musa\|cann) | | `--host` | `string` | `127.0.0.1` | Host address to bind Docker Model Runner | | `--port` | `uint16` | `0` | Docker container port for Docker Model Runner (default: 12434 for Docker Engine, 12435 for Cloud mode) | +| `--proxy-cert` | `string` | | Path to a CA certificate file for proxy SSL inspection | diff --git a/cmd/cli/docs/reference/model_reinstall-runner.md b/cmd/cli/docs/reference/model_reinstall-runner.md index be04e7723..e711f7107 100644 --- a/cmd/cli/docs/reference/model_reinstall-runner.md +++ b/cmd/cli/docs/reference/model_reinstall-runner.md @@ -13,6 +13,7 @@ Reinstall Docker Model Runner (Docker Engine only) | `--gpu` | `string` | `auto` | Specify GPU support (none\|auto\|cuda\|rocm\|musa\|cann) | | `--host` | `string` | `127.0.0.1` | Host address to bind Docker Model Runner | | `--port` | `uint16` | `0` | Docker container port for Docker Model Runner (default: 12434 for Docker Engine, 12435 for Cloud mode) | +| `--proxy-cert` | `string` | | Path to a CA certificate file for proxy SSL inspection | diff --git a/cmd/cli/docs/reference/model_restart-runner.md b/cmd/cli/docs/reference/model_restart-runner.md index b63f3a77a..80565a8df 100644 --- a/cmd/cli/docs/reference/model_restart-runner.md +++ b/cmd/cli/docs/reference/model_restart-runner.md @@ -12,6 +12,7 @@ Restart Docker Model Runner (Docker Engine only) | `--gpu` | `string` | `auto` | Specify GPU support (none\|auto\|cuda\|rocm\|musa\|cann) | | `--host` | `string` | `127.0.0.1` | Host address to bind Docker Model Runner | | `--port` | `uint16` | `0` | Docker container port for Docker Model Runner (default: 12434 for Docker Engine, 12435 for Cloud mode) | +| `--proxy-cert` | `string` | | Path to a CA certificate file for proxy SSL inspection | diff --git a/cmd/cli/docs/reference/model_start-runner.md b/cmd/cli/docs/reference/model_start-runner.md index 5376f8729..2e43a92c2 100644 --- a/cmd/cli/docs/reference/model_start-runner.md +++ b/cmd/cli/docs/reference/model_start-runner.md @@ -12,6 +12,7 @@ Start Docker Model Runner (Docker Engine only) | `--do-not-track` | `bool` | | Do not track models usage in Docker Model Runner | | `--gpu` | `string` | `auto` | Specify GPU support (none\|auto\|cuda\|rocm\|musa\|cann) | | `--port` | `uint16` | `0` | Docker container port for Docker Model Runner (default: 12434 for Docker Engine, 12435 for Cloud mode) | +| `--proxy-cert` | `string` | | Path to a CA certificate file for proxy SSL inspection | diff --git a/cmd/cli/pkg/standalone/containers.go b/cmd/cli/pkg/standalone/containers.go index 3ff5bcd21..cac89834f 100644 --- a/cmd/cli/pkg/standalone/containers.go +++ b/cmd/cli/pkg/standalone/containers.go @@ -91,7 +91,8 @@ func copyDockerConfigToContainer(ctx context.Context, dockerClient *client.Clien func execInContainer(ctx context.Context, dockerClient *client.Client, containerID, cmd string) error { execConfig := container.ExecOptions{ - Cmd: []string{"sh", "-c", cmd}, + Cmd: []string{"sh", "-c", cmd}, + User: "root", } execResp, err := dockerClient.ContainerExecCreate(ctx, containerID, execConfig) if err != nil { @@ -267,8 +268,12 @@ func tryGetBindAscendMounts(printer StatusPrinter, debug bool) []mount.Mount { return newMounts } +// proxyCertContainerPath is the path where the proxy certificate will be mounted in the container. +// This location is used by update-ca-certificates to add the cert to the system trust store. +const proxyCertContainerPath = "/usr/local/share/ca-certificates/proxy-ca.crt" + // CreateControllerContainer creates and starts a controller container. -func CreateControllerContainer(ctx context.Context, dockerClient *client.Client, port uint16, host string, environment string, doNotTrack bool, gpu gpupkg.GPUSupport, backend string, modelStorageVolume string, printer StatusPrinter, engineKind types.ModelRunnerEngineKind, debug bool, vllmOnWSL bool) error { +func CreateControllerContainer(ctx context.Context, dockerClient *client.Client, port uint16, host string, environment string, doNotTrack bool, gpu gpupkg.GPUSupport, backend string, modelStorageVolume string, printer StatusPrinter, engineKind types.ModelRunnerEngineKind, debug bool, vllmOnWSL bool, proxyCert string) error { imageName := controllerImageName(gpu, backend) // Set up the container configuration. @@ -288,6 +293,7 @@ func CreateControllerContainer(ctx context.Context, dockerClient *client.Client, env = append(env, proxyVar+"="+value) } } + config := &container.Config{ Image: imageName, Env: env, @@ -316,6 +322,15 @@ func CreateControllerContainer(ctx context.Context, dockerClient *client.Client, hostConfig.Mounts = append(hostConfig.Mounts, ascendMounts...) } + if proxyCert != "" { + hostConfig.Mounts = append(hostConfig.Mounts, mount.Mount{ + Type: mount.TypeBind, + Source: proxyCert, + Target: proxyCertContainerPath, + ReadOnly: true, + }) + } + portBindings := []nat.PortBinding{{HostIP: host, HostPort: portStr}} if os.Getenv("_MODEL_RUNNER_TREAT_DESKTOP_AS_MOBY") != "1" { // Don't bind the bridge gateway IP if we're treating Docker Desktop as Moby. @@ -437,6 +452,20 @@ func CreateControllerContainer(ctx context.Context, dockerClient *client.Client, printer.Printf("Warning: failed to copy Docker config: %v\n", err) } } + + // Add proxy certificate to the system CA bundle + if created && proxyCert != "" { + printer.Printf("Updating CA certificates...\n") + if err := execInContainer(ctx, dockerClient, resp.ID, "update-ca-certificates"); err != nil { + printer.Printf("Warning: failed to update CA certificates: %v\n", err) + } else { + printer.Printf("Restarting container to apply CA certificate...\n") + if err := dockerClient.ContainerRestart(ctx, resp.ID, container.StopOptions{}); err != nil { + printer.Printf("Warning: failed to restart container after adding CA certificate: %v\n", err) + } + } + } + return nil }