add local HTTPS reverse proxy
This commit is contained in:
parent
be242e98de
commit
d75e5a3ff3
|
@ -47,9 +47,10 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Forward struct {
|
type Forward struct {
|
||||||
scheme string
|
scheme string
|
||||||
pattern string
|
pattern string
|
||||||
port string
|
port string
|
||||||
|
localTLS bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var authorizer telebit.Authorizer
|
var authorizer telebit.Authorizer
|
||||||
|
@ -83,6 +84,7 @@ func main() {
|
||||||
secret := flag.String("secret", "", "the same secret used by telebit-relay (used for JWT authentication)")
|
secret := flag.String("secret", "", "the same secret used by telebit-relay (used for JWT authentication)")
|
||||||
token := flag.String("token", "", "a pre-generated token to give the server (instead of generating one with --secret)")
|
token := flag.String("token", "", "a pre-generated token to give the server (instead of generating one with --secret)")
|
||||||
bindAddrsStr := flag.String("listen", "", "list of bind addresses on which to listen, such as localhost:80, or :443")
|
bindAddrsStr := flag.String("listen", "", "list of bind addresses on which to listen, such as localhost:80, or :443")
|
||||||
|
tlsLocals := flag.String("tls-locals", "", "like --locals, but TLS will be used to connect to the local port")
|
||||||
locals := flag.String("locals", "", "a list of <from-domain>:<to-port>")
|
locals := flag.String("locals", "", "a list of <from-domain>:<to-port>")
|
||||||
portToPorts := flag.String("port-forward", "", "a list of <from-port>:<to-port> for raw port-forwarding")
|
portToPorts := flag.String("port-forward", "", "a list of <from-port>:<to-port> for raw port-forwarding")
|
||||||
verbose := flag.Bool("verbose", false, "log excessively")
|
verbose := flag.Bool("verbose", false, "log excessively")
|
||||||
|
@ -147,6 +149,32 @@ func main() {
|
||||||
domains = append(domains, domain)
|
domains = append(domains, domain)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if 0 == len(*tlsLocals) {
|
||||||
|
*tlsLocals = os.Getenv("TLS_LOCALS")
|
||||||
|
}
|
||||||
|
for _, cfg := range strings.Fields(strings.ReplaceAll(*tlsLocals, ",", " ")) {
|
||||||
|
parts := strings.Split(cfg, ":")
|
||||||
|
last := len(parts) - 1
|
||||||
|
port := parts[last]
|
||||||
|
domain := parts[last-1]
|
||||||
|
scheme := ""
|
||||||
|
if len(parts) > 2 {
|
||||||
|
scheme = parts[0]
|
||||||
|
}
|
||||||
|
forwards = append(forwards, Forward{
|
||||||
|
scheme: scheme,
|
||||||
|
pattern: domain,
|
||||||
|
port: port,
|
||||||
|
localTLS: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
// don't load wildcard into jwt domains
|
||||||
|
if "*" == domain {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
domains = append(domains, domain)
|
||||||
|
}
|
||||||
|
|
||||||
if 0 == len(*portToPorts) {
|
if 0 == len(*portToPorts) {
|
||||||
*portToPorts = os.Getenv("PORT_FORWARDS")
|
*portToPorts = os.Getenv("PORT_FORWARDS")
|
||||||
}
|
}
|
||||||
|
@ -467,7 +495,12 @@ func muxAll(
|
||||||
for _, fwd := range forwards {
|
for _, fwd := range forwards {
|
||||||
//mux.ForwardTCP("*", "localhost:"+fwd.port, 120*time.Second)
|
//mux.ForwardTCP("*", "localhost:"+fwd.port, 120*time.Second)
|
||||||
if "https" == fwd.scheme {
|
if "https" == fwd.scheme {
|
||||||
mux.ReverseProxyHTTP(fwd.pattern, "localhost:"+fwd.port, 120*time.Second, "[Servername Reverse Proxy]")
|
if fwd.localTLS {
|
||||||
|
// this doesn't make much sense, but... security theatre
|
||||||
|
mux.ReverseProxyHTTPS(fwd.pattern, "localhost:"+fwd.port, 120*time.Second, "[Servername Reverse Proxy TLS]")
|
||||||
|
} else {
|
||||||
|
mux.ReverseProxyHTTP(fwd.pattern, "localhost:"+fwd.port, 120*time.Second, "[Servername Reverse Proxy]")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
mux.ForwardTCP(fwd.pattern, "localhost:"+fwd.port, 120*time.Second, "[Servername Forward]")
|
mux.ForwardTCP(fwd.pattern, "localhost:"+fwd.port, 120*time.Second, "[Servername Forward]")
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ VERBOSE_RAW=${VERBOSE_RAW:-}
|
||||||
--secret "$CLIENT_SECRET" \
|
--secret "$CLIENT_SECRET" \
|
||||||
--tunnel-relay-url $TUNNEL_RELAY_URL \
|
--tunnel-relay-url $TUNNEL_RELAY_URL \
|
||||||
--listen "$LISTEN" \
|
--listen "$LISTEN" \
|
||||||
|
--tls-locals "$TLS_LOCALS" \
|
||||||
--locals "$LOCALS" \
|
--locals "$LOCALS" \
|
||||||
--acme-agree=${ACME_AGREE} \
|
--acme-agree=${ACME_AGREE} \
|
||||||
--acme-email "$ACME_EMAIL" \
|
--acme-email "$ACME_EMAIL" \
|
||||||
|
|
10
routemux.go
10
routemux.go
|
@ -158,6 +158,16 @@ func (m *RouteMux) ForwardTCP(servername string, target string, timeout time.Dur
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *RouteMux) ReverseProxyHTTPS(servername string, target string, timeout time.Duration, comment ...string) error {
|
||||||
|
m.routes = append(m.routes, meta{
|
||||||
|
addr: servername,
|
||||||
|
terminate: false,
|
||||||
|
handler: NewTheatricalProxier(target, timeout),
|
||||||
|
comment: append(comment, "")[0],
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (m *RouteMux) ReverseProxyHTTP(servername string, target string, timeout time.Duration, comment ...string) error {
|
func (m *RouteMux) ReverseProxyHTTP(servername string, target string, timeout time.Duration, comment ...string) error {
|
||||||
m.routes = append(m.routes, meta{
|
m.routes = append(m.routes, meta{
|
||||||
addr: servername,
|
addr: servername,
|
||||||
|
|
97
telebit.go
97
telebit.go
|
@ -69,15 +69,72 @@ func NewForwarder(target string, timeout time.Duration) HandlerFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewTheatricalProxier exists because... reasons... but should not be used
|
||||||
|
func NewTheatricalProxier(target string, timeout time.Duration) HandlerFunc {
|
||||||
|
return newReverseProxier(target, timeout, true)
|
||||||
|
}
|
||||||
|
|
||||||
func NewReverseProxier(target string, timeout time.Duration) HandlerFunc {
|
func NewReverseProxier(target string, timeout time.Duration) HandlerFunc {
|
||||||
|
return newReverseProxier(target, timeout, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newReverseProxier(target string, timeout time.Duration, theatre bool) HandlerFunc {
|
||||||
// TODO accept listener?
|
// TODO accept listener?
|
||||||
proxyListener := httpshim.NewListener()
|
proxyListener := httpshim.NewListener()
|
||||||
myURL, err := url.Parse("http://" + target)
|
scheme := "http://"
|
||||||
|
if theatre {
|
||||||
|
scheme = "https://"
|
||||||
|
}
|
||||||
|
targetURL, err := url.Parse(scheme + target)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
// TODO headers
|
//proxyHandler := httputil.NewSingleHostReverseProxy(targetURL)
|
||||||
proxyHandler := httputil.NewSingleHostReverseProxy(myURL)
|
proxyHandler := &httputil.ReverseProxy{
|
||||||
|
Director: func(req *http.Request) {
|
||||||
|
req.Header.Del("X-Forwarded-For")
|
||||||
|
req.Header.Del("X-Forwarded-Proto")
|
||||||
|
req.Header.Del("X-Forwarded-Port")
|
||||||
|
|
||||||
|
targetQuery := targetURL.RawQuery
|
||||||
|
req.URL.Scheme = targetURL.Scheme
|
||||||
|
req.URL.Host = targetURL.Host
|
||||||
|
req.URL.Path, req.URL.RawPath = joinURLPath(targetURL, req.URL)
|
||||||
|
if targetQuery == "" || req.URL.RawQuery == "" {
|
||||||
|
req.URL.RawQuery = targetQuery + req.URL.RawQuery
|
||||||
|
} else {
|
||||||
|
req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
|
||||||
|
}
|
||||||
|
if _, ok := req.Header["User-Agent"]; !ok {
|
||||||
|
// explicitly disable User-Agent so it's not set to default value
|
||||||
|
req.Header.Set("User-Agent", "")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if theatre {
|
||||||
|
/*
|
||||||
|
// TODO we could take control of the SNI here
|
||||||
|
proxyHandler.Transport = &http.Transport{
|
||||||
|
DialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||||
|
// would need timeout
|
||||||
|
dialer := tls.Dialer{
|
||||||
|
Config: &tls.Config{
|
||||||
|
ServerName: "localhost",
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
TLSHandshakeTimeout: 10 * time.Second,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return dialer.DialContext(ctx, network, addr)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
//*/
|
||||||
|
///*
|
||||||
|
proxyHandler.Transport = &http.Transport{
|
||||||
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||||
|
TLSHandshakeTimeout: 10 * time.Second,
|
||||||
|
}
|
||||||
|
//*/
|
||||||
|
}
|
||||||
proxyServer := &http.Server{
|
proxyServer := &http.Server{
|
||||||
Handler: proxyHandler,
|
Handler: proxyHandler,
|
||||||
}
|
}
|
||||||
|
@ -92,6 +149,40 @@ func NewReverseProxier(target string, timeout time.Duration) HandlerFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Taken from https://golang.org/src/net/http/httputil/reverseproxy.go
|
||||||
|
func joinURLPath(a, b *url.URL) (path, rawpath string) {
|
||||||
|
if a.RawPath == "" && b.RawPath == "" {
|
||||||
|
return singleJoiningSlash(a.Path, b.Path), ""
|
||||||
|
}
|
||||||
|
// Same as singleJoiningSlash, but uses EscapedPath to determine
|
||||||
|
// whether a slash should be added
|
||||||
|
apath := a.EscapedPath()
|
||||||
|
bpath := b.EscapedPath()
|
||||||
|
|
||||||
|
aslash := strings.HasSuffix(apath, "/")
|
||||||
|
bslash := strings.HasPrefix(bpath, "/")
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case aslash && bslash:
|
||||||
|
return a.Path + b.Path[1:], apath + bpath[1:]
|
||||||
|
case !aslash && !bslash:
|
||||||
|
return a.Path + "/" + b.Path, apath + "/" + bpath
|
||||||
|
}
|
||||||
|
return a.Path + b.Path, apath + bpath
|
||||||
|
}
|
||||||
|
|
||||||
|
func singleJoiningSlash(a, b string) string {
|
||||||
|
aslash := strings.HasSuffix(a, "/")
|
||||||
|
bslash := strings.HasPrefix(b, "/")
|
||||||
|
switch {
|
||||||
|
case aslash && bslash:
|
||||||
|
return a + b[1:]
|
||||||
|
case !aslash && !bslash:
|
||||||
|
return a + "/" + b
|
||||||
|
}
|
||||||
|
return a + b
|
||||||
|
}
|
||||||
|
|
||||||
// Forward port-forwards a relay (websocket) client to a target (local) server
|
// Forward port-forwards a relay (websocket) client to a target (local) server
|
||||||
func Forward(client net.Conn, target net.Conn, timeout time.Duration) error {
|
func Forward(client net.Conn, target net.Conn, timeout time.Duration) error {
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue