Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove ADCS feature flag gates #388

Merged
merged 5 commits into from
Feb 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 11 additions & 12 deletions cmd/api/src/api/registration/v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import (
"github.com/specterops/bloodhound/src/auth"
"github.com/specterops/bloodhound/src/config"
"github.com/specterops/bloodhound/src/database"
"github.com/specterops/bloodhound/src/model/appcfg"
)

func samlWriteAPIErrorResponse(request *http.Request, response http.ResponseWriter, statusCode int, message string) {
Expand Down Expand Up @@ -150,7 +149,7 @@ func NewV2API(cfg config.Configuration, resources v2.Resources, routerInst *rout

routerInst.GET("/api/v2/pathfinding", resources.GetPathfindingResult).Queries("start_node", "{start_node}", "end_node", "{end_node}").RequirePermissions(permissions.GraphDBRead),
routerInst.GET("/api/v2/graphs/shortest-path", resources.GetShortestPath).Queries(params.StartNode.String(), params.StartNode.RouteMatcher(), params.EndNode.String(), params.EndNode.RouteMatcher()).RequirePermissions(permissions.GraphDBRead),
routerInst.GET("/api/v2/graphs/edge-composition", resources.GetEdgeComposition).RequirePermissions(permissions.GraphDBRead).CheckFeatureFlag(resources.DB, appcfg.FeatureAdcs), // TODO: Cleanup #ADCSFeatureFlag after full launch.
routerInst.GET("/api/v2/graphs/edge-composition", resources.GetEdgeComposition).RequirePermissions(permissions.GraphDBRead),

// TODO discuss if this should be a post endpoint
routerInst.GET("/api/v2/graph-search", resources.GetSearchResult).RequirePermissions(permissions.GraphDBRead),
Expand Down Expand Up @@ -218,24 +217,24 @@ func NewV2API(cfg config.Configuration, resources v2.Resources, routerInst *rout
routerInst.GET(fmt.Sprintf("/api/v2/gpos/{%s}/ous", api.URIPathVariableObjectID), resources.ListADGPOAffectedContainers).RequirePermissions(permissions.GraphDBRead),

// AIACA Entity API
routerInst.GET(fmt.Sprintf("/api/v2/aiacas/{%s}", api.URIPathVariableObjectID), resources.GetAIACAEntityInfo).RequirePermissions(permissions.GraphDBRead).CheckFeatureFlag(resources.DB, appcfg.FeatureAdcs), // TODO: Cleanup #ADCSFeatureFlag after full launch.
routerInst.GET(fmt.Sprintf("/api/v2/aiacas/{%s}/controllers", api.URIPathVariableObjectID), resources.ListADEntityControllers).RequirePermissions(permissions.GraphDBRead).CheckFeatureFlag(resources.DB, appcfg.FeatureAdcs), // TODO: Cleanup #ADCSFeatureFlag after full launch.
routerInst.GET(fmt.Sprintf("/api/v2/aiacas/{%s}", api.URIPathVariableObjectID), resources.GetAIACAEntityInfo).RequirePermissions(permissions.GraphDBRead),
routerInst.GET(fmt.Sprintf("/api/v2/aiacas/{%s}/controllers", api.URIPathVariableObjectID), resources.ListADEntityControllers).RequirePermissions(permissions.GraphDBRead),

// RootCA Entity API
routerInst.GET(fmt.Sprintf("/api/v2/rootcas/{%s}", api.URIPathVariableObjectID), resources.GetRootCAEntityInfo).RequirePermissions(permissions.GraphDBRead).CheckFeatureFlag(resources.DB, appcfg.FeatureAdcs), // TODO: Cleanup #ADCSFeatureFlag after full launch.
routerInst.GET(fmt.Sprintf("/api/v2/rootcas/{%s}/controllers", api.URIPathVariableObjectID), resources.ListADEntityControllers).RequirePermissions(permissions.GraphDBRead).CheckFeatureFlag(resources.DB, appcfg.FeatureAdcs), // TODO: Cleanup #ADCSFeatureFlag after full launch.
routerInst.GET(fmt.Sprintf("/api/v2/rootcas/{%s}", api.URIPathVariableObjectID), resources.GetRootCAEntityInfo).RequirePermissions(permissions.GraphDBRead),
routerInst.GET(fmt.Sprintf("/api/v2/rootcas/{%s}/controllers", api.URIPathVariableObjectID), resources.ListADEntityControllers).RequirePermissions(permissions.GraphDBRead),

// EnterpriseCA Entity API
routerInst.GET(fmt.Sprintf("/api/v2/enterprisecas/{%s}", api.URIPathVariableObjectID), resources.GetEnterpriseCAEntityInfo).RequirePermissions(permissions.GraphDBRead).CheckFeatureFlag(resources.DB, appcfg.FeatureAdcs), // TODO: Cleanup #ADCSFeatureFlag after full launch.
routerInst.GET(fmt.Sprintf("/api/v2/enterprisecas/{%s}/controllers", api.URIPathVariableObjectID), resources.ListADEntityControllers).RequirePermissions(permissions.GraphDBRead).CheckFeatureFlag(resources.DB, appcfg.FeatureAdcs), // TODO: Cleanup #ADCSFeatureFlag after full launch.
routerInst.GET(fmt.Sprintf("/api/v2/enterprisecas/{%s}", api.URIPathVariableObjectID), resources.GetEnterpriseCAEntityInfo).RequirePermissions(permissions.GraphDBRead),
routerInst.GET(fmt.Sprintf("/api/v2/enterprisecas/{%s}/controllers", api.URIPathVariableObjectID), resources.ListADEntityControllers).RequirePermissions(permissions.GraphDBRead),

// NTAuthStore Entity API
routerInst.GET(fmt.Sprintf("/api/v2/ntauthstores/{%s}", api.URIPathVariableObjectID), resources.GetNTAuthStoreEntityInfo).RequirePermissions(permissions.GraphDBRead).CheckFeatureFlag(resources.DB, appcfg.FeatureAdcs), // TODO: Cleanup #ADCSFeatureFlag after full launch.
routerInst.GET(fmt.Sprintf("/api/v2/ntauthstores/{%s}/controllers", api.URIPathVariableObjectID), resources.ListADEntityControllers).RequirePermissions(permissions.GraphDBRead).CheckFeatureFlag(resources.DB, appcfg.FeatureAdcs), // TODO: Cleanup #ADCSFeatureFlag after full launch.
routerInst.GET(fmt.Sprintf("/api/v2/ntauthstores/{%s}", api.URIPathVariableObjectID), resources.GetNTAuthStoreEntityInfo).RequirePermissions(permissions.GraphDBRead),
routerInst.GET(fmt.Sprintf("/api/v2/ntauthstores/{%s}/controllers", api.URIPathVariableObjectID), resources.ListADEntityControllers).RequirePermissions(permissions.GraphDBRead),

// CertTemplate Entity API
routerInst.GET(fmt.Sprintf("/api/v2/certtemplates/{%s}", api.URIPathVariableObjectID), resources.GetCertTemplateEntityInfo).RequirePermissions(permissions.GraphDBRead).CheckFeatureFlag(resources.DB, appcfg.FeatureAdcs), // TODO: Cleanup #ADCSFeatureFlag after full launch.
routerInst.GET(fmt.Sprintf("/api/v2/certtemplates/{%s}/controllers", api.URIPathVariableObjectID), resources.ListADEntityControllers).RequirePermissions(permissions.GraphDBRead).CheckFeatureFlag(resources.DB, appcfg.FeatureAdcs), // TODO: Cleanup #ADCSFeatureFlag after full launch.
routerInst.GET(fmt.Sprintf("/api/v2/certtemplates/{%s}", api.URIPathVariableObjectID), resources.GetCertTemplateEntityInfo).RequirePermissions(permissions.GraphDBRead),
routerInst.GET(fmt.Sprintf("/api/v2/certtemplates/{%s}/controllers", api.URIPathVariableObjectID), resources.ListADEntityControllers).RequirePermissions(permissions.GraphDBRead),

// OU Entity API
routerInst.GET(fmt.Sprintf("/api/v2/ous/{%s}", api.URIPathVariableObjectID), resources.GetOUEntityInfo).RequirePermissions(permissions.GraphDBRead),
Expand Down
1 change: 0 additions & 1 deletion cmd/api/src/api/v2/file_uploads_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,6 @@ func Test_FileUploadWorkFlowVersion6(t *testing.T) {

func Test_FileUploadVersion6AllOptionADCS(t *testing.T) {
testCtx := integration.NewFOSSContext(t)
testCtx.ToggleFeatureFlag("adcs")

testCtx.SendFileIngest([]string{
"v6/all/aiacas.json",
Expand Down
6 changes: 2 additions & 4 deletions cmd/api/src/daemons/datapipe/convertors.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
"github.com/specterops/bloodhound/graphschema/ad"
)

func convertComputerData(data []ein.Computer, adcsEnabled bool) ConvertedData {
func convertComputerData(data []ein.Computer) ConvertedData {
converted := ConvertedData{}

for _, computer := range data {
Expand Down Expand Up @@ -57,9 +57,7 @@ func convertComputerData(data []ein.Computer, adcsEnabled bool) ConvertedData {
}
}

if adcsEnabled {
converted.NodeProps = append(converted.NodeProps, ein.ParseDCRegistryData(computer))
}
converted.NodeProps = append(converted.NodeProps, ein.ParseDCRegistryData(computer))

converted.NodeProps = append(converted.NodeProps, baseNodeProp)
}
Expand Down
80 changes: 31 additions & 49 deletions cmd/api/src/daemons/datapipe/ingest.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package datapipe

import (
"encoding/json"
"fmt"
"io"
"strings"
"time"
Expand All @@ -29,7 +28,6 @@ import (
"github.com/specterops/bloodhound/graphschema/azure"
"github.com/specterops/bloodhound/graphschema/common"
"github.com/specterops/bloodhound/log"
"github.com/specterops/bloodhound/src/model/appcfg"
)

func (s *Daemon) ReadWrapper(batch graph.Batch, reader io.Reader) error {
Expand Down Expand Up @@ -60,12 +58,6 @@ func (s *Daemon) IngestAzureData(batch graph.Batch, converted ConvertedAzureData
}

func (s *Daemon) IngestWrapper(batch graph.Batch, wrapper DataWrapper) error {
// TODO: Cleanup #ADCSFeatureFlag after full launch.
adcsFlag, err := s.db.GetFlagByKey(appcfg.FeatureAdcs)
if err != nil {
return fmt.Errorf("error getting ADCS feature flag: %w", err)
}

switch wrapper.Metadata.Type {
case DataTypeComputer:
// We should not be getting anything with Version < 5 at this point, and we don't want to ingest it if we do as post-processing will blow it away anyways
Expand All @@ -75,7 +67,7 @@ func (s *Daemon) IngestWrapper(batch graph.Batch, wrapper DataWrapper) error {
if err := json.Unmarshal(wrapper.Payload, &computerData); err != nil {
return err
} else {
converted := convertComputerData(computerData, adcsFlag.Enabled)
converted := convertComputerData(computerData)
s.IngestBasicData(batch, converted)
}
}
Expand Down Expand Up @@ -143,58 +135,48 @@ func (s *Daemon) IngestWrapper(batch graph.Batch, wrapper DataWrapper) error {
}

case DataTypeAIACA:
if adcsFlag.Enabled {
var aiacaData []ein.AIACA
if err := json.Unmarshal(wrapper.Payload, &aiacaData); err != nil {
return err
} else {
converted := convertAIACAData(aiacaData)
s.IngestBasicData(batch, converted)
}
var aiacaData []ein.AIACA
if err := json.Unmarshal(wrapper.Payload, &aiacaData); err != nil {
return err
} else {
converted := convertAIACAData(aiacaData)
s.IngestBasicData(batch, converted)
}

case DataTypeRootCA:
if adcsFlag.Enabled {
var rootcaData []ein.RootCA
if err := json.Unmarshal(wrapper.Payload, &rootcaData); err != nil {
return err
} else {
converted := convertRootCAData(rootcaData)
s.IngestBasicData(batch, converted)
}
var rootcaData []ein.RootCA
if err := json.Unmarshal(wrapper.Payload, &rootcaData); err != nil {
return err
} else {
converted := convertRootCAData(rootcaData)
s.IngestBasicData(batch, converted)
}

case DataTypeEnterpriseCA:
if adcsFlag.Enabled {
var enterprisecaData []ein.EnterpriseCA
if err := json.Unmarshal(wrapper.Payload, &enterprisecaData); err != nil {
return err
} else {
converted := convertEnterpriseCAData(enterprisecaData)
s.IngestBasicData(batch, converted)
}
var enterprisecaData []ein.EnterpriseCA
if err := json.Unmarshal(wrapper.Payload, &enterprisecaData); err != nil {
return err
} else {
converted := convertEnterpriseCAData(enterprisecaData)
s.IngestBasicData(batch, converted)
}

case DataTypeNTAuthStore:
if adcsFlag.Enabled {
var ntauthstoreData []ein.NTAuthStore
if err := json.Unmarshal(wrapper.Payload, &ntauthstoreData); err != nil {
return err
} else {
converted := convertNTAuthStoreData(ntauthstoreData)
s.IngestBasicData(batch, converted)
}
var ntauthstoreData []ein.NTAuthStore
if err := json.Unmarshal(wrapper.Payload, &ntauthstoreData); err != nil {
return err
} else {
converted := convertNTAuthStoreData(ntauthstoreData)
s.IngestBasicData(batch, converted)
}

case DataTypeCertTemplate:
if adcsFlag.Enabled {
var certtemplateData []ein.CertTemplate
if err := json.Unmarshal(wrapper.Payload, &certtemplateData); err != nil {
return err
} else {
converted := convertCertTemplateData(certtemplateData)
s.IngestBasicData(batch, converted)
}
var certtemplateData []ein.CertTemplate
if err := json.Unmarshal(wrapper.Payload, &certtemplateData); err != nil {
return err
} else {
converted := convertCertTemplateData(certtemplateData)
s.IngestBasicData(batch, converted)
}

case DataTypeAzure:
Expand Down
2 changes: 1 addition & 1 deletion cmd/api/src/model/appcfg/flag.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func AvailableFlags() FeatureFlagSet {
Name: "Enable collection and processing of Active Directory Certificate Services Data",
Description: "Enables the ability to collect, analyze, and explore Active Directory Certificate Services data and previews new attack paths.",
Enabled: false,
UserUpdatable: true,
UserUpdatable: false,
},
}
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/api/src/test/integration/harnesses/harnessgen.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ def create_statement(self):
nodes[id] = UserNode(name)
elif 'Group' in name:
nodes[id] = GroupNode(name)
elif 'Computer' in name:
elif 'Computer' or 'DC' in name:
nodes[id] = ComputerNode(name)
elif 'OU' in name:
nodes[id] = OUNode(name)
Expand Down
50 changes: 26 additions & 24 deletions packages/go/analysis/ad/adcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,6 @@ var (
)

func PostADCS(ctx context.Context, db graph.Database, groupExpansions impact.PathAggregator, adcsEnabled bool) (*analysis.AtomicPostProcessingStats, error) {
if !adcsEnabled {
return &analysis.AtomicPostProcessingStats{}, nil
}

if enterpriseCertAuthorities, err := FetchNodesByKind(ctx, db, ad.EnterpriseCA); err != nil {
return &analysis.AtomicPostProcessingStats{}, fmt.Errorf("failed fetching enterpriseCA nodes: %w", err)
} else if rootCertAuthorities, err := FetchNodesByKind(ctx, db, ad.RootCA); err != nil {
Expand Down Expand Up @@ -67,7 +63,7 @@ func PostADCS(ctx context.Context, db graph.Database, groupExpansions impact.Pat
innerEnterpriseCA := enterpriseCA

if cache.DoesCAChainProperlyToDomain(innerEnterpriseCA, innerDomain) {
processEnterpriseCAWithValidCertChainToDomain(innerEnterpriseCA, innerDomain, groupExpansions, cache, operation)
processEnterpriseCAWithValidCertChainToDomain(innerEnterpriseCA, innerDomain, groupExpansions, cache, operation, adcsEnabled)
}
}
}
Expand Down Expand Up @@ -113,7 +109,7 @@ func postADCSPreProcessStep2(ctx context.Context, db graph.Database, certTemplat
}
}

func processEnterpriseCAWithValidCertChainToDomain(enterpriseCA, domain *graph.Node, groupExpansions impact.PathAggregator, cache ADCSCache, operation analysis.StatTrackedOperation[analysis.CreatePostRelationshipJob]) {
func processEnterpriseCAWithValidCertChainToDomain(enterpriseCA, domain *graph.Node, groupExpansions impact.PathAggregator, cache ADCSCache, operation analysis.StatTrackedOperation[analysis.CreatePostRelationshipJob], adcsEnabled bool) {

operation.Operation.SubmitReader(func(ctx context.Context, tx graph.Transaction, outC chan<- analysis.CreatePostRelationshipJob) error {
if err := PostGoldenCert(ctx, tx, outC, domain, enterpriseCA); err != nil {
Expand Down Expand Up @@ -143,12 +139,14 @@ func processEnterpriseCAWithValidCertChainToDomain(enterpriseCA, domain *graph.N
return nil
})

operation.Operation.SubmitReader(func(ctx context.Context, tx graph.Transaction, outC chan<- analysis.CreatePostRelationshipJob) error {
if err := PostADCSESC6b(ctx, tx, outC, groupExpansions, enterpriseCA, domain, cache); err != nil {
log.Errorf("failed post processing for %s: %v", ad.ADCSESC6a.String(), err)
}
return nil
})
if adcsEnabled {
operation.Operation.SubmitReader(func(ctx context.Context, tx graph.Transaction, outC chan<- analysis.CreatePostRelationshipJob) error {
if err := PostADCSESC6b(ctx, tx, outC, groupExpansions, enterpriseCA, domain, cache); err != nil {
log.Errorf("failed post processing for %s: %v", ad.ADCSESC6a.String(), err)
}
return nil
})
}

operation.Operation.SubmitReader(func(ctx context.Context, tx graph.Transaction, outC chan<- analysis.CreatePostRelationshipJob) error {
if err := PostADCSESC9a(ctx, tx, outC, groupExpansions, enterpriseCA, domain, cache); err != nil {
Expand All @@ -157,12 +155,14 @@ func processEnterpriseCAWithValidCertChainToDomain(enterpriseCA, domain *graph.N
return nil
})

operation.Operation.SubmitReader(func(ctx context.Context, tx graph.Transaction, outC chan<- analysis.CreatePostRelationshipJob) error {
if err := PostADCSESC9b(ctx, tx, outC, groupExpansions, enterpriseCA, domain, cache); err != nil {
log.Errorf("failed post processing for %s: %v", ad.ADCSESC9a.String(), err)
}
return nil
})
if adcsEnabled {
operation.Operation.SubmitReader(func(ctx context.Context, tx graph.Transaction, outC chan<- analysis.CreatePostRelationshipJob) error {
if err := PostADCSESC9b(ctx, tx, outC, groupExpansions, enterpriseCA, domain, cache); err != nil {
log.Errorf("failed post processing for %s: %v", ad.ADCSESC9a.String(), err)
}
return nil
})
}

operation.Operation.SubmitReader(func(ctx context.Context, tx graph.Transaction, outC chan<- analysis.CreatePostRelationshipJob) error {
if err := PostADCSESC10a(ctx, tx, outC, groupExpansions, enterpriseCA, domain, cache); err != nil {
Expand All @@ -171,10 +171,12 @@ func processEnterpriseCAWithValidCertChainToDomain(enterpriseCA, domain *graph.N
return nil
})

operation.Operation.SubmitReader(func(ctx context.Context, tx graph.Transaction, outC chan<- analysis.CreatePostRelationshipJob) error {
if err := PostADCSESC10b(ctx, tx, outC, groupExpansions, enterpriseCA, domain, cache); err != nil {
log.Errorf("failed post processing for %s: %v", ad.ADCSESC10b.String(), err)
}
return nil
})
if adcsEnabled {
operation.Operation.SubmitReader(func(ctx context.Context, tx graph.Transaction, outC chan<- analysis.CreatePostRelationshipJob) error {
if err := PostADCSESC10b(ctx, tx, outC, groupExpansions, enterpriseCA, domain, cache); err != nil {
log.Errorf("failed post processing for %s: %v", ad.ADCSESC10b.String(), err)
}
return nil
})
}
}
Loading