diff --git a/.gitignore b/.gitignore index 1052456..ed0590f 100755 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,7 @@ *.dll *.so *.dylib - +*.patch *.exe *.x86_64 *.rpm diff --git a/dist/resource/cert b/dist/resource/cert index b92121a..0298f45 160000 --- a/dist/resource/cert +++ b/dist/resource/cert @@ -1 +1 @@ -Subproject commit b92121a0bfb9afc27ac284476032d71b2e51c253 +Subproject commit 0298f45e58bfc27e3d4c65823687ca01bd992d9a diff --git a/misc/learn/append.go b/misc/learn/append.go index dcb267d..a43cb89 100755 --- a/misc/learn/append.go +++ b/misc/learn/append.go @@ -38,5 +38,4 @@ func main() { fmt.Println(b, bb) //fmt.Println(cap(bb), len(bb)) //fmt.Println(cap(b), len(b)) - } diff --git a/misc/learn/cert.go b/misc/learn/cert.go new file mode 100644 index 0000000..e185383 --- /dev/null +++ b/misc/learn/cert.go @@ -0,0 +1,61 @@ +package main + +import ( + "bytes" + "crypto/x509" + "encoding/pem" + "fmt" + "io/ioutil" + "log" + "time" +) + +func CertificateText(cert *x509.Certificate) (string, error) { + var buf bytes.Buffer + buf.Grow(4096) // 4KiB should be enough + + buf.WriteString(fmt.Sprintf("Certificate:\n")) + buf.WriteString(fmt.Sprintf("%4sData:\n", "")) + buf.WriteString(fmt.Sprintf("%8sSerial Number: %d (%#x)\n", "", cert.SerialNumber, cert.SerialNumber)) + buf.WriteString(fmt.Sprintf("%4sSignature Algorithm: %s\n", "", cert.SignatureAlgorithm)) + + // Issuer information + buf.WriteString(fmt.Sprintf("%8sIssuer: ", "")) + // Validity information + buf.WriteString(fmt.Sprintf("%8sValidity\n", "")) + buf.WriteString(fmt.Sprintf("%12sNot Before: %s\n", "", cert.NotBefore.Format("Jan 2 15:04:05 2006 MST"))) + buf.WriteString(fmt.Sprintf("%12sNot After : %s\n", "", cert.NotAfter.Format("Jan 2 15:04:05 2006 MST"))) + + now := time.Now() + if now.Before(cert.NotBefore) { + buf.WriteString(fmt.Sprintf("current time %s is before %s\n", now.Format(time.RFC3339), cert.NotBefore.Format(time.RFC3339))) + + } else if now.After(cert.NotAfter) { + buf.WriteString(fmt.Sprintf("current time %s is after %s\n", now.Format(time.RFC3339), cert.NotAfter.Format(time.RFC3339))) + } + + return buf.String(), nil +} + +func main() { + // Read and parse the PEM certificate file + pemData, err := ioutil.ReadFile("cert.pem") + if err != nil { + log.Fatal(err) + } + block, rest := pem.Decode(pemData) + if block == nil || len(rest) > 0 { + log.Fatal("Certificate decoding error") + } + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + log.Fatal(err) + } + + // Print the certificate + result, err := CertificateText(cert) + if err != nil { + log.Fatal(err) + } + fmt.Print(result) +} diff --git a/pkg/access/worker.go b/pkg/access/worker.go index 6c023ac..eed1de2 100755 --- a/pkg/access/worker.go +++ b/pkg/access/worker.go @@ -116,7 +116,7 @@ func GetSocketClient(p *config.Point) libol.SocketClient { WrQus: p.Queue.SockWr, } if p.Cert != nil { - c.Cert = &libol.WebCert{ + c.Cert = &libol.CertConfig{ Insecure: p.Cert.Insecure, RootCa: p.Cert.CaFile, } diff --git a/pkg/app/access.go b/pkg/app/access.go index ab68f7d..8946ae3 100755 --- a/pkg/app/access.go +++ b/pkg/app/access.go @@ -65,7 +65,7 @@ func (p *Access) handleLogin(client libol.SocketClient, data []byte) error { } user.Update() out.Info("Access.handleLogin: %s on %s", user.Id(), user.Alias) - if now, _ := cache.User.Check(user); now != nil { + if now, err := cache.User.Check(user); now != nil { if now.Role != "admin" && now.Last != nil { // To offline lastly client if guest. p.master.OffClient(now.Last) @@ -76,10 +76,11 @@ func (p *Access) handleLogin(client libol.SocketClient, data []byte) error { out.Info("Access.handleLogin: success") _ = p.onAuth(client, user) return nil + } else { + p.failed++ + client.SetStatus(libol.ClUnAuth) + return err } - p.failed++ - client.SetStatus(libol.ClUnAuth) - return libol.NewErr("Auth failed.") } func (p *Access) onAuth(client libol.SocketClient, user *models.User) error { diff --git a/pkg/cache/user.go b/pkg/cache/user.go index b5b39fd..fcd31ec 100755 --- a/pkg/cache/user.go +++ b/pkg/cache/user.go @@ -2,8 +2,11 @@ package cache import ( "bufio" + "crypto/x509" + "encoding/pem" "github.com/luscis/openlan/pkg/libol" "github.com/luscis/openlan/pkg/models" + "io/ioutil" "strings" "sync" "time" @@ -12,6 +15,7 @@ import ( type user struct { Lock sync.RWMutex File string + Cert string Users *libol.SafeStrMap LdapCfg *libol.LDAPConfig LdapSvc *libol.LDAPService @@ -168,6 +172,26 @@ func (w *user) Timeout(user *models.User) bool { } func (w *user) Check(obj *models.User) (*models.User, error) { + if w.Cert != "" { + pemData, err := ioutil.ReadFile(w.Cert) + if err != nil { + return nil, err + } + block, rest := pem.Decode(pemData) + if block == nil || len(rest) > 0 { + return nil, libol.NewErr("certificate decoding error") + } + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return nil, err + } + now := time.Now() + if now.Before(cert.NotBefore) { + return nil, libol.NewErr("certificate isn't yet valid") + } else if now.After(cert.NotAfter) { + return nil, libol.NewErr("certificate has expired") + } + } if u := w.Get(obj.Id()); u != nil { if u.Role == "" || u.Role == "admin" || u.Role == "guest" { if u.Password == obj.Password { @@ -183,7 +207,7 @@ func (w *user) Check(obj *models.User) (*models.User, error) { if u := w.CheckLdap(obj); u != nil { return u, nil } - return nil, libol.NewErr("wrong user or password") + return nil, libol.NewErr("wrong password") } func (w *user) GetLdap() *libol.LDAPService { @@ -217,6 +241,12 @@ func (w *user) SetLdap(cfg *libol.LDAPConfig) { } } +func (w *user) SetCert(cfg *libol.CertConfig) { + w.Lock.Lock() + defer w.Lock.Unlock() + w.Cert = cfg.Crt +} + var User = user{ Users: libol.NewSafeStrMap(1024), } diff --git a/pkg/config/cert.go b/pkg/config/cert.go index 8888ff4..97dd365 100755 --- a/pkg/config/cert.go +++ b/pkg/config/cert.go @@ -34,7 +34,7 @@ type Cert struct { func (c *Cert) Correct() { if c.Dir == "" { - return + c.Dir = VarDir("cert") } if c.CrtFile == "" { c.CrtFile = fmt.Sprintf("%s/crt", c.Dir) diff --git a/pkg/config/switch.go b/pkg/config/switch.go index c95f225..ae03f1b 100755 --- a/pkg/config/switch.go +++ b/pkg/config/switch.go @@ -96,6 +96,7 @@ func DefaultSwitch() *Switch { Listen: "0.0.0.0:10000", }, Listen: "0.0.0.0:10002", + Cert: &Cert{}, } obj.Correct(nil) return obj @@ -142,6 +143,8 @@ func (s *Switch) Correct(obj *Switch) { s.File = filepath.Join(s.ConfDir, "switch.json") if s.Cert != nil { s.Cert.Correct() + } else { + s.Cert = obj.Cert } perf := &s.Perf perf.Correct(DefaultPerf()) diff --git a/pkg/libol/logger.go b/pkg/libol/logger.go index 31c7992..102d6eb 100755 --- a/pkg/libol/logger.go +++ b/pkg/libol/logger.go @@ -67,8 +67,9 @@ func (l *logger) Write(level int, format string, v ...interface{}) { func (l *logger) Save(level string, format string, v ...interface{}) { m := fmt.Sprintf(format, v...) + now := time.Now() if l.FileLog != nil { - l.FileLog.Println(level + "|" + m) + l.FileLog.Printf("%s %s|%s\n", now.Format(time.RFC3339), level, m) } l.Lock.Lock() defer l.Lock.Unlock() @@ -77,11 +78,9 @@ func (l *logger) Save(level string, format string, v ...interface{}) { l.Errors.Remove(e) } } - yy, mm, dd := time.Now().Date() - hh, mn, se := time.Now().Clock() ele := &Message{ Level: level, - Date: fmt.Sprintf("%d/%02d/%02d %02d:%02d:%02d", yy, mm, dd, hh, mn, se), + Date: now.Format(time.RFC3339), Message: m, } l.Errors.PushBack(ele) diff --git a/pkg/libol/websocket.go b/pkg/libol/websocket.go index b78fb59..f0706cf 100755 --- a/pkg/libol/websocket.go +++ b/pkg/libol/websocket.go @@ -27,7 +27,7 @@ func (ws *wsConn) RemoteAddr() net.Addr { return nil } -type WebCert struct { +type CertConfig struct { Key string Crt string RootCa string @@ -35,7 +35,7 @@ type WebCert struct { } type WebConfig struct { - Cert *WebCert + Cert *CertConfig Block kcp.BlockCrypt Timeout time.Duration // ns RdQus int // per frames diff --git a/pkg/switch/switch.go b/pkg/switch/switch.go index daa3b12..cf8c834 100755 --- a/pkg/switch/switch.go +++ b/pkg/switch/switch.go @@ -51,7 +51,7 @@ func GetSocketServer(s *co.Switch) libol.SocketServer { WrQus: s.Queue.SockWr, } if s.Cert != nil { - c.Cert = &libol.WebCert{ + c.Cert = &libol.CertConfig{ Crt: s.Cert.CrtFile, Key: s.Cert.KeyFile, } @@ -405,22 +405,6 @@ func (v *Switch) preAllow() { } } -func (v *Switch) SetLdap(ldap *co.LDAP) { - if ldap == nil || ldap.Server == "" { - return - } - cfg := libol.LDAPConfig{ - Server: ldap.Server, - BindUser: ldap.BindDN, - BindPass: ldap.BindPass, - BaseDN: ldap.BaseDN, - Attr: ldap.Attribute, - Filter: ldap.Filter, - EnableTls: ldap.EnableTls, - } - cache.User.SetLdap(&cfg) -} - func (v *Switch) SetPass(file string) { cache.User.SetFile(file) } @@ -451,7 +435,26 @@ func (v *Switch) Initialize() { // Load password for guest access v.SetPass(v.cfg.PassFile) v.LoadPass() - v.SetLdap(v.cfg.Ldap) + + ldap := v.cfg.Ldap + if ldap != nil { + cache.User.SetLdap(&libol.LDAPConfig{ + Server: ldap.Server, + BindUser: ldap.BindDN, + BindPass: ldap.BindPass, + BaseDN: ldap.BaseDN, + Attr: ldap.Attribute, + Filter: ldap.Filter, + EnableTls: ldap.EnableTls, + }) + } + // Enable cert verify for access + cert := v.cfg.Cert + if cert != nil { + cache.User.SetCert(&libol.CertConfig{ + Crt: cert.CrtFile, + }) + } // Start confd monitor v.confd.Initialize() }