Skip to content

Commit

Permalink
fix: bugfixes for the encryption migration (#131)
Browse files Browse the repository at this point in the history
* fix: copy the whole database when encrypting, not just all collections
* fix: only perform encryption migration if a database file already exists
  • Loading branch information
DarkAtra authored Nov 16, 2024
1 parent bf2b5bb commit 5f08a62
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 39 deletions.
2 changes: 1 addition & 1 deletion docs/self-hosting.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ You can also build it from scratch by cloning the repository and then running `m
```yaml
services:
v-rising-discord-bot:
image: ghcr.io/darkatra/v-rising-discord-bot:2.12.0-native # find the latest version here: https://github.com/DarkAtra/v-rising-discord-bot/releases
image: ghcr.io/darkatra/v-rising-discord-bot:2.12.1-native # find the latest version here: https://github.com/DarkAtra/v-rising-discord-bot/releases
command: -Dagql.nativeTransport=false
mem_reservation: 128M
mem_limit: 256M
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

<groupId>de.darkatra</groupId>
<artifactId>v-rising-discord-bot</artifactId>
<version>2.12.0</version>
<version>2.12.1-next.2</version>
<packaging>jar</packaging>

<licenses>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,5 @@ fun Nitrite.getNitriteMap(name: String): NitriteMap<NitriteId, Document> {
}

fun Nitrite.listAllCollectionNames(): List<String> {
return store.openMap<String, Attributes>(Constants.META_MAP_NAME, String::class.java, Attributes::class.java).keys()
.filter { key -> !key.startsWith("\$nitrite") }
return store.openMap<String, Attributes>(Constants.META_MAP_NAME, String::class.java, Attributes::class.java).keys().toList()
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package de.darkatra.vrising.discord.persistence

import de.darkatra.vrising.discord.BotProperties
import de.darkatra.vrising.discord.migration.SchemaEntityConverter
import de.darkatra.vrising.discord.migration.getNitriteMap
import de.darkatra.vrising.discord.migration.listAllCollectionNames
import de.darkatra.vrising.discord.persistence.model.converter.ErrorEntityConverter
import de.darkatra.vrising.discord.persistence.model.converter.PlayerActivityFeedEntityConverter
Expand All @@ -11,6 +10,8 @@ import de.darkatra.vrising.discord.persistence.model.converter.ServerEntityConve
import de.darkatra.vrising.discord.persistence.model.converter.StatusMonitorEntityConverter
import org.dizitart.no2.Nitrite
import org.dizitart.no2.NitriteBuilder
import org.dizitart.no2.collection.Document
import org.dizitart.no2.collection.NitriteId
import org.dizitart.no2.exceptions.NitriteIOException
import org.dizitart.no2.mvstore.MVStoreModule
import org.dizitart.no2.store.StoreModule
Expand Down Expand Up @@ -41,49 +42,52 @@ class DatabaseConfiguration(
fun buildNitriteDatabase(databaseFile: Path, username: String? = null, password: String? = null): Nitrite {

// version 2.12.0 introduced database encryption at rest. the following code attempts to perform the migration if necessary
val firstFewBytes = databaseFile.inputStream().readNBytes(ENCRYPTED_MARKER.length).toString(StandardCharsets.UTF_8)
if (firstFewBytes != ENCRYPTED_MARKER) {

// if the automated migration was aborted while writing the files to disc, restore the backup
val unencryptedDatabaseBackupFile = Path.of(System.getProperty("java.io.tmpdir")).resolve("v-rising-bot.db.unencrypted")
if (unencryptedDatabaseBackupFile.exists()) {
logger.info("Found an unencrypted backup of the database at: ${unencryptedDatabaseBackupFile.absolutePathString()}")
unencryptedDatabaseBackupFile.copyTo(databaseFile, overwrite = true)
logger.info("Successfully restored the backup. Will re-attempt the migration.")
}
if (databaseFile.exists()) {

logger.info("Attempting to encrypt the bot database with the provided database password.")
val firstFewBytes = databaseFile.inputStream().readNBytes(ENCRYPTED_MARKER.length).toString(StandardCharsets.UTF_8)
if (firstFewBytes != ENCRYPTED_MARKER) {

// retry opening the database without encryption if we encounter an error
val unencryptedDatabase = try {
getNitriteBuilder(getStoreModule(databaseFile, null)).openOrCreate(username, password)
} catch (e: NitriteIOException) {
throw IllegalStateException("Could not encrypt the database.", e)
}
// if the automated migration was aborted while writing the files to disc, restore the backup
val unencryptedDatabaseBackupFile = Path.of(System.getProperty("java.io.tmpdir")).resolve("v-rising-bot.db.unencrypted")
if (unencryptedDatabaseBackupFile.exists()) {
logger.info("Found an unencrypted backup of the database at: ${unencryptedDatabaseBackupFile.absolutePathString()}")
unencryptedDatabaseBackupFile.copyTo(databaseFile, overwrite = true)
logger.info("Successfully restored the backup. Will re-attempt the migration.")
}

unencryptedDatabaseBackupFile.deleteIfExists()
logger.info("Attempting to encrypt the bot database with the provided database password.")

// create an encrypted copy of the existing database
val tempDatabaseFile = Files.createTempFile("v-rising-bot", ".db")
// retry opening the database without encryption if we encounter an error
val unencryptedDatabase = try {
getNitriteBuilder(getStoreModule(databaseFile, null)).openOrCreate(username, password)
} catch (e: NitriteIOException) {
throw IllegalStateException("Could not encrypt the database.", e)
}

val encryptedDatabase = getNitriteBuilder(getStoreModule(tempDatabaseFile, password)).openOrCreate(username, password)
for (collectionName in unencryptedDatabase.listAllCollectionNames()) {
unencryptedDatabaseBackupFile.deleteIfExists()

val oldCollection = unencryptedDatabase.getNitriteMap(collectionName)
val newCollection = encryptedDatabase.getNitriteMap(collectionName)
// create an encrypted copy of the existing database
val tempDatabaseFile = Files.createTempFile("v-rising-bot", ".db")

oldCollection.values().forEach { document -> newCollection.put(document.id, document) }
}
unencryptedDatabase.close()
encryptedDatabase.close()
val encryptedDatabase = getNitriteBuilder(getStoreModule(tempDatabaseFile, password)).openOrCreate(username, password)
for (collectionName in unencryptedDatabase.listAllCollectionNames()) {

databaseFile.copyTo(unencryptedDatabaseBackupFile)
tempDatabaseFile.copyTo(databaseFile, overwrite = true)
val oldCollection = unencryptedDatabase.store.openMap<NitriteId, Any>(collectionName, NitriteId::class.java, Document::class.java)
val newCollection = encryptedDatabase.store.openMap<NitriteId, Any>(collectionName, NitriteId::class.java, Document::class.java)

unencryptedDatabaseBackupFile.deleteIfExists()
tempDatabaseFile.deleteIfExists()
oldCollection.entries().forEach { entry -> newCollection.put(entry.first, entry.second) }
}
unencryptedDatabase.close()
encryptedDatabase.close()

logger.info("Successfully encrypted the database.")
databaseFile.copyTo(unencryptedDatabaseBackupFile)
tempDatabaseFile.copyTo(databaseFile, overwrite = true)

unencryptedDatabaseBackupFile.deleteIfExists()
tempDatabaseFile.deleteIfExists()

logger.info("Successfully encrypted the database.")
}
}

return getNitriteBuilder(getStoreModule(databaseFile, password)).openOrCreate(username, password)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import java.nio.file.Files
import java.nio.file.StandardOpenOption.CREATE
import java.nio.file.StandardOpenOption.TRUNCATE_EXISTING
import kotlin.io.path.absolutePathString
import kotlin.io.path.deleteIfExists
import kotlin.io.path.outputStream

object DatabaseConfigurationTestUtils {
Expand All @@ -24,7 +25,7 @@ object DatabaseConfigurationTestUtils {

val databaseFile = Files.createTempFile("v-rising-bot", ".db").also {
logger.info("Test Db location: " + it.absolutePathString())
}
}.also { it.deleteIfExists() }

if (fromTemplate != null) {
logger.info("Loading template from '$fromTemplate'.")
Expand Down

0 comments on commit 5f08a62

Please sign in to comment.