Skip to content

Commit

Permalink
Custom proxy error message
Browse files Browse the repository at this point in the history
  • Loading branch information
Morishiri committed Dec 17, 2020
1 parent bbd497f commit bb1590a
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 10 deletions.
1 change: 1 addition & 0 deletions cmd/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ To expose port running on some local host e.g. 192.168.1.20 use 'loophole http <
func init() {
initServeCommand(httpCmd)
localEndpointSpecs.HTTPS = false
httpCmd.Flags().BoolVar(&displayOptions.DisableProxyErrorPage, "disable-proxy-error-page", false, "disable proxy error page and return 502 when backend is not available")

rootCmd.AddCommand(httpCmd)
}
8 changes: 6 additions & 2 deletions internal/app/loophole/loophole.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ func connectViaSSH(gatewayEndpoint lm.Endpoint, username string, authMethod ssh.
return serverSSHConnHTTPS
}

func createTLSReverseProxy(localEndpoint lm.Endpoint, siteID string, basicAuthUsername string, basicAuthPassword string) *http.Server {
func createTLSReverseProxy(localEndpoint lm.Endpoint, siteID string, basicAuthUsername string, basicAuthPassword string, displayOptions lm.DisplayOptions) *http.Server {
communication.StartLoading("Starting local TLS proxy server")
serverBuilder := httpserver.New().
WithHostname(siteID).
Expand All @@ -118,6 +118,10 @@ func createTLSReverseProxy(localEndpoint lm.Endpoint, siteID string, basicAuthUs
serverBuilder = serverBuilder.
WithBasicAuth(basicAuthUsername, basicAuthPassword)
}
if displayOptions.DisableProxyErrorPage {
serverBuilder = serverBuilder.
DisableProxyErrorPage()
}

if el := log.Debug(); el.Enabled() {
el.
Expand Down Expand Up @@ -256,7 +260,7 @@ func ForwardPort(config lm.ExposeHttpConfig) {

publicKeyAuthMethod, publicKey := parsePublicKey(config.Remote.IdentityFile)
siteID := registerDomain(config.Remote.APIEndpoint.URI(), &publicKey, config.Remote.SiteID)
server := createTLSReverseProxy(localEndpoint, siteID, config.Remote.BasicAuthUsername, config.Remote.BasicAuthPassword)
server := createTLSReverseProxy(localEndpoint, siteID, config.Remote.BasicAuthUsername, config.Remote.BasicAuthPassword, config.Display)
forward(config.Remote, config.Display, publicKeyAuthMethod, siteID, server, localEndpoint.URI(), []string{"https"})
}

Expand Down
7 changes: 4 additions & 3 deletions internal/app/loophole/models/DisplayOptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ package models

// DisplayOptions represents configuration used to display certain things in CLI
type DisplayOptions struct {
Verbose bool
QR bool
FeedbackFormURL string
Verbose bool
QR bool
FeedbackFormURL string
DisableProxyErrorPage bool
}
28 changes: 23 additions & 5 deletions internal/pkg/httpserver/httpserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ import (
"golang.org/x/net/webdav"
)

const (
logoURL = "https://raw.githubusercontent.com/loophole/website/master/static/img/logo.png"
)

type ServerBuilder interface {
WithHostname(string) ServerBuilder
Proxy() ProxyServerBuilder
Expand Down Expand Up @@ -52,14 +56,16 @@ func (sb *serverBuilder) ServeWebdav() WebdavServerBuilder {
type ProxyServerBuilder interface {
ToEndpoint(lm.Endpoint) ProxyServerBuilder
WithBasicAuth(string, string) ProxyServerBuilder
DisableProxyErrorPage() ProxyServerBuilder
Build() (*http.Server, error)
}
type proxyServerBuilder struct {
serverBuilder *serverBuilder
endpoint lm.Endpoint
basicAuthEnabled bool
basicAuthUsername string
basicAuthPassword string
serverBuilder *serverBuilder
endpoint lm.Endpoint
basicAuthEnabled bool
basicAuthUsername string
basicAuthPassword string
disableProxyErrorPage bool
}

func (psb *proxyServerBuilder) ToEndpoint(endpoint lm.Endpoint) ProxyServerBuilder {
Expand All @@ -74,11 +80,19 @@ func (psb *proxyServerBuilder) WithBasicAuth(username string, password string) P
return psb
}

func (psb *proxyServerBuilder) DisableProxyErrorPage() ProxyServerBuilder {
psb.disableProxyErrorPage = true
return psb
}

func (psb *proxyServerBuilder) Build() (*http.Server, error) {
proxy := httputil.NewSingleHostReverseProxy(&url.URL{
Scheme: psb.endpoint.Protocol,
Host: psb.endpoint.Hostname(),
})
if !psb.disableProxyErrorPage {
proxy.ErrorHandler = proxyErrorHandler
}

var server *http.Server

Expand Down Expand Up @@ -244,3 +258,7 @@ func getBasicAuthSecretParser(username string, hashedPassword string) auth.Secre
return ""
}
}

func proxyErrorHandler(w http.ResponseWriter, r *http.Request, err error) {
w.Write([]byte(fmt.Sprintf(proxyErrorTemplate, logoURL, err.Error())))
}
69 changes: 69 additions & 0 deletions internal/pkg/httpserver/proxyerrortemplate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package httpserver

const (
// first %s is logoUrl, second %s is error message
proxyErrorTemplate = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Loophole is running...</title>
<style>
html {
height: 100%%;
}
body {
max-height: 100%%;
font-family: system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu,
Cantarell, "Noto Sans", sans-serif, BlinkMacSystemFont, "Segoe UI",
Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
"Segoe UI Symbol";
overflow-y: auto;
}
.container {
text-align: center;
width: 800px;
height: fit-content;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
}
.error {
color: #fa383e;
}
</style>
</head>
<body>
<div class="container">
<img
src="%s"
width="500px"
alt="Loophole"
/>
<h1>Congratulations, your tunnel is up and running!</h1>
<p>
However... it looks like the application you're trying to expose is not
available.
<br />
<br />
Original error: <em class="error">%s</em>
<br />
<br />
<small>
This request would normally end up with 502 status code.
<br />
If that's what you intended please restart the tunnel with
<code>--disable-proxy-error-page</code> option
<br />
to remove this page and get regular 502 error.
</small>
</p>
</div>
</body>
</html>
`
)

0 comments on commit bb1590a

Please sign in to comment.