Add proxy auth

Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
This commit is contained in:
Hadi
2026-05-19 14:00:57 +02:00
parent 7bbc00880a
commit 4643989ab6
3 changed files with 36 additions and 1 deletions
+1
View File
@@ -25,6 +25,7 @@ type Config struct {
ProjectDir string `mapstructure:"project_dir"` ProjectDir string `mapstructure:"project_dir"`
PluginsDir string `mapstructure:"plugins_dir"` PluginsDir string `mapstructure:"plugins_dir"`
UpstreamProxy string `mapstructure:"upstream_proxy"` UpstreamProxy string `mapstructure:"upstream_proxy"`
ProxyAuth string `mapstructure:"proxy_auth"`
MaxBodySizeMB int `mapstructure:"max_body_size_mb"` MaxBodySizeMB int `mapstructure:"max_body_size_mb"`
ExternalEditor string `mapstructure:"external_editor"` ExternalEditor string `mapstructure:"external_editor"`
} `mapstructure:"app"` } `mapstructure:"app"`
+1
View File
@@ -5,6 +5,7 @@ app:
project_dir: ~/.local/share/spilltea project_dir: ~/.local/share/spilltea
plugins_dir: ~/.config/spilltea/plugins plugins_dir: ~/.config/spilltea/plugins
upstream_proxy: "" # e.g. http://corporate-proxy:8888 or http://user:pass@host:8888 upstream_proxy: "" # e.g. http://corporate-proxy:8888 or http://user:pass@host:8888
proxy_auth: "" # require basic auth to use the proxy, format: user:pass (empty = disabled)
max_body_size_mb: 50 # max response body size read into memory for large streamed responses (MB) max_body_size_mb: 50 # max response body size read into memory for large streamed responses (MB)
external_editor: "" # override $EDITOR for external editing (e.g. nvim, code --wait) external_editor: "" # override $EDITOR for external editing (e.g. nvim, code --wait)
+34 -1
View File
@@ -1,11 +1,13 @@
package proxy package proxy
import ( import (
"encoding/base64"
"fmt" "fmt"
"io" "io"
"log" "log"
"net/http" "net/http"
"os" "os"
"strings"
tea "charm.land/bubbletea/v2" tea "charm.land/bubbletea/v2"
"github.com/anotherhadi/spilltea/internal/config" "github.com/anotherhadi/spilltea/internal/config"
@@ -115,7 +117,7 @@ func Start(broker *intercept.Broker, mgr *plugins.Manager) error {
opts := &goproxy.Options{ opts := &goproxy.Options{
Addr: addr, Addr: addr,
StreamLargeBodies: 1024 * 1024 * 5, StreamLargeBodies: int64(cfg.MaxBodySizeMB) * 1024 * 1024,
CaRootPath: caPath, CaRootPath: caPath,
Upstream: cfg.UpstreamProxy, Upstream: cfg.UpstreamProxy,
} }
@@ -125,10 +127,41 @@ func Start(broker *intercept.Broker, mgr *plugins.Manager) error {
return err return err
} }
if cfg.ProxyAuth != "" {
parts := strings.SplitN(cfg.ProxyAuth, ":", 2)
if len(parts) == 2 {
wantUser, wantPass := parts[0], parts[1]
p.SetAuthProxy(func(res http.ResponseWriter, req *http.Request) (bool, error) {
user, pass, ok := parseBasicProxyAuth(req.Header.Get("Proxy-Authorization"))
if !ok || user != wantUser || pass != wantPass {
res.Header().Set("Proxy-Authenticate", `Basic realm="spilltea"`)
return false, fmt.Errorf("invalid credentials")
}
return true, nil
})
}
}
p.AddAddon(&interceptAddon{broker: broker, plugins: mgr}) p.AddAddon(&interceptAddon{broker: broker, plugins: mgr})
return p.Start() return p.Start()
} }
func parseBasicProxyAuth(header string) (user, pass string, ok bool) {
const prefix = "Basic "
if !strings.HasPrefix(header, prefix) {
return "", "", false
}
decoded, err := base64.StdEncoding.DecodeString(header[len(prefix):])
if err != nil {
return "", "", false
}
parts := strings.SplitN(string(decoded), ":", 2)
if len(parts) != 2 {
return "", "", false
}
return parts[0], parts[1], true
}
func dropResponse() *goproxy.Response { func dropResponse() *goproxy.Response {
return &goproxy.Response{ return &goproxy.Response{
StatusCode: 502, StatusCode: 502,