authlogger.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. package authlogger
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "log"
  6. "net/http"
  7. "os"
  8. "strconv"
  9. "strings"
  10. "time"
  11. "imuslab.com/arozos/mod/database"
  12. )
  13. /*
  14. AuthLogger
  15. Author: tobychui
  16. This module help log the login request and help the user to trace who is trying
  17. to break into their system.
  18. */
  19. type Logger struct {
  20. database *database.Database
  21. }
  22. type LoginRecord struct {
  23. Timestamp int64
  24. TargetUsername string
  25. LoginSucceed bool
  26. IpAddr string
  27. AuthType string
  28. Port int
  29. }
  30. //New Logger create a new logger object
  31. func NewLogger() (*Logger, error) {
  32. os.MkdirAll("./system/auth/", 0775)
  33. db, err := database.NewDatabase("./system/auth/authlog.db", false)
  34. if err != nil {
  35. return nil, errors.New("*ERROR* Failed to create database for login tracking: " + err.Error())
  36. }
  37. return &Logger{
  38. database: db,
  39. }, nil
  40. }
  41. //Log the current authentication to record, Require the request object and login status
  42. func (l *Logger) LogAuth(r *http.Request, loginStatus bool) error {
  43. username, _ := mv(r, "username", true)
  44. timestamp := time.Now().Unix()
  45. //handling the reverse proxy remote IP issue
  46. remoteIP := r.Header.Get("X-FORWARDED-FOR")
  47. if remoteIP != "" {
  48. //grab the last known remote IP from header
  49. remoteIPs := strings.Split(remoteIP, ", ")
  50. remoteIP = remoteIPs[len(remoteIPs)-1]
  51. } else {
  52. //if there is no X-FORWARDED-FOR, use default remote IP
  53. remoteIP = r.RemoteAddr
  54. }
  55. return l.LogAuthByRequestInfo(username, remoteIP, timestamp, loginStatus, "web")
  56. }
  57. //Log the current authentication to record by custom filled information. Use LogAuth if your module is authenticating via web interface
  58. func (l *Logger) LogAuthByRequestInfo(username string, remoteAddr string, timestamp int64, loginSucceed bool, authType string) error {
  59. //Get the current month as the table name, create table if not exists
  60. current := time.Now().UTC()
  61. tableName := current.Format("Jan-2006")
  62. //Create table if not exists
  63. if !l.database.TableExists(tableName) {
  64. l.database.NewTable(tableName)
  65. }
  66. //Split the remote address into ipaddr and port
  67. remoteAddrInfo := []string{"unknown", "N/A"}
  68. if strings.Contains(remoteAddr, ":") {
  69. //For general IPv4 address
  70. remoteAddrInfo = strings.Split(remoteAddr, ":")
  71. }
  72. //Check for IPV6
  73. if strings.Contains(remoteAddr, "[") && strings.Contains(remoteAddr, "]") {
  74. //This is an IPV6 address. Rewrite the split
  75. //IPv6 should have the format of something like this [::1]:80
  76. ipv6info := strings.Split(remoteAddr, ":")
  77. port := ipv6info[len(ipv6info)-1:]
  78. ipAddr := ipv6info[:len(ipv6info)-1]
  79. remoteAddrInfo = []string{strings.Join(ipAddr, ":"), strings.Join(port, ":")}
  80. }
  81. port := -1
  82. if len(remoteAddrInfo) > 1 {
  83. port, _ = strconv.Atoi(remoteAddrInfo[1])
  84. }
  85. //Create the entry log struct
  86. thisRecord := LoginRecord{
  87. Timestamp: timestamp,
  88. TargetUsername: username,
  89. LoginSucceed: loginSucceed,
  90. IpAddr: remoteAddrInfo[0],
  91. AuthType: authType,
  92. Port: port,
  93. }
  94. //Write the log to it
  95. entryKey := strconv.Itoa(int(time.Now().UnixNano()))
  96. err := l.database.Write(tableName, entryKey, thisRecord)
  97. if err != nil {
  98. log.Println("*ERROR* Failed to write authentication log. Is the storage fulled?")
  99. log.Println(err.Error())
  100. return err
  101. }
  102. return nil
  103. }
  104. //Close the database when system shutdown
  105. func (l *Logger) Close() {
  106. l.database.Close()
  107. }
  108. //List the total number of months recorded in the database
  109. func (l *Logger) ListSummary() []string {
  110. tableNames := []string{}
  111. l.database.Tables.Range(func(tableName, _ interface{}) bool {
  112. tableNames = append(tableNames, tableName.(string))
  113. return true
  114. })
  115. return tableNames
  116. }
  117. func (l *Logger) ListRecords(key string) ([]LoginRecord, error) {
  118. results := []LoginRecord{}
  119. if l.database.TableExists(key) {
  120. //Read all record from the database to login records
  121. entries, err := l.database.ListTable(key)
  122. if err != nil {
  123. return results, err
  124. }
  125. for _, keypairs := range entries {
  126. record := LoginRecord{}
  127. json.Unmarshal(keypairs[1], &record)
  128. results = append(results, record)
  129. }
  130. return results, nil
  131. } else {
  132. return results, errors.New("Table not exists")
  133. }
  134. }
  135. //Extract the address information from the request object, the first one is the remote address from the last hop,
  136. //and the 2nd one is the source address filled in by the client (not accurate)
  137. func getIpAddressFromRequest(r *http.Request) (string, []string) {
  138. lastHopAddress := r.RemoteAddr
  139. possibleSourceAddress := []string{}
  140. rawHeader := r.Header.Get("X-Forwarded-For")
  141. possibleSourceAddress = strings.Split(rawHeader, ", ")
  142. return lastHopAddress, possibleSourceAddress
  143. }