authlogger.go 4.5 KB

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