From 175da8f22cd791e81338fe61e6099125868cf5a0 Mon Sep 17 00:00:00 2001 From: Melonai Date: Mon, 26 Jul 2021 23:50:43 +0200 Subject: Basic Authentication and Authorization --- assets/templates/authenticate.template.html | 8 +++++- assets/templates/information.template.html | 2 +- config.go | 11 ++++++++ go.mod | 1 + go.sum | 2 ++ handlers/passthrough.go | 8 +++++- handlers/portgate.go | 43 +++++++++++++++++++++++++++-- token.go | 42 ++++++++++++++++++++++++++++ 8 files changed, 111 insertions(+), 6 deletions(-) create mode 100644 token.go diff --git a/assets/templates/authenticate.template.html b/assets/templates/authenticate.template.html index 21f31e4..7d6d72c 100644 --- a/assets/templates/authenticate.template.html +++ b/assets/templates/authenticate.template.html @@ -1,3 +1,9 @@ {{define "content"}} -

This is the authentication page...

+

Authentication

+
+ + +
{{end}} \ No newline at end of file diff --git a/assets/templates/information.template.html b/assets/templates/information.template.html index e87885c..a881cf8 100644 --- a/assets/templates/information.template.html +++ b/assets/templates/information.template.html @@ -1,3 +1,3 @@ {{define "content"}} -

This is the information page...

+

You are authenticated!

{{end}} \ No newline at end of file diff --git a/config.go b/config.go index b4e0754..7630e87 100644 --- a/config.go +++ b/config.go @@ -13,6 +13,10 @@ type Config struct { allowedPorts []int forbiddenPorts []int + + key string + + jwtSecret string } // GetConfig creates the Portgate config from outside sources such as @@ -25,6 +29,8 @@ func GetConfig() (Config, error) { allowedPorts: []int{80}, forbiddenPorts: []int{}, + + key: "password", }, nil } @@ -43,3 +49,8 @@ func (c *Config) MakeUrl(p Path) string { // TODO: Figure out what to do with TLS return fmt.Sprintf("http://%s:%d%s", c.targetHost, p.DestinationIdentifier, p.ResourcePath) } + +// CheckKey checks whether the givenKey matches the one in the config. +func (c *Config) CheckKey(givenKey string) bool { + return c.key == givenKey +} diff --git a/go.mod b/go.mod index 6ba1ccd..122ac96 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.16 require ( github.com/valyala/fasthttp v1.28.0 + github.com/golang-jwt/jwt v3.2.1+incompatible github.com/andybalholm/brotli v1.0.3 // indirect github.com/klauspost/compress v1.13.1 // indirect diff --git a/go.sum b/go.sum index c912579..0a7383f 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,8 @@ github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/andybalholm/brotli v1.0.3 h1:fpcw+r1N1h0Poc1F/pHbW40cUm/lMEQslZtCkBQ0UnM= github.com/andybalholm/brotli v1.0.3/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c= +github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.1 h1:wXr2uRxZTJXHLly6qhJabee5JqIhTRoLBhDOA74hDEQ= diff --git a/handlers/passthrough.go b/handlers/passthrough.go index b2daa88..3f8aafc 100644 --- a/handlers/passthrough.go +++ b/handlers/passthrough.go @@ -2,6 +2,7 @@ package handlers import ( "github.com/valyala/fasthttp" + "net/http" "portgate" ) @@ -9,9 +10,14 @@ import ( // If the user is authorized they are allowed to pass, otherwise they should be redirected to // the authentication page. (/_portgate) func (h *RequestHandler) handlePassthroughRequest(ctx *fasthttp.RequestCtx, p portgate.Path) { - // TODO: Check authorization. // TODO: Check whether port is allowed to be accessed. + // Check whether given cookie is ok, if not redirect to the authentication page. + if !portgate.VerifyTokenFromCookie(h.config, ctx) { + ctx.Redirect("/_portgate", http.StatusTemporaryRedirect) + return + } + // We reuse the request given to us by the user with minor changes to route it to the // destination host. ctx.Request.SetRequestURI(h.config.MakeUrl(p)) diff --git a/handlers/portgate.go b/handlers/portgate.go index f53f3c9..6e002e7 100644 --- a/handlers/portgate.go +++ b/handlers/portgate.go @@ -2,7 +2,9 @@ package handlers import ( "github.com/valyala/fasthttp" + "net/http" "portgate" + "time" ) // handlePortgateRequest handles all Portgate specific request for either showing Portgate @@ -32,14 +34,49 @@ func (h *RequestHandler) handlePortgateIndexRequest(ctx *fasthttp.RequestCtx) { // handlePortgatePageRequest renders the Portgate page with either the authentication page or // a basic information page. func (h *RequestHandler) handlePortgatePageRequest(ctx *fasthttp.RequestCtx) { - // We render the page template and pass it to the user. ctx.Response.Header.SetContentType("text/html") - err := h.templates.ExecuteTemplate(ctx, "authenticate.template.html", nil) + + var err error + + // We render the page template and pass it to the user. + if portgate.VerifyTokenFromCookie(h.config, ctx) { + // User is authenticated, show the information page + err = h.templates.ExecuteTemplate(ctx, "information.template.html", nil) + } else { + // Show the authentication page + err = h.templates.ExecuteTemplate(ctx, "authenticate.template.html", nil) + } + if err != nil { h.handleError(ctx) } } func (h *RequestHandler) handleAuthenticateRequest(ctx *fasthttp.RequestCtx) { - // TODO + + givenKey := ctx.PostArgs().Peek("key") + if givenKey == nil || !h.config.CheckKey(string(givenKey)) { + ctx.Error("Wrong key.", http.StatusUnauthorized) + return + } + + token, err := portgate.CreateToken(h.config, string(givenKey)) + if err != nil { + h.handleError(ctx) + } + + cookie := fasthttp.AcquireCookie() + defer fasthttp.ReleaseCookie(cookie) + + cookie.SetExpire(portgate.GetExpirationDateFrom(time.Now())) + cookie.SetSameSite(fasthttp.CookieSameSiteStrictMode) + cookie.SetHTTPOnly(true) + cookie.SetKey("_portgate_token") + cookie.SetValue(token) + + ctx.Response.Header.SetCookie(cookie) + + // TODO: Redirect to previously request path. + // http.StatusFound redirects a POST request to a GET request. + ctx.Redirect("/_portgate", http.StatusFound) } diff --git a/token.go b/token.go new file mode 100644 index 0000000..1031e7c --- /dev/null +++ b/token.go @@ -0,0 +1,42 @@ +package portgate + +import ( + "github.com/golang-jwt/jwt" + "github.com/valyala/fasthttp" + "time" +) + +func CreateToken(config *Config, givenKey string) (string, error) { + // Our token will last 7 days + token := jwt.NewWithClaims(jwt.SigningMethodHS512, jwt.StandardClaims{ + ExpiresAt: GetExpirationDateFrom(time.Now()).Unix(), + IssuedAt: time.Now().Unix(), + }) + + return token.SignedString([]byte(config.jwtSecret)) +} + +func VerifyToken(config *Config, tokenString string) (bool, error) { + token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { + return []byte(config.jwtSecret), nil + }) + + if err == nil && token.Valid { + return true, nil + } else { + return false, err + } +} + +func VerifyTokenFromCookie(config *Config, ctx *fasthttp.RequestCtx) bool { + cookie := ctx.Request.Header.Cookie("_portgate_token") + if cookie != nil { + ok, _ := VerifyToken(config, string(cookie)) + return ok + } + return false +} + +func GetExpirationDateFrom(date time.Time) time.Time { + return date.AddDate(0, 0, 7) +} -- cgit 1.4.1