Merge branch 'update-scs'

This commit is contained in:
paul 2019-05-15 19:31:19 +02:00
commit 4c0faeee3e
6 changed files with 36 additions and 59 deletions

View file

@ -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"]

View file

@ -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

View file

@ -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,
},
}

View file

@ -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)

View file

@ -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{

View file

@ -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
}