Merge: + Auth: add "web_session_ttl" setting; improve logging
Close #1006 * commit 'b03b36e47e1f230567fce0936ee1285b6861350a': * auth: improve logging + config: "web_session_ttl" setting
This commit is contained in:
commit
1b7ef0e4f4
50
home/auth.go
50
home/auth.go
|
@ -18,7 +18,6 @@ import (
|
|||
)
|
||||
|
||||
const cookieTTL = 365 * 24 // in hours
|
||||
const expireTime = 30 * 24 // in hours
|
||||
|
||||
type session struct {
|
||||
userName string
|
||||
|
@ -60,6 +59,7 @@ type Auth struct {
|
|||
sessions map[string]*session // session name -> session data
|
||||
lock sync.Mutex
|
||||
users []User
|
||||
sessionTTL uint32 // in seconds
|
||||
}
|
||||
|
||||
// User object
|
||||
|
@ -69,8 +69,9 @@ type User struct {
|
|||
}
|
||||
|
||||
// InitAuth - create a global object
|
||||
func InitAuth(dbFilename string, users []User) *Auth {
|
||||
func InitAuth(dbFilename string, users []User, sessionTTL uint32) *Auth {
|
||||
a := Auth{}
|
||||
a.sessionTTL = sessionTTL
|
||||
a.sessions = make(map[string]*session)
|
||||
rand.Seed(time.Now().UTC().Unix())
|
||||
var err error
|
||||
|
@ -145,18 +146,21 @@ func (a *Auth) loadSessions() {
|
|||
|
||||
// store session data in file
|
||||
func (a *Auth) addSession(data []byte, s *session) {
|
||||
name := hex.EncodeToString(data)
|
||||
a.lock.Lock()
|
||||
a.sessions[hex.EncodeToString(data)] = s
|
||||
a.sessions[name] = s
|
||||
a.lock.Unlock()
|
||||
a.storeSession(data, s)
|
||||
if a.storeSession(data, s) {
|
||||
log.Info("Auth: created session %s: expire=%d", name, s.expire)
|
||||
}
|
||||
}
|
||||
|
||||
// store session data in file
|
||||
func (a *Auth) storeSession(data []byte, s *session) {
|
||||
func (a *Auth) storeSession(data []byte, s *session) bool {
|
||||
tx, err := a.db.Begin(true)
|
||||
if err != nil {
|
||||
log.Error("Auth: bbolt.Begin: %s", err)
|
||||
return
|
||||
return false
|
||||
}
|
||||
defer func() {
|
||||
_ = tx.Rollback()
|
||||
|
@ -165,21 +169,20 @@ func (a *Auth) storeSession(data []byte, s *session) {
|
|||
bkt, err := tx.CreateBucketIfNotExists(bucketName())
|
||||
if err != nil {
|
||||
log.Error("Auth: bbolt.CreateBucketIfNotExists: %s", err)
|
||||
return
|
||||
return false
|
||||
}
|
||||
err = bkt.Put(data, s.serialize())
|
||||
if err != nil {
|
||||
log.Error("Auth: bbolt.Put: %s", err)
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
log.Error("Auth: bbolt.Commit: %s", err)
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
log.Debug("Auth: stored session in DB")
|
||||
return true
|
||||
}
|
||||
|
||||
// remove session from file
|
||||
|
@ -233,7 +236,7 @@ func (a *Auth) CheckSession(sess string) int {
|
|||
return 1
|
||||
}
|
||||
|
||||
newExpire := now + expireTime*60*60
|
||||
newExpire := now + a.sessionTTL
|
||||
if s.expire/(24*60*60) != newExpire/(24*60*60) {
|
||||
// update expiration time once a day
|
||||
update = true
|
||||
|
@ -244,7 +247,9 @@ func (a *Auth) CheckSession(sess string) int {
|
|||
|
||||
if update {
|
||||
key, _ := hex.DecodeString(sess)
|
||||
a.storeSession(key, s)
|
||||
if a.storeSession(key, s) {
|
||||
log.Debug("Auth: updated session %s: expire=%d", sess, s.expire)
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
|
@ -270,8 +275,8 @@ func getSession(u *User) []byte {
|
|||
return hash[:]
|
||||
}
|
||||
|
||||
func httpCookie(req loginJSON) string {
|
||||
u := config.auth.UserFind(req.Name, req.Password)
|
||||
func (a *Auth) httpCookie(req loginJSON) string {
|
||||
u := a.UserFind(req.Name, req.Password)
|
||||
if len(u.Name) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
@ -286,8 +291,8 @@ func httpCookie(req loginJSON) string {
|
|||
|
||||
s := session{}
|
||||
s.userName = u.Name
|
||||
s.expire = uint32(now.Unix()) + expireTime*60*60
|
||||
config.auth.addSession(sess, &s)
|
||||
s.expire = uint32(now.Unix()) + a.sessionTTL
|
||||
a.addSession(sess, &s)
|
||||
|
||||
return fmt.Sprintf("session=%s; Path=/; HttpOnly; Expires=%s", hex.EncodeToString(sess), expstr)
|
||||
}
|
||||
|
@ -300,10 +305,11 @@ func handleLogin(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
cookie := httpCookie(req)
|
||||
cookie := config.auth.httpCookie(req)
|
||||
if len(cookie) == 0 {
|
||||
log.Info("Auth: invalid user name or password: name='%s'", req.Name)
|
||||
time.Sleep(1 * time.Second)
|
||||
httpError(w, http.StatusBadRequest, "invalid login or password")
|
||||
http.Error(w, "invalid user name or password", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -365,7 +371,7 @@ func optionalAuth(handler func(http.ResponseWriter, *http.Request)) func(http.Re
|
|||
w.WriteHeader(http.StatusFound)
|
||||
return
|
||||
} else if r < 0 {
|
||||
log.Debug("Auth: invalid cookie value: %s", cookie)
|
||||
log.Info("Auth: invalid cookie value: %s", cookie)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -382,7 +388,7 @@ func optionalAuth(handler func(http.ResponseWriter, *http.Request)) func(http.Re
|
|||
if r == 0 {
|
||||
ok = true
|
||||
} else if r < 0 {
|
||||
log.Debug("Auth: invalid cookie value: %s", cookie)
|
||||
log.Info("Auth: invalid cookie value: %s", cookie)
|
||||
}
|
||||
} else {
|
||||
// there's no Cookie, check Basic authentication
|
||||
|
@ -391,6 +397,8 @@ func optionalAuth(handler func(http.ResponseWriter, *http.Request)) func(http.Re
|
|||
u := config.auth.UserFind(user, pass)
|
||||
if len(u.Name) != 0 {
|
||||
ok = true
|
||||
} else {
|
||||
log.Info("Auth: invalid Basic Authorization value")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ func TestAuth(t *testing.T) {
|
|||
users := []User{
|
||||
User{Name: "name", PasswordHash: "$2y$05$..vyzAECIhJPfaQiOK17IukcQnqEgKJHy0iETyYqxn3YXJl8yZuo2"},
|
||||
}
|
||||
a := InitAuth(fn, nil)
|
||||
a := InitAuth(fn, nil, 60)
|
||||
s := session{}
|
||||
|
||||
user := User{Name: "name"}
|
||||
|
@ -54,7 +54,7 @@ func TestAuth(t *testing.T) {
|
|||
a.Close()
|
||||
|
||||
// load saved session
|
||||
a = InitAuth(fn, users)
|
||||
a = InitAuth(fn, users, 60)
|
||||
|
||||
// the session is still alive
|
||||
assert.True(t, a.CheckSession(sessStr) == 0)
|
||||
|
@ -69,7 +69,7 @@ func TestAuth(t *testing.T) {
|
|||
time.Sleep(3 * time.Second)
|
||||
|
||||
// load and remove expired sessions
|
||||
a = InitAuth(fn, users)
|
||||
a = InitAuth(fn, users, 60)
|
||||
assert.True(t, a.CheckSession(sessStr) == -1)
|
||||
|
||||
a.Close()
|
||||
|
@ -100,7 +100,7 @@ func TestAuthHTTP(t *testing.T) {
|
|||
users := []User{
|
||||
User{Name: "name", PasswordHash: "$2y$05$..vyzAECIhJPfaQiOK17IukcQnqEgKJHy0iETyYqxn3YXJl8yZuo2"},
|
||||
}
|
||||
config.auth = InitAuth(fn, users)
|
||||
config.auth = InitAuth(fn, users, 60)
|
||||
|
||||
handlerCalled := false
|
||||
handler := func(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -129,7 +129,7 @@ func TestAuthHTTP(t *testing.T) {
|
|||
assert.True(t, handlerCalled)
|
||||
|
||||
// perform login
|
||||
cookie := httpCookie(loginJSON{Name: "name", Password: "password"})
|
||||
cookie := config.auth.httpCookie(loginJSON{Name: "name", Password: "password"})
|
||||
assert.True(t, cookie != "")
|
||||
|
||||
// get /
|
||||
|
|
|
@ -91,6 +91,10 @@ type configuration struct {
|
|||
Language string `yaml:"language"` // two-letter ISO 639-1 language code
|
||||
RlimitNoFile uint `yaml:"rlimit_nofile"` // Maximum number of opened fd's per process (0: default)
|
||||
|
||||
// TTL for a web session (in hours)
|
||||
// An active session is automatically refreshed once a day.
|
||||
WebSessionTTLHours uint32 `yaml:"web_session_ttl"`
|
||||
|
||||
DNS dnsConfig `yaml:"dns"`
|
||||
TLS tlsConfig `yaml:"tls"`
|
||||
Filters []filter `yaml:"filters"`
|
||||
|
@ -210,6 +214,8 @@ func initConfig() {
|
|||
Transport: config.transport,
|
||||
}
|
||||
|
||||
config.WebSessionTTLHours = 30 * 24
|
||||
|
||||
if runtime.GOARCH == "mips" || runtime.GOARCH == "mipsle" {
|
||||
// Use plain DNS on MIPS, encryption is too slow
|
||||
defaultDNS = []string{"1.1.1.1", "1.0.0.1"}
|
||||
|
|
|
@ -68,7 +68,7 @@ func initDNSServer() {
|
|||
config.dnsServer = dnsforward.NewServer(config.dnsFilter, config.stats, config.queryLog)
|
||||
|
||||
sessFilename := filepath.Join(baseDir, "sessions.db")
|
||||
config.auth = InitAuth(sessFilename, config.Users)
|
||||
config.auth = InitAuth(sessFilename, config.Users, config.WebSessionTTLHours*60*60)
|
||||
config.Users = nil
|
||||
|
||||
config.dnsctx.rdns = InitRDNS(&config.clients)
|
||||
|
|
Loading…
Reference in New Issue