Skip to content

Commit

Permalink
Merge pull request #80 from loophole/info-about-client
Browse files Browse the repository at this point in the history
Add more information about used client
  • Loading branch information
Morishiri committed Dec 21, 2020
2 parents 0e7a74a + 29865b4 commit 5f3291e
Show file tree
Hide file tree
Showing 9 changed files with 77 additions and 35 deletions.
1 change: 1 addition & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ func initLogger() {
// Execute runs command parsing chain
func Execute(version string, commit string) {
rootCmd.Version = fmt.Sprintf("%s (%s)", version, commit)
displayOptions.Version = fmt.Sprintf("%s-%s", version, commit)
if err := rootCmd.Execute(); err != nil {
os.Exit(1)
}
Expand Down
12 changes: 6 additions & 6 deletions internal/app/loophole/loophole.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ func handleClient(client net.Conn, local net.Conn) {
<-chDone
}

func registerDomain(apiURL string, publicKey *ssh.PublicKey, requestedSiteID string) string {
func registerDomain(apiURL string, publicKey *ssh.PublicKey, requestedSiteID, version string) string {
communication.StartLoading("Registering your domain...")
siteID, err := apiclient.RegisterSite(apiURL, *publicKey, requestedSiteID)
siteID, err := apiclient.RegisterSite(apiURL, *publicKey, requestedSiteID, version)
if err != nil {
communication.LoadingFailure()
if requestErr, ok := err.(apiclient.RequestError); ok {
Expand Down Expand Up @@ -259,7 +259,7 @@ func ForwardPort(config lm.ExposeHttpConfig) {
}

publicKeyAuthMethod, publicKey := parsePublicKey(config.Remote.IdentityFile)
siteID := registerDomain(config.Remote.APIEndpoint.URI(), &publicKey, config.Remote.SiteID)
siteID := registerDomain(config.Remote.APIEndpoint.URI(), &publicKey, config.Remote.SiteID, config.Display.Version)
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 All @@ -269,7 +269,7 @@ func ForwardDirectory(config lm.ExposeDirectoryConfig) {
communication.PrintWelcomeMessage()

publicKeyAuthMethod, publicKey := parsePublicKey(config.Remote.IdentityFile)
siteID := registerDomain(config.Remote.APIEndpoint.URI(), &publicKey, config.Remote.SiteID)
siteID := registerDomain(config.Remote.APIEndpoint.URI(), &publicKey, config.Remote.SiteID, config.Display.Version)
server := getStaticFileServer(config.Local.Path, siteID, config.Remote.BasicAuthUsername, config.Remote.BasicAuthPassword)

forward(config.Remote, config.Display, publicKeyAuthMethod, siteID, server, config.Local.Path, []string{"https"})
Expand All @@ -280,7 +280,7 @@ func ForwardDirectoryViaWebdav(config lm.ExposeWebdavConfig) {
communication.PrintWelcomeMessage()

publicKeyAuthMethod, publicKey := parsePublicKey(config.Remote.IdentityFile)
siteID := registerDomain(config.Remote.APIEndpoint.URI(), &publicKey, config.Remote.SiteID)
siteID := registerDomain(config.Remote.APIEndpoint.URI(), &publicKey, config.Remote.SiteID, config.Display.Version)
server := getWebdavServer(config.Local.Path, siteID, config.Remote.BasicAuthUsername, config.Remote.BasicAuthPassword)

forward(config.Remote, config.Display, publicKeyAuthMethod, siteID, server, config.Local.Path, []string{"https", "davs", "webdav"})
Expand Down Expand Up @@ -308,7 +308,7 @@ func forward(remoteEndpointSpecs lm.RemoteEndpointSpecs, displayOptions lm.Displ
Timeout: time.Second * 30,
Transport: netTransport,
}
_, err := netClient.Get(urlmaker.GetSiteUrl("https", siteID))
_, err := netClient.Get(urlmaker.GetSiteURL("https", siteID))

if err != nil {
log.Error().Msg("TLS Certificate failed to provision. Will be obtained with first request made by any client, therefore first execution may be slower")
Expand Down
1 change: 1 addition & 0 deletions internal/app/loophole/models/DisplayOptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ type DisplayOptions struct {
QR bool
FeedbackFormURL string
DisableProxyErrorPage bool
Version string
}
28 changes: 23 additions & 5 deletions internal/pkg/apiclient/apiclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"net"
"net/http"
"os"
"runtime"
"time"

"github.com/loophole/cli/internal/pkg/token"
"github.com/loophole/cli/internal/pkg/urlmaker"
"github.com/rs/zerolog/log"
"golang.org/x/crypto/ssh"
)
Expand Down Expand Up @@ -40,7 +44,7 @@ var getAccessToken = token.GetAccessToken
var tokenWasRefreshed = false

// RegisterSite is a funtion used to obtain site id and register keys in the gateway
func RegisterSite(apiURL string, publicKey ssh.PublicKey, siteID string) (string, error) {
func RegisterSite(apiURL string, publicKey ssh.PublicKey, siteID, version string) (string, error) {
publicKeyString := publicKey.Type() + " " + base64.StdEncoding.EncodeToString(publicKey.Marshal())

if !isTokenSaved() {
Expand Down Expand Up @@ -71,15 +75,29 @@ func RegisterSite(apiURL string, publicKey ssh.PublicKey, siteID string) (string
if err != nil {
return "", err
}

req, err := http.NewRequest("POST", fmt.Sprintf("%s/api/register-site", apiURL), bytes.NewBuffer(jsonData))
if err != nil {
return "", err
}

req.Header.Add("Content-Type", "application/json")
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", accessToken))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Content-Type", "application/json")
req.Header.Set("User-Agent", fmt.Sprintf("loophole/%s (%s/%s) %s", version, runtime.GOOS, runtime.GOARCH, urlmaker.HostURL))
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", accessToken))

var netTransport = &http.Transport{
Dial: (&net.Dialer{
Timeout: 5 * time.Second,
}).Dial,
TLSHandshakeTimeout: 5 * time.Second,
}
var netClient = &http.Client{
Timeout: time.Second * 30,
Transport: netTransport,
}

resp, err := http.DefaultClient.Do(req)
resp, err := netClient.Do(req)
if err != nil {
return "", err
}
Expand Down Expand Up @@ -112,7 +130,7 @@ func RegisterSite(apiURL string, publicKey ssh.PublicKey, siteID string) (string
}
}
tokenWasRefreshed = true
return RegisterSite(apiURL, publicKey, siteID)
return RegisterSite(apiURL, publicKey, siteID, version)
}
return "", RequestError{
Message: "Authentication failed, try logging out and logging in again",
Expand Down
18 changes: 9 additions & 9 deletions internal/pkg/apiclient/apiclient_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func TestRegisterSiteSuccessOKShouldPropagateWithoutIdProvided(t *testing.T) {
if err != nil {
t.Fatal(err)
}
result, err := RegisterSite(srv.URL, publicKey, "")
result, err := RegisterSite(srv.URL, publicKey, "", "1.0.0")

if err != nil {
t.Fatalf("Unexpected error returned: %v", err)
Expand Down Expand Up @@ -57,7 +57,7 @@ func TestRegisterSiteSuccessCreatedShouldPropagateWithoutIdProvided(t *testing.T
if err != nil {
t.Fatal(err)
}
result, err := RegisterSite(srv.URL, publicKey, "")
result, err := RegisterSite(srv.URL, publicKey, "", "1.0.0")

if err != nil {
t.Fatalf("Unexpected error returned: %v", err)
Expand Down Expand Up @@ -86,7 +86,7 @@ func TestRegisterSiteSuccessOKShouldPropagateWithIdProvided(t *testing.T) {
if err != nil {
t.Fatal(err)
}
result, err := RegisterSite(srv.URL, publicKey, expectedSiteID)
result, err := RegisterSite(srv.URL, publicKey, expectedSiteID, "1.0.0")

if err != nil {
t.Fatalf("Unexpected error returned: %v", err)
Expand Down Expand Up @@ -114,7 +114,7 @@ func TestRegisterSiteSuccessCreatedShouldPropagateWithIdProvided(t *testing.T) {
if err != nil {
t.Fatal(err)
}
result, err := RegisterSite(srv.URL, publicKey, expectedSiteID)
result, err := RegisterSite(srv.URL, publicKey, expectedSiteID, "1.0.0")

if err != nil {
t.Fatalf("Unexpected error returned: %v", err)
Expand Down Expand Up @@ -145,7 +145,7 @@ func TestRegisterSiteError400ShouldPropagateError(t *testing.T) {
if err != nil {
t.Fatal(err)
}
result, err := RegisterSite(srv.URL, publicKey, "")
result, err := RegisterSite(srv.URL, publicKey, "", "1.0.0")

if err == nil {
t.Fatalf("Expected an error to be returned")
Expand Down Expand Up @@ -186,7 +186,7 @@ func TestRegisterSiteError401ShouldPropagateError(t *testing.T) {
if err != nil {
t.Fatal(err)
}
result, err := RegisterSite(srv.URL, publicKey, "")
result, err := RegisterSite(srv.URL, publicKey, "", "1.0.0")

if err == nil {
t.Fatalf("Expected an error to be returned")
Expand Down Expand Up @@ -227,7 +227,7 @@ func TestRegisterSiteError403ShouldPropagateError(t *testing.T) {
if err != nil {
t.Fatal(err)
}
result, err := RegisterSite(srv.URL, publicKey, "")
result, err := RegisterSite(srv.URL, publicKey, "", "1.0.0")

if err == nil {
t.Fatalf("Expected an error to be returned")
Expand Down Expand Up @@ -264,7 +264,7 @@ func TestRegisterTokenNotSavedReturns401(t *testing.T) {
if err != nil {
t.Fatal(err)
}
result, err := RegisterSite(srv.URL, publicKey, "")
result, err := RegisterSite(srv.URL, publicKey, "", "1.0.0")

if err == nil {
t.Fatalf("Expected an error to be returned")
Expand Down Expand Up @@ -298,7 +298,7 @@ func TestRegisterTokenReadingProblemReturns401(t *testing.T) {
if err != nil {
t.Fatal(err)
}
result, err := RegisterSite(srv.URL, publicKey, "")
result, err := RegisterSite(srv.URL, publicKey, "", "1.0.0")

if err == nil {
t.Fatalf("Expected an error to be returned")
Expand Down
4 changes: 2 additions & 2 deletions internal/pkg/communication/communication.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func PrintTunnelSuccessMessage(siteID string, protocols []string, localAddr stri

for _, protocol := range protocols {
NewLine()
siteAddr := urlmaker.GetSiteUrl(protocol, siteID)
siteAddr := urlmaker.GetSiteURL(protocol, siteID)
fmt.Fprint(colorableOutput, "Forwarding ")
fmt.Fprint(colorableOutput, aurora.Green(siteAddr))
fmt.Fprint(colorableOutput, " -> ")
Expand All @@ -43,7 +43,7 @@ func PrintTunnelSuccessMessage(siteID string, protocols []string, localAddr stri
NewLine()
Write("Scan the below QR code to open the site:")
NewLine()
QRCode(urlmaker.GetSiteUrl(protocols[0], siteID))
QRCode(urlmaker.GetSiteURL(protocols[0], siteID))
}

if len(protocols) > 1 {
Expand Down
5 changes: 3 additions & 2 deletions internal/pkg/httpserver/httpserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
auth "github.com/abbot/go-http-auth"
lm "github.com/loophole/cli/internal/app/loophole/models"
"github.com/loophole/cli/internal/pkg/cache"
"github.com/loophole/cli/internal/pkg/urlmaker"
"golang.org/x/crypto/acme/autocert"
"golang.org/x/crypto/bcrypt"
"golang.org/x/net/webdav"
Expand Down Expand Up @@ -230,7 +231,7 @@ func New() ServerBuilder {
func getTLSConfig(siteID string) *tls.Config {
certManager := autocert.Manager{
Prompt: autocert.AcceptTOS,
HostPolicy: autocert.HostWhitelist(fmt.Sprintf("%s.loophole.host", siteID)),
HostPolicy: autocert.HostWhitelist(urlmaker.GetSiteFQDN(siteID)),
Cache: autocert.DirCache(cache.GetLocalStorageDir("certs")),
Email: fmt.Sprintf("lh-%s@main.dev", siteID),
}
Expand All @@ -246,7 +247,7 @@ func getBasicAuthHandler(siteID string, username string, password string, handle

secret := getBasicAuthSecretParser(username, string(hashedPassword))

authenticator := auth.NewBasicAuthenticator(fmt.Sprintf("%s.loophole.host", siteID), secret)
authenticator := auth.NewBasicAuthenticator(urlmaker.GetSiteFQDN(siteID), secret)
return auth.JustCheck(authenticator, handler), nil
}

Expand Down
16 changes: 14 additions & 2 deletions internal/pkg/urlmaker/urlmaker.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@ package urlmaker

import "fmt"

func GetSiteUrl(protocol string, siteID string) string {
return fmt.Sprintf("%s://%s.loophole.host", protocol, siteID)
const (
// HostURL is the top-level domain used for hosting loophole sites
HostURL = "loophole.site"
)

// GetSiteURL produces URL for the site (with the protocol)
func GetSiteURL(protocol string, siteID string) string {
return fmt.Sprintf("%s://%s.%s", protocol, siteID, HostURL)
}

// GetSiteFQDN produces fully qualified domain name for the site (without the protocol)
func GetSiteFQDN(siteID string) string {
return fmt.Sprintf("%s.%s", siteID, HostURL)

}
27 changes: 18 additions & 9 deletions internal/pkg/urlmaker/urlmaker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,37 @@ package urlmaker
import "testing"

func TestReturnsCorrectHttpsUrl(t *testing.T) {
expectedSiteID := "https://some-site.loophole.host"
result := GetSiteUrl("https", "some-site")
expectedSiteID := "https://some-site.loophole.site"
result := GetSiteURL("https", "some-site")

if result != expectedSiteID {
t.Fatalf("Site ID '%s' is different than expected: %s", result, expectedSiteID)
t.Fatalf("Site URL '%s' is different than expected: %s", result, expectedSiteID)
}
}

func TestReturnsCorrectWebdavUrl(t *testing.T) {
expectedSiteID := "webdav://some-site.loophole.host"
result := GetSiteUrl("webdav", "some-site")
expectedSiteID := "webdav://some-site.loophole.site"
result := GetSiteURL("webdav", "some-site")

if result != expectedSiteID {
t.Fatalf("Site ID '%s' is different than expected: %s", result, expectedSiteID)
t.Fatalf("Site URL '%s' is different than expected: %s", result, expectedSiteID)
}
}

func TestReturnsCorrectDavsUrl(t *testing.T) {
expectedSiteID := "davs://some-site.loophole.host"
result := GetSiteUrl("davs", "some-site")
expectedSiteID := "davs://some-site.loophole.site"
result := GetSiteURL("davs", "some-site")

if result != expectedSiteID {
t.Fatalf("Site ID '%s' is different than expected: %s", result, expectedSiteID)
t.Fatalf("Site URL '%s' is different than expected: %s", result, expectedSiteID)
}
}

func TestReturnsCorrectFQDN(t *testing.T) {
expectedSiteID := "some-site.loophole.site"
result := GetSiteFQDN("some-site")

if result != expectedSiteID {
t.Fatalf("Site FQDN '%s' is different than expected: %s", result, expectedSiteID)
}
}

0 comments on commit 5f3291e

Please sign in to comment.