Skip to content

Commit 4591818

Browse files
committed
firewalldb: handle deleted sessions in kv stores mig
If the user has deleted their session.db file, but kept their rules.db file, there can exist kv entry values that point to a now deleted session ID. Such kv entries should be ignored during the migration, as they are cannot be used anymore. This commit updates the migration to handle this case.
1 parent 52dcd20 commit 4591818

File tree

2 files changed

+155
-5
lines changed

2 files changed

+155
-5
lines changed

firewalldb/sql_migration.go

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ func migrateKVStoresDBToSQL(ctx context.Context, kvStore *bbolt.DB,
139139
// 1) Collect all key-value pairs from the KV store.
140140
err := kvStore.View(func(tx *bbolt.Tx) error {
141141
var err error
142-
pairs, err = collectAllPairs(tx)
142+
pairs, err = collectAllPairs(sessMap, tx)
143143
return err
144144
})
145145
if err != nil {
@@ -198,7 +198,9 @@ func migrateKVStoresDBToSQL(ctx context.Context, kvStore *bbolt.DB,
198198
// designed to iterate over all buckets and values that exist in the KV store.
199199
// That ensures that we find all stores and values that exist in the KV store,
200200
// and can be sure that the kv store actually follows the expected structure.
201-
func collectAllPairs(tx *bbolt.Tx) ([]*kvEntry, error) {
201+
func collectAllPairs(sessMap map[[4]byte]sqlc.Session,
202+
tx *bbolt.Tx) ([]*kvEntry, error) {
203+
202204
var entries []*kvEntry
203205
for _, perm := range []bool{true, false} {
204206
mainBucket, err := getMainBucket(tx, false, perm)
@@ -228,7 +230,7 @@ func collectAllPairs(tx *bbolt.Tx) ([]*kvEntry, error) {
228230
}
229231

230232
pairs, err := collectRulePairs(
231-
ruleBucket, perm, string(rule),
233+
sessMap, ruleBucket, perm, string(rule),
232234
)
233235
if err != nil {
234236
return err
@@ -248,8 +250,8 @@ func collectAllPairs(tx *bbolt.Tx) ([]*kvEntry, error) {
248250

249251
// collectRulePairs processes a single rule bucket, which should contain the
250252
// global and session-kv-store key buckets.
251-
func collectRulePairs(bkt *bbolt.Bucket, perm bool, rule string) ([]*kvEntry,
252-
error) {
253+
func collectRulePairs(sessMap map[[4]byte]sqlc.Session, bkt *bbolt.Bucket,
254+
perm bool, rule string) ([]*kvEntry, error) {
253255

254256
var params []*kvEntry
255257

@@ -281,6 +283,25 @@ func collectRulePairs(bkt *bbolt.Bucket, perm bool, rule string) ([]*kvEntry,
281283
"under %s bucket", sessKVStoreBucketKey)
282284
}
283285

286+
var alias [4]byte
287+
copy(alias[:], groupAlias)
288+
if _, ok := sessMap[alias]; !ok {
289+
// If we can't find the session group in the
290+
// SQL db, that indicates that the session was
291+
// never migrated from KVDB. This likely means
292+
// that the user deleted their session.db file,
293+
// but kept the rules.db file. As the KV entries
294+
// are useless when the session no longer
295+
// exists, we can just skip the migration of the
296+
// KV entries for this group.
297+
log.Warnf("Skipping migration of KV store "+
298+
"entries for session group %x, as the "+
299+
"session group was not found",
300+
groupAlias)
301+
302+
return nil
303+
}
304+
284305
groupBucket := sessBkt.Bucket(groupAlias)
285306
if groupBucket == nil {
286307
return fmt.Errorf("group bucket for group "+
@@ -413,6 +434,9 @@ func insertPair(ctx context.Context, tx SQLQueries,
413434

414435
sess, ok := sessMap[groupAlias]
415436
if !ok {
437+
// This should be unreachable, as we check for the
438+
// existence of the session group when collecting
439+
// the kv pairs.
416440
err = fmt.Errorf("session group %x not found in map",
417441
alias)
418442
}

firewalldb/sql_migration_test.go

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,10 +418,26 @@ func TestFirewallDBMigration(t *testing.T) {
418418
name: "session specific kv entries",
419419
populateDB: sessionSpecificEntries,
420420
},
421+
{
422+
name: "session specific kv entries deleted session",
423+
populateDB: sessionSpecificEntriesDeletedSession,
424+
},
425+
{
426+
name: "session specific kv entries deleted and existing sessions",
427+
populateDB: sessionSpecificEntriesDeletedAndExistingSessions,
428+
},
421429
{
422430
name: "feature specific kv entries",
423431
populateDB: featureSpecificEntries,
424432
},
433+
{
434+
name: "feature specific kv entries deleted session",
435+
populateDB: featureSpecificEntriesDeletedSession,
436+
},
437+
{
438+
name: "feature specific kv entries deleted and existing sessions",
439+
populateDB: featureSpecificEntriesDeletedAndExistingSessions,
440+
},
425441
{
426442
name: "all kv entry combinations",
427443
populateDB: allEntryCombinations,
@@ -585,6 +601,61 @@ func sessionSpecificEntries(t *testing.T, ctx context.Context, boltDB *BoltDB,
585601
)
586602
}
587603

604+
// sessionSpecificEntriesDeletedSession populates the kv store with one session
605+
// specific entry for the local temp store, and one session specific entry for
606+
// the perm local store. Once populated, the session that the entries are linked
607+
// to is deleted. When migrating, we therefore expect that the kv entries linked
608+
// to the deleted session are not migrated.
609+
func sessionSpecificEntriesDeletedSession(t *testing.T, ctx context.Context,
610+
boltDB *BoltDB, sessionStore session.Store, _ accounts.Store,
611+
_ *rootKeyMockStore) *expectedResult {
612+
613+
groupAlias := getNewSessionAlias(t, ctx, sessionStore)
614+
615+
_ = insertTempAndPermEntry(
616+
t, ctx, boltDB, testRuleName, groupAlias, fn.None[string](),
617+
testEntryKey, testEntryValue,
618+
)
619+
620+
err := sessionStore.DeleteReservedSessions(ctx)
621+
require.NoError(t, err)
622+
623+
return &expectedResult{
624+
// Since the session the kx entries were linked to has been
625+
// deleted, we expect no kv entries to be migrated.
626+
kvEntries: []*kvEntry{},
627+
privPairs: make(privacyPairs),
628+
actions: []*Action{},
629+
}
630+
}
631+
632+
// sessionSpecificEntriesDeletedAndExistingSessions populates the kv store with
633+
// two sessions and their corresponding kv entries.
634+
// One of the sessions is deleted prior to the migration though, and therefore
635+
// the migration should only migrate the kv entries linked to the still existing
636+
// session.
637+
func sessionSpecificEntriesDeletedAndExistingSessions(t *testing.T,
638+
ctx context.Context, boltDB *BoltDB, sessionStore session.Store,
639+
_ accounts.Store, _ *rootKeyMockStore) *expectedResult {
640+
641+
groupAlias1 := getNewSessionAlias(t, ctx, sessionStore)
642+
643+
_ = insertTempAndPermEntry(
644+
t, ctx, boltDB, testRuleName, groupAlias1, fn.None[string](),
645+
testEntryKey, testEntryValue,
646+
)
647+
648+
err := sessionStore.DeleteReservedSessions(ctx)
649+
require.NoError(t, err)
650+
651+
groupAlias2 := getNewSessionAlias(t, ctx, sessionStore)
652+
653+
return insertTempAndPermEntry(
654+
t, ctx, boltDB, testRuleName2, groupAlias2, fn.None[string](),
655+
testEntryKey2, testEntryValue,
656+
)
657+
}
658+
588659
// featureSpecificEntries populates the kv store with one feature specific
589660
// entry for the local temp store, and one feature specific entry for the perm
590661
// local store.
@@ -600,6 +671,61 @@ func featureSpecificEntries(t *testing.T, ctx context.Context, boltDB *BoltDB,
600671
)
601672
}
602673

674+
// featureSpecificEntriesDeletedSession populates the kv store with one feature
675+
// specific entry for the local temp store, and one feature specific entry for
676+
// the perm local store. Once populated, the session that the entries are linked
677+
// to is deleted. When migrating, we therefore expect that the kv entries linked
678+
// to the deleted session are not migrated.
679+
func featureSpecificEntriesDeletedSession(t *testing.T, ctx context.Context,
680+
boltDB *BoltDB, sessionStore session.Store, _ accounts.Store,
681+
_ *rootKeyMockStore) *expectedResult {
682+
683+
groupAlias := getNewSessionAlias(t, ctx, sessionStore)
684+
685+
_ = insertTempAndPermEntry(
686+
t, ctx, boltDB, testRuleName, groupAlias,
687+
fn.Some(testFeatureName), testEntryKey, testEntryValue,
688+
)
689+
690+
err := sessionStore.DeleteReservedSessions(ctx)
691+
require.NoError(t, err)
692+
693+
return &expectedResult{
694+
// Since the session the kv entries were linked to has been
695+
// deleted, we expect no kv entries to be migrated.
696+
kvEntries: []*kvEntry{},
697+
privPairs: make(privacyPairs),
698+
actions: []*Action{},
699+
}
700+
}
701+
702+
// featureSpecificEntriesDeletedAndExistingSessions populates the kv store with
703+
// two sessions and their corresponding feature specific kv entries.
704+
// One of the sessions is deleted prior to the migration though, and therefore
705+
// the migration should only migrate the kv entries linked to the still existing
706+
// session.
707+
func featureSpecificEntriesDeletedAndExistingSessions(t *testing.T,
708+
ctx context.Context, boltDB *BoltDB, sessionStore session.Store,
709+
_ accounts.Store, _ *rootKeyMockStore) *expectedResult {
710+
711+
groupAlias1 := getNewSessionAlias(t, ctx, sessionStore)
712+
713+
_ = insertTempAndPermEntry(
714+
t, ctx, boltDB, testRuleName, groupAlias1,
715+
fn.Some(testFeatureName), testEntryKey, testEntryValue,
716+
)
717+
718+
err := sessionStore.DeleteReservedSessions(ctx)
719+
require.NoError(t, err)
720+
721+
groupAlias2 := getNewSessionAlias(t, ctx, sessionStore)
722+
723+
return insertTempAndPermEntry(
724+
t, ctx, boltDB, testRuleName2, groupAlias2,
725+
fn.Some(testFeatureName2), testEntryKey2, testEntryValue,
726+
)
727+
}
728+
603729
// allEntryCombinations adds all types of different entries at all possible
604730
// levels of the kvstores, including multple entries with the same
605731
// ruleName, groupAlias and featureName. The test aims to cover all possible

0 commit comments

Comments
 (0)