Skip to content

Commit 97c3529

Browse files
committed
Merge remote-tracking branch 'origin/main'
# Conflicts: # correlation/go.mod # correlation/go.sum # installer/go.mod
2 parents 75f4a26 + f628b12 commit 97c3529

File tree

33 files changed

+618
-395
lines changed

33 files changed

+618
-395
lines changed

CHANGELOG.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
# UTMStack 10.5.20 Release Notes
22
## Bug Fixes
33
- Fixed the IP location component to accurately determine whether an IP address is public or private.
4+
- Fixed communication from/to agents using secure connections.
5+
- Fixed negative operator evaluation matching on wrong input value due to insufficient checking in correlation engine.
6+
- Reorganized GeoIP database and threat intelligence loading into more modular functions for improved maintainability and code readability. Simplified caching, removed unused database function, and restructured rule-handling logic. Addressed minor variable renames and logging adjustments for consistency.
7+
- Removed unused docker volume configuration for GeoIp.
8+
- Fixed Kernel modules wheren't loaded because incorrect function call
49

510
## New Features
611
- Introduced new standards, sections, dashboards, and visualizations to compliance reports.
7-
- Update ip address to agent
8-
- Alert generation for down data sources
12+
- Update ip address to agent.
13+
- Alert generation for down data sources.

backend/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@
326326
<dependency>
327327
<groupId>com.utmstack.grpc.jclient</groupId>
328328
<artifactId>collector-client-4j</artifactId>
329-
<version>1.2.5</version>
329+
<version>2.0.1</version>
330330
</dependency>
331331

332332
<!-- WebSocket dependency -->

backend/src/main/java/com/park/utmstack/config/GrpcConfiguration.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@
22

33
import com.park.utmstack.security.GrpcInterceptor;
44
import io.grpc.ManagedChannel;
5-
import io.grpc.ManagedChannelBuilder;
5+
import io.grpc.netty.GrpcSslContexts;
6+
import io.grpc.netty.NettyChannelBuilder;
7+
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
68
import org.springframework.beans.factory.annotation.Value;
79
import org.springframework.context.annotation.Bean;
810
import org.springframework.context.annotation.Configuration;
911

1012
import javax.annotation.PreDestroy;
13+
import javax.net.ssl.SSLException;
1114

1215
@Configuration
1316
public class GrpcConfiguration {
@@ -20,11 +23,10 @@ public class GrpcConfiguration {
2023
private Integer serverPort;
2124

2225
@Bean
23-
public ManagedChannel managedChannel() {
24-
this.channel = ManagedChannelBuilder.forAddress(serverAddress, serverPort)
26+
public ManagedChannel managedChannel() throws SSLException {
27+
this.channel = NettyChannelBuilder.forAddress(serverAddress, serverPort)
2528
.intercept(new GrpcInterceptor())
26-
.usePlaintext()
27-
.enableRetry()
29+
.sslContext(GrpcSslContexts.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build())
2830
.build();
2931
return this.channel;
3032
}

correlation/Dockerfile

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
FROM ubuntu:22.04
2-
RUN apt update
3-
RUN apt install -y ca-certificates git
1+
FROM ubuntu:24.04
2+
RUN apt-get update
3+
RUN apt-get install -y ca-certificates git wget
44
COPY correlation /app/
55
COPY docs/swagger.json /app/docs/
66
COPY docs/swagger.yaml /app/docs/
@@ -9,4 +9,13 @@ COPY run.sh /
99
RUN chmod +x /app/correlation
1010
RUN chmod +x /run.sh
1111
RUN update-ca-certificates
12+
RUN wget -O /app/asn-blocks-v4.csv https://cdn.utmstack.com/geoip/asn-blocks-v4.csv
13+
RUN wget -O /app/asn-blocks-v6.csv https://cdn.utmstack.com/geoip/asn-blocks-v6.csv
14+
RUN wget -O /app/blocks-v4.csv https://cdn.utmstack.com/geoip/blocks-v4.csv
15+
RUN wget -O /app/blocks-v6.csv https://cdn.utmstack.com/geoip/blocks-v6.csv
16+
RUN wget -O /app/locations-en.csv https://cdn.utmstack.com/geoip/locations-en.csv
17+
RUN wget -O /app/ip_blocklist.list https://intelligence.threatwinds.com/feeds/public/ip/cumulative.list
18+
RUN wget -O /app/domain_blocklist.list https://intelligence.threatwinds.com/feeds/public/domain/cumulative.list
19+
RUN wget -O /app/hostname_blocklist.list https://intelligence.threatwinds.com/feeds/public/hostname/cumulative.list
20+
1221
ENTRYPOINT [ "/run.sh" ]

correlation/api/newLogHandler.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package api
33
import (
44
"encoding/json"
55
"fmt"
6+
"github.com/utmstack/UTMStack/correlation/ti"
67
"io"
78
"log"
89
"net/http"
@@ -74,6 +75,7 @@ func NewLog(c *gin.Context) {
7475
}
7576

7677
cache.AddToCache(l)
78+
ti.Enqueue(l)
7779
search.AddToQueue(l)
7880
response["status"] = "queued"
7981
c.JSON(http.StatusOK, response)

correlation/cache/cache.go

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,15 @@ import (
1313

1414
const bufferSize int = 1000000
1515

16-
var cacheStorageMutex = &sync.RWMutex{}
16+
var storageMutex = &sync.RWMutex{}
1717

18-
var CacheStorage []string
18+
var storage []string
1919

2020
func Status() {
2121
for {
22-
log.Printf("Logs in cache: %v", len(CacheStorage))
23-
if len(CacheStorage) != 0 {
24-
est := gjson.Get(CacheStorage[0], "@timestamp").String()
22+
log.Printf("Logs in cache: %v", len(storage))
23+
if len(storage) != 0 {
24+
est := gjson.Get(storage[0], "@timestamp").String()
2525
log.Printf("Old document in cache: %s", est)
2626
}
2727
time.Sleep(60 * time.Second)
@@ -31,8 +31,8 @@ func Status() {
3131

3232
func Search(allOf []rules.AllOf, oneOf []rules.OneOf, seconds int64) []string {
3333
var elements []string
34-
cacheStorageMutex.RLock()
35-
defer cacheStorageMutex.RUnlock()
34+
storageMutex.RLock()
35+
defer storageMutex.RUnlock()
3636

3737
cToBreak := 0
3838
ait := time.Now().UTC().Unix() - func() int64 {
@@ -43,8 +43,8 @@ func Search(allOf []rules.AllOf, oneOf []rules.OneOf, seconds int64) []string {
4343
return seconds
4444
}
4545
}()
46-
for i := len(CacheStorage) - 1; i >= 0; i-- {
47-
est := gjson.Get(CacheStorage[i], "@timestamp").String()
46+
for i := len(storage) - 1; i >= 0; i-- {
47+
est := gjson.Get(storage[i], "@timestamp").String()
4848
eit, err := time.Parse(time.RFC3339Nano, est)
4949
if err != nil {
5050
log.Printf("Could not parse @timestamp: %v", err)
@@ -61,23 +61,23 @@ func Search(allOf []rules.AllOf, oneOf []rules.OneOf, seconds int64) []string {
6161
var allCatch bool
6262
var oneCatch bool
6363
for _, of := range oneOf {
64-
oneCatch = evalElement(CacheStorage[i], of.Field, of.Operator, of.Value)
64+
oneCatch = evalElement(storage[i], of.Field, of.Operator, of.Value)
6565
if oneCatch {
6666
break
6767
}
6868
}
6969
for _, af := range allOf {
70-
allCatch = evalElement(CacheStorage[i], af.Field, af.Operator, af.Value)
70+
allCatch = evalElement(storage[i], af.Field, af.Operator, af.Value)
7171
if !allCatch {
7272
break
7373
}
7474
}
7575
if (len(allOf) == 0 || allCatch) && (len(oneOf) == 0 || oneCatch) {
76-
elements = append(elements, CacheStorage[i])
76+
elements = append(elements, storage[i])
7777
}
7878
}
7979
}
80-
80+
8181
return elements
8282
}
8383

@@ -97,9 +97,9 @@ func ProcessQueue() {
9797
go func() {
9898
for {
9999
l := <-logs
100-
cacheStorageMutex.Lock()
101-
CacheStorage = append(CacheStorage, l)
102-
cacheStorageMutex.Unlock()
100+
storageMutex.Lock()
101+
storage = append(storage, l)
102+
storageMutex.Unlock()
103103
}
104104
}()
105105
}
@@ -109,11 +109,11 @@ func Clean() {
109109
for {
110110
var clean bool
111111

112-
if len(CacheStorage) > 1 {
112+
if len(storage) > 1 {
113113
if utils.AssignedMemory >= 80 {
114114
clean = true
115115
} else {
116-
old := gjson.Get(CacheStorage[0], "@timestamp").String()
116+
old := gjson.Get(storage[0], "@timestamp").String()
117117
oldTime, err := time.Parse(time.RFC3339Nano, old)
118118
if err != nil {
119119
log.Printf("Could not parse old log timestamp. Cleaning up")
@@ -129,9 +129,9 @@ func Clean() {
129129
}
130130

131131
if clean {
132-
cacheStorageMutex.Lock()
133-
CacheStorage = CacheStorage[1:]
134-
cacheStorageMutex.Unlock()
132+
storageMutex.Lock()
133+
storage = storage[1:]
134+
storageMutex.Unlock()
135135
} else {
136136
time.Sleep(5 * time.Second)
137137
}

correlation/cache/cache_test.go

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
1-
package cache_test
1+
package cache
22

33
import (
4-
"testing"
5-
6-
7-
"github.com/utmstack/UTMStack/correlation/cache"
84
"github.com/utmstack/UTMStack/correlation/rules"
5+
"testing"
96
)
107

118
func TestSearch(t *testing.T) {
@@ -16,7 +13,7 @@ func TestSearch(t *testing.T) {
1613
`{"@timestamp":"2022-01-01T00:00:03.000Z","field1":"value1","field2":"value2"}`,
1714
`{"@timestamp":"2022-01-01T00:00:04.000Z","field1":"value1","field2":"value2"}`,
1815
}
19-
cache.CacheStorage = cacheStorage
16+
storage = cacheStorage
2017
allOf := []rules.AllOf{
2118
{Field: "field1", Operator: "==", Value: "value1"},
2219
}
@@ -31,7 +28,7 @@ func TestSearch(t *testing.T) {
3128
`{"@timestamp":"2022-01-01T00:00:01.000Z","field1":"value1","field2":"value2"}`,
3229
`{"@timestamp":"2022-01-01T00:00:00.000Z","field1":"value1","field2":"value2"}`,
3330
}
34-
result := cache.Search(allOf, oneOf, int64(seconds))
31+
result := Search(allOf, oneOf, int64(seconds))
3532
if len(result) != len(expected) {
3633
t.Errorf("Expected %d elements, but got %d", len(expected), len(result))
3734
}
@@ -40,4 +37,4 @@ func TestSearch(t *testing.T) {
4037
t.Errorf("Expected %s, but got %s", expected[i], r)
4138
}
4239
}
43-
}
40+
}

correlation/cache/operators.go

Lines changed: 62 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package cache
22

33
import (
4+
"fmt"
45
"net"
56
"regexp"
67
"strconv"
@@ -9,17 +10,16 @@ import (
910
"github.com/tidwall/gjson"
1011
)
1112

12-
func inCIDR(addr, network string) bool {
13+
func inCIDR(addr, network string) (bool, error) {
1314
_, subnet, err := net.ParseCIDR(network)
14-
if err == nil {
15-
ip := net.ParseIP(addr)
16-
if ip != nil {
17-
if subnet.Contains(ip) {
18-
return true
19-
}
20-
}
15+
if err != nil {
16+
return false, fmt.Errorf("invalid CIDR")
2117
}
22-
return false
18+
ip := net.ParseIP(addr)
19+
if ip == nil {
20+
return false, fmt.Errorf("invalid IP address")
21+
}
22+
return subnet.Contains(ip), nil
2323
}
2424

2525
func equal(val1, val2 string) bool {
@@ -52,27 +52,26 @@ func endWith(str, suff string) bool {
5252
return strings.HasSuffix(str, suff)
5353
}
5454

55-
func expresion(exp, str string) bool {
55+
func expression(exp, str string) (bool, error) {
5656
re, err := regexp.Compile(exp)
57-
if err == nil {
58-
if re.MatchString(str) {
59-
return true
60-
}
57+
if err != nil {
58+
return false, err
6159
}
62-
return false
60+
return re.MatchString(str), nil
6361
}
6462

65-
func minThan(min, may string) bool {
66-
minN, err := strconv.ParseFloat(min, 64)
63+
func parseFloats(val1, val2 string) (float64, float64, error) {
64+
f1, err := strconv.ParseFloat(val1, 64)
6765
if err != nil {
68-
return false
66+
return 0, 0, err
6967
}
70-
mayN, err := strconv.ParseFloat(may, 64)
68+
69+
f2, err := strconv.ParseFloat(val2, 64)
7170
if err != nil {
72-
return false
71+
return 0, 0, err
7372
}
7473

75-
return minN < mayN
74+
return f1, f2, nil
7675
}
7776

7877
func compare(operator, val1, val2 string) bool {
@@ -104,31 +103,63 @@ func compare(operator, val1, val2 string) bool {
104103
case "not end with":
105104
return !endWith(val1, val2)
106105
case "regexp":
107-
return expresion(val2, val1)
106+
matched, err := expression(val2, val1)
107+
if err != nil {
108+
return false
109+
}
110+
return matched
108111
case "not regexp":
109-
return !expresion(val2, val1)
112+
matched, err := expression(val2, val1)
113+
if err != nil {
114+
return false
115+
}
116+
return !matched
110117
case "<":
111-
return minThan(val1, val2)
118+
f1, f2, err := parseFloats(val1, val2)
119+
if err != nil {
120+
return false
121+
}
122+
return f1 < f2
112123
case ">":
113-
return !minThan(val1, val2)
124+
f1, f2, err := parseFloats(val1, val2)
125+
if err != nil {
126+
return false
127+
}
128+
return f1 > f2
114129
case "<=":
115-
return equal(val1, val2) || minThan(val1, val2)
130+
f1, f2, err := parseFloats(val1, val2)
131+
if err != nil {
132+
return false
133+
}
134+
return f1 <= f2
116135
case ">=":
117-
return equal(val1, val2) || !minThan(val1, val2)
136+
f1, f2, err := parseFloats(val1, val2)
137+
if err != nil {
138+
return false
139+
}
140+
return f1 >= f2
118141
case "exist":
119142
return true
120143
case "in cidr":
121-
return inCIDR(val1, val2)
144+
matched, err := inCIDR(val1, val2)
145+
if err != nil {
146+
return false
147+
}
148+
return matched
122149
case "not in cidr":
123-
return !inCIDR(val1, val2)
150+
matched, err := inCIDR(val1, val2)
151+
if err != nil {
152+
return false
153+
}
154+
return !matched
124155
default:
125156
return false
126157
}
127158
}
128159

129160
func evalElement(elem, field, operator, value string) bool {
130-
if gjson.Get(elem, field).Exists() {
131-
return compare(operator, gjson.Get(elem, field).String(), value)
161+
if elem := gjson.Get(elem, field); elem.Exists() {
162+
return compare(operator, elem.String(), value)
132163
} else if operator == "not exist" {
133164
return true
134165
}

0 commit comments

Comments
 (0)