authlogger.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  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. return l.LogAuthByRequestInfo(username, r.RemoteAddr, timestamp, loginStatus, "web")
  44. }
  45. //Log the current authentication to record by custom filled information. Use LogAuth if your module is authenticating via web interface
  46. func (l *Logger) LogAuthByRequestInfo(username string, remoteAddr string, timestamp int64, loginSuceed bool, authType string) error {
  47. //Get the current month as the table name, create table if not exists
  48. current := time.Now().UTC()
  49. tableName := current.Format("Jan-2006")
  50. //Create table if not exists
  51. if !l.database.TableExists(tableName) {
  52. l.database.NewTable(tableName)
  53. }
  54. //Split the remote address into ipaddr and port
  55. remoteAddrInfo := []string{"unknown", "N/A"}
  56. if strings.Contains(remoteAddr, ":") {
  57. //For general IPv4 address
  58. remoteAddrInfo = strings.Split(remoteAddr, ":")
  59. }
  60. //Check for IPV6
  61. if strings.Contains(remoteAddr, "[") && strings.Contains(remoteAddr, "]") {
  62. //This is an IPV6 address. Rewrite the split
  63. //IPv6 should have the format of something like this [::1]:80
  64. ipv6info := strings.Split(remoteAddr, ":")
  65. port := ipv6info[len(ipv6info)-1:]
  66. ipAddr := ipv6info[:len(ipv6info)-1]
  67. remoteAddrInfo = []string{strings.Join(ipAddr, ":"), strings.Join(port, ":")}
  68. }
  69. port := -1
  70. if len(remoteAddrInfo) > 1 {
  71. port, _ = strconv.Atoi(remoteAddrInfo[1])
  72. }
  73. //Create the entry log struct
  74. thisRecord := LoginRecord{
  75. Timestamp: timestamp,
  76. TargetUsername: username,
  77. LoginSucceed: loginSuceed,
  78. IpAddr: remoteAddrInfo[0],
  79. AuthType: authType,
  80. Port: port,
  81. }
  82. //Write the log to it
  83. entryKey := strconv.Itoa(int(time.Now().UnixNano()))
  84. err := l.database.Write(tableName, entryKey, thisRecord)
  85. if err != nil {
  86. log.Println("*ERROR* Failed to write authentication log. Is the storage fulled?")
  87. log.Println(err.Error())
  88. return err
  89. }
  90. return nil
  91. }
  92. //Close the database when system shutdown
  93. func (l *Logger) Close() {
  94. l.database.Close()
  95. }
  96. //List the total number of months recorded in the database
  97. func (l *Logger) ListSummary() []string {
  98. tableNames := []string{}
  99. l.database.Tables.Range(func(tableName, _ interface{}) bool {
  100. tableNames = append(tableNames, tableName.(string))
  101. return true
  102. })
  103. return tableNames
  104. }
  105. func (l *Logger) ListRecords(key string) ([]LoginRecord, error) {
  106. results := []LoginRecord{}
  107. if l.database.TableExists(key) {
  108. //Read all record from the database to login records
  109. entries, err := l.database.ListTable(key)
  110. if err != nil {
  111. return results, err
  112. }
  113. for _, keypairs := range entries {
  114. record := LoginRecord{}
  115. json.Unmarshal(keypairs[1], &record)
  116. results = append(results, record)
  117. }
  118. return results, nil
  119. } else {
  120. return results, errors.New("Table not exists")
  121. }
  122. }
  123. //Extract the address information from the request object, the first one is the remote address from the last hop,
  124. //and the 2nd one is the source address filled in by the client (not accurate)
  125. func getIpAddressFromRequest(r *http.Request) (string, []string) {
  126. lastHopAddress := r.RemoteAddr
  127. possibleSourceAddress := []string{}
  128. rawHeader := r.Header.Get("X-Forwarded-For")
  129. possibleSourceAddress = strings.Split(rawHeader, ", ")
  130. return lastHopAddress, possibleSourceAddress
  131. }