Browse Source

add support to the server to do basic authentication of http requests

Alan Shreve 12 years ago
parent
commit
70d52e0ebc
5 changed files with 40 additions and 16 deletions
  1. 4 4
      src/ngrok/client/cli.go
  2. 1 0
      src/ngrok/client/main.go
  3. 9 10
      src/ngrok/msg/msg.go
  4. 20 2
      src/ngrok/server/http.go
  5. 6 0
      src/ngrok/server/tunnel.go

+ 4 - 4
src/ngrok/client/cli.go

@@ -15,7 +15,7 @@ var (
 
 type Options struct {
 	server      string
-	auth        string
+	httpAuth    string
 	hostname    string
 	localaddr   string
 	protocol    string
@@ -95,8 +95,8 @@ func parseArgs() *Options {
 		"ngrok.com:2280",
 		"The remote ngrok server")
 
-	auth := flag.String(
-		"auth",
+	httpAuth := flag.String(
+		"httpauth",
 		"",
 		"username:password HTTP basic auth creds protecting the public tunnel endpoint")
 
@@ -124,7 +124,7 @@ func parseArgs() *Options {
 
 	return &Options{
 		server:    *server,
-		auth:      *auth,
+		httpAuth:  *httpAuth,
 		hostname:  *hostname,
 		subdomain: *subdomain,
 		localaddr: parseLocalAddr(),

+ 1 - 0
src/ngrok/client/main.go

@@ -104,6 +104,7 @@ func control(s *State, ctl *ui.Controller) {
 	err = msg.WriteMsg(conn, &msg.RegMsg{
 		Protocol:  s.opts.protocol,
 		OS:        runtime.GOOS,
+		HttpAuth:  s.opts.httpAuth,
 		Hostname:  s.opts.hostname,
 		Subdomain: s.opts.subdomain,
 		ClientId:  s.id,

+ 9 - 10
src/ngrok/msg/msg.go

@@ -44,16 +44,15 @@ type TypeEmbed struct {
 
 type RegMsg struct {
 	TypeEmbed
-	Protocol         string
-	Hostname         string
-	Subdomain        string
-	ClientId         string
-	HttpAuthUser     string
-	HttpAuthPassword string
-	User             string
-	Password         string
-	OS               string
-	Arch             string
+	Protocol  string
+	Hostname  string
+	Subdomain string
+	ClientId  string
+	HttpAuth  string
+	User      string
+	Password  string
+	OS        string
+	Arch      string
 }
 
 type RegAckMsg struct {

+ 20 - 2
src/ngrok/server/http.go

@@ -6,6 +6,15 @@ import (
 	"ngrok/conn"
 )
 
+const (
+	NotAuthorized = `HTTP/1.0 401 Not Authorized
+WWW-Authenticate: Basic realm="ngrok"
+Content-Length: 23
+
+Authorization required
+`
+)
+
 /**
  * Listens for new http connections from the public internet
  */
@@ -50,14 +59,23 @@ func httpHandler(tcpConn net.Conn) {
 	if err != nil {
 		panic(err)
 	}
-	conn.Debug("Found hostname %s in request", req.Host)
 
+	// multiplex to find the right backend host
+	conn.Debug("Found hostname %s in request", req.Host)
 	tunnel := tunnels.Get("http://" + req.Host)
-
 	if tunnel == nil {
 		conn.Info("No tunnel found for hostname %s", req.Host)
 		return
 	}
 
+	// satisfy auth, if necessary
+	conn.Debug("From client: %s", req.Header.Get("Authorization"))
+	conn.Debug("To match: %s", tunnel.regMsg.HttpAuth)
+	if req.Header.Get("Authorization") != tunnel.regMsg.HttpAuth {
+		conn.Info("Authentication failed")
+		conn.Write([]byte(NotAuthorized))
+		return
+	}
+
 	tunnel.HandlePublicConnection(conn)
 }

+ 6 - 0
src/ngrok/server/tunnel.go

@@ -2,6 +2,7 @@ package server
 
 import (
 	log "code.google.com/p/log4go"
+	"encoding/base64"
 	"fmt"
 	"net"
 	"ngrok/conn"
@@ -59,6 +60,11 @@ func newTunnel(m *msg.RegMsg, ctl *Control) (t *Tunnel) {
 		return
 	}
 
+	// pre-encode the http basic auth for fast comparisons later
+	if m.HttpAuth != "" {
+		m.HttpAuth = "Basic " + base64.StdEncoding.EncodeToString([]byte(m.HttpAuth))
+	}
+
 	t.ctl.conn.AddLogPrefix(t.Id())
 	t.AddLogPrefix(t.Id())
 	t.Info("Registered new tunnel")