diff --git a/netquery/database.go b/netquery/database.go index dab486c9..df038103 100644 --- a/netquery/database.go +++ b/netquery/database.go @@ -355,7 +355,7 @@ func (db *Database) dumpTo(ctx context.Context, w io.Writer) error { //nolint:un // // Save uses the database write connection instead of relying on the // connection pool. -func (db *Database) Save(ctx context.Context, conn Conn) error { +func (db *Database) Save(ctx context.Context, conn Conn, enableHistory bool) error { connMap, err := orm.ToParamMap(ctx, conn, "", orm.DefaultEncodeConfig) if err != nil { return fmt.Errorf("failed to encode connection for SQL: %w", err) @@ -387,7 +387,13 @@ func (db *Database) Save(ctx context.Context, conn Conn) error { // TODO(ppacher): make sure this one can be cached to speed up inserting // and save some CPU cycles for the user - for _, dbName := range []string{"main", "history"} { + dbNames := []string{"main"} + + if enableHistory { + dbNames = append(dbNames, "history") + } + + for _, dbName := range dbNames { sql := fmt.Sprintf( `INSERT INTO %s.connections (%s) VALUES(%s) diff --git a/netquery/manager.go b/netquery/manager.go index 6599d619..bcd60618 100644 --- a/netquery/manager.go +++ b/netquery/manager.go @@ -25,7 +25,7 @@ type ( // insert or an update. // The ID of Conn is unique and can be trusted to never collide with other // connections of the save device. - Save(context.Context, Conn) error + Save(context.Context, Conn, bool) error } // Manager handles new and updated network.Connections feeds and persists them @@ -100,7 +100,7 @@ func (mng *Manager) HandleFeed(ctx context.Context, feed <-chan *network.Connect log.Tracef("netquery: updating connection %s", conn.ID) - if err := mng.store.Save(ctx, *model); err != nil { + if err := mng.store.Save(ctx, *model, conn.Process().Profile().HistoryEnabled()); err != nil { log.Errorf("netquery: failed to save connection %s in sqlite database: %s", conn.ID, err) continue diff --git a/profile/config.go b/profile/config.go index 416de06b..3c87b009 100644 --- a/profile/config.go +++ b/profile/config.go @@ -105,6 +105,10 @@ var ( // Setting "Permanent Verdicts" at order 96. + CfgOptionEnableHistoryKey = "filter/enableHistory" + cfgOptionEnableHistory config.BoolOption + cfgOptionEnableHistoryOrder = 66 + // Setting "Enable SPN" at order 128. CfgOptionUseSPNKey = "spn/use" @@ -239,6 +243,26 @@ func registerConfiguration() error { //nolint:maintidx cfgOptionDisableAutoPermit = config.Concurrent.GetAsInt(CfgOptionDisableAutoPermitKey, int64(status.SecurityLevelsAll)) cfgIntOptions[CfgOptionDisableAutoPermitKey] = cfgOptionDisableAutoPermit + // Enable History + err = config.Register(&config.Option{ + Name: "Enable Connection History", + Key: CfgOptionEnableHistoryKey, + Description: "Whether or not to save connections to the history database", + OptType: config.OptTypeBool, + ReleaseLevel: config.ReleaseLevelExperimental, + ExpertiseLevel: config.ExpertiseLevelExpert, + DefaultValue: false, + Annotations: config.Annotations{ + config.DisplayOrderAnnotation: cfgOptionEnableHistoryOrder, + config.CategoryAnnotation: "Advanced", + }, + }) + if err != nil { + return err + } + cfgOptionEnableHistory = config.Concurrent.GetAsBool(CfgOptionEnableHistoryKey, false) + cfgBoolOptions[CfgOptionEnableHistoryKey] = cfgOptionEnableHistory + rulesHelp := strings.ReplaceAll(`Rules are checked from top to bottom, stopping after the first match. They can match: - By address: "192.168.0.1" diff --git a/profile/profile-layered.go b/profile/profile-layered.go index b2f7850b..5380aca8 100644 --- a/profile/profile-layered.go +++ b/profile/profile-layered.go @@ -49,6 +49,7 @@ type LayeredProfile struct { DomainHeuristics config.BoolOption `json:"-"` UseSPN config.BoolOption `json:"-"` SPNRoutingAlgorithm config.StringOption `json:"-"` + HistoryEnabled config.BoolOption `json:"-"` } // NewLayeredProfile returns a new layered profile based on the given local profile. @@ -120,6 +121,10 @@ func NewLayeredProfile(localProfile *Profile) *LayeredProfile { CfgOptionRoutingAlgorithmKey, cfgOptionRoutingAlgorithm, ) + lp.HistoryEnabled = lp.wrapBoolOption( + CfgOptionEnableHistoryKey, + cfgOptionEnableHistory, + ) lp.LayerIDs = append(lp.LayerIDs, localProfile.ScopedID()) lp.layers = append(lp.layers, localProfile) diff --git a/profile/profile.go b/profile/profile.go index 1fa12ff8..2d0eb9c4 100644 --- a/profile/profile.go +++ b/profile/profile.go @@ -136,6 +136,7 @@ type Profile struct { //nolint:maligned // not worth the effort filterListIDs []string spnUsagePolicy endpoints.Endpoints spnExitHubPolicy endpoints.Endpoints + enableHistory bool // Lifecycle Management outdated *abool.AtomicBool @@ -233,6 +234,11 @@ func (profile *Profile) parseConfig() error { } } + enableHistory, ok := profile.configPerspective.GetAsBool(CfgOptionEnableHistoryKey) + if ok { + profile.enableHistory = enableHistory + } + return lastErr } @@ -315,6 +321,11 @@ func (profile *Profile) IsOutdated() bool { return profile.outdated.IsSet() } +// HistoryEnabled returns true if connection history is enabled for the profile. +func (profile *Profile) HistoryEnabled() bool { + return profile.enableHistory +} + // GetEndpoints returns the endpoint list of the profile. This functions // requires the profile to be read locked. func (profile *Profile) GetEndpoints() endpoints.Endpoints {