add digest auth support for proxy

master
Dingjun 8 years ago
parent 3cb3cdb8dd
commit 41ea1f20ca

@ -13,6 +13,9 @@ type server struct {
Docroot string
URLRules []rule
EnableProxy bool
EnableAuth bool
PasswdFile string
Realm string
Vhost []vhost
}

@ -14,7 +14,10 @@
# default document root
docroot: /srv/www
enableproxy: false
enableproxy: true
enableauth: true
passwdfile: ./passwdfile
realm: example.com
# default host's url rule
# urlrules:

@ -0,0 +1,90 @@
package main
import (
"bufio"
"os"
"strings"
"sync"
"time"
)
type digestPwFile struct {
path string
entry []pwEntry
mtime time.Time
mu *sync.Mutex
}
type pwEntry struct {
user string
realm string
hashPw string
}
func newDigestSecret(f string) (*digestPwFile, error) {
a := &digestPwFile{path: f, mu: new(sync.Mutex)}
if err := a.loadFile(); err != nil {
return nil, err
}
go a.tryReload()
return a, nil
}
func (df *digestPwFile) tryReload() {
for {
time.Sleep(10 * time.Second)
fi, _ := os.Stat(df.path)
t1 := fi.ModTime()
if t1 != df.mtime {
df.loadFile()
}
}
}
func (df *digestPwFile) loadFile() error {
df.mu.Lock()
defer df.mu.Unlock()
fp, err := os.Open(df.path)
if err != nil {
return err
}
defer fp.Close()
entry := []pwEntry{}
r := bufio.NewReader(fp)
for {
line, err := r.ReadString('\n')
if err != nil {
break
}
line1 := strings.Trim(line, " \r\n")
if line1 == "" || line1[0] == '#' {
continue
}
fields := strings.SplitN(line1, ":", 3)
entry = append(entry, pwEntry{fields[0], fields[1], fields[2]})
}
df.entry = entry
fi, _ := os.Stat(df.path)
df.mtime = fi.ModTime()
return nil
}
func (df *digestPwFile) getPw(user, realm string) string {
df.mu.Lock()
defer df.mu.Unlock()
for i := range df.entry {
if df.entry[i].user == user && df.entry[i].realm == realm {
return df.entry[i].hashPw
}
}
return ""
}

@ -2,6 +2,7 @@ package main
import (
"fmt"
auth "github.com/abbot/go-http-auth"
"io"
"log"
"net"
@ -15,6 +16,8 @@ import (
type handler struct {
handler http.Handler
enableProxy bool
enableAuth bool
authMethod *auth.DigestAuth
localDomains []string
}
@ -57,6 +60,14 @@ func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
if h.enableAuth {
u, _ := h.authMethod.CheckAuth(r)
if u == "" {
h.authMethod.RequireAuth(w, r)
return
}
}
if r.Method == http.MethodConnect {
// CONNECT request
h.handleCONNECT(w, r)

@ -0,0 +1,10 @@
# format
# user:realm:hashed_passwd
#
# hashed_passwd = MD5(user:realm:plain_passwd)
#
# user "test", realm "example.com", password "test"
# MD5("test:example:test") = 3441b753b98a6dc702183c989e35970f
# the entry is
test:example.com:3441b753b98a6dc702183c989e35970f

@ -3,6 +3,7 @@ package main
import (
"crypto/tls"
"fmt"
auth "github.com/abbot/go-http-auth"
"github.com/fangdingjun/gofast"
"github.com/gorilla/mux"
"log"
@ -82,8 +83,23 @@ func initRouters(cfg conf) {
hdlr := &handler{
handler: router,
enableProxy: l.EnableProxy,
enableAuth: l.EnableAuth,
localDomains: domains,
}
if l.EnableAuth {
if l.PasswdFile == "" {
log.Fatal("passwdfile required")
}
du, err := newDigestSecret(l.PasswdFile)
if err != nil {
log.Fatal(err)
}
digestAuth := auth.NewDigestAuthenticator(l.Realm, du.getPw)
digestAuth.Headers = auth.ProxyHeaders
hdlr.authMethod = digestAuth
}
if len(certs) > 0 {
tlsconfig := &tls.Config{
Certificates: certs,

Loading…
Cancel
Save