From 8aebe43fd38de9e36f526363d6102385e9daec15 Mon Sep 17 00:00:00 2001 From: Paul Date: Wed, 15 May 2019 19:08:24 +0200 Subject: [PATCH] Update SCS session manager --- Dockerfile | 5 ++- README.md | 1 - main.go | 6 ++-- middleware/requirelogin.go | 1 + router/router.go | 17 +++++----- services/sessions.go | 65 ++++++++++++++------------------------ 6 files changed, 36 insertions(+), 59 deletions(-) diff --git a/Dockerfile b/Dockerfile index 4196688..89eeb37 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,8 +19,7 @@ ENV \ VPN_DEV="tun" \ VPN_HOST="vpn.example.com" \ VPN_PORT="1194" \ - VPN_PROTO="udp" \ - APP_KEY="" + VPN_PROTO="udp" COPY --from=0 /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ -COPY --from=0 /go/src/github.com/zom-bi/ovpn-certman/certman / +COPY --from=0 /go/src/github.com/zom-bi/ovpn-certman/ovpn-certman /certman ENTRYPOINT ["/certman"] diff --git a/README.md b/README.md index bda4a26..46afe08 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,6 @@ variables: * `OAUTH2_TOKEN_URL` the URL to the "/token" endpoint of the identity provider * `OAUTH2_REDIRECT_URL` the redirect URL used by the app, usually the hostname suffixed by "/login/oauth2/redirect" * `USER_ENDPOINT` the URL to the Identity provider user endpoint, for gitlab this is "/api/v4/user". The "username" attribute of the returned JSON will used for authentication. - * `APP_KEY` random ASCII string, 32 characters in length. Used for cookie generation. * `APP_LISTEN` port and ip to listen on, e.g. `:8000` or `127.0.0.1:3000` * `VPN_DEV` which device is used by the network, either `tun` or `tap` (check server cfg) * `VPN_HOST` Hostname or IP address of the server diff --git a/main.go b/main.go index 215c18e..31b58e9 100644 --- a/main.go +++ b/main.go @@ -22,10 +22,8 @@ func main() { c := services.Config{ CollectionPath: "./clients.json", Sessions: &services.SessionsConfig{ - SessionName: "_session", - CookieKey: os.Getenv("APP_KEY"), - HttpOnly: true, - Lifetime: 24 * time.Hour, + HTTPOnly: true, + Lifetime: 24 * time.Hour, }, } diff --git a/middleware/requirelogin.go b/middleware/requirelogin.go index f3b993e..2ac6b42 100644 --- a/middleware/requirelogin.go +++ b/middleware/requirelogin.go @@ -13,6 +13,7 @@ func RequireLogin(sessions *services.Sessions) func(http.Handler) http.Handler { fn := func(w http.ResponseWriter, req *http.Request) { if username := sessions.GetUsername(req); username == "" { http.Redirect(w, req, "/login", http.StatusFound) + return } next.ServeHTTP(w, req) diff --git a/router/router.go b/router/router.go index 40fce9d..de22143 100644 --- a/router/router.go +++ b/router/router.go @@ -5,25 +5,24 @@ import ( "os" "strings" - "github.com/zom-bi/ovpn-certman/services" + "github.com/go-chi/chi" + "github.com/go-chi/chi/middleware" + "github.com/gorilla/csrf" + "github.com/gorilla/securecookie" "golang.org/x/oauth2" "github.com/zom-bi/ovpn-certman/assets" "github.com/zom-bi/ovpn-certman/handlers" - "github.com/zom-bi/ovpn-certman/views" - "github.com/go-chi/chi" - "github.com/go-chi/chi/middleware" - "github.com/gorilla/csrf" - mw "github.com/zom-bi/ovpn-certman/middleware" + "github.com/zom-bi/ovpn-certman/services" + "github.com/zom-bi/ovpn-certman/views" ) var ( // TODO: make this configurable csrfCookieName = "csrf" csrfFieldName = "csrf_token" - csrfKey = []byte("7Oj4DllZ9lTsxJnisTuWiiQBGQIzi6gX") - cookieKey = []byte("osx70sMD8HZG2ouUl8uKI4wcMugiJ2WH") + csrfKey = securecookie.GenerateRandomKey(32) ) func HandleRoutes(provider *services.Provider) http.Handler { @@ -34,7 +33,7 @@ func HandleRoutes(provider *services.Provider) http.Handler { mux.Use(middleware.RealIP) // use proxy headers mux.Use(middleware.RedirectSlashes) // redirect trailing slashes mux.Use(mw.Recoverer) // recover on panic - mux.Use(provider.Sessions.Manager.Use) // use session storage + mux.Use(provider.Sessions.LoadAndSave) // use session storage // TODO: move this code away from here oauth2Config := &oauth2.Config{ diff --git a/services/sessions.go b/services/sessions.go index 83da679..4c594c8 100644 --- a/services/sessions.go +++ b/services/sessions.go @@ -2,6 +2,7 @@ package services import ( "encoding/gob" + "errors" "fmt" "html/template" "log" @@ -11,7 +12,7 @@ import ( "github.com/alexedwards/scs" ) -var ( +const ( // FlashesKey is the key used for the flashes in the cookie FlashesKey = "_flashes" // UserEmailKey is the key used to reference usernames @@ -24,29 +25,24 @@ func init() { } type SessionsConfig struct { - SessionName string - CookieKey string - HttpOnly bool - Secure bool - Lifetime time.Duration + HTTPOnly bool + Secure bool + Lifetime time.Duration } // Sessions is a wrapped scs.Store in order to implement custom logic type Sessions struct { - *scs.Manager + *scs.Session } // NewSessions populates the default sessions Store func NewSessions(conf *SessionsConfig) *Sessions { - store := scs.NewCookieManager( - conf.CookieKey, - ) - store.Name(conf.SessionName) - store.HttpOnly(true) - store.Lifetime(conf.Lifetime) - store.Secure(conf.Secure) + session := scs.NewSession() + session.Lifetime = conf.Lifetime + session.Cookie.HttpOnly = true + session.Cookie.Secure = conf.Secure - return &Sessions{store} + return &Sessions{session} } func (store *Sessions) GetUsername(req *http.Request) string { @@ -56,17 +52,8 @@ func (store *Sessions) GetUsername(req *http.Request) string { return "" } - sess := store.Load(req) - - email, err := sess.GetString(UserEmailKey) - if err != nil { - // Username found - return "" - - } - - // User is logged in - return email + email := store.GetString(req.Context(), UserEmailKey) + return email // "" if no user is logged in } func (store *Sessions) SetUsername(w http.ResponseWriter, req *http.Request, username string) { @@ -75,13 +62,10 @@ func (store *Sessions) SetUsername(w http.ResponseWriter, req *http.Request, use return } - sess := store.Load(req) - // renew token to avoid session pinning/fixation attack - sess.RenewToken(w) - - sess.PutString(w, UserEmailKey, username) + store.RenewToken(req.Context()) + store.Put(req.Context(), UserEmailKey, username) } type Flash struct { @@ -101,23 +85,20 @@ func (flash Flash) Render() template.HTML { // Flash add flash message to session data func (store *Sessions) Flash(w http.ResponseWriter, req *http.Request, flash Flash) error { - var flashes []Flash - - sess := store.Load(req) - - if err := sess.GetObject(FlashesKey, &flashes); err != nil { - return err + flashes, ok := store.Get(req.Context(), FlashesKey).([]Flash) + if !ok { + return errors.New("Could not get flashes") } flashes = append(flashes, flash) - - return sess.PutObject(w, FlashesKey, flashes) + return nil } // Flashes returns a slice of flash messages from session data func (store *Sessions) Flashes(w http.ResponseWriter, req *http.Request) []Flash { - var flashes []Flash - sess := store.Load(req) - sess.PopObject(w, FlashesKey, &flashes) + flashes, ok := store.Pop(req.Context(), FlashesKey).([]Flash) + if !ok { + return nil + } return flashes }