Skip to content

Commit 42a19ad

Browse files
committed
feat(o365-plugin): add multi-cloud environment support for Microsoft Cloud (Commercial, GCC, GCC High, DoD)
1 parent f98cb0b commit 42a19ad

File tree

3 files changed

+82
-38
lines changed

3 files changed

+82
-38
lines changed

plugins/o365/go.mod

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ go 1.24.2
55
require (
66
github.com/google/uuid v1.6.0
77
github.com/threatwinds/go-sdk v1.0.43
8-
github.com/utmstack/config-client-go v1.2.7
8+
google.golang.org/grpc v1.74.2
9+
google.golang.org/protobuf v1.36.6
910
)
1011

1112
require (
@@ -45,8 +46,6 @@ require (
4546
golang.org/x/text v0.27.0 // indirect
4647
google.golang.org/genproto/googleapis/api v0.0.0-20250721164621-a45f3dfb1074 // indirect
4748
google.golang.org/genproto/googleapis/rpc v0.0.0-20250721164621-a45f3dfb1074 // indirect
48-
google.golang.org/grpc v1.74.2 // indirect
49-
google.golang.org/protobuf v1.36.6 // indirect
5049
gopkg.in/yaml.v3 v3.0.1 // indirect
5150
sigs.k8s.io/yaml v1.5.0 // indirect
5251
)

plugins/o365/go.sum

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,6 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS
9393
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
9494
github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
9595
github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
96-
github.com/utmstack/config-client-go v1.2.7 h1:JeRdI5JjH1liNzMW3LmyevjuPd67J/yt9MAO3+oJAuM=
97-
github.com/utmstack/config-client-go v1.2.7/go.mod h1:kM0KoUizM9ZlcQp0qKviGTWn/+anT5Rfjx3zfZk79nM=
9896
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
9997
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
10098
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=

plugins/o365/main.go

Lines changed: 80 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -17,31 +17,56 @@ import (
1717
"github.com/utmstack/UTMStack/plugins/o365/config"
1818
)
1919

20+
type CloudEnvironment string
21+
2022
const (
21-
loginUrl = "https://login.microsoftonline.com/"
22-
GRANTTYPE = "client_credentials"
23-
SCOPE = "https://manage.office.com/.default"
24-
endPointLogin = "/oauth2/v2.0/token"
25-
endPointStartSubscription = "/activity/feed/subscriptions/start"
26-
endPointContent = "/activity/feed/subscriptions/content"
27-
BASEURL = "https://manage.office.com/api/v1.0/"
28-
DefaultTenant = "ce66672c-e36d-4761-a8c8-90058fee1a24"
23+
GRANTTYPE = "client_credentials"
24+
endPointLogin = "/oauth2/v2.0/token"
25+
endPointStartSubscription = "/activity/feed/subscriptions/start"
26+
endPointContent = "/activity/feed/subscriptions/content"
27+
DefaultTenant = "ce66672c-e36d-4761-a8c8-90058fee1a24"
28+
apiVersion = "api/v1.0/"
29+
CloudCommercial CloudEnvironment = "Commercial"
30+
CloudGCC CloudEnvironment = "GCC"
31+
CloudGCCHigh CloudEnvironment = "GCCHigh"
32+
CloudDoD CloudEnvironment = "DoD"
2933
)
3034

31-
func GetMicrosoftLoginLink(tenant string) string {
32-
return fmt.Sprintf("%s%s%s", loginUrl, tenant, endPointLogin)
33-
}
34-
35-
func GetStartSubscriptionLink(tenant string) string {
36-
return fmt.Sprintf("%s%s%s", BASEURL, tenant, endPointStartSubscription)
35+
type CloudConfig struct {
36+
LoginAuthority string
37+
ManagementEndpoint string
38+
Scope string
3739
}
3840

39-
func GetContentLink(tenant string) string {
40-
return fmt.Sprintf("%s%s%s", BASEURL, tenant, endPointContent)
41-
}
41+
func GetCloudConfig(env CloudEnvironment) CloudConfig {
42+
configs := map[CloudEnvironment]CloudConfig{
43+
CloudCommercial: {
44+
LoginAuthority: "https://login.microsoftonline.com/",
45+
ManagementEndpoint: "https://manage.office.com/",
46+
Scope: "https://manage.office.com/.default",
47+
},
48+
CloudGCC: {
49+
LoginAuthority: "https://login.microsoftonline.com/",
50+
ManagementEndpoint: "https://manage-gcc.office.com/",
51+
Scope: "https://manage-gcc.office.com/.default",
52+
},
53+
CloudGCCHigh: {
54+
LoginAuthority: "https://login.microsoftonline.us/",
55+
ManagementEndpoint: "https://manage.office365.us/",
56+
Scope: "https://manage.office365.us/.default",
57+
},
58+
CloudDoD: {
59+
LoginAuthority: "https://login.microsoftonline.us/",
60+
ManagementEndpoint: "https://manage.protection.apps.mil/",
61+
Scope: "https://manage.protection.apps.mil/.default",
62+
},
63+
}
4264

43-
func GetTenantId() string {
44-
return DefaultTenant
65+
config, exists := configs[env]
66+
if !exists {
67+
return configs[CloudCommercial]
68+
}
69+
return config
4570
}
4671

4772
func main() {
@@ -65,7 +90,8 @@ func main() {
6590
for range ticker.C {
6691
endTime := time.Now().UTC()
6792

68-
if err := ConnectionChecker(loginUrl); err != nil {
93+
defaultConfig := GetCloudConfig(CloudCommercial)
94+
if err := ConnectionChecker(defaultConfig.LoginAuthority); err != nil {
6995
_ = catcher.Error("External connection failure detected: %v", err, nil)
7096
}
7197

@@ -117,7 +143,7 @@ func pull(startTime time.Time, endTime time.Time, group *config.ModuleGroup) {
117143
for _, log := range logs {
118144
plugins.EnqueueLog(&plugins.Log{
119145
Id: uuid.New().String(),
120-
TenantId: GetTenantId(),
146+
TenantId: agent.TenantId,
121147
DataType: "o365",
122148
DataSource: group.GroupName,
123149
Timestamp: time.Now().UTC().Format(time.RFC3339Nano),
@@ -127,11 +153,13 @@ func pull(startTime time.Time, endTime time.Time, group *config.ModuleGroup) {
127153
}
128154

129155
type OfficeProcessor struct {
130-
Credentials MicrosoftLoginResponse
131-
TenantId string
132-
ClientId string
133-
ClientSecret string
134-
Subscriptions []string
156+
Credentials MicrosoftLoginResponse
157+
TenantId string
158+
ClientId string
159+
ClientSecret string
160+
Subscriptions []string
161+
CloudEnvironment CloudEnvironment
162+
CloudConfig CloudConfig
135163
}
136164

137165
type MicrosoftLoginResponse struct {
@@ -162,7 +190,10 @@ type ContentList struct {
162190
type ContentDetailsResponse []map[string]any
163191

164192
func GetOfficeProcessor(group *config.ModuleGroup) OfficeProcessor {
165-
offProc := OfficeProcessor{}
193+
offProc := OfficeProcessor{
194+
CloudEnvironment: CloudCommercial,
195+
}
196+
166197
for _, cnf := range group.ModuleGroupConfigurations {
167198
switch cnf.ConfKey {
168199
case "office365_client_id":
@@ -171,27 +202,34 @@ func GetOfficeProcessor(group *config.ModuleGroup) OfficeProcessor {
171202
offProc.ClientSecret = cnf.ConfValue
172203
case "office365_tenant_id":
173204
offProc.TenantId = cnf.ConfValue
205+
case "office365_cloud_environment":
206+
if cnf.ConfValue != "" {
207+
offProc.CloudEnvironment = CloudEnvironment(cnf.ConfValue)
208+
}
174209
}
175210
}
176211

177-
offProc.Subscriptions = append(offProc.Subscriptions, []string{
212+
offProc.CloudConfig = GetCloudConfig(offProc.CloudEnvironment)
213+
214+
offProc.Subscriptions = []string{
178215
"Audit.AzureActiveDirectory",
179216
"Audit.Exchange",
180217
"Audit.General",
181218
"DLP.All",
182-
"Audit.SharePoint"}...)
219+
"Audit.SharePoint",
220+
}
183221

184222
return offProc
185223
}
186224

187225
func (o *OfficeProcessor) GetAuth() error {
188-
requestUrl := GetMicrosoftLoginLink(o.TenantId)
226+
requestUrl := fmt.Sprintf("%s%s%s", o.CloudConfig.LoginAuthority, o.TenantId, endPointLogin)
189227

190228
data := url.Values{}
191229
data.Set("grant_type", GRANTTYPE)
192230
data.Set("client_id", o.ClientId)
193231
data.Set("client_secret", o.ClientSecret)
194-
data.Set("scope", SCOPE)
232+
data.Set("scope", o.CloudConfig.Scope)
195233

196234
headers := map[string]string{
197235
"Content-Type": "application/x-www-form-urlencoded",
@@ -230,7 +268,12 @@ func (o *OfficeProcessor) GetAuth() error {
230268

231269
func (o *OfficeProcessor) StartSubscriptions() error {
232270
for _, subscription := range o.Subscriptions {
233-
link := GetStartSubscriptionLink(o.TenantId) + "?contentType=" + subscription
271+
link := fmt.Sprintf("%s%s%s%s?contentType=%s",
272+
o.CloudConfig.ManagementEndpoint,
273+
apiVersion,
274+
o.TenantId,
275+
endPointStartSubscription,
276+
subscription)
234277
headers := map[string]string{
235278
"Content-Type": "application/json",
236279
"Authorization": fmt.Sprintf("%s %s", o.Credentials.TokenType, o.Credentials.AccessToken),
@@ -277,7 +320,11 @@ func (o *OfficeProcessor) StartSubscriptions() error {
277320
}
278321

279322
func (o *OfficeProcessor) GetContentList(subscription string, startTime time.Time, endTime time.Time) ([]ContentList, error) {
280-
link := GetContentLink(o.TenantId) + fmt.Sprintf("?startTime=%s&endTime=%s&contentType=%s",
323+
link := fmt.Sprintf("%s%s%s%s?startTime=%s&endTime=%s&contentType=%s",
324+
o.CloudConfig.ManagementEndpoint,
325+
apiVersion,
326+
o.TenantId,
327+
endPointContent,
281328
startTime.UTC().Format("2006-01-02T15:04:05"),
282329
endTime.UTC().Format("2006-01-02T15:04:05"),
283330
subscription)

0 commit comments

Comments
 (0)