Skip to content

Commit 6837c23

Browse files
refs : #19042 - listUnusualIPs () function added
1 parent 21fc1b7 commit 6837c23

File tree

6 files changed

+172
-10
lines changed

6 files changed

+172
-10
lines changed

logharbour/clientFn.go

Lines changed: 63 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"github.com/elastic/go-elasticsearch/v8/typedapi/some"
1616
"github.com/elastic/go-elasticsearch/v8/typedapi/types"
1717
"github.com/elastic/go-elasticsearch/v8/typedapi/types/enums/sortorder"
18+
"github.com/oschwald/geoip2-golang"
1819
)
1920

2021
const (
@@ -37,6 +38,7 @@ const (
3738
ACTIVITY = "A"
3839
DEBUG = "D"
3940
field = "data.change_data.changes.field"
41+
EN = "en"
4042
)
4143

4244
var (
@@ -85,6 +87,13 @@ type GetSetParam struct {
8587
RemoteIP *string `json:"remoteIP" validate:"omitempty"`
8688
Pri *LogPriority `json:"pri" validate:"omitempty,oneof=1 2 3 4 5 6 7 8"`
8789
}
90+
type IPLocation struct {
91+
Ipaddress string `json:"ipaddr" validate:"required"`
92+
City string `json:"city"`
93+
Country string `json:"country"`
94+
Latitude float64 `json:"lat"`
95+
Longitude float64 `json:"long"`
96+
}
8897

8998
// GetLogs retrieves an slice of logEntry from Elasticsearch based on the fields provided in logParam.
9099
func GetLogs(querytoken string, client *elasticsearch.TypedClient, logParam GetLogsParam) ([]LogEntry, int, error) {
@@ -200,7 +209,6 @@ func GetLogs(querytoken string, client *elasticsearch.TypedClient, logParam GetL
200209
return nil, 0, fmt.Errorf("Error while searching document in es:%v", err)
201210
}
202211

203-
204212
// Unmarshalling hit.source into LogEntry
205213
if res != nil {
206214
for _, hit := range res.Hits.Hits {
@@ -242,14 +250,11 @@ func GetUnusualIP(queryToken string, client *elasticsearch.TypedClient, unusualP
242250

243251
localIp, err := GetLocalIPAddress()
244252
if err != nil {
245-
println("Error:", err)
246253
return nil, err
247254
}
248-
println("Local IP address:", localIp)
249255

250256
if percentThreshold > 1 {
251257
for ip, count := range aggregatedIPs {
252-
fmt.Printf("IP: %s, Count: %d\n", ip, count)
253258
if count <= int64(percentThreshold) {
254259
if ip != localIp {
255260
unusualIPs = append(unusualIPs, ip)
@@ -728,3 +733,57 @@ func GetChanges(querytoken string, client *elasticsearch.TypedClient, logParam G
728733
}
729734
return logEntries, int(res.Hits.Total.Value), nil
730735
}
736+
737+
// This function gives a list of unusual IPS with it's geographical location details
738+
func ListUnusualIPs(queryToken string, client *elasticsearch.TypedClient, geoLiteDb *geoip2.Reader, unusualPercent float64, logParam GetUnusualIPParam) ([]IPLocation, error) {
739+
unusualIPList := []IPLocation{}
740+
741+
// calling GetUnusualIP() to get all unsualIPS based on app and ndays
742+
unusualIPs, err := GetUnusualIP(queryToken, client, unusualPercent, GetUnusualIPParam{App: logParam.App, NDays: logParam.NDays})
743+
if err != nil {
744+
return nil, err
745+
}
746+
// getting IPLocation details for each IP address
747+
for _, ip := range unusualIPs {
748+
iplocation := getIPLocation(ip, geoLiteDb)
749+
unusualIPList = append(unusualIPList, iplocation)
750+
751+
}
752+
753+
return unusualIPList, nil
754+
755+
}
756+
757+
// This function is used to retrieves the geographical location details of a given IP address using a GeoLite database.
758+
func getIPLocation(ipAddress string, geoLiteDb *geoip2.Reader) IPLocation {
759+
760+
var (
761+
IPLocationDetails IPLocation
762+
IP net.IP
763+
)
764+
// parse IP address
765+
IP = net.ParseIP(ipAddress)
766+
767+
// searching city details based on IP address
768+
record, err := geoLiteDb.City(IP)
769+
if err != nil {
770+
IPLocationDetails = IPLocation{
771+
Ipaddress: "0.0.0.0",
772+
City: "",
773+
Country: "",
774+
Latitude: 0,
775+
Longitude: 0,
776+
}
777+
return IPLocationDetails
778+
}
779+
780+
IPLocationDetails = IPLocation{
781+
Ipaddress: ipAddress,
782+
City: record.City.Names[EN],
783+
Country: record.Country.Names[EN],
784+
Latitude: record.Location.Latitude,
785+
Longitude: record.Location.Longitude,
786+
}
787+
788+
return IPLocationDetails
789+
}

server/config_dev.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@
55
"db_user": "elastic",
66
"index_name": "logharbour",
77
"db_password": "9jjWQryjHca-9flzDcKU",
8-
"certificate_fingerprint": "3395adb7832f24e043ea7101b7f821bd97786fc808c335ba439a3681585119fc"
8+
"certificate_fingerprint": "3395adb7832f24e043ea7101b7f821bd97786fc808c335ba439a3681585119fc",
9+
"geolite_db_path" :"../logharbour/GeoLite2-City.mmdb"
910
}

server/main.go

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/elastic/elastic-transport-go/v8/elastictransport"
1010
"github.com/elastic/go-elasticsearch/v8"
1111
"github.com/gin-gonic/gin"
12+
"github.com/oschwald/geoip2-golang"
1213
"github.com/remiges-tech/alya/config"
1314
"github.com/remiges-tech/alya/service"
1415
"github.com/remiges-tech/alya/wscutils"
@@ -110,34 +111,51 @@ func main() {
110111
// show request query logger
111112
Logger: &elastictransport.TextLogger{Output: log.Writer(), EnableRequestBody: true},
112113
}
114+
//elasticsearch client
113115
client, err := elasticsearch.NewTypedClient(dbConfig)
114116
if err != nil {
115117
log.Fatalf("Failed to create db connection: %v", err)
116118
wscutils.NewErrorResponse(502, "error establishing a database connection")
117119
return
118120
}
119121

122+
// GeoLite2-City database
123+
geoLiteCityDb, err := geoip2.Open(appConfig.GeoLiteDbPath)
124+
if err != nil {
125+
log.Fatalf("Failed to create GeoLite2-City db connection: %v", err)
126+
wscutils.NewErrorResponse(502, "error establishing a GeoLite2-City database connection")
127+
return
128+
}
129+
defer geoLiteCityDb.Close()
130+
120131
// router
121132
r := gin.Default()
122133

123-
// r.Use(corsMiddleware())
134+
// r.Use(corsMiddleware())
135+
136+
apiV1Group := r.Group("/api/v1/")
124137

125138
// services
126139
s := service.NewService(r).
127140
WithLogHarbour(l).
128141
WithDependency("client", client).
129142
WithDependency("index", "logharbour")
130143

131-
apiV1Group := r.Group("/api/v1/")
132-
133144
s.RegisterRouteWithGroup(apiV1Group, http.MethodPost, "/highprilog", wsc.GetHighprilog)
134145
s.RegisterRouteWithGroup(apiV1Group, http.MethodPost, "/activitylog", wsc.ShowActivityLog)
135146
s.RegisterRouteWithGroup(apiV1Group, http.MethodPost, "/debuglog", wsc.GetDebugLog)
136147
s.RegisterRouteWithGroup(apiV1Group, http.MethodPost, "/datachange", wsc.ShowDataChange)
137-
s.RegisterRouteWithGroup(apiV1Group, http.MethodPost, "/unusalip", wsc.GetUnusualIP)
138148
s.RegisterRouteWithGroup(apiV1Group, http.MethodPost, "/getset", wsc.GetSet)
139149
s.RegisterRouteWithGroup(apiV1Group, http.MethodGet, "/getapps", wsc.GetApps)
140150

151+
// creating a seprate service for getting list of unusualIPS with geoLiteCityDb dependency
152+
unusualIPServ := service.NewService(r).
153+
WithLogHarbour(l).
154+
WithDependency("client", client).
155+
WithDependency("index", "logharbour").WithDependency("geoLiteCityDb", geoLiteCityDb)
156+
157+
unusualIPServ.RegisterRouteWithGroup(apiV1Group, http.MethodPost, "/getunusualips", wsc.GetUnusualIPs)
158+
141159
err = r.Run(":" + appConfig.AppServerPort)
142160
if err != nil {
143161
l.LogActivity("Failed to start server", err)

server/types/types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ type AppConfig struct {
1414
KeycloakURL string `json:"keycloak_url"`
1515
KeycloakClientID string `json:"keycloak_client_id"`
1616
CertificateFingerprint string `json:"certificate_fingerprint"`
17+
GeoLiteDbPath string `json:"geolite_db_path"`
1718
}
1819

1920

server/wsc/getunusualIPs.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package wsc
2+
3+
import (
4+
"github.com/elastic/go-elasticsearch/v8"
5+
"github.com/gin-gonic/gin"
6+
"github.com/go-playground/validator/v10"
7+
"github.com/oschwald/geoip2-golang"
8+
"github.com/remiges-tech/alya/service"
9+
"github.com/remiges-tech/alya/wscutils"
10+
"github.com/remiges-tech/logharbour/logharbour"
11+
)
12+
13+
//var unusualPercent = 10.0
14+
15+
type GetUnusualIpParam struct {
16+
App string `json:"app" validate:"required,alpha,lt=15"`
17+
Days int `json:"days" validate:"required,number,lt=500"`
18+
UnusualPercent float64 `json:"unusualPercent"`
19+
}
20+
type GetUnusualIPResponse struct {
21+
UnusualIPs []logharbour.IPLocation `json:"unusualIPs" validate:"required"`
22+
}
23+
24+
func GetUnusualIPs(c *gin.Context, s *service.Service) {
25+
l := s.LogHarbour
26+
l.Debug0().Log("starting execution of GetUnusualIPs()")
27+
28+
var (
29+
req GetUnusualIpParam
30+
unusualIPs []logharbour.IPLocation
31+
)
32+
33+
// binding json request
34+
err := wscutils.BindJSON(c, &req)
35+
if err != nil {
36+
l.Debug0().Error(err).Log("error unmarshalling request payload to struct")
37+
return
38+
}
39+
40+
// Validate request
41+
validationErrors := wscutils.WscValidate(req, func(err validator.FieldError) []string { return []string{} })
42+
if len(validationErrors) > 0 {
43+
l.Debug0().LogDebug("standard validation errors", validationErrors)
44+
wscutils.SendErrorResponse(c, wscutils.NewResponse(wscutils.ErrorStatus, nil, validationErrors))
45+
return
46+
}
47+
48+
es, ok := s.Dependencies["client"].(*elasticsearch.TypedClient)
49+
if !ok {
50+
wscutils.SendErrorResponse(c, wscutils.NewErrorResponse(100, "ErrCode_DatabaseError"))
51+
return
52+
}
53+
54+
geoLiteDb, ok := s.Dependencies["geoLiteCityDb"].(*geoip2.Reader)
55+
if !ok {
56+
wscutils.SendErrorResponse(c, wscutils.NewErrorResponse(100, "ErrCode_DatabaseError"))
57+
return
58+
}
59+
60+
unusualIPs, err = logharbour.ListUnusualIPs("", es, geoLiteDb, req.UnusualPercent, logharbour.GetUnusualIPParam{
61+
App: &req.App,
62+
NDays: &req.Days,
63+
})
64+
65+
if err != nil {
66+
l.Debug0().Error(err).Log("error in GetUnusualIPs")
67+
wscutils.SendErrorResponse(c, wscutils.NewErrorResponse(222, err.Error()))
68+
return
69+
}
70+
if len(unusualIPs) != 0 {
71+
72+
response := GetUnusualIPResponse{
73+
UnusualIPs: unusualIPs,
74+
}
75+
76+
l.Debug0().Log("finished execution of GetUnusualIPs()")
77+
wscutils.SendSuccessResponse(c, wscutils.NewSuccessResponse(response))
78+
return
79+
}
80+
l.Debug0().Log("starting execution of GetUnusualIPs()")
81+
wscutils.SendSuccessResponse(c, wscutils.NewSuccessResponse("No unusual IP"))
82+
83+
}

server/wsc/test/main_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ func registerRoutes(typedClient *es.TypedClient) (*gin.Engine, error) {
147147
s.RegisterRoute(http.MethodPost, "/highprilog", wsc.GetHighprilog)
148148
s.RegisterRoute(http.MethodPost, "/activitylog", wsc.ShowActivityLog)
149149
s.RegisterRoute(http.MethodPost, "/debuglog", wsc.GetDebugLog)
150-
s.RegisterRoute(http.MethodPost, "/getUnusualIP", wsc.GetUnusualIP)
150+
s.RegisterRoute(http.MethodPost, "/getUnusualIP", wsc.GetUnusualIPs)
151151
s.RegisterRoute(http.MethodPost, "/datachange", wsc.ShowDataChange)
152152
s.RegisterRoute(http.MethodGet, "/getapps", wsc.GetApps)
153153
s.RegisterRoute(http.MethodPost, "/getset", wsc.GetSet)

0 commit comments

Comments
 (0)