146 lines
2.6 KiB
Go
146 lines
2.6 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha256"
|
|
"crypto/subtle"
|
|
"flag"
|
|
"net"
|
|
"net/http"
|
|
"net/http/httputil"
|
|
"net/url"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/armon/go-proxyproto"
|
|
)
|
|
|
|
func main() {
|
|
var configFile string
|
|
flag.StringVar(&configFile, "config", "config.yaml", "Config file")
|
|
flag.Parse()
|
|
|
|
parseConfig(configFile)
|
|
|
|
handler := loghttpreq(proxy())
|
|
|
|
wg := sync.WaitGroup{}
|
|
|
|
for _, ld := range listenerDefs {
|
|
listener, err := net.Listen(ld.protocol, ld.address)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if ld.acceptProxy {
|
|
listener = &proxyproto.Listener{
|
|
Listener: listener,
|
|
}
|
|
}
|
|
|
|
wg.Add(1)
|
|
go func() {
|
|
http.Serve(listener, handler)
|
|
wg.Done()
|
|
}()
|
|
}
|
|
|
|
wg.Wait()
|
|
}
|
|
|
|
func rewrite(proxyRequest *httputil.ProxyRequest) {
|
|
target, ok := proxyRequest.In.Context().Value("target").(*url.URL)
|
|
if !ok {
|
|
panic("unreachable")
|
|
}
|
|
proxyRequest.SetURL(target)
|
|
proxyRequest.SetXForwarded()
|
|
}
|
|
|
|
func proxy() http.Handler {
|
|
revproxy := &httputil.ReverseProxy{
|
|
Rewrite: rewrite,
|
|
}
|
|
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
tmp := strings.Split(r.Host, ":")
|
|
if len(tmp) == 0 {
|
|
w.WriteHeader(400)
|
|
return
|
|
}
|
|
hst := tmp[0]
|
|
|
|
hd, ok := hostDefs[hst]
|
|
if !ok {
|
|
hd, ok = hostDefs[""]
|
|
if !ok {
|
|
w.WriteHeader(400)
|
|
return
|
|
}
|
|
}
|
|
|
|
escapedPath := r.URL.EscapedPath()
|
|
|
|
var pathDef pathDef
|
|
matched := false
|
|
for _, hostPathDef := range hd {
|
|
if !strings.HasPrefix(escapedPath, hostPathDef.path) {
|
|
continue
|
|
}
|
|
if !matched || len(hostPathDef.path) > len(pathDef.path) {
|
|
matched = true
|
|
pathDef = hostPathDef
|
|
}
|
|
}
|
|
|
|
if !matched {
|
|
w.WriteHeader(400)
|
|
return
|
|
}
|
|
|
|
r.URL.RawPath = escapedPath[len(pathDef.path):]
|
|
unescapedPath, err := url.PathUnescape(r.URL.RawPath)
|
|
if err != nil {
|
|
panic("unreachable")
|
|
}
|
|
r.URL.Path = unescapedPath
|
|
|
|
if pathDef.credentials != nil {
|
|
match := false
|
|
|
|
username, password, ok := r.BasicAuth()
|
|
if ok {
|
|
hash := sha256.New()
|
|
hash.Write([]byte(username))
|
|
usernameSum := hash.Sum(nil)
|
|
|
|
hash = sha256.New()
|
|
hash.Write([]byte(password))
|
|
passwordSum := hash.Sum(nil)
|
|
|
|
for _, cred := range pathDef.credentials {
|
|
res1 := subtle.ConstantTimeCompare(cred.username, usernameSum) == 1
|
|
res2 := subtle.ConstantTimeCompare(cred.password, passwordSum) == 1
|
|
if res1 && res2 {
|
|
match = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
if !match {
|
|
w.Header().Set("WWW-Authenticate", "Basic")
|
|
w.WriteHeader(401)
|
|
return
|
|
}
|
|
}
|
|
|
|
ctx := r.Context()
|
|
ctx = context.WithValue(ctx, "target", pathDef.targetURL)
|
|
r = r.WithContext(ctx)
|
|
|
|
w.Header().Add("Server", "dgrp")
|
|
|
|
revproxy.ServeHTTP(w, r)
|
|
})
|
|
}
|