authlogger.go 3.8 KB

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