e0e362d700
SDK Tests / changes (push) Successful in 2m29s
Real E2E Tests / changes (push) Successful in 2m29s
Deploy Docs Pages / build (push) Has been cancelled
Deploy Docs Pages / deploy (push) Has been cancelled
Real E2E Tests / JavaScript E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Python E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Java E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / C# E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Go E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Real E2E CI (push) Has been cancelled
SDK Tests / SDK CI (push) Has been cancelled
SDK Tests / CLI Tests (push) Has been cancelled
SDK Tests / Python SDK Quality (code-interpreter) (push) Has been cancelled
SDK Tests / Python SDK Quality (sandbox) (push) Has been cancelled
SDK Tests / Python SDK Tests (code-interpreter) (push) Has been cancelled
SDK Tests / JavaScript SDK Quality And Tests (code-interpreter) (push) Has been cancelled
SDK Tests / JavaScript SDK Quality And Tests (sandbox) (push) Has been cancelled
SDK Tests / Python SDK Tests (sandbox) (push) Has been cancelled
SDK Tests / CLI Quality (push) Has been cancelled
SDK Tests / Kotlin SDK Quality And Tests (sandbox) (push) Has been cancelled
SDK Tests / Kotlin SDK Quality And Tests (code-interpreter) (push) Has been cancelled
SDK Tests / C# SDK Quality And Tests (code-interpreter) (push) Has been cancelled
SDK Tests / C# SDK Quality And Tests (sandbox) (push) Has been cancelled
SDK Tests / Go SDK Quality And Tests (push) Has been cancelled
160 lines
4.8 KiB
Go
160 lines
4.8 KiB
Go
// Copyright 2026 Alibaba Group Holding Ltd.
|
|
//
|
|
// 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 mitmproxy
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"os/user"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
"syscall"
|
|
|
|
"github.com/alibaba/opensandbox/egress/pkg/constants"
|
|
"github.com/alibaba/opensandbox/egress/pkg/log"
|
|
"github.com/alibaba/opensandbox/internal/safego"
|
|
)
|
|
|
|
const RunAsUser = "mitmproxy"
|
|
|
|
// Loopback: transparent mode receives via REDIRECT; do not listen on 0.0.0.0 in the netns.
|
|
// Kept as a Go constant only for the startup log line; the actual listen_host is set in
|
|
// /var/lib/mitmproxy/.mitmproxy/config.yaml (shipped via the egress Dockerfile).
|
|
const listenHostLoopback = "127.0.0.1"
|
|
|
|
// systemScriptPath: bundled system addon shipped via the egress Dockerfile
|
|
// (COPY components/egress/mitmscripts /var/egress/mitmscripts). Always loaded.
|
|
const systemScriptPath = "/var/egress/mitmscripts/system.py"
|
|
|
|
// Config: mitmdump --mode transparent. Static options (mode, connection_strategy,
|
|
// listen_host, stream_large_bodies, ignore_hosts,
|
|
// ssl_verify_upstream_trusted_confdir) live in
|
|
// /var/lib/mitmproxy/.mitmproxy/config.yaml and are auto-loaded by mitmdump.
|
|
// This struct carries only per-launch dynamic values that override those
|
|
// defaults via `--set`.
|
|
type Config struct {
|
|
ListenPort int
|
|
UserName string
|
|
// ScriptPaths are optional user-supplied addons, loaded after the system addon
|
|
// in the order given. Parsed from the comma-separated OPENSANDBOX_EGRESS_MITMPROXY_SCRIPT env var.
|
|
ScriptPaths []string
|
|
// OnExit is called (if non-nil) when mitmdump exits. Called from a background goroutine.
|
|
OnExit func(error)
|
|
}
|
|
|
|
// Running: child mitmdump; use GracefulShutdown to SIGTERM+reap before process exit.
|
|
type Running struct {
|
|
Cmd *exec.Cmd
|
|
done chan error
|
|
}
|
|
|
|
func LookupUser(userName string) (uid, gid uint32, home string, err error) {
|
|
if strings.TrimSpace(userName) == "" {
|
|
userName = RunAsUser
|
|
}
|
|
u, err := user.Lookup(userName)
|
|
if err != nil {
|
|
return 0, 0, "", err
|
|
}
|
|
uid64, err := strconv.ParseUint(u.Uid, 10, 32)
|
|
if err != nil {
|
|
return 0, 0, "", err
|
|
}
|
|
gid64, err := strconv.ParseUint(u.Gid, 10, 32)
|
|
if err != nil {
|
|
return 0, 0, "", err
|
|
}
|
|
return uint32(uid64), uint32(gid64), u.HomeDir, nil
|
|
}
|
|
|
|
// Launch starts mitmdump in the background; check Wait/GracefulShutdown on the returned Running.
|
|
func Launch(cfg Config) (*Running, error) {
|
|
if runtime.GOOS != "linux" {
|
|
return nil, fmt.Errorf("mitmproxy: transparent mitmdump is only supported on linux")
|
|
}
|
|
|
|
if cfg.ListenPort <= 0 {
|
|
return nil, fmt.Errorf("mitmproxy: invalid listen port")
|
|
}
|
|
uname := cfg.UserName
|
|
if strings.TrimSpace(uname) == "" {
|
|
uname = RunAsUser
|
|
}
|
|
uid, gid, home, err := LookupUser(uname)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("mitmproxy: lookup user %q: %w", uname, err)
|
|
}
|
|
|
|
args := buildMitmdumpArgs(cfg)
|
|
|
|
cmd := exec.Command("mitmdump", args...)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
cmd.SysProcAttr = &syscall.SysProcAttr{
|
|
Credential: &syscall.Credential{Uid: uid, Gid: gid},
|
|
}
|
|
// HOME determines mitm's confdir (~/.mitmproxy) which holds both the CA
|
|
// and the baked-in config.yaml.
|
|
cmd.Env = buildMitmdumpEnv(os.Environ(), home)
|
|
|
|
if err := cmd.Start(); err != nil {
|
|
return nil, fmt.Errorf("mitmproxy: start mitmdump: %w", err)
|
|
}
|
|
done := make(chan error, 1)
|
|
onExit := cfg.OnExit
|
|
safego.Go(func() {
|
|
err := cmd.Wait()
|
|
done <- err
|
|
if onExit != nil {
|
|
onExit(err)
|
|
}
|
|
})
|
|
|
|
log.Infof("[mitmproxy] mitmdump started (pid %d, transparent on %s:%d)", cmd.Process.Pid, listenHostLoopback, cfg.ListenPort)
|
|
return &Running{Cmd: cmd, done: done}, nil
|
|
}
|
|
|
|
func buildMitmdumpArgs(cfg Config) []string {
|
|
args := []string{
|
|
"--listen-port", strconv.Itoa(cfg.ListenPort),
|
|
"--set", "flow_detail=0",
|
|
}
|
|
|
|
if trustDir := strings.TrimSpace(os.Getenv(constants.EnvMitmproxyUpstreamTrustDir)); trustDir != "" {
|
|
args = append(args, "--set", "ssl_verify_upstream_trusted_confdir="+trustDir)
|
|
}
|
|
|
|
if constants.IsTruthy(os.Getenv(constants.EnvMitmproxySslInsecure)) {
|
|
args = append(args, "--set", "ssl_insecure=true")
|
|
}
|
|
|
|
args = append(args, "-s", systemScriptPath)
|
|
for _, p := range cfg.ScriptPaths {
|
|
if s := strings.TrimSpace(p); s != "" {
|
|
args = append(args, "-s", s)
|
|
}
|
|
}
|
|
return args
|
|
}
|
|
|
|
func buildMitmdumpEnv(base []string, home string) []string {
|
|
env := make([]string, 0, len(base)+1)
|
|
env = append(env, base...)
|
|
env = append(env, "HOME="+home)
|
|
return env
|
|
}
|