You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by jv...@apache.org on 2017/03/11 18:09:53 UTC

[3/5] incubator-trafficcontrol git commit: original webfront, auth services taken form https://github.com/rarenivar/project5799

original webfront, auth services taken form https://github.com/rarenivar/project5799


Project: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/commit/c6221c2e
Tree: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/tree/c6221c2e
Diff: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/diff/c6221c2e

Branch: refs/heads/master
Commit: c6221c2eaa10b287ce8c55aec0b5269829971b3e
Parents: cb08fc3
Author: Amir Yeshurun <am...@qwilt.com>
Authored: Sun Feb 26 22:51:53 2017 +0200
Committer: Jan van Doorn <ja...@cable.comcast.com>
Committed: Sat Mar 11 11:06:09 2017 -0700

----------------------------------------------------------------------
 traffic_ops/experimental/auth/Dockerfile        |  23 ++
 traffic_ops/experimental/auth/auth.config       |   8 +
 traffic_ops/experimental/auth/auth.go           | 172 +++++++++++
 traffic_ops/experimental/auth/server.key        |  27 ++
 traffic_ops/experimental/auth/server.pem        |  21 ++
 traffic_ops/experimental/webfront/Dockerfile    |  25 ++
 traffic_ops/experimental/webfront/README.md     |  98 +++++++
 traffic_ops/experimental/webfront/rules.json    |  32 ++
 traffic_ops/experimental/webfront/server.key    |  27 ++
 traffic_ops/experimental/webfront/server.pem    |  21 ++
 .../webfront/simpleserver/server.key            |  27 ++
 .../webfront/simpleserver/server.pem            |  21 ++
 .../webfront/simpleserver/simpleserver.go       |  25 ++
 traffic_ops/experimental/webfront/startfakes.sh |   7 +
 .../experimental/webfront/webfront.config       |   5 +
 traffic_ops/experimental/webfront/webfront.go   | 293 +++++++++++++++++++
 16 files changed, 832 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/c6221c2e/traffic_ops/experimental/auth/Dockerfile
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/auth/Dockerfile b/traffic_ops/experimental/auth/Dockerfile
new file mode 100644
index 0000000..4ec7a98
--- /dev/null
+++ b/traffic_ops/experimental/auth/Dockerfile
@@ -0,0 +1,23 @@
+# Start from a Debian image with the latest version of Go installed
+# and a workspace (GOPATH) configured at /go.
+FROM golang
+
+# Copy the local package files to the container's workspace.
+# Note: need to move to ~/go?
+ADD . /go/src/github.com/rarenivar/project5799/auth
+
+# Build the outyet command inside the container.
+# (You may fetch or manage dependencies here,
+# either manually or with a tool like "godep".)
+RUN go get github.com/dgrijalva/jwt-go
+RUN go get github.com/jmoiron/sqlx
+RUN go get github.com/lib/pq
+WORKDIR ./src/github.com/rarenivar/project5799/auth
+RUN export GOBIN=$GOPATH/bin && go install auth.go
+
+# Document that the service listens on port 9000.
+EXPOSE 8080
+
+# Run the outyet command by default when the container starts.
+ENTRYPOINT /go/bin/auth auth.config
+

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/c6221c2e/traffic_ops/experimental/auth/auth.config
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/auth/auth.config b/traffic_ops/experimental/auth/auth.config
new file mode 100644
index 0000000..642923e
--- /dev/null
+++ b/traffic_ops/experimental/auth/auth.config
@@ -0,0 +1,8 @@
+{
+	"dbName":"amiry",
+	"dbUser":"amiry",
+	"dbPassword":"blabla",
+	"dbServer":"localhost",
+	"dbPort":5432,
+	"listenerPort":"8080"
+}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/c6221c2e/traffic_ops/experimental/auth/auth.go
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/auth/auth.go b/traffic_ops/experimental/auth/auth.go
new file mode 100644
index 0000000..937124f
--- /dev/null
+++ b/traffic_ops/experimental/auth/auth.go
@@ -0,0 +1,172 @@
+package main
+
+import (
+	"encoding/json"
+	"fmt"
+	jwt "github.com/dgrijalva/jwt-go"
+	"github.com/jmoiron/sqlx"
+	_ "github.com/lib/pq"
+	"io/ioutil"
+	"log"
+	"net/http"
+	"os"
+	"path"
+	"time"
+)
+
+// Config holds the configuration of the server.
+type Config struct {
+	DbName       string `json:"dbName"`
+	DbUser       string `json:"dbUser"`
+	DbPassword   string `json:"dbPassword"`
+	DbServer     string `json:"dbServer,omitempty"`
+	DbPort       uint   `json:"dbPort,omitempty"`
+	ListenerPort string `json:"listenerPort"`
+}
+
+type User struct {
+	Username  string `db:"username" json:"username"`
+	FirstName string `db:"first_name" json:"firstName,omitempty"`
+	LastName  string `db:"last_name" json:"lastName,omitempty"`
+	Password  string `db:"password" json:"password"`
+}
+
+type Claims struct {
+    Capabilities []string `json:"cap"`
+    jwt.StandardClaims
+}
+
+type TokenResponse struct {
+	Token string
+}
+
+var db *sqlx.DB // global and simple
+
+func printUsage() {
+	exampleConfig := `{
+	"dbName":"my-db",
+	"dbUser":"my-user",
+	"dbPassword":"secret",
+	"dbServer":"localhost",
+	"dbPort":5432,
+	"listenerPort":"8080"
+}`
+	Logger.Println("Usage: " + path.Base(os.Args[0]) + " configfile")
+	Logger.Println("")
+	Logger.Println("Example config file:")
+	Logger.Println(exampleConfig)
+}
+
+var Logger *log.Logger
+
+func main() {
+	if len(os.Args) < 2 {
+		printUsage()
+		return
+	}
+
+	// log.SetOutput(os.Stdout)
+	Logger = log.New(os.Stdout, " ", log.Ldate|log.Ltime|log.Lshortfile)
+
+	file, err := os.Open(os.Args[1])
+	if err != nil {
+		Logger.Println("Error opening config file:", err)
+		return
+	}
+	decoder := json.NewDecoder(file)
+	config := Config{}
+	err = decoder.Decode(&config)
+	if err != nil {
+		Logger.Println("Error reading config file:", err)
+		return
+	}
+
+	db, err = InitializeDatabase(config.DbUser, config.DbPassword, config.DbName, config.DbServer, config.DbPort)
+	if err != nil {
+		Logger.Println("Error initializing database:", err)
+		return
+	}
+
+	http.HandleFunc("/", handler)
+	if _, err := os.Stat("server.pem"); os.IsNotExist(err) {
+		Logger.Fatal("server.pem file not found")
+	}
+	if _, err := os.Stat("server.key"); os.IsNotExist(err) {
+		Logger.Fatal("server.key file not found")
+	}
+	Logger.Printf("Starting server on port " + config.ListenerPort + "...")
+	Logger.Fatal(http.ListenAndServeTLS(":"+config.ListenerPort, "server.pem", "server.key", nil))
+}
+
+func InitializeDatabase(username, password, dbname, server string, port uint) (*sqlx.DB, error) {
+	connString := fmt.Sprintf("host=%s dbname=%s user=%s password=%s sslmode=disable", server, dbname, username, password)
+
+	db, err := sqlx.Connect("postgres", connString)
+	if err != nil {
+		return nil, err
+	}
+
+	return db, nil
+}
+
+func handler(w http.ResponseWriter, r *http.Request) {
+
+	Logger.Println(r.Method, r.URL.Scheme, r.Host, r.URL.RequestURI())
+
+	if r.Method == "POST" {
+		var u User
+		userlist := []User{}
+		body, err := ioutil.ReadAll(r.Body)
+		log.Println(string(body))
+		if err != nil {
+			Logger.Println("Error reading body: ", err.Error())
+			http.Error(w, "Error reading body: "+err.Error(), http.StatusBadRequest)
+			return
+		}
+		err = json.Unmarshal(body, &u)
+		if err != nil {
+			Logger.Println("Error unmarshalling JSON: ", err.Error())
+			http.Error(w, "Invalid JSON: "+err.Error(), http.StatusBadRequest)
+			return
+		}
+		stmt, err := db.PrepareNamed("SELECT * FROM users WHERE username=:username")
+		err = stmt.Select(&userlist, u)
+		if err != nil {
+			Logger.Println(err.Error())
+			http.Error(w, "Database error: "+err.Error(), http.StatusInternalServerError)
+			return
+		}
+		if len(userlist) == 0 || userlist[0].Password != u.Password {
+			http.Error(w, "Invalid username/password ", http.StatusUnauthorized)
+			return
+		}
+
+		claims := Claims {
+	        []string{"read-a", "write-a", "read-b"}, // TODO(amiry) - Read from DB
+	        jwt.StandardClaims {
+	        	Subject: u.Username,
+	            ExpiresAt: time.Now().Add(time.Hour * 24).Unix(), // TODO(amiry) - Use shorter expiration
+	        },
+	    }
+
+		token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
+
+		tokenString, err := token.SignedString([]byte("CAmeRAFiveSevenNineNine")) // TODO(amiry) - Where to keep secret?
+		if err != nil {
+			Logger.Println(err.Error())
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+			return
+		}
+		js, err := json.Marshal(TokenResponse{Token: tokenString})
+		if err != nil {
+			Logger.Println(err.Error())
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+			return
+		}
+		// w.WriteHeader(http.StatusOK)
+		w.Header().Set("Content-Type", "application/json")
+		w.Write(js)
+		return
+	}
+	http.Error(w, r.Method+" "+r.URL.Path+" not valid for this microservice", http.StatusNotFound)
+}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/c6221c2e/traffic_ops/experimental/auth/server.key
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/auth/server.key b/traffic_ops/experimental/auth/server.key
new file mode 100644
index 0000000..b674147
--- /dev/null
+++ b/traffic_ops/experimental/auth/server.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEA0g8TSrCJLfzMxzCB+zLo19NIrOAA2kR25vi8Hu+aM2PGxJbH
+FPSV0LG1HF3l/4QoG1JNYKuyXaPFDtPcefU7fQ7MqpAiBWj2HyIfnKyZyUX867Pg
+2CnSPTgMZyK3NMSnkAG+FSZzXKj6Vxjh0mKN1X+JRqyv1c/rWLg3RxecuVdq0D23
+l/1nZPWidiRp2XhUKeO3L3rLKu9tf80HJZ1TN3TsUxOGUgZHectN577qAaHr3KzD
+HVA4v7fmNRXQEUg4Iax8FxWIw0CQOFY9+lUqW1TKr7UmrqnzmhQmLFNBP5wuU1fI
+yz5HqNeR2VOt10VDBfYg7Wy08mFsSr3bhj9GMwIDAQABAoIBAQCC+H9QzG1bzQlp
+EKealg1zs/rWPvyJGrMAJAo3R7FfZVCjdlc+i5l1e7euriUfgaj4EALKyYL2u4u8
+SQBo0ix7NuUJW8C6ms0KcF5Bc6SXSBoAZDFG1hyNqwqgq5aLQiovscZwrX60QW+F
+KrByWpyQh2pyNG2V5IOa15EvtFs1e1f7+DCZ/KLjd5odxEZ93lLcDMQ4zmSl47uR
+I5rdixCQm8slUtRJxAB+nRck5htVGY8cwT6c+hx/GlZqY6xynMOwa9pC3zFegnr3
+nvHIiFEVu0CksaVBZNRe0S92r8VjHQPVRBa87Tsom6vJ0GxaeDfXchedrklCxMJh
+Y+QOPIVhAoGBAPgDxXGBD7VaUsBgimJKE1Q/inT2aZtwKrJi4l2fRE1Q0kOZvePv
+/LMiUZwcV0M+y45Y0VU2VXIdqAC1tYFhFfRKdmjCABFSidyhpIMJJZQrX3fFEDZq
+Vt95PrjlRC3+AVvVXVNWOxP4ObgorPzqXKtgxt10OltJfiLHHe0LF9nJAoGBANjS
+dUNU9OhCFA54MpfKtNLXvnYsxXigkN9rXAiOt9yd3x4l3o1EJs9t4a6stgzPaXDc
+ANkIPf1xsO9N0c+rikYQRo+JmbTaAQhac19NF7guTV6lHRa9bR4N4O14+62oXFEX
+3z5hp2Tr0/1qjp62ubDR8S7zY8ACsKiqd0msZ94bAoGAEy3XeuuMF24gsBfHG8q2
+q/Et99WGXSrTYnAbKTpDwebaG7gr4xCP7hpdTUEzzlNw0lUz+u70tJpuf3+Nxa+I
+TxfjCD7YWn6TgqhNXIS8jzS7cTugAVU/2pA5tXqlRxk5aayaRvOIJgcwD3m1xuko
+uWgGeOGgEachRfc37TjKY2ECgYAmDMDxYP5dqAL3Cxbt1m/nNF9DQ9YftiXWX3PW
+OJ2BhN773m9w4Q3ihwj862hDVT7OxH0NmUmvqX0AceqpBBSO+Ro45E/qEfvuaFko
+11GbS9zeCCHTtMhqZssbQYkBT7Nrs8HEo8SJjG02YrXGmpB5vE4UDDhsIMy5vLiU
+YEIWEwKBgQCXvBaNGJxPDu3//8yJJC4gFSWUxWYZ9x12Kke6AaUoeEn8RjTlaLRm
+Ol1ZiqHTSbRJQHjqmwKJAWSA3QX4S3ISeVOms7k30Bt02m4W0NgVRB2GxkFcnESH
+OxREDAQXG2lLP9O2HZPPHZ9aKKFM+M0GlOdQlqKk3AHPx33qCXLYtg==
+-----END RSA PRIVATE KEY-----

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/c6221c2e/traffic_ops/experimental/auth/server.pem
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/auth/server.pem b/traffic_ops/experimental/auth/server.pem
new file mode 100644
index 0000000..c09c2ca
--- /dev/null
+++ b/traffic_ops/experimental/auth/server.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDbzCCAlegAwIBAgIJAJozT3F7lmQwMA0GCSqGSIb3DQEBCwUAME4xCzAJBgNV
+BAYTAlVTMQswCQYDVQQIDAJDTzEPMA0GA1UEBwwGRGVudmVyMSEwHwYDVQQKDBhJ
+bnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMTYwMzI2MjIyODExWhcNMjYwMzI0
+MjIyODExWjBOMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ08xDzANBgNVBAcMBkRl
+bnZlcjEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkq
+hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0g8TSrCJLfzMxzCB+zLo19NIrOAA2kR2
+5vi8Hu+aM2PGxJbHFPSV0LG1HF3l/4QoG1JNYKuyXaPFDtPcefU7fQ7MqpAiBWj2
+HyIfnKyZyUX867Pg2CnSPTgMZyK3NMSnkAG+FSZzXKj6Vxjh0mKN1X+JRqyv1c/r
+WLg3RxecuVdq0D23l/1nZPWidiRp2XhUKeO3L3rLKu9tf80HJZ1TN3TsUxOGUgZH
+ectN577qAaHr3KzDHVA4v7fmNRXQEUg4Iax8FxWIw0CQOFY9+lUqW1TKr7Umrqnz
+mhQmLFNBP5wuU1fIyz5HqNeR2VOt10VDBfYg7Wy08mFsSr3bhj9GMwIDAQABo1Aw
+TjAdBgNVHQ4EFgQUdme3OnuvS+09/3Nkx1FS/4xgXoUwHwYDVR0jBBgwFoAUdme3
+OnuvS+09/3Nkx1FS/4xgXoUwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC
+AQEANNo5qASaoCc5/Utr10KIO/KoFesWL0v0bDIG3r0HaEp2gD44o884yQ8pFDNR
+bhUjs1SaHHcnGX5X3OGJVuP2vuz0pFaeKZJUawT1DTwaXQCTkRFw0Cb7oWdF6/FV
+pXuLBXWvEsu19jnrZGQbJw1lsOchPDNpp/h460JBWQOOYRfu3jSyBMwih40lEb+F
+1lA943aA2oGsDYJHnGRpUMLafe4gqcGWpfHUf2TaGe+tUSI7+JrhYLzjRyqTBdD3
+5Ss+eQo5tP0aOVMMCMCHmFfxgjBHGu4syQW5VYIZtzWMEKu2fEZCjkbV6XwVYVFL
+SKh7EgT1te7yUhfLSAF2969WHg==
+-----END CERTIFICATE-----

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/c6221c2e/traffic_ops/experimental/webfront/Dockerfile
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/webfront/Dockerfile b/traffic_ops/experimental/webfront/Dockerfile
new file mode 100644
index 0000000..c46e36a
--- /dev/null
+++ b/traffic_ops/experimental/webfront/Dockerfile
@@ -0,0 +1,25 @@
+# Start from a Debian image with the latest version of Go installed
+# and a workspace (GOPATH) configured at /go.
+FROM golang
+
+# Copy the local package files to the container's workspace.
+# Note: need to move to ~/go?
+ADD . /go/src/github.com/rarenivar/project5799/webfront
+
+# Build the outyet command inside the container.
+# (You may fetch or manage dependencies here,
+# either manually or with a tool like "godep".)
+RUN go get github.com/dgrijalva/jwt-go
+#RUN ls -lR
+WORKDIR ./src/github.com/rarenivar/project5799/webfront
+#RUN cd ./src/github.com/rarenivar/project5799/webfront && export GOBIN=$GOPATH/bin && go install webfront.go
+RUN export GOBIN=$GOPATH/bin && go install webfront.go
+#RUN ls -l
+#RUN go install webfront.go
+
+# Document that the service listens on port 9000.
+EXPOSE 9000
+
+# Run the outyet command by default when the container starts.
+ENTRYPOINT /go/bin/webfront webfront.config
+

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/c6221c2e/traffic_ops/experimental/webfront/README.md
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/webfront/README.md b/traffic_ops/experimental/webfront/README.md
new file mode 100644
index 0000000..041a219
--- /dev/null
+++ b/traffic_ops/experimental/webfront/README.md
@@ -0,0 +1,98 @@
+This application - webfront is a reverse proxy written in go that can front any number of microservices. It uses a rules file to map from requested host/path to microservice host/port/path.  Example rule file:
+
+   
+    [
+		{"Host": "local.com", "Path" : "/8001", "Forward": "localhost:8001"},
+		{"Host": "local.com", "Path" : "/8002", "Forward": "localhost:8002"},
+		{"Host": "local.com", "Path" : "/8003", "Forward": "localhost:8003"},
+		{"Host": "local.com", "Path" : "/8004", "Forward": "localhost:8004"},
+		{"Host": "local.com", "Path" : "/8005", "Forward": "localhost:8005"},
+		{"Host": "local.com", "Path" : "/8006", "Forward": "localhost:8006"},
+		{"Host": "local.com", "Path" : "/8007", "Forward": "localhost:8007"}
+	]
+
+
+No restart is needed to re-read the rule file and apply; within 60 seconds of a change in the file, it will pick up the new mappings.
+
+To run
+
+	go run webfront.go -rules=rules.json -https=:9000 -https_cert=server.pem -https_key=server.key 
+
+(or compile a binary, and run that)
+
+To get a token:
+
+	curl --insecure -Lkvs --header "Content-Type:application/json" -XPOST https://localhost:9000/login -d'{"username":"jvd", "password":"tootoo"}'
+   
+in my case that returned: 
+
+	{"Token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJQYXNzd29yZCI6InRvb3RvbyIsIlVzZXIiOiIiLCJleHAiOjE0NTg5NDg2MTl9.quCwZ5vghVBucxMxQ4fSfD84yw_yPEp9qLGGQNcHNUk"}``
+   
+ Example:
+  
+	[jvd@laika webfront (master *=)]$ curl --insecure -Lkvs --header "Content-Type:application/json" -XPOST https://localhost:9000/login -d'{"username":"jvd", "password":"tootoo"}'
+	*   Trying ::1...
+	* Connected to localhost (::1) port 9000 (#0)
+	* TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
+	* Server certificate: CU
+	> POST /login HTTP/1.1
+	> Host: localhost:9000
+	> User-Agent: curl/7.43.0
+	> Accept: */*
+	> Content-Type:application/json
+	> Content-Length: 39
+	>
+	* upload completely sent off: 39 out of 39 bytes
+	< HTTP/1.1 200 OK
+	< Content-Type: application/json
+	< Date: Thu, 24 Mar 2016 23:30:19 GMT
+	< Content-Length: 157
+	<
+	* Connection #0 to host localhost left intact
+	{"Token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJQYXNzd29yZCI6InRvb3RvbyIsIlVzZXIiOiIiLCJleHAiOjE0NTg5NDg2MTl9.quCwZ5vghVBucxMxQ4fSfD84yw_yPEp9qLGGQNcHNUk"}[jvd@laika webfront (master *=)]$
+ 
+ * To use a token: 
+
+	curl --insecure -H'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJQYXNzd29yZCI6InRvb3RvbyIsIlVzZXIiOiIiLCJleHAiOjE0NTg5NDg2MTl9.quCwZ5vghVBucxMxQ4fSfD84yw_yPEp9qLGGQNcHNUk' -Lkvs  https://localhost:9000/8003/r
+
+Example:
+
+   
+    [jvd@laika webfront (master *%=)]$ curl --insecure -H'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJQYXNzd29yZCI6InRvb3RvbyIsIlVzZXIiOiIiLCJleHAiOjE0NTg5NDg2MTl9.quCwZ5vghVBucxMxQ4fSfD84yw_yPEp9qLGGQNcHNUk' -Lkvs  https://localhost:9000/8003/r
+	*   Trying ::1...
+	* Connected to localhost (::1) port 9000 (#0)
+	* TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
+	* Server certificate: CU
+	> GET /8003/r HTTP/1.1
+	> Host: localhost:9000
+	> User-Agent: curl/7.43.0
+	> Accept: */*
+	> Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJQYXNzd29yZCI6InRvb3RvbyIsIlVzZXIiOiIiLCJleHAiOjE0NTg5NDg2MTl9.quCwZ5vghVBucxMxQ4fSfD84yw_yPEp9qLGGQNcHNUk
+	>
+	< HTTP/1.1 200 OK
+	< Content-Length: 24
+	< Content-Type: text/plain; charset=utf-8
+	< Date: Thu, 24 Mar 2016 23:34:08 GMT
+	<
+	Hitting 8003 with /boo1
+	* Connection #0 to host localhost left intact
+	[jvd@laika webfront (master *%=)]$
+
+	[jvd@laika webfront (master=)]$ curl --insecure -H'Authorization: Bearer FAKETOKEN' -Lkvs  https://localhost:9000/8003/r *   Trying ::1...
+	* Connected to localhost (::1) port 9000 (#0)
+	* TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
+	* Server certificate: CU
+	> GET /8003/r HTTP/1.1
+	> Host: localhost:9000
+	> User-Agent: curl/7.43.0
+	> Accept: */*
+	> Authorization: Bearer FAKETOKEN
+	>
+	< HTTP/1.1 403 Forbidden
+	< Date: Thu, 24 Mar 2016 23:43:11 GMT
+	< Content-Length: 0
+	< Content-Type: text/plain; charset=utf-8
+	<
+	* Connection #0 to host localhost left intact
+	[jvd@laika webfront (master=)]$
+  
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/c6221c2e/traffic_ops/experimental/webfront/rules.json
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/webfront/rules.json b/traffic_ops/experimental/webfront/rules.json
new file mode 100644
index 0000000..fd6e792
--- /dev/null
+++ b/traffic_ops/experimental/webfront/rules.json
@@ -0,0 +1,32 @@
+[
+  {
+    "host": "local.com",
+    "path": "/login",
+    "forward": "localhost:8080",
+    "secure": false
+  },
+  {
+    "host": "local.com",
+    "path": "/a",
+    "forward": "localhost:8081",
+    "secure": true,
+    "capabilities": {
+    	"GET": "read-a",
+    	"POST": "write-a",
+    	"PUT": "write-a",
+    	"PATCH": "write-a"
+    }
+  },
+  {
+    "host": "local.com",
+    "path": "/b",
+    "forward": "localhost:8082",
+    "secure": true,
+    "capabilities": {
+    	"GET": "read-b",
+    	"POST": "write-b",
+    	"PUT": "write-b",
+    	"PATCH": "write-b"
+    }
+  }
+]
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/c6221c2e/traffic_ops/experimental/webfront/server.key
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/webfront/server.key b/traffic_ops/experimental/webfront/server.key
new file mode 100644
index 0000000..b674147
--- /dev/null
+++ b/traffic_ops/experimental/webfront/server.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEA0g8TSrCJLfzMxzCB+zLo19NIrOAA2kR25vi8Hu+aM2PGxJbH
+FPSV0LG1HF3l/4QoG1JNYKuyXaPFDtPcefU7fQ7MqpAiBWj2HyIfnKyZyUX867Pg
+2CnSPTgMZyK3NMSnkAG+FSZzXKj6Vxjh0mKN1X+JRqyv1c/rWLg3RxecuVdq0D23
+l/1nZPWidiRp2XhUKeO3L3rLKu9tf80HJZ1TN3TsUxOGUgZHectN577qAaHr3KzD
+HVA4v7fmNRXQEUg4Iax8FxWIw0CQOFY9+lUqW1TKr7UmrqnzmhQmLFNBP5wuU1fI
+yz5HqNeR2VOt10VDBfYg7Wy08mFsSr3bhj9GMwIDAQABAoIBAQCC+H9QzG1bzQlp
+EKealg1zs/rWPvyJGrMAJAo3R7FfZVCjdlc+i5l1e7euriUfgaj4EALKyYL2u4u8
+SQBo0ix7NuUJW8C6ms0KcF5Bc6SXSBoAZDFG1hyNqwqgq5aLQiovscZwrX60QW+F
+KrByWpyQh2pyNG2V5IOa15EvtFs1e1f7+DCZ/KLjd5odxEZ93lLcDMQ4zmSl47uR
+I5rdixCQm8slUtRJxAB+nRck5htVGY8cwT6c+hx/GlZqY6xynMOwa9pC3zFegnr3
+nvHIiFEVu0CksaVBZNRe0S92r8VjHQPVRBa87Tsom6vJ0GxaeDfXchedrklCxMJh
+Y+QOPIVhAoGBAPgDxXGBD7VaUsBgimJKE1Q/inT2aZtwKrJi4l2fRE1Q0kOZvePv
+/LMiUZwcV0M+y45Y0VU2VXIdqAC1tYFhFfRKdmjCABFSidyhpIMJJZQrX3fFEDZq
+Vt95PrjlRC3+AVvVXVNWOxP4ObgorPzqXKtgxt10OltJfiLHHe0LF9nJAoGBANjS
+dUNU9OhCFA54MpfKtNLXvnYsxXigkN9rXAiOt9yd3x4l3o1EJs9t4a6stgzPaXDc
+ANkIPf1xsO9N0c+rikYQRo+JmbTaAQhac19NF7guTV6lHRa9bR4N4O14+62oXFEX
+3z5hp2Tr0/1qjp62ubDR8S7zY8ACsKiqd0msZ94bAoGAEy3XeuuMF24gsBfHG8q2
+q/Et99WGXSrTYnAbKTpDwebaG7gr4xCP7hpdTUEzzlNw0lUz+u70tJpuf3+Nxa+I
+TxfjCD7YWn6TgqhNXIS8jzS7cTugAVU/2pA5tXqlRxk5aayaRvOIJgcwD3m1xuko
+uWgGeOGgEachRfc37TjKY2ECgYAmDMDxYP5dqAL3Cxbt1m/nNF9DQ9YftiXWX3PW
+OJ2BhN773m9w4Q3ihwj862hDVT7OxH0NmUmvqX0AceqpBBSO+Ro45E/qEfvuaFko
+11GbS9zeCCHTtMhqZssbQYkBT7Nrs8HEo8SJjG02YrXGmpB5vE4UDDhsIMy5vLiU
+YEIWEwKBgQCXvBaNGJxPDu3//8yJJC4gFSWUxWYZ9x12Kke6AaUoeEn8RjTlaLRm
+Ol1ZiqHTSbRJQHjqmwKJAWSA3QX4S3ISeVOms7k30Bt02m4W0NgVRB2GxkFcnESH
+OxREDAQXG2lLP9O2HZPPHZ9aKKFM+M0GlOdQlqKk3AHPx33qCXLYtg==
+-----END RSA PRIVATE KEY-----

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/c6221c2e/traffic_ops/experimental/webfront/server.pem
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/webfront/server.pem b/traffic_ops/experimental/webfront/server.pem
new file mode 100644
index 0000000..c09c2ca
--- /dev/null
+++ b/traffic_ops/experimental/webfront/server.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDbzCCAlegAwIBAgIJAJozT3F7lmQwMA0GCSqGSIb3DQEBCwUAME4xCzAJBgNV
+BAYTAlVTMQswCQYDVQQIDAJDTzEPMA0GA1UEBwwGRGVudmVyMSEwHwYDVQQKDBhJ
+bnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMTYwMzI2MjIyODExWhcNMjYwMzI0
+MjIyODExWjBOMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ08xDzANBgNVBAcMBkRl
+bnZlcjEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkq
+hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0g8TSrCJLfzMxzCB+zLo19NIrOAA2kR2
+5vi8Hu+aM2PGxJbHFPSV0LG1HF3l/4QoG1JNYKuyXaPFDtPcefU7fQ7MqpAiBWj2
+HyIfnKyZyUX867Pg2CnSPTgMZyK3NMSnkAG+FSZzXKj6Vxjh0mKN1X+JRqyv1c/r
+WLg3RxecuVdq0D23l/1nZPWidiRp2XhUKeO3L3rLKu9tf80HJZ1TN3TsUxOGUgZH
+ectN577qAaHr3KzDHVA4v7fmNRXQEUg4Iax8FxWIw0CQOFY9+lUqW1TKr7Umrqnz
+mhQmLFNBP5wuU1fIyz5HqNeR2VOt10VDBfYg7Wy08mFsSr3bhj9GMwIDAQABo1Aw
+TjAdBgNVHQ4EFgQUdme3OnuvS+09/3Nkx1FS/4xgXoUwHwYDVR0jBBgwFoAUdme3
+OnuvS+09/3Nkx1FS/4xgXoUwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC
+AQEANNo5qASaoCc5/Utr10KIO/KoFesWL0v0bDIG3r0HaEp2gD44o884yQ8pFDNR
+bhUjs1SaHHcnGX5X3OGJVuP2vuz0pFaeKZJUawT1DTwaXQCTkRFw0Cb7oWdF6/FV
+pXuLBXWvEsu19jnrZGQbJw1lsOchPDNpp/h460JBWQOOYRfu3jSyBMwih40lEb+F
+1lA943aA2oGsDYJHnGRpUMLafe4gqcGWpfHUf2TaGe+tUSI7+JrhYLzjRyqTBdD3
+5Ss+eQo5tP0aOVMMCMCHmFfxgjBHGu4syQW5VYIZtzWMEKu2fEZCjkbV6XwVYVFL
+SKh7EgT1te7yUhfLSAF2969WHg==
+-----END CERTIFICATE-----

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/c6221c2e/traffic_ops/experimental/webfront/simpleserver/server.key
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/webfront/simpleserver/server.key b/traffic_ops/experimental/webfront/simpleserver/server.key
new file mode 100644
index 0000000..b674147
--- /dev/null
+++ b/traffic_ops/experimental/webfront/simpleserver/server.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEA0g8TSrCJLfzMxzCB+zLo19NIrOAA2kR25vi8Hu+aM2PGxJbH
+FPSV0LG1HF3l/4QoG1JNYKuyXaPFDtPcefU7fQ7MqpAiBWj2HyIfnKyZyUX867Pg
+2CnSPTgMZyK3NMSnkAG+FSZzXKj6Vxjh0mKN1X+JRqyv1c/rWLg3RxecuVdq0D23
+l/1nZPWidiRp2XhUKeO3L3rLKu9tf80HJZ1TN3TsUxOGUgZHectN577qAaHr3KzD
+HVA4v7fmNRXQEUg4Iax8FxWIw0CQOFY9+lUqW1TKr7UmrqnzmhQmLFNBP5wuU1fI
+yz5HqNeR2VOt10VDBfYg7Wy08mFsSr3bhj9GMwIDAQABAoIBAQCC+H9QzG1bzQlp
+EKealg1zs/rWPvyJGrMAJAo3R7FfZVCjdlc+i5l1e7euriUfgaj4EALKyYL2u4u8
+SQBo0ix7NuUJW8C6ms0KcF5Bc6SXSBoAZDFG1hyNqwqgq5aLQiovscZwrX60QW+F
+KrByWpyQh2pyNG2V5IOa15EvtFs1e1f7+DCZ/KLjd5odxEZ93lLcDMQ4zmSl47uR
+I5rdixCQm8slUtRJxAB+nRck5htVGY8cwT6c+hx/GlZqY6xynMOwa9pC3zFegnr3
+nvHIiFEVu0CksaVBZNRe0S92r8VjHQPVRBa87Tsom6vJ0GxaeDfXchedrklCxMJh
+Y+QOPIVhAoGBAPgDxXGBD7VaUsBgimJKE1Q/inT2aZtwKrJi4l2fRE1Q0kOZvePv
+/LMiUZwcV0M+y45Y0VU2VXIdqAC1tYFhFfRKdmjCABFSidyhpIMJJZQrX3fFEDZq
+Vt95PrjlRC3+AVvVXVNWOxP4ObgorPzqXKtgxt10OltJfiLHHe0LF9nJAoGBANjS
+dUNU9OhCFA54MpfKtNLXvnYsxXigkN9rXAiOt9yd3x4l3o1EJs9t4a6stgzPaXDc
+ANkIPf1xsO9N0c+rikYQRo+JmbTaAQhac19NF7guTV6lHRa9bR4N4O14+62oXFEX
+3z5hp2Tr0/1qjp62ubDR8S7zY8ACsKiqd0msZ94bAoGAEy3XeuuMF24gsBfHG8q2
+q/Et99WGXSrTYnAbKTpDwebaG7gr4xCP7hpdTUEzzlNw0lUz+u70tJpuf3+Nxa+I
+TxfjCD7YWn6TgqhNXIS8jzS7cTugAVU/2pA5tXqlRxk5aayaRvOIJgcwD3m1xuko
+uWgGeOGgEachRfc37TjKY2ECgYAmDMDxYP5dqAL3Cxbt1m/nNF9DQ9YftiXWX3PW
+OJ2BhN773m9w4Q3ihwj862hDVT7OxH0NmUmvqX0AceqpBBSO+Ro45E/qEfvuaFko
+11GbS9zeCCHTtMhqZssbQYkBT7Nrs8HEo8SJjG02YrXGmpB5vE4UDDhsIMy5vLiU
+YEIWEwKBgQCXvBaNGJxPDu3//8yJJC4gFSWUxWYZ9x12Kke6AaUoeEn8RjTlaLRm
+Ol1ZiqHTSbRJQHjqmwKJAWSA3QX4S3ISeVOms7k30Bt02m4W0NgVRB2GxkFcnESH
+OxREDAQXG2lLP9O2HZPPHZ9aKKFM+M0GlOdQlqKk3AHPx33qCXLYtg==
+-----END RSA PRIVATE KEY-----

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/c6221c2e/traffic_ops/experimental/webfront/simpleserver/server.pem
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/webfront/simpleserver/server.pem b/traffic_ops/experimental/webfront/simpleserver/server.pem
new file mode 100644
index 0000000..c09c2ca
--- /dev/null
+++ b/traffic_ops/experimental/webfront/simpleserver/server.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDbzCCAlegAwIBAgIJAJozT3F7lmQwMA0GCSqGSIb3DQEBCwUAME4xCzAJBgNV
+BAYTAlVTMQswCQYDVQQIDAJDTzEPMA0GA1UEBwwGRGVudmVyMSEwHwYDVQQKDBhJ
+bnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMTYwMzI2MjIyODExWhcNMjYwMzI0
+MjIyODExWjBOMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ08xDzANBgNVBAcMBkRl
+bnZlcjEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkq
+hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0g8TSrCJLfzMxzCB+zLo19NIrOAA2kR2
+5vi8Hu+aM2PGxJbHFPSV0LG1HF3l/4QoG1JNYKuyXaPFDtPcefU7fQ7MqpAiBWj2
+HyIfnKyZyUX867Pg2CnSPTgMZyK3NMSnkAG+FSZzXKj6Vxjh0mKN1X+JRqyv1c/r
+WLg3RxecuVdq0D23l/1nZPWidiRp2XhUKeO3L3rLKu9tf80HJZ1TN3TsUxOGUgZH
+ectN577qAaHr3KzDHVA4v7fmNRXQEUg4Iax8FxWIw0CQOFY9+lUqW1TKr7Umrqnz
+mhQmLFNBP5wuU1fIyz5HqNeR2VOt10VDBfYg7Wy08mFsSr3bhj9GMwIDAQABo1Aw
+TjAdBgNVHQ4EFgQUdme3OnuvS+09/3Nkx1FS/4xgXoUwHwYDVR0jBBgwFoAUdme3
+OnuvS+09/3Nkx1FS/4xgXoUwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC
+AQEANNo5qASaoCc5/Utr10KIO/KoFesWL0v0bDIG3r0HaEp2gD44o884yQ8pFDNR
+bhUjs1SaHHcnGX5X3OGJVuP2vuz0pFaeKZJUawT1DTwaXQCTkRFw0Cb7oWdF6/FV
+pXuLBXWvEsu19jnrZGQbJw1lsOchPDNpp/h460JBWQOOYRfu3jSyBMwih40lEb+F
+1lA943aA2oGsDYJHnGRpUMLafe4gqcGWpfHUf2TaGe+tUSI7+JrhYLzjRyqTBdD3
+5Ss+eQo5tP0aOVMMCMCHmFfxgjBHGu4syQW5VYIZtzWMEKu2fEZCjkbV6XwVYVFL
+SKh7EgT1te7yUhfLSAF2969WHg==
+-----END CERTIFICATE-----

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/c6221c2e/traffic_ops/experimental/webfront/simpleserver/simpleserver.go
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/webfront/simpleserver/simpleserver.go b/traffic_ops/experimental/webfront/simpleserver/simpleserver.go
new file mode 100644
index 0000000..e4baeb2
--- /dev/null
+++ b/traffic_ops/experimental/webfront/simpleserver/simpleserver.go
@@ -0,0 +1,25 @@
+package main
+
+import (
+	"io"
+	"log"
+	"net/http"
+	"os"
+)
+
+var Logger *log.Logger
+
+func hello(w http.ResponseWriter, r *http.Request) {
+	io.WriteString(w, "Hitting "+os.Args[1]+" with "+r.URL.Path+"\n")
+}
+
+func main() {
+	Logger = log.New(os.Stdout, " ", log.Ldate|log.Ltime|log.Lshortfile)
+	
+	http.HandleFunc("/", hello)
+
+	// Make sure you have the server.pem and server.key file. To gen self signed:
+	// openssl genrsa -out server.key 2048
+	// openssl req -new -x509 -key server.key -out server.pem -days 3650
+	Logger.Println(http.ListenAndServeTLS(":"+os.Args[1], "server.pem", "server.key", nil))
+}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/c6221c2e/traffic_ops/experimental/webfront/startfakes.sh
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/webfront/startfakes.sh b/traffic_ops/experimental/webfront/startfakes.sh
new file mode 100644
index 0000000..66675e5
--- /dev/null
+++ b/traffic_ops/experimental/webfront/startfakes.sh
@@ -0,0 +1,7 @@
+go run simpleserver/simpleserver.go  8001 &
+go run simpleserver/simpleserver.go  8002 &
+go run simpleserver/simpleserver.go  8003 &
+go run simpleserver/simpleserver.go  8004 &
+go run simpleserver/simpleserver.go  8005 &
+go run simpleserver/simpleserver.go  8006 &
+go run simpleserver/simpleserver.go  8007

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/c6221c2e/traffic_ops/experimental/webfront/webfront.config
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/webfront/webfront.config b/traffic_ops/experimental/webfront/webfront.config
new file mode 100644
index 0000000..f487dbd
--- /dev/null
+++ b/traffic_ops/experimental/webfront/webfront.config
@@ -0,0 +1,5 @@
+{
+	"httpsAddr": ":9000",
+	"ruleFile": "rules.json", 
+	"pollInterval": 60
+}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/c6221c2e/traffic_ops/experimental/webfront/webfront.go
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/webfront/webfront.go b/traffic_ops/experimental/webfront/webfront.go
new file mode 100644
index 0000000..76be08d
--- /dev/null
+++ b/traffic_ops/experimental/webfront/webfront.go
@@ -0,0 +1,293 @@
+//  Started with https://github.com/nf/webfront/blob/master/main.go
+// by Andrew Gerrand <ad...@golang.org>
+
+package main
+
+import (
+	"crypto/tls"
+	"encoding/json"
+	"fmt"
+	jwt "github.com/dgrijalva/jwt-go"
+	"log"
+	"net/http"
+	"net/http/httputil"
+	"os"
+	"path"
+	"strings"
+	"sync"
+	"time"
+)
+
+// TODO(amiry) - Handle token expiration
+// TODO(amiry) - Handle refresh tokens
+// TODO(amiry) - Where to keep the jwt secret? In the command line
+
+// Server implements an http.Handler that acts as a reverse proxy
+type Server struct {
+	mu    sync.RWMutex // guards the fields below
+	last  time.Time
+	rules []*Rule
+}
+
+// Rule represents a rule in a configuration file.
+type Rule struct {
+	Host          string              // to match against request Host header
+	Path          string              // to match against a path (start)
+	Forward       string              // reverse proxy map-to
+	Secure        bool                // protect with jwt?
+	Capabilities  map[string]string   // map HTTP methods to capabilitues  
+
+	handler http.Handler
+}
+
+type Claims struct {
+    Capabilities []string `json:"cap"`
+    jwt.StandardClaims
+}
+
+// Config holds the configuration of the server.
+type Config struct {
+	HTTPSAddr    string `json:"httpsAddr"`
+	RuleFile     string `json:"ruleFile"`
+	PollInterval int    `json:"pollInterval"`
+}
+
+var Logger *log.Logger
+
+func printUsage() {
+	exampleConfig := `{
+	"httpsAddr": ":9000",
+	"ruleFile": "rules.json",
+	"pollInterval": 60
+}`
+	fmt.Println("Usage: " + path.Base(os.Args[0]) + " config-file")
+	fmt.Println("")
+	fmt.Println("Example config-file:")
+	fmt.Println(exampleConfig)
+}
+
+func main() {
+
+	if len(os.Args) < 2 {
+		printUsage()
+		return
+	}
+
+	Logger = log.New(os.Stdout, " ", log.Ldate|log.Ltime|log.Lshortfile)
+
+	file, err := os.Open(os.Args[1])
+	if err != nil {
+		Logger.Println("Error opening config file:", err)
+		return
+	}
+	decoder := json.NewDecoder(file)
+	config := Config{}
+	err = decoder.Decode(&config)
+	if err != nil {
+		Logger.Println("Error reading config file:", err)
+		return
+	}
+	Logger.Println("Starting webfront...")
+	s, err := NewServer(config.RuleFile, time.Duration(config.PollInterval)*time.Second)
+	if err != nil {
+		Logger.Fatal(err)
+	}
+
+	// override the default so we can use self-signed certs on our microservices
+	// and use a self-signed cert in this server
+	http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
+	if _, err := os.Stat("server.pem"); os.IsNotExist(err) {
+		Logger.Fatal("server.pem file not found")
+	}
+	if _, err := os.Stat("server.key"); os.IsNotExist(err) {
+		Logger.Fatal("server.key file not found")
+	}
+	http.ListenAndServeTLS(config.HTTPSAddr, "server.pem", "server.key", s)
+}
+
+func validateToken(tokenString string) (*jwt.Token, error) {
+
+	tokenString = strings.Replace(tokenString, "Bearer ", "", 1)
+	token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
+		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
+			return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
+		}
+		return []byte("CAmeRAFiveSevenNineNine"), nil
+	})
+	return token, err
+}
+
+// NewServer constructs a Server that reads rules from file with a period
+// specified by poll.
+func NewServer(file string, poll time.Duration) (*Server, error) {
+	s := new(Server)
+	if err := s.loadRules(file); err != nil {
+		Logger.Fatal("Error loading rules file: ", err)
+	}
+	go s.refreshRules(file, poll)
+	return s, nil
+}
+
+// ServeHTTP matches the Request with a Rule and, if found, serves the
+// request with the Rule's handler. If the rule's secure field is true, it will
+// only allow access if the request has a valid JWT bearer token.
+func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+
+	rule := s.getRule(r)
+	if rule == nil {
+		Logger.Printf("%v %v No mapping in rules file!", r.Method, r.URL.RequestURI())
+		http.Error(w, "Not found", http.StatusNotFound)
+		return
+	}
+
+	isAuthorized := false
+
+	if rule.Secure {
+		tokenValid := false
+		token, err := validateToken(r.Header.Get("Authorization"))
+
+		if err == nil {
+			tokenValid = true
+		} else {
+			Logger.Println("Token Error:", err.Error())
+		}
+
+		if !tokenValid {
+			Logger.Printf("%v %v Valid token required, but none found!", r.Method, r.URL.RequestURI())
+			w.WriteHeader(http.StatusForbidden)
+			return
+		}
+
+		claims, ok := token.Claims.(*Claims)
+		if !ok {
+			Logger.Printf("%v %v Valid token found, but cannot parse claims!", r.Method, r.URL.RequestURI())
+			w.WriteHeader(http.StatusForbidden)
+			return
+		}
+
+		// Authorization: Check is the list of capabilities in the token's claims contains the reqired capability
+		// that is listed in the rule
+		Logger.Printf("Required capabilities %v", rule.Capabilities)
+		for _, c := range claims.Capabilities {
+        	if c == rule.Capabilities[r.Method] {
+				isAuthorized = true
+				break
+			}
+        }
+
+		Logger.Printf("%v %v Valid token. Subject=%v, ExpiresAt=%v, Capabilities=%v, Required=%v, Authorized=%v", 
+			r.Method, r.URL.RequestURI(), claims.Subject, claims.ExpiresAt, claims.Capabilities, 
+			rule.Capabilities[r.Method], isAuthorized)
+
+	} else {
+		isAuthorized = true
+	}
+
+	if isAuthorized {
+		if h := rule.handler; h != nil {
+			h.ServeHTTP(w, r)
+			return
+		}
+	}
+
+	http.Error(w, "Not Authorized", http.StatusUnauthorized)
+	return
+}
+
+func rejectNoToken(handler http.Handler) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		w.WriteHeader(http.StatusForbidden)
+	}
+}
+
+func (s *Server) getRule(req *http.Request) *Rule {
+	s.mu.RLock()
+	defer s.mu.RUnlock()
+
+	h := req.Host
+	p := req.URL.Path
+
+	// Some clients include a port in the request host; strip it.
+	if i := strings.Index(h, ":"); i >= 0 {
+		h = h[:i]
+	}
+
+	for _, r := range s.rules {
+		if strings.HasPrefix(p, r.Path) {
+			// Logger.Printf("Found rule")
+			return r
+		}
+	}
+
+	// Logger.Printf("Rule not found")
+	return nil
+}
+
+// refreshRules polls file periodically and refreshes the Server's rule
+// set if the file has been modified.
+func (s *Server) refreshRules(file string, poll time.Duration) {
+	for {
+		// Logger.Printf("loading rule file")
+		if err := s.loadRules(file); err != nil {
+			Logger.Println(file, ":", err)
+		}
+		time.Sleep(poll)
+	}
+}
+
+// loadRules tests whether file has been modified since its last invocation
+// and, if so, loads the rule set from file.
+func (s *Server) loadRules(file string) error {
+	fi, err := os.Stat(file)
+	if err != nil {
+		return err
+	}
+	mtime := fi.ModTime()
+	if !mtime.After(s.last) && s.rules != nil {
+		return nil // no change
+	}
+	rules, err := parseRules(file)
+	if err != nil {
+		return err
+	}
+	s.mu.Lock()
+	s.last = mtime
+	s.rules = rules
+	s.mu.Unlock()
+	return nil
+}
+
+// parseRules reads rule definitions from file, constructs the Rule handlers,
+// and returns the resultant Rules.
+func parseRules(file string) ([]*Rule, error) {
+	f, err := os.Open(file)
+	if err != nil {
+		return nil, err
+	}
+	defer f.Close()
+	var rules []*Rule
+	if err := json.NewDecoder(f).Decode(&rules); err != nil {
+		return nil, err
+	}
+	for _, r := range rules {
+		r.handler = makeHandler(r)
+		if r.handler == nil {
+			Logger.Printf("Bad rule: %#v", r)
+		}
+	}
+	return rules, nil
+}
+
+// makeHandler constructs the appropriate Handler for the given Rule.
+func makeHandler(r *Rule) http.Handler {
+	if h := r.Forward; h != "" {
+		return &httputil.ReverseProxy{
+			Director: func(req *http.Request) {
+				req.URL.Scheme = "https"
+				req.URL.Host = h
+				// req.URL.Path = "/boo1" // TODO JvD - regex to change path here
+			},
+		}
+	}
+	return nil
+}