From 84e16230327d4d05b98f47358eb1b250d569b73a Mon Sep 17 00:00:00 2001 From: Bel LaPointe <153096461+breel-render@users.noreply.github.com> Date: Sun, 31 May 2026 08:45:06 -0700 Subject: [PATCH] wip --- config.go | 51 ++++++++++++++++++++++++++ go.mod | 5 +++ go.sum | 4 +++ main.go | 90 ++++++++++++++++++++++++++++++++++++++++++++++ testdata/test.yaml | 6 ++++ 5 files changed, 156 insertions(+) create mode 100644 config.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go create mode 100644 testdata/test.yaml diff --git a/config.go b/config.go new file mode 100644 index 0000000..4ac4474 --- /dev/null +++ b/config.go @@ -0,0 +1,51 @@ +package main + +import ( + "flag" + "fmt" + "os" + + "gopkg.in/yaml.v2" +) + +type ( + Config struct { + Cert Cert + Port int + Endpoints map[string]Endpoint + } + + Cert struct { + CRT string + Key string + } + + Endpoint struct { + From string + To string + BasicAuth string + } +) + +func NewConfig() (Config, error) { + var c Config + + fs := flag.NewFlagSet(os.Args[0], flag.ContinueOnError) + fs.StringVar(&c.Cert.CRT, "crt", "", "path to .crt") + fs.StringVar(&c.Cert.Key, "key", "", "path to .key") + fs.IntVar(&c.Port, "p", 56112, "port to listen on") + f := fs.String("f", "/dev/null", `file of {endpoints:{"": {from:"", to:"", basicAuth:""}}}`) + if err := fs.Parse(os.Args[1:]); err != nil { + return c, err + } + + if b, err := os.ReadFile(*f); err != nil { + return c, err + } else if err := yaml.Unmarshal(b, &c); err != nil { + return c, err + } else if len(c.Endpoints) == 0 { + return c, fmt.Errorf("no endpoints configured") + } + + return c, nil +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..6017e80 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module rproxy4 + +go 1.24.2 + +require gopkg.in/yaml.v2 v2.4.0 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..dd0bc19 --- /dev/null +++ b/go.sum @@ -0,0 +1,4 @@ +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/main.go b/main.go new file mode 100644 index 0000000..6bd9330 --- /dev/null +++ b/main.go @@ -0,0 +1,90 @@ +package main + +import ( + "fmt" + "log" + "net/http" + "strings" +) + +func main() { + c, err := NewConfig() + if err != nil { + panic(err) + } + + s := &http.Server{ + Addr: fmt.Sprintf(":%d", c.Port), + Handler: c, + } + foo := s.ListenAndServe + if c.Cert.CRT != "" { + foo = func() error { + return s.ListenAndServeTLS(c.Cert.CRT, c.Cert.Key) + } + } + log.Printf("listening on %v...", s.Addr) + if err := foo(); err != nil { + panic(err) + } +} + +func (c Config) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodOptions { + cors(w) + return + } + + if r.URL.Scheme == "https" { + w.Header().Set("X-Forwarded-Proto", "https") + } + + if c.handleAdmin(w, r) { + return + } + + if !c.basicAuth(w, r) { + return + } + + http.Error(w, "not yet", http.StatusNotImplemented) +} + +func cors(w http.ResponseWriter) { + w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Set("Access-Control-Allow-Headers", "X-Auth-Token, content-type, Content-Type") + w.Header().Set("Content-Length", "0") + w.Header().Set("Content-Type", "text/plain") + w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, OPTIONS, TRACE, PATCH, HEAD, DELETE") + w.WriteHeader(http.StatusOK) +} + +func (c Config) endpoint(r *http.Request) string { + return strings.Split(r.Host, ".")[0] +} + +func (c Config) handleAdmin(w http.ResponseWriter, r *http.Request) bool { + switch c.endpoint(r) { + case "_": + panic("not impl: list") + case "home": + panic("not impl: home") + } + return false +} + +func (c Config) basicAuth(w http.ResponseWriter, r *http.Request) bool { + basicAuth := c.Endpoints[c.endpoint(r)].BasicAuth + if noAuth := basicAuth == ""; noAuth { + return true + } + + u, p, _ := r.BasicAuth() + if fmt.Sprintf("%s:%s", u, p) != basicAuth { + w.Header().Set("WWW-Authenticate", "Basic") + http.Error(w, "unexpected basic auth", http.StatusUnauthorized) + return false + } + + return true +} diff --git a/testdata/test.yaml b/testdata/test.yaml new file mode 100644 index 0000000..d76d26f --- /dev/null +++ b/testdata/test.yaml @@ -0,0 +1,6 @@ +#port: 51552 # priority over cli +endpoints: + foo: + from: foo.blapointe.com + to: http://192.168.0.86:51552 + basicAuth: u:p