Skip to content

Commit 39ff859

Browse files
committed
feat(modules-config): add logging and decryption support
1 parent f0e6109 commit 39ff859

5 files changed

Lines changed: 293 additions & 2 deletions

File tree

plugins/modules-config/config/config.go

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ type failedModule struct {
3030
lastError string
3131
}
3232

33+
type Decrypter func(*ConfigurationSection) error
34+
3335
type ConfigServer struct {
3436
UnimplementedConfigServiceServer
3537

@@ -38,6 +40,7 @@ type ConfigServer struct {
3840
cache map[PluginType]*ConfigurationSection
3941
failedModules map[PluginType]*failedModule
4042
failedMu sync.RWMutex
43+
decrypt Decrypter
4144
}
4245

4346
func GetConfigServer() *ConfigServer {
@@ -51,6 +54,17 @@ func GetConfigServer() *ConfigServer {
5154
return configServer
5255
}
5356

57+
func (s *ConfigServer) SetDecrypter(d Decrypter) {
58+
s.decrypt = d
59+
}
60+
61+
func (s *ConfigServer) runDecrypter(section *ConfigurationSection) error {
62+
if s.decrypt == nil {
63+
return nil
64+
}
65+
return s.decrypt(section)
66+
}
67+
5468
func (s *ConfigServer) GetModuleGroup(moduleName PluginType) *ConfigurationSection {
5569
s.mu.RLock()
5670
defer s.mu.RUnlock()
@@ -146,7 +160,15 @@ func (s *ConfigServer) NotifyUpdate(moduleName string, section *ConfigurationSec
146160
}
147161

148162
func (s *ConfigServer) fetchModuleConfig(backend, moduleName, internalKey string) (*ConfigurationSection, int, error) {
149-
url := fmt.Sprintf("%s/api/utm-modules/module-details-decrypted?nameShort=%s&serverId=1", backend, moduleName)
163+
url := fmt.Sprintf("%s/api/utm-modules/moduleDetails?nameShort=%s&serverId=1", backend, moduleName)
164+
// Remove after testing, before release to production
165+
catcher.Info("fetchModuleConfig: requesting", map[string]any{
166+
"process": "plugin_com.utmstack.modules-config",
167+
"module": moduleName,
168+
"url": url,
169+
"internalKey": internalKey,
170+
"internalKeyLen": len(internalKey),
171+
})
150172

151173
response, status, err := utils.DoReq[ConfigurationSection](
152174
url,
@@ -155,10 +177,54 @@ func (s *ConfigServer) fetchModuleConfig(backend, moduleName, internalKey string
155177
map[string]string{"Utm-Internal-Key": internalKey},
156178
true,
157179
)
180+
// Remove after testing, before release to production
181+
catcher.Info("fetchModuleConfig: response received", map[string]any{
182+
"process": "plugin_com.utmstack.modules-config",
183+
"module": moduleName,
184+
"status": status,
185+
"err": fmt.Sprintf("%v", err),
186+
"groupCount": len(response.ModuleGroups),
187+
"moduleName": response.ModuleName,
188+
})
158189

159190
if err != nil || status != http.StatusOK {
160191
return nil, status, err
161192
}
193+
// Remove after testing, before release to production
194+
for _, g := range response.ModuleGroups {
195+
for _, cnf := range g.ModuleGroupConfigurations {
196+
catcher.Info("fetchModuleConfig: incoming field (pre-decrypt)", map[string]any{
197+
"process": "plugin_com.utmstack.modules-config",
198+
"module": moduleName,
199+
"groupId": g.Id,
200+
"confKey": cnf.ConfKey,
201+
"confDataType": cnf.ConfDataType,
202+
"valueLen": len(cnf.ConfValue),
203+
"confValue": cnf.ConfValue,
204+
})
205+
}
206+
}
207+
208+
if err := s.runDecrypter(&response); err != nil {
209+
return nil, status, catcher.Error("failed to decrypt module config", err, map[string]any{
210+
"process": "plugin_com.utmstack.modules-config",
211+
"module": moduleName,
212+
})
213+
}
214+
// Remove after testing, before release to production
215+
for _, g := range response.ModuleGroups {
216+
for _, cnf := range g.ModuleGroupConfigurations {
217+
catcher.Info("fetchModuleConfig: field (post-decrypt)", map[string]any{
218+
"process": "plugin_com.utmstack.modules-config",
219+
"module": moduleName,
220+
"groupId": g.Id,
221+
"confKey": cnf.ConfKey,
222+
"confDataType": cnf.ConfDataType,
223+
"valueLen": len(cnf.ConfValue),
224+
"confValue": cnf.ConfValue,
225+
})
226+
}
227+
}
162228

163229
return &response, status, nil
164230
}
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package crypto
2+
3+
import (
4+
"fmt"
5+
"runtime/debug"
6+
"strings"
7+
8+
"github.com/AtlasInsideCorp/AtlasInsideAES"
9+
"github.com/threatwinds/go-sdk/catcher"
10+
"github.com/utmstack/UTMStack/plugins/modules-config/config"
11+
)
12+
13+
const (
14+
confTypePassword = "password"
15+
confTypeFile = "file"
16+
moduleGCP = "GCP"
17+
)
18+
19+
func DecryptConfigurationSection(section *config.ConfigurationSection, key string) error {
20+
if section == nil {
21+
return nil
22+
}
23+
24+
for _, group := range section.ModuleGroups {
25+
decryptGroupConfigurations(section.ModuleName, group, key)
26+
}
27+
28+
return nil
29+
}
30+
31+
func DecryptModuleGroup(moduleName string, group *config.ModuleGroup, key string) error {
32+
if group == nil {
33+
return nil
34+
}
35+
36+
decryptGroupConfigurations(moduleName, group, key)
37+
return nil
38+
}
39+
40+
func decryptGroupConfigurations(moduleName string, group *config.ModuleGroup, key string) {
41+
if group == nil {
42+
return
43+
}
44+
// Remove after testing, before release to production
45+
for _, cnf := range group.ModuleGroupConfigurations {
46+
catcher.Info("crypto: evaluating field", map[string]any{
47+
"process": "plugin_com.utmstack.modules-config",
48+
"module": moduleName,
49+
"groupId": group.Id,
50+
"confKey": cnf.ConfKey,
51+
"confDataType": cnf.ConfDataType,
52+
"valueLen": len(cnf.ConfValue),
53+
"confValue": cnf.ConfValue,
54+
"key": key,
55+
"keyLen": len(key),
56+
})
57+
// Remove after testing, before release to production
58+
if !shouldDecrypt(moduleName, cnf.ConfDataType, cnf.ConfValue) {
59+
catcher.Info("crypto: skipped (shouldDecrypt=false)", map[string]any{
60+
"process": "plugin_com.utmstack.modules-config",
61+
"module": moduleName,
62+
"confKey": cnf.ConfKey,
63+
"confDataType": cnf.ConfDataType,
64+
})
65+
continue
66+
}
67+
// Remove after testing, before release to production
68+
plain, err := safeAESDecrypt(cnf.ConfValue, key)
69+
if err != nil {
70+
_ = catcher.Error("failed to decrypt configuration value", err, map[string]any{
71+
"process": "plugin_com.utmstack.modules-config",
72+
"module": moduleName,
73+
"groupId": group.Id,
74+
"confKey": cnf.ConfKey,
75+
"confDataType": cnf.ConfDataType,
76+
"valueLen": len(cnf.ConfValue),
77+
"confValue": cnf.ConfValue,
78+
"key": key,
79+
"keyLen": len(key),
80+
})
81+
continue
82+
}
83+
// Remove after testing, before release to production
84+
catcher.Info("crypto: decrypted OK", map[string]any{
85+
"process": "plugin_com.utmstack.modules-config",
86+
"module": moduleName,
87+
"groupId": group.Id,
88+
"confKey": cnf.ConfKey,
89+
"plainLen": len(plain),
90+
"plainHead": firstChars(plain, 64),
91+
})
92+
cnf.ConfValue = plain
93+
}
94+
}
95+
96+
func safeAESDecrypt(cipherText, key string) (plain string, err error) {
97+
defer func() {
98+
if r := recover(); r != nil {
99+
stack := string(debug.Stack())
100+
err = fmt.Errorf("decryption panic recovered: %v | stack: %s", r, stack)
101+
}
102+
}()
103+
return AtlasInsideAES.AESDecrypt(cipherText, []byte(key))
104+
}
105+
106+
func firstChars(s string, n int) string {
107+
if len(s) <= n {
108+
return s
109+
}
110+
return s[:n]
111+
}
112+
113+
func shouldDecrypt(moduleName, confDataType, confValue string) bool {
114+
if confValue == "" {
115+
return false
116+
}
117+
118+
dataType := strings.ToLower(strings.TrimSpace(confDataType))
119+
switch dataType {
120+
case confTypePassword:
121+
return true
122+
case confTypeFile:
123+
return strings.EqualFold(moduleName, moduleGCP)
124+
default:
125+
return false
126+
}
127+
}

plugins/modules-config/go.mod

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ go 1.25.5
44

55
require (
66
cloud.google.com/go/pubsub v1.50.1
7+
github.com/AtlasInsideCorp/AtlasInsideAES v1.0.0
78
github.com/Azure/azure-sdk-for-go/sdk/messaging/azeventhubs/v2 v2.0.1
89
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.4
910
github.com/aws/aws-sdk-go-v2/config v1.32.8
@@ -13,6 +14,7 @@ require (
1314
github.com/crowdstrike/gofalcon v0.19.0
1415
github.com/gin-gonic/gin v1.11.0
1516
github.com/threatwinds/go-sdk v1.1.15
17+
golang.org/x/sync v0.19.0
1618
google.golang.org/api v0.267.0
1719
google.golang.org/grpc v1.79.1
1820
google.golang.org/protobuf v1.36.11
@@ -118,7 +120,6 @@ require (
118120
golang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect
119121
golang.org/x/net v0.49.0 // indirect
120122
golang.org/x/oauth2 v0.35.0 // indirect
121-
golang.org/x/sync v0.19.0 // indirect
122123
golang.org/x/sys v0.40.0 // indirect
123124
golang.org/x/text v0.33.0 // indirect
124125
golang.org/x/time v0.14.0 // indirect

plugins/modules-config/go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ cloud.google.com/go/pubsub v1.50.1 h1:fzbXpPyJnSGvWXF1jabhQeXyxdbCIkXTpjXHy7xviB
1919
cloud.google.com/go/pubsub v1.50.1/go.mod h1:6YVJv3MzWJUVdvQXG081sFvS0dWQOdnV+oTo++q/xFk=
2020
cloud.google.com/go/pubsub/v2 v2.3.0 h1:DgAN907x+sP0nScYfBzneRiIhWoXcpCD8ZAut8WX9vs=
2121
cloud.google.com/go/pubsub/v2 v2.3.0/go.mod h1:O5f0KHG9zDheZAd3z5rlCRhxt2JQtB+t/IYLKK3Bpvw=
22+
github.com/AtlasInsideCorp/AtlasInsideAES v1.0.0 h1:TBiBl9KCa4i4epY0/q9WSC4ugavL6+6JUkOXWDnMM6I=
23+
github.com/AtlasInsideCorp/AtlasInsideAES v1.0.0/go.mod h1:cRhQ3TS/VEfu/z+qaciyuDZdtxgaXgaX8+G6Wa5NzBk=
2224
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0 h1:fou+2+WFTib47nS+nz/ozhEBnvU96bKHy6LjRsY4E28=
2325
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0/go.mod h1:t76Ruy8AHvUAC8GfMWJMa0ElSbuIcO03NLpynfbgsPA=
2426
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4=

0 commit comments

Comments
 (0)