Update SCS session manager
This commit is contained in:
parent
f9f6fb64b9
commit
8aebe43fd3
6 changed files with 36 additions and 59 deletions
|
@ -19,8 +19,7 @@ ENV \
|
||||||
VPN_DEV="tun" \
|
VPN_DEV="tun" \
|
||||||
VPN_HOST="vpn.example.com" \
|
VPN_HOST="vpn.example.com" \
|
||||||
VPN_PORT="1194" \
|
VPN_PORT="1194" \
|
||||||
VPN_PROTO="udp" \
|
VPN_PROTO="udp"
|
||||||
APP_KEY=""
|
|
||||||
COPY --from=0 /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
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"]
|
ENTRYPOINT ["/certman"]
|
||||||
|
|
|
@ -38,7 +38,6 @@ variables:
|
||||||
* `OAUTH2_TOKEN_URL` the URL to the "/token" endpoint of the identity provider
|
* `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"
|
* `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.
|
* `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`
|
* `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_DEV` which device is used by the network, either `tun` or `tap` (check server cfg)
|
||||||
* `VPN_HOST` Hostname or IP address of the server
|
* `VPN_HOST` Hostname or IP address of the server
|
||||||
|
|
6
main.go
6
main.go
|
@ -22,10 +22,8 @@ func main() {
|
||||||
c := services.Config{
|
c := services.Config{
|
||||||
CollectionPath: "./clients.json",
|
CollectionPath: "./clients.json",
|
||||||
Sessions: &services.SessionsConfig{
|
Sessions: &services.SessionsConfig{
|
||||||
SessionName: "_session",
|
HTTPOnly: true,
|
||||||
CookieKey: os.Getenv("APP_KEY"),
|
Lifetime: 24 * time.Hour,
|
||||||
HttpOnly: true,
|
|
||||||
Lifetime: 24 * time.Hour,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ func RequireLogin(sessions *services.Sessions) func(http.Handler) http.Handler {
|
||||||
fn := func(w http.ResponseWriter, req *http.Request) {
|
fn := func(w http.ResponseWriter, req *http.Request) {
|
||||||
if username := sessions.GetUsername(req); username == "" {
|
if username := sessions.GetUsername(req); username == "" {
|
||||||
http.Redirect(w, req, "/login", http.StatusFound)
|
http.Redirect(w, req, "/login", http.StatusFound)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
next.ServeHTTP(w, req)
|
next.ServeHTTP(w, req)
|
||||||
|
|
|
@ -5,25 +5,24 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"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"
|
"golang.org/x/oauth2"
|
||||||
|
|
||||||
"github.com/zom-bi/ovpn-certman/assets"
|
"github.com/zom-bi/ovpn-certman/assets"
|
||||||
"github.com/zom-bi/ovpn-certman/handlers"
|
"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"
|
mw "github.com/zom-bi/ovpn-certman/middleware"
|
||||||
|
"github.com/zom-bi/ovpn-certman/services"
|
||||||
|
"github.com/zom-bi/ovpn-certman/views"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// TODO: make this configurable
|
// TODO: make this configurable
|
||||||
csrfCookieName = "csrf"
|
csrfCookieName = "csrf"
|
||||||
csrfFieldName = "csrf_token"
|
csrfFieldName = "csrf_token"
|
||||||
csrfKey = []byte("7Oj4DllZ9lTsxJnisTuWiiQBGQIzi6gX")
|
csrfKey = securecookie.GenerateRandomKey(32)
|
||||||
cookieKey = []byte("osx70sMD8HZG2ouUl8uKI4wcMugiJ2WH")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func HandleRoutes(provider *services.Provider) http.Handler {
|
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.RealIP) // use proxy headers
|
||||||
mux.Use(middleware.RedirectSlashes) // redirect trailing slashes
|
mux.Use(middleware.RedirectSlashes) // redirect trailing slashes
|
||||||
mux.Use(mw.Recoverer) // recover on panic
|
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
|
// TODO: move this code away from here
|
||||||
oauth2Config := &oauth2.Config{
|
oauth2Config := &oauth2.Config{
|
||||||
|
|
|
@ -2,6 +2,7 @@ package services
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/gob"
|
"encoding/gob"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
"log"
|
"log"
|
||||||
|
@ -11,7 +12,7 @@ import (
|
||||||
"github.com/alexedwards/scs"
|
"github.com/alexedwards/scs"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
const (
|
||||||
// FlashesKey is the key used for the flashes in the cookie
|
// FlashesKey is the key used for the flashes in the cookie
|
||||||
FlashesKey = "_flashes"
|
FlashesKey = "_flashes"
|
||||||
// UserEmailKey is the key used to reference usernames
|
// UserEmailKey is the key used to reference usernames
|
||||||
|
@ -24,29 +25,24 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
type SessionsConfig struct {
|
type SessionsConfig struct {
|
||||||
SessionName string
|
HTTPOnly bool
|
||||||
CookieKey string
|
Secure bool
|
||||||
HttpOnly bool
|
Lifetime time.Duration
|
||||||
Secure bool
|
|
||||||
Lifetime time.Duration
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sessions is a wrapped scs.Store in order to implement custom logic
|
// Sessions is a wrapped scs.Store in order to implement custom logic
|
||||||
type Sessions struct {
|
type Sessions struct {
|
||||||
*scs.Manager
|
*scs.Session
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSessions populates the default sessions Store
|
// NewSessions populates the default sessions Store
|
||||||
func NewSessions(conf *SessionsConfig) *Sessions {
|
func NewSessions(conf *SessionsConfig) *Sessions {
|
||||||
store := scs.NewCookieManager(
|
session := scs.NewSession()
|
||||||
conf.CookieKey,
|
session.Lifetime = conf.Lifetime
|
||||||
)
|
session.Cookie.HttpOnly = true
|
||||||
store.Name(conf.SessionName)
|
session.Cookie.Secure = conf.Secure
|
||||||
store.HttpOnly(true)
|
|
||||||
store.Lifetime(conf.Lifetime)
|
|
||||||
store.Secure(conf.Secure)
|
|
||||||
|
|
||||||
return &Sessions{store}
|
return &Sessions{session}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (store *Sessions) GetUsername(req *http.Request) string {
|
func (store *Sessions) GetUsername(req *http.Request) string {
|
||||||
|
@ -56,17 +52,8 @@ func (store *Sessions) GetUsername(req *http.Request) string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
sess := store.Load(req)
|
email := store.GetString(req.Context(), UserEmailKey)
|
||||||
|
return email // "" if no user is logged in
|
||||||
email, err := sess.GetString(UserEmailKey)
|
|
||||||
if err != nil {
|
|
||||||
// Username found
|
|
||||||
return ""
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// User is logged in
|
|
||||||
return email
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (store *Sessions) SetUsername(w http.ResponseWriter, req *http.Request, username string) {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
sess := store.Load(req)
|
|
||||||
|
|
||||||
// renew token to avoid session pinning/fixation attack
|
// renew token to avoid session pinning/fixation attack
|
||||||
sess.RenewToken(w)
|
store.RenewToken(req.Context())
|
||||||
|
|
||||||
sess.PutString(w, UserEmailKey, username)
|
|
||||||
|
|
||||||
|
store.Put(req.Context(), UserEmailKey, username)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Flash struct {
|
type Flash struct {
|
||||||
|
@ -101,23 +85,20 @@ func (flash Flash) Render() template.HTML {
|
||||||
|
|
||||||
// Flash add flash message to session data
|
// Flash add flash message to session data
|
||||||
func (store *Sessions) Flash(w http.ResponseWriter, req *http.Request, flash Flash) error {
|
func (store *Sessions) Flash(w http.ResponseWriter, req *http.Request, flash Flash) error {
|
||||||
var flashes []Flash
|
flashes, ok := store.Get(req.Context(), FlashesKey).([]Flash)
|
||||||
|
if !ok {
|
||||||
sess := store.Load(req)
|
return errors.New("Could not get flashes")
|
||||||
|
|
||||||
if err := sess.GetObject(FlashesKey, &flashes); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
flashes = append(flashes, flash)
|
flashes = append(flashes, flash)
|
||||||
|
return nil
|
||||||
return sess.PutObject(w, FlashesKey, flashes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flashes returns a slice of flash messages from session data
|
// Flashes returns a slice of flash messages from session data
|
||||||
func (store *Sessions) Flashes(w http.ResponseWriter, req *http.Request) []Flash {
|
func (store *Sessions) Flashes(w http.ResponseWriter, req *http.Request) []Flash {
|
||||||
var flashes []Flash
|
flashes, ok := store.Pop(req.Context(), FlashesKey).([]Flash)
|
||||||
sess := store.Load(req)
|
if !ok {
|
||||||
sess.PopObject(w, FlashesKey, &flashes)
|
return nil
|
||||||
|
}
|
||||||
return flashes
|
return flashes
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue